박상권의 삽질블로그

[안드로이드/Android]개발할때만 Log 남기는 방법 - Debug Log 본문

IT/Android-TIP (한글)

[안드로이드/Android]개발할때만 Log 남기는 방법 - Debug Log

박상권 2015. 7. 3. 10:01

제가 운영하고 있는 유튜브 채널 '개발자 테드박'에도 많은 관심 부탁드려요.
스타트업/개발자/IT 관련된 여러 영상을 올리고 있습니다.
영상보러가기



블로그를 Medium으로 옮겨서 운영하고 있습니다.
앞으로 새로운 글은 모두 미디엄 블로그를 통해서 올릴 예정입니다.
미디엄에서 다양하고 유익한 포스팅을 살펴보세요
미디엄 블로그 보기






저는 안드로이드 개발하면서 디버깅보다는 곳곳에 로그를 남겨두고 예상되는대로 실행이되는지 확인합니다.

혹은 변수값이나 클래스등의 값을 확인하기도 합니다.










로그를 찍을때는 Log.d(TAG,로그내용) 과 같이 쓰고 계실겁니다.


Log.d("TedPark","로그 내용");

Log.i("TedPark","로그 내용");

Log.e("TedPark","에러 내용");




하지만 이렇게 남긴 로그들은 실제 배포버전으로 배포해서 플레이스토어에 배포한뒤에도 로그캣에 계속 찍히게됩니다.

제가 몇개의 앱을 받아서 Logcat을 연결해보면 해당 앱의 개발자가 남겨둔 로그들을 저도 확인할 수 있는 경우가 더러 있습니다.

다른사람이 알아도 문제없는 로그라면 상관없겠지만 특정 정보가 있거나 URL정보 등 기타 민감한 정보들을 개발자가 아닌 다른 일반사용자가 보게된다고 생각하면 마음이 편하지는 않을것입니다.



여기서 우리들이 선택할만한 방법은 3가지정도로 생각해볼수 있을것 같습니다.


1. 개발할때부터 아예 로그를 남기지 않는다.

2. 개발을 완료하고 배포버전을 만들기전 모든 로그코드를 삭제한다.

3. 개발할때만 로그를 남기고 배포버전에서는 로그가 남지않도록 코드를 작성한다.





로그가 필요없이 생각하는대로 코딩을 잘 하는 개발자분이라면 당연히 1번의 방법도 상관없겠죠...

하지만 저는 천재개발자가 아니기 때문에 무식하게 로그를 왕창 찍어두어야 합니다..

그래서 제가 택한 방법은 3번 이었습니다.




설명없이 바로 적용시켜보고싶으신분은 제가 GitHub에 만들어둔 DLog를 사용하시면 됩니다.

Dlog바로 보기







먼저, 개발버전과 배포버전일때 무엇이 바뀌는지 부터 고민했습니다.

특정 변수나 값들이 개발/배포버전에 따라서 변경된다면 해당 값을 참조해서 Flag처럼 로그를 남기거나 남기지 않는 방식의 아이디어로 접근하려고 한것입니다.

열심히 방법을생각해보다가  (구글링을 하다가)  찾아낸것이 바로 BuildConfig.DEBUG였습니다.

개발시에는 BuildConfig.DEBUG 값이 true,

배포시에는 BuildConfig.DEBUG 값이 false 로 변경된다고 합니다.


결론적으로 말씀드리자면 이 값을 100% 신뢰할수가 없었습니다.

어떠한 이유인지는 몰라도 간혹가다가 배포버전인데도 불구하고 BuildConfig.DEBUG값이 true로 되어있는 현상을 발견할수 있었습니다.

Release버전인데도 제가 찍어둔 로그들이 Logcat에 찍히는 현상이 간헐적으로 있었습니다.




그래서 확실하게 Debug모드인지 아닌지를 구분할수 있는 함수를 활용하고 이를 참조해서 로그를 남기거나 남기지 않는 방법을 사용하기로 했습니다.




Debug Log (일명 Dlog) 만드는 방법






1. Debug모드 여부를 알려주는 함수를 만듭니다



/**
* 현재 디버그모드여부를 리턴
*
* @param context
* @return
*/
private boolean isDebuggable(Context context) {
boolean debuggable = false;

PackageManager pm = context.getPackageManager();
try {
ApplicationInfo appinfo = pm.getApplicationInfo(context.getPackageName(), 0);
debuggable = (0 != (appinfo.flags & ApplicationInfo.FLAG_DEBUGGABLE));
} catch (NameNotFoundException e) {
/* debuggable variable will remain false */
}

return debuggable;
}









