SLF4J 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:
libraryDependencies += "dev.zio" %% "zio-logging-slf4j-bridge" % "2.1.8"
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 slf4j_logger_name
(Slf4jBridge.loggerNameAnnotationKey
), 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
)
SLF4J bridge with custom logger can be setup:
import zio.logging.slf4j.Slf4jBridge
val logger = Runtime.removeDefaultLoggers >>> zio.logging.consoleJson(LogFormat.default, LogLevel.Debug) >+> 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 org.slf4j.{ Logger, LoggerFactory }
import zio.logging.{ LogFilter, LogFormat, LoggerNameExtractor, consoleJson }
import zio.{ ExitCode, LogLevel, Runtime, Scope, ZIO, ZIOAppArgs, ZIOAppDefault, ZLayer }
object Slf4jBridgeExampleApp extends ZIOAppDefault {
private val slf4jLogger: Logger = LoggerFactory.getLogger("SLF4J-LOGGER")
private val loggerName = LoggerNameExtractor.annotationOrTrace(Slf4jBridge.loggerNameAnnotationKey)
private val logFilter: LogFilter[String] = LogFilter.logLevelByGroup(
LogLevel.Info,
loggerName.toLogGroup(),
"zio.logging.slf4j" -> LogLevel.Debug,
"SLF4J-LOGGER" -> LogLevel.Warning
)
override val bootstrap: ZLayer[ZIOAppArgs, Any, Any] =
Runtime.removeDefaultLoggers >>> consoleJson(
LogFormat.label("name", loggerName.toLogFormat()) + LogFormat.default,
logFilter
) >+> Slf4jBridge.initialize
override def run: ZIO[Scope, Any, ExitCode] =
for {
_ <- ZIO.logDebug("Start")
_ <- ZIO.succeed(slf4jLogger.debug("Test {}!", "DEBUG"))
_ <- ZIO.succeed(slf4jLogger.warn("Test {}!", "WARNING"))
_ <- ZIO.logInfo("Done")
} yield ExitCode.success
}
Expected Console Output:
{"name":"zio.logging.slf4j.bridge.Slf4jBridgeExampleApp","timestamp":"2023-01-07T18:25:40.397593+01:00","level":"DEBUG","thread":"zio-fiber-4","message":"Start"}
{"name":"SLF4J-LOGGER","timestamp":"2023-01-07T18:25:40.416612+01:00","level":"WARN","thread":"zio-fiber-6","message":"Test WARNING!"}
{"name":"zio.logging.slf4j.bridge.Slf4jBridgeExampleApp","timestamp":"2023-01-07T18:25:40.42043+01:00 ","level":"INFO","thread":"zio-fiber-4","message":"Done"}