Easy Android logging with Kotlin and Timber

Overview

Kotlin logging extensions for Timber

Jake Wharton's Timber library is great. It's a Java library with an API that works well for Java, but that isn't as idiomatic when used in Kotlin.

This library builds on Timber with an API that's easier to use from Kotlin. Instead of using formatting parameters, you pass a lambda that is only evaluated if the message is logged.

Usage

  1. Configure any Tree instances in your Application's onCreate, the same way as with plain Timber.
  2. Call the extension functions from anywhere in your code.
// Standard timber
Timber.d("%d %s", intVar + 3, stringFun())

// Kotlin extensions
Timber.d { "${intVar + 3} ${stringFun()}" }
// or
d { "${intVar + 3} ${stringFun()}" }

The same message and tags will be logged in all three cases.

The Kotlin extensions have the advantage of being more convenient to write, and are also more performant in some circumstances. The passed block is only evaluated if the message is logged, and even if the message is logged to multiple trees, the block is only evaluated once. All extension methods are inlined, so there is no method count penalty to using this library.

Logging exception objects works the same way:

// Standard timber
Timber.e(exception, "%d exceptions", errorCount)

// Kotlin extensions
Timber.e(exception) { "$errorCount exceptions" }
// or
e(exception) { "$errorCount exceptions" }

What about Timber's custom lint checks?

Timber comes with half a dozen lint checks that help you spot incorrect usage of the log calls.

With the exception of long custom tags, none of the errors those checks look for are possible with this library. You can perform arbitrary code inside of the lambdas passed to the log extensions, and there's no risk of performance problems in your release code since the blocks won't be evaluated unless the messages are printed.

Download

The Kotlin extensions for Timber are distributed with Maven Central, JCenter and JitPack.

implementation 'com.github.ajalt:timberkt:1.5.1'

Documentation

The documentation is hosted online here.

License

Copyright 2017-2018 AJ Alt

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Comments
  • Timber object should also support string arguments

    Timber object should also support string arguments

    The Timber object only supports lambda syntax: Timber.d { "some string" }. Even though it's effectively the same thing, it should also support the same syntax as the original Timber: Timber.d("some string").

    opened by bj0 8
  • [Question] Why are the extension methods on Timber's Tree object?

    [Question] Why are the extension methods on Timber's Tree object?

    I noticed that the extension methods in timberkt are on Timber's Tree class: Timber.Tree.d(...) Why is this the case? Why aren't they on the Timber class directly? Because of this I have to import your Timber object instead of using the regular Timber class. Thanks!

    opened by Plastix 2
  • Provide a proxy to timber.log.Timber.DebugTree

    Provide a proxy to timber.log.Timber.DebugTree

    If com.github.ajalt.timberkt.Timber is imported in the class you wish to plant the Timber.DebugTree, then you have to use a qualified import e.g. Timber.plant(timber.log.Timber.DebugTree()).

    It would be nice if com.github.ajalt.timberkt.Timber contained a function to return timber.log.Timber.DebugTree so that the code could read Timber.plant(Timber.DebugTree()).

    opened by JoshuaJamesOng 1
  • Timber tree subclass

    Timber tree subclass

    so can i get rid of my Timber java logger in gradle and use yours ? I still have java and new kotlin files. i have a subclass of the timber tree i am using. can i swap it for yours and it will work in java calls as well ?

    can i do this: Timber.plant(timber.log.Timber.DebugTree())

    opened by j2emanue 0
  • Mocking framework

    Mocking framework

    Hi,

    hope find you well with this cold call.

    I am an author of mocking framework for Kotlin

    I see you are using mockito-kotlin.

    I just want you to be aware that there is solution that fully supports Kotlin and ask to try it in your new/current projects.

    I can help you if you answer to this issue.

    Thanks and please star it

    opened by oleksiyp 0
  • Make the throwable in error nullable

    Make the throwable in error nullable

    I am using an object that is containing an optional exception inside (http response), but that is nullable and I cannot use your wrapper around Timber for logging this optional exception.

    Wouldn't it be better if the throwable was nullable and simply call Timber.e(msg) if it as null?

    inline fun e(t: Throwable? = null, message: () -> String) = log { if (t==null) Timber.e(message()) else Timber.e(t, message()) }

    I think that even without with the null check we would be safe since the throwable in the Tree is nullable.

    opened by toteto 0
  • Lazy message evaluation all the way

    Lazy message evaluation all the way

    Lazy evaluation of the message is one of the selling points of TimberKt, BUT...

    Currently TimberKt doesn't care if a tag is loggable or not. It evaluates the message string and lets Timber do the dirty work. The message evaluation happens always, including cases when the log would be discarded.

    if (Timber.treeCount() > 0) is just not good enough for this case.

    Example: I have a CrashlyticsTree that logs just warnings and errors to Crashlytics. However, all log messages are evaluated just so that debug, info, and verbose could be dropped later.

    class CrashlyticsTree : Timber.Tree() {
    
        override fun isLoggable(tag: String?, priority: Int): Boolean = priority >= Log.WARN
    	
        override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {
            Crashlytics.log(message)
            t?.let { Crashlytics.logException(it) }
        }
    }
    

    Another example: Logging that's controlled at runtime. With a permanently attached tree.

    class AndroidTree : Timber.Tree() {
    
        override fun isLoggable(priority: Int): Boolean = Log.isLoggable("ASDF", priority)
    
        override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {
            // ...
        }
    }
    

    or something like this

    class AndroidTree(private val prefs: SharedPreferences) : Timber.Tree() {
    
        override fun isLoggable(priority: Int): Boolean = prefs.getBoolean("log", false)
    
        override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {
            // ...
        }
    }
    

    Solution 1)

    Wrap the message lambda in a object that's only evaluated when timber gets to format the output.

    private class MessageWrapper internal constructor(messageProvider: () -> String) {
        
        private val message by lazy(LazyThreadSafetyMode.NONE) { messageProvider() }
        
        override fun toString(): String = message
    }
    
    inline fun td1(throwable: Throwable? = null, noinline message: () -> String) {
        Timber.d(throwable, "%s", MessageWrapper(message))
    }
    
    inline fun td1(throwable: Throwable) {
        Timber.d(throwable)
    }
    
    inline fun td1(noinline message: () -> String) {
        Timber.d("%s", MessageWrapper(message))
    }
    

    This could be made more efficient maybe with inline classes or whatnot... Current cost of this is one extra wrapper object and one extra lazy delegate object per call.

    Solution 2)

    Well... totally copy Timber into TimberKt and adapt prepareLog method for Kotlin use style.

    private void prepareLog(int priority, Throwable t, String message, Object... args) {
      // Consume tag even when message is not loggable so that next message is correctly tagged.
      String tag = getTag();
    
      if (!isLoggable(tag, priority)) {
        return;
      }
      if (message != null && message.length() == 0) {
        message = null;
      }
      if (message == null) {
        if (t == null) {
          return; // Swallow message if it's null and there's no throwable.
        }
        message = getStackTraceString(t);
      } else {
        if (args != null && args.length > 0) {
          message = formatMessage(message, args);
        }
        if (t != null) {
          message += "\n" + getStackTraceString(t);
        }
      }
    
      log(priority, tag, message, t);
    }
    
    private fun prepareLog(priority: Int, t: Throwable?, messageProvider: () -> String?) {
        // ...
    }
    
    opened by consp1racy 2
  • Add @JvmOverloads to support optional arguments in Java

    Add @JvmOverloads to support optional arguments in Java

    For example replace:

    @JvmStatic inline fun i(t: Throwable? = null, message: () -> String) = log { Timber.i(t, message()) }
    

    with

    @JvmOverloads @JvmStatic inline fun i(t: Throwable? = null, message: () -> String) = log { Timber.i(t, message()) }
    

    Currently, we have to pass null explicitly in Java

    opened by lukaville 1
Releases(1.5.1)
Owner
AJ Alt
AJ Alt
Minimalistic and multiplatform logging for Kotlin

klog This project is not abandonned. It's just nothing to add. KLogging provides unified logging API, which you can use from Kotlin code targeted for

Lev Shagalov 11 Oct 3, 2022
Simple, fast, efficient logging facade for Android apps

