发布之前删除所有debugging日志logging调用:有工具可以做到这一点吗?
根据谷歌,我必须“发布我的Android应用程序之前, 停用任何调用在源代码中的日志方法 ”。 从出版物清单第5部分摘录:
确保在构build要发布的应用程序之前停用日志logging并禁用debugging选项。 您可以通过删除对源文件中的日志方法的调用来停用日志logging。
我的开放源代码项目很大,每次发布时手动都会很痛苦。 此外,删除日志行可能会非常棘手,例如:
if(condition) Log.d(LOG_TAG, "Something"); data.load(); data.show();
如果我注释了Log行,那么这个条件适用于下一行,并且机会是load()不被调用。 这种情况是否足够罕见,我可以认定它不应该存在?
这是在正式的清单上,所以我想很多人定期做这个。
那么,是否有一个工具,可以删除所有的日志行?
最好不要被上面的代码所欺骗。
我发现一个更简单的解决scheme是忘记所有的地方if
检查,只要使用ProGuard在我们调用我们的Ant release
目标时Log.v()
任何Log.d()
或Log.v()
方法调用。
这样,我们总是有正常的构build输出debugging信息,并不需要对发布构build进行任何代码更改。 ProGuard还可以对字节码进行多次传递,以删除其他不需要的语句,空块,并可以在适当的地方自动内联简短的方法。
例如,下面是Android的一个非常基本的ProGuardconfiguration:
-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年后):现在我使用Timber进行Android日志logging。
它不仅比默认的Log
实现更好 – 日志标记是自动设置的,并且很容易logging格式化的string和exception – 但是您也可以在运行时指定不同的日志logging行为。
在这个例子中,日志语句只会写入我的应用程序的debugging版本中的logcat:
木材在我的Application
onCreate()
方法中设置:
if (BuildConfig.DEBUG) { Timber.plant(new Timber.DebugTree()); }
然后在我的代码中的其他地方我可以轻松地login:
Timber.d("Downloading URL: %s", url); try { // ... } catch (IOException ioe) { Timber.e(ioe, "Bad things happened!"); }
请参阅Timber示例应用程序以获取更高级的示例,其中所有日志语句在开发过程中都发送到logcat,并且在生产过程中不loggingdebugging语句,但将错误默认报告给Crashlytics。
所有好的答案,但是当我完成我的发展时,我不想使用如果所有的日志调用周围的声明,也不想使用外部工具。
所以我使用的解决scheme是用我自己的Log类replaceandroid.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); } }
我唯一需要做的就是用自己的类来replaceandroid.util.Log的导入。
我build议有一个静态的布尔值指示是否login:
class MyDebug { static final boolean LOG = true; }
然后,无论你想login你的代码,只要这样做:
if(MyDebug.LOG){ if(condition)Log.i(...); }
现在,当你设置MyDebug.LOG为false时,编译器会去掉这些检查中的所有代码(因为它是一个静态的final,它知道在编译时不使用代码)。
对于较大的项目,您可能希望在单个文件中启用布尔值,以便根据需要轻松启用或禁用日志logging。 例如,这些是窗口pipe理器中的各种日志常量:
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 + ")");
克里斯托弗的Proguard解决scheme是最好的,但如果因为任何原因你不喜欢Proguard,这是一个非常低技术解决scheme:
评论日志:
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'
约束是您的日志logging指令不能跨越多行。
(在您的项目的根目录下的UNIX shell中执行这些行。如果使用Windows,则获取UNIX层或使用等效的Windows命令)
我想添加一些有关在Android Studio和Gradle中使用Proguard的精度,因为我有很多问题来从最终的二进制文件中删除日志行。
为了使Proguard的作品具有一定的影响力,有一个先决条件。
在您的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并激活日志,因此可以将日志redirect到文件或LogCat。
如果我真的需要从最终的库中删除日志,然后添加到我的Proguard文件(当然启用了proguard-android-optimize.txt
文件之后):
-assumenosideeffects class * implements org.slf4j.Logger { public *** trace(...); public *** debug(...); public *** info(...); public *** warn(...); public *** error(...); }
我在Google IO示例应用程序中使用了一个LogUtils类。 我修改了这个使用应用程序特定的DEBUG常量而不是BuildConfig.DEBUG,因为BuildConfig.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"); } }
我会考虑使用roboguice的日志设施,而不是内置的android.util.Log
他们的工具自动禁用debugging和详细的日志发布版本。 另外,你可以免费获得一些漂亮的function(例如可定制的日志logging行为,每个日志的附加数据等等)
使用proguard可能会相当麻烦,我不会经历configuration和使其与您的应用程序工作的麻烦,除非你有一个很好的理由(禁用日志不是一个好的)
每个android.util.Log提供了一个启用/禁用日志的方法:
public static native boolean isLoggable(String tag, int level);
默认的方法isLoggable(…)返回false,只有当你在设备中的setprop像这样:
adb shell setprop log.tag.MyAppTag DEBUG
这意味着任何高于DEBUG级别的日志都可以打印出来。 参考android文档:
检查指定标记的日志是否可以在指定级别login。 任何标签的默认级别都设置为INFO。 这意味着任何高于INFO的级别都将被logging。 在对日志logging方法进行任何调用之前,您应该检查是否应该logging您的标记。 您可以通过设置系统属性来更改默认级别:'setprop log.tag。 'VERBOSE,DEBUG,INFO,WARN,ERROR,ASSERT或SUPPRESS等级。 SUPPRESS将会closures你的标签的所有logging。 您也可以创build一个local.prop文件,其中包含以下内容:'log.tag。=',并将其放在/data/local.prop中。
所以我们可以使用自定义日志util:
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); } }
我强烈build议使用杰克·沃顿的木材
https://github.com/JakeWharton/timber
它解决您的问题与启用/禁用加自动添加标签类
只是
public class MyApp extends Application { public void onCreate() { super.onCreate(); //Timber if (BuildConfig.DEBUG) { Timber.plant(new DebugTree()); } ...
日志将只在你的debugging版中使用,然后使用
Timber.d("lol");
要么
Timber.i("lol says %s","lol");
打印
“你的class级/味精”没有specyfing标签
我发布这个专门针对Android Studio用户的解决scheme。 我最近还发现了木材,并成功地将它导入到我的应用程序通过执行以下操作:
把最新版本的库放到你的build.gradle中:
compile 'com.jakewharton.timber:timber:4.1.1'
然后在Android Studios中,转到编辑 – >查找 – >replacepath…
inputLog.e(TAG,
或者你已经将日志消息定义到"Text to find"
文本框中,然后用Timber.e(
单击查找,然后全部replace。
Android工作室现在将浏览您项目中的所有文件,并用木材replace所有的日志。
我用这个方法唯一的问题是,Gradle之后会出现一百万个错误消息,因为它无法在每个java文件的导入中find“Timber”。 只需点击错误,Android工作室就会自动将“Timber”导入到您的Java中。 一旦你完成了所有的错误文件,gradle将会重新编译。
您还需要将这段代码放在Application
类的onCreate
方法中:
if (BuildConfig.DEBUG) { Timber.plant(new Timber.DebugTree()); }
只有当您处于开发模式而不是生产时,这将导致应用程序日志logging。 您也可以使用BuildConfig.RELEASE
login发行模式。
这是我在我的android项目上做的事情..
在Android Studio中,我们可以通过Ctrl + Shift + F从整个项目(MacOs中的Command + Shift + F)和Ctrl + Shift + Rreplace(MacOs中的Command + Shift + R)
我有一个非常简单的解决scheme。 我使用IntelliJ开发,所以细节有所不同,但是这个想法应该适用于所有的IDE。
我select我的源代码树的根,右键单击并select做“replace”。 然后我selectreplace所有的“日志”。 与“/ /日志”。 这将删除所有日志语句。 为了让他们回来,我重复同样的replace,但这次取代所有的“/ /logging”。 与“日志”。
对我来说工作很好。 只要记住将replace设置为区分大小写以避免诸如“对话框”之类的事故。 为了增加保证,你也可以用“日志”来完成第一步。 作为要search的string。
辉煌。
正如zserge的评论所言 ,
木材非常好,但如果你已经有一个现有的项目 – 你可以尝试github.com/zserge/log。 这是一个替代android.util.Log的替代品,并具有木材的大部分function,甚至更多。
他的日志库提供了简单的启用/禁用日志打印开关如下。
另外, 它只需要改变import
行, Log.d(...);
不需要改变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
通过提供对不同日志级别的支持,以及通过根据代码是在现场设备上还是在仿真器上运行来自动更改日志级别,我改进了上述解决scheme。
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); } } }
ProGuard会在你的发布版本中为你做,现在来自android.com的好消息:
http://developer.android.com/tools/help/proguard.html
ProGuard工具通过删除未使用的代码并使用语义上晦涩难懂的名称重命名类,字段和方法来缩小,优化和混淆代码。 结果是一个更小的.apk文件,更难以进行反向工程。 由于ProGuard使您的应用程序更难于进行逆向工程,因此当您的应用程序使用对安全敏感的function(如许可您的应用程序)时,请务必使用它。
ProGuard已集成到Android构build系统中,因此您不必手动调用它。 只有在发布模式下构build应用程序时,ProGuard才会运行,因此,在debugging模式下构build应用程序时,不必处理混淆代码。 ProGuard运行完全是可选的,但强烈build议。
本文档介绍如何启用和configurationProGuard,以及如何使用回溯工具来解码混淆的堆栈跟踪
将以下内容添加到您的proguard-rules.txt文件中
-assumenosideeffects class android.util.Log { public static *** d(...); public static *** w(...); public static *** v(...); public static *** i(...); }
如果你可以运行全局replace(一次),然后保留一些编码约定,你可以按照Android 框架中经常使用的模式。
而不是写作
Log.d(TAG, string1 + string2 + arg3.toString());
拥有它
if (BuildConfig.DEBUG) Log.d(TAG, string1 + String.format("%.2f", arg2) + arg3.toString());
现在,proguard可以从优化的版本DEX中删除StringBuilder及其使用的所有string和方法。 使用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
BuildConfig. DEBUG
是相当可靠的,所以你不需要额外的常量来控制剥离。
我喜欢使用Log.d(TAG,一些string,通常是一个String.format())。
TAG总是类名
转换Log.d(TAG, – > Logd(在你的课文中)
private void Logd(String str){ if (MainClass.debug) Log.d(className, str); }
用这种方法当你准备发布版本时,将MainClass.debug设置为false!
可以使用bash在linux和sed中删除日志:
find . -name "*\.java" | xargs sed -ri ':a; s%Log\.[ivdwe].*\);%;%; ta; /Log\.[ivdwe]/ !b; N; ba'
适用于多行日志。 在这个解决scheme中,您可以确定,生产代码中不存在该日志。
我知道这是一个古老的问题,但为什么你不用布尔logCallWasHere = truereplace所有的日志调用; // —这里的其他日志
这就是为什么你会知道什么时候你想把它们放回去,而不会影响你的if语句的调用:)