Skip to content

Commit

Permalink
slf4j bridge - filter (#810)
Browse files Browse the repository at this point in the history
* slf4j-bridge - isEnabled - wip

* slf4j-bridge - filter test

* fmt

* examples

* slf4j-bridge - init with config

* slf4j-bridge - layers, docs

* fix
  • Loading branch information
justcoon authored Feb 16, 2024
1 parent 89a937c commit fda85bb
Show file tree
Hide file tree
Showing 19 changed files with 478 additions and 113 deletions.
5 changes: 4 additions & 1 deletion core/shared/src/main/scala/zio/logging/LogFilter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
package zio.logging

import zio.prelude.Equal
import zio.{ Cause, Config, FiberId, FiberRefs, LogLevel, LogSpan, Trace }
import zio.{ Cause, Config, FiberId, FiberRefs, LogLevel, LogSpan, NonEmptyChunk, Trace, ZIO }

import scala.annotation.tailrec

Expand Down Expand Up @@ -278,6 +278,9 @@ object LogFilter {
}

implicit val equal: Equal[LogLevelByNameConfig] = Equal.default

def load(configPath: NonEmptyChunk[String]): ZIO[Any, Config.Error, LogLevelByNameConfig] =
ZIO.config(LogLevelByNameConfig.config.nested(configPath.head, configPath.tail: _*))
}

