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


397

根据Google的说法,在将我的Android应用发布到Google Play之前,我必须“ 停用对源代码中Log方法的任何调用 ”。摘录于发布清单的第3节:

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

我的开源项目很大,每次发布时都很难手动进行。此外,删除日志行可能很棘手,例如:

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

如果我在Log行中注释,则该条件适用于下一行,并且可能不会调用load()。这样的情况难道不足以使我决定不应该存在吗?

那么,是否有更好的源代码级方法可以做到这一点?还是一些聪明的ProGuard语法可以有效但安全地删除所有Log行?


2
+1,因为我不记得它在发布清单中。
rds

51
为了注释掉未阻塞的行,我使用“; //”代替“ //”。
yingted 2012年

如果您需要能够撤消此操作,则可能需要使用它sed 's_^\(\s*Log\.\)_;//'`date|tr -s \ -`'\1_g'
yingted 2012年

2
Dimitar添加的链接不再起作用。我改为找到了source.android.com/source/code-style.html#log-sparingly
JosephL 2013年

1
@mboy:可能主要是在今天实现性能,但是在旧的Android版本上,它也具有安全性。
Nicolas Raoul

Answers:


488

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

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

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

-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日志记录。

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

在此示例中,日志记录语句仅在我的应用程序的调试版本中写入logcat:

用我的Application onCreate()方法设置木材:

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

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

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

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


59
为何默认Proguard文件中没有呢?
rds

10
+ rds,因为删除行后,它将使生产堆栈跟踪的行号不同于代码中的行号。
Guy

5
我可以确认剥离Log调用会移动堆栈跟踪中的行号。它不会总是不同步(我做了几次快速测试,但无法确切查明是什么原因,可能是如果您在Log调用中连接了一个字符串),但有时会有些脱节。值得麻烦的是IMO能够轻松删除Log调用。
Tony Chan

5
@Fraggle在ADT工具中的proguard-android.txt中:“请注意,如果要启用优化,则不能仅在自己的项目配置文件中包含优化标志;相反,您需要指向“ proguard-android-optimize。 txt”文件,而不是您的#project.properties文件中的该文件。
拉南2012年

3
正如espinchi在下面的回答中所述。“这种方法的唯一问题是,即使执行Log.d(“ tag”,“ Processed:” + new ItemCounter(blabla)+“ items”),即使此日志消息未出现在您的发行版中,使用StringBuilder创建消息,创建消息可能会很昂贵。“在Timber情况下也是这样吗?
奇朗

117

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

所以我正在使用的解决方案是用我自己的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);
    }
}

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


143
这种方法的唯一问题是,如果执行Log.d(“ tag”,“ Processed:” + new ItemCounter(blabla)+“ items”),即使此日志消息未出现在您的发行版本中, StringBuilder用于创建消息,创建消息可能会很昂贵。
espinchi 2011年

9
该解决方案存在很大的问题。espinchi提到的只是冰山一角。问题是,当您调用Log.d("tag", someValue.toString());它时,很容易忘记检查someValue是否不为null,这意味着它可能会NullPointerException在生产中抛出。它建议一种安全的解决方案,但会欺骗您。我们private static boolean DEBUG然后是if(DEBUG)Log.d(TAG, msg);
philipp 2012年

2
@espinchi您的担忧似乎适用于所有日志记录库,如此答案stackoverflow.com/a/15452492/433718中讨论的(Slf4j,积压等)。不建议使用它们吗?
OneWorld 2013年

1
最小化@espinchi第1条评论中提到的开销的唯一方法是更改​​日志记录方法以接受varargs而不是String此处描述了完整的解决方案。显然这还有另一个缺点:每个呼叫都应被编辑(不仅是一个导入行)。
Stan

21
仅供参考,如果您使用的是Android Studio和gradle构建系统,则可以使用static final boolean LOG = BuildConfig.DEBUG而不必修改此文件。
ashishduh 2014年

61

我建议在某处有一个静态布尔值,指示是否要记录:

类MyDebug {
  静态最终布尔值LOG = true;
}

然后,无论您要登录代码的哪里,都可以执行以下操作:

如果(MyDebug.LOG){
  如果(条件)Log.i(...);
}

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

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

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

1
我也将投票支持这种方法。它也用在Google的官方应用内结算示例中。
LA_ 2011年

4
将条件作为第一个参数传递会不会很麻烦?
尼克斯(Snicolas)

1
这似乎是最好的解决方案,尽管它在每个日志语句上都需要附加代码:保留行号(ProGuard方法的缺点),不执行用于创建日志消息的代码(包装类方法的弱点以及显然是日志库方法的弱点) 。根据@LA_在Google的应用程序计费示例中使用此方法也支持我的想法。
OneWorld 2013年

2
@Snicolas如何在不实现包装器的情况下将条件作为第一个参数传递?此外,如果将其添加为参数,则在输入方法之前,需要评估所有参数,即消息字符串。在构建参数之前,需要测试条件。在没有外部工具的情况下,提出的解决方案可能是最好的解决方案。
type-a1pha

2
明智的二进制代码,这是最好的。但是,对于简单的调试日志输出,像这样的编码就花了很多精力。代码的可读性大大下降。我猜赢了,输了一些……
理查德·勒·梅苏里尔

30

Christopher的Proguard解决方案是最好的,但是如果由于某种原因您不喜欢Proguard,这是技术含量很低的解决方案:

评论日志:

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'

一个约束是您的日志记录指令不能跨越多行。

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


1
如果在Mac上运行,则Sed中-i后需要一个“”(按照此方法)谢谢。
维沙尔(Vishal)2012年

我觉得这可能是我最终将要用于工作的内容的原因,因为与Proguard一起做运气并不算好
Joe Plante 2012年

如果您在第一篇文章中建议过,如果在非括号的while分支之后有一个Log,该怎么办?
type-a1pha

@ type-a1pha:如果采用此解决方案,则必须将方括号块视为必需项。
Nicolas Raoul

