Logging can sometimes be seen as the tool to be used as a last resort. You write tons of throwaway log lines such as ++code>"being here"++/code>, ++code>"user=23939"++/code>, 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.
Of course you can use the built-in Log class and its convenient static methods, ++code>d(),e(),i()++/code> ?
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.
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 ++code>[PREFIX].[FEATURE]++/code>
For instance for My Good App (MGA), you would define:
++code>TAG="mga.featureA"++/code>
++code>TAG="mga.featureB++/code>
You can add a ++code>module.kt++/code> at the root of a feature package containing a const like
++code>const val TAG = "mga.user"++/code>
This file can also contain other definitions for the module, such as dependency injection definitions.
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.
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.
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.
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!
Log content should be simple and reliable, and is not intended to be eye-candy or fun.
In other words don't give in to the siren song and keep-it super boring and simple.
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:
Try to use other tools such as Facebook Flipper or simply dedicated terminal windows
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:
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 :
++code>$ adb logcat | grep - line-buffered "mga.network"++/code>
Want to know all emitted analytics events ?
++code>$ adb logcat | grep - line-buffered "mga.analytics"++/code>
Finally, you are working with the location and booking modules and want to see only the logs related to these?
++code>$ adb logcat | grep - line-buffered "mga.location|mga.booking"++/code>
Of course it's always better to colorize your logs.
Logcat now provides this option:
++code>$ adb logcat -v color++/code>
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!
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.
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 ++code>Log++/code> statements using Proguard (see https://stackoverflow.com/a/2466662)
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.