ChatGPT解决这个技术问题 Extra ChatGPT

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. Extract from section 3 of the publication checklist:

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.

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:

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?

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?

+1 because I didn't remember this was in the publication checklist.
To comment out a non-blocked line, I use ";//" instead of "//".
If you need to be able to undo this, you will probably want to use sed 's_^\(\s*Log\.\)_;//'`date|tr -s \ -`'\1_g' instead.
The link that Dimitar added does not work any more. I found this instead source.android.com/source/code-style.html#log-sparingly.
@mboy: Probably for performance mainly nowadays, but on old Android versions it has security benefits too.

C
Christopher Orr

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.

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.

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

-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.

See also the examples in the ProGuard manual.

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

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.

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

Timber is set up in my Application onCreate() method:

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.


And why isn't that in the default proguard file?
+rds since it will make production stacktraces line numbers different than the ones in your code, as lines are removed.
I can confirm that stripping out Log calls will shift line numbers in stacktraces. It won't always be out of sync (I did several quick tests but can't exactly pinpoint what the cause is, possibly if you concatenate a string in the Log call), but sometimes it will be a few lines off. Worth the trouble IMO for the ability to easily remove Log calls.
@Fraggle From proguard-android.txt in ADT tools: "Note that if you want to enable optimization, you cannot just include optimization flags in your own project configuration file; instead you will need to point to the "proguard-android-optimize.txt" file instead of this one from your" # project.properties file.
As espinchi said in below answer. "The only problem with this approach is that, if you do Log.d("tag", "Processed: " + new ItemCounter(blabla) + " items "), even if this log message does not appear in your released version, a StringBuilder is used to create the message, which could be expensive to create. " Is this true in Timber case too?
R
Ramesh R

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.

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

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.


The only problem with this approach is that, if you do Log.d("tag", "Processed: " + new ItemCounter(blabla) + " items "), even if this log message does not appear in your released version, a StringBuilder is used to create the message, which could be expensive to create.
This solution has a big problem. espinchi mentioned just the tip of the iceberg. The problem is that when you call Log.d("tag", someValue.toString()); that it's very easy to forget to check someValue for not being null what means that it might throw a NullPointerException in production. It suggest a secure solution but it will trick you. We us a private static boolean DEBUG and then if(DEBUG)Log.d(TAG, msg);
@espinchi Your concern seems to apply to all logging libraries like discussed in this answer stackoverflow.com/a/15452492/433718 (Slf4j, backlog,...). Is it not suggested to use them?
The only way to minimize overheads mentioned in the 1-st comment by @espinchi is to change logging methods to accept varargs instead of String. Complete solution is decribed here. This apparently has another drawback: every call should be edited (not only one import line).
Just an FYI, if you're using Android Studio and gradle build system, you can use static final boolean LOG = BuildConfig.DEBUG and not have to modify this file ever.
h
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.)

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

I would vote for such approach also. It also used in the official Google's in-app billing sample.
Wouldn't it be less verbose to pass the condition as first parameter ?
This appears to be the best solution although it requires additional code on each log statement: Line numbers are preserved (weakness of ProGuard approach), No code to create log message is executed (weakness of wrapper class approach and apparently of logging library approach also). The use of this approach in Googles in app billing sample according to @LA_ supports my thoughts also.
@Snicolas How can you pass the condition as first parameter without implementing a wrapper? Moreover if you add it as parameter, then before entering the method, all the parameters needs to be evaluated that is, also the message string. The condition needs to be tested before building the parameters. The solution proposed is possibly the best one given no external tool.
Binary code wise, this is best. But coding like this just a lot of effort for a simple debug log output. Code readability drops significantly. Win some, lose some, I guess...
N
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:

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)


need a "" after the -i in Sed if running on Mac (as per this ) Thanks.
I feel that this might be what I end up using for something I'm working on because I didn't have much luck doing it with Proguard at all
And what if you have a Log after a non-bracketed while branch, as you suggested in your first post?
@type-a1pha: If you adopt this solution, then you have to consider bracket blocks as mandatory.
@NicolasRaoul The semi colon fixes this issue (// vs. ;//)
Y
Yojimbo

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.

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

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

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:

-dontoptimize
-dontpreverify

The proguard-android-optimize.txt file does not add those lines, so now assumenosideeffects can work.

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.

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):

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

This doesn't work with the new Jack compiler-- stackoverflow.com/questions/37932114/…
This helped me; both proguard-android-optimize.txt as default Proguard file and -assumenosideeffects in custom Proguard file were needed! I'm using R8 shinker (the default nowadays) and default Android logging.
A
AndroidGecko

I highly suggest using Timber from Jake Wharton

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


Timber is very nice, but if you already have an existing project - you may try 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.
zserge, your log solution looks good. Lots of features. Have you considered adding Lint rules like Timber has?
v
vman

I have used a LogUtils class 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.

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 for bug report on Build.DEBUG that I used to use. I also gave up with the various "correct" workarounds and use a similar style solution to you.
Z
Zvi

I would consider using roboguice's logging facility instead of the built-in 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 work with your application unless you have a good reason for that (disabling logs isn't a good one)


A very nice approach when you can't use Obfuscation....particularly because of roboguice breaking because of proguard LOL
Updated link for robojuice's logging facility: github.com/roboguice/roboguice/wiki/Logging-via-Ln
S
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:

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

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