2
@NicolasRaoul半冒号解决了这个问题(//主场迎战;//
Alex Gittemeier

18

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

为了使assumenosideeffectsProguard能够正常工作,有一个先决条件。

在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并激活日志,因此可以将日志重定向到文件或LogCat。

如果确实需要从最终库中剥离日志,则可以将其添加到Proguard文件中(proguard-android-optimize.txt当然,在启用文件之后):

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

这不符合新杰克工作compiler-- stackoverflow.com/questions/37932114/...
fattire

这帮助了我;既proguard-android-optimize.txt需要使用默认的Proguard文件,-assumenosideeffects也需要使用自定义的Proguard文件!我正在使用R8 shinker(当今默认设置)和默认Android日志记录。
Jonik

10

我强烈建议您使用Jake Wharton的Timber

https://github.com/JakeWharton/timber

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

只是

public class MyApp extends Application {

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

日志将仅在调试版本中使用,然后使用

Timber.d("lol");

要么

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

打印

“您的课程/味精”,无需指定标签


2
Timber非常好,但是如果您已经有一个现有项目,则可以尝试github.com/zserge/log。它是android.util.Log的直接替代品,并具有Timber的大部分功能,甚至更多。
zserge

zserge,您的日志解决方案看起来不错。很多功能。您是否考虑过像Timber这样添加Lint规则?
jk7'3

8

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

+1 Build.DEBUG我过去使用过的错误报告。我也放弃了各种“正确”的解决方法,并使用了类似的样式解决方案。
理查德·勒·马苏里尔

7

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

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

使用proguard可能会很麻烦,除非您有充分的理由(禁用日志不是一个好方法),否则我不会经历配置和使其与您的应用程序一起工作的麻烦。


当您无法使用混淆功能时,这是一个非常不错的方法。...尤其是由于proguard LOL导致机器人滑盖损坏
Snicolas 2012年

1
robojuice的日志记录工具的更新链接:github.com/roboguice/roboguice/wiki/Logging-via-Ln
RenniePet 2014年

7

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

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

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

然后在Android Studio中,转到编辑->查找->在路径中替换...

输入Log.e(TAG,,但是您已经在"Text to find"文本框中定义了日志消息。然后,您只需将其替换为Timber.e(

在此处输入图片说明

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

Android Studios现在将遍历项目中的所有文件,并用Timbers替换所有Logs。

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

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

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

仅当您处于开发模式而不是生产环境时,这将导致应用程序日志记录。您还可以BuildConfig.RELEASE在发布模式下进行登录。


3
尝试对您的导入执行相同的操作,并确保选中“正则表达式”框要查找的文本:import android\.util\.Log\;替换为:import android\.util\.Log\;\nimport timber\.log\.Timber\;
Clark Wilson

或者您可以使用结构搜索并替换像Chike Mgbemena在其帖子中
Maksim Turaev,

@MaksimTuraev您的链接不再相关。现在,这是一个有关发型的博客。
瓦迪姆·科托夫

貌似后取出=(不能在任何地方找到它。
马克西姆Turaev

@MaksimTuraev这里是从Wayback机器的副本,但图像被打破- web.archive.org/web/20161004161318/http://chikemgbemena.com/...
瓦迪姆·科托夫

6

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

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

默认情况下,仅在设备中设置了setprop之后,方法isLoggable(...)才会返回false:

adb shell setprop log.tag.MyAppTag DEBUG

这意味着可以打印出DEBUG级别以上的任何日志。参考android doc:

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

因此,我们可以使用自定义日志工具:

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

6

如果您可以运行一次全局替换,然后保留一些编码约定,则可以遵循Android 框架中经常使用的模式。

而不是写作

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

有它

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

现在,proguard可以从优化的发行版DEX中删除StringBuilder及其使用的所有字符串和方法。使用proguard-android-optimize.txt,您无需担心android.util.Log登录您的proguard-rules.pro

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

有了Android Studio gradle插件,它是相当可靠的,因此您不需要额外的常量来控制剥离。BuildConfig.DEBUG


4

在此处输入图片说明

这就是我以前在Android项目上所做的。

在Android Studio中,我们可以通过以下操作执行类似操作:Ctrl + Shift + F从整个项目中查找(在MacOs中为Command + Shift + F)和Ctrl + Shift + R进行替换(在MacOs中为Command + Shift + R))


这似乎打开了Eclipse项目的工作。搜索选项甚至在android studios上都不可用。
西蒙(Simon)

2
Android Studio中你可以用CTRL + SHIFT +类似的搜索˚F快捷方式
林斯路易

问题中的示例代码说明了为什么这不可靠。
Nicolas Raoul

删除包含在日志中的任何命令可能会导致问题。例如ChocolateLog.recipie();
安德鲁·S

找不到适用于Android Studio 2.1的选项。另外,通过正常的搜索/替换,我可以一次在1个文件上使用此技巧。
VVB

3

我有一个非常简单的解决方案。我使用IntelliJ进行开发,因此细节有所不同,但该想法应适用于所有IDE。

我选择源树的根,右键单击并选择“替换”。然后,我选择替换所有“日志”。用“ // Log。”。这将删除所有日志语句。为了稍后再放回去,我重复相同的替换,但是这次替换所有的“ // Log”。与“日志”。

对我来说很棒。只需记住将替换设置为区分大小写,以避免发生诸如“对话框”之类的事故。为了进一步保证,您还可以使用“日志”进行第一步。作为要搜索的字符串。

辉煌。


2
请阅读我的问题中的“如果我评论日志行”段落。
Nicolas Raoul 2015年

好的,是的,在浏览答案后,我应该重新阅读更多:)。如果遇到这种情况,则可能需要其他建议的解决方案,例如将所有日志放在另一个接口之后。我的建议也许对较小的团队和项目,人们希望避免开销的额外的日志记录库更好,你认识的人与代码很好,等等
kg_sYy

1
用; // Log.d替换Log.d也可以解决“ If”情况。
贾斯珀

3

正如zserge的评论所建议的那样,

Timber非常好,但是如果您已经有一个现有项目,则可以尝试github.com/zserge/log。它是android.util.Log的直接替代品,并具有Timber的大部分功能,甚至更多。

他的日志库提供了以下简单的启用/禁用日志打印开关。

此外,它只需要更改import行,而无需更改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

您是否必须将这行代码放在每个“活动/片段”中,或放在一个地方?
诺亚·特努罗

@NoahTernullo //在派生的应用程序文件中。DefaultApplication.java
Youngjae 2016年

3

将以下内容添加到您的proguard-rules.txt文件中

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

1

通过提供对不同日志级别的支持以及根据代码是在实时设备上还是在仿真器上运行而自动更改日志级别,我对上述解决方案进行了改进。

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


}

1
与以前的解决方案相同的问题。如果string参数是使用昂贵的调用构建的,则仍会浪费资源。需要在构建传递的参数之前完成对调用的检查。
type-a1pha

1

ProGuard会在您的发行版本中为您做到这一点,现在,您将从android.com获得好消息:

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

ProGuard工具通过删除未使用的代码并使用语义上晦涩的名称重命名类,字段和方法来收缩,优化和混淆您的代码。结果是一个较小的.apk文件,很难进行反向工程。由于ProGuard使您的应用程序更难进行逆向工程,因此,当您的应用程序使用对安全敏感的功能时(例如在为您的应用程序授予许可时),请务必使用它。

ProGuard已集成到Android构建系统中,因此您无需手动调用它。ProGuard仅在以发布模式构建应用程序时运行,因此在以调试模式构建应用程序时,您不必处理混淆的代码。运行ProGuard完全是可选的,但强烈建议这样做。

本文档介绍了如何启用和配置ProGuard以及如何使用retrace工具解码混淆的堆栈跟踪


2
不过,它似乎并未默认删除调试日志记录。因此,克里斯托弗的答案听起来更好。
Nicolas Raoul

0

我喜欢使用Log.d(TAG,一些字符串,通常是String.format())。

TAG始终是班级名称

在类的文本中转换Log.d(TAG,-> Logd(

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

这样,当您准备制作发行版本时,请将MainClass.debug设置为false!


1
此解决方案和其他解决方案的问题(除了保护者或没有评论)是您留在代码中,可能导致大量的字符串生成。在一个普通的应用程序中没有问题,但是如果您要优化它就成为一个问题。
Lassi Kinnunen

0

可以在Linux和sed中使用bash删除日志:

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

适用于多行日志。在此解决方案中,您可以确定生产代码中不存在日志。


0

我知道这是一个老问题,但是为什么不使用Boolean logCallWasHere = true之类的东西替换所有日志调用;//-其余的记录在这里

这就是为什么您会知道何时要将它们放回原处的原因,它们不会影响您的if语句调用:)


有趣的是,希望这样的行可以被编译器/优化器忽略。但是,变量名必须是唯一的,因为某些方法有多个日志调用,并且您不能两次声明相同的变量。
Nicolas Raoul

您可以在活动的顶部声明该变量,然后从此行中删除布尔声明;)
masood elsad

0

为什么不做

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

?不需要额外的库,没有倾向于使项目搞砸的proguard规则,并且在您进行发行版本构建时,java编译器只会为此调用忽略字节码。


一个不便之处在于,它不仅比编写Log.d("tag","msg");还要冗长,而且很容易忘记编写if(BuildConfig.DEBUG)零件。
Nicolas Raoul

1
另一个问题是字符串保留在打包的发行版中。
straya

0

如果您不想弄乱其他库或手动编辑代码,这是我的解决方案。我创建了这个Jupyter笔记本,以遍历所有Java文件并注释掉所有Log消息。虽然不完美,但为我完成了工作。


0

我的方式:

1)启用列选择模式(alt + shift + insert)

2)在一个Log.d(TAG,“ text”)上选择;“日志”部分。

3)然后执行shift + Ctrl + Alt + J

4)点击向左箭头

5)做移位+结束

6)点击删除。

这会立即删除Java文件中的所有LOG调用。


0

您可以尝试使用这种简单的常规方法:

Ctrl+ Shift+R

更换

Log.e(

// Log.e(

这与问题中给出的示例代码不能很好地配合。
Nicolas Raoul

0

使用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)
}

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.