25

What is the best way to remove logging from production android application. It appears that proguard does not do this completely, strings still get written, so I was wondering what is best solution to remove the logging from production code?

Code Droid
  • 10,344
  • 17
  • 72
  • 112

8 Answers8

25

The IMO best solution is to write code like below wherever you call your logging methods.

if (SOME_LOG_CONSTANT) Log.d(TAG, "event:" + someSlowToEvaluateMethod());

By changing 1 constant you strip away every logging that you don't want. That way the part behind if (false) should not even get into your .class file since the compiler can completely remove it (it is unreachable code).

This also means that you can exclude whole code blocks from your release software if you wrap them in that if. That's something even proguard can't do.

the SOME_LOG_CONSTANT can be BuildConfig.DEBUG if you use SDK Tools r17 and above. That constant is automatically changed for you depending on build type. Thx @Christopher

zapl
  • 63,179
  • 10
  • 123
  • 154
  • AND you should wrap this in a class that exposes the same methods. that way, you just jumble up your sourcecode. DebugLog.d(), DebugLog.e(), etc. – William Melani Apr 24 '12 at 19:02
  • 1
    if you do that then every expression has to be evaluated, if you do it the painful way and prepend the `if(??)` everywhere in your code then expressions don't need to be evaluated. – zapl Apr 24 '12 at 19:06
  • 4
    If you must go this route, I'd add that wrapping log clauses with `BuildConfig.DEBUG` does this for you automatically with SDK Tools r17 and above. This avoids the clumsiness of having to alter code for release builds. – Christopher Orr Apr 24 '12 at 19:43
  • It's a bad choice !!! Try to use Timber library or configure your proguard to not calling Logs in Production. https://stackoverflow.com/a/2466662/3343174 – Fakher Sep 15 '17 at 12:57
  • 1
    @Fakher Ok, yes, probably for some. But using proguard has it's downsides and timber requires a library and code rewrite. Why exactly is a simple `if` a bad choice? – zapl Sep 15 '17 at 13:27
  • PERFORMANCE !! if you have 1K log in your app it's 1K conditions !! – Fakher Sep 15 '17 at 21:55
9

Use Timber, it's a nice library to configure your logs: https://github.com/JakeWharton/timber

Example of use: Timber.d("Activity Created");

Example of configuration:

public class ExampleApp extends Application {
  @Override public void onCreate() {
   super.onCreate();

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




/** A tree which logs important information for crash reporting. */
 private static class CrashReportingTree extends Timber.Tree {
   @Override protected void log(int priority, String tag, String message, Throwable t) {
     if (priority == Log.VERBOSE || priority == Log.DEBUG) {
       return;
     }

     FakeCrashLibrary.log(priority, tag, message);

     if (t != null) {
       if (priority == Log.ERROR) {
         FakeCrashLibrary.logError(t);
       } else if (priority == Log.WARN) {
         FakeCrashLibrary.logWarning(t);
       }
     }
   }
 }
Yair Kukielka
  • 10,686
  • 1
  • 38
  • 46
6

Configuring Your Application for Release tells you to simply remove Log commands before releasing your application.

You can deactivate logging by removing calls to Log methods in your source files.

What I do is to create a proprietary static log method that reads a global boolean of something like this:

class MyLog {

    private static final boolean LOGGING = true; //false to disable logging

    public static void d(String tag, String message) {
        if (LOGGING) {
            Log.d(tag, message);
        }
    }

    /* ... same for v, e, w, i */
}

Use this everywhere you want to log.

MyLog.d("Tag", "This will only work with LOGGING true");
Tyler
  • 19,113
  • 19
  • 94
  • 151
  • 5
    This way, everything you pass as `message` will be evaluated at runtime, regardless of the value of `LOGGING`. This is bad for performance. – Joffrey Mar 07 '14 at 12:47
  • In most cases that evaluation cost is very small. If it starts to impact performance it can be addressed by adding a similar check at the call site, but that should be very rare. – Mark McKenna Jul 15 '15 at 18:17
  • 2
    im wondering if the compiler is smart enough to know it will never be executed and just simply not compile that into bytecode. – Tyler Jul 15 '15 at 19:31
2

The best way is to handle this in from my experience,

  • firstly while logging check for BuildConfig.DEBUG flag to print log.

    Class LogUtils{
    
        public static void log(Class clazz, String message) {
            if (BuildConfig.DEBUG) {//log only in debug mode
            Log.d(clazz.getSimpleName(), message);
            }
        }
    }
    
  • then enable minify to your app module gradle file, this will enable proguard to optimize your binary

    android {
        buildTypes {
            release {
                minifyEnabled true
                //shrinkResources true
                proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
                signingConfig signingConfigs.release
            }
        }
    }
    

this will keep debug logs in your debug builds. But in your release builds the if (BuildConfig.DEBUG) condition is never true, hence proguard will strip off the unreachable log code from your binary.

ir2pid
  • 5,604
  • 12
  • 63
  • 107
1

I suggest you to see this Log extension class.

It enables you to have a fine control on logs. For example, you can disable all logs (thanks to a configuration file) or just the logs of some packages or classes.

Moreover, it adds some useful functionalities (for instance you don't have to pass a tag for each log).

Donkey
  • 1,176
  • 11
  • 19
1

Simply add this method to your code and use Logd instead of Log.d

private static final String TAG = "your own tag to recognize log entries from this app";

public static void Logd(String txt) {
    if (BuildConfig.DEBUG&&BuildConfig.BUILD_TYPE.equals("debug")) Log.d(TAG,txt);
}

I know that the string will still be evaluated, but we are talking microseconds in extra processing time. Noone will ever notice a difference in app speed, unless you are doing many thousands of loggings in a loop.

The DEBUG and BUILD_TYPE are available and working in Android Studio. The DEBUG boolean is the "debuggable" option in the build.gradle file:

buildTypes {
    debug {
        debuggable true
        runProguard false
        proguardFile getDefaultProguardFile('proguard-android.txt')
    }
    release {
        debuggable false
        runProguard true
        proguardFile getDefaultProguardFile('proguard-android.txt')
    }
}

The BUILD_TYPE is set automatically to "debug" when you run in Android Studio.

So if you set debuggable to false OR the user has a release version, then logs will be gone.

Martin B
  • 337
  • 4
  • 9
  • As explained on the answers written two years earlier than this - this is bad for performance. It means a check has to be carried out for each and every log at runtime vs just being stripped away while compiling – Nick Cardoso Jan 15 '16 at 11:39
  • 2
    The performance hit is microseconds!! Your caveat is academic! – Martin B Feb 17 '16 at 22:42
  • @NickCardoso, what is you suggestion to solve this? Would you kindly point to some alternative code to use? – W.M. Jan 25 '20 at 09:58
  • 1
    @W.M. Just use the regular log with proguard rules to remove them. I don't see a reason to pollute the heap with another static class and then need to add that method call to the stack every time we call a log when we already have a better built-in solution. Anyone who's ever worked with embedded or performance critical systems or tight loops knows Martin B's comment is way off the mark – Nick Cardoso Jan 27 '20 at 09:41
  • @NickCardoso, thank you very much. You mean I should use `Log.d("tag", "message")` ? Would be great if you could refer to some documentation explaining how to exclude logging code using proguard. – W.M. Jan 27 '20 at 13:44
  • 1
    @W.M. sure - there's a good answer on S.O. actually: https://stackoverflow.com/a/13327603/984830 – Nick Cardoso Jan 27 '20 at 14:11
  • @MartinB your comment is way off the mark. – ino Oct 15 '22 at 19:13
0

I'd recommend you look at Roboguice - http://code.google.com/p/roboguice/ - if you use the built-in Ln function, it will automatically not log on a signed APK. It simplifies a ton of other Android boilerplate as well. You can see it in action with Basedroid - https://github.com/achuinard/basedroid

Anthony Chuinard
  • 1,028
  • 10
  • 17
  • This is not entirely correct: http://roboguice.googlecode.com/hg/roboguice/docs/apidocs/index.html "For apps built using SDK Tools r8 or later, this means any debug build. Release builds built with r8 or later will have verbose and debug log messages turned off" .... This means Warn and Error will still be logged by default. – forgemo Nov 08 '12 at 12:37
  • Ah, for some reason I thought Roboguice checked debug vs release signature. I guess it just depends on the SDK version. – Anthony Chuinard Dec 21 '12 at 02:25
0

You can use DebugLog class. All logs are disabled by DebugLog when the app is released. And it provides more understandable DDMS logs for developers.

Ex;

DebugLog.e("your message");
Mustafa Ferhan
  • 240
  • 2
  • 4