Then in Android Studios, go to 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(

https://i.stack.imgur.com/h9lqi.jpg

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.

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.

You also need to put this piece of code in your onCreate method of your Application class:

    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.


Try doing the same thing for your imports, and make sure the Regular Expression box is checked Text to find: import android\.util\.Log\; Replace with: import android\.util\.Log\;\nimport timber\.log\.Timber\;
or you can use structual search and replace like Chike Mgbemena shows in his post
@MaksimTuraev Your link is no longer relevant. Now it is a blog about hairstyles.
Looks like post is removed =( can't find it anywhere.
@MaksimTuraev here is a copy from Wayback machine, but the images are broken - web.archive.org/web/20161004161318/http://chikemgbemena.com/…
A
Alex Cohn

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.

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. Use proguard-android-optimize.txt and you don't need to worry about android.util.Log in your proguard-rules.pro:

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

With Android Studio gradle plugin, BuildConfig.DEBUG is quite reliable, so you don't need extra constants to control the stripping.


R
Richard

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

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

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

adb shell setprop log.tag.MyAppTag DEBUG

It means any log above DEBUG level can be printed out. 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. 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.

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

e
eranga

Add following to your proguard-rules.txt file

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

k
kg_sYy

I have a very simple solution. I use IntelliJ for development, so the details vary but the idea should apply across all IDE's.

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." 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.


Please read the "If I comment the Log line" paragraph in my question.
OK, yes I should re-read more often after browsing the answers :). If you have such cases, you might want a different solution such as suggested before such as putting all your logs behind another interface. My suggestion perhaps works better for smaller teams and projects, where people wish to avoid overhead of extra logging libs, you know the people and code well, etc.
Replacing Log.d with ;//Log.d takes care of that "If" scenario too.
C
Community

As zserge's comment suggested,

Timber is very nice, but if you already have an existing project - you may try 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.

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(...); 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

Do you have to put that line of code in each Activity/Fragment, or just in one place?
@NoahTernullo // in derived Application file. DefaultApplication.java
L
Lins Louis

https://i.stack.imgur.com/frGV1.jpg

This is what i used to do on my android projects..

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


This seems to open work with eclipse projects. The search option is not even available on android studios.
in Android Studio you can do similar search with Ctrl+Shift+F shortcut
The example code in the question explains why this is not reliable.
It could cause problems removing any command that contains in Log. For example chocolateLog.recipie();
Unable to find this option for Android Studio 2.1. Also, I can use this trick on 1 file at a time by normal search/replace.
N
Nicolas Raoul

This is how I solve it in my Kotlin Project before going to production:

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

M
Manthan Patel

Go to Application->app->proguard-rules.pro Enter below code inside 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

In build.gradle(app) ->android do this thing 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 }


d
danwms

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


}

Same problem as the previous solution. If the string parameter is build using expensive calls, it still wastes resources. The check for calling needs to be done before building the parameters passed.
M
Max Gold

ProGuard will do it for you on your release build and now the good news from android.com:

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. The result is a smaller sized .apk file that is more difficult to reverse engineer. 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 is integrated into the Android build system, so you do not have to invoke it manually. 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. Having ProGuard run is completely optional, but highly recommended.

This document describes how to enable and configure ProGuard as well as use the retrace tool to decode obfuscated stack traces


It does not seem to remove debug logging by default, though. So Christopher's answer sounds better.
D
Domenico

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.

So, if you don't want to log anything when in release, simply implement a Logger that does nothing, like the example below:

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:

// 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.


if you do Log.d("tag", "Processed: " + new ItemCounter(blabla) + " items "), even if this log message does not appear in your released version, a StringBuilder is used to create the message, which could be expensive to create.
In a performance-critical code situation, you are right, a string concatenation could be expensive to create, expecially inside loops. In these cases i would completely remove the logging code with PorGuard or some other method. Otherwise, if we still want to avoid string concats but we do want to solve the problem programmatically, we can use an inline function block that would be called only if we are in debug configuration.
u
user462990

I like to use Log.d(TAG, some string, often a String.format ()).

TAG is always the class name

Transform Log.d(TAG, --> Logd( in the text of your class

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!


the problem with this and other solutions apart form proguard or commenting them is that you're leaving in the code, causing possibly a large amount of string builds. in an average app not a problem, but if you're trying to optimize it becomes a problem.
s
sylwano

Logs can be removed using bash in linux and sed:

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.


m
masood elsad

I know this is an old question, but why didn't you replace all your log calls with something like 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 :)


Interesting, hopefully such lines are then ignored by the compiler/optimizer. The variable name would need to be unique, though, because some methods have several log calls, and you can't declare the same variable twice.
You can declare the variable on the top on activity and remove the boolean declaration from this line ;)
c
c0dehunter

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.


An inconvenient is that it is more verbose than just writing Log.d("tag","msg");, and also it is easy to forget writing the if(BuildConfig.DEBUG) part.
Another problem with this is the strings remain in the packed release.
R
Ronny Bigler

my Way:

1) enable Column Selection Mode (alt+shift+insert)

2) select on one Log.d(TAG, "text"); the part 'Log.'

3) then do shift + ctrl + alt + j

4) click left arrow

5) do shift+end

6) hit delete.

this removes all LOG calls at once in a java file.


Z
Zakhar Rodionov

Easy with kotlin, just declare a few top level functions

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

K
Kishan Thakkar

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

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.


M
Mustafa Ferhan

the simplest way;

use DebugLog

All logs are disabled by DebugLog when the app is released.

https://github.com/MustafaFerhan/DebugLog


This is absolutely wrong. This only causes the logs to not be logged, it doesn't remove them from the code, so they're still there to help people reverse-engineer your code, and it still has the cost of formatting the strings of all those logs.
N
Naci

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. Not perfect but it got the job done for me.


P
Paul Roub

You can try use this simple conventional method:

Ctrl+Shift+R

replace

Log.e(

With

// Log.e(

That would not work well with the example code given in the question.