android – Removing logging with ProGuard doesn't remove the strings being logged-ThrowExceptions

Exception or error:

Possible Duplicate:
Removing unused strings during ProGuard optimisation

I have an Android application with dozens of logging statements. I’d prefer they wouldn’t appear in the release version, so I used Proguard with something like this in the proguard.cfg file:

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

But the problem is that there are a lot of Log.d("something is " + something), and although the Log.d() statement is being removed from the bytecode, the strings are still there.

So, following this answer, I created a simple wrapper class, something along the lines of:

public class MyLogger {
    public static void d(Object... msgs) {
        StringBuilder log = new StringBuilder();
        for(Object msg : msgs) {
            log.append(msg.toString());
        }
        Log.d(TAG, log.toString());
    }
}

Then I edited my proguard.cfg:

-assumenosideeffects class my.package.MyLogger {
    public static *** d(...);
}

But the strings are still found in the generated bytecode!

Other than this, I am using the standard proguard.cfg provided by the Android SDK. Am I doing something wrong?


Edit: after examining the generated bytecode, I saw that the strings were there, but they were not being appended one to another, as I thought. They were being stored in an array. Why? To pass them as variable parameters to my method. It looks like ProGuard doesn’t like that, so I modified my logger class like this:

public static void d(Object a) {
    log(a);
}

public static void d(Object a, Object b) {
    log(a, b);
}

(... I had to put like seven d() methods ...)

private static void log(Object... msgs) {
    (same as before)
}

It’s ugly, but now the strings are nowhere in the bytecode.

Is this some kind of bug/limitation of ProGuard? Or is it just me not understanding how it works?

How to solve:

Your solution with different methods for different numbers of arguments is probably the most convenient one. A single method with a variable number of arguments would be nicer, but the current version of ProGuard is not smart enough to remove all the unused code.

The java compiler compiles a variable number of arguments as a single array argument. On the invocation side, this means creating an array of the right size, filling out the elements, and passing it to the method. ProGuard sees that the array is being created and then being used to store elements in it. It doesn’t realize (yet) that it then isn’t used for actual useful operations, with the method invocation being gone. As a result, the array creation and initialization are preserved.

It’s a good practice to check the processed code if you’re expecting some particular optimization.

###

Proguard doesn’t work me, too, and I don’t like to create a wrapper around Log, so I’ve tested the following solutions:

WORKING Solution

final static boolean IsDebugging = false;

and in your code:

if(IsDebugging)
   Log.i(TAG, "Debugging");

consider that you must use final keyword so that IsDebugging become true or false at compile time and log strings doesn’t appear in final bytecode.

NOT WORKING (IsDebugMode)

    public static boolean IsDebugMode(Context context) {
    PackageManager pm = context.getPackageManager();
    PackageInfo pi;
    try {
        pi = pm.getPackageInfo(context.getPackageName(),0);
    } catch (NameNotFoundException e) {
        return false;
    }
    return (pi.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
}

and in my code:

    boolean IsDebugging = Utils.IsDebugMode(this);

    if(IsDebugging)
      Log.i(TAG, "Debugging");

The above solution doesn’t print any Log in Logcat but the log string are compiled and exist in final bytecode which is bad.

The other solution was :

NOT WORKING (BuildConfid.DEBUG)

    if(BuildConfig.DEBUG)
      Log.i(TAG, "Debugging");

This is same as IsDebugMode solution. It doesn’t print anything but strings are in final bytecode. Bad again!

Leave a Reply

Your email address will not be published. Required fields are marked *