A brief intro to SLF4J and the Java logging hell
Tags: kotlin, android, logging, programming
Published: 2022-09-04 18:06:08

## Intro

This will be breaking my chain of Articles written in Brazilian Portuguese, I've fallen into the habit of making my study notes in English to make them easier to share. I'll soon translate everything here and create a translated version!

## A brief history of logging in Java

At the dawn of Java *logging* still wasn’t as important as it is today, and as most languages of at that time everything boiled down to **writing lines to the console** and that was good enough! *Why do you need standards and fancy structures when memory is sparse, right?*

So it was common to see folks using the rather annoying `System.out` and `System.err` and doing their own solutions to write logs to files and other places!

### log4j is born

This is where the Open Source community first steps in! Since Java lacked any logging standard whatsoever and it’s “native logger” (being generous here given it was `err` and `out`...)  was rather poor when it came to features the community itself rose to the challenge and **log4j** was created as a mean to provide **extensible, configurable and standardized logging for Java.**

Nowadays log4j is infamous due to the [security incident](https://www.ncsc.gov.uk/information/log4j-vulnerability-what-everyone-needs-to-know) that wreaked havoc across the industry  back in December 2021, but at its time **it was a good thing**. Not perfect, but good!

Of course Sun, the Java owner back then, quickly got wind of a need for standards and native ways to log stuff so they decided to make that happen! 

### The ‘native’ library enters the fray

With JDK 1.4, in 2002, `java.util.logging` was released as a native offering for logging. But… it wasn’t well received. First it was hard to decouple and isolate, then *for some unknown reason* they also decided it’d be nice to reinvent the wheel and use a bunch of random names for log levels instead of the standardized ones the industry had. Thanks to that you had pearls such as being able to `log.fine("this is fine")` but also being able to `log.finer("this is even finer!")` and not to mention the best of all `log.finest("really great logging levels, innit?")`... as you may have notice the way they decided to standardize log levels was quite `log.severe("not good")`!

<aside>
🤦🏻 Worth noting that up to this day the `java.util.logging` library keeps this standard due to backwards compatibility. So even when doing Kotlin stuff in Android you can still log great `fine` and `severe` messages if you wish!

</aside>

### I need to switch loggers! Now what?

Now that there were two ‘popular’ contenders a new problem began to surface: What if I want to move my project from the amazing `java.util.logging` library into `log4j`?

Since both libraries were coupled to their own implementation that meant you’d need to refactor your whole code at once *or* roll with two logging libraries for a while. You could also create your own interfaces and implementations to hide the logging details from caller! And that’s what the community did!

Apache `common-logging` was born to provide means to abstract the implementation details of any logging library and provide a **common, standard, API that any Application may use without caring who’s writing the logs** behind the curtains! It had its shortcomings and wasn’t that widely adopted, but it’s idea lived on to see a better implementation be born. Enter **SLF4J.**

## SLF4J to the Rescue

**Simple Logging Facade for Java (SLF4J for short)** is an out-of-the-box set of `Interface` and Abstractions that aim to **consolidate and standardize logging in the JVM environment, while allowing decoupling.**

The decoupling is achieved because **you need only to rely on SLF4J interfaces** during our implementation. This wizardry is achievable **because logging libraries itself need to provide “Wrappers” or “Adapters”** **that plug into SLF4J abstractions** to then deliver their logging functionalities.

```kotlin
import org.slf4j.Logger
import org.slf4j.LoggerFactory

/**
 * get a SL4J logger and do something
 */
fun getLoggerAndLog() {
    val logger: Logger = LoggerFactory.getLogger("my logger") // note that this is a SLF4J Logger Interface!
    // whichever library is 'wrapped' by SLF4J now handles the levels, formatting and everything!
    logger.debug("this is a debug message")
    logger.info("this is some information")
    logger.warn("this is a warning!")
    logger.error("this is an error")
}
```

In practical terms what happens here is that you need at least two dependencies to be added to your project for this to work:

1. `'org.slf4j:slf4j-api'` for the SLF4J interfaces
2. `log4j-slf4j18-impl`, `com.github.tony19:logback-android`, `ch.qos.logback:logback-classic` for the SLF4J ‘providers’

The provides themselves use `Reflection` and other technologies to “tie” into the `LoggerFactory` class and routines provided by SLF4J!

### What is a “Facade”

The “Facade” within SLF4J stands for the [Facade Design Pattern](https://refactoring.guru/design-patterns/facade). In a nutshell **a Facade attempts to provide a simpler (and common) access method to complex systems beneath it.** This is good because it **can allow for decoupling between clients of those subsystems and themselves while also reducing the need to completely understand those subsystems!**

Imagine you want to buy something online. As a developer you know that you’re only inputting information in a website (or a mobile app) but you also know that *there’s something beneath it*. Beneath every ecommerce out there there’ll be APIs, Messaging and Databases! **In a way you could say that the Frontend is a Facade for everything that happens underneath.** As an online shopper you don’t need to know about which API calls are happening and which Messages are being sent over RabbitMQ, you **just care that interacting with the ‘buy’ routine does all the magic for you.** 

### Sample - Logback on Android!

First things first: Let’s start by adding the SLF4J dependency to our Application’s Gradle file:

```groovy
/* snip */
dependencies {
		/* Adding SLF4J's Interfaces */
    implementation 'org.slf4j:slf4j-api:1.7.36'
}
```

Now we’d need to add Logback’s library, but since there are some additional steps required to run Logback on Android we can use a forked version that does all the extra work for us already: [Logback-android](https://github.com/tony19/logback-android). Now, to get Logback working we’ll need to first add the dependency and add the logback `xml` configuration to the application’s `assets`:

```groovy
/* snip */
dependencies {
    implementation 'org.slf4j:slf4j-api:1.7.36'
		/* Logback-android */
    implementation 'com.github.tony19:logback-android:2.0.0'
}
```

```xml
<configuration>
  <appender name="logcat" class="ch.qos.logback.classic.android.LogcatAppender">
    <tagEncoder>
      <pattern>%logger{12}</pattern>
    </tagEncoder>
    <encoder>
      <pattern>[%-20thread] %msg</pattern>
    </encoder>
  </appender>

  <root level="DEBUG">
    <appender-ref ref="logcat" />
  </root>
</configuration>
```

Now all that’s left is to request a `Logger` by invoking the `LoggerFactory.GetLogger` method from SLF4J and you can Log away!

```kotlin
lateinit var log: Logger

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    log = LoggerFactory.getLogger("test")
    log.debug("Initializing, now with 101% more HILT")
    setContent {
        AppCanvas {
            Greeting(log)
        }
    }
}
```

## Additional Resources

Complete notes available on [my Notion workspace](https://ardc-overflow.notion.site/Java-Logging-Hell-SLF4J-to-the-Rescue-005e136d1dac449eabc9fdcbd8f2ec36).

This post's cover is from https://undraw.co/