Skip to main content
Version: 2.0.x

SLF4J v1 bridge

It is possible to use zio-logging for SLF4J loggers, usually third-party non-ZIO libraries. To do so, import the zio-logging-slf4j-bridge module for SLF4J v1 (working with JDK8):

libraryDependencies += "dev.zio" %% "zio-logging-slf4j-bridge" % "2.1.15"

and use the Slf4jBridge.initialize layer when setting up logging:

import zio.logging.slf4j.Slf4jBridge

program.provideCustom(Slf4jBridge.initialize)

SLF4J logger name is stored in log annotation with key logger_name (zio.logging.loggerNameAnnotationKey), following log format

import zio.logging.slf4j.Slf4jBridge
import zio.logging.LoggerNameExtractor

val loggerName = LoggerNameExtractor.loggerNameAnnotationOrTrace
val loggerNameFormat = loggerName.toLogFormat()

may be used to get logger name from log annotation or ZIO Trace.


SLF4J bridge with custom logger can be setup:

import zio.logging.slf4j.Slf4jBridge
import zio.logging.consoleJsonLogger

val logger = Runtime.removeDefaultLoggers >>> consoleJsonLogger() >+> Slf4jBridge.initialize

NOTE You should either use zio-logging-slf4j to send all ZIO logs to an SLF4j provider (such as logback, log4j etc) OR zio-logging-slf4j-bridge to send all SLF4j logs to ZIO logging. Enabling both causes circular logging and makes no sense.

Examples

SLF4J bridge with JSON console logger

package zio.logging.slf4j.bridge

import zio.logging._
import zio.{ ExitCode, LogLevel, Runtime, Scope, ZIO, ZIOAppArgs, ZIOAppDefault, ZLayer }

import java.util.UUID

object Slf4jBridgeExampleApp extends ZIOAppDefault {

private val slf4jLogger = org.slf4j.LoggerFactory.getLogger("SLF4J-LOGGER")

private val logFilter: LogFilter[String] = LogFilter.logLevelByName(
LogLevel.Info,
"zio.logging.slf4j" -> LogLevel.Debug,
"SLF4J-LOGGER" -> LogLevel.Warning
)

override val bootstrap: ZLayer[ZIOAppArgs, Any, Any] =
Runtime.removeDefaultLoggers >>> consoleJsonLogger(
ConsoleLoggerConfig(
LogFormat.label(
"name",
LoggerNameExtractor.loggerNameAnnotationOrTrace.toLogFormat()
) + LogFormat.logAnnotation(LogAnnotation.UserId) + LogFormat.logAnnotation(
LogAnnotation.TraceId
) + LogFormat.default,
logFilter
)
) >+> Slf4jBridge.initialize

private val uuids = List.fill(2)(UUID.randomUUID())

override def run: ZIO[Scope, Any, ExitCode] =
for {
_ <- ZIO.logInfo("Start")
_ <- ZIO.foreachPar(uuids) { u =>
ZIO.succeed(slf4jLogger.warn("Test {}!", "WARNING")) @@ LogAnnotation.UserId(
u.toString
)
} @@ LogAnnotation.TraceId(UUID.randomUUID())
_ <- ZIO.logDebug("Done")
} yield ExitCode.success

}

Expected Console Output:

{"name":"zio.logging.slf4j.bridge.Slf4jBridgeExampleApp","timestamp":"2023-05-15T20:14:20.712983+02:00","level":"INFO","thread":"zio-fiber-6","message":"Start"}
{"name":"SLF4J-LOGGER","user_id":"81e517bb-c69b-4187-a6e9-9911c427994c","trace_id":"bd317853-2b88-43d3-84dc-109e7e0eba70","timestamp":"2023-05-15T20:14:20.76863+02:00 ","level":"WARN","thread":"zio-fiber-9","message":"Test WARNING!"}
{"name":"SLF4J-LOGGER","user_id":"844f97ef-7f09-469b-9f4b-765887beea9a","trace_id":"bd317853-2b88-43d3-84dc-109e7e0eba70","timestamp":"2023-05-15T20:14:20.768628+02:00","level":"WARN","thread":"zio-fiber-10","message":"Test WARNING!"}
{"name":"zio.logging.slf4j.bridge.Slf4jBridgeExampleApp","timestamp":"2023-05-15T20:14:20.777529+02:00","level":"DEBUG","thread":"zio-fiber-6","message":"Done"}

Feature changes

Version 2.1.9

SLF4J logger name is stored in log annotation with key logger_name (zio.logging.loggerNameAnnotationKey), in previous versions, logger name was stored in log annotation with key slf4j_logger_name (Slf4jBridge.loggerNameAnnotationKey), for backward compatibility, if there is need to use legacy annotation key, it can be done with following initialisation

import zio.logging.slf4j.Slf4jBridge

program.provideCustom(Slf4jBridge.initialize(Slf4jBridge.loggerNameAnnotationKey))

NOTE: this feature may be removed in future versions

Following log format

import zio.logging.slf4j.Slf4jBridge
import zio.logging.LoggerNameExtractor

val loggerName = LoggerNameExtractor.annotationOrTrace(Slf4jBridge.loggerNameAnnotationKey)
val loggerNameFormat = loggerName.toLogFormat()

may be used to get logger name from log annotation or ZIO Trace.

This logger name extractor can be used also in log filter, which applying log filtering by defined logger name and level:

val logFilter: LogFilter[String] = LogFilter.logLevelByGroup(
LogLevel.Info,
loggerName.toLogGroup(),
"zio.logging.slf4j" -> LogLevel.Debug,
"SLF4J-LOGGER" -> LogLevel.Warning
)