Logging in Kotlin

Hadi Lashkari Ghouchani
ProAndroidDev

--

Logging with Java libraries like Log4j, Slf4j or their wrappers like kotlin-logging is common, but those libraries are limited to Java restrictions. For instance, for creating the logger instance in Java libraries you have to pass the class instance of the current class like this

Logger logger = LoggerFactory.getLogger(HelloWorld.class);

Then you can start logging like this

logger.info("Hello World");

This way of accessing the class instance is old and is needed in Java implementations. Also you have another option, which is the Timber. The problem about this library is that it always calls .getStackTrace() which makes logging inefficient. Instead in Kotlin we can resolve the class instance of the current class easily by relying on inline and reified keywords.

Here in Sheypoor, we create a simple library to handle logs in Kotlin. Its source code is available here. To see how simplified is the logging with this library, for two lines above we just need to call a one letter method as following.

i("Hello World")

If you like it, continue to read.

Installation

For an Android application you can download it as follows.

implementation "com.log4k:log4k:${log4kVersion}"
implementation "com.log4k:log4k-android:${log4kVersion}"

After downloading you need to customize it for your need. For instance if you need to just use Android logs then in the onCreate method of your Application class, add the followings.

if (BuildConfig.DEBUG) {
Log4k.add(Level.Verbose, ".*", AndroidAppender())
}

In this line, we added the AndroidAppender when your app is running on the debug mode. This appender just logs the same as when in the above example you call Log.i of Android API. Also you can see that you have options to restrict the logging levels and packages that this appender is enable on. There is two rules for this purpose.

Basic Selection Rule

A log request of level p in a logger with level q, is enabled if p ≥ q.

This rule just claiming that if you add your appender with the log level of q and in some class we try to log with the level p, then only if p ≥ q the appender would be notified.

Match Inheritance Rule

An appender with a defined pattern is enabled if its pattern matches the full class name of the log request.

Which means the ".*" in the above example is a regular expression that matches to the full class name of the class that requested the log, so in this case, the appender would be notified for all log requests from any class. It’s useful when you want to restrict your appender to a specific packagename, which mimics the inheritance of loggers. More generally if you want to restrict your appender to a specific regex pattern, this is your way.

Also you can add more appenders, which makes this library so useful. For instance, in the release mode, you can add an appender to send your logs to some services, such as Crashlytics.

More Advanced logs

Did you ever have problem reading other people’s code because they didn’t mentioned what was the assumptions they had in mind when they wrote that piece of code? Or do you want to use !! assertion in Kotlin because they can clear your code and your assumptions, but you afraid by changing the code by other developers your assumptions about not-null variables could be break down so you can end up having a runtime crash because of !! assertions? Then we have a solution for you.

After adding this library to your project, just describe your assumptions in the starting point of the methods. It’s like contracts in Kotlin but more flexible, because they can be check in runtime, not just with code analysers. For instance, imagine you have an exampleMethod as follows.

Then in case of breaking of any of your assumptions above, this library will send a log request with Assert level to appenders that can handle it. This implies if you have an appender that can send this exception to a service like Crashlytics, then you would be notify of breaking your assumptions in the runtime and add a task for reviewing this method to solve the problem.

In the end, I want to mention that you can have more customize levels and logs with this library. Just checkout the code.

Happy logging!

--

--