446

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?

Nicolas Raoul
  • 58,567
  • 58
  • 222
  • 373
  • 2
    +1 because I didn't remember this was in the publication checklist. – rds Jul 22 '11 at 16:08
  • 54
    To comment out a non-blocked line, I use ";//" instead of "//". – yingted Jan 05 '12 at 21:02
  • 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. – yingted Jan 05 '12 at 21:08
  • Possible duplicate: http://stackoverflow.com/q/2018263/2291 – Jon Adams Sep 24 '12 at 20:25
  • 2
    The link that Dimitar added does not work any more. I found this instead http://source.android.com/source/code-style.html#log-sparingly. – JosephL Mar 30 '13 at 03:00
  • this is why it's not recommended to use if statement without {}, especially when you move expression to the next line; use Sonar Luke. – Marian Paździoch Sep 26 '14 at 06:24
  • Is there any effects on the performance is the logging is on or this note is on the security side purposes only? – mboy Nov 12 '15 at 01:02
  • 1
    @mboy: Probably for performance mainly nowadays, but on old Android versions it has security benefits too. – Nicolas Raoul Nov 12 '15 at 02:46

31 Answers31

532

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.

Christopher Orr
  • 110,418
  • 27
  • 198
  • 193
  • For Android right? Is your project open-source? Could I get your Ant file somewhere? :-) Thanks a lot! – Nicolas Raoul Mar 18 '10 at 07:11
  • I added a basic config file which you can use when calling ProGuard from an Ant target. – Christopher Orr Mar 18 '10 at 09:00
  • 73
    And why isn't that in the default proguard file? – rds Jul 22 '11 at 16:08
  • 13
    +rds since it will make production stacktraces line numbers different than the ones in your code, as lines are removed. – Guy Feb 21 '12 at 11:01
  • 1
    ProGuard acts on the bytecode, not the Java source, so line numbers shouldn't be affected. But if you're optimising, line numbers are removed anyway; you need to keep then with a `-keepattributes` flag. – Christopher Orr Feb 22 '12 at 17:41
  • 6
    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. – Tony Chan Jun 15 '12 at 18:37
  • In ADT19 optimization is turned off by default and therefore -assumenosideeffects will be ignored. Guess it's the same for ADT18 and 19. – tidbeck Jul 04 '12 at 14:18
  • Can't we just edit proguard.cfg in Eclipse and be done with it (no need for Ant for us simple Eclipse build folks right?). Also what up with @tidebeck's comment that assumenosideeffects will be ignored? – Fraggle Jul 26 '12 at 21:01
  • 5
    @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. – Raanan Dec 07 '12 at 11:04
  • I added that lines in proguard-project.txt and proguard.config=proguard-project.txt to project.properties and with device connected into PC with Eclipse opened i can always retrieve Logs. Is that normal ? – androniennn Dec 23 '12 at 22:47
  • 2
    @androniennn Yes. ProGuard is only run if you do a release build of your app. – Christopher Orr Dec 24 '12 at 02:17
  • Doesn't this keep the logging in the "-keep" classes, such as those that extend `android.app.Activity` in this example? – mparaz Dec 27 '12 at 06:16
  • @mparaz No, the logging is removed. – Christopher Orr Dec 29 '12 at 00:02
  • 1
    Is it safe? It works only if in proguard configuration I turn on optimization, and - as proguard-android-optimize.txt says - some proguard's optimizations don't work with some verions of Dalvik. – user983447 Mar 06 '13 at 04:15
  • 1
    @user983447 Yes, though you should test your app thoroughly once you've applied ProGuard optimisations. – Christopher Orr Mar 06 '13 at 11:48
  • @LucasTan I asked this exact question here: http://stackoverflow.com/q/6009078/234938 – Christopher Orr Apr 22 '14 at 12:23
  • Any directions on how to set this up in Android Studio? – Rudey Apr 30 '14 at 08:18
  • 1
    This is not working for me. The logs still show up, probably due to optimization not being enabled. Can someone explain how to enable it? I have this line in my proguard-rules.txt: `-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*`, is that part of the problem? Also, is Raanan right, that you shouldn't include those in your own custom proguard file? – Micky Aug 20 '14 at 12:36
  • Ugh. I don't like that I have to call Timber.tag before every log call. Are you doing something to avoid this? Or you just don't use tags? – Lo-Tan Jan 26 '15 at 21:44
  • @Lo-Tan As I mention in the answer, Timber automatically adds tags; I've never used `Timber.tag`. – Christopher Orr Jan 27 '15 at 10:44
  • In eclipse the proguard config works as expected and none of the logs are displayed.But in Android studio the logs still appears.Is anyone else facing the same issue.? – Aditya Kamath Feb 09 '15 at 06:52
  • 1
    its not working... still showing all logs after releasing with proguard. I used default android Log class – Gopal Singh Sirvi Apr 07 '16 at 05:25
  • Though its an old post, out of curiosity, was thinking what if there is an uncaught exception in the app? Such exceptions will be logged in console, how to avoid uncaught exception being thrown to the console? – Kaps Apr 21 '16 at 11:00
  • 2
    @kaps That's unrelated to this question, but Java lets you register a handler to catch otherwise unhandled exceptions. – Christopher Orr Apr 21 '16 at 11:02
  • 4
    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? – Chitrang May 17 '16 at 06:21
  • 1
    @Chitrang If you use Timber properly (i.e. using format parameters), only one String constant will be created, and no StringBuilders. Any methods called in the parameters wouldn't be executed for builds where Timber is a no-op. If you really want to ensure that the String constants are removed from the APK, I imagine you could use ProGuard with an `assumenosideeffects` declaration for `Timber.x`. – Christopher Orr May 18 '16 at 14:02
  • @ChristopherOrr With the tree planted in the `Application` class, is there a need to `uproot` it? If so, could you guide on where the uproot should be placed? – Zhi Kai Dec 01 '16 at 06:18
  • What if we accidentally left the log statements and publish the apk? does this crash the application? – Anus Kaleem Mar 05 '17 at 10:18
  • Quick note that tripped me up-- The Jack compiler does not currently support stripping out the logs. See http://stackoverflow.com/a/37942939/3035127 – fattire Mar 08 '17 at 04:59
  • Use **BuildConfig.DEBUG** to check for Debug Build is risky, the more reliable way is use this: `boolean isDebug = ((getContext().getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0);` please check [this](https://medium.com/@elye.project/checking-debug-build-the-right-way-d12da1098120) for more detail. – gary Feb 09 '18 at 05:37
  • @gary That's irrelevant to this answer, as `BuildConfig.DEBUG` is only being called once, during `Application` creation. – Christopher Orr Feb 09 '18 at 08:07
  • @ChristopherOrr problem with `proguard` approach is , it leaves the string constants of logs in the code and it make reverse engineering and tampering with your `apk ` easier for those who have such intentions. For me it is also important to remove the strings from release code, and seems it is possible just by `if(DEBUG) {Log.d(...)}` approach – Akram Mar 11 '18 at 08:34
  • Using timber like libraries: when okhttp logs the data; it still is visible in logcat. – MMK Oct 22 '20 at 10:47
  • In the timber case, If I'm using a somewhat expensive call to be printed in debug code such as (kotlin) `Timber.d("${printDebugOutputIteratingStuff()")`, I guess timber wouldn't protect me from that call being executed anyways, isn't it? – Treviño Jan 10 '21 at 15:05
  • why dontobfuscate? shouldn't this be enabled for release? – MartianMartian Oct 21 '22 at 05:53
133

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.

Ramesh R
  • 7,009
  • 4
  • 25
  • 38
Reiner
  • 1,371
  • 1
  • 8
  • 2
  • 150
    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. – espinchi Sep 07 '11 at 17:06
  • 10
    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);` – philipp Aug 08 '12 at 19:10
  • Wouldn't ProGuard (with defaut settings) detect these blank Log.d() calls as unused code and remove them? – Pavel Alexeev Aug 21 '12 at 13:20
  • Yes Proguard does remove them. Simple check : see how dfferent the size of the APK is with and without because the strings were removed (assuming you have lots fo debugging strings) – Philippe Girolami Aug 29 '12 at 15:56
  • 2
    @espinchi Your concern seems to apply to all logging libraries like discussed in this answer http://stackoverflow.com/a/15452492/433718 (Slf4j, backlog,...). Is it not suggested to use them? – OneWorld Jun 20 '13 at 20:14
  • To answer my own comment: When using those libraries, if statements should be used also to check the log level before logging something. See also http://stackoverflow.com/questions/4958860/should-i-comment-my-log-calls-when-creating-my-final-package/4959362?noredirect=1#comment24952637_4959362 – OneWorld Jun 20 '13 at 20:59
  • This solution is good if you're not using any libraries. but if you are, you'd have to change the Log class in all of them provided they're open source and not already compiled. Basically if you're using any jar you will still see a log from it – 124697 Oct 25 '13 at 09:45
  • 1
    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](https://blogs.oracle.com/binublog/entry/debug_print_and_varargs). This apparently has another drawback: every call should be edited (not only one import line). – Stan Nov 27 '13 at 15:17
  • 22
    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. – ashishduh May 20 '14 at 19:23
  • 1
    Another advantage I see, you can plan another Tree which log into file or cache in memory, and can share these logs for debugging purpose. – Prakash Aug 04 '16 at 17:50
  • @ashishduh, better is `private static final boolean IS_DEBUGGABLE = com.name.app.BuildConfig.DEBUG`. – CoolMind May 19 '17 at 08:54
62

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 + ")");
hackbod
  • 90,665
  • 16
  • 140
  • 154
  • 1
    I would vote for such approach also. It also used in the official Google's in-app billing sample. – LA_ Apr 05 '11 at 16:56
  • 4
    Wouldn't it be less verbose to pass the condition as first parameter ? – Snicolas Aug 31 '12 at 12:36
  • 1
    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](http://stackoverflow.com/questions/2446248/remove-all-debug-logging-calls-before-publishing-are-there-tools-to-do-this?rq=1#comment8849301_4592958) 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. – OneWorld Jun 20 '13 at 20:47
  • 2
    @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. – type-a1pha Jul 21 '13 at 12:45
  • 1
    @type-a1pha you are right. I realize now that it's more optimal. Never the less, I never met a library that would match my needs exactly to log stuff on android. – Snicolas Jul 21 '13 at 16:26
  • 2
    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... – Richard Le Mesurier Dec 12 '13 at 08:19
  • I know this answer is old, but for the sake of Lint I would replace `LOG = true` with `LOG = Boolean.parseBoolean("true")` – Nick Cardoso May 11 '18 at 07:31
31

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)

Nicolas Raoul
  • 58,567
  • 58
  • 222
  • 373
20

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(...);
}
Yojimbo
  • 23,288
  • 5
  • 44
  • 48
Vincent Hiribarren
  • 5,254
  • 2
  • 41
  • 65
  • This doesn't work with the new Jack compiler-- http://stackoverflow.com/questions/37932114/android-jack-compiler-not-removing-logs-from-proguard-rules?rq=1 – fattire Mar 08 '17 at 05:02
  • 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. – Jonik Apr 28 '20 at 15:45
12

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

AndroidGecko
  • 14,034
  • 3
  • 35
  • 49
  • 2
    Timber is very nice, but if you already have an existing project - you may try https://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 Jun 09 '15 at 19:16
  • zserge, your log solution looks good. Lots of features. Have you considered adding Lint rules like Timber has? – jk7 Mar 03 '17 at 20:28
8

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");
  }
}
vman
  • 1,264
  • 11
  • 20
JosephL
  • 5,952
  • 1
  • 28
  • 25
  • +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. – Richard Le Mesurier Dec 12 '13 at 08:42
7

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(

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.

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.

Simon
  • 19,658
  • 27
  • 149
  • 217
  • 3
    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\;``` – Clark Wilson Aug 11 '16 at 15:04
  • or you can use **structual search** and replace like Chike Mgbemena shows in his [post](http://chikemgbemena.com/2016/10/01/android-studio-structural-search-and-replace/) – Maksim Turaev Nov 29 '16 at 11:54
  • @MaksimTuraev Your link is no longer relevant. Now it is a blog about hairstyles. – Vadim Kotov Feb 05 '19 at 14:42
  • Looks like post is removed =( can't find it anywhere. – Maksim Turaev Feb 05 '19 at 14:54
  • @MaksimTuraev here is a copy from Wayback machine, but the images are broken - https://web.archive.org/web/20161004161318/http://chikemgbemena.com/2016/10/01/android-studio-structural-search-and-replace/ – Vadim Kotov Feb 05 '19 at 15:30
  • Replacing with : `Timber.e(TAG + ": " +` would be a better practice. – DeveloperKurt Jun 04 '19 at 04:14
7

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.

Alex Cohn
  • 56,089
  • 9
  • 113
  • 307
7

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)

Zvi
  • 237
  • 3
  • 8
  • A very nice approach when you can't use Obfuscation....particularly because of roboguice breaking because of proguard LOL – Snicolas Aug 31 '12 at 12:36
  • 1
    Updated link for robojuice's logging facility: https://github.com/roboguice/roboguice/wiki/Logging-via-Ln – RenniePet Oct 21 '14 at 12:46
6

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);
    }
}
Richard
  • 339
  • 3
  • 10
6
  1. Go to Application->app->proguard-rules.pro enter image description here

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

  1. 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
      }
    
Manthan Patel
  • 1,784
  • 19
  • 23
4

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(...);
}
eranga
  • 519
  • 7
  • 17
3

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.

kg_sYy
  • 1,127
  • 9
  • 26
  • 2
    Please read the *"If I comment the Log line"* paragraph in my question. – Nicolas Raoul Jan 05 '15 at 01:23
  • 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. – kg_sYy Jan 05 '15 at 23:13
  • 1
    Replacing Log.d with ;//Log.d takes care of that "If" scenario too. – Jasper Jul 11 '15 at 05:47
3

enter image description here

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

Lins Louis
  • 2,393
  • 2
  • 25
  • 30
3

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
Community
  • 1
  • 1
Youngjae
  • 24,352
  • 18
  • 113
  • 198
3

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(...);
}
Nicolas Raoul
  • 58,567
  • 58
  • 222
  • 373
1

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

Max Gold
  • 11
  • 2
1

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.

Domenico
  • 1,331
  • 18
  • 22
  • 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. – Nicolas Raoul Dec 21 '20 at 01:46
  • 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. – Domenico Dec 21 '20 at 15:01
1

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


}
danwms
  • 54
  • 2
  • 1
    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. – type-a1pha Jul 21 '13 at 12:39
