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!
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 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!
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>
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.
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.
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: