首页 文章

如何在将应用程序发布到Google Play之前删除所有调试日志记录调用? [关闭]

提问于
浏览
345

根据谷歌的说法,我必须在发布我的Android应用程序之前“停用对源代码中任何Log方法的调用” . 摘自publication checklist的第5部分:

在构建应用程序以进行发布之前,请确保停用日志记录并禁用调试选项 . 您可以通过删除源文件中对Log方法的调用来停用日志记录 .

我的开源项目很大,每次发布时手动执行都很痛苦 . 此外,删除日志行可能很棘手,例如:

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

如果我注释Log行,那么条件适用于下一行,并且很可能没有调用load() . 这种情况是否足够罕见,我可以决定它不应该存在?

这是在官方清单上,所以我想很多人会定期这样做 .
那么,如何有效但安全地删除所有日志行?

21 回答

  • 2

    我喜欢使用Log.d(TAG,一些字符串,通常是String.format()) .

    TAG始终是 class 名称

    转换Log.d(TAG, - > Logd(在您的课程文本中)

    private void Logd(String str){
        if (MainClass.debug) Log.d(className, str);
    }
    

    这样,当您准备好发布版本时,将MainClass.debug设置为false!

  • 2

    我会考虑使用roboguice的logging facility而不是内置的android.util.Log

    他们的工具会自动禁用发布版本的调试和详细日志 . 此外,您还可以免费获得一些漂亮的功能(例如,可自定义的日志记录行为,每个日志的附加数据等)

    使用proguard可能会非常麻烦,除非你有充分的理由(禁用日志不是很好),否则我不会遇到使用你的应用程序进行配置和制作 work 的麻烦 .

  • 1

    我有一个非常简单的解决方案 . 我使用IntelliJ进行开发,因此细节会有所不同,但这个想法应该适用于所有IDE .

    我选择了源树的根,右键单击并选择“替换” . 然后我选择替换所有“日志” . 用“// Log . ” . 这将删除所有日志语句 . 为了稍后再重复,我重复相同的替换,但这次更换所有“// Log” . 用“Log . ” .

    对我来说非常棒 . 只需记住将替换设置为区分大小写以避免诸如“Dialog”之类的事故 . 为了进一步保证,您还可以使用“日志”执行第一步 . 作为要搜索的字符串 .

    辉煌 .

  • -1

    我发现一个更容易的解决方案是忘记所有 if 检查,只需使用ProGuard去掉我们调用Ant release 目标时的任何 Log.d()Log.v() 方法调用 .

    这样,我们总是为常规构建输出调试信息,而不必为发布版本进行任何代码更改 . 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手册中的the examples .


    Update (4.5 years later): 如今我使用Timber进行Android日志记录 .

    它不仅比默认的 Log 实现更好 - 日志标记是自动设置的,并且很容易记录格式化的字符串和异常 - 但您也可以在运行时指定不同的日志记录行为 .

    在此示例中,日志记录语句将仅在我的应用程序的调试版本中写入logcat:

    Timber在我的 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!");
    }
    

    有关更高级的示例,请参阅Timber sample app,其中所有日志语句在开发期间发送到logcat,在 生产环境 中,不会记录任何调试语句,但会向Crashlytics静默报告错误 .

  • 2

    我在Google IO示例应用程序中使用了LogUtils类 . 我将其修改为使用特定于应用程序的DEBUG常量而不是BuildConfig.DEBUG,因为BuildConfig.DEBUG is unreliable . 然后在我的课程中,我有以下内容 .

    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");
      }
    }
    
  • 6

    enter image description here

    这是我以前在我的android项目上做的事情..

    在Android Studio中,我们可以通过Ctrl Shift F从整个项目(MacOs中的Command Shift F)和Ctrl Shift R替换((MacO中的命令Shift R))进行类似操作

  • 4

    如果你可以运行全局替换(一次),之后保留一些编码约定,你可以遵循Android framework中经常使用的模式 .

    而不是写作

    Log.d(TAG, string1 + string2 + arg3.toString());
    

    有它作为

    if (BuildConfig.DEBUG) Log.d(TAG, string1 + String.format("%.2f", arg2) + arg3.toString());
    

    现在,proguard可以从优化的版本DEX中删除StringBuilder以及它在途中使用的所有字符串和方法 . 使用 proguard-android-optimize.txt ,您无需担心 proguard-rules.pro 中的android.util.Log:

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

    使用Android Studio gradle插件, BuildConfig.DEBUG 非常可靠,因此您无需额外的常量来控制剥离 .

  • 1

    所有好的答案,但是当我完成我的开发时,我不想使用围绕所有Log调用的if语句,也不想使用外部工具 .

    所以我使用的解决方案是用我自己的Log类替换android.util.Log类:

    public class Log {
        static final boolean LOG = false;
    
        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与我自己的类 .

  • 15

    我建议有一个静态布尔值,指示是否记录:

    class MyDebug {
      static final boolean LOG = true;
    }
    

    然后,无论您想要在哪里登录代码,只需执行以下操作:

    if (MyDebug.LOG) {
      if (condition) Log.i(...);
    }
    

    现在,当您将MyDebug.LOG设置为false时,编译器将删除此类检查中的所有代码(因为它是静态final,它在编译时知道不使用代码 . )

    对于较大的项目,您可能希望在单个文件中开始使用布尔值,以便能够根据需要轻松启用或禁用日志记录 . 例如,这些是我们在窗口管理器中具有的各种日志记录常量:

    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 + ")");
    
  • 30

    我知道这是一个老问题,但为什么不用布尔logCallWasHere = true替换所有的日志调用; // ---你的其余日志在这里

    这就是为什么你会知道什么时候你想把它们放回去,它们不会影响你的if语句调用:)

  • 440

    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 shell中执行这些行 . 如果使用Windows,获取UNIX层或使用等效的Windows命令)

  • 57

    我发布的此解决方案专门适用于Android Studio用户 . 我最近还发现了Timber并通过以下方式将其成功导入我的应用程序:

    将最新版本的库放入build.gradle:

    compile 'com.jakewharton.timber:timber:4.1.1'
    

    然后在Android工作室中,转到编辑 - >查找 - >替换路径...

    输入 Log.e(TAG, 或者您已将日志消息定义到 "Text to find" 文本框中 . 然后你只需用 Timber.e( 替换它

    enter image description here

    单击查找,然后单击全部替换 .

    Android工作室现在将浏览项目中的所有文件,并将所有日志替换为Timbers .

    我使用这种方法的唯一问题是gradle后来出现了数百万条错误消息,因为它无法在每个java文件的导入中找到“Timber” . 只需点击错误,Android Studios就会自动将“Timber”导入您的java . 完成所有错误文件后,gradle将再次编译 .

    您还需要将这段代码放在 Application 类的 onCreate 方法中:

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

    这将导致应用程序仅在您处于开发模式而非 生产环境 模式时进行日志记录 . 您还可以使用 BuildConfig.RELEASE 登录发布模式 .

  • 0

    最简单的方法;

    使用 DebugLog

    应用程序发布时,DebugLog会禁用所有日志 .

    https://github.com/MustafaFerhan/DebugLog

  • 3

    正如zserge's comment建议的那样,

    Timber非常好,但是如果你已经有一个现有的项目 - 你可以试试github.com/zserge/log . 它是android.util.Log的直接替代品,具有Timber的大部分功能,甚至更多 .

    his log library提供简单的启用/禁用日志打印开关,如下所示 .

    此外,它只需要更改 import 行,并且 Log.d(...); 语句不需要更改 .

    if (!BuildConfig.DEBUG)
        Log.usePrinter(Log.ANDROID, false); // from now on Log.d etc do nothing and is likely to be optimized with JIT
    
  • 108

    我想添加一些关于在Android Studio和gradle中使用Proguard的精度,因为我从最终的二进制文件中删除日志行有很多问题 .

    为了在Proguard作品中制作 assumenosideeffects ,有一个先决条件 .

    在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 . 优点是默认情况下没有输出 . 如果集成商需要一些日志输出,他可以使用Logback for Android并激活日志,因此可以将日志重定向到文件或LogCat .

    如果我真的需要从最终库中删除日志,那么我会添加到我的Proguard文件中(当然在启用了 proguard-android-optimize.txt 文件之后):

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

    将以下内容添加到 proguard-rules.txt 文件中

    -assumenosideeffects class android.util.Log {
      public static *** d(...);
      public static *** w(...);
      public static *** v(...);
      public static *** i(...);
    }
    
  • 0

    ProGuard将在您的发布版本上为您完成,现在来自android.com的好消息:

    http://developer.android.com/tools/help/proguard.html

    ProGuard工具通过删除未使用的代码并使用语义模糊的名称重命名类,字段和方法来缩小,优化和混淆代码 . 结果是较小的.apk文件,更难以进行逆向工程 . 由于ProGuard使您的应用程序更难以进行逆向工程,因此当您的应用程序使用对安全性敏感的功能时(如您正在许可您的应用程序 .

    ProGuard已集成到Android构建系统中,因此您无需手动调用它 . 只有在发布模式下构建应用程序时,ProGuard才会运行,因此在调试模式下构建应用程序时,不必处理混淆代码 . ProGuard运行是完全可选的,但强烈建议 .

    本文档介绍了如何启用和配置ProGuard,以及如何使用回扫工具解码混淆的堆栈跟踪

  • 0

    可以使用linux和sed中的bash删除日志:

    find . -name "*\.java" | xargs sed -ri ':a; s%Log\.[ivdwe].*\);%;%; ta; /Log\.[ivdwe]/ !b; N; ba'
    

    适用于多行日志 . 在此解决方案中,您可以确定 生产环境 代码中不存在日志 .

  • 7

    我强烈建议使用杰克沃顿的木材

    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”没有指定标签

  • 7

    每个android.util.Log提供了一种启用/禁用日志的方法:

    public static native boolean isLoggable(String tag, int level);
    

    默认方法isLoggable(...)返回false,只有在设备中的setprop之后才会这样:

    adb shell setprop log.tag.MyAppTag DEBUG
    

    这意味着可以打印任何DEBUG级别以上的日志 . 参考android doc:

    检查指定标记的日志是否可以在指定级别进行记录 . 任何标记的默认级别都设置为INFO . 这意味着将记录任何级别以上且包括INFO . 在对日志记录方法进行任何调用之前,应检查是否应记录您的标记 . 您可以通过设置系统属性来更改默认级别:'setprop log.tag . '级别是VERBOSE,DEBUG,INFO,WARN,ERROR,ASSERT或SUPPRESS . SUPPRESS将关闭标签的所有日志记录 . 您还可以创建一个local.prop文件,其中包含以下内容:'log.tag . ='并将其放在/data/local.prop中 .

    所以我们可以使用自定义日志工具:

    public final class Dlog 
    {
        public static void v(String tag, String msg)
        {
            if (Log.isLoggable(tag, Log.VERBOSE))
                Log.v(tag, msg);
        }
    
        public static void d(String tag, String msg)
        {
            if (Log.isLoggable(tag, Log.DEBUG))
                Log.d(tag, msg);
        }
    
        public static void i(String tag, String msg)
        {
            if (Log.isLoggable(tag, Log.INFO))
                Log.i(tag, msg);
        }
    
        public static void w(String tag, String msg)
        {
            if (Log.isLoggable(tag, Log.WARN))
                Log.w(tag, msg);
        }
    
        public static void e(String tag, String msg)
        {
            if (Log.isLoggable(tag, Log.ERROR))
                Log.e(tag, msg);
        }
    }
    
  • 8

    我通过提供对不同日志级别的支持以及根据代码是在实时设备上运行还是在模拟器上自动更改日志级别来改进上述解决方案 .

    public class Log {
    
    final static int WARN = 1;
    final static int INFO = 2;
    final static int DEBUG = 3;
    final static int VERB = 4;
    
    static int LOG_LEVEL;
    
    static
    {
        if ("google_sdk".equals(Build.PRODUCT) || "sdk".equals(Build.PRODUCT)) {
            LOG_LEVEL = VERB;
        } else {
            LOG_LEVEL = INFO;
        }
    
    }
    
    
    /**
     *Error
     */
    public static void e(String tag, String string)
    {
            android.util.Log.e(tag, string);
    }
    
    /**
     * Warn
     */
    public static void w(String tag, String string)
    {
            android.util.Log.w(tag, string);
    }
    
    /**
     * Info
     */
    public static void i(String tag, String string)
    {
        if(LOG_LEVEL >= INFO)
        {
            android.util.Log.i(tag, string);
        }
    }
    
    /**
     * Debug
     */
    public static void d(String tag, String string)
    {
        if(LOG_LEVEL >= DEBUG)
        {
            android.util.Log.d(tag, string);
        }
    }
    
    /**
     * Verbose
     */
    public static void v(String tag, String string)
    {
        if(LOG_LEVEL >= VERB)
        {
            android.util.Log.v(tag, string);
        }
    }
    
    
    }
    

相关问题