简体   繁体   English

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

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

According to Google, I must " deactivate any calls to Log methods in the source code " before publishing my Android app to Google Play.根据 Google 的说法,在将我的 Android 应用程序发布到 Google Play 之前,我必须“取消对源代码中对 Log 方法的任何调用”。 Extract from section 3 of the publication checklist :摘自出版物清单第 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.如果我注释 Log 行,则条件适用于下一行,并且有可能不调用 load()。 Are such situations rare enough that I can decide it should not exist?这种情况是否罕见到我可以决定它不应该存在?

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 语法来有效但安全地删除所有日志行?

I find a far easier solution is to forget all the if checks all over the place and just use ProGuard to strip out any Log.d() or Log.v() method calls when we call our Ant release target.我发现一个更简单的解决方案是忘记所有的if检查,并在我们调用 Ant release目标时使用ProGuard Log.d()任何Log.d()Log.v()方法调用。

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 examples in the ProGuard manual.另请参阅 ProGuard 手册中的示例


Update (4.5 years later): Nowadays I used Timber for Android logging.更新(4.5 年后):现在我使用Timber进行 Android 日志记录。

Not only is it a bit nicer than the default Log implementation — 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 Application onCreate() method:木材是在我的Application onCreate()方法中设置的:

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 app for 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。

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 的导入。

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 + ")");

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 命令)

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 assumenosideeffects in Proguard works, there is a prerequisite.为了使 Proguard 工作中的assumenosideeffects ,有一个先决条件。

In your gradle file, you have to specify the usage of the proguard-android-optimize.txt as 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.txt file, optimization is disabled with the two flags:实际上,在默认的proguard-android.txt文件中,优化被禁用了两个标志:

-dontoptimize
-dontpreverify

The proguard-android-optimize.txt file does not add those lines, so now assumenosideeffects can work. proguard-android-optimize.txt文件没有添加这些行,所以现在assumenosideeffects可以工作。

Then, personnally, I use SLF4J , all the more when I develop some libraries that are distributed to others.然后,就个人而言,我使用SLF4J ,尤其是当我开发一些分发给其他人的库时。 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.如果集成商想要一些日志输出,他可以使用 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.txt file of course):如果我真的需要从最终库中删除日志,我然后添加到我的 Proguard 文件中(当然在启用proguard-android-optimize.txt文件之后):

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

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 “你的班级/味精”没有指定标签

I have used a LogUtils class like in the Google IO example application.我在 Google IO 示例应用程序中使用了LogUtils类。 I modified this to use an application specific DEBUG constant instead of BuildConfig.DEBUG because BuildConfig.DEBUG is unreliable .我将其修改为使用特定于应用程序的 DEBUG 常量而不是 BuildConfig.DEBUG,因为BuildConfig.DEBUG 是不可靠的 Then in my Classes I have the following.然后在我的课程中,我有以下内容。

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

I'm posting this solution which applies specifically for Android Studio users.我发布了这个专门适用于 Android Studio 用户的解决方案。 I also recently discovered Timber and have imported it successfully into my app by doing the following:我最近还发现了 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"文本"Text to find"文本框中。然后您只需将其替换为Timber.e(

在此处输入图片说明

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.我使用这种方法的唯一问题是 gradle 之后确实会出现一百万条错误消息,因为它无法在每个 java 文件的导入中找到“Timber”。 Just click on the errors and Android Studios will automatically import "Timber" into your java.只需单击错误,Android Studios 就会自动将“Timber”导入到您的 Java 中。 Once you have done it for all your errors files, gradle will compile again.为所有错误文件完成此操作后,gradle 将再次编译。

You also need to put this piece of code in your onCreate method of your Application class:您还需要将这段代码放在Application类的onCreate方法中:

    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.RELEASE for logging in release mode.您还可以使用BuildConfig.RELEASE以在发布模式下登录。