µlog Simple, fast, and efficient logging facade for Android apps. Inspired by Timber and Logcat. Features Lazy message evaluation Pluggable backends (

Danny Lin 9 Oct 21, 2022
A simple, classic Kotlin MVI implementation based on coroutines with Android support, clean DSL and easy to understand logic

A simple, classic Kotlin MVI implementation based on coroutines with Android support, clean DSL and easy to understand logic

Nek.12 4 Oct 31, 2022
A simple and easy adapter for RecyclerView. You don't have to make adapters and view holders anymore. Slush will help you.

한국어 No more boilerplate adapters and view holders. Slush will make using RecyclerView easy and fast. The goal of this project is to make RecyclerView,

SeungHyun 26 Sep 13, 2022
Android Kotlin paged endpoints made easy

A smart and simple way to work with paged endpoints. To see an example of how to use it, check out the introducing Fountain posts: part one and part t

xmartlabs 171 Nov 15, 2022
Easy lightweight SharedPreferences library for Android in Kotlin using delegated properties

Easy lightweight SharedPreferences library for Android in Kotlin using delegated properties Idea Delegated properties in Kotlin allow you to execute a

null 25 Dec 27, 2022
An Easy-to-use Kotlin based Customizable Modules Collection with Material Layouts by BlackBeared.

Fusion By BlackBeared An Easy-to-use Kotlin based Customizable Library with Material Layouts by @blackbeared. Features Custom Floating Action Buttons

Sandip Savaliya 38 Oct 5, 2022
Kotlin parser library with an easy-to-use DSL

Pratt Library for parsing expressions and a beautiful Kotlin DSL Just define your operators and operands with the Kotlin DSL and the parser is ready!

furetur 9 Oct 17, 2022
🔨 Template for easy hosting of your Java/Kotlin libraries on GitHub

?? kotlin-jvm-library-template Another template for easy hosting your Java/Kotlin libraries on GitHub. Features boilerplate for Kotlin/Java projects w

Viktor 0 Jan 7, 2022
kinstall is an easy way to install gradle-based command-line kotlin projects that use the application plugin.

kinstall kinstall is an easy way to install gradle-based command-line kotlin projects that use the application plugin. use First, install kinstall its

david kilmer 0 Apr 24, 2022
:bouquet: An easy way to persist and run code block only as many times as necessary on Android.

Only ?? An easy way to persist and run code block only as many times as necessary on Android. Download Gradle Add below codes to your root build.gradl

Jaewoong Eum 479 Dec 25, 2022
:bouquet: An easy way to persist and run code block only as many times as necessary on Android.

Only ?? An easy way to persist and run code block only as many times as necessary on Android. Download Gradle Add below codes to your root build.gradl

Jaewoong Eum 468 Apr 14, 2021
:performing_arts: An easy, flexible way to implement veil skeletons and shimmering effect for Android.

AndroidVeil An easy, flexible way to implement veil skeletons and shimmering effect for Android. Download Gradle Add below codes to your root build.gr

Jaewoong Eum 1.2k Dec 28, 2022
Easy to use cryptographic framework for data protection: secure messaging with forward secrecy and secure data storage. Has unified APIs across 14 platforms.

Themis provides strong, usable cryptography for busy people General purpose cryptographic library for storage and messaging for iOS (Swift, Obj-C), An

Cossack Labs 1.6k Jan 8, 2023
A library with many useful and easy-to-use features

This library was made as a replacement for qLib and in the future cubed. These 2 plugins are hard to get you hands on and one of them has many outdated methods so this is a more modern version of those things

Max 1 May 6, 2022
Easy Android camera integration, advanced features.

CameraViewEx This is an extended version of Google's cameraview library with better stability and many more features. CameraViewEx highly simplifies i

Priyank Vasa 205 Dec 24, 2022
[Android Library] Get easy access to device information super fast, real quick

DeviceInfo-Sample Simple, single class wrapper to get device information from an android device. This library provides an easy way to access all the d

Anitaa Murthy 193 Nov 20, 2022
Android calendar library provides easy to use widget with events

Kotlin-AgendaCalendarView Kotlin-AgendaCalendarView based on AgendaCalendarView Kotlin-AgendaCalendarView is a awesome calendar widget with a list of

Ognev Zair 88 Nov 21, 2022
💡🚀⭐️ A generalized adapter for RecyclerView on Android which makes it easy to add heterogeneous items to a list

Mystique is a Kotlin library for Android’s RecyclerView which allows you to create homogeneous and heterogeneous lists effortlessly using an universal

Rahul Chowdhury 48 Oct 3, 2022