From fda85bbe7a138fe5b850a15e1e8f3634d1ad0adb Mon Sep 17 00:00:00 2001 From: Peter Kotula Date: Sat, 17 Feb 2024 00:22:25 +0100 Subject: [PATCH] slf4j bridge - filter (#810) * slf4j-bridge - isEnabled - wip * slf4j-bridge - filter test * fmt * examples * slf4j-bridge - init with config * slf4j-bridge - layers, docs * fix --- .../main/scala/zio/logging/LogFilter.scala | 5 +- docs/slf4j1-bridge.md | 40 +++++-- docs/slf4j2-bridge.md | 34 ++++-- .../example/Slf4jBridgeExampleApp.scala | 10 +- .../java/org/slf4j/helpers/ZioLoggerBase.java | 112 +++++++++++++----- .../scala/org/slf4j/impl/LoggerRuntime.scala | 2 + .../main/scala/org/slf4j/impl/ZioLogger.scala | 2 + .../org/slf4j/impl/ZioLoggerFactory.scala | 3 + .../logging/slf4j/bridge/Slf4jBridge.scala | 57 +++++++-- .../slf4j/bridge/ZioLoggerRuntime.scala | 19 ++- .../slf4j/bridge/Slf4jBridgeExampleApp.scala | 10 +- .../slf4j/bridge/Slf4jBridgeSpec.scala | 86 +++++++++++++- .../java/zio/logging/slf4j/bridge/Logger.java | 20 ++-- .../logging/slf4j/bridge/LoggerFactory.java | 8 ++ .../logging/slf4j/bridge/LoggerRuntime.java | 2 + .../logging/slf4j/bridge/Slf4jBridge.scala | 66 ++++++++--- .../slf4j/bridge/ZioLoggerRuntime.scala | 19 ++- .../slf4j/bridge/Slf4jBridgeExampleApp.scala | 10 +- .../slf4j/bridge/Slf4jBridgeSpec.scala | 86 +++++++++++++- 19 files changed, 478 insertions(+), 113 deletions(-) diff --git a/core/shared/src/main/scala/zio/logging/LogFilter.scala b/core/shared/src/main/scala/zio/logging/LogFilter.scala index 2b08663f..26c34de4 100644 --- a/core/shared/src/main/scala/zio/logging/LogFilter.scala +++ b/core/shared/src/main/scala/zio/logging/LogFilter.scala @@ -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 @@ -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]( diff --git a/docs/slf4j1-bridge.md b/docs/slf4j1-bridge.md index df921d35..65ac0821 100644 --- a/docs/slf4j1-bridge.md +++ b/docs/slf4j1-bridge.md @@ -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. +
SLF4J logger name is stored in log annotation with key `logger_name` (`zio.logging.loggerNameAnnotationKey`), following log format @@ -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() ```
@@ -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 } @@ -65,6 +73,12 @@ 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() @@ -72,10 +86,10 @@ 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()) @@ -83,7 +97,9 @@ object Slf4jBridgeExampleApp extends ZIOAppDefault { 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()) @@ -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 diff --git a/docs/slf4j2-bridge.md b/docs/slf4j2-bridge.md index 7ddddf5c..4f862b80 100644 --- a/docs/slf4j2-bridge.md +++ b/docs/slf4j2-bridge.md @@ -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. +
SLF4J logger name is stored in log annotation with key `logger_name` (`zio.logging.loggerNameAnnotationKey`), following log format @@ -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() ```
@@ -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 } @@ -92,10 +100,10 @@ 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()) @@ -103,7 +111,9 @@ object Slf4jBridgeExampleApp extends ZIOAppDefault { 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()) @@ -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"} ``` diff --git a/examples/slf4j2-bridge/src/main/scala/zio/logging/example/Slf4jBridgeExampleApp.scala b/examples/slf4j2-bridge/src/main/scala/zio/logging/example/Slf4jBridgeExampleApp.scala index d7043cca..bdcf4ab5 100644 --- a/examples/slf4j2-bridge/src/main/scala/zio/logging/example/Slf4jBridgeExampleApp.scala +++ b/examples/slf4j2-bridge/src/main/scala/zio/logging/example/Slf4jBridgeExampleApp.scala @@ -38,10 +38,10 @@ 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()) @@ -49,7 +49,9 @@ object Slf4jBridgeExampleApp extends ZIOAppDefault { 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()) diff --git a/slf4j-bridge/src/main/java/org/slf4j/helpers/ZioLoggerBase.java b/slf4j-bridge/src/main/java/org/slf4j/helpers/ZioLoggerBase.java index d5544ae9..e9c1ad1f 100644 --- a/slf4j-bridge/src/main/java/org/slf4j/helpers/ZioLoggerBase.java +++ b/slf4j-bridge/src/main/java/org/slf4j/helpers/ZioLoggerBase.java @@ -26,6 +26,8 @@ public ZioLoggerBase(String name) { abstract protected void log(Level level, Marker marker, String messagePattern, Object[] arguments, Throwable throwable); + abstract protected boolean isEnabled(String name, Level level); + private void logWithThrowable(Level level, Marker marker, String msg, Throwable t) { log(level, marker, msg, null, t); } @@ -54,140 +56,190 @@ private void logWithArgs(Level level, Marker marker, String msg, Object[] args) @Override public boolean isTraceEnabled() { - return true; + return isEnabled(name, Level.TRACE); } @Override public boolean isDebugEnabled() { - return true; + return isEnabled(name, Level.DEBUG); } @Override public boolean isErrorEnabled() { - return true; + return isEnabled(name, Level.ERROR); } @Override public boolean isWarnEnabled() { - return true; + return isEnabled(name, Level.WARN); } @Override public boolean isInfoEnabled() { - return true; + return isEnabled(name, Level.INFO); } @Override public void trace(String msg) { - logWithThrowable(Level.TRACE, null, msg, null); + if (isTraceEnabled()) { + logWithThrowable(Level.TRACE, null, msg, null); + } } @Override public void trace(String format, Object arg) { - logWithArg(Level.TRACE, null, format, arg); + if (isTraceEnabled()) { + logWithArg(Level.TRACE, null, format, arg); + } } @Override public void trace(String format, Object arg1, Object arg2) { - logWithArgs(Level.TRACE, null, format, arg1, arg2); + if (isTraceEnabled()) { + logWithArgs(Level.TRACE, null, format, arg1, arg2); + } } @Override public void trace(String format, Object... arguments) { - logWithArgs(Level.TRACE, null, format, arguments); + if (isTraceEnabled()) { + logWithArgs(Level.TRACE, null, format, arguments); + } } @Override public void trace(String msg, Throwable t) { - logWithThrowable(Level.TRACE, null, msg, t); + if (isTraceEnabled()) { + logWithThrowable(Level.TRACE, null, msg, t); + } } public void debug(String msg) { - logWithThrowable(Level.DEBUG, null, msg, null); + if (isDebugEnabled()) { + logWithThrowable(Level.DEBUG, null, msg, null); + } } public void debug(String format, Object arg) { - logWithArg(Level.DEBUG, null, format, arg); + if (isDebugEnabled()) { + logWithArg(Level.DEBUG, null, format, arg); + } } public void debug(String format, Object arg1, Object arg2) { - logWithArgs(Level.DEBUG, null, format, arg1, arg2); + if (isDebugEnabled()) { + logWithArgs(Level.DEBUG, null, format, arg1, arg2); + } } public void debug(String format, Object... arguments) { - logWithArgs(Level.DEBUG, null, format, arguments); + if (isDebugEnabled()) { + logWithArgs(Level.DEBUG, null, format, arguments); + } } public void debug(String msg, Throwable t) { - logWithThrowable(Level.DEBUG, null, msg, t); + if (isDebugEnabled()) { + logWithThrowable(Level.DEBUG, null, msg, t); + } } public void info(String msg) { - logWithThrowable(Level.INFO, null, msg, null); + if (isInfoEnabled()) { + logWithThrowable(Level.INFO, null, msg, null); + } } public void info(String format, Object arg) { - logWithArg(Level.INFO, null, format, arg); + if (isInfoEnabled()) { + logWithArg(Level.INFO, null, format, arg); + } } public void info(String format, Object arg1, Object arg2) { - logWithArgs(Level.INFO, null, format, arg1, arg2); + if (isInfoEnabled()) { + logWithArgs(Level.INFO, null, format, arg1, arg2); + } } public void info(String format, Object... arguments) { - logWithArgs(Level.INFO, null, format, arguments); + if (isInfoEnabled()) { + logWithArgs(Level.INFO, null, format, arguments); + } } public void info(String msg, Throwable t) { - logWithThrowable(Level.INFO, null, msg, t); + if (isInfoEnabled()) { + logWithThrowable(Level.INFO, null, msg, t); + } } public void warn(String msg) { - logWithThrowable(Level.WARN, null, msg, null); + if (isWarnEnabled()) { + logWithThrowable(Level.WARN, null, msg, null); + } } public void warn(String format, Object arg) { - logWithArg(Level.WARN, null, format, arg); + if (isWarnEnabled()) { + logWithArg(Level.WARN, null, format, arg); + } } public void warn(String format, Object arg1, Object arg2) { - logWithArgs(Level.WARN, null, format, arg1, arg2); + if (isWarnEnabled()) { + logWithArgs(Level.WARN, null, format, arg1, arg2); + } } public void warn(String format, Object... arguments) { - logWithArgs(Level.WARN, null, format, arguments); + if (isWarnEnabled()) { + logWithArgs(Level.WARN, null, format, arguments); + } } public void warn(String msg, Throwable t) { - logWithThrowable(Level.WARN, null, msg, t); + if (isWarnEnabled()) { + logWithThrowable(Level.WARN, null, msg, t); + } } public void error(String msg) { - logWithThrowable(Level.ERROR, null, msg, null); + if (isErrorEnabled()) { + logWithThrowable(Level.ERROR, null, msg, null); + } } public void error(String format, Object arg) { - logWithArg(Level.ERROR, null, format, arg); + if (isErrorEnabled()) { + logWithArg(Level.ERROR, null, format, arg); + } } public void error(String format, Object arg1, Object arg2) { - logWithArgs(Level.ERROR, null, format, arg1, arg2); + if (isErrorEnabled()) { + logWithArgs(Level.ERROR, null, format, arg1, arg2); + } } public void error(String format, Object... arguments) { - logWithArgs(Level.ERROR, null, format, arguments); + if (isErrorEnabled()) { + logWithArgs(Level.ERROR, null, format, arguments); + } } public void error(String msg, Throwable t) { - logWithThrowable(Level.ERROR, null, msg, t); + if (isErrorEnabled()) { + logWithThrowable(Level.ERROR, null, msg, t); + } } } \ No newline at end of file diff --git a/slf4j-bridge/src/main/scala/org/slf4j/impl/LoggerRuntime.scala b/slf4j-bridge/src/main/scala/org/slf4j/impl/LoggerRuntime.scala index e481f8e1..ac342f00 100644 --- a/slf4j-bridge/src/main/scala/org/slf4j/impl/LoggerRuntime.scala +++ b/slf4j-bridge/src/main/scala/org/slf4j/impl/LoggerRuntime.scala @@ -28,4 +28,6 @@ trait LoggerRuntime { arguments: Array[AnyRef], throwable: Throwable ): Unit + + def isEnabled(name: String, level: Level): Boolean } diff --git a/slf4j-bridge/src/main/scala/org/slf4j/impl/ZioLogger.scala b/slf4j-bridge/src/main/scala/org/slf4j/impl/ZioLogger.scala index 0fb052a9..29459812 100644 --- a/slf4j-bridge/src/main/scala/org/slf4j/impl/ZioLogger.scala +++ b/slf4j-bridge/src/main/scala/org/slf4j/impl/ZioLogger.scala @@ -28,4 +28,6 @@ final class ZioLogger(name: String, factory: ZioLoggerFactory) extends ZioLogger throwable: Throwable ): Unit = factory.log(name, level, marker, messagePattern, arguments, throwable) + + override protected def isEnabled(name: String, level: Level): Boolean = factory.isEnabled(name, level) } diff --git a/slf4j-bridge/src/main/scala/org/slf4j/impl/ZioLoggerFactory.scala b/slf4j-bridge/src/main/scala/org/slf4j/impl/ZioLoggerFactory.scala index 2bbe8ca5..5c920899 100644 --- a/slf4j-bridge/src/main/scala/org/slf4j/impl/ZioLoggerFactory.scala +++ b/slf4j-bridge/src/main/scala/org/slf4j/impl/ZioLoggerFactory.scala @@ -39,6 +39,9 @@ class ZioLoggerFactory extends ILoggerFactory { ): Unit = if (runtime != null) runtime.log(name, level, marker, messagePattern, arguments, throwable) + private[impl] def isEnabled(name: String, level: Level): Boolean = + if (runtime != null) runtime.isEnabled(name, level) else false + override def getLogger(name: String): Logger = loggers.getOrElseUpdate(name, new ZioLogger(name, this)) } diff --git a/slf4j-bridge/src/main/scala/zio/logging/slf4j/bridge/Slf4jBridge.scala b/slf4j-bridge/src/main/scala/zio/logging/slf4j/bridge/Slf4jBridge.scala index da30aa72..f0fb19cf 100644 --- a/slf4j-bridge/src/main/scala/zio/logging/slf4j/bridge/Slf4jBridge.scala +++ b/slf4j-bridge/src/main/scala/zio/logging/slf4j/bridge/Slf4jBridge.scala @@ -16,30 +16,61 @@ package zio.logging.slf4j.bridge import org.slf4j.impl.ZioLoggerFactory -import zio.{ Runtime, Semaphore, Unsafe, ZIO, ZLayer } +import zio.logging.LogFilter +import zio.{ Config, NonEmptyChunk, Runtime, Semaphore, Unsafe, ZIO, ZLayer } object Slf4jBridge { + val logFilterConfigPath: NonEmptyChunk[String] = zio.logging.loggerConfigPath :+ "filter" + /** * initialize SLF4J bridge */ - def initialize: ZLayer[Any, Nothing, Unit] = - Runtime.enableCurrentFiber ++ layer + def initialize: ZLayer[Any, Nothing, Unit] = init(LogFilter.acceptAll) + + /** + * initialize SLF4J bridge with `LogFilter` + * @param filter Log filter + */ + def init(filter: LogFilter[Any]): ZLayer[Any, Nothing, Unit] = Runtime.enableCurrentFiber ++ layer(filter) + + /** + * initialize SLF4J bridge with `LogFilter` from configuration + * @param configPath configuration path + */ + def init(configPath: NonEmptyChunk[String] = logFilterConfigPath): ZLayer[Any, Config.Error, Unit] = + Runtime.enableCurrentFiber ++ layer(configPath) /** * initialize SLF4J bridge without `FiberRef` propagation */ - def initializeWithoutFiberRefPropagation: ZLayer[Any, Nothing, Unit] = layer + def initializeWithoutFiberRefPropagation: ZLayer[Any, Nothing, Unit] = initWithoutFiberRefPropagation( + LogFilter.acceptAll + ) + + /** + * initialize SLF4J bridge with `LogFilter`, without `FiberRef` propagation + * @param filter Log filter + */ + def initWithoutFiberRefPropagation(filter: LogFilter[Any]): ZLayer[Any, Nothing, Unit] = layer(filter) private val initLock = Semaphore.unsafe.make(1)(Unsafe.unsafe) - private def layer: ZLayer[Any, Nothing, Unit] = - ZLayer { - for { - runtime <- ZIO.runtime[Any] - _ <- initLock.withPermit { - ZIO.succeed(ZioLoggerFactory.initialize(new ZioLoggerRuntime(runtime))) - } - } yield () - } + private def layer(filter: LogFilter[Any]): ZLayer[Any, Nothing, Unit] = + ZLayer(make(filter)) + + private def layer(configPath: NonEmptyChunk[String]): ZLayer[Any, Config.Error, Unit] = + ZLayer(make(configPath)) + + def make(filter: LogFilter[Any]): ZIO[Any, Nothing, Unit] = + for { + runtime <- ZIO.runtime[Any] + _ <- initLock.withPermit { + ZIO.succeed(ZioLoggerFactory.initialize(new ZioLoggerRuntime(runtime, filter))) + } + } yield () + + def make(configPath: NonEmptyChunk[String] = logFilterConfigPath): ZIO[Any, Config.Error, Unit] = + LogFilter.LogLevelByNameConfig.load(configPath).flatMap(c => make(c.toFilter)) + } diff --git a/slf4j-bridge/src/main/scala/zio/logging/slf4j/bridge/ZioLoggerRuntime.scala b/slf4j-bridge/src/main/scala/zio/logging/slf4j/bridge/ZioLoggerRuntime.scala index 864b2cd1..745d50ba 100644 --- a/slf4j-bridge/src/main/scala/zio/logging/slf4j/bridge/ZioLoggerRuntime.scala +++ b/slf4j-bridge/src/main/scala/zio/logging/slf4j/bridge/ZioLoggerRuntime.scala @@ -19,9 +19,10 @@ import org.slf4j.Marker import org.slf4j.event.Level import org.slf4j.helpers.MessageFormatter import org.slf4j.impl.LoggerRuntime +import zio.logging.LogFilter import zio.{ Cause, Fiber, FiberId, FiberRef, FiberRefs, LogLevel, Runtime, Trace, Unsafe } -final class ZioLoggerRuntime(runtime: Runtime[Any]) extends LoggerRuntime { +final class ZioLoggerRuntime(runtime: Runtime[Any], filter: LogFilter[Any]) extends LoggerRuntime { override def log( name: String, @@ -69,6 +70,22 @@ final class ZioLoggerRuntime(runtime: Runtime[Any]) extends LoggerRuntime { fiberRuntime.log(() => msg, cause, Some(logLevel), trace) } + + override def isEnabled(name: String, level: Level): Boolean = { + val logLevel = ZioLoggerRuntime.logLevelMapping(level) + + filter( + Trace(name, "", 0), + FiberId.None, + logLevel, + () => "", + Cause.empty, + FiberRefs.empty, + List.empty, + Map(zio.logging.loggerNameAnnotationKey -> name) + ) + } + } object ZioLoggerRuntime { diff --git a/slf4j-bridge/src/test/scala/zio/logging/slf4j/bridge/Slf4jBridgeExampleApp.scala b/slf4j-bridge/src/test/scala/zio/logging/slf4j/bridge/Slf4jBridgeExampleApp.scala index 489d8b44..60898855 100644 --- a/slf4j-bridge/src/test/scala/zio/logging/slf4j/bridge/Slf4jBridgeExampleApp.scala +++ b/slf4j-bridge/src/test/scala/zio/logging/slf4j/bridge/Slf4jBridgeExampleApp.scala @@ -22,10 +22,10 @@ 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()) @@ -33,7 +33,9 @@ object Slf4jBridgeExampleApp extends ZIOAppDefault { 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()) diff --git a/slf4j-bridge/src/test/scala/zio/logging/slf4j/bridge/Slf4jBridgeSpec.scala b/slf4j-bridge/src/test/scala/zio/logging/slf4j/bridge/Slf4jBridgeSpec.scala index d03247d8..89e9d14e 100644 --- a/slf4j-bridge/src/test/scala/zio/logging/slf4j/bridge/Slf4jBridgeSpec.scala +++ b/slf4j-bridge/src/test/scala/zio/logging/slf4j/bridge/Slf4jBridgeSpec.scala @@ -2,8 +2,9 @@ package zio.logging.slf4j.bridge import org.slf4j.MarkerFactory import org.slf4j.impl.StaticMarkerBinder +import zio.logging.LogFilter import zio.test._ -import zio.{ Cause, Chunk, LogLevel, ZIO, ZIOAspect } +import zio.{ Cause, Chunk, ConfigProvider, LogLevel, Runtime, ZIO, ZIOAspect } object Slf4jBridgeSpec extends ZIOSpecDefault { @@ -142,6 +143,87 @@ object Slf4jBridgeSpec extends ZIOSpecDefault { ) ) ) - }.provide(Slf4jBridge.initializeWithoutFiberRefPropagation) + }.provide(Slf4jBridge.initializeWithoutFiberRefPropagation), + test("logs through slf4j with filter") { + filterTest + }.provide( + Slf4jBridge.init( + LogFilter.logLevelByName( + LogLevel.Debug, + "test.logger" -> LogLevel.Info, + "test.test.logger" -> LogLevel.Warning + ) + ) + ), + test("logs through slf4j with filter from config") { + filterTest + }.provide { + val configProvider: ConfigProvider = ConfigProvider.fromMap( + Map( + "logger/filter/rootLevel" -> "DEBUG", + "logger/filter/mappings/test.logger" -> "INFO", + "logger/filter/mappings/test.test.logger" -> "WARN" + ), + "/" + ) + Runtime.setConfigProvider(configProvider) >>> Slf4jBridge.init() + } ) @@ TestAspect.sequential + + def filterTest: ZIO[Any, Nothing, TestResult] = + for { + _ <- (for { + logger1 <- ZIO.attempt(org.slf4j.LoggerFactory.getLogger("test.abc")) + logger2 <- ZIO.attempt(org.slf4j.LoggerFactory.getLogger("test.logger.def")) + logger3 <- ZIO.attempt(org.slf4j.LoggerFactory.getLogger("test.test.logger.xyz")) + _ <- ZIO.attempt(logger1.debug("test debug message")) + _ <- ZIO.attempt(logger1.warn("test warn message")) + _ <- ZIO.attempt(logger2.debug("hello2 {} debug", "world")) + _ <- ZIO.attempt(logger2.warn("hello2 {} warn", "world")) + _ <- ZIO.attempt(logger3.info("hello3 {} info", "world")) + _ <- ZIO.attempt(logger3.warn("hello3 {} warn", "world")) + } yield ()).exit + output <- ZTestLogger.logOutput + lines = output.map { logEntry => + LogEntry( + logEntry.spans.map(_.label), + logEntry.logLevel, + logEntry.annotations, + logEntry.message(), + logEntry.cause + ) + } + } yield assertTrue( + lines == Chunk( + LogEntry( + List("test.abc"), + LogLevel.Debug, + Map(zio.logging.loggerNameAnnotationKey -> "test.abc"), + "test debug message", + Cause.empty + ), + LogEntry( + List("test.abc"), + LogLevel.Warning, + Map(zio.logging.loggerNameAnnotationKey -> "test.abc"), + "test warn message", + Cause.empty + ), + LogEntry( + List("test.logger.def"), + LogLevel.Warning, + Map(zio.logging.loggerNameAnnotationKey -> "test.logger.def"), + "hello2 world warn", + Cause.empty + ), + LogEntry( + List("test.test.logger.xyz"), + LogLevel.Warning, + Map(zio.logging.loggerNameAnnotationKey -> "test.test.logger.xyz"), + "hello3 world warn", + Cause.empty + ) + ) + ) + } diff --git a/slf4j2-bridge/src/main/java/zio/logging/slf4j/bridge/Logger.java b/slf4j2-bridge/src/main/java/zio/logging/slf4j/bridge/Logger.java index 00dc666a..6d84bf34 100644 --- a/slf4j2-bridge/src/main/java/zio/logging/slf4j/bridge/Logger.java +++ b/slf4j2-bridge/src/main/java/zio/logging/slf4j/bridge/Logger.java @@ -40,51 +40,51 @@ protected void handleNormalizedLoggingCall(Level level, Marker marker, String me @Override public boolean isTraceEnabled() { - return true; + return factory.isEnabled(name, Level.TRACE); } @Override public boolean isTraceEnabled(Marker marker) { - return true; + return isTraceEnabled(); } @Override public boolean isDebugEnabled() { - return true; + return factory.isEnabled(name, Level.DEBUG); } @Override public boolean isDebugEnabled(Marker marker) { - return true; + return isDebugEnabled(); } @Override public boolean isInfoEnabled() { - return true; + return factory.isEnabled(name, Level.INFO); } @Override public boolean isInfoEnabled(Marker marker) { - return true; + return isInfoEnabled(); } @Override public boolean isWarnEnabled() { - return true; + return factory.isEnabled(name, Level.WARN); } @Override public boolean isWarnEnabled(Marker marker) { - return true; + return isWarnEnabled(); } @Override public boolean isErrorEnabled() { - return true; + return factory.isEnabled(name, Level.ERROR); } @Override public boolean isErrorEnabled(Marker marker) { - return true; + return isErrorEnabled(); } } diff --git a/slf4j2-bridge/src/main/java/zio/logging/slf4j/bridge/LoggerFactory.java b/slf4j2-bridge/src/main/java/zio/logging/slf4j/bridge/LoggerFactory.java index 230d0267..23ee925e 100644 --- a/slf4j2-bridge/src/main/java/zio/logging/slf4j/bridge/LoggerFactory.java +++ b/slf4j2-bridge/src/main/java/zio/logging/slf4j/bridge/LoggerFactory.java @@ -38,6 +38,14 @@ void log(String name, Level level, Marker marker, String messagePattern, Object[ } } + boolean isEnabled(String name, Level level) { + if (runtime != null) { + return runtime.isEnabled(name, level); + } else { + return false; + } + } + @Override public org.slf4j.Logger getLogger(String name) { return loggers.computeIfAbsent(name, n -> new Logger(n, this)); diff --git a/slf4j2-bridge/src/main/java/zio/logging/slf4j/bridge/LoggerRuntime.java b/slf4j2-bridge/src/main/java/zio/logging/slf4j/bridge/LoggerRuntime.java index 4354c881..522bda95 100644 --- a/slf4j2-bridge/src/main/java/zio/logging/slf4j/bridge/LoggerRuntime.java +++ b/slf4j2-bridge/src/main/java/zio/logging/slf4j/bridge/LoggerRuntime.java @@ -20,4 +20,6 @@ interface LoggerRuntime { void log(String name, Level level, Marker marker, String messagePattern, Object[] arguments, Throwable throwable); + + boolean isEnabled(String name, Level level); } diff --git a/slf4j2-bridge/src/main/scala/zio/logging/slf4j/bridge/Slf4jBridge.scala b/slf4j2-bridge/src/main/scala/zio/logging/slf4j/bridge/Slf4jBridge.scala index e22cd298..9240b145 100644 --- a/slf4j2-bridge/src/main/scala/zio/logging/slf4j/bridge/Slf4jBridge.scala +++ b/slf4j2-bridge/src/main/scala/zio/logging/slf4j/bridge/Slf4jBridge.scala @@ -15,34 +15,66 @@ */ package zio.logging.slf4j.bridge -import zio.{ Runtime, Semaphore, Unsafe, ZIO, ZLayer } +import zio.logging.LogFilter +import zio.{ Config, NonEmptyChunk, Runtime, Semaphore, Unsafe, ZIO, ZLayer } object Slf4jBridge { + val logFilterConfigPath: NonEmptyChunk[String] = zio.logging.loggerConfigPath :+ "filter" + /** * initialize SLF4J bridge */ - def initialize: ZLayer[Any, Nothing, Unit] = Runtime.enableCurrentFiber ++ layer + def initialize: ZLayer[Any, Nothing, Unit] = init(LogFilter.acceptAll) + + /** + * initialize SLF4J bridge with `LogFilter` + * @param filter Log filter + */ + def init(filter: LogFilter[Any]): ZLayer[Any, Nothing, Unit] = Runtime.enableCurrentFiber ++ layer(filter) + + /** + * initialize SLF4J bridge with `LogFilter` from configuration + * @param configPath configuration path + */ + def init(configPath: NonEmptyChunk[String] = logFilterConfigPath): ZLayer[Any, Config.Error, Unit] = + Runtime.enableCurrentFiber ++ layer(configPath) /** * initialize SLF4J bridge without `FiberRef` propagation */ - def initializeWithoutFiberRefPropagation: ZLayer[Any, Nothing, Unit] = layer + def initializeWithoutFiberRefPropagation: ZLayer[Any, Nothing, Unit] = initWithoutFiberRefPropagation( + LogFilter.acceptAll + ) + + /** + * initialize SLF4J bridge with `LogFilter`, without `FiberRef` propagation + * @param filter Log filter + */ + def initWithoutFiberRefPropagation(filter: LogFilter[Any]): ZLayer[Any, Nothing, Unit] = layer(filter) private val initLock = Semaphore.unsafe.make(1)(Unsafe.unsafe) - private def layer: ZLayer[Any, Nothing, Unit] = - ZLayer { - for { - runtime <- ZIO.runtime[Any] - _ <- initLock.withPermit { - ZIO.succeed( - org.slf4j.LoggerFactory - .getILoggerFactory() - .asInstanceOf[LoggerFactory] - .attachRuntime(new ZioLoggerRuntime(runtime)) - ) - } - } yield () - } + private def layer(filter: LogFilter[Any]): ZLayer[Any, Nothing, Unit] = + ZLayer(make(filter)) + + private def layer(configPath: NonEmptyChunk[String]): ZLayer[Any, Config.Error, Unit] = + ZLayer(make(configPath)) + + def make(filter: LogFilter[Any]): ZIO[Any, Nothing, Unit] = + for { + runtime <- ZIO.runtime[Any] + _ <- initLock.withPermit { + ZIO.succeed( + org.slf4j.LoggerFactory + .getILoggerFactory() + .asInstanceOf[LoggerFactory] + .attachRuntime(new ZioLoggerRuntime(runtime, filter)) + ) + } + } yield () + + def make(configPath: NonEmptyChunk[String] = logFilterConfigPath): ZIO[Any, Config.Error, Unit] = + LogFilter.LogLevelByNameConfig.load(configPath).flatMap(c => make(c.toFilter)) + } diff --git a/slf4j2-bridge/src/main/scala/zio/logging/slf4j/bridge/ZioLoggerRuntime.scala b/slf4j2-bridge/src/main/scala/zio/logging/slf4j/bridge/ZioLoggerRuntime.scala index a959b901..bbf43ab1 100644 --- a/slf4j2-bridge/src/main/scala/zio/logging/slf4j/bridge/ZioLoggerRuntime.scala +++ b/slf4j2-bridge/src/main/scala/zio/logging/slf4j/bridge/ZioLoggerRuntime.scala @@ -18,9 +18,10 @@ package zio.logging.slf4j.bridge import org.slf4j.Marker import org.slf4j.event.Level import org.slf4j.helpers.MessageFormatter +import zio.logging.LogFilter import zio.{ Cause, Fiber, FiberId, FiberRef, FiberRefs, LogLevel, Runtime, Trace, Unsafe } -final class ZioLoggerRuntime(runtime: Runtime[Any]) extends LoggerRuntime { +final class ZioLoggerRuntime(runtime: Runtime[Any], filter: LogFilter[Any]) extends LoggerRuntime { override def log( name: String, @@ -68,6 +69,22 @@ final class ZioLoggerRuntime(runtime: Runtime[Any]) extends LoggerRuntime { fiberRuntime.log(() => msg, cause, Some(logLevel), trace) } + + override def isEnabled(name: String, level: Level): Boolean = { + val logLevel = ZioLoggerRuntime.logLevelMapping(level) + + filter( + Trace(name, "", 0), + FiberId.None, + logLevel, + () => "", + Cause.empty, + FiberRefs.empty, + List.empty, + Map(zio.logging.loggerNameAnnotationKey -> name) + ) + } + } object ZioLoggerRuntime { diff --git a/slf4j2-bridge/src/test/scala/zio/logging/slf4j/bridge/Slf4jBridgeExampleApp.scala b/slf4j2-bridge/src/test/scala/zio/logging/slf4j/bridge/Slf4jBridgeExampleApp.scala index 06c7ff16..fec9497a 100644 --- a/slf4j2-bridge/src/test/scala/zio/logging/slf4j/bridge/Slf4jBridgeExampleApp.scala +++ b/slf4j2-bridge/src/test/scala/zio/logging/slf4j/bridge/Slf4jBridgeExampleApp.scala @@ -22,10 +22,10 @@ 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()) @@ -33,7 +33,9 @@ object Slf4jBridgeExampleApp extends ZIOAppDefault { 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()) diff --git a/slf4j2-bridge/src/test/scala/zio/logging/slf4j/bridge/Slf4jBridgeSpec.scala b/slf4j2-bridge/src/test/scala/zio/logging/slf4j/bridge/Slf4jBridgeSpec.scala index c4dd09bb..c052a8c4 100644 --- a/slf4j2-bridge/src/test/scala/zio/logging/slf4j/bridge/Slf4jBridgeSpec.scala +++ b/slf4j2-bridge/src/test/scala/zio/logging/slf4j/bridge/Slf4jBridgeSpec.scala @@ -1,7 +1,8 @@ package zio.logging.slf4j.bridge +import zio.logging.LogFilter import zio.test._ -import zio.{ Cause, Chunk, LogLevel, ZIO, ZIOAspect } +import zio.{ Cause, Chunk, ConfigProvider, LogLevel, Runtime, ZIO, ZIOAspect } object Slf4jBridgeSpec extends ZIOSpecDefault { @@ -137,6 +138,87 @@ object Slf4jBridgeSpec extends ZIOSpecDefault { ) ) ) - }.provide(Slf4jBridge.initializeWithoutFiberRefPropagation) + }.provide(Slf4jBridge.initializeWithoutFiberRefPropagation), + test("logs through slf4j with filter") { + filterTest + }.provide( + Slf4jBridge.init( + LogFilter.logLevelByName( + LogLevel.Debug, + "test.logger" -> LogLevel.Info, + "test.test.logger" -> LogLevel.Warning + ) + ) + ), + test("logs through slf4j with filter from config") { + filterTest + }.provide { + val configProvider: ConfigProvider = ConfigProvider.fromMap( + Map( + "logger/filter/rootLevel" -> "DEBUG", + "logger/filter/mappings/test.logger" -> "INFO", + "logger/filter/mappings/test.test.logger" -> "WARN" + ), + "/" + ) + Runtime.setConfigProvider(configProvider) >>> Slf4jBridge.init() + } ) @@ TestAspect.sequential + + def filterTest: ZIO[Any, Nothing, TestResult] = + for { + _ <- (for { + logger1 <- ZIO.attempt(org.slf4j.LoggerFactory.getLogger("test.abc")) + logger2 <- ZIO.attempt(org.slf4j.LoggerFactory.getLogger("test.logger.def")) + logger3 <- ZIO.attempt(org.slf4j.LoggerFactory.getLogger("test.test.logger.xyz")) + _ <- ZIO.attempt(logger1.debug("test debug message")) + _ <- ZIO.attempt(logger1.warn("test warn message")) + _ <- ZIO.attempt(logger2.debug("hello2 {} debug", "world")) + _ <- ZIO.attempt(logger2.warn("hello2 {} warn", "world")) + _ <- ZIO.attempt(logger3.info("hello3 {} info", "world")) + _ <- ZIO.attempt(logger3.warn("hello3 {} warn", "world")) + } yield ()).exit + output <- ZTestLogger.logOutput + lines = output.map { logEntry => + LogEntry( + logEntry.spans.map(_.label), + logEntry.logLevel, + logEntry.annotations, + logEntry.message(), + logEntry.cause + ) + } + } yield assertTrue( + lines == Chunk( + LogEntry( + List("test.abc"), + LogLevel.Debug, + Map(zio.logging.loggerNameAnnotationKey -> "test.abc"), + "test debug message", + Cause.empty + ), + LogEntry( + List("test.abc"), + LogLevel.Warning, + Map(zio.logging.loggerNameAnnotationKey -> "test.abc"), + "test warn message", + Cause.empty + ), + LogEntry( + List("test.logger.def"), + LogLevel.Warning, + Map(zio.logging.loggerNameAnnotationKey -> "test.logger.def"), + "hello2 world warn", + Cause.empty + ), + LogEntry( + List("test.test.logger.xyz"), + LogLevel.Warning, + Map(zio.logging.loggerNameAnnotationKey -> "test.test.logger.xyz"), + "hello3 world warn", + Cause.empty + ) + ) + ) + }