从 Android 的生产代码中删除日志记录?

Posted

技术标签:

【中文标题】从 Android 的生产代码中删除日志记录?【英文标题】:Removing Logging from Production Code in Android? 【发布时间】:2012-05-05 11:06:26 【问题描述】:

从生产 android 应用程序中删除日志记录的最佳方法是什么。看来 proguard 并没有完全做到这一点,字符串仍然被写入,所以我想知道从生产代码中删除日志记录的最佳解决方案是什么?

【问题讨论】:

相关:***.com/questions/6009078/… 【参考方案1】:

IMO 最佳解决方案是在调用日志记录方法的任何位置编写如下代码。

if (SOME_LOG_CONSTANT) Log.d(TAG, "event:" + someSlowToEvaluateMethod());

通过更改 1 个常量,您可以删除所有不需要的日志记录。这样if (false) 后面的部分甚至不应该进入你的 .class 文件,因为编译器可以完全删除它(它是无法访问的代码)。

这也意味着如果您将整个代码块包装在 if 中,您可以从发布软件中排除整个代码块。这是连proguard都做不到的。

如果您使用 SDK Tools r17 及更高版本,SOME_LOG_CONSTANT 可以是 BuildConfig.DEBUG。该常量会根据构建类型自动为您更改。谢谢@Christopher

【讨论】:

并且您应该将其包装在一个公开相同方法的类中。这样,您只需将源代码弄乱。 DebugLog.d()、DebugLog.e()等 如果你这样做了,那么每个表达式都必须被计算,如果你这样做很痛苦,并且在代码中的任何地方都加上 if(??),那么表达式就不需要被计算了。 如果您必须走这条路,我会添加带有BuildConfig.DEBUG 的包装日志子句,使用 SDK Tools r17 及更高版本会自动为您执行此操作。这避免了必须为发布版本更改代码的笨拙。 这是个糟糕的选择!!!尝试使用 Timber 库或将您的 proguard 配置为不调用生产中的日志。 ***.com/a/2466662/3343174 @Fakher 好的,是的,可能对某些人来说。但是使用 proguard 有它的缺点,并且木材需要一个库和代码重写。为什么简单的if 是一个糟糕的选择?【参考方案2】:

使用 Timber,它是配置日志的好库: https://github.com/JakeWharton/timber

使用示例:Timber.d("Activity Created");

配置示例:

public class ExampleApp extends Application 
  @Override public void onCreate() 
   super.onCreate();

    if (BuildConfig.DEBUG) 
     Timber.plant(new DebugTree());
     else 
     Timber.plant(new CrashReportingTree());
    
  




/** A tree which logs important information for crash reporting. */
 private static class CrashReportingTree extends Timber.Tree 
   @Override protected void log(int priority, String tag, String message, Throwable t) 
     if (priority == Log.VERBOSE || priority == Log.DEBUG) 
       return;
     

     FakeCrashLibrary.log(priority, tag, message);

     if (t != null) 
       if (priority == Log.ERROR) 
         FakeCrashLibrary.logError(t);
        else if (priority == Log.WARN) 
         FakeCrashLibrary.logWarning(t);
       
     
   
 

【讨论】:

【参考方案3】:

Configuring Your Application for Release 告诉您在发布应用程序之前只需删除 Log 命令。

您可以通过删除源文件中对 Log 方法的调用来停用日志记录。

我所做的是创建一个专有的静态日志方法,该方法读取类似这样的全局布尔值:

class MyLog 

    private static final boolean LOGGING = true; //false to disable logging

    public static void d(String tag, String message) 
        if (LOGGING) 
            Log.d(tag, message);
        
    

    /* ... same for v, e, w, i */

在您想登录的任何地方使用它。

MyLog.d("Tag", "This will only work with LOGGING true");

【讨论】:

这样,您作为message 传递的所有内容都将在运行时进行评估,而不管LOGGING 的值如何。这对性能不利。 在大多数情况下,评估成本非常小。如果它开始影响性能,可以通过在调用站点添加类似的检查来解决,但这应该非常罕见。 我想知道编译器是否足够聪明,知道它永远不会被执行,只是不把它编译成字节码。【参考方案4】:

我建议你看看这个Log extension class。

它使您能够对日志进行精细控制。 例如,您可以禁用所有日志(感谢配置文件)或仅禁用某些包或类的日志。