2. Application클래스의 onCreate()에서 해당 함수를 실행해서 DEBUG라는 변수에 저장을 해둡니다


// 디버그모드에 따라서 로그를 남기거나 남기지 않는다
this.DEBUG = isDebuggable(this);









3. Application의 DEBUG값에 따라서 로그를 남기거나 남기지 않도록 하는 Dlog 클래스를 만들어 줍니다.






Dlog에는 DEBUG여부에 따라 로그를 남기는 외에도 로그를 찍는 위치의 파일이름,함수이름을 기본적으로 찍어주는 기능을 포함하고 있습니다.

사용할때 단순히 '"만 찍어주더라도 어느 파일의 어느 함수에서 남겨진 코드인지 알수 있어서 매우 유용합니다.





실제 사용은 아래와 같이 하시면 됩니다.



Dlog.d("로그 내용");
Dlog.e("에러");
Dlog.i("로그 로그");





또한 Logcat에는 아래와 같이 로그가 남겨질것입니다.







간결하게 정리되어있는걸 확인해보고 싶으시다면 제 Github에 정리되어있는 Dlog소개를 확인해보셔도 좋습니다.




4 Comments
  • BlogIcon 낭군이 2015.12.14 10:59 신고 우왕 좋네요~~~
  • BlogIcon 덜구 2015.12.15 17:51 신고 좋은 정보 얻어갑니다 :ㅇ
  • BlogIcon 카카토토 2016.01.14 18:15 신고 안녕하세요 뭐좀 여쭤볼게 있어서 댓글남깁니다. 프로가드에서 옵션을 통해 릴리즈시 log를 다삭제하는 방식과 이방식의 차이점이 있나요..??
  • BlogIcon 데브킹 2016.06.09 17:59 신고 @카카토토
    늦어서 보실지는 모르겠지만 ...ㅋ
    proguard로 코드를 삭제하는 작업은 로그를 호출한 해당 코드라인 자체가 dex에서 제거됩니다.
    dex2jar등의 디컴파일 툴이나 smali 코드로 보아도 호출부 자체가 제거되었기 때문에 볼 수 없습니다.

    본문의 방법은 DEBUG라는 flag가 Runtime시 결정되기 때문에 proguard가 아무런 작업도 하지 않습니다.
    Dlog 호출부와 Dlog 내부 구현부가 dex에 그대로 남아있죠.
    (true가 될지 false가 될지 source 레벨에서는 아직 모름...)

    대안으로 BuildConfig.DEBUG 같은 gradle build injection 값을 이용한다면 final 값이기 때문에 컴파일시 false라면 제외되므로 Dlog 클래스 내부 구현부는 숨겨지겠네요.

    하지만 Final Flag를 이용한다 하더라도 Dlog 클래스를 호출한 scope의 호출 코드는 남아있습니다.
    단순히 if문을 통해 final flag를 분기처리한 Dlog 내 메서드 구현부만 컴파일에서 제외됩니다.

    본문의 방법의 단점은 Dlog.d("The quick brown fox jumps over the lazy dog") 처럼 이 클래스를 사용해서 호출한 호출부가 코드라인에 남아있다는 겁니다.
    dex2jar등의 디컴파일을 거치면 해당 호출부를 확인할 수 있습니다.
    문자열 리터럴로 전환될 수 있는 값이라면 해당 라인에서 모두 다 바로 확인 되겠죠.

    본문의 방법의 장점은 LOG_TAG없이 StackTrace를 이용해서 Calling Scope(호출 영역)를 알 수 있다는 겁니다.
    그리고 Android Studio의 환경설정의 Live Templates을 변경하면 기존에 로그를 사용하던 logd 같은 Abbreviation(약어)를 그대로 사용할 수 있습니다.

    장점은 취하고 단점은 버리면 됩니다.
    proguard assumenosideeffects 하실때 해당 클래스를 추가하면 됩니다.(본문에서라면 Dlog를 assumenosideeffects 해야겠죠.)
    Dlog클래스의 package가 com.util 이라고 가정한다면 아래와 같은 형태가 되겠네요.
    (메서드 선언부를 동일하게 맞춰줍니다. android.util.Log는 final 메서드가 아니지만 본문은 final이네요)

    -assumenosideeffects class com.util.Dlog {
    public static final void v(...);
    public static final void i(...);
    public static final void w(...);
    public static final void d(...);
    public static final void e(...);
    }
댓글쓰기 폼