Giter Site home page Giter Site logo

timberkt's Introduction

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.

timberkt's People

Contributors

ajalt avatar joshuajamesong avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

timberkt's Issues

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").

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.

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()).

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

[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!

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?) {
    // ...
}

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())

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

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.