I am currently removing Android Logging by using the common Proguard/R8 configuration:
-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(...);
}
But I have found this official documentation which recommends to set the following R8 option with the corresponding log level (in the example 4) too:
-maximumremovedandroidloglevel 4
I checked to R8 source code to understand what this additional option does. I only found this comment:
Replace Android logging statements like Log.w(...) and Log.IsLoggable(..., WARNING) at or below a certain logging level by false.
Is this really required and what are the benefit vs only using the
assumenosideeffects
rule?
I was curious about this rule as well, so I have researched a bit.
Is this really required and what are the benefit vs only using the assumenosideeffects rule?
This -maximumremovedandroidloglevel
rule is intended to have the same effect as the -assumenosideeffects
for Log.*
methods, therefore it might be used as a complete substitute.
However, as of the latest version (3.3.75), it performs slightly differently than -assumenosideeffects
. If you intend to remove all Log.*
method invocations - then you shouldn't care (you can use one, or the other). But if you're removing only a portion of those methods and you rely on Log#isLoggable
, then I'd suggest sticking to your current configuration, and avoiding adding the -maximumremovedandroidloglevel
rule to your proguard configuration file.
A deeper dig
Let's assume we have the following lines of code in our source code, and then I'll show what it looks like after R8 processing with different configurations.
if (Log.isLoggable("FOO_TAG", Log.VERBOSE)) {
Log.v("FOO_TAG", "verbose message");
}
if (Log.isLoggable("FOO_TAG", Log.WARN)) {
Log.w("FOO_TAG", "warn message");
}
<1> R8 v3.3.75, with the following rules (note that I have commented out the w
and e
methods):
-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(...);
}
Produces the following output:
if (Log.isLoggable("FOO_TAG", 5)) {
Log.w("FOO_TAG", "warn message");
}
R8 removed the VERBOSE logs as expected. Note that it keeps the Log#isLoggable
method invocation where the level (the second parameter) is WARN
(5).
<2> R8 v3.3.75, with the following rules (4 means we want to remove all the log methods up to INFO
, including):
-maximumremovedandroidloglevel 4
Produces the following output:
Log.w("FOO_TAG", "warn message");
Note that this rule keeps the Log#w
method invocation, but removes the Log#isLoggable
invocation (this is where the behavior slightly differs).
This means that the latest R8 version in regards to -maximumremovedandroidloglevel
doesn't work exactly as advertised (here):
Example: The value of android.util.log.INFO is 4. Therefore, specifying
-maximumremovedandroidloglevel 4
will remove all calls to Log.v(), Log.d(), and Log.i(), as well as it will replace calls to Log.isLoggable(X, {2, 3, 4}) by false.
<3> R8 from the tip of the main
branch, with the following rules (4 means we want to remove all the log methods up to INFO
, including):
-maximumremovedandroidloglevel 4
Produces the following output:
if (Log.isLoggable("FOO_TAG", 5)) {
Log.w("FOO_TAG", "warn message");
}
It seems that main
branch includes a fix that reinstates the behavior parity of two approaches (Log#isLoggable
with WARN
as a parameter was not removed).
The relevant diff between the main
and 3.3.75
tag is those two commits: 1, 2.
Why the official documentation includes both -assumenosideeffects
and -maximumremovedandroidloglevel
?
According to my tests - it seems misleading, as they should have suggested to using one or the other, definitely not both.
I have opened an issue with a request to elaborate on that.