从 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 日志记录调用