Coder Thoughts on software, technology and programming.

Piotr Mionskowski

Introducing slf4android - a simple slf4j implementation for android

01 November 2014 android

Every now and then you have a bug that is hard to reproduce or only happens on certain phones or android versions. The thing that really comes handy in such case is a detailed application log. That’s why it’s so important to take time to add useful log entries in every non trivial part of the codebase. At the very minimum you’ll want to log any errors.

Logging frameworks

That’s why it’s so important to create log entries easily. The default solution that comes with Android by means of Log is the most commonly used. However for me it’s really the least pleasant to use:

Log.e("MyTag", "Failed to download " + url +  "due to occurred " + ex);

I really don’t like that I have to specify a tag each time I need to log something. Moreover having to concatenate strings seems tedious and error prone.

Logging with java.util.logging

Another alternative that is available by default on android is packaged inside java.util.logging.*. And here’s an example of how to use it:

    private static final Logger LOG = Logger.getLogger(MyActivity.class.getName());
    // and then
    LOG.log(Level.FINE, "Starting activity {0} saved instance {1}", new Object[]{this, savedInstanceState});

You’re able to use MessageFormatter style to format log entries. However no variable arguments method overload makes it both harder to read and write. More importantly by default if you use above statement the message will not be printed anywhere. It’s easy to fix when you know where to look for.

Powerful logback

logback is probably the most powerful and configurable logging framework. Among many features you can for example send an email with 50 last log entries - I’ve used it in one project, it can be a bit hard to configure but it really comes handy during testing. In order to use it on android one needs to use a ported version logback-android. There is one caveat though - this library is costs about 512kB - and takes about 4200 out of dex method limit.

Simple android-logger

android-logger is a small (<50KB) library that let’s you use slf4j api to print to logcat. It ships with various configuration options that let you change the format of output messages as well as log level based on hierarchical logger names. However you won’t be able to print messages to an additional file and you can only configure the logger through properties files.

slf4android

Since I wasn’t perfectly happy with above and because some design decisions made in android-logger make it not so easy to add features like logging to a file, creating custom patterns and configuring it from code I decided to create yet another logging utility.

It’s a tiny wrapper around slf4j api baked by the java.util.logging logger mechanism. This means you can easily hook in any existing java.util.logging.Handler implementations.

To use this little gem you’ll need to add http://bright.github.io/maven-repo/ to repository list:

repositories {
    maven {
        url "http://bright.github.io/maven-repo/"
    }
}

and then declare a dependency inside a module:

dependencies {
    compile('pl.brightinventions:slf4android:[email protected]'){
      transitive = true
    }
    //other dependencies
}

As with any slf4j compatible implementation using slf4android looks like this:

class HomeActivity extends Activity {
    private static final Logger LOG = LoggerFactory.getLogger(HomeActivity.class.getSimpleName());

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        LOG.debug("Hello from {} saved instance state {}", this, savedInstanceState);
    }
}

Logging to a file

To print messages to a separate file just add:

LoggerConfiguration.configuration().addHandlerToLogger("", LoggerConfiguration.fileLogHandler(this));

inside your custom android.app.Application onCreate method. This will create rotated log files inside context.getApplicationInfo().dataDir with a name derived from context.getPackageName() and a default message pattern %date %level [%thread] %name - %message%newline

More features

slf4android let’s you register custom message patterns and configure logging level - although the api for that is still rough around the edges. It also features a simple (and not well tested) mechanism for error reporting that, when enabled, will display a Dialog prompting tester to notify developer through email whenever an error is encountered. You can enable it with:

LoggerConfiguration.configuration().notifyDeveloperWithLogcatDataHandler(applicationContext, "[email protected]")

and receive emails with attached logcat output which comes handy during development.