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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2446248/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 05:52:57  来源:igfitidea点击:

How to remove all debug logging calls before building the release version of an Android app?

androidloggingproguardandroid-log

提问by Nicolas Raoul

According to Google, I must "deactivate any calls to Log methods in the source code" before publishing my Android app to Google Play. Extract from section 3 of the publication checklist:

根据 Google 的说法,在将我的 Android 应用程序发布到 Google Play 之前,我必须“取消对源代码中对 Log 方法的任何调用”。摘自出版物清单第 3 节:

Make sure you deactivate logging and disable the debugging option before you build your application for release. You can deactivate logging by removing calls to Log methods in your source files.

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

My open-source project is large and it is a pain to do it manually every time I release. Additionally, removing a Log line is potentially tricky, for instance:

我的开源项目很大,每次发布都手动做很痛苦。此外,删除 Log 行可能很棘手,例如:

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

If I comment the Log line, then the condition applies to the next line, and chances are load() is not called. Are such situations rare enough that I can decide it should not exist?

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

So, is there a better source code-level way to do that? Or maybe some clever ProGuard syntax to efficiently but safely remove all Log lines?

那么,有没有更好的源代码级方法来做到这一点?或者也许一些聪明的 ProGuard 语法可以有效但安全地删除所有日志行?

回答by Christopher Orr

I find a far easier solution is to forget all the ifchecks all over the place and just use ProGuardto strip out any Log.d()or Log.v()method calls when we call our Ant releasetarget.

我发现一个更简单的解决方案是忘记所有if地方的所有检查,并在我们调用 Ant目标时使用ProGuard 去除任何Log.d()Log.v()方法调用release

That way, we always have the debug info being output for regular builds and don't have to make any code changes for release builds. ProGuard can also do multiple passes over the bytecode to remove other undesired statements, empty blocks and can automatically inline short methods where appropriate.

这样,我们始终可以为常规构建输出调试信息,并且不必为发布构建进行任何代码更改。ProGuard 还可以对字节码进行多次传递以删除其他不需要的语句、空块,并且可以在适当的情况下自动内联短方法。

For example, here's a very basic ProGuard config for Android:

例如,这是一个非常基本的 Android ProGuard 配置:

-dontskipnonpubliclibraryclasses
-dontobfuscate
-forceprocessing
-optimizationpasses 5

-keep class * extends android.app.Activity
-assumenosideeffects class android.util.Log {
    public static *** d(...);
    public static *** v(...);
}

So you would save that to a file, then call ProGuard from Ant, passing in your just-compiled JAR and the Android platform JAR you're using.

因此,您可以将其保存到一个文件中,然后从 Ant 调用 ProGuard,传入您刚刚编译的 JAR 和您正在使用的 Android 平台 JAR。

See also the examplesin the ProGuard manual.

另请参阅ProGuard 手册中的示例



Update (4.5 years later):Nowadays I used Timberfor Android logging.

更新(4.5 年后):现在我使用Timber进行 Android 日志记录。

Not only is it a bit nicer than the default Logimplementation — the log tag is set automatically, and it's easy to log formatted strings and exceptions — but you can also specify different logging behaviours at runtime.

它不仅比默认Log实现好一点——日志标签是自动设置的,而且很容易记录格式化的字符串和异常——而且你还可以在运行时指定不同的日志记录行为。

In this example, logging statements will only be written to logcat in debug builds of my app:

在这个例子中,日志语句只会在我的应用程序的调试版本中写入 logcat:

Timber is set up in my ApplicationonCreate()method:

木材是在我的ApplicationonCreate()方法中设置的:

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

Then anywhere else in my code I can log easily:

然后在我的代码中的任何其他地方,我都可以轻松登录:

Timber.d("Downloading URL: %s", url);
try {
  // ...
} catch (IOException ioe) {
  Timber.e(ioe, "Bad things happened!");
}

See the Timber sample appfor a more advanced example, where all log statements are sent to logcat during development and, in production, no debug statements are logged, but errors are silently reported to Crashlytics.

有关更高级的示例,请参阅Timber 示例应用程序,其中所有日志语句在开发期间都发送到 logcat,在生产中,不记录任何调试语句,但错误会以静默方式报告给 Crashlytics。