I would consider using roboguice's logging facility instead 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 (eg 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 work with your application unless you have a good reason for that (disabling logs isn't a good one)使用ProGuard可能是相当麻烦,我也不会通过配置,使之与您的应用程序工作,除非你有一个很好的理由的麻烦去(禁用日志不是一个好)

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.这意味着可以打印出任何高于 DEBUG 级别的日志。 Reference android doc:参考安卓文档:

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.任何标签的默认级别都设置为 INFO。 This means that any level above and including INFO will be logged.这意味着任何高于并包括 INFO 的级别都将被记录。 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.您可以通过设置系统属性来更改默认级别:'setprop log.tag。 ' Where level is either VERBOSE, DEBUG, INFO, WARN, ERROR, ASSERT, or SUPPRESS. ' 其中级别为 VERBOSE、DEBUG、INFO、WARN、ERROR、ASSERT 或 SUPPRESS。 SUPPRESS will turn off all logging for your tag. SUPPRESS 将关闭您的标签的所有日志记录。 You can also create a local.prop file that with the following in it: 'log.tag.=' and place that in /data/local.prop.您还可以创建一个 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);
    }
}

If you can run a global replace (once), and after that preserve some coding convention, you can follow the pattern often used in Android framework .如果您可以运行全局替换(一次),然后保留一些编码约定,则可以遵循 Android 框架中常用的模式。

Instead of writing而不是写作

Log.d(TAG, string1 + string2 + arg3.toString());

have it as把它当作

if (BuildConfig.DEBUG) Log.d(TAG, string1 + String.format("%.2f", arg2) + arg3.toString());

Now proguard can remove the StringBuilder and all strings and methods it uses on the way, from optimized release DEX.现在 proguard 可以从优化的 DEX 版本中删除 StringBuilder 和它在途中使用的所有字符串和方法。 Use proguard-android-optimize.txt and you don't need to worry about android.util.Log in your proguard-rules.pro :使用proguard-android-optimize.txt ,您无需担心android.util.Log在您的proguard-rules.pro

android {
  …
  buildTypes {
    release {
      minifyEnabled true
      proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
    }
  }
}

With Android Studio gradle plugin, BuildConfig. DEBUG使用 Android Studio gradle 插件BuildConfig. DEBUG BuildConfig. DEBUG is quite reliable, so you don't need extra constants to control the stripping. BuildConfig. DEBUG非常可靠,因此您不需要额外的常量来控制剥离。

Add following to your proguard-rules.txt file将以下内容添加到您的proguard-rules.txt文件中

-assumenosideeffects class android.util.Log {
  public static *** d(...);
  public static *** w(...);
  public static *** v(...);
  public static *** i(...);
}

I have a very simple solution.我有一个非常简单的解决方案。 I use IntelliJ for development, so the details vary but the idea should apply across all IDE's.我使用 IntelliJ 进行开发,所以细节有所不同,但这个想法应该适用于所有 IDE。

I pick to root of my source tree, right-click and select to do "replace".我选择我的源代码树的根,右键单击并选择执行“替换”。 I then choose to replace all "Log."然后我选择替换所有“日志”。 with "//Log.".用“//日志。”。 This removes all log statements.这将删除所有日志语句。 To put them back later I repeat the same replace but this time as replace all "//Log."为了稍后将它们放回去,我重复相同的替换,但这次替换所有“//Log”。 with "Log.".与“日志。”。

Works just great for me.对我来说很好用。 Just remember to set the replace as case sensitive to avoid accidents such as "Dialog.".请记住将替换设置为区分大小写,以避免出现“对话框”等意外。 For added assurance you can also do the first step with " Log."为了增加保证,您还可以使用“日志”执行第一步。 as the string to search.作为要搜索的字符串。

Brilliant.杰出的。

在此处输入图片说明

This is what i used to do on my android projects..这就是我过去在我的 android 项目中所做的。

In Android Studio we can do similar operation by, Ctrl+Shift+F to find from whole project (Command+Shift+F in MacOs) and Ctrl+Shift+R to Replace ((Command+Shift+R in MacOs))在 Android Studio 中,我们可以通过 Ctrl+Shift+F 从整个项目中查找(MacOs 中的 Command+Shift+F)和 Ctrl+Shift+R 替换((MacOs 中的 Command+Shift+R))进行类似的操作

