Android 앱의 릴리스 버전을 빌드하기 전에 모든 디버그 로깅 호출을 제거하는 방법은 무엇입니까? 소스 코드에서 Log

Google에 따르면 Android 앱을 Google Play에 게시하기 전에 ” 소스 코드에서 Log 메소드 호출을 비활성화 “해야합니다 . 발행 체크리스트 의 섹션 3에서 추출하십시오 .

릴리스 할 응용 프로그램을 빌드하기 전에 로깅을 비활성화하고 디버깅 옵션을 비활성화하십시오. 소스 파일에서 Log 메소드에 대한 호출을 제거하여 로깅을 비활성화 할 수 있습니다.

내 오픈 소스 프로젝트는 규모가 크며 릴리스 할 때마다 수동으로 수행하기가 어렵습니다. 또한 다음과 같이 로그 줄을 제거하는 것이 까다로울 수 있습니다.

if(condition)
  Log.d(LOG_TAG, "Something");
data.load();
data.show();

Log 줄을 주석 처리하면 조건이 다음 줄에 적용되며 기회는 load ()가 호출되지 않습니다. 그러한 상황이 존재하지 않아야한다고 결정할 수있을 정도로 드문 일입니까?

그렇다면 더 나은 소스 코드 수준의 방법이 있습니까? 또는 모든 Log 행을 효율적이지만 안전하게 제거하는 영리한 ProGuard 구문입니까?



답변

나는 훨씬 쉽게 솔루션은 모든 잊지 찾을 if여기 저기 검사를 그냥 사용 ProGuard에서을 어떤 밖으로 제거하기 위해서 Log.d()또는 Log.v()우리가 우리의 개미 호출 할 때 메서드 호출을 release대상입니다.

이렇게하면 항상 일반 빌드에 대해 디버그 정보가 출력되므로 릴리스 빌드에 대해 코드를 변경할 필요가 없습니다. 또한 ProGuard는 바이트 코드를 여러 번 통과하여 원하지 않는 다른 명령문, 빈 블록을 제거하고 적절한 경우 짧은 방법을 자동으로 인라인 할 수 있습니다.

예를 들어 다음은 Android 용 기본 ProGuard 구성입니다.

-dontskipnonpubliclibraryclasses
-dontobfuscate
-forceprocessing
-optimizationpasses 5

-keep class * extends android.app.Activity
-assumenosideeffects class android.util.Log {
    public static *** d(...);
    public static *** v(...);
}

파일에 저장 한 다음 Ant에서 ProGuard를 호출하여 방금 컴파일 한 JAR과 사용중인 Android 플랫폼 JAR을 전달합니다.

ProGuard 매뉴얼 의 예제 도 참조하십시오 .


업데이트 (4.5 년 후) : 요즘에는 Android 로깅에 Timber 를 사용했습니다 .

Log로그 태그가 자동으로 설정되고 서식이 지정된 문자열과 예외를 쉽게 기록 할 수 있는 기본 구현 보다 약간 우수 할뿐만 아니라 런타임에 다른 로깅 동작을 지정할 수도 있습니다.

이 예제에서 로깅 명령문은 내 앱의 디버그 빌드에서 logcat에만 작성됩니다.

Application onCreate()방법으로 목재를 설정했습니다 .

if (BuildConfig.DEBUG) {
  Timber.plant(new Timber.DebugTree());
}

그런 다음 내 코드의 다른 곳에서 쉽게 로깅 할 수 있습니다.

Timber.d("Downloading URL: %s", url);
try {
  // ...
} catch (IOException ioe) {
  Timber.e(ioe, "Bad things happened!");
}

개발 중에 모든 로그 문이 logcat으로 전송되고 프로덕션 환경에서는 디버그 문이 기록되지 않지만 오류는 Crashlytics에 자동으로보고되는 고급 예제 는 Timber 샘플 앱 을 참조하십시오 .


답변

좋은 답변이지만 개발이 끝나면 모든 Log 호출에 if 문을 사용하거나 외부 도구를 사용하고 싶지 않았습니다.

그래서 내가 사용하는 솔루션은 android.util.Log 클래스를 내 자신의 Log 클래스로 바꾸는 것입니다.

public class Log {
    static final boolean LOG = BuildConfig.DEBUG;

    public static void i(String tag, String string) {
        if (LOG) android.util.Log.i(tag, string);
    }
    public static void e(String tag, String string) {
        if (LOG) android.util.Log.e(tag, string);
    }
    public static void d(String tag, String string) {
        if (LOG) android.util.Log.d(tag, string);
    }
    public static void v(String tag, String string) {
        if (LOG) android.util.Log.v(tag, string);
    }
    public static void w(String tag, String string) {
        if (LOG) android.util.Log.w(tag, string);
    }
}

모든 소스 파일에서해야 할 유일한 일은 android.util.Log 가져 오기를 내 클래스로 바꾸는 것입니다.


답변

로깅 여부를 나타내는 어딘가에 정적 부울을 갖는 것이 좋습니다.

MyDebug 클래스 {
  정적 최종 부울 LOG = true;
}

그런 다음 코드에 로그인하려는 위치에 다음을 수행하십시오.

if (MyDebug.LOG) {
  if (조건) Log.i (...);
}

이제 MyDebug.LOG를 false로 설정하면 컴파일러는 그러한 검사 내부의 모든 코드를 제거합니다 (정적 최종이므로 컴파일 타임에 코드가 사용되지 않음을 알고 있음).