def apply[M, V](
Expand Down
40 changes: 28 additions & 12 deletions docs/slf4j1-bridge.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,23 @@ It is possible to use `zio-logging` for SLF4J loggers, usually third-party non-Z
libraryDependencies += "dev.zio" %% "zio-logging-slf4j-bridge" % "@VERSION@"
```

and use the `Slf4jBridge.initialize` layer when setting up logging:
and use one of the `Slf4jBridge` layers when setting up logging:

```scala
import zio.logging.slf4j.Slf4jBridge

program.provideCustom(Slf4jBridge.initialize)
program.provideCustom(Slf4jBridge.init())
```

`Slf4jBridge` layers:
* `Slf4jBridge.init(configPath: NonEmptyChunk[String] = logFilterConfigPath)` - setup with `LogFilter` from [filter configuration](log-filter.md#configuration), default configuration path: `logger.filter`, default `LogLevel` is `INFO`
* `Slf4jBridge.init(filter: LogFilter[Any])` - setup with given `LogFilter`
* `Slf4jBridge.initialize` - setup without filtering

Need for log filtering in slf4j bridge: libraries with slf4j loggers, may have conditional logic for logging,
which using functions like [org.slf4j.Logger.isTraceEnabled()](https://github.com/qos-ch/slf4j/blob/master/slf4j-api/src/main/java/org/slf4j/Logger.java#L170).
logging parts may contain message and log parameters construction, which may be expensive and degrade performance of application.

<br/>

SLF4J logger name is stored in log annotation with key `logger_name` (`zio.logging.loggerNameAnnotationKey`), following log format
Expand All @@ -38,7 +47,7 @@ SLF4J bridge with custom logger can be setup:
import zio.logging.slf4j.Slf4jBridge
import zio.logging.consoleJsonLogger

val logger = Runtime.removeDefaultLoggers >>> consoleJsonLogger() >+> Slf4jBridge.initialize
val logger = Runtime.removeDefaultLoggers >>> consoleJsonLogger() >+> Slf4jBridge.init()
```
<br/>

Expand All @@ -55,7 +64,6 @@ ZIO logging. Enabling both causes circular logging and makes no sense.
```scala
package zio.logging.example

import zio.logging.slf4j.bridge.Slf4jBridge
import zio.logging.{ ConsoleLoggerConfig, LogAnnotation, LogFilter, LogFormat, LoggerNameExtractor, consoleJsonLogger }
import zio.{ ExitCode, LogLevel, Runtime, Scope, ZIO, ZIOAppArgs, ZIOAppDefault, ZLayer }

Expand All @@ -65,25 +73,33 @@ object Slf4jBridgeExampleApp extends ZIOAppDefault {

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

private val logFilterConfig = LogFilter.LogLevelByNameConfig(
LogLevel.Info,
"zio.logging.slf4j" -> LogLevel.Debug,
"SLF4J-LOGGER" -> LogLevel.Warning
)

private val logFormat = LogFormat.label(
"name",
LoggerNameExtractor.loggerNameAnnotationOrTrace.toLogFormat()
) + LogFormat.logAnnotation(LogAnnotation.UserId) + LogFormat.logAnnotation(
LogAnnotation.TraceId
) + LogFormat.default

private val loggerConfig = ConsoleLoggerConfig(logFormat, logFilterConfig)

override val bootstrap: ZLayer[ZIOAppArgs, Any, Any] =
Runtime.removeDefaultLoggers >>> consoleJsonLogger(
ConsoleLoggerConfig(logFormat, logFilterConfig)
) >+> Slf4jBridge.initialize
Runtime.removeDefaultLoggers >>> consoleJsonLogger(loggerConfig) >+> Slf4jBridge.init(loggerConfig.toFilter)

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(
ZIO.succeed(slf4jLogger.info("Test {}!", "INFO")) *> ZIO.succeed(
slf4jLogger.warn("Test {}!", "WARNING")
) @@ LogAnnotation.UserId(
u.toString
)
} @@ LogAnnotation.TraceId(UUID.randomUUID())
Expand All @@ -95,10 +111,10 @@ object Slf4jBridgeExampleApp extends ZIOAppDefault {

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"}
{"name":"zio.logging.slf4j.bridge.Slf4jBridgeExampleApp","timestamp":"2024-02-16T08:10:45.373807+01:00","level":"INFO","thread":"zio-fiber-6","message":"Start"}
{"name":"SLF4J-LOGGER","user_id":"d13f90ad-6b0a-45fd-bf94-1db7a0d8c0b7","trace_id":"561300a9-e6f1-4f61-8dcc-dfef476dab20","timestamp":"2024-02-16T08:10:45.421448+01:00","level":"WARN","thread":"zio-fiber-10","message":"Test WARNING!"}
{"name":"SLF4J-LOGGER","user_id":"0f28521f-ac8f-4d8e-beeb-13c85c90c041","trace_id":"561300a9-e6f1-4f61-8dcc-dfef476dab20","timestamp":"2024-02-16T08:10:45.421461+01:00","level":"WARN","thread":"zio-fiber-9","message":"Test WARNING!"}
{"name":"zio.logging.slf4j.bridge.Slf4jBridgeExampleApp","timestamp":"2024-02-16T08:10:45.428162+01:00","level":"DEBUG","thread":"zio-fiber-6","message":"Done"}
```

## Feature changes
Expand Down
34 changes: 22 additions & 12 deletions docs/slf4j2-bridge.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,23 @@ It is possible to use `zio-logging` for SLF4J loggers, usually third-party non-Z
libraryDependencies += "dev.zio" %% "zio-logging-slf4j2-bridge" % "@VERSION@"
```

and use the `Slf4jBridge.initialize` layer when setting up logging:
and use one of the `Slf4jBridge` layers when setting up logging:

```scala
import zio.logging.slf4j.Slf4jBridge

program.provideCustom(Slf4jBridge.initialize)
program.provideCustom(Slf4jBridge.init())
```

`Slf4jBridge` layers:
* `Slf4jBridge.init(configPath: NonEmptyChunk[String] = logFilterConfigPath)` - setup with `LogFilter` from [filter configuration](log-filter.md#configuration), default configuration path: `logger.filter`, default `LogLevel` is `INFO`
* `Slf4jBridge.init(filter: LogFilter[Any])` - setup with given `LogFilter`
* `Slf4jBridge.initialize` - setup without filtering

Need for log filtering in slf4j bridge: libraries with slf4j loggers, may have conditional logic for logging,
which using functions like [org.slf4j.Logger.isTraceEnabled()](https://github.com/qos-ch/slf4j/blob/master/slf4j-api/src/main/java/org/slf4j/Logger.java#L170).
logging parts may contain message and log parameters construction, which may be expensive and degrade performance of application.

<br/>

SLF4J logger name is stored in log annotation with key `logger_name` (`zio.logging.loggerNameAnnotationKey`), following log format
Expand Down Expand Up @@ -49,7 +58,7 @@ SLF4J bridge with custom logger can be setup:
import zio.logging.slf4j.Slf4jBridge
import zio.logging.consoleJsonLogger

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

<br/>
Expand All @@ -69,7 +78,6 @@ You can find the source code [here](https://github.com/zio/zio-logging/tree/mast
```scala
package zio.logging.example

import zio.logging.slf4j.bridge.Slf4jBridge
import zio.logging.{ ConsoleLoggerConfig, LogAnnotation, LogFilter, LogFormat, LoggerNameExtractor, consoleJsonLogger }
import zio.{ ExitCode, LogLevel, Runtime, Scope, ZIO, ZIOAppArgs, ZIOAppDefault, ZLayer }

Expand All @@ -92,18 +100,20 @@ object Slf4jBridgeExampleApp extends ZIOAppDefault {
LogAnnotation.TraceId
) + LogFormat.default

private val loggerConfig = ConsoleLoggerConfig(logFormat, logFilterConfig)

override val bootstrap: ZLayer[ZIOAppArgs, Any, Any] =
Runtime.removeDefaultLoggers >>> consoleJsonLogger(
ConsoleLoggerConfig(logFormat, logFilterConfig)
) >+> Slf4jBridge.initialize
Runtime.removeDefaultLoggers >>> consoleJsonLogger(loggerConfig) >+> Slf4jBridge.init(loggerConfig.toFilter)

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(
ZIO.succeed(slf4jLogger.info("Test {}!", "INFO")) *> ZIO.succeed(
slf4jLogger.warn("Test {}!", "WARNING")
) @@ LogAnnotation.UserId(
u.toString
)
} @@ LogAnnotation.TraceId(UUID.randomUUID())
Expand All @@ -115,8 +125,8 @@ object Slf4jBridgeExampleApp extends ZIOAppDefault {

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"}
{"name":"zio.logging.slf4j.bridge.Slf4jBridgeExampleApp","timestamp":"2024-02-16T08:10:45.373807+01:00","level":"INFO","thread":"zio-fiber-6","message":"Start"}
{"name":"SLF4J-LOGGER","user_id":"d13f90ad-6b0a-45fd-bf94-1db7a0d8c0b7","trace_id":"561300a9-e6f1-4f61-8dcc-dfef476dab20","timestamp":"2024-02-16T08:10:45.421448+01:00","level":"WARN","thread":"zio-fiber-10","message":"Test WARNING!"}
{"name":"SLF4J-LOGGER","user_id":"0f28521f-ac8f-4d8e-beeb-13c85c90c041","trace_id":"561300a9-e6f1-4f61-8dcc-dfef476dab20","timestamp":"2024-02-16T08:10:45.421461+01:00","level":"WARN","thread":"zio-fiber-9","message":"Test WARNING!"}
{"name":"zio.logging.slf4j.bridge.Slf4jBridgeExampleApp","timestamp":"2024-02-16T08:10:45.428162+01:00","level":"DEBUG","thread":"zio-fiber-6","message":"Done"}
```
Original file line number Diff line number Diff line change
Expand Up @@ -38,18 +38,20 @@ object Slf4jBridgeExampleApp extends ZIOAppDefault {
LogAnnotation.TraceId
) + LogFormat.default

private val loggerConfig = ConsoleLoggerConfig(logFormat, logFilterConfig)

override val bootstrap: ZLayer[ZIOAppArgs, Any, Any] =
Runtime.removeDefaultLoggers >>> consoleJsonLogger(
ConsoleLoggerConfig(logFormat, logFilterConfig)
) >+> Slf4jBridge.initialize
Runtime.removeDefaultLoggers >>> consoleJsonLogger(loggerConfig) >+> Slf4jBridge.init(loggerConfig.toFilter)

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(
ZIO.succeed(slf4jLogger.info("Test {}!", "INFO")) *> ZIO.succeed(
slf4jLogger.warn("Test {}!", "WARNING")
) @@ LogAnnotation.UserId(
u.toString
)
} @@ LogAnnotation.TraceId(UUID.randomUUID())
Expand Down
Loading

0 comments on commit fda85bb

Please sign in to comment.