0

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!

user462990
  • 5,472
  • 3
  • 33
  • 35
  • 1
    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. – Lassi Kinnunen Apr 20 '16 at 16:57
0

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.

sylwano
  • 467
  • 4
  • 9
0

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

masood elsad
  • 434
  • 2
  • 6
  • 18
  • 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. – Nicolas Raoul Sep 07 '17 at 02:52
  • You can declare the variable on the top on activity and remove the boolean declaration from this line ;) – masood elsad Sep 08 '17 at 17:47
0

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.

c0dehunter
  • 6,412
  • 16
  • 77
  • 139
  • 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. – Nicolas Raoul Sep 12 '19 at 10:47
  • 2
    Another problem with this is the strings remain in the packed release. – straya Oct 10 '19 at 04:51
0

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.

Ronny Bigler
  • 654
  • 6
  • 12
0

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)
}
Zakhar Rodionov
  • 1,398
  • 16
  • 18
0

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.

Kishan Thakkar
  • 619
  • 6
  • 11
0

Here's a simple Kotlin solution that isn't Android or logging API-specific:

Set up some helper object LoggingUtils:

object LoggingUtils {
  const val DEBUG_LOGGING_ENABLED = false

  /** Wraps log lines that should be removed from the prod binary. */
  inline fun debugLog(logBlock: () -> Unit) {
    if (DEBUG_LOGGING_ENABLED) logBlock()
  }
}