대규모 프로젝트의 경우 필요에 따라 로깅을 쉽게 활성화 또는 비활성화 할 수 있도록 개별 파일에 부울을 갖기를 시작할 수 있습니다. 예를 들어, 창 관리자에있는 다양한 로깅 상수는 다음과 같습니다.

static final String TAG = "WindowManager";
static final boolean DEBUG = false;
static final boolean DEBUG_FOCUS = false;
static final boolean DEBUG_ANIM = false;
static final boolean DEBUG_LAYOUT = false;
static final boolean DEBUG_RESIZE = false;
static final boolean DEBUG_LAYERS = false;
static final boolean DEBUG_INPUT = false;
static final boolean DEBUG_INPUT_METHOD = false;
static final boolean DEBUG_VISIBILITY = false;
static final boolean DEBUG_WINDOW_MOVEMENT = false;
static final boolean DEBUG_ORIENTATION = false;
static final boolean DEBUG_APP_TRANSITIONS = false;
static final boolean DEBUG_STARTING_WINDOW = false;
static final boolean DEBUG_REORDER = false;
static final boolean DEBUG_WALLPAPER = false;
static final boolean SHOW_TRANSACTIONS = false;
static final boolean HIDE_STACK_CRAWLS = true;
static final boolean MEASURE_LATENCY = false;

다음과 같은 해당 코드를 사용하십시오.

    if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT) Log.v(
        TAG, "Adding window " + window + " at "
        + (i+1) + " of " + mWindows.size() + " (after " + pos + ")");

답변

Christopher의 Proguard 솔루션이 가장 좋지만 어떤 이유로 든 Proguard가 마음에 들지 않으면 매우 낮은 기술 솔루션이 있습니다.

댓글 로그 :

find . -name "*\.java" | xargs grep -l 'Log\.' | xargs sed -i 's/Log\./;\/\/ Log\./g'

주석 해제 로그 :

find . -name "*\.java" | xargs grep -l 'Log\.' | xargs sed -i 's/;\/\/ Log\./Log\./g'

제한 사항은 로깅 명령이 여러 줄에 걸쳐 있지 않아야한다는 것입니다.

(프로젝트의 루트에있는 UNIX 쉘에서이 행을 실행하십시오. Windows를 사용하는 경우 UNIX 계층을 얻거나 동등한 Windows 명령을 사용하십시오)


답변

최종 바이너리에서 로그 라인을 제거하는 데 많은 문제가 있었으므로 Android Studio 및 gradle과 함께 Proguard를 사용하는 것에 대한 정밀도를 추가하고 싶습니다.

assumenosideeffectsProguard 작품 을 만들기 위해서는 전제 조건이 있습니다.

gradle 파일에서 proguard-android-optimize.txt기본 파일 사용법을 지정해야 합니다.

buildTypes {
    release {
        minifyEnabled true
        proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'

        // With the file below, it does not work!
        //proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
    }
}

실제로 기본 proguard-android.txt파일에서 두 플래그를 사용하여 최적화가 비활성화됩니다.

-dontoptimize
-dontpreverify

proguard-android-optimize.txt파일이 그 라인을 추가하지 않습니다, 이제 assumenosideeffects작업 할 수 있습니다.

그런 다음 개인적으로 SLF4J를 사용 합니다. 다른 라이브러리에 배포되는 라이브러리를 개발할 때 더 많이 사용됩니다. 장점은 기본적으로 출력이 없다는 것입니다. 통합자가 일부 로그 출력을 원하는 경우 Android 용 Logback을 사용하고 로그를 활성화하여 로그를 파일 또는 LogCat으로 경로 재 지정할 수 있습니다.

최종 라이브러리에서 로그를 제거해야하는 경우 파일을 활성화 한 후 Proguard 파일에 추가합니다 proguard-android-optimize.txt.

-assumenosideeffects class * implements org.slf4j.Logger {
    public *** trace(...);
    public *** debug(...);
    public *** info(...);
    public *** warn(...);
    public *** error(...);
}

답변

Jake Wharton의 Timber를 사용하는 것이 좋습니다.

https://github.com/JakeWharton/timber

활성화 / 비활성화로 문제를 해결하고 자동으로 태그 클래스를 추가합니다.

다만

public class MyApp extends Application {

  public void onCreate() {
    super.onCreate();
    //Timber
    if (BuildConfig.DEBUG) {
      Timber.plant(new DebugTree());
    }
    ...

로그는 디버그 버전에서만 사용됩니다.

Timber.d("lol");

또는

Timber.i("lol says %s","lol");

인쇄

태그를 지정하지 않은 “귀하의 클래스 / msg”


답변

Google IO 예제 응용 프로그램에서와 같이 LogUtils 클래스를 사용했습니다 . BuildConfig.DEBUG가 신뢰할 수 없기 때문에 BuildConfig.DEBUG 대신 응용 프로그램 특정 DEBUG 상수를 사용하도록 이것을 수정했습니다 . 그런 다음 내 수업에는 다음이 있습니다.

import static my.app.util.LogUtils.makeLogTag;
import static my.app.util.LogUtils.LOGV;

public class MyActivity extends FragmentActivity {
  private static final String TAG = makeLogTag(MyActivity.class);

  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    LOGV(TAG, "my message");
  }
}