As zserge's comment suggested,正如zserge 的评论所建议的那样,

Timber is very nice, but if you already have an existing project - you may try github.com/zserge/log . Timber 非常好,但如果您已经有一个现有项目 - 您可以尝试 github.com/zserge/log 。 It's a drop-in replacement for android.util.Log and has most of the the features that Timber has and even more.它是 android.util.Log 的直接替代品,具有 Timber 的大部分功能,甚至更多。

his log library provides simple enable/disable log printing switch as below.他的日志库提供了简单的启用/禁用日志打印开关,如下所示。

In addition, it only requires to change import lines, and nothing needs to change for Log.d(...);此外,需要改变import线,并没有什么需要改变的Log.d(...); statement.陈述。

if (!BuildConfig.DEBUG)
    Log.usePrinter(Log.ANDROID, false); // from now on Log.d etc do nothing and is likely to be optimized with JIT

ProGuard will do it for you on your release build and now the good news from android.com: ProGuard 将在您的发布版本中为您完成,现在来自 android.com 的好消息是:

http://developer.android.com/tools/help/proguard.html http://developer.android.com/tools/help/proguard.html

The ProGuard tool shrinks, optimizes, and obfuscates your code by removing unused code and renaming classes, fields, and methods with semantically obscure names. ProGuard 工具通过删除未使用的代码并使用语义模糊的名称重命名类、字段和方法来缩小、优化和混淆您的代码。 The result is a smaller sized .apk file that is more difficult to reverse engineer.结果是较小的 .apk 文件更难逆向工程。 Because ProGuard makes your application harder to reverse engineer, it is important that you use it when your application utilizes features that are sensitive to security like when you are Licensing Your Applications.因为 ProGuard 使您的应用程序更难被逆向工程,所以当您的应用程序利用对安全性敏感的功能时(例如在您许可应用程序时),使用它很重要。

ProGuard is integrated into the Android build system, so you do not have to invoke it manually. ProGuard 已集成到 Android 构建系统中,因此您无需手动调用它。 ProGuard runs only when you build your application in release mode, so you do not have to deal with obfuscated code when you build your application in debug mode. ProGuard 仅在您在发布模式下构建应用程序时运行,因此在调试模式下构建应用程序时您不必处理混淆代码。 Having ProGuard run is completely optional, but highly recommended. ProGuard 运行是完全可选的,但强烈推荐。

This document describes how to enable and configure ProGuard as well as use the retrace tool to decode obfuscated stack traces本文档描述了如何启用和配置 ProGuard 以及如何使用回溯工具来解码混淆的堆栈跟踪

This is how I solve it in my Kotlin Project before going to production:这是我在投入生产之前在 Kotlin 项目中解决它的方法:

buildTypes {
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }

-assumenosideeffects class android.util.Log {
    public static boolean isLoggable(java.lang.String, int);
    public static int d(...);
    public static int w(...);
    public static int v(...);
    public static int i(...);
    public static int e(...);
}
  1. Go to Application->app->proguard-rules.pro转到Application->app->proguard-rules.pro 在此处输入图片说明

  2. Enter below code inside proguard-rules.pro`在 proguard-rules.pro` 中输入以下代码

    -assumenosideeffects class android.util.Log { public static *** d(...); public static *** v(...); public static *** w(...); public static *** i(...); public static *** e(...); }

# You can remove the particular debug class if you want that debug type bug in log # 如果你想要日志中的调试类型错误,你可以删除特定的调试类

  1. In build.gradle(app) ->android do this thingbuild.gradle(app) ->android做这件事

    buildTypes { debug{ debuggable false minifyEnabled true shrinkResources true proguardFiles getDefaultProguardFile('proguard-android- optimize.txt'), 'proguard-rules.pro' } release { debuggable false minifyEnabled true shrinkResources true proguardFiles getDefaultProguardFile('proguard-android- optimize.txt'), 'proguard-rules.pro' } } lintOptions { checkReleaseBuilds false // Or, if you prefer, you can continue to check for errors in release builds, // but continue the build even when errors are found: abortOnError false }

I have improved on the solution above by providing support for different log levels and by changing the log levels automatically depending on if the code is being run on a live device or on the emulator.我通过提供对不同日志级别的支持以及根据代码是在实时设备上还是在模拟器上运行来自动更改日志级别来改进上述解决方案。

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


}