回答by Reiner

All good answers, but when I was finished with my development I didn′t want to either use if statements around all the Log calls, nor did I want to use external tools.

所有的答案都很好,但是当我完成我的开发时,我既不想在所有 Log 调用中使用 if 语句,也不想使用外部工具。

So the solution I`m using is to replace the android.util.Log class with my own Log class:

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

public class Log {
    static final boolean LOG = BuildConfig.DEBUG;

    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);
    }
}

The only thing I had to do in all the source files was to replace the import of android.util.Log with my own class.

我在所有源文件中唯一要做的就是用我自己的类替换 android.util.Log 的导入。

回答by hackbod

I suggest having a static boolean somewhere indicating whether or not to log:

我建议在某处使用一个静态布尔值来指示是否记录:

class MyDebug {
  static final boolean LOG = true;
}

Then wherever you want to log in your code, just do this:

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

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

Now when you set MyDebug.LOG to false, the compiler will strip out all code inside such checks (since it is a static final, it knows at compile time that code is not used.)

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

For larger projects, you may want to start having booleans in individual files to be able to easily enable or disable logging there as needed. For example, these are the various logging constants we have in the window manager:

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

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;

With corresponding code like:

使用相应的代码,如:

    if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT) Log.v(
        TAG, "Adding window " + window + " at "
        + (i+1) + " of " + mWindows.size() + " (after " + pos + ")");

回答by Nicolas Raoul

Christopher's Proguard solution is the best, but if for any reason you don't like Proguard, here is a very low-tech solution:

Christopher 的 Proguard 解决方案是最好的,但如果出于任何原因您不喜欢 Proguard,这里有一个非常低技术的解决方案:

Comment logs:

评论日志:

find . -name "*\.java" | xargs grep -l 'Log\.' | xargs sed -i 's/Log\./;\/\/ Log\./g'

Uncomment logs:

取消注释日志:

find . -name "*\.java" | xargs grep -l 'Log\.' | xargs sed -i 's/;\/\/ Log\./Log\./g'

A constraint is that your logging instructions must not span over multiple lines.

一个限制是你的日志指令不能跨越多行。

(Execute these lines in a UNIX shell at the root of your project. If using Windows, get a UNIX layer or use equivalent Windows commands)

(在项目根目录下的 UNIX shell 中执行这些行。如果使用 Windows,请获取 UNIX 层或使用等效的 Windows 命令)

回答by Vincent Hiribarren

I would like to add some precisions about using Proguard with Android Studio and gradle, since I had lots of problems to remove log lines from the final binary.

我想添加一些关于将 Proguard 与 Android Studio 和 gradle 一起使用的精确性,因为我在从最终二进制文件中删除日志行时遇到了很多问题。

In order to make assumenosideeffectsin Proguard works, there is a prerequisite.

为了assumenosideeffects在 Proguard 中工作,有一个先决条件。

In your gradle file, you have to specify the usage of the proguard-android-optimize.txtas default file.

在您的 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'
    }
}

Actually, in the default proguard-android.txtfile, optimization is disabled with the two flags:

实际上,在默认proguard-android.txt文件中,使用两个标志禁用优化:

-dontoptimize
-dontpreverify

The proguard-android-optimize.txtfile does not add those lines, so now assumenosideeffectscan work.

proguard-android-optimize.txt文件没有添加这些行,所以现在assumenosideeffects可以工作了。

Then, personnally, I use SLF4J, all the more when I develop some libraries that are distributed to others. The advantage is that by default there is no output. And if the integrator wants some log outputs, he can uses Logback for Android and activate the logs, so logs can be redirected to a file or to LogCat.

然后,就个人而言,我使用SLF4J,尤其是当我开发一些分发给其他人的库时。优点是默认情况下没有输出。如果集成商想要一些日志输出,他可以使用 Logback for Android 并激活日志,因此可以将日志重定向到文件或 LogCat。

If I really need to strip the logs from the final library, I then add to my Proguard file (after having enabled the proguard-android-optimize.txtfile of course):

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

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

回答by AndroidGecko

I highly suggest using Timber from Jake Wharton

我强烈建议使用 Jake Wharton 的 Timber

https://github.com/JakeWharton/timber

https://github.com/JakeWharton/timber

it solves your issue with enabling/disabling plus adds tag class automagically

它通过启用/禁用解决您的问题,并自动添加标签类

just

只是

public class MyApp extends Application {

  public void onCreate() {
    super.onCreate();
    //Timber
    if (BuildConfig.DEBUG) {
      Timber.plant(new DebugTree());
    }
    ...

logs will only be used in your debug ver, and then use

日志只会在您的调试版本中使用,然后使用

Timber.d("lol");

or

或者

Timber.i("lol says %s","lol");

to print

打印

"Your class / msg" without specyfing the tag

“你的班级/味精”没有指定标签

回答by JosephL

I have used a LogUtilsclass like in the Google IO example application. I modified this to use an application specific DEBUG constant instead of BuildConfig.DEBUG because BuildConfig.DEBUG is unreliable. Then in my Classes I have the following.

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

回答by Zvi

I would consider using roboguice's logging facilityinstead of the built-in android.util.Log

我会考虑使用 roboguice 的日志记录工具而不是内置的 android.util.Log

Their facility automatically disables debug and verbose logs for release builds. Plus, you get some nifty features for free (e.g. customizable logging behavior, additional data for every log and more)

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

Using proguard could be quite a hassle and I wouldn't go through the trouble of configuring and making it workwith your application unless you have a good reason for that (disabling logs isn't a good one)

使用ProGuard可能是相当麻烦,我也不会通过配置并使它的麻烦去工作,你的应用程序,除非你有一个很好的理由(禁用日志不是一个好)

回答by Simon

I'm posting this solution which applies specifically for Android Studio users. I also recently discovered Timber and have imported it successfully into my app by doing the following:

我发布了这个专门适用于 Android Studio 用户的解决方案。我最近还发现了 Timber 并通过执行以下操作将其成功导入到我的应用程序中:

Put the latest version of the library into your build.gradle:

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

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

Then in Android Studios, go to Edit -> Find -> Replace in Path...

然后在 Android Studios 中,转到 Edit -> Find -> Replace in Path...

Type in Log.e(TAG,or however you have defined your Log messages into the "Text to find"textbox. Then you just replace it with Timber.e(

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

enter image description here

在此处输入图片说明

Click Find and then replace all.

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

Android Studios will now go through all your files in your project and replace all the Logs with Timbers.

Android Studios 现在将检查您项目中的所有文件,并用木材替换所有日志。

The only problem I had with this method is that gradle does come up witha million error messages afterwards because it cannot find "Timber" in the imports for each of your java files. Just click on the errors and Android Studios will automatically import "Timber" into your java. Once you have done it for all your errors files, gradle will compile again.

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

You also need to put this piece of code in your onCreatemethod of your Applicationclass:

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

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

This will result in the app logging only when you are in development mode not in production. You can also have BuildConfig.RELEASEfor logging in release mode.

这将导致仅当您处于开发模式而非生产模式时才会记录应用程序。您还可以BuildConfig.RELEASE在发布模式下登录。

回答by Richard

Per android.util.Log provides a way to enable/disable log:

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

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

Default the method isLoggable(...) returns false, only after you setprop in device likes this:

默认情况下,方法 isLoggable(...) 返回 false,只有在您在设备中 setprop 之后才像这样:

adb shell setprop log.tag.MyAppTag DEBUG

It means any log above DEBUG level can be printed out. Reference android doc:

这意味着可以打印出任何高于 DEBUG 级别的日志。参考安卓文档:

Checks to see whether or not a log for the specified tag is loggable at the specified level. The default level of any tag is set to INFO. This means that any level above and including INFO will be logged. Before you make any calls to a logging method you should check to see if your tag should be logged. You can change the default level by setting a system property: 'setprop log.tag. ' Where level is either VERBOSE, DEBUG, INFO, WARN, ERROR, ASSERT, or SUPPRESS. SUPPRESS will turn off all logging for your tag. You can also create a local.prop file that with the following in it: 'log.tag.=' and place that in /data/local.prop.

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

So we could use custom log 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);
    }
}