Logging can sometimes be seen as the tool to be used as a last resort. You write tons of throwaway log lines such as
"user=23939", etc… and delete everything as soon as you understood the root cause of the bug. And the next time, you start again.
I suggest that logs can be useful on a daily basis if done correctly. Here are some tips to do better logging on your Android App.
How to log?
Of course you can use the built-in Log class and its convenient static methods,
But this may lack some flexibility.
An option is to create your own wrapper for this class and inject it into your classes. This will allow you to easily disable the logs in production build, redirect them to another output, or decorate the lines if needed.
Timber is a popular library which will allow you to do exactly that.
Use the relevant Log Level
You should try not to flood the most important log levels. For instance, you may target this kind of pace per log level :
Although it’s a popular pattern to use the class name as the log tag, I suggest you don’t use a class name for your tag.
First it’s leaking implementation details.
It also makes filtering more difficult based on this tag, because your feature will often be implemented with multiple classes. It can also lead to tag length greater than 23, which gets truncated in output.
You may use a pattern like
For instance for My Good App (MGA), you would define:
You can add a
module.kt at the root of a feature package containing a const like
const val TAG = "mga.user"
This file can also contain other definitions for the module, such as dependency injection definitions.
What to Log?
Important lifecycle events
Lots of Android apps bugs and mis-behaviors are related to the Android lifecycle. You should always be logging this kind of information. If you have a base Fragment inherited by all your Fragments (and you should!) use it to log these events.
This also has the advantage of leaving a trace of all navigation actions, and gives easy access to the current Fragment name. This can save a lot of time for a newcomer discovering the app and its code base.
Almost all of the user actions
All user actions should lead to something being printed into logs. This doesn’t mean that all the click handlers in the UI layer should print something, but that the action that is triggered in your domain layer should print something with some relevant parameters.
App version, buildFlavor
Use your app’s Application class to log some basic information about your App version and variant. This way you will be able to link a log to a specific version without ambiguity, even if some days or months have passed.
In addition to the previous point, also log dynamic configuration at startup and when it is changed. Since this can influence the behavior of your app, it is crucial to have this information to interpret the logs.
Remote Logging Solutions
Sometimes problems only happen on customer devices and you may need contextual information to figure out what is going on.
Remote logging is your friend in this situation.
Did you know that Firebase Crashlytics provides a way to log Non-fatal exceptions?
It should be enough to get more context on an inconsistent state that you can’t reproduce.
Should you need more contextual information, you could use the remote logging feature provided by Bugfender, which basically provides the same output as Logcat (ie. all the traces) as if you were plugging a USB debug cable in to your customer’s device!
Don’t use Unicode symbols
Log content should be simple and reliable, and is not intended to be eye-candy or fun.
You may have to copy-past it from various terminals, text editors and web sites that may not support UTF-8 well (or at all).
You will have to search through it, and it seems that looking for a text pattern instead of a random emojis will be more efficient.
In other words don’t give in to the siren song and keep-it super boring and simple.
Use more than Android Studio Log View
Although the Logcat tool window is one click away from your code and provides convenient filtering features, you may find it a little bit limited for advanced usage:
You can only have one instance of it
Filtering is handy but somehow limited
You may randomly lose all the content
Try to use other tools such as Facebook Flipper or simply dedicated terminal windows
Use a Terminal
As you pay more attention to logging, using dedicated terminal windows will become obvious.
First, you will be able to open several logging windows. Why would you want to do this? Because it will allow you to log different parts of your app in different windows.
You can have for instance:
Debug log for the feature currently in work
I also suggest always having an unfiltered Logcat window always running. It will record all the activity with infinite scroll history and will be helpful for transient and hard to reproduce crashes. Also sometimes Android system logs can help you figure out what’s going on.
If you are using meaningful tagging, you can then easily see all network logs using :
$ adb logcat | grep — line-buffered “mga.network”
Want to know all emitted analytics events ?
$ adb logcat | grep — line-buffered “mga.analytics”
Finally, you are working with the location and booking modules and want to see only the logs related to these?
$ adb logcat | grep — line-buffered “mga.location|mga.booking”
Colorize your logs
Of course it’s always better to colorize your logs.
Logcat now provides this option:
$ adb logcat -v color
You may also use a third-party wrapper such as logcat-color, that some people will find more readable.
Edit: You may also try pidcat which allow to filter logcat output by application package just like Android Studio!
Intend your logs for non-developer people
Do you think Logcat is for developers only?
You might want to consider having your logs readable by anybody involved in the project, particularly people involved in testing. Sooner or later you will have a bug that will take hours to reproduce and can be detected only by a logged statement, or you may have to leak some information (auth token, database ID…) to people in charge of testing without creating a dedicated “hidden diagnostic” menu entry. Should any of this occur, having your testing team used to read the typical Logcat output of your App may help a lot.
Remove your logs from your production build
Since logs can contain security and privacy sensitive information, it’s good practice to remove logs from your production build. That’s where custom log module or Timber come handy, but you can simply get rid of
Log statements using Proguard (see https://stackoverflow.com/a/2466662)
Always keep an eye on your logs
While developing and testing your app, you should always have an eye on your log. This way you will be able to detect unusual behavior more easily. Think about a bug where your app is repeating a network call every 5 seconds. This can be completely invisible from the UI point of view and can therefore pass the QA tests successfully. However, it can have dramatic consequences for your user’s battery and data plan, as well as your server.
A trained eye would have immediately noticed the unusual log flood on the idle app.
In conclusion, it’s quite easy to pay better attention to your App logs, and it can be of great help in critical times and also help you and your teammates be more effective on a daily basis.