此外,它还添加了一些有用的功能(例如,您不必为每个日志传递标签)。

【讨论】:

【参考方案5】:

只需将此方法添加到您的代码中并使用 Logd 代替 Log.d

private static final String TAG = "your own tag to recognize log entries from this app";

public static void Logd(String txt) 
    if (BuildConfig.DEBUG&&BuildConfig.BUILD_TYPE.equals("debug")) Log.d(TAG,txt);

我知道字符串仍将被评估,但我们在额外的处理时间中谈论微秒。除非您循环执行数千次日志记录,否则没人会注意到应用速度的差异。

DEBUG 和 BUILD_TYPE 可在 Android Studio 中使用。 DEBUG 布尔值是 build.gradle 文件中的“可调试”选项:

buildTypes 
    debug 
        debuggable true
        runProguard false
        proguardFile getDefaultProguardFile('proguard-android.txt')
    
    release 
        debuggable false
        runProguard true
        proguardFile getDefaultProguardFile('proguard-android.txt')
    

当您在 Android Studio 中运行时,BUILD_TYPE 会自动设置为“调试”。

因此,如果您将 debuggable 设置为 false 或用户拥有发布版本,则日志将消失。

【讨论】:

正如两年前写的答案所解释的那样-这对性能不利。这意味着必须在运行时对每个日志进行检查,而不是在编译时被剥离 性能损失是微秒!!你的警告是学术性的! @NickCardoso,你有什么建议来解决这个问题?您能否指出一些可供使用的替代代码? @W.M.只需使用带有 proguard 规则的常规日志来删除它们。我认为没有理由用另一个静态类污染堆,然后在我们已经有更好的内置解决方案时,每次调用日志时都需要将该方法调用添加到堆栈中。任何曾经使用过嵌入式或性能关键系统或紧密循环的人都知道 Martin B 的评论离题 @W.M.当然 - 在 S.O. 上有一个很好的答案。实际上:***.com/a/13327603/984830【参考方案6】:

我建议你看看 Roboguice - http://code.google.com/p/roboguice/ - 如果你使用内置的 Ln 功能,它会自动不登录已签名的 APK。它还简化了大量其他 Android 样板。您可以使用 Basedroid 看到它的实际效果 - https://github.com/achuinard/basedroid

【讨论】:

这并不完全正确:roboguice.googlecode.com/hg/roboguice/docs/apidocs/index.html "对于使用 SDK Tools r8 或更高版本构建的应用程序,这意味着任何调试版本。使用 r8 或更高版本构建的发布版本将关闭详细和调试日志消息" .... 这意味着默认情况下仍会记录警告和错误。 啊,出于某种原因,我认为 Roboguice 检查了调试与发布签名。我想这取决于 SDK 版本。【参考方案7】:

您可以使用DebugLog 类。发布应用程序时,DebugLog 会禁用所有日志。并且它为开发者提供了更易理解的 DDMS 日志。

前;

DebugLog.e("your message");

【讨论】:

【参考方案8】:

根据我的经验,最好的方法是处理这个问题,

首先在记录时检查 BuildConfig.DEBUG 标志以打印日志。

Class LogUtils

    public static void log(Class clazz, String message) 
        if (BuildConfig.DEBUG) //log only in debug mode
        Log.d(clazz.getSimpleName(), message);
        
    

然后启用 minify 到您的应用程序模块 gradle 文件,这将使 proguard 能够优化您的二进制文件

android 
    buildTypes 
        release 
            minifyEnabled true
            //shrinkResources true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            signingConfig signingConfigs.release
        
    

这将在您的调试版本中保留调试日志。 但是在您的发布版本中,if (BuildConfig.DEBUG) 条件永远不会为真,因此 proguard 将从您的二进制文件中删除无法访问的日志代码。

【讨论】:

以上是关于从 Android 的生产代码中删除日志记录?的主要内容,如果未能解决你的问题,请参考以下文章

如何将 proguard 配置为仅删除 android 日志记录调用

如何从 Java 中的生产代码中删除调试语句

如何在构建 Android 应用程序的发布版本之前删除所有调试日志记录调用?

适用于 Android 的良好日志记录技术

oracle 11g生产环境清理归档日志脚本

在android L上加载OpenCV时如何删除日志记录