then wrap lines in that method:

fun handleRequest(req: Request) {
  debugLog { logger.atFinest().log("This is a high-volume debug log! %s", request) }

  // ...

  try {
    // ...
  } catch (e: Exception) {
    logger.atSevere().withCause(e).log("I want this to appear in prod logs!")
  }
}

Since the debugLog method is marked as inline and the variable DEBUG_LOGGING_ENABLED is a constant, the log line is simply included or optimized away at compile time. No lambdas are allocated, no method calls.

It's a little cleaner and easier to refactor than wrapping each log line with if() {} statements individually, and the tempting option of making wrappers for your loggers can have significant downsides in terms of compiler and logging server optimizations, safeguards against logging user data inappropriately, etc.

Jonn
  • 1,594
  • 1
  • 14
  • 25
-1

the simplest way;

use DebugLog

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

https://github.com/MustafaFerhan/DebugLog

Mustafa Ferhan
  • 240
  • 2
  • 4
  • 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. – Glenn Maynard Mar 16 '16 at 02:56
-2

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.

Naci
  • 231
  • 3
  • 16
-3

You can try use this simple conventional method:

Ctrl+Shift+R

replace

Log.e(

With

// Log.e(
Paul Roub
  • 36,322
  • 27
  • 84
  • 93
Fthr
  • 769
  • 9
  • 10