I like to use Log.d(TAG, some string, often a String.format ()).我喜欢用Log.d(TAG,一些字符串,通常是一个String.format())。

TAG is always the class name TAG 始终是类名

Transform Log.d(TAG, --> Logd( in the text of your class转换 Log.d(TAG, --> Logd( 在你的课文中

private void Logd(String str){
    if (MainClass.debug) Log.d(className, str);
}

In this way when you are ready to make a release version, set MainClass.debug to false!这样当你准备制作发布版本时,将 MainClass.debug 设置为 false!

Logs can be removed using bash in linux and sed:可以在 linux 和 sed 中使用 bash 删除日志:

find . -name "*\.java" | xargs sed -ri ':a; s%Log\.[ivdwe].*\);%;%; ta; /Log\.[ivdwe]/ !b; N; ba'

Works for multiline logs.适用于多行日志。 In this solution you can be sure, that logs are not present in production code.在此解决方案中,您可以确定,生产代码中不存在日志。

I know this is an old question, but why didn't you replace all your log calls with something like Boolean logCallWasHere=true;我知道这是一个老问题,但你为什么不用类似 Boolean logCallWasHere=true; 的东西替换所有的日志调用? //---rest of your log here //--- 你的日志的其余部分在这里

This why you will know when you want to put them back, and they won't affect your if statement call :)这就是为什么您会知道何时要将它们放回原处,并且它们不会影响您的 if 语句调用 :)

Why not just do为什么不做

if(BuildConfig.DEBUG)
  Log.d("tag","msg");

? ? No additional libraries needed, no proguard rules which tend to screw up the project and java compiler will just leave out bytecode for for this call when you make release build.不需要额外的库,没有容易搞砸项目的 proguard 规则,当你进行发布构建时,java 编译器只会忽略这个调用的字节码。

Here is my solution if you don't want to mess with additional libraries or edit your code manually.如果您不想弄乱其他库或手动编辑代码,这是我的解决方案。 I created this Jupyter notebook to go over all java files and comment out all the Log messages.我创建了这个 Jupyter notebook来查看所有 java 文件并注释掉所有日志消息。 Not perfect but it got the job done for me.不完美,但它为我完成了工作。

my Way:我的方式:

1) enable Column Selection Mode (alt+shift+insert) 1)启用列选择模式(alt+shift+insert)

2) select on one Log.d(TAG, "text"); 2) 选择一个 Log.d(TAG, "text"); the part 'Log.' “日志”部分。

3) then do shift + ctrl + alt + j 3)然后做 shift + ctrl + alt + j

4) click left arrow 4)点击左箭头

5) do shift+end 5)做移位+结束

6) hit delete. 6)点击删除。

this removes all LOG calls at once in a java file.这会立即删除 java 文件中的所有 LOG 调用。

Easy with kotlin, just declare a few top level functions使用 kotlin 很容易,只需声明几个顶级函数

val isDebug: Boolean
    get() = BuildConfig.DEBUG

fun logE(tag: String, message: String) {
    if (isDebug) Log.e(tag, message)
}

fun logD(tag: String, message: String) {
    if (isDebug) Log.d(tag, message)
}

If you want to use a programmatic approach instead of using ProGuard, then by creating your own class with two instances, one for debug and one for release, you can choose what to log in either circumstances.如果您想使用编程方法而不是使用 ProGuard,那么通过创建您自己的具有两个实例的类,一个用于调试,一个用于发布,您可以选择在任一情况下登录的内容。

So, if you don't want to log anything when in release, simply implement a Logger that does nothing, like the example below:因此,如果您不想在发布时记录任何内容,只需实现一个什么都不做的 Logger,如下例所示:

