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?
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.
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.
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 + ")");
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)
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(...);
}
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
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");
}
}
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)
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(
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.
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.
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);
}
}
Go to Application->app->proguard-rules.pro
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
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
}
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(...);
}
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.
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
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))
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(...);
}
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);
}
}
}
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
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.
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!
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.
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 :)
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.
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.
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)
}
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.
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.
the simplest way;
use DebugLog
All logs are disabled by DebugLog when the app is released.
https://github.com/MustafaFerhan/DebugLog
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.
Related
This question already has answers here:
How to remove all debug logging calls before building the release version of an Android app?
(31 answers)
Closed 7 years ago.
so.. I'm using Log.d("", ""); in many places in my app for debuging.
but I don't want those logs in the store.
right now in order to hide them in the store version, I created a Constant.class and put a boolean there called debugMode, and wrapped every log i have in an if statement like this :
if (Constant.debugMode) {
Log.d(TAG, "check123");
}
and then when I build a google store apk I change that boolean to true instead of false.
that's kind of clumsy in my opinion, is there any more efficient way to do that?
Make a simple logger class that has public static methods and a swich to enable logs only for debug versions of your app. Here is a sample code.
public class Logger {
public static final boolean D = BuildConfig.DEBUG;
public static void LogInfo(String TAG, String msg) {
if (D) {
Log.i(TAG, msg);
}
}
public static void LogWarrning(String TAG, String msg) {
if (D) {
Log.w(TAG, msg);
}
}
public static void LogDebug(String TAG, String msg) {
if (D) {
Log.d(TAG, msg);
}
}
public static void LogError(String TAG, String msg) {
if (D) {
Log.e(TAG, msg);
}
}
}
Usage
Logger.LogDebug("Test", "Example");
This way you can keep the if clauses in one place and don't have to worry about them. Also I don't think it clumsy this way.
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.
#REF:Remove all debug logging calls before publishing: are there tools to do this?
I use Log functions to debug my Android app. I'm close to releasing my app and I'd like to reduce debug level. However I don't know how to disable some of these function. Say I just want to disable Log.v() logs.
Android doc says here that one should remove Log functions calls in code or remove/set to false the android::debbugable option. The first option does not fit my needs, since I'd like to Log calls in my source code (for future updates). The second option disables all logs, which is not convenient for pre-release testing.
Do you know how to disable only verbose (Log.v) logs ?
Many thanks!
You could create your own wrapper for the Log-class, maybe something like this:
static class MyLog{
private static int mLevel;
public final static void setLevel(int level){
mLevel = level;
}
public final static void v(String tag, String message){
if( mLevel > 0 ) return;
Log.v(tag, message);
}
public final static void d(String tag, String message){
if( mLevel > 1 ) return;
Log.d(tag, message);
}
public final static void i(String tag, String message){
if( mLevel > 2 ) return;
Log.d(tag, message);
}
//Same for w and e if neccessary..
}
Then you can use MyLog.setLevel(1); to disable any verbose logging (or a higher level to disable those logs as well).
If you are going to obfuscate your release with Proguard, then putting lines such as
-assumenosideeffects class android.util.Log {
public static *** d(...);
public static *** v(...);
}
into the config file will remove those categories of logging from your code.
I am having lots of logging statements to debug for example.
Log.v(TAG, "Message here");
Log.w(TAG, " WARNING HERE");
while deploying this application on device phone i want to turn off the verbose logging from where i can enable/disable logging.
The Android Documentation says the following about Log Levels:
Verbose should never be compiled into an application except during development. Debug logs are compiled in but stripped at runtime. Error, warning and info logs are always kept.
So you may want to consider stripping the log Verbose logging statements out, possibly using ProGuard as suggested in another answer.
According to the documentation, you can configure logging on a development device using System Properties. The property to set is log.tag.<YourTag> and it should be set to one of the following values: VERBOSE, DEBUG, INFO, WARN, ERROR, ASSERT, or SUPPRESS. More information on this is available in the documentation for the isLoggable() method.
You can set properties temporarily using the setprop command. For example:
C:\android>adb shell setprop log.tag.MyAppTag WARN
C:\android>adb shell getprop log.tag.MyAppTag
WARN
Alternatively, you can specify them in the file '/data/local.prop' as follows:
log.tag.MyAppTag=WARN
Later versions of Android appear to require that /data/local.prop be read only. This file is read at boot time so you'll need to restart after updating it. If /data/local.prop is world writable, it will likely be ignored.
Finally, you can set them programmatically using the System.setProperty() method.
The easiest way is probably to run your compiled JAR through ProGuard before deployment, with a config like:
-assumenosideeffects class android.util.Log {
public static int v(...);
}
That will — aside from all the other ProGuard optimisations — remove any verbose log statements directly from the bytecode.
A common way is to make an int named loglevel, and define its debug level based on loglevel.
public static int LOGLEVEL = 2;
public static boolean ERROR = LOGLEVEL > 0;
public static boolean WARN = LOGLEVEL > 1;
...
public static boolean VERBOSE = LOGLEVEL > 4;
if (VERBOSE) Log.v(TAG, "Message here"); // Won't be shown
if (WARN) Log.w(TAG, "WARNING HERE"); // Still goes through
Later, you can just change the LOGLEVEL for all debug output level.
I took a simple route - creating a wrapper class that also makes use of variable parameter lists.
public class Log{
public static int LEVEL = android.util.Log.WARN;
static public void d(String tag, String msgFormat, Object...args)
{
if (LEVEL<=android.util.Log.DEBUG)
{
android.util.Log.d(tag, String.format(msgFormat, args));
}
}
static public void d(String tag, Throwable t, String msgFormat, Object...args)
{
if (LEVEL<=android.util.Log.DEBUG)
{
android.util.Log.d(tag, String.format(msgFormat, args), t);
}
}
//...other level logging functions snipped
The better way is to use SLF4J API + some of its implementation.
For Android applications you can use the following:
Android Logger is the lightweight but easy-to-configure SLF4J implementation (< 50 Kb).
LOGBack is the most powerful and optimized implementation but its size is about 1 Mb.
Any other by your taste: slf4j-android, slf4android.
You should use
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "my log message");
}
Stripping out the logging with proguard (see answer from #Christopher ) was easy and fast, but it caused stack traces from production to mismatch the source if there was any debug logging in the file.
Instead, here's a technique that uses different logging levels in development vs. production, assuming that proguard is used only in production. It recognizes production by seeing if proguard has renamed a given class name (in the example, I use "com.foo.Bar"--you would replace this with a fully-qualified class name that you know will be renamed by proguard).
This technique makes use of commons logging.
private void initLogging() {
Level level = Level.WARNING;
try {
// in production, the shrinker/obfuscator proguard will change the
// name of this class (and many others) so in development, this
// class WILL exist as named, and we will have debug level
Class.forName("com.foo.Bar");
level = Level.FINE;
} catch (Throwable t) {
// no problem, we are in production mode
}
Handler[] handlers = Logger.getLogger("").getHandlers();
for (Handler handler : handlers) {
Log.d("log init", "handler: " + handler.getClass().getName());
handler.setLevel(level);
}
}
Log4j or slf4j can also be used as logging frameworks in Android together with logcat. See the project android-logging-log4j or log4j support in android
There is a tiny drop-in replacement for the standard android Log class - https://github.com/zserge/log
Basically all you have to do is to replace imports from android.util.Log to trikita.log.Log. Then in your Application.onCreate() or in some static initalizer check for the BuilConfig.DEBUG or any other flag and use Log.level(Log.D) or Log.level(Log.E) to change the minimal log level. You can use Log.useLog(false) to disable logging at all.
May be you can see this Log extension class: https://github.com/dbauduin/Android-Tools/tree/master/logs.
It enables you to have a fine control on logs.
You can for example disable all logs 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).
I created a Utility/Wrapper which solves this problem + other common problems around Logging.
A Debugging utility with the following features:
The usual features provided by Log class wrapped around by LogMode s.
Method Entry-Exit logs: Can be turned off by a switch
Selective Debugging: Debug specific classes.
Method Execution-Time Measurement: Measure Execution time for individual methods as well as collective time spent on all methods of a class.
How To Use?
Include the class in your project.
Use it like you use android.util.Log methods, to start with.
Use the Entry-Exit logs feature by placing calls to entry_log()-exit_log() methods at the beginning and ending of methods in your app.
I have tried to make the documentation self suffiecient.
Suggestions to improve this Utility are welcome.
Free to use/share.
Download it from GitHub.
Here is a more complex solution. You will get full stack trace and the method toString() will be called only if needed(Performance). The attribute BuildConfig.DEBUG will be false in the production mode so all trace and debug logs will be removed. The hot spot compiler has the chance to remove the calls because off final static properties.
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import android.util.Log;
public class Logger {
public enum Level {
error, warn, info, debug, trace
}
private static final String DEFAULT_TAG = "Project";
private static final Level CURRENT_LEVEL = BuildConfig.DEBUG ? Level.trace : Level.info;
private static boolean isEnabled(Level l) {
return CURRENT_LEVEL.compareTo(l) >= 0;
}
static {
Log.i(DEFAULT_TAG, "log level: " + CURRENT_LEVEL.name());
}
private String classname = DEFAULT_TAG;
public void setClassName(Class<?> c) {
classname = c.getSimpleName();
}
public String getClassname() {
return classname;
}
public boolean isError() {
return isEnabled(Level.error);
}
public boolean isWarn() {
return isEnabled(Level.warn);
}
public boolean isInfo() {
return isEnabled(Level.info);
}
public boolean isDebug() {
return isEnabled(Level.debug);
}
public boolean isTrace() {
return isEnabled(Level.trace);
}
public void error(Object... args) {
if (isError()) Log.e(buildTag(), build(args));
}
public void warn(Object... args) {
if (isWarn()) Log.w(buildTag(), build(args));
}
public void info(Object... args) {
if (isInfo()) Log.i(buildTag(), build(args));
}
public void debug(Object... args) {
if (isDebug()) Log.d(buildTag(), build(args));
}
public void trace(Object... args) {
if (isTrace()) Log.v(buildTag(), build(args));
}
public void error(String msg, Throwable t) {
if (isError()) error(buildTag(), msg, stackToString(t));
}
public void warn(String msg, Throwable t) {
if (isWarn()) warn(buildTag(), msg, stackToString(t));
}
public void info(String msg, Throwable t) {
if (isInfo()) info(buildTag(), msg, stackToString(t));
}
public void debug(String msg, Throwable t) {
if (isDebug()) debug(buildTag(), msg, stackToString(t));
}
public void trace(String msg, Throwable t) {
if (isTrace()) trace(buildTag(), msg, stackToString(t));
}
private String buildTag() {
String tag ;
if (BuildConfig.DEBUG) {
StringBuilder b = new StringBuilder(20);
b.append(getClassname());
StackTraceElement stackEntry = Thread.currentThread().getStackTrace()[4];
if (stackEntry != null) {
b.append('.');
b.append(stackEntry.getMethodName());
b.append(':');
b.append(stackEntry.getLineNumber());
}
tag = b.toString();
} else {
tag = DEFAULT_TAG;
}
}
private String build(Object... args) {
if (args == null) {
return "null";
} else {
StringBuilder b = new StringBuilder(args.length * 10);
for (Object arg : args) {
if (arg == null) {
b.append("null");
} else {
b.append(arg);
}
}
return b.toString();
}
}
private String stackToString(Throwable t) {
ByteArrayOutputStream baos = new ByteArrayOutputStream(500);
baos.toString();
t.printStackTrace(new PrintStream(baos));
return baos.toString();
}
}
use like this:
Loggor log = new Logger();
Map foo = ...
List bar = ...
log.error("Foo:", foo, "bar:", bar);
// bad example (avoid something like this)
// log.error("Foo:" + " foo.toString() + "bar:" + bar);
In a very simple logging scenario, where you're literally just trying to write to console during development for debugging purposes, it might be easiest to just do a search and replace before your production build and comment out all the calls to Log or System.out.println.
For example, assuming you didn't use the "Log." anywhere outside of a call to Log.d or Log.e, etc, you could simply do a find and replace across the entire solution to replace "Log." with "//Log." to comment out all your logging calls, or in my case I'm just using System.out.println everywhere, so before going to production I'll simply do a full search and replace for "System.out.println" and replace with "//System.out.println".
I know this isn't ideal, and it would be nice if the ability to find and comment out calls to Log and System.out.println were built into Eclipse, but until that happens the easiest and fastest and best way to do this is to comment out by search and replace. If you do this, you don't have to worry about mismatching stack trace line numbers, because you're editing your source code, and you're not adding any overhead by checking some log level configuration, etc.
In my apps I have a class which wraps the Log class which has a static boolean var called "state". Throughout my code I check the value of the "state" variable using a static method before actually writing to the Log. I then have a static method to set the "state" variable which ensures the value is common across all instances created by the app. This means I can enable or disable all logging for the App in one call - even when the App is running. Useful for support calls... It does mean that you have to stick to your guns when debugging and not regress to using the standard Log class though...
It's also useful (convenient) that Java interprets a boolean var as false if it hasn't been assigned a value, which means it can be left as false until you need to turn on logging :-)
We can use class Log in our local component and define the methods as v/i/e/d.
Based on the need of we can make call further.
example is shown below.
public class Log{
private static boolean TAG = false;
public static void d(String enable_tag, String message,Object...args){
if(TAG)
android.util.Log.d(enable_tag, message+args);
}
public static void e(String enable_tag, String message,Object...args){
if(TAG)
android.util.Log.e(enable_tag, message+args);
}
public static void v(String enable_tag, String message,Object...args){
if(TAG)
android.util.Log.v(enable_tag, message+args);
}
}
if we do not need any print(s), at-all make TAG as false for all else
remove the check for type of Log (say Log.d).
as
public static void i(String enable_tag, String message,Object...args){
// if(TAG)
android.util.Log.i(enable_tag, message+args);
}
here message is for string and and args is the value you want to print.
For me it is often useful being able to set different log levels for each TAG.
I am using this very simple wrapper class:
public class Log2 {
public enum LogLevels {
VERBOSE(android.util.Log.VERBOSE), DEBUG(android.util.Log.DEBUG), INFO(android.util.Log.INFO), WARN(
android.util.Log.WARN), ERROR(android.util.Log.ERROR);
int level;
private LogLevels(int logLevel) {
level = logLevel;
}
public int getLevel() {
return level;
}
};
static private HashMap<String, Integer> logLevels = new HashMap<String, Integer>();
public static void setLogLevel(String tag, LogLevels level) {
logLevels.put(tag, level.getLevel());
}
public static int v(String tag, String msg) {
return Log2.v(tag, msg, null);
}
public static int v(String tag, String msg, Throwable tr) {
if (logLevels.containsKey(tag)) {
if (logLevels.get(tag) > android.util.Log.VERBOSE) {
return -1;
}
}
return Log.v(tag, msg, tr);
}
public static int d(String tag, String msg) {
return Log2.d(tag, msg, null);
}
public static int d(String tag, String msg, Throwable tr) {
if (logLevels.containsKey(tag)) {
if (logLevels.get(tag) > android.util.Log.DEBUG) {
return -1;
}
}
return Log.d(tag, msg);
}
public static int i(String tag, String msg) {
return Log2.i(tag, msg, null);
}
public static int i(String tag, String msg, Throwable tr) {
if (logLevels.containsKey(tag)) {
if (logLevels.get(tag) > android.util.Log.INFO) {
return -1;
}
}
return Log.i(tag, msg);
}
public static int w(String tag, String msg) {
return Log2.w(tag, msg, null);
}
public static int w(String tag, String msg, Throwable tr) {
if (logLevels.containsKey(tag)) {
if (logLevels.get(tag) > android.util.Log.WARN) {
return -1;
}
}
return Log.w(tag, msg, tr);
}
public static int e(String tag, String msg) {
return Log2.e(tag, msg, null);
}
public static int e(String tag, String msg, Throwable tr) {
if (logLevels.containsKey(tag)) {
if (logLevels.get(tag) > android.util.Log.ERROR) {
return -1;
}
}
return Log.e(tag, msg, tr);
}
}
Now just set the log level per TAG at the beginning of each class:
Log2.setLogLevel(TAG, LogLevels.INFO);
Another way is to use a logging platform that has the capabilities of opening and closing logs. This can give much of flexibility sometimes even on a production app which logs should be open and which closed depending on which issues you have
for example:
LumberJack
Shipbook (disclaimer: I'm the author of this package)
https://limxtop.blogspot.com/2019/05/app-log.html
Read this article please, where provides complete implement:
For debug version, all the logs will be output;
For release version, only the logs whose level is above DEBUG (exclude) will be output by default. In the meanwhile, the DEBUG and VERBOSE log can be enable through setprop log.tag.<YOUR_LOG_TAG> <LEVEL> in running time.
I am having lots of logging statements to debug for example.
Log.v(TAG, "Message here");
Log.w(TAG, " WARNING HERE");
while deploying this application on device phone i want to turn off the verbose logging from where i can enable/disable logging.
The Android Documentation says the following about Log Levels:
Verbose should never be compiled into an application except during development. Debug logs are compiled in but stripped at runtime. Error, warning and info logs are always kept.
So you may want to consider stripping the log Verbose logging statements out, possibly using ProGuard as suggested in another answer.
According to the documentation, you can configure logging on a development device using System Properties. The property to set is log.tag.<YourTag> and it should be set to one of the following values: VERBOSE, DEBUG, INFO, WARN, ERROR, ASSERT, or SUPPRESS. More information on this is available in the documentation for the isLoggable() method.
You can set properties temporarily using the setprop command. For example:
C:\android>adb shell setprop log.tag.MyAppTag WARN
C:\android>adb shell getprop log.tag.MyAppTag
WARN
Alternatively, you can specify them in the file '/data/local.prop' as follows:
log.tag.MyAppTag=WARN
Later versions of Android appear to require that /data/local.prop be read only. This file is read at boot time so you'll need to restart after updating it. If /data/local.prop is world writable, it will likely be ignored.
Finally, you can set them programmatically using the System.setProperty() method.
The easiest way is probably to run your compiled JAR through ProGuard before deployment, with a config like:
-assumenosideeffects class android.util.Log {
public static int v(...);
}
That will — aside from all the other ProGuard optimisations — remove any verbose log statements directly from the bytecode.
A common way is to make an int named loglevel, and define its debug level based on loglevel.
public static int LOGLEVEL = 2;
public static boolean ERROR = LOGLEVEL > 0;
public static boolean WARN = LOGLEVEL > 1;
...
public static boolean VERBOSE = LOGLEVEL > 4;
if (VERBOSE) Log.v(TAG, "Message here"); // Won't be shown
if (WARN) Log.w(TAG, "WARNING HERE"); // Still goes through
Later, you can just change the LOGLEVEL for all debug output level.
I took a simple route - creating a wrapper class that also makes use of variable parameter lists.
public class Log{
public static int LEVEL = android.util.Log.WARN;
static public void d(String tag, String msgFormat, Object...args)
{
if (LEVEL<=android.util.Log.DEBUG)
{
android.util.Log.d(tag, String.format(msgFormat, args));
}
}
static public void d(String tag, Throwable t, String msgFormat, Object...args)
{
if (LEVEL<=android.util.Log.DEBUG)
{
android.util.Log.d(tag, String.format(msgFormat, args), t);
}
}
//...other level logging functions snipped
The better way is to use SLF4J API + some of its implementation.
For Android applications you can use the following:
Android Logger is the lightweight but easy-to-configure SLF4J implementation (< 50 Kb).
LOGBack is the most powerful and optimized implementation but its size is about 1 Mb.
Any other by your taste: slf4j-android, slf4android.
You should use
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "my log message");
}
Stripping out the logging with proguard (see answer from #Christopher ) was easy and fast, but it caused stack traces from production to mismatch the source if there was any debug logging in the file.
Instead, here's a technique that uses different logging levels in development vs. production, assuming that proguard is used only in production. It recognizes production by seeing if proguard has renamed a given class name (in the example, I use "com.foo.Bar"--you would replace this with a fully-qualified class name that you know will be renamed by proguard).
This technique makes use of commons logging.
private void initLogging() {
Level level = Level.WARNING;
try {
// in production, the shrinker/obfuscator proguard will change the
// name of this class (and many others) so in development, this
// class WILL exist as named, and we will have debug level
Class.forName("com.foo.Bar");
level = Level.FINE;
} catch (Throwable t) {
// no problem, we are in production mode
}
Handler[] handlers = Logger.getLogger("").getHandlers();
for (Handler handler : handlers) {
Log.d("log init", "handler: " + handler.getClass().getName());
handler.setLevel(level);
}
}
Log4j or slf4j can also be used as logging frameworks in Android together with logcat. See the project android-logging-log4j or log4j support in android
There is a tiny drop-in replacement for the standard android Log class - https://github.com/zserge/log
Basically all you have to do is to replace imports from android.util.Log to trikita.log.Log. Then in your Application.onCreate() or in some static initalizer check for the BuilConfig.DEBUG or any other flag and use Log.level(Log.D) or Log.level(Log.E) to change the minimal log level. You can use Log.useLog(false) to disable logging at all.
May be you can see this Log extension class: https://github.com/dbauduin/Android-Tools/tree/master/logs.
It enables you to have a fine control on logs.
You can for example disable all logs 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).
I created a Utility/Wrapper which solves this problem + other common problems around Logging.
A Debugging utility with the following features:
The usual features provided by Log class wrapped around by LogMode s.
Method Entry-Exit logs: Can be turned off by a switch
Selective Debugging: Debug specific classes.
Method Execution-Time Measurement: Measure Execution time for individual methods as well as collective time spent on all methods of a class.
How To Use?
Include the class in your project.
Use it like you use android.util.Log methods, to start with.
Use the Entry-Exit logs feature by placing calls to entry_log()-exit_log() methods at the beginning and ending of methods in your app.
I have tried to make the documentation self suffiecient.
Suggestions to improve this Utility are welcome.
Free to use/share.
Download it from GitHub.
Here is a more complex solution. You will get full stack trace and the method toString() will be called only if needed(Performance). The attribute BuildConfig.DEBUG will be false in the production mode so all trace and debug logs will be removed. The hot spot compiler has the chance to remove the calls because off final static properties.
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import android.util.Log;
public class Logger {
public enum Level {
error, warn, info, debug, trace
}
private static final String DEFAULT_TAG = "Project";
private static final Level CURRENT_LEVEL = BuildConfig.DEBUG ? Level.trace : Level.info;
private static boolean isEnabled(Level l) {
return CURRENT_LEVEL.compareTo(l) >= 0;
}
static {
Log.i(DEFAULT_TAG, "log level: " + CURRENT_LEVEL.name());
}
private String classname = DEFAULT_TAG;
public void setClassName(Class<?> c) {
classname = c.getSimpleName();
}
public String getClassname() {
return classname;
}
public boolean isError() {
return isEnabled(Level.error);
}
public boolean isWarn() {
return isEnabled(Level.warn);
}
public boolean isInfo() {
return isEnabled(Level.info);
}
public boolean isDebug() {
return isEnabled(Level.debug);
}
public boolean isTrace() {
return isEnabled(Level.trace);
}
public void error(Object... args) {
if (isError()) Log.e(buildTag(), build(args));
}
public void warn(Object... args) {
if (isWarn()) Log.w(buildTag(), build(args));
}
public void info(Object... args) {
if (isInfo()) Log.i(buildTag(), build(args));
}
public void debug(Object... args) {
if (isDebug()) Log.d(buildTag(), build(args));
}
public void trace(Object... args) {
if (isTrace()) Log.v(buildTag(), build(args));
}
public void error(String msg, Throwable t) {
if (isError()) error(buildTag(), msg, stackToString(t));
}
public void warn(String msg, Throwable t) {
if (isWarn()) warn(buildTag(), msg, stackToString(t));
}
public void info(String msg, Throwable t) {
if (isInfo()) info(buildTag(), msg, stackToString(t));
}
public void debug(String msg, Throwable t) {
if (isDebug()) debug(buildTag(), msg, stackToString(t));
}
public void trace(String msg, Throwable t) {
if (isTrace()) trace(buildTag(), msg, stackToString(t));
}
private String buildTag() {
String tag ;
if (BuildConfig.DEBUG) {
StringBuilder b = new StringBuilder(20);
b.append(getClassname());
StackTraceElement stackEntry = Thread.currentThread().getStackTrace()[4];
if (stackEntry != null) {
b.append('.');
b.append(stackEntry.getMethodName());
b.append(':');
b.append(stackEntry.getLineNumber());
}
tag = b.toString();
} else {
tag = DEFAULT_TAG;
}
}
private String build(Object... args) {
if (args == null) {
return "null";
} else {
StringBuilder b = new StringBuilder(args.length * 10);
for (Object arg : args) {
if (arg == null) {
b.append("null");
} else {
b.append(arg);
}
}
return b.toString();
}
}
private String stackToString(Throwable t) {
ByteArrayOutputStream baos = new ByteArrayOutputStream(500);
baos.toString();
t.printStackTrace(new PrintStream(baos));
return baos.toString();
}
}
use like this:
Loggor log = new Logger();
Map foo = ...
List bar = ...
log.error("Foo:", foo, "bar:", bar);
// bad example (avoid something like this)
// log.error("Foo:" + " foo.toString() + "bar:" + bar);
In a very simple logging scenario, where you're literally just trying to write to console during development for debugging purposes, it might be easiest to just do a search and replace before your production build and comment out all the calls to Log or System.out.println.
For example, assuming you didn't use the "Log." anywhere outside of a call to Log.d or Log.e, etc, you could simply do a find and replace across the entire solution to replace "Log." with "//Log." to comment out all your logging calls, or in my case I'm just using System.out.println everywhere, so before going to production I'll simply do a full search and replace for "System.out.println" and replace with "//System.out.println".
I know this isn't ideal, and it would be nice if the ability to find and comment out calls to Log and System.out.println were built into Eclipse, but until that happens the easiest and fastest and best way to do this is to comment out by search and replace. If you do this, you don't have to worry about mismatching stack trace line numbers, because you're editing your source code, and you're not adding any overhead by checking some log level configuration, etc.
In my apps I have a class which wraps the Log class which has a static boolean var called "state". Throughout my code I check the value of the "state" variable using a static method before actually writing to the Log. I then have a static method to set the "state" variable which ensures the value is common across all instances created by the app. This means I can enable or disable all logging for the App in one call - even when the App is running. Useful for support calls... It does mean that you have to stick to your guns when debugging and not regress to using the standard Log class though...
It's also useful (convenient) that Java interprets a boolean var as false if it hasn't been assigned a value, which means it can be left as false until you need to turn on logging :-)
We can use class Log in our local component and define the methods as v/i/e/d.
Based on the need of we can make call further.
example is shown below.
public class Log{
private static boolean TAG = false;
public static void d(String enable_tag, String message,Object...args){
if(TAG)
android.util.Log.d(enable_tag, message+args);
}
public static void e(String enable_tag, String message,Object...args){
if(TAG)
android.util.Log.e(enable_tag, message+args);
}
public static void v(String enable_tag, String message,Object...args){
if(TAG)
android.util.Log.v(enable_tag, message+args);
}
}
if we do not need any print(s), at-all make TAG as false for all else
remove the check for type of Log (say Log.d).
as
public static void i(String enable_tag, String message,Object...args){
// if(TAG)
android.util.Log.i(enable_tag, message+args);
}
here message is for string and and args is the value you want to print.
For me it is often useful being able to set different log levels for each TAG.
I am using this very simple wrapper class:
public class Log2 {
public enum LogLevels {
VERBOSE(android.util.Log.VERBOSE), DEBUG(android.util.Log.DEBUG), INFO(android.util.Log.INFO), WARN(
android.util.Log.WARN), ERROR(android.util.Log.ERROR);
int level;
private LogLevels(int logLevel) {
level = logLevel;
}
public int getLevel() {
return level;
}
};
static private HashMap<String, Integer> logLevels = new HashMap<String, Integer>();
public static void setLogLevel(String tag, LogLevels level) {
logLevels.put(tag, level.getLevel());
}
public static int v(String tag, String msg) {
return Log2.v(tag, msg, null);
}
public static int v(String tag, String msg, Throwable tr) {
if (logLevels.containsKey(tag)) {
if (logLevels.get(tag) > android.util.Log.VERBOSE) {
return -1;
}
}
return Log.v(tag, msg, tr);
}
public static int d(String tag, String msg) {
return Log2.d(tag, msg, null);
}
public static int d(String tag, String msg, Throwable tr) {
if (logLevels.containsKey(tag)) {
if (logLevels.get(tag) > android.util.Log.DEBUG) {
return -1;
}
}
return Log.d(tag, msg);
}
public static int i(String tag, String msg) {
return Log2.i(tag, msg, null);
}
public static int i(String tag, String msg, Throwable tr) {
if (logLevels.containsKey(tag)) {
if (logLevels.get(tag) > android.util.Log.INFO) {
return -1;
}
}
return Log.i(tag, msg);
}
public static int w(String tag, String msg) {
return Log2.w(tag, msg, null);
}
public static int w(String tag, String msg, Throwable tr) {
if (logLevels.containsKey(tag)) {
if (logLevels.get(tag) > android.util.Log.WARN) {
return -1;
}
}
return Log.w(tag, msg, tr);
}
public static int e(String tag, String msg) {
return Log2.e(tag, msg, null);
}
public static int e(String tag, String msg, Throwable tr) {
if (logLevels.containsKey(tag)) {
if (logLevels.get(tag) > android.util.Log.ERROR) {
return -1;
}
}
return Log.e(tag, msg, tr);
}
}
Now just set the log level per TAG at the beginning of each class:
Log2.setLogLevel(TAG, LogLevels.INFO);
Another way is to use a logging platform that has the capabilities of opening and closing logs. This can give much of flexibility sometimes even on a production app which logs should be open and which closed depending on which issues you have
for example:
LumberJack
Shipbook (disclaimer: I'm the author of this package)
https://limxtop.blogspot.com/2019/05/app-log.html
Read this article please, where provides complete implement:
For debug version, all the logs will be output;
For release version, only the logs whose level is above DEBUG (exclude) will be output by default. In the meanwhile, the DEBUG and VERBOSE log can be enable through setprop log.tag.<YOUR_LOG_TAG> <LEVEL> in running time.
I am having lots of logging statements to debug for example.
Log.v(TAG, "Message here");
Log.w(TAG, " WARNING HERE");
while deploying this application on device phone i want to turn off the verbose logging from where i can enable/disable logging.
The Android Documentation says the following about Log Levels:
Verbose should never be compiled into an application except during development. Debug logs are compiled in but stripped at runtime. Error, warning and info logs are always kept.
So you may want to consider stripping the log Verbose logging statements out, possibly using ProGuard as suggested in another answer.
According to the documentation, you can configure logging on a development device using System Properties. The property to set is log.tag.<YourTag> and it should be set to one of the following values: VERBOSE, DEBUG, INFO, WARN, ERROR, ASSERT, or SUPPRESS. More information on this is available in the documentation for the isLoggable() method.
You can set properties temporarily using the setprop command. For example:
C:\android>adb shell setprop log.tag.MyAppTag WARN
C:\android>adb shell getprop log.tag.MyAppTag
WARN
Alternatively, you can specify them in the file '/data/local.prop' as follows:
log.tag.MyAppTag=WARN
Later versions of Android appear to require that /data/local.prop be read only. This file is read at boot time so you'll need to restart after updating it. If /data/local.prop is world writable, it will likely be ignored.
Finally, you can set them programmatically using the System.setProperty() method.
The easiest way is probably to run your compiled JAR through ProGuard before deployment, with a config like:
-assumenosideeffects class android.util.Log {
public static int v(...);
}
That will — aside from all the other ProGuard optimisations — remove any verbose log statements directly from the bytecode.
A common way is to make an int named loglevel, and define its debug level based on loglevel.
public static int LOGLEVEL = 2;
public static boolean ERROR = LOGLEVEL > 0;
public static boolean WARN = LOGLEVEL > 1;
...
public static boolean VERBOSE = LOGLEVEL > 4;
if (VERBOSE) Log.v(TAG, "Message here"); // Won't be shown
if (WARN) Log.w(TAG, "WARNING HERE"); // Still goes through
Later, you can just change the LOGLEVEL for all debug output level.
I took a simple route - creating a wrapper class that also makes use of variable parameter lists.
public class Log{
public static int LEVEL = android.util.Log.WARN;
static public void d(String tag, String msgFormat, Object...args)
{
if (LEVEL<=android.util.Log.DEBUG)
{
android.util.Log.d(tag, String.format(msgFormat, args));
}
}
static public void d(String tag, Throwable t, String msgFormat, Object...args)
{
if (LEVEL<=android.util.Log.DEBUG)
{
android.util.Log.d(tag, String.format(msgFormat, args), t);
}
}
//...other level logging functions snipped
The better way is to use SLF4J API + some of its implementation.
For Android applications you can use the following:
Android Logger is the lightweight but easy-to-configure SLF4J implementation (< 50 Kb).
LOGBack is the most powerful and optimized implementation but its size is about 1 Mb.
Any other by your taste: slf4j-android, slf4android.
You should use
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "my log message");
}
Stripping out the logging with proguard (see answer from #Christopher ) was easy and fast, but it caused stack traces from production to mismatch the source if there was any debug logging in the file.
Instead, here's a technique that uses different logging levels in development vs. production, assuming that proguard is used only in production. It recognizes production by seeing if proguard has renamed a given class name (in the example, I use "com.foo.Bar"--you would replace this with a fully-qualified class name that you know will be renamed by proguard).
This technique makes use of commons logging.
private void initLogging() {
Level level = Level.WARNING;
try {
// in production, the shrinker/obfuscator proguard will change the
// name of this class (and many others) so in development, this
// class WILL exist as named, and we will have debug level
Class.forName("com.foo.Bar");
level = Level.FINE;
} catch (Throwable t) {
// no problem, we are in production mode
}
Handler[] handlers = Logger.getLogger("").getHandlers();
for (Handler handler : handlers) {
Log.d("log init", "handler: " + handler.getClass().getName());
handler.setLevel(level);
}
}
Log4j or slf4j can also be used as logging frameworks in Android together with logcat. See the project android-logging-log4j or log4j support in android
There is a tiny drop-in replacement for the standard android Log class - https://github.com/zserge/log
Basically all you have to do is to replace imports from android.util.Log to trikita.log.Log. Then in your Application.onCreate() or in some static initalizer check for the BuilConfig.DEBUG or any other flag and use Log.level(Log.D) or Log.level(Log.E) to change the minimal log level. You can use Log.useLog(false) to disable logging at all.
May be you can see this Log extension class: https://github.com/dbauduin/Android-Tools/tree/master/logs.
It enables you to have a fine control on logs.
You can for example disable all logs 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).
I created a Utility/Wrapper which solves this problem + other common problems around Logging.
A Debugging utility with the following features:
The usual features provided by Log class wrapped around by LogMode s.
Method Entry-Exit logs: Can be turned off by a switch
Selective Debugging: Debug specific classes.
Method Execution-Time Measurement: Measure Execution time for individual methods as well as collective time spent on all methods of a class.
How To Use?
Include the class in your project.
Use it like you use android.util.Log methods, to start with.
Use the Entry-Exit logs feature by placing calls to entry_log()-exit_log() methods at the beginning and ending of methods in your app.
I have tried to make the documentation self suffiecient.
Suggestions to improve this Utility are welcome.
Free to use/share.
Download it from GitHub.
Here is a more complex solution. You will get full stack trace and the method toString() will be called only if needed(Performance). The attribute BuildConfig.DEBUG will be false in the production mode so all trace and debug logs will be removed. The hot spot compiler has the chance to remove the calls because off final static properties.
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import android.util.Log;
public class Logger {
public enum Level {
error, warn, info, debug, trace
}
private static final String DEFAULT_TAG = "Project";
private static final Level CURRENT_LEVEL = BuildConfig.DEBUG ? Level.trace : Level.info;
private static boolean isEnabled(Level l) {
return CURRENT_LEVEL.compareTo(l) >= 0;
}
static {
Log.i(DEFAULT_TAG, "log level: " + CURRENT_LEVEL.name());
}
private String classname = DEFAULT_TAG;
public void setClassName(Class<?> c) {
classname = c.getSimpleName();
}
public String getClassname() {
return classname;
}
public boolean isError() {
return isEnabled(Level.error);
}
public boolean isWarn() {
return isEnabled(Level.warn);
}
public boolean isInfo() {
return isEnabled(Level.info);
}
public boolean isDebug() {
return isEnabled(Level.debug);
}
public boolean isTrace() {
return isEnabled(Level.trace);
}
public void error(Object... args) {
if (isError()) Log.e(buildTag(), build(args));
}
public void warn(Object... args) {
if (isWarn()) Log.w(buildTag(), build(args));
}
public void info(Object... args) {
if (isInfo()) Log.i(buildTag(), build(args));
}
public void debug(Object... args) {
if (isDebug()) Log.d(buildTag(), build(args));
}
public void trace(Object... args) {
if (isTrace()) Log.v(buildTag(), build(args));
}
public void error(String msg, Throwable t) {
if (isError()) error(buildTag(), msg, stackToString(t));
}
public void warn(String msg, Throwable t) {
if (isWarn()) warn(buildTag(), msg, stackToString(t));
}
public void info(String msg, Throwable t) {
if (isInfo()) info(buildTag(), msg, stackToString(t));
}
public void debug(String msg, Throwable t) {
if (isDebug()) debug(buildTag(), msg, stackToString(t));
}
public void trace(String msg, Throwable t) {
if (isTrace()) trace(buildTag(), msg, stackToString(t));
}
private String buildTag() {
String tag ;
if (BuildConfig.DEBUG) {
StringBuilder b = new StringBuilder(20);
b.append(getClassname());
StackTraceElement stackEntry = Thread.currentThread().getStackTrace()[4];
if (stackEntry != null) {
b.append('.');
b.append(stackEntry.getMethodName());
b.append(':');
b.append(stackEntry.getLineNumber());
}
tag = b.toString();
} else {
tag = DEFAULT_TAG;
}
}
private String build(Object... args) {
if (args == null) {
return "null";
} else {
StringBuilder b = new StringBuilder(args.length * 10);
for (Object arg : args) {
if (arg == null) {
b.append("null");
} else {
b.append(arg);
}
}
return b.toString();
}
}
private String stackToString(Throwable t) {
ByteArrayOutputStream baos = new ByteArrayOutputStream(500);
baos.toString();
t.printStackTrace(new PrintStream(baos));
return baos.toString();
}
}
use like this:
Loggor log = new Logger();
Map foo = ...
List bar = ...
log.error("Foo:", foo, "bar:", bar);
// bad example (avoid something like this)
// log.error("Foo:" + " foo.toString() + "bar:" + bar);
In a very simple logging scenario, where you're literally just trying to write to console during development for debugging purposes, it might be easiest to just do a search and replace before your production build and comment out all the calls to Log or System.out.println.
For example, assuming you didn't use the "Log." anywhere outside of a call to Log.d or Log.e, etc, you could simply do a find and replace across the entire solution to replace "Log." with "//Log." to comment out all your logging calls, or in my case I'm just using System.out.println everywhere, so before going to production I'll simply do a full search and replace for "System.out.println" and replace with "//System.out.println".
I know this isn't ideal, and it would be nice if the ability to find and comment out calls to Log and System.out.println were built into Eclipse, but until that happens the easiest and fastest and best way to do this is to comment out by search and replace. If you do this, you don't have to worry about mismatching stack trace line numbers, because you're editing your source code, and you're not adding any overhead by checking some log level configuration, etc.
In my apps I have a class which wraps the Log class which has a static boolean var called "state". Throughout my code I check the value of the "state" variable using a static method before actually writing to the Log. I then have a static method to set the "state" variable which ensures the value is common across all instances created by the app. This means I can enable or disable all logging for the App in one call - even when the App is running. Useful for support calls... It does mean that you have to stick to your guns when debugging and not regress to using the standard Log class though...
It's also useful (convenient) that Java interprets a boolean var as false if it hasn't been assigned a value, which means it can be left as false until you need to turn on logging :-)
We can use class Log in our local component and define the methods as v/i/e/d.
Based on the need of we can make call further.
example is shown below.
public class Log{
private static boolean TAG = false;
public static void d(String enable_tag, String message,Object...args){
if(TAG)
android.util.Log.d(enable_tag, message+args);
}
public static void e(String enable_tag, String message,Object...args){
if(TAG)
android.util.Log.e(enable_tag, message+args);
}
public static void v(String enable_tag, String message,Object...args){
if(TAG)
android.util.Log.v(enable_tag, message+args);
}
}
if we do not need any print(s), at-all make TAG as false for all else
remove the check for type of Log (say Log.d).
as
public static void i(String enable_tag, String message,Object...args){
// if(TAG)
android.util.Log.i(enable_tag, message+args);
}
here message is for string and and args is the value you want to print.
For me it is often useful being able to set different log levels for each TAG.
I am using this very simple wrapper class:
public class Log2 {
public enum LogLevels {
VERBOSE(android.util.Log.VERBOSE), DEBUG(android.util.Log.DEBUG), INFO(android.util.Log.INFO), WARN(
android.util.Log.WARN), ERROR(android.util.Log.ERROR);
int level;
private LogLevels(int logLevel) {
level = logLevel;
}
public int getLevel() {
return level;
}
};
static private HashMap<String, Integer> logLevels = new HashMap<String, Integer>();
public static void setLogLevel(String tag, LogLevels level) {
logLevels.put(tag, level.getLevel());
}
public static int v(String tag, String msg) {
return Log2.v(tag, msg, null);
}
public static int v(String tag, String msg, Throwable tr) {
if (logLevels.containsKey(tag)) {
if (logLevels.get(tag) > android.util.Log.VERBOSE) {
return -1;
}
}
return Log.v(tag, msg, tr);
}
public static int d(String tag, String msg) {
return Log2.d(tag, msg, null);
}
public static int d(String tag, String msg, Throwable tr) {
if (logLevels.containsKey(tag)) {
if (logLevels.get(tag) > android.util.Log.DEBUG) {
return -1;
}
}
return Log.d(tag, msg);
}
public static int i(String tag, String msg) {
return Log2.i(tag, msg, null);
}
public static int i(String tag, String msg, Throwable tr) {
if (logLevels.containsKey(tag)) {
if (logLevels.get(tag) > android.util.Log.INFO) {
return -1;
}
}
return Log.i(tag, msg);
}
public static int w(String tag, String msg) {
return Log2.w(tag, msg, null);
}
public static int w(String tag, String msg, Throwable tr) {
if (logLevels.containsKey(tag)) {
if (logLevels.get(tag) > android.util.Log.WARN) {
return -1;
}
}
return Log.w(tag, msg, tr);
}
public static int e(String tag, String msg) {
return Log2.e(tag, msg, null);
}
public static int e(String tag, String msg, Throwable tr) {
if (logLevels.containsKey(tag)) {
if (logLevels.get(tag) > android.util.Log.ERROR) {
return -1;
}
}
return Log.e(tag, msg, tr);
}
}
Now just set the log level per TAG at the beginning of each class:
Log2.setLogLevel(TAG, LogLevels.INFO);
Another way is to use a logging platform that has the capabilities of opening and closing logs. This can give much of flexibility sometimes even on a production app which logs should be open and which closed depending on which issues you have
for example:
LumberJack
Shipbook (disclaimer: I'm the author of this package)
https://limxtop.blogspot.com/2019/05/app-log.html
Read this article please, where provides complete implement:
For debug version, all the logs will be output;
For release version, only the logs whose level is above DEBUG (exclude) will be output by default. In the meanwhile, the DEBUG and VERBOSE log can be enable through setprop log.tag.<YOUR_LOG_TAG> <LEVEL> in running time.