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

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

【问题讨论】:

标签: android


【解决方案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 配置为不调用生产中的日志。 stackoverflow.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. 上有一个很好的答案。实际上:stackoverflow.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 将从您的二进制文件中删除无法访问的日志代码。

        【讨论】:

          猜你喜欢
          • 2011-08-25
          • 1970-01-01
          • 1970-01-01
          • 2014-04-03
          • 2013-04-22
          • 2012-03-05
          • 2014-08-03
          • 1970-01-01
          • 2011-05-02
          相关资源
          最近更新 更多