import android.util.Log

sealed class Logger(defaultTag: String? = null) {
    protected val defaultTag: String = defaultTag ?: "[APP-DEBUG]"

    abstract fun log(string: String, tag: String = defaultTag)

    object LoggerDebug : Logger() {
        override fun log(string: String, tag: String) {
            Log.d(tag, string)
        }
    }

    object LoggerRelease : Logger() {
        override fun log(string: String, tag: String) {}
    }

    companion object {
        private val isDebugConfig = BuildConfig.DEBUG

        val instance: Logger by lazy {
            if(isDebugConfig)
            LoggerDebug
            else
                LoggerRelease
        }

    }
}

Then to use your logger class:然后使用您的记录器类:

class MainActivity : AppCompatActivity() {

private val logger = Logger.instance

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    logger.log("Activity launched...")
    ...
    myView.setOnClickListener {
        ...

        logger.log("My View clicked!", "View-click")
    }
}

== UPDATE == == 更新 ==

If we want to avoid string concatenations for better performances, we can add an inline function with a lambda that will be called only in debug config:如果我们想避免字符串连接以获得更好的性能,我们可以添加一个带有 lambda 的内联函数,该函数将仅在调试配置中调用:

// Add this function to the Logger class.
inline fun commit(block: Logger.() -> Unit) {
    if(this is LoggerDebug)
        block.invoke(this)
}

And then:然后:

 logger.commit {
     log("Logging without $myVar waste of resources"+ "My fancy concat")
 }

Since we are using an inline function, there are no extra object allocation and no extra virtual method calls.由于我们使用的是内联函数,因此没有额外的对象分配和额外的虚方法调用。

I have used below approach in my project我在我的项目中使用了以下方法

Created custom logger class:创建自定义记录器类:

public class LoggerData 
{
   
    public static void showLog(String type, Object object) {
        try {
            Log.d("loggerData:" + type + "-", "showLog: " + new Gson().toJson(object));
        } catch (Exception e) {
            Log.d("TAG", "showLog: " + e.getLocalizedMessage());
            Log.d("loggerData:" + type + "-", "showLog: " + object);
        }

    }

    public static void showLog(Object object) {
        
            try {
                Log.d("loggerData:" + "-", "showLog: +" + new Gson().toJson(object));
            } catch (Exception e) {
                Log.d("TAG", "showLog: " + e.getLocalizedMessage());
                Log.d("loggerData:" + "-", "showLog: " + object);
            }
        
    }
}

Then whenever required logs in code use like this way然后每当需要登录代码时就使用这种方式

  LoggerData.showLog("Refreshed token: ", token);

before building release APK, disable logs only one place in LoggerData class在构建发布 APK 之前,仅禁用 LoggerData 类中的一处日志

example例子

public class LoggerData {
    

    public static void showLog(String type, Object object) {
        try {
            //Log.d("loggerData:" + type + "-", "showLog: " + new Gson().toJson(object));
        } catch (Exception e) {
            //Log.d("TAG", "showLog: " + e.getLocalizedMessage());
            //Log.d("loggerData:" + type + "-", "showLog: " + object);
        }

    }

    public static void showLog(Object object) {
       
            try {
              //  Log.d("loggerData:" + "-", "showLog: +" + new Gson().toJson(object));
            } catch (Exception e) {
                //Log.d("TAG", "showLog: " + e.getLocalizedMessage());
                //Log.d("loggerData:" + "-", "showLog: " + object);
            }
        }
    }

Hope it will help you as well.希望它也能帮助你。

the simplest way;最简单的方法;

use DebugLog使用DebugLog

All logs are disabled by DebugLog when the app is released.当应用程序发布时,所有日志都被 DebugLog 禁用。

https://github.com/MustafaFerhan/DebugLog https://github.com/MustafaFerhan/DebugLog

You can try use this simple conventional method:您可以尝试使用这种简单的常规方法:

Ctrl + Shift + R Ctrl + Shift + R

replace代替

Log.e(

With

// Log.e(

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM