An SLF4J backend that provides opinionated JSON structured logging for cloud-native environments.
It takes an SLF4J logging event and produces a JSON
structured log line including context information to the process STDOUT
.
It requires at least SLF4J version 2.0.0.
It supports Scala 2.13+ and Scala 3.1+
SLF4J is the de-facto standard for implementing backend agnostic logging within the JVM ecosystem. With log4j and logback, featureful backends exist that can fulfill the logging needs for most applications.
In cloud native environments, most of these features are often neither deeded nor desired. On the flip side, the complexity of these backends also come with the risks, legacy- and complexity tradeoffs that come with a large set of features.
The motivation behind this backend is to provide an implementation that provides the bare minimum functionality required.
SLF4J version 2 relies on the ServiceLoader mechanism to find its logging backend. Including the backend as a dependency or putting it into the classpath is sufficient for it to work.
Be aware that this backend does not provide any JSON serialization on its own. Instead, the application is supposed to ship its own dependencies and pick the flavor of backend that matches those dependencies.
For circe json, pick
ThisBuild / libraryDependencies += "de.spaceteams" %% "json-logging-circe" % <version>
For spray json, pick
ThisBuild / libraryDependencies += "de.spaceteams" %% "json-logging-spray" % <version>
Loggers and levels can be configured programatically.
val logger = LoggerFactory.getLogger("logger-name")
logger.asInstanceOf[JsonLogger].setLevel(Some(Level.INFO))
The backend does not come with any support for configuration files or similar mechanisms built-in.
If desired, a configuration system can be plugged in by providing a Service Provider
that implements the de.spaceteams.jsonlogging.ConfigurationService
interface.
The typesafe-config
sub-module is included as an example of how such a service provider might look like.
The typesafe-config
service provider can be used to apply simple configurations via the well-known Typesafe Config library that comes included in many scala frameworks.
To make use of it, include it as a dependency in your build.sbt
:
ThisBuild / libraryDependencies += "de.spaceteams" %% "json-logging-typesafe-config" % <version>
Logger configuration can then be defined in a projects application.conf
logging {
levels {
"de.spaceteams" = "TRACE"
"de.spaceteams.logging" = "DEBUG"
}
}
The backend supports the concept of a named hirarchy.
A logger is said to be an ancestor of another logger if its name followed by a dot is a prefix of the descendant logger name. A logger is said to be a parent of a child logger if there are no ancestors between itself and the descendant logger.
For example, java
is a parent of java.util
and an ancestor of java.util.Vector
. This naming scheme should be familiar to developers familiar with backends like log4j or logback
Likewise, logger levels propagate from parents to children if these do not have a level assigned themselves. This to should be familiar form how well known backends operate.
Logger name | Assigned level | Effective level |
---|---|---|
root | DEBUG | DEBUG |
X | none | DEBUG |
X.Y | INFO | INFO |
X.Y.Z | none | INFO |
X.Y.Z.A | ERROR | ERROR |
This backend has rudamentary support for adding context information via the MDC. Key / values pairs that are present in the MDC will be added as such to the log event context and appear in the resulting JSON line.
Global contextual information can be added to the logging context via environment variables.
Environment variables prefixed with LOGGING_CONTEXT_
will be added to the context map for every event, eg.
export LOGGING_CONTEXT_hostname="$(hostname)"
will add the hostname
key with the machines current host name to all log events, eg.
logger.info("test message")
produces a log line similar to
{ "hostname": "spacemachine", "message": "test message", "level": "INFO", ... }
The current version was not optimized for performance.