2

I have the following statement in my proguard-rules.pro to strip out logging in the production apk:

-assumenosideeffects class android.util.Log { public static boolean isLoggable(java.lang.String, int); public static int v(...); public static int i(...); public static int w(...); public static int d(...); public static int e(...); } 

This has the effect of removing statements like:

Log.d(TAG, "setup finished"); 

Now let's consider the following statement:

Log.d(TAG, "setup finished with status " + client.getStatus()); 

My assumption until now was that this Log.d() statement would be stripped out and therefore that client.getStatus() would never get called.

Specifically, if client happens to be null then my assumption had been that there would be no NullPointerException.

But now I'm not so sure. Perhaps the compiler looks at the above statement more like the following:

int status = client.getStatus(); Log.d(TAG, "setup finished with status " + status); 

Now the client.getStatus() statement is executed and NullPointerException may result.

What is the actual situation?

Note: I'm actually now using R8 rather than ProGuard.

9
  • consider looking at a library like Timber github.com/JakeWharton/timber if you're really paranoid about production logs Commented Jul 25, 2019 at 7:43
  • @a_local_nobody I'm not paranoid about what appears in the logs... I'm pretty confident that in the above example the obfuscator will prevent the status from appearing in the logs, even if the status is actually fetched. I'm just trying to track down the elusive source of a NullPointerException in my production apk and now I'm starting to consider what I am executing in these Log statements, whereas previously I'd assumed that they would never result in a NullPointerException because they are stripped out completely. Commented Jul 25, 2019 at 7:51
  • if it is removed from code on preprocessing, how can it be executed ?? Commented Jul 25, 2019 at 8:34
  • use a wrapper class around the normal logs....proguard did not work in my case and the logs were still on in production. I did a project-wide search and changed the name of the log statement to my custom class. I also have specific sequence of buttons that turn on logging in the production app. Pretty random clicks that no one would do. Commented Jul 25, 2019 at 8:44
  • @Antoniossss look at the code snippet in the question in which the client.getStatus() statement is separated out from the Log.d() statement. Even if the Log.d() statement is removed from the code during preprocessing, this still leaves the client.getStatus() statement! And it is the client.getStatus() statement that may be the source of a NullPointerException, not the Log.d() statement per se. Or is it the case that R8/ProGuard will also (automatically) remove the client.getStatus() statement because its result is never used? Commented Jul 25, 2019 at 8:50

1 Answer 1

0

OK so this came as a surprise to me, and possibly to others (@Antoniossss), but it seems that statements executed inside a Log.d() are executed despite ProGuard/R8 being configured to remove all Log.d() statements.

So, even with ProGuard/R8 being configured to remove all Log.d() statements, the following will result in a NullPointerException:

String myString = null; Log.d(TAG, "myString length: " + myString.length()); 

So, in future I will remember to check these Log statements more carefully when reviewing my code to find the source of a NullPointerException from my Developer Console, and not just pass over them assuming that they cannot have been the cause.

Sign up to request clarification or add additional context in comments.

3 Comments

On which line it is thrown?
nice link, thanks @Antoniossss. Which line? All the logcat says is Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.lang.String.length()' on a null object reference, and below that it gives the method in which the call was made... how can I get more specific information about where in the method the offending call was made?
huh you wont. If the stacktrace does not contain line number, it simply means numbers were striped down during compilation. But anyway, we know what causes it.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.