From fecfddb739c47847665e734e46ee45c38b6073a0 Mon Sep 17 00:00:00 2001 From: Peter Kotula Date: Sat, 28 Jan 2023 19:53:00 +0100 Subject: [PATCH 01/14] slf4j2 --- build.sbt | 32 ++- .../main/scala/zio/logging/slf4j/SLF4J.scala | 250 ++++++++++++++++ slf4j2/src/test/resources/logback-test.xml | 24 ++ .../scala/zio/logging/backend/SLF4JSpec.scala | 272 ++++++++++++++++++ .../zio/logging/backend/TestAppender.scala | 70 +++++ 5 files changed, 641 insertions(+), 7 deletions(-) create mode 100644 slf4j2/src/main/scala/zio/logging/slf4j/SLF4J.scala create mode 100644 slf4j2/src/test/resources/logback-test.xml create mode 100644 slf4j2/src/test/scala/zio/logging/backend/SLF4JSpec.scala create mode 100644 slf4j2/src/test/scala/zio/logging/backend/TestAppender.scala diff --git a/build.sbt b/build.sbt index 116b3525..9f015671 100644 --- a/build.sbt +++ b/build.sbt @@ -22,10 +22,11 @@ inThisBuild( ) ) -val ZioVersion = "2.0.6" -val slf4jVersion = "1.7.36" -val slf4j2Version = "2.0.6" -val logbackVersion = "1.2.11" +val ZioVersion = "2.0.6" +val slf4jVersion = "1.7.36" +val slf4j2Version = "2.0.6" +val logbackVersion = "1.2.11" +val logback2Version = "1.4.5" addCommandAlias("fix", "; all compile:scalafix test:scalafix; all scalafmtSbt scalafmtAll") addCommandAlias("check", "; scalafmtSbtCheck; scalafmtCheckAll; compile:scalafix --check; test:scalafix --check") @@ -37,7 +38,7 @@ addCommandAlias( addCommandAlias( "testJVM", - ";coreJVM/test;slf4j/test;jpl/test;slf4jBridge/test;slf4j2Bridge/test" + ";coreJVM/test;slf4j/test;slf4j2/test;jpl/test;slf4jBridge/test;slf4j2Bridge/test" ) addCommandAlias( @@ -47,7 +48,7 @@ addCommandAlias( addCommandAlias( "mimaChecks", - "all coreJVM/mimaReportBinaryIssues slf4j/mimaReportBinaryIssues slf4jBridge/mimaReportBinaryIssues" + "all coreJVM/mimaReportBinaryIssues slf4j/mimaReportBinaryIssues slf4j2/mimaReportBinaryIssues slf4jBridge/mimaReportBinaryIssues" ) lazy val root = project @@ -55,7 +56,7 @@ lazy val root = project .settings( publish / skip := true ) - .aggregate(coreJVM, coreJS, slf4j, slf4jBridge, slf4j2Bridge, jpl, benchmarks, examples, docs) + .aggregate(coreJVM, coreJS, slf4j, slf4j2, slf4jBridge, slf4j2Bridge, jpl, benchmarks, examples, docs) lazy val core = crossProject(JSPlatform, JVMPlatform) .crossType(CrossType.Full) @@ -98,6 +99,23 @@ lazy val slf4j = project testFrameworks := Seq(new TestFramework("zio.test.sbt.ZTestFramework")) ) +lazy val slf4j2 = project + .in(file("slf4j2")) + .dependsOn(coreJVM) + .settings(stdSettings("zio-logging-slf4j2")) + .settings(mimaSettings(failOnProblem = true)) + .settings( + libraryDependencies ++= Seq( + "org.slf4j" % "slf4j-api" % slf4j2Version, + "dev.zio" %%% "zio-test" % ZioVersion % Test, + "dev.zio" %%% "zio-test-sbt" % ZioVersion % Test, + "ch.qos.logback" % "logback-classic" % logback2Version % Test, + "net.logstash.logback" % "logstash-logback-encoder" % "7.2" % Test, + "org.scala-lang.modules" %% "scala-collection-compat" % "2.9.0" % Test + ), + testFrameworks := Seq(new TestFramework("zio.test.sbt.ZTestFramework")) + ) + lazy val slf4jBridge = project .in(file("slf4j-bridge")) .dependsOn(coreJVM) diff --git a/slf4j2/src/main/scala/zio/logging/slf4j/SLF4J.scala b/slf4j2/src/main/scala/zio/logging/slf4j/SLF4J.scala new file mode 100644 index 00000000..c145a19b --- /dev/null +++ b/slf4j2/src/main/scala/zio/logging/slf4j/SLF4J.scala @@ -0,0 +1,250 @@ +/* + * Copyright 2019-2023 John A. De Goes and the ZIO Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package zio.logging.backend + +import org.slf4j.{ Logger, LoggerFactory, MDC, Marker, MarkerFactory } +import org.slf4j.event.Level +import zio.logging.internal.LogAppender +import zio.logging.{ LogFormat, LoggerNameExtractor } +import zio.{ Cause, FiberFailure, FiberId, FiberRefs, LogLevel, LogSpan, Runtime, Trace, ZIOAspect, ZLayer, ZLogger } + +import java.util + +object SLF4J { + + /** + * log aspect annotation key for slf4j logger name + */ + @deprecated + val loggerNameAnnotationKey = "logger_name" + + /** + * log aspect annotation key for slf4j marker name + */ + val logMarkerNameAnnotationKey = "slf4j_log_marker_name" + + /** + * default log format for slf4j logger + */ + val logFormatDefault: LogFormat = + LogFormat.allAnnotations(excludeKeys = + Set(loggerNameAnnotationKey, logMarkerNameAnnotationKey) + ) + LogFormat.line + LogFormat.cause + + /** + * slf4j logger name aspect, by this aspect is possible to change default logger name (default logger name is extracted from [[Trace]]) + * + * annotation key: [[SLF4J.loggerNameAnnotationKey]] + */ + @deprecated + def loggerName(value: String): ZIOAspect[Nothing, Any, Nothing, Any, Nothing, Any] = + ZIOAspect.annotated(loggerNameAnnotationKey, value) + + /** + * slf4j marker name aspect + * + * annotation key: [[SLF4J.logMarkerNameAnnotationKey]] + */ + def logMarkerName(value: String): ZIOAspect[Nothing, Any, Nothing, Any, Nothing, Any] = + ZIOAspect.annotated(logMarkerNameAnnotationKey, value) + + /** + * get logger name from [[Trace]] + * + * trace with value ''example.LivePingService.ping(PingService.scala:22)'' + * will have ''example.LivePingService'' as logger name + */ + def getLoggerName(default: String = "zio-slf4j-logger"): Trace => String = + trace => LoggerNameExtractor.trace(trace, FiberRefs.empty, Map.empty).getOrElse(default) + + private val logLevelMapping: Map[LogLevel, Level] = Map( + LogLevel.All -> Level.TRACE, + LogLevel.Trace -> Level.TRACE, + LogLevel.Debug -> Level.DEBUG, + LogLevel.Info -> Level.INFO, + LogLevel.Warning -> Level.WARN, + LogLevel.Error -> Level.ERROR, + LogLevel.Fatal -> Level.ERROR + ) + + private def isLogLevelEnabled(slf4jLogger: Logger, slf4jMarker: Option[Marker], logLevel: LogLevel): Boolean = + logLevel match { + case LogLevel.All => slf4jMarker.fold(slf4jLogger.isTraceEnabled)(m => slf4jLogger.isTraceEnabled(m)) + case LogLevel.Trace => slf4jMarker.fold(slf4jLogger.isTraceEnabled)(m => slf4jLogger.isTraceEnabled(m)) + case LogLevel.Debug => slf4jMarker.fold(slf4jLogger.isDebugEnabled)(m => slf4jLogger.isDebugEnabled(m)) + case LogLevel.Info => slf4jMarker.fold(slf4jLogger.isInfoEnabled)(m => slf4jLogger.isInfoEnabled(m)) + case LogLevel.Warning => slf4jMarker.fold(slf4jLogger.isWarnEnabled)(m => slf4jLogger.isWarnEnabled(m)) + case LogLevel.Error => slf4jMarker.fold(slf4jLogger.isErrorEnabled)(m => slf4jLogger.isErrorEnabled(m)) + case LogLevel.Fatal => slf4jMarker.fold(slf4jLogger.isErrorEnabled)(m => slf4jLogger.isErrorEnabled(m)) + case _ => false + } + + private def logAppender(slf4jLogger: Logger, slf4jMarker: Option[Marker], logLevel: LogLevel): LogAppender = + new LogAppender { self => + val message: StringBuilder = new StringBuilder() + val keyValues: scala.collection.mutable.HashMap[String, String] = new scala.collection.mutable.HashMap[String, String]() + var throwable: Throwable = null + + /** + * cause as throwable + */ + override def appendCause(cause: Cause[Any]): Unit = { + if (!cause.isEmpty) { + throwable = FiberFailure(cause) + } + () + } + + override def appendNumeric[A](numeric: A): Unit = appendText(numeric.toString) + + override def appendText(text: String): Unit = { + message.append(text) + () + } + + override def closeKeyOpenValue(): Unit = + appendText("=") + + /** + * all key-value into mdc + */ + override def appendKeyValue(key: String, value: String): Unit = { + keyValues.put(key, value) + () + } + + /** + * all key-value into mdc + */ + override def appendKeyValue(key: String, appendValue: LogAppender => Unit): Unit = { + val builder = new StringBuilder() + appendValue(LogAppender.unstructured(builder.append(_))) + builder.toString() + keyValues.put(key, builder.toString()) + () + } + + override def closeLogEntry(): Unit = { + logLevelMapping.get(logLevel).foreach { level => + var builder = slf4jLogger.atLevel(level).setMessage(message.toString).setCause(throwable) + + slf4jMarker.foreach { m => + builder = builder.addMarker(m) + } + + builder = keyValues.foldLeft(builder) { case (b, (k, v)) => + b.addKeyValue(k, v) + } + + builder.log() + } + () + } + + override def closeValue(): Unit = () + + override def openKey(): Unit = () + + override def openLogEntry(): Unit = { + message.clear() + keyValues.clear() + throwable = null + () + } + } + + @deprecated("use layer without logLevel", "2.0.1") + def slf4j( + logLevel: zio.LogLevel, + format: LogFormat, + loggerName: Trace => String + ): ZLayer[Any, Nothing, Unit] = + Runtime.addLogger(slf4jLogger(format, loggerName).filterLogLevel(_ >= logLevel)) + + @deprecated("use layer without logLevel", "2.0.1") + def slf4j( + logLevel: zio.LogLevel, + format: LogFormat + ): ZLayer[Any, Nothing, Unit] = + slf4j(logLevel, format, getLoggerName()) + + @deprecated("use layer without logLevel", "2.0.1") + def slf4j( + logLevel: zio.LogLevel + ): ZLayer[Any, Nothing, Unit] = + slf4j(logLevel, logFormatDefault, getLoggerName()) + + /** + * Use this layer to register an use an Slf4j logger in your app. + * To avoid double logging, you should create this layer only once in your application + */ + def slf4j( + format: LogFormat, + loggerName: Trace => String + ): ZLayer[Any, Nothing, Unit] = + Runtime.addLogger(slf4jLogger(format, loggerName)) + + /** + * Use this layer to register an use an Slf4j logger in your app. + * To avoid double logging, you should create this layer only once in your application + */ + def slf4j( + format: LogFormat + ): ZLayer[Any, Nothing, Unit] = + slf4j(format, getLoggerName()) + + /** + * Use this layer to register an use an Slf4j logger in your app. + * To avoid double logging, you should create this layer only once in your application + */ + def slf4j: ZLayer[Any, Nothing, Unit] = + slf4j(logFormatDefault) + + def slf4jLogger( + format: LogFormat, + loggerName: Trace => String + ): ZLogger[String, Unit] = { + // get some slf4j logger to invoke slf4j initialisation + // as in some program failure cases it may happen, that program exit sooner then log message will be logged (#616) + LoggerFactory.getLogger("zio-slf4j-logger") + + new ZLogger[String, Unit] { + override def apply( + trace: Trace, + fiberId: FiberId, + logLevel: LogLevel, + message: () => String, + cause: Cause[Any], + context: FiberRefs, + spans: List[LogSpan], + annotations: Map[String, String] + ): Unit = { + val slf4jLoggerName = annotations.getOrElse(loggerNameAnnotationKey, loggerName(trace)) + val slf4jLogger = LoggerFactory.getLogger(slf4jLoggerName) + val slf4jMarkerName = annotations.get(logMarkerNameAnnotationKey) + val slf4jMarker = slf4jMarkerName.map(n => MarkerFactory.getMarker(n)) + if (isLogLevelEnabled(slf4jLogger, slf4jMarker, logLevel)) { + val appender = logAppender(slf4jLogger, slf4jMarker, logLevel) + + format.unsafeFormat(appender)(trace, fiberId, logLevel, message, cause, context, spans, annotations) + appender.closeLogEntry() + } + () + } + } + } + +} diff --git a/slf4j2/src/test/resources/logback-test.xml b/slf4j2/src/test/resources/logback-test.xml new file mode 100644 index 00000000..eb6acedc --- /dev/null +++ b/slf4j2/src/test/resources/logback-test.xml @@ -0,0 +1,24 @@ + + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} %msg%n + + + + + + + + + CONFIDENTIAL_FILTER + CONFIDENTIAL + DENY + + + + + + + \ No newline at end of file diff --git a/slf4j2/src/test/scala/zio/logging/backend/SLF4JSpec.scala b/slf4j2/src/test/scala/zio/logging/backend/SLF4JSpec.scala new file mode 100644 index 00000000..59101092 --- /dev/null +++ b/slf4j2/src/test/scala/zio/logging/backend/SLF4JSpec.scala @@ -0,0 +1,272 @@ +package zio.logging.backend + +import zio.logging.backend.TestAppender.LogEntry +import zio.logging.{ LogAnnotation, LogFormat } +import zio.test.Assertion._ +import zio.test._ +import zio.{ LogLevel, Runtime, ZIO, ZIOAspect, _ } + +import java.util.UUID +import scala.annotation.tailrec + +object SLF4JSpec extends ZIOSpecDefault { + + val loggerDefault: ZLayer[Any, Nothing, Unit] = + Runtime.removeDefaultLoggers >>> SLF4J.slf4j + + val loggerTraceAnnotation: ZLayer[Any, Nothing, Unit] = + Runtime.removeDefaultLoggers >>> SLF4J.slf4j( + LogFormat.logAnnotation(LogAnnotation.TraceId) + LogFormat.line + LogFormat.cause + ) + + val loggerUserAnnotation: ZLayer[Any, Nothing, Unit] = + Runtime.removeDefaultLoggers >>> SLF4J.slf4j( + LogFormat.annotation("user") + LogFormat.line + LogFormat.cause + ) + + val loggerLineCause: ZLayer[Any, Nothing, Unit] = + Runtime.removeDefaultLoggers >>> SLF4J.slf4j(LogFormat.line + LogFormat.cause) + + val loggerLine: ZLayer[Any, Nothing, Unit] = + Runtime.removeDefaultLoggers >>> SLF4J.slf4j(LogFormat.line) + + val loggerLabels: ZLayer[Any, Nothing, Unit] = + Runtime.removeDefaultLoggers >>> SLF4J.slf4j(LogFormat.label("fiber", LogFormat.fiberId) |-| LogFormat.line) + + def startStop(): ZIO[Any, Nothing, (UUID, Chunk[UUID])] = { + val users = Chunk.fill(2)(UUID.randomUUID()) + for { + traceId <- ZIO.succeed(UUID.randomUUID()) + _ = TestAppender.reset() + _ <- ZIO.foreach(users) { uId => + { + ZIO.logInfo("Starting operation") *> ZIO.sleep(500.millis) *> ZIO.logInfo("Stopping operation") + } @@ ZIOAspect.annotated("user", uId.toString) + } @@ LogAnnotation.TraceId(traceId) + _ <- ZIO.logInfo("Done") + } yield traceId -> users + } + + def startStopAssert(loggerOutput: Chunk[LogEntry], loggerName: String = "zio.logging.backend.SLF4JSpec"): TestResult = + assertTrue(loggerOutput.size == 5) && assertTrue( + loggerOutput.forall(_.loggerName == loggerName) + ) && assertTrue(loggerOutput.forall(_.logLevel == LogLevel.Info)) && assert(loggerOutput.map(_.message))( + equalTo( + Chunk( + "Starting operation", + "Stopping operation", + "Starting operation", + "Stopping operation", + "Done" + ) + ) + ) + + def someError(): ZIO[Any, Nothing, Unit] = { + def someTestFunction(input: Int): Int = { + @tailrec + def innerFunction(input: Int): Int = + if (input < 1) throw new Exception("input < 1") + else innerFunction(input - 1) + + innerFunction(input) + } + + for { + start <- ZIO.succeed(10) + _ = TestAppender.reset() + _ <- ZIO + .attempt(someTestFunction(start)) + .tap(result => ZIO.logInfo(s"Calculation result: $result")) + .catchAllCause(error => ZIO.logErrorCause("Calculation error", error) *> ZIO.succeed(-1)) + } yield () + } + + def someErrorAssert(loggerOutput: Chunk[LogEntry], loggerName: String = "zio.logging.backend.SLF4JSpec"): TestResult = + assertTrue(loggerOutput.size == 1) && assert(loggerOutput(0).loggerName)( + equalTo(loggerName) + ) && assert(loggerOutput(0).logLevel)( + equalTo(LogLevel.Error) + ) && assert(loggerOutput(0).message)( + equalTo("Calculation error") + ) + + val spec: Spec[Environment, Any] = suite("SLF4JSpec")( + test("log only user annotation into MDC") { + startStop().map { case (_, users) => + val loggerOutput = TestAppender.logOutput + startStopAssert(loggerOutput) && assert(loggerOutput.map(_.mdc.get(LogAnnotation.TraceId.name)))( + equalTo(Chunk.fill(5)(None)) + ) && assert(loggerOutput.map(_.mdc.get("user")))( + equalTo(users.flatMap(u => Chunk.fill(2)(Some(u.toString))) :+ None) + ) + } + }.provide(loggerUserAnnotation), + test("log only trace annotation into MDC") { + startStop().map { case (traceId, _) => + val loggerOutput = TestAppender.logOutput + startStopAssert(loggerOutput) && assert(loggerOutput.map(_.mdc.get(LogAnnotation.TraceId.name)))( + equalTo( + Chunk.fill(4)(Some(traceId.toString)) :+ None + ) + ) && assert(loggerOutput.map(_.mdc.get("user")))( + equalTo(Chunk.fill(5)(None)) + ) + } + }.provide(loggerTraceAnnotation), + test("log all annotations into MDC with custom logger name") { + (startStop() @@ SLF4J.loggerName("my-logger")).map { case (traceId, users) => + val loggerOutput = TestAppender.logOutput + startStopAssert(loggerOutput, "my-logger") && assert(loggerOutput.map(_.mdc.get(LogAnnotation.TraceId.name)))( + equalTo( + Chunk.fill(4)(Some(traceId.toString)) :+ None + ) + ) && assert(loggerOutput.map(_.mdc.get("user")))( + equalTo(users.flatMap(u => Chunk.fill(2)(Some(u.toString))) :+ None) + ) && assert(loggerOutput.map(_.mdc.contains(SLF4J.loggerNameAnnotationKey)))( + equalTo(Chunk.fill(5)(false)) + ) + } + }.provide(loggerDefault), + test("logger name changes") { + val users = Chunk.fill(2)(UUID.randomUUID()) + for { + traceId <- ZIO.succeed(UUID.randomUUID()) + _ = TestAppender.reset() + _ <- ZIO.logInfo("Start") @@ SLF4J.loggerName("root-logger") + _ <- ZIO.foreach(users) { uId => + { + ZIO.logInfo("Starting user operation") *> ZIO.sleep(500.millis) *> ZIO.logInfo( + "Stopping user operation" + ) + } @@ ZIOAspect.annotated("user", uId.toString) @@ SLF4J.loggerName("user-logger") + } @@ LogAnnotation.TraceId(traceId) @@ SLF4J.loggerName("user-root-logger") + _ <- ZIO.logInfo("Done") @@ SLF4J.loggerName("root-logger") + } yield { + val loggerOutput = TestAppender.logOutput + assertTrue(loggerOutput.forall(_.logLevel == LogLevel.Info)) && assert(loggerOutput.map(_.message))( + equalTo( + Chunk( + "Start", + "Starting user operation", + "Stopping user operation", + "Starting user operation", + "Stopping user operation", + "Done" + ) + ) + ) && assert(loggerOutput.map(_.loggerName))( + equalTo( + Chunk( + "root-logger", + "user-logger", + "user-logger", + "user-logger", + "user-logger", + "root-logger" + ) + ) + ) + } + }.provide(loggerDefault), + test("log error with cause") { + someError().map { _ => + val loggerOutput = TestAppender.logOutput + someErrorAssert(loggerOutput) && assertTrue(loggerOutput(0).cause.exists(_.contains("input < 1"))) + } + }.provide(loggerLineCause), + test("log error with cause with custom logger name") { + (someError() @@ SLF4J.loggerName("my-logger")).map { _ => + val loggerOutput = TestAppender.logOutput + someErrorAssert(loggerOutput, "my-logger") && assertTrue( + loggerOutput(0).cause.exists(_.contains("input < 1")) + ) && + assertTrue(!loggerOutput(0).mdc.contains(SLF4J.loggerNameAnnotationKey)) + } + }.provide(loggerLineCause), + test("log error without cause") { + someError().map { _ => + val loggerOutput = TestAppender.logOutput + someErrorAssert(loggerOutput) && assertTrue(loggerOutput(0).cause.isEmpty) + } + }.provide(loggerLine), + test("log with fiber label") { + for { + _ <- ZIO.succeed(TestAppender.reset()) + _ <- ZIO.logInfo("info") + _ <- ZIO.logWarning("warn") + } yield { + val loggerOutput = TestAppender.logOutput + assert(loggerOutput.map(_.message))(forall(startsWithString("fiber=zio-fiber-"))) && assert( + loggerOutput.map(_.logLevel) + )(equalTo(Chunk(LogLevel.Info, LogLevel.Warning))) + } + }.provide(loggerLabels), + test("log only enabled levels in configuration") { + for { + _ <- ZIO.succeed(TestAppender.reset()) + _ <- ZIO.logTrace("trace") + _ <- ZIO.logDebug("debug") + _ <- ZIO.logInfo("info") + _ <- ZIO.logWarning("warn") + _ <- ZIO.logError("error") + _ <- ZIO.logFatal("fatal") + } yield { + val loggerOutput = TestAppender.logOutput + assert(loggerOutput.map(_.message))( + equalTo( + Chunk( + "info", + "warn", + "error", + "fatal" + ) + ) + ) && assert(loggerOutput.map(_.logLevel))( + equalTo( + Chunk( + LogLevel.Info, + LogLevel.Warning, + LogLevel.Error, + LogLevel.Error + ) + ) + ) + } + }.provide(loggerDefault), + test("not log messages denied by marker") { + val confidentialMarker = SLF4J.logMarkerName("CONFIDENTIAL") + for { + _ <- ZIO.succeed(TestAppender.reset()) + _ <- ZIO.logInfo("not confidential info") + _ <- ZIO.logInfo("confidential info") @@ confidentialMarker + _ <- ZIO.logWarning("not confidential warn") + _ <- ZIO.logWarning("confidential warn") @@ confidentialMarker + _ <- ZIO.logError("not confidential error") + _ <- ZIO.logError("confidential error") @@ confidentialMarker + _ <- ZIO.logFatal("not confidential fatal") + } yield { + val loggerOutput = TestAppender.logOutput + assert(loggerOutput.map(_.message))( + equalTo( + Chunk( + "not confidential info", + "not confidential warn", + "not confidential error", + "not confidential fatal" + ) + ) + ) && assert(loggerOutput.map(_.logLevel))( + equalTo( + Chunk( + LogLevel.Info, + LogLevel.Warning, + LogLevel.Error, + LogLevel.Error + ) + ) + ) + } + }.provide(loggerDefault) + ) @@ TestAspect.sequential @@ TestAspect.withLiveClock +} diff --git a/slf4j2/src/test/scala/zio/logging/backend/TestAppender.scala b/slf4j2/src/test/scala/zio/logging/backend/TestAppender.scala new file mode 100644 index 00000000..4f7f7abd --- /dev/null +++ b/slf4j2/src/test/scala/zio/logging/backend/TestAppender.scala @@ -0,0 +1,70 @@ +package zio.logging.backend + +import ch.qos.logback.classic.Level +import ch.qos.logback.classic.spi.ILoggingEvent +import ch.qos.logback.core.AppenderBase +import zio.{ Chunk, LogLevel } + +import java.util.concurrent.atomic.AtomicReference +import scala.annotation.tailrec +import scala.jdk.CollectionConverters._ + +class TestAppender extends AppenderBase[ILoggingEvent] { + override def append(event: ILoggingEvent): Unit = + TestAppender.appendLogEntry(event) +} + +object TestAppender { + + val logLevelMapping: Map[Level, LogLevel] = Map( + Level.ALL -> LogLevel.All, + Level.TRACE -> LogLevel.Trace, + Level.DEBUG -> LogLevel.Debug, + Level.INFO -> LogLevel.Info, + Level.WARN -> LogLevel.Warning, + Level.ERROR -> LogLevel.Error, + Level.OFF -> LogLevel.None + ) + + final case class LogEntry( + loggerName: String, + threadName: String, + logLevel: LogLevel, + message: String, + timestamp: Long, + cause: Option[String], + mdc: Map[String, String] + ) + + object LogEntry { + def apply(event: ILoggingEvent): LogEntry = + LogEntry( + event.getLoggerName, + event.getThreadName, + logLevelMapping(event.getLevel), + event.getMessage, + event.getTimeStamp, + Option(event.getThrowableProxy).map(_.getMessage), + event.getKeyValuePairs.asScala.map(kv => (kv.key, kv.value.toString)).toMap + ) + } + + private val logEntriesRef: AtomicReference[Chunk[LogEntry]] = new AtomicReference[Chunk[LogEntry]](Chunk.empty) + + private def appendLogEntry(event: ILoggingEvent): Unit = { + + @tailrec + def append(entry: LogEntry): Unit = { + val old = logEntriesRef.get() + if (logEntriesRef.compareAndSet(old, old :+ entry)) () + else append(entry) + } + + append(LogEntry(event)) + } + + def reset(): Unit = logEntriesRef.set(Chunk.empty) + + def logOutput: Chunk[LogEntry] = logEntriesRef.get() + +} From 57a45ed01ba4aae9035e5e655418c0903baffeba Mon Sep 17 00:00:00 2001 From: Peter Kotula Date: Sat, 28 Jan 2023 20:49:50 +0100 Subject: [PATCH 02/14] slf4j2 --- .../main/scala/zio/logging/slf4j/SLF4J.scala | 36 ++++--------------- .../scala/zio/logging/backend/SLF4JSpec.scala | 24 +++++++------ .../zio/logging/backend/TestAppender.scala | 17 ++++++--- 3 files changed, 32 insertions(+), 45 deletions(-) diff --git a/slf4j2/src/main/scala/zio/logging/slf4j/SLF4J.scala b/slf4j2/src/main/scala/zio/logging/slf4j/SLF4J.scala index c145a19b..55d5df1a 100644 --- a/slf4j2/src/main/scala/zio/logging/slf4j/SLF4J.scala +++ b/slf4j2/src/main/scala/zio/logging/slf4j/SLF4J.scala @@ -15,14 +15,12 @@ */ package zio.logging.backend -import org.slf4j.{ Logger, LoggerFactory, MDC, Marker, MarkerFactory } import org.slf4j.event.Level +import org.slf4j.{ Logger, LoggerFactory, Marker, MarkerFactory } import zio.logging.internal.LogAppender import zio.logging.{ LogFormat, LoggerNameExtractor } import zio.{ Cause, FiberFailure, FiberId, FiberRefs, LogLevel, LogSpan, Runtime, Trace, ZIOAspect, ZLayer, ZLogger } -import java.util - object SLF4J { /** @@ -94,9 +92,10 @@ object SLF4J { private def logAppender(slf4jLogger: Logger, slf4jMarker: Option[Marker], logLevel: LogLevel): LogAppender = new LogAppender { self => - val message: StringBuilder = new StringBuilder() - val keyValues: scala.collection.mutable.HashMap[String, String] = new scala.collection.mutable.HashMap[String, String]() - var throwable: Throwable = null + val message: StringBuilder = new StringBuilder() + val keyValues: scala.collection.mutable.HashMap[String, String] = + new scala.collection.mutable.HashMap[String, String]() + var throwable: Throwable = null /** * cause as throwable @@ -119,7 +118,7 @@ object SLF4J { appendText("=") /** - * all key-value into mdc + * all key-value into slf4j key-values */ override def appendKeyValue(key: String, value: String): Unit = { keyValues.put(key, value) @@ -127,7 +126,7 @@ object SLF4J { } /** - * all key-value into mdc + * all key-value into slf4j key-values */ override def appendKeyValue(key: String, appendValue: LogAppender => Unit): Unit = { val builder = new StringBuilder() @@ -166,27 +165,6 @@ object SLF4J { } } - @deprecated("use layer without logLevel", "2.0.1") - def slf4j( - logLevel: zio.LogLevel, - format: LogFormat, - loggerName: Trace => String - ): ZLayer[Any, Nothing, Unit] = - Runtime.addLogger(slf4jLogger(format, loggerName).filterLogLevel(_ >= logLevel)) - - @deprecated("use layer without logLevel", "2.0.1") - def slf4j( - logLevel: zio.LogLevel, - format: LogFormat - ): ZLayer[Any, Nothing, Unit] = - slf4j(logLevel, format, getLoggerName()) - - @deprecated("use layer without logLevel", "2.0.1") - def slf4j( - logLevel: zio.LogLevel - ): ZLayer[Any, Nothing, Unit] = - slf4j(logLevel, logFormatDefault, getLoggerName()) - /** * Use this layer to register an use an Slf4j logger in your app. * To avoid double logging, you should create this layer only once in your application diff --git a/slf4j2/src/test/scala/zio/logging/backend/SLF4JSpec.scala b/slf4j2/src/test/scala/zio/logging/backend/SLF4JSpec.scala index 59101092..961cdc81 100644 --- a/slf4j2/src/test/scala/zio/logging/backend/SLF4JSpec.scala +++ b/slf4j2/src/test/scala/zio/logging/backend/SLF4JSpec.scala @@ -92,38 +92,40 @@ object SLF4JSpec extends ZIOSpecDefault { ) val spec: Spec[Environment, Any] = suite("SLF4JSpec")( - test("log only user annotation into MDC") { + test("log only user annotation into key values") { startStop().map { case (_, users) => val loggerOutput = TestAppender.logOutput - startStopAssert(loggerOutput) && assert(loggerOutput.map(_.mdc.get(LogAnnotation.TraceId.name)))( + startStopAssert(loggerOutput) && assert(loggerOutput.map(_.keyValues.get(LogAnnotation.TraceId.name)))( equalTo(Chunk.fill(5)(None)) - ) && assert(loggerOutput.map(_.mdc.get("user")))( + ) && assert(loggerOutput.map(_.keyValues.get("user")))( equalTo(users.flatMap(u => Chunk.fill(2)(Some(u.toString))) :+ None) ) } }.provide(loggerUserAnnotation), - test("log only trace annotation into MDC") { + test("log only trace annotation into key values") { startStop().map { case (traceId, _) => val loggerOutput = TestAppender.logOutput - startStopAssert(loggerOutput) && assert(loggerOutput.map(_.mdc.get(LogAnnotation.TraceId.name)))( + startStopAssert(loggerOutput) && assert(loggerOutput.map(_.keyValues.get(LogAnnotation.TraceId.name)))( equalTo( Chunk.fill(4)(Some(traceId.toString)) :+ None ) - ) && assert(loggerOutput.map(_.mdc.get("user")))( + ) && assert(loggerOutput.map(_.keyValues.get("user")))( equalTo(Chunk.fill(5)(None)) ) } }.provide(loggerTraceAnnotation), - test("log all annotations into MDC with custom logger name") { + test("log all annotations into key values with custom logger name") { (startStop() @@ SLF4J.loggerName("my-logger")).map { case (traceId, users) => val loggerOutput = TestAppender.logOutput - startStopAssert(loggerOutput, "my-logger") && assert(loggerOutput.map(_.mdc.get(LogAnnotation.TraceId.name)))( + startStopAssert(loggerOutput, "my-logger") && assert( + loggerOutput.map(_.keyValues.get(LogAnnotation.TraceId.name)) + )( equalTo( Chunk.fill(4)(Some(traceId.toString)) :+ None ) - ) && assert(loggerOutput.map(_.mdc.get("user")))( + ) && assert(loggerOutput.map(_.keyValues.get("user")))( equalTo(users.flatMap(u => Chunk.fill(2)(Some(u.toString))) :+ None) - ) && assert(loggerOutput.map(_.mdc.contains(SLF4J.loggerNameAnnotationKey)))( + ) && assert(loggerOutput.map(_.keyValues.contains(SLF4J.loggerNameAnnotationKey)))( equalTo(Chunk.fill(5)(false)) ) } @@ -181,7 +183,7 @@ object SLF4JSpec extends ZIOSpecDefault { someErrorAssert(loggerOutput, "my-logger") && assertTrue( loggerOutput(0).cause.exists(_.contains("input < 1")) ) && - assertTrue(!loggerOutput(0).mdc.contains(SLF4J.loggerNameAnnotationKey)) + assertTrue(!loggerOutput(0).keyValues.contains(SLF4J.loggerNameAnnotationKey)) } }.provide(loggerLineCause), test("log error without cause") { diff --git a/slf4j2/src/test/scala/zio/logging/backend/TestAppender.scala b/slf4j2/src/test/scala/zio/logging/backend/TestAppender.scala index 4f7f7abd..3d7c19ba 100644 --- a/slf4j2/src/test/scala/zio/logging/backend/TestAppender.scala +++ b/slf4j2/src/test/scala/zio/logging/backend/TestAppender.scala @@ -33,20 +33,27 @@ object TestAppender { message: String, timestamp: Long, cause: Option[String], - mdc: Map[String, String] + keyValues: Map[String, String] ) object LogEntry { - def apply(event: ILoggingEvent): LogEntry = + def apply(event: ILoggingEvent): LogEntry = { + val keyValues = if (event.getKeyValuePairs != null) { + event.getKeyValuePairs.asScala.map(kv => (kv.key, kv.value.toString)).toMap + } else Map.empty[String, String] + val cause = Option(event.getThrowableProxy).map(_.getMessage) + val level = logLevelMapping(event.getLevel) + LogEntry( event.getLoggerName, event.getThreadName, - logLevelMapping(event.getLevel), + level, event.getMessage, event.getTimeStamp, - Option(event.getThrowableProxy).map(_.getMessage), - event.getKeyValuePairs.asScala.map(kv => (kv.key, kv.value.toString)).toMap + cause, + keyValues ) + } } private val logEntriesRef: AtomicReference[Chunk[LogEntry]] = new AtomicReference[Chunk[LogEntry]](Chunk.empty) From b33e188d00f7d5276fe6781394f12173924cb4ee Mon Sep 17 00:00:00 2001 From: Peter Kotula Date: Sun, 29 Jan 2023 19:11:01 +0100 Subject: [PATCH 03/14] examples --- build.sbt | 83 +++++++++++++++++-- .../logging/example/ConsoleColoredApp.scala | 0 .../zio/logging/example/ConsoleJsonApp.scala | 0 .../logging/example/ConsoleSimpleApp.scala | 0 .../zio/logging/example/MetricsApp.scala | 0 .../zio/logging/example/PingService.scala | 0 .../scala/zio/logging/example/SimpleApp.scala | 0 .../zio/logging/example/LoggingSpec.scala | 0 .../zio/logging/example/PingServiceSpec.scala | 0 .../zio/logging/example/JplSimpleApp.scala | 0 .../example}/Slf4jBridgeExampleApp.scala | 18 +++- .../slf4j-log4j/src/main/resources/log4j2.xml | 18 ++++ .../zio/logging/example/Slf4jSimpleApp.scala | 0 .../src/main/resources/logback.xml | 0 .../example/CustomTracingAnnotationApp.scala | 0 .../zio/logging/example/PingService.scala | 44 ++++++++++ .../zio/logging/example/Slf4jExampleApp.scala | 0 .../zio/logging/example/Slf4jFailureApp.scala | 0 .../zio/logging/example/Slf4jSimpleApp.scala | 45 ++++++++++ .../src/main/resources/logback.xml | 20 +++++ .../zio/logging/example/Slf4j2SimpleApp.scala | 45 ++++++++++ .../zio/logging/example/JplExampleApp.scala | 39 --------- 22 files changed, 263 insertions(+), 49 deletions(-) rename examples/{ => core}/src/main/scala/zio/logging/example/ConsoleColoredApp.scala (100%) rename examples/{ => core}/src/main/scala/zio/logging/example/ConsoleJsonApp.scala (100%) rename examples/{ => core}/src/main/scala/zio/logging/example/ConsoleSimpleApp.scala (100%) rename examples/{ => core}/src/main/scala/zio/logging/example/MetricsApp.scala (100%) rename examples/{ => core}/src/main/scala/zio/logging/example/PingService.scala (100%) rename examples/{ => core}/src/main/scala/zio/logging/example/SimpleApp.scala (100%) rename examples/{ => core}/src/test/scala/zio/logging/example/LoggingSpec.scala (100%) rename examples/{ => core}/src/test/scala/zio/logging/example/PingServiceSpec.scala (100%) rename examples/{ => jpl}/src/main/scala/zio/logging/example/JplSimpleApp.scala (100%) rename {slf4j-bridge/src/test/scala/zio/logging/slf4j/bridge => examples/slf4j-bridge/src/main/scala/zio/logging/example}/Slf4jBridgeExampleApp.scala (59%) create mode 100644 examples/slf4j-log4j/src/main/resources/log4j2.xml rename examples/{ => slf4j-log4j}/src/main/scala/zio/logging/example/Slf4jSimpleApp.scala (100%) rename examples/{ => slf4j-logback}/src/main/resources/logback.xml (100%) rename examples/{ => slf4j-logback}/src/main/scala/zio/logging/example/CustomTracingAnnotationApp.scala (100%) create mode 100644 examples/slf4j-logback/src/main/scala/zio/logging/example/PingService.scala rename examples/{ => slf4j-logback}/src/main/scala/zio/logging/example/Slf4jExampleApp.scala (100%) rename examples/{ => slf4j-logback}/src/main/scala/zio/logging/example/Slf4jFailureApp.scala (100%) create mode 100644 examples/slf4j-logback/src/main/scala/zio/logging/example/Slf4jSimpleApp.scala create mode 100644 examples/slf4j2-logback/src/main/resources/logback.xml create mode 100644 examples/slf4j2-logback/src/main/scala/zio/logging/example/Slf4j2SimpleApp.scala delete mode 100644 examples/src/main/scala/zio/logging/example/JplExampleApp.scala diff --git a/build.sbt b/build.sbt index 9f015671..17d9570a 100644 --- a/build.sbt +++ b/build.sbt @@ -56,7 +56,23 @@ lazy val root = project .settings( publish / skip := true ) - .aggregate(coreJVM, coreJS, slf4j, slf4j2, slf4jBridge, slf4j2Bridge, jpl, benchmarks, examples, docs) + .aggregate( + coreJVM, + coreJS, + slf4j, + slf4j2, + slf4jBridge, + slf4j2Bridge, + jpl, + benchmarks, + examplesCore, + examplesJpl, + examplesSlf4jBridge, + examplesSlf4jLogback, + examplesSlf4j2Logback, + examplesSlf4jLog4j, + docs + ) lazy val core = crossProject(JSPlatform, JVMPlatform) .crossType(CrossType.Full) @@ -176,21 +192,70 @@ lazy val benchmarks = project .dependsOn(coreJVM) .enablePlugins(JmhPlugin) -lazy val examples = project - .in(file("examples")) - .dependsOn(slf4j, jpl) - .settings(stdSettings("zio-logging-examples")) +lazy val examplesCore = project + .in(file("examples/core")) + .dependsOn(coreJVM) + .settings(stdSettings("zio-logging-examples-core")) + .settings( + publish / skip := true, + libraryDependencies ++= Seq( + "dev.zio" %% "zio-metrics-connectors" % "2.0.4", + "dev.zio" %%% "zio-test" % ZioVersion % Test, + "dev.zio" %%% "zio-test-sbt" % ZioVersion % Test + ) + ) + +lazy val examplesSlf4jLogback = project + .in(file("examples/slf4j-logback")) + .dependsOn(slf4j) + .settings(stdSettings("zio-logging-examples-slf4j-logback")) .settings( publish / skip := true, libraryDependencies ++= Seq( "ch.qos.logback" % "logback-classic" % logbackVersion, - "net.logstash.logback" % "logstash-logback-encoder" % "6.6", - "dev.zio" %% "zio-metrics-connectors" % "2.0.4", - "dev.zio" %%% "zio-test" % ZioVersion % Test, - "dev.zio" %%% "zio-test-sbt" % ZioVersion % Test + "net.logstash.logback" % "logstash-logback-encoder" % "6.6" + ) + ) + +lazy val examplesSlf4j2Logback = project + .in(file("examples/slf4j2-logback")) + .dependsOn(slf4j2) + .settings(stdSettings("zio-logging-examples-slf4j2-logback")) + .settings( + publish / skip := true, + libraryDependencies ++= Seq( + "ch.qos.logback" % "logback-classic" % logback2Version, + "net.logstash.logback" % "logstash-logback-encoder" % "7.2" ) ) +lazy val examplesSlf4jLog4j = project + .in(file("examples/slf4j-log4j")) + .dependsOn(slf4j) + .settings(stdSettings("zio-logging-examples-slf4j-log4j")) + .settings( + publish / skip := true, + libraryDependencies ++= Seq( + "org.apache.logging.log4j" % "log4j-slf4j-impl" % "2.17.2" + ) + ) + +lazy val examplesJpl = project + .in(file("examples/jpl")) + .dependsOn(jpl) + .settings(stdSettings("zio-logging-examples-jpl")) + .settings( + publish / skip := true + ) + +lazy val examplesSlf4jBridge = project + .in(file("examples/slf4j-bridge")) + .dependsOn(slf4jBridge) + .settings(stdSettings("zio-logging-examples-slf4j-bridge")) + .settings( + publish / skip := true + ) + lazy val docs = project .in(file("zio-logging-docs")) .settings( diff --git a/examples/src/main/scala/zio/logging/example/ConsoleColoredApp.scala b/examples/core/src/main/scala/zio/logging/example/ConsoleColoredApp.scala similarity index 100% rename from examples/src/main/scala/zio/logging/example/ConsoleColoredApp.scala rename to examples/core/src/main/scala/zio/logging/example/ConsoleColoredApp.scala diff --git a/examples/src/main/scala/zio/logging/example/ConsoleJsonApp.scala b/examples/core/src/main/scala/zio/logging/example/ConsoleJsonApp.scala similarity index 100% rename from examples/src/main/scala/zio/logging/example/ConsoleJsonApp.scala rename to examples/core/src/main/scala/zio/logging/example/ConsoleJsonApp.scala diff --git a/examples/src/main/scala/zio/logging/example/ConsoleSimpleApp.scala b/examples/core/src/main/scala/zio/logging/example/ConsoleSimpleApp.scala similarity index 100% rename from examples/src/main/scala/zio/logging/example/ConsoleSimpleApp.scala rename to examples/core/src/main/scala/zio/logging/example/ConsoleSimpleApp.scala diff --git a/examples/src/main/scala/zio/logging/example/MetricsApp.scala b/examples/core/src/main/scala/zio/logging/example/MetricsApp.scala similarity index 100% rename from examples/src/main/scala/zio/logging/example/MetricsApp.scala rename to examples/core/src/main/scala/zio/logging/example/MetricsApp.scala diff --git a/examples/src/main/scala/zio/logging/example/PingService.scala b/examples/core/src/main/scala/zio/logging/example/PingService.scala similarity index 100% rename from examples/src/main/scala/zio/logging/example/PingService.scala rename to examples/core/src/main/scala/zio/logging/example/PingService.scala diff --git a/examples/src/main/scala/zio/logging/example/SimpleApp.scala b/examples/core/src/main/scala/zio/logging/example/SimpleApp.scala similarity index 100% rename from examples/src/main/scala/zio/logging/example/SimpleApp.scala rename to examples/core/src/main/scala/zio/logging/example/SimpleApp.scala diff --git a/examples/src/test/scala/zio/logging/example/LoggingSpec.scala b/examples/core/src/test/scala/zio/logging/example/LoggingSpec.scala similarity index 100% rename from examples/src/test/scala/zio/logging/example/LoggingSpec.scala rename to examples/core/src/test/scala/zio/logging/example/LoggingSpec.scala diff --git a/examples/src/test/scala/zio/logging/example/PingServiceSpec.scala b/examples/core/src/test/scala/zio/logging/example/PingServiceSpec.scala similarity index 100% rename from examples/src/test/scala/zio/logging/example/PingServiceSpec.scala rename to examples/core/src/test/scala/zio/logging/example/PingServiceSpec.scala diff --git a/examples/src/main/scala/zio/logging/example/JplSimpleApp.scala b/examples/jpl/src/main/scala/zio/logging/example/JplSimpleApp.scala similarity index 100% rename from examples/src/main/scala/zio/logging/example/JplSimpleApp.scala rename to examples/jpl/src/main/scala/zio/logging/example/JplSimpleApp.scala diff --git a/slf4j-bridge/src/test/scala/zio/logging/slf4j/bridge/Slf4jBridgeExampleApp.scala b/examples/slf4j-bridge/src/main/scala/zio/logging/example/Slf4jBridgeExampleApp.scala similarity index 59% rename from slf4j-bridge/src/test/scala/zio/logging/slf4j/bridge/Slf4jBridgeExampleApp.scala rename to examples/slf4j-bridge/src/main/scala/zio/logging/example/Slf4jBridgeExampleApp.scala index dc9c408f..4b4e7b2f 100644 --- a/slf4j-bridge/src/test/scala/zio/logging/slf4j/bridge/Slf4jBridgeExampleApp.scala +++ b/examples/slf4j-bridge/src/main/scala/zio/logging/example/Slf4jBridgeExampleApp.scala @@ -1,5 +1,21 @@ -package zio.logging.slf4j.bridge +/* + * Copyright 2019-2023 John A. De Goes and the ZIO Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package zio.logging.example +import zio.logging.slf4j.bridge.Slf4jBridge import zio.logging.{ LogFilter, LogFormat, LoggerNameExtractor, consoleJson } import zio.{ ExitCode, LogLevel, Runtime, Scope, ZIO, ZIOAppArgs, ZIOAppDefault, ZLayer } diff --git a/examples/slf4j-log4j/src/main/resources/log4j2.xml b/examples/slf4j-log4j/src/main/resources/log4j2.xml new file mode 100644 index 00000000..4711e91b --- /dev/null +++ b/examples/slf4j-log4j/src/main/resources/log4j2.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/src/main/scala/zio/logging/example/Slf4jSimpleApp.scala b/examples/slf4j-log4j/src/main/scala/zio/logging/example/Slf4jSimpleApp.scala similarity index 100% rename from examples/src/main/scala/zio/logging/example/Slf4jSimpleApp.scala rename to examples/slf4j-log4j/src/main/scala/zio/logging/example/Slf4jSimpleApp.scala diff --git a/examples/src/main/resources/logback.xml b/examples/slf4j-logback/src/main/resources/logback.xml similarity index 100% rename from examples/src/main/resources/logback.xml rename to examples/slf4j-logback/src/main/resources/logback.xml diff --git a/examples/src/main/scala/zio/logging/example/CustomTracingAnnotationApp.scala b/examples/slf4j-logback/src/main/scala/zio/logging/example/CustomTracingAnnotationApp.scala similarity index 100% rename from examples/src/main/scala/zio/logging/example/CustomTracingAnnotationApp.scala rename to examples/slf4j-logback/src/main/scala/zio/logging/example/CustomTracingAnnotationApp.scala diff --git a/examples/slf4j-logback/src/main/scala/zio/logging/example/PingService.scala b/examples/slf4j-logback/src/main/scala/zio/logging/example/PingService.scala new file mode 100644 index 00000000..727ec48b --- /dev/null +++ b/examples/slf4j-logback/src/main/scala/zio/logging/example/PingService.scala @@ -0,0 +1,44 @@ +/* + * Copyright 2019-2023 John A. De Goes and the ZIO Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package zio.logging.example + +import zio.{ Task, ULayer, ZIO, ZLayer } + +import java.net.InetAddress + +trait PingService { + def ping(address: String): Task[Boolean] +} + +object PingService { + def ping(address: String): ZIO[PingService, Throwable, Boolean] = ZIO.serviceWithZIO[PingService](_.ping(address)) +} + +final class LivePingService extends PingService { + override def ping(address: String): Task[Boolean] = + for { + inetAddress <- + ZIO + .attempt(InetAddress.getByName(address)) + .tapErrorCause(error => ZIO.logErrorCause(s"ping: $address - invalid address error", error)) + _ <- ZIO.logDebug(s"ping: $inetAddress") + result <- ZIO.attempt(inetAddress.isReachable(10000)) + } yield result +} + +object LivePingService { + val layer: ULayer[PingService] = ZLayer.succeed(new LivePingService) +} diff --git a/examples/src/main/scala/zio/logging/example/Slf4jExampleApp.scala b/examples/slf4j-logback/src/main/scala/zio/logging/example/Slf4jExampleApp.scala similarity index 100% rename from examples/src/main/scala/zio/logging/example/Slf4jExampleApp.scala rename to examples/slf4j-logback/src/main/scala/zio/logging/example/Slf4jExampleApp.scala diff --git a/examples/src/main/scala/zio/logging/example/Slf4jFailureApp.scala b/examples/slf4j-logback/src/main/scala/zio/logging/example/Slf4jFailureApp.scala similarity index 100% rename from examples/src/main/scala/zio/logging/example/Slf4jFailureApp.scala rename to examples/slf4j-logback/src/main/scala/zio/logging/example/Slf4jFailureApp.scala diff --git a/examples/slf4j-logback/src/main/scala/zio/logging/example/Slf4jSimpleApp.scala b/examples/slf4j-logback/src/main/scala/zio/logging/example/Slf4jSimpleApp.scala new file mode 100644 index 00000000..c704c3d8 --- /dev/null +++ b/examples/slf4j-logback/src/main/scala/zio/logging/example/Slf4jSimpleApp.scala @@ -0,0 +1,45 @@ +/* + * Copyright 2019-2023 John A. De Goes and the ZIO Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package zio.logging.example + +import zio.logging.LogAnnotation +import zio.logging.backend.SLF4J +import zio.{ ExitCode, Runtime, Scope, ZIO, ZIOAppDefault, _ } + +import java.util.UUID + +object Slf4jSimpleApp extends ZIOAppDefault { + + override val bootstrap: ZLayer[ZIOAppArgs, Any, Any] = Runtime.removeDefaultLoggers >>> SLF4J.slf4j + + private val users = List.fill(2)(UUID.randomUUID()) + + override def run: ZIO[Scope, Any, ExitCode] = + for { + _ <- ZIO.logInfo("Start") + traceId <- ZIO.succeed(UUID.randomUUID()) + _ <- ZIO.foreachPar(users) { uId => + { + ZIO.logInfo("Starting user operation") *> + ZIO.logInfo("Confidential user operation") @@ SLF4J.logMarkerName("CONFIDENTIAL") *> + ZIO.sleep(500.millis) *> + ZIO.logInfo("Stopping user operation") + } @@ ZIOAspect.annotated("user", uId.toString) + } @@ LogAnnotation.TraceId(traceId) @@ SLF4J.loggerName("zio.logging.example.UserOperation") + _ <- ZIO.logInfo("Done") + } yield ExitCode.success + +} diff --git a/examples/slf4j2-logback/src/main/resources/logback.xml b/examples/slf4j2-logback/src/main/resources/logback.xml new file mode 100644 index 00000000..1f10faf1 --- /dev/null +++ b/examples/slf4j2-logback/src/main/resources/logback.xml @@ -0,0 +1,20 @@ + + + + + + %d{HH:mm:ss.SSS} [%thread] [%kvp] %-5level %logger{36} %msg%n + + + + + CONFIDENTIAL_FILTER + CONFIDENTIAL + DENY + + + + + + + \ No newline at end of file diff --git a/examples/slf4j2-logback/src/main/scala/zio/logging/example/Slf4j2SimpleApp.scala b/examples/slf4j2-logback/src/main/scala/zio/logging/example/Slf4j2SimpleApp.scala new file mode 100644 index 00000000..6a00daf9 --- /dev/null +++ b/examples/slf4j2-logback/src/main/scala/zio/logging/example/Slf4j2SimpleApp.scala @@ -0,0 +1,45 @@ +/* + * Copyright 2019-2023 John A. De Goes and the ZIO Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package zio.logging.example + +import zio.logging.LogAnnotation +import zio.logging.backend.SLF4J +import zio.{ ExitCode, Runtime, Scope, ZIO, ZIOAppDefault, _ } + +import java.util.UUID + +object Slf4j2SimpleApp extends ZIOAppDefault { + + override val bootstrap: ZLayer[ZIOAppArgs, Any, Any] = Runtime.removeDefaultLoggers >>> SLF4J.slf4j + + private val users = List.fill(2)(UUID.randomUUID()) + + override def run: ZIO[Scope, Any, ExitCode] = + for { + _ <- ZIO.logInfo("Start") + traceId <- ZIO.succeed(UUID.randomUUID()) + _ <- ZIO.foreachPar(users) { uId => + { + ZIO.logInfo("Starting user operation") *> + ZIO.logInfo("Confidential user operation") @@ SLF4J.logMarkerName("CONFIDENTIAL") *> + ZIO.sleep(500.millis) *> + ZIO.logInfo("Stopping user operation") + } @@ ZIOAspect.annotated("user", uId.toString) + } @@ LogAnnotation.TraceId(traceId) @@ SLF4J.loggerName("zio.logging.example.UserOperation") + _ <- ZIO.logInfo("Done") + } yield ExitCode.success + +} diff --git a/examples/src/main/scala/zio/logging/example/JplExampleApp.scala b/examples/src/main/scala/zio/logging/example/JplExampleApp.scala deleted file mode 100644 index 9af4ea1c..00000000 --- a/examples/src/main/scala/zio/logging/example/JplExampleApp.scala +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2019-2023 John A. De Goes and the ZIO Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package zio.logging.example - -import zio.logging.backend.JPL -import zio.{ ExitCode, Runtime, Scope, URIO, ZIO, ZIOAppArgs, ZIOAppDefault, ZLayer } - -object JplExampleApp extends ZIOAppDefault { - - override val bootstrap: ZLayer[ZIOAppArgs, Any, Any] = Runtime.removeDefaultLoggers >>> JPL.jpl - - private def ping(address: String): URIO[PingService, Unit] = - PingService - .ping(address) - .tap(result => ZIO.logInfo(s"ping: $address - result: $result")) - .tapErrorCause(error => ZIO.logErrorCause(s"ping: $address - error", error)) - .unit - .ignore - - override def run: ZIO[Scope, Any, ExitCode] = - (for { - _ <- ping("127.0.0.1") - _ <- ping("x8.8.8.8") - } yield ExitCode.success).provide(LivePingService.layer) - -} From 0deca17dd7ebb2d4c1d779b981585c58b81e2ce7 Mon Sep 17 00:00:00 2001 From: Peter Kotula Date: Sun, 5 Feb 2023 12:20:39 +0100 Subject: [PATCH 04/14] slf4j2 --- docs/index.md | 5 +- docs/sidebars.js | 5 +- docs/slf4j1-bridge.md | 2 +- docs/{slf4j.md => slf4j1.md} | 16 +- docs/slf4j2.md | 180 ++++++++++++++++++ .../src/main/resources/logback.xml | 6 +- .../zio/logging/example/Slf4jSimpleApp.scala | 2 +- .../src/main/resources/logback.xml | 4 - .../zio/logging/example/Slf4j2SimpleApp.scala | 2 +- .../main/scala/zio/logging/slf4j/SLF4J.scala | 19 +- .../scala/zio/logging/backend/SLF4JSpec.scala | 16 +- 11 files changed, 209 insertions(+), 48 deletions(-) rename docs/{slf4j.md => slf4j1.md} (84%) create mode 100644 docs/slf4j2.md diff --git a/docs/index.md b/docs/index.md index 9c7fe876..5bfd1f5c 100644 --- a/docs/index.md +++ b/docs/index.md @@ -36,9 +36,12 @@ There are also some optional dependencies: // JPL integration libraryDependencies += "dev.zio" %% "zio-logging-jpl" % "@VERSION@" -// SLF4j integration +// SLF4j v1 integration libraryDependencies += "dev.zio" %% "zio-logging-slf4j" % "@VERSION@" +// SLF4j v2 integration +libraryDependencies += "dev.zio" %% "zio-logging-slf4j2" % "@VERSION@" + // Using ZIO Logging for SLF4j v1 loggers, usually third-party non-ZIO libraries libraryDependencies += "dev.zio" %% "zio-logging-slf4j-bridge" % "@VERSION@" diff --git a/docs/sidebars.js b/docs/sidebars.js index d2e0987e..b2fea302 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -11,9 +11,10 @@ const sidebars = { 'log-filter', 'console-logger', 'jpl', - 'slf4j', - 'slf4j1-bridge', + 'slf4j2', + 'slf4j1', 'slf4j2-bridge', + 'slf4j1-bridge', 'metrics', 'testing' ] diff --git a/docs/slf4j1-bridge.md b/docs/slf4j1-bridge.md index c601e292..b848b425 100644 --- a/docs/slf4j1-bridge.md +++ b/docs/slf4j1-bridge.md @@ -3,7 +3,7 @@ id: slf4j1-bridge title: "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: +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): ```scala libraryDependencies += "dev.zio" %% "zio-logging-slf4j-bridge" % "@VERSION@" diff --git a/docs/slf4j.md b/docs/slf4j1.md similarity index 84% rename from docs/slf4j.md rename to docs/slf4j1.md index edbccb5c..8280a783 100644 --- a/docs/slf4j.md +++ b/docs/slf4j1.md @@ -3,7 +3,7 @@ id: slf4j title: "SLF4J" --- -The Simple Logging Facade for Java ([`SLF4J`](https://www.slf4j.org/)) serves as a simple facade or abstraction for various logging frameworks (e.g. java.util.logging, logback, log4j). +The Simple Logging Facade for Java ([`SLF4J v1`](https://www.slf4j.org/) - working with JDK8) serves as a simple facade or abstraction for various logging frameworks (e.g. java.util.logging, logback, log4j). In order to use this logging backend, we need to add the following line in our build.sbt file: @@ -90,7 +90,7 @@ Logback configuration: - %d{HH:mm:ss.SSS} [%thread] trace_id=%X{trace_id} user_id=%X{user} %-5level %logger{36} %msg%n + %d{HH:mm:ss.SSS} [%thread] [%mdc] %-5level %logger{36} %msg%n @@ -106,12 +106,12 @@ Logback configuration: Expected Console Output: ``` -13:49:14.060 [ZScheduler-Worker-2] trace_id= user_id= INFO zio.logging.example.Slf4jSimpleApp Start -13:49:14.090 [ZScheduler-Worker-12] trace_id=98cdf7b7-dc09-4935-8cbc-4a3399b67d2a user_id=3b6163f5-0677-4909-b17f-c181b53312b6 INFO zio.logging.example.UserOperation Starting user operation -13:49:14.091 [ZScheduler-Worker-8] trace_id=98cdf7b7-dc09-4935-8cbc-4a3399b67d2a user_id=75e17c12-d397-455c-89b1-4e5292d860ba INFO zio.logging.example.UserOperation Starting user operation -13:49:14.616 [ZScheduler-Worker-0] trace_id=98cdf7b7-dc09-4935-8cbc-4a3399b67d2a user_id=3b6163f5-0677-4909-b17f-c181b53312b6 INFO zio.logging.example.UserOperation Stopping user operation -13:49:14.616 [ZScheduler-Worker-10] trace_id=98cdf7b7-dc09-4935-8cbc-4a3399b67d2a user_id=75e17c12-d397-455c-89b1-4e5292d860ba INFO zio.logging.example.UserOperation Stopping user operation -13:49:14.626 [ZScheduler-Worker-0] trace_id= user_id= INFO zio.logging.example.Slf4jSimpleApp Done +12:16:21.951 [ZScheduler-Worker-8] [] INFO zio.logging.example.Slf4jSimpleApp Start +12:16:22.024 [ZScheduler-Worker-12] [user=0e3bd69c-ee62-4096-82b2-593760d3fb19, trace_id=6e956bcf-d534-4c16-9402-fb6bca13c9ab] INFO zio.logging.example.UserOperation Starting user operation +12:16:22.024 [ZScheduler-Worker-10] [user=869ed4c7-924d-4c02-ab5c-c30c1996a139, trace_id=6e956bcf-d534-4c16-9402-fb6bca13c9ab] INFO zio.logging.example.UserOperation Starting user operation +12:16:22.592 [ZScheduler-Worker-14] [user=869ed4c7-924d-4c02-ab5c-c30c1996a139, trace_id=6e956bcf-d534-4c16-9402-fb6bca13c9ab] INFO zio.logging.example.UserOperation Stopping user operation +12:16:22.592 [ZScheduler-Worker-1] [user=0e3bd69c-ee62-4096-82b2-593760d3fb19, trace_id=6e956bcf-d534-4c16-9402-fb6bca13c9ab] INFO zio.logging.example.UserOperation Stopping user operation +12:16:22.598 [ZScheduler-Worker-14] [] INFO zio.logging.example.Slf4jSimpleApp Done ``` ### Custom tracing annotation diff --git a/docs/slf4j2.md b/docs/slf4j2.md new file mode 100644 index 00000000..0ec4edc2 --- /dev/null +++ b/docs/slf4j2.md @@ -0,0 +1,180 @@ +--- +id: slf4j +title: "SLF4J" +--- + +The Simple Logging Facade for Java ([`SLF4J v2`](https://www.slf4j.org/) - using JDK9+ module system [JPMS](http://openjdk.java.net/projects/jigsaw/spec/)) serves as a simple facade or abstraction for various logging frameworks (e.g. java.util.logging, logback, log4j). + +In order to use this logging backend, we need to add the following line in our build.sbt file: + +```scala +libraryDependencies += "dev.zio" %% "zio-logging-slf4j2" % "@VERSION@" +``` + +Logger layer: + +```scala +import zio.logging.backend.SLF4J + +val logger = Runtime.removeDefaultLoggers >>> SLF4J.slf4j +``` + +Default `SLF4J` logger setup: +* logger name (by default) is extracted from `zio.Trace` + * for example, trace `zio.logging.example.Slf4jSimpleApp.run(Slf4jSimpleApp.scala:17)` will have `zio.logging.example.Slf4jSimpleApp` as logger name + * NOTE: custom logger name may be set by `zio.logging.loggerName` aspect +* all annotations (logger name and log marker name annotations are excluded) are passed like [key-value pairs](https://www.slf4j.org/manual.html#fluent) +* cause is logged as throwable + +See also [LogFormat and LogAppender](formatting-log-records.md#logformat-and-logappender) + +Custom logger name set by aspect: + +```scala +ZIO.logInfo("Starting user operation") @@ zio.logging.loggerName("zio.logging.example.UserOperation") +``` + +Log marker name set by aspect: + +```scala +ZIO.logInfo("Confidential user operation") @@ SLF4J.logMarkerName("CONFIDENTIAL") +``` + + +## Examples + +You can find the source code [here](https://github.com/zio/zio-logging/tree/master/examples/src/main/scala/zio/logging/example) + + +### SLF4J logger name and annotations + +[//]: # (TODO: make snippet type-checked using mdoc) + +```scala +package zio.logging.example + +import zio.logging.LogAnnotation +import zio.logging.backend.SLF4J +import zio.{ ExitCode, Runtime, Scope, ZIO, ZIOAppDefault, _ } + +import java.util.UUID + +object Slf4jSimpleApp extends ZIOAppDefault { + + override val bootstrap: ZLayer[ZIOAppArgs, Any, Any] = Runtime.removeDefaultLoggers >>> SLF4J.slf4j + + private val users = List.fill(2)(UUID.randomUUID()) + + override def run: ZIO[Scope, Any, ExitCode] = + for { + _ <- ZIO.logInfo("Start") + traceId <- ZIO.succeed(UUID.randomUUID()) + _ <- ZIO.foreachPar(users) { uId => + { + ZIO.logInfo("Starting user operation") *> + ZIO.logInfo("Confidential user operation") @@ SLF4J.logMarkerName("CONFIDENTIAL") *> + ZIO.sleep(500.millis) *> + ZIO.logInfo("Stopping user operation") + } @@ ZIOAspect.annotated("user", uId.toString) + } @@ LogAnnotation.TraceId(traceId) @@ zio.logging.loggerName("zio.logging.example.UserOperation") + _ <- ZIO.logInfo("Done") + } yield ExitCode.success + +} +``` + +Logback configuration: + +```xml + + + + + %d{HH:mm:ss.SSS} [%thread] [%kvp] %-5level %logger{36} %msg%n + + + + CONFIDENTIAL_FILTER + CONFIDENTIAL + DENY + + + + + +``` + +Expected Console Output: +``` +12:13:17.495 [ZScheduler-Worker-8] [] INFO zio.logging.example.Slf4j2SimpleApp Start +12:13:17.601 [ZScheduler-Worker-11] [trace_id="7826dd28-7e37-4b54-b84d-05399bb6cbeb" user="7b6a1af5-bad2-4846-aa49-138d31b40a24"] INFO zio.logging.example.UserOperation Starting user operation +12:13:17.601 [ZScheduler-Worker-10] [trace_id="7826dd28-7e37-4b54-b84d-05399bb6cbeb" user="4df9cdbc-e770-4bc9-b884-756e671a6435"] INFO zio.logging.example.UserOperation Starting user operation +12:13:18.167 [ZScheduler-Worker-13] [trace_id="7826dd28-7e37-4b54-b84d-05399bb6cbeb" user="7b6a1af5-bad2-4846-aa49-138d31b40a24"] INFO zio.logging.example.UserOperation Stopping user operation +12:13:18.167 [ZScheduler-Worker-15] [trace_id="7826dd28-7e37-4b54-b84d-05399bb6cbeb" user="4df9cdbc-e770-4bc9-b884-756e671a6435"] INFO zio.logging.example.UserOperation Stopping user operation +12:13:18.173 [ZScheduler-Worker-13] [] INFO zio.logging.example.Slf4j2SimpleApp Done +``` + +### Custom tracing annotation + +Following application has custom aspect `currentTracingSpanAspect` implementation which taking current span from `Tracing` service +which then is added to logs by log annotation. + +```scala +package zio.logging.example + +import zio.logging.backend.SLF4J +import zio.{ ExitCode, Runtime, Scope, ZIO, ZIOAppDefault, _ } + +import java.util.UUID + +trait Tracing { + def getCurrentSpan(): UIO[String] +} + +final class LiveTracing extends Tracing { + override def getCurrentSpan(): UIO[String] = ZIO.succeed(UUID.randomUUID().toString) +} + +object LiveTracing { + val layer: ULayer[Tracing] = ZLayer.succeed(new LiveTracing) +} + +object CustomTracingAnnotationApp extends ZIOAppDefault { + + private def currentTracingSpanAspect(key: String): ZIOAspect[Nothing, Tracing, Nothing, Any, Nothing, Any] = + new ZIOAspect[Nothing, Tracing, Nothing, Any, Nothing, Any] { + def apply[R <: Tracing, E, A](zio: ZIO[R, E, A])(implicit trace: Trace): ZIO[R, E, A] = + ZIO.serviceWithZIO[Tracing] { tracing => + tracing.getCurrentSpan().flatMap { span => + ZIO.logAnnotate(key, span)(zio) + } + } + } + + override val bootstrap: ZLayer[ZIOAppArgs, Any, Any] = Runtime.removeDefaultLoggers >>> SLF4J.slf4j + + private val users = List.fill(2)(UUID.randomUUID()) + + override def run: ZIO[Scope, Any, ExitCode] = + (for { + _ <- ZIO.foreachPar(users) { uId => + { + ZIO.logInfo("Starting operation") *> + ZIO.sleep(500.millis) *> + ZIO.logInfo("Stopping operation") + } @@ ZIOAspect.annotated("user", uId.toString) + } @@ currentTracingSpanAspect("trace_id") + _ <- ZIO.logInfo("Done") + } yield ExitCode.success).provide(LiveTracing.layer) + +} +``` + +Expected Console Output: +``` +19:09:57.695 [ZScheduler-Worker-9] trace_id= user_id= INFO z.l.e.CustomTracingAnnotationApp Starting operation +19:09:57.695 [ZScheduler-Worker-9] trace_id=403fe6e9-f666-4688-a609-04813ac26892 user_id=35d36d10-4b64-48fc-bf9d-6b6b37d2f4cc INFO z.l.e.CustomTracingAnnotationApp Starting operation +19:09:58.056 [ZScheduler-Worker-8] trace_id=403fe6e9-f666-4688-a609-04813ac26892 user_id=068a35f2-2633-4404-9522-ffbfabe63730 INFO z.l.e.CustomTracingAnnotationApp Stopping operation +19:09:58.197 [ZScheduler-Worker-10] trace_id=403fe6e9-f666-4688-a609-04813ac26892 user_id=35d36d10-4b64-48fc-bf9d-6b6b37d2f4cc INFO z.l.e.CustomTracingAnnotationApp Stopping operation +19:09:58.202 [ZScheduler-Worker-13] trace_id= user_id= INFO z.l.e.CustomTracingAnnotationApp Done +``` diff --git a/examples/slf4j-logback/src/main/resources/logback.xml b/examples/slf4j-logback/src/main/resources/logback.xml index 15f9c98a..7fae9aee 100644 --- a/examples/slf4j-logback/src/main/resources/logback.xml +++ b/examples/slf4j-logback/src/main/resources/logback.xml @@ -1,20 +1,16 @@ - - %d{HH:mm:ss.SSS} [%thread] trace_id=%X{trace_id} user_id=%X{user} %-5level %logger{36} %msg%n + %d{HH:mm:ss.SSS} [%thread] [%mdc] %-5level %logger{36} %msg%n - CONFIDENTIAL_FILTER CONFIDENTIAL DENY - - \ No newline at end of file diff --git a/examples/slf4j-logback/src/main/scala/zio/logging/example/Slf4jSimpleApp.scala b/examples/slf4j-logback/src/main/scala/zio/logging/example/Slf4jSimpleApp.scala index c704c3d8..3714a484 100644 --- a/examples/slf4j-logback/src/main/scala/zio/logging/example/Slf4jSimpleApp.scala +++ b/examples/slf4j-logback/src/main/scala/zio/logging/example/Slf4jSimpleApp.scala @@ -38,7 +38,7 @@ object Slf4jSimpleApp extends ZIOAppDefault { ZIO.sleep(500.millis) *> ZIO.logInfo("Stopping user operation") } @@ ZIOAspect.annotated("user", uId.toString) - } @@ LogAnnotation.TraceId(traceId) @@ SLF4J.loggerName("zio.logging.example.UserOperation") + } @@ LogAnnotation.TraceId(traceId) @@ zio.logging.loggerName("zio.logging.example.UserOperation") _ <- ZIO.logInfo("Done") } yield ExitCode.success diff --git a/examples/slf4j2-logback/src/main/resources/logback.xml b/examples/slf4j2-logback/src/main/resources/logback.xml index 1f10faf1..a13bdf45 100644 --- a/examples/slf4j2-logback/src/main/resources/logback.xml +++ b/examples/slf4j2-logback/src/main/resources/logback.xml @@ -1,20 +1,16 @@ - %d{HH:mm:ss.SSS} [%thread] [%kvp] %-5level %logger{36} %msg%n - CONFIDENTIAL_FILTER CONFIDENTIAL DENY - - \ No newline at end of file diff --git a/examples/slf4j2-logback/src/main/scala/zio/logging/example/Slf4j2SimpleApp.scala b/examples/slf4j2-logback/src/main/scala/zio/logging/example/Slf4j2SimpleApp.scala index 6a00daf9..9b4bc3a4 100644 --- a/examples/slf4j2-logback/src/main/scala/zio/logging/example/Slf4j2SimpleApp.scala +++ b/examples/slf4j2-logback/src/main/scala/zio/logging/example/Slf4j2SimpleApp.scala @@ -38,7 +38,7 @@ object Slf4j2SimpleApp extends ZIOAppDefault { ZIO.sleep(500.millis) *> ZIO.logInfo("Stopping user operation") } @@ ZIOAspect.annotated("user", uId.toString) - } @@ LogAnnotation.TraceId(traceId) @@ SLF4J.loggerName("zio.logging.example.UserOperation") + } @@ LogAnnotation.TraceId(traceId) @@ zio.logging.loggerName("zio.logging.example.UserOperation") _ <- ZIO.logInfo("Done") } yield ExitCode.success diff --git a/slf4j2/src/main/scala/zio/logging/slf4j/SLF4J.scala b/slf4j2/src/main/scala/zio/logging/slf4j/SLF4J.scala index 55d5df1a..12cb2cf9 100644 --- a/slf4j2/src/main/scala/zio/logging/slf4j/SLF4J.scala +++ b/slf4j2/src/main/scala/zio/logging/slf4j/SLF4J.scala @@ -23,12 +23,6 @@ import zio.{ Cause, FiberFailure, FiberId, FiberRefs, LogLevel, LogSpan, Runtime object SLF4J { - /** - * log aspect annotation key for slf4j logger name - */ - @deprecated - val loggerNameAnnotationKey = "logger_name" - /** * log aspect annotation key for slf4j marker name */ @@ -39,18 +33,9 @@ object SLF4J { */ val logFormatDefault: LogFormat = LogFormat.allAnnotations(excludeKeys = - Set(loggerNameAnnotationKey, logMarkerNameAnnotationKey) + Set(zio.logging.loggerNameAnnotationKey, logMarkerNameAnnotationKey) ) + LogFormat.line + LogFormat.cause - /** - * slf4j logger name aspect, by this aspect is possible to change default logger name (default logger name is extracted from [[Trace]]) - * - * annotation key: [[SLF4J.loggerNameAnnotationKey]] - */ - @deprecated - def loggerName(value: String): ZIOAspect[Nothing, Any, Nothing, Any, Nothing, Any] = - ZIOAspect.annotated(loggerNameAnnotationKey, value) - /** * slf4j marker name aspect * @@ -210,7 +195,7 @@ object SLF4J { spans: List[LogSpan], annotations: Map[String, String] ): Unit = { - val slf4jLoggerName = annotations.getOrElse(loggerNameAnnotationKey, loggerName(trace)) + val slf4jLoggerName = annotations.getOrElse(zio.logging.loggerNameAnnotationKey, loggerName(trace)) val slf4jLogger = LoggerFactory.getLogger(slf4jLoggerName) val slf4jMarkerName = annotations.get(logMarkerNameAnnotationKey) val slf4jMarker = slf4jMarkerName.map(n => MarkerFactory.getMarker(n)) diff --git a/slf4j2/src/test/scala/zio/logging/backend/SLF4JSpec.scala b/slf4j2/src/test/scala/zio/logging/backend/SLF4JSpec.scala index 961cdc81..61363c15 100644 --- a/slf4j2/src/test/scala/zio/logging/backend/SLF4JSpec.scala +++ b/slf4j2/src/test/scala/zio/logging/backend/SLF4JSpec.scala @@ -115,7 +115,7 @@ object SLF4JSpec extends ZIOSpecDefault { } }.provide(loggerTraceAnnotation), test("log all annotations into key values with custom logger name") { - (startStop() @@ SLF4J.loggerName("my-logger")).map { case (traceId, users) => + (startStop() @@ zio.logging.loggerName("my-logger")).map { case (traceId, users) => val loggerOutput = TestAppender.logOutput startStopAssert(loggerOutput, "my-logger") && assert( loggerOutput.map(_.keyValues.get(LogAnnotation.TraceId.name)) @@ -125,7 +125,7 @@ object SLF4JSpec extends ZIOSpecDefault { ) ) && assert(loggerOutput.map(_.keyValues.get("user")))( equalTo(users.flatMap(u => Chunk.fill(2)(Some(u.toString))) :+ None) - ) && assert(loggerOutput.map(_.keyValues.contains(SLF4J.loggerNameAnnotationKey)))( + ) && assert(loggerOutput.map(_.keyValues.contains(zio.logging.loggerNameAnnotationKey)))( equalTo(Chunk.fill(5)(false)) ) } @@ -135,15 +135,15 @@ object SLF4JSpec extends ZIOSpecDefault { for { traceId <- ZIO.succeed(UUID.randomUUID()) _ = TestAppender.reset() - _ <- ZIO.logInfo("Start") @@ SLF4J.loggerName("root-logger") + _ <- ZIO.logInfo("Start") @@ zio.logging.loggerName("root-logger") _ <- ZIO.foreach(users) { uId => { ZIO.logInfo("Starting user operation") *> ZIO.sleep(500.millis) *> ZIO.logInfo( "Stopping user operation" ) - } @@ ZIOAspect.annotated("user", uId.toString) @@ SLF4J.loggerName("user-logger") - } @@ LogAnnotation.TraceId(traceId) @@ SLF4J.loggerName("user-root-logger") - _ <- ZIO.logInfo("Done") @@ SLF4J.loggerName("root-logger") + } @@ ZIOAspect.annotated("user", uId.toString) @@ zio.logging.loggerName("user-logger") + } @@ LogAnnotation.TraceId(traceId) @@ zio.logging.loggerName("user-root-logger") + _ <- ZIO.logInfo("Done") @@ zio.logging.loggerName("root-logger") } yield { val loggerOutput = TestAppender.logOutput assertTrue(loggerOutput.forall(_.logLevel == LogLevel.Info)) && assert(loggerOutput.map(_.message))( @@ -178,12 +178,12 @@ object SLF4JSpec extends ZIOSpecDefault { } }.provide(loggerLineCause), test("log error with cause with custom logger name") { - (someError() @@ SLF4J.loggerName("my-logger")).map { _ => + (someError() @@ zio.logging.loggerName("my-logger")).map { _ => val loggerOutput = TestAppender.logOutput someErrorAssert(loggerOutput, "my-logger") && assertTrue( loggerOutput(0).cause.exists(_.contains("input < 1")) ) && - assertTrue(!loggerOutput(0).keyValues.contains(SLF4J.loggerNameAnnotationKey)) + assertTrue(!loggerOutput(0).keyValues.contains(zio.logging.loggerNameAnnotationKey)) } }.provide(loggerLineCause), test("log error without cause") { From dfc892d5215077554e8c9babeae9592e2c98b40c Mon Sep 17 00:00:00 2001 From: Peter Kotula Date: Sun, 5 Feb 2023 15:31:42 +0100 Subject: [PATCH 05/14] doc fix --- docs/slf4j1.md | 4 ++-- docs/slf4j2.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/slf4j1.md b/docs/slf4j1.md index 8280a783..e2ee3771 100644 --- a/docs/slf4j1.md +++ b/docs/slf4j1.md @@ -1,6 +1,6 @@ --- -id: slf4j -title: "SLF4J" +id: slf4j1 +title: "SLF4J v1" --- The Simple Logging Facade for Java ([`SLF4J v1`](https://www.slf4j.org/) - working with JDK8) serves as a simple facade or abstraction for various logging frameworks (e.g. java.util.logging, logback, log4j). diff --git a/docs/slf4j2.md b/docs/slf4j2.md index 0ec4edc2..6818b4bd 100644 --- a/docs/slf4j2.md +++ b/docs/slf4j2.md @@ -1,6 +1,6 @@ --- -id: slf4j -title: "SLF4J" +id: slf4j2 +title: "SLF4J v2" --- The Simple Logging Facade for Java ([`SLF4J v2`](https://www.slf4j.org/) - using JDK9+ module system [JPMS](http://openjdk.java.net/projects/jigsaw/spec/)) serves as a simple facade or abstraction for various logging frameworks (e.g. java.util.logging, logback, log4j). From 823b7f70263bfe16d08fd9360f329e3ac3ae73af Mon Sep 17 00:00:00 2001 From: Peter Kotula Date: Sun, 5 Feb 2023 16:03:58 +0100 Subject: [PATCH 06/14] doc fix, mimaCheck fix --- README.md | 2 +- build.sbt | 12 ++-- docs/index.md | 2 +- docs/jpl.md | 2 +- docs/metrics.md | 2 +- docs/slf4j1.md | 12 ++-- docs/slf4j2-bridge.md | 2 + docs/slf4j2.md | 69 +------------------ .../example/Slf4jBridgeExampleApp.scala | 0 9 files changed, 20 insertions(+), 83 deletions(-) rename examples/{slf4j-bridge => slf4j2-bridge}/src/main/scala/zio/logging/example/Slf4jBridgeExampleApp.scala (100%) diff --git a/README.md b/README.md index dc6c7c1a..1c7cb3c3 100644 --- a/README.md +++ b/README.md @@ -81,7 +81,7 @@ timestamp=2022-10-28T18:40:25.54676+02:00 level=ERROR thread=zio-fiber-0 messag at zio.logging.example.SimpleApp.run(SimpleApp.scala:14) ``` -You can find the source code of examples [here](https://github.com/zio/zio-logging/tree/master/examples/src/main/scala/zio/logging/example) +You can find the source code of examples [here](https://github.com/zio/zio-logging/tree/master/examples) ## Documentation diff --git a/build.sbt b/build.sbt index 17d9570a..43dbc8b9 100644 --- a/build.sbt +++ b/build.sbt @@ -48,7 +48,7 @@ addCommandAlias( addCommandAlias( "mimaChecks", - "all coreJVM/mimaReportBinaryIssues slf4j/mimaReportBinaryIssues slf4j2/mimaReportBinaryIssues slf4jBridge/mimaReportBinaryIssues" + "all coreJVM/mimaReportBinaryIssues slf4j/mimaReportBinaryIssues slf4jBridge/mimaReportBinaryIssues" ) lazy val root = project @@ -67,7 +67,7 @@ lazy val root = project benchmarks, examplesCore, examplesJpl, - examplesSlf4jBridge, + examplesSlf4j2Bridge, examplesSlf4jLogback, examplesSlf4j2Logback, examplesSlf4jLog4j, @@ -248,10 +248,10 @@ lazy val examplesJpl = project publish / skip := true ) -lazy val examplesSlf4jBridge = project - .in(file("examples/slf4j-bridge")) - .dependsOn(slf4jBridge) - .settings(stdSettings("zio-logging-examples-slf4j-bridge")) +lazy val examplesSlf4j2Bridge = project + .in(file("examples/slf4j2-bridge")) + .dependsOn(slf4j2Bridge) + .settings(stdSettings("zio-logging-examples-slf4j2-bridge")) .settings( publish / skip := true ) diff --git a/docs/index.md b/docs/index.md index 5bfd1f5c..2a0ccff2 100644 --- a/docs/index.md +++ b/docs/index.md @@ -84,4 +84,4 @@ timestamp=2022-10-28T18:40:25.54676+02:00 level=ERROR thread=zio-fiber-0 messag at zio.logging.example.SimpleApp.run(SimpleApp.scala:14) ``` -You can find the source code of examples [here](https://github.com/zio/zio-logging/tree/master/examples/src/main/scala/zio/logging/example) +You can find the source code of examples [here](https://github.com/zio/zio-logging/tree/master/examples) diff --git a/docs/jpl.md b/docs/jpl.md index 8e7d0108..eb116fbe 100644 --- a/docs/jpl.md +++ b/docs/jpl.md @@ -39,7 +39,7 @@ ZIO.logInfo("Starting user operation") @@ zio.logging.loggerName("zio.logging.ex ## Examples -You can find the source code [here](https://github.com/zio/zio-logging/tree/master/examples/src/main/scala/zio/logging/example) +You can find the source code [here](https://github.com/zio/zio-logging/tree/master/examples) ### Java Platform/System logger name and annotations diff --git a/docs/metrics.md b/docs/metrics.md index 698ff0d3..eb9c33ae 100644 --- a/docs/metrics.md +++ b/docs/metrics.md @@ -36,7 +36,7 @@ val layer = zio.logging.logMetricsWith("log_counter", "log_level") ## Examples You can find the source -code [here](https://github.com/zio/zio-logging/tree/master/examples/src/main/scala/zio/logging/example) +code [here](https://github.com/zio/zio-logging/tree/master/examples) ### Console logger with metrics diff --git a/docs/slf4j1.md b/docs/slf4j1.md index e2ee3771..6d0d9af8 100644 --- a/docs/slf4j1.md +++ b/docs/slf4j1.md @@ -43,7 +43,7 @@ ZIO.logInfo("Confidential user operation") @@ SLF4J.logMarkerName("CONFIDENTIAL" ## Examples -You can find the source code [here](https://github.com/zio/zio-logging/tree/master/examples/src/main/scala/zio/logging/example) +You can find the source code [here](https://github.com/zio/zio-logging/tree/master/examples) ### SLF4J logger name and annotations @@ -172,9 +172,9 @@ object CustomTracingAnnotationApp extends ZIOAppDefault { Expected Console Output: ``` -19:09:57.695 [ZScheduler-Worker-9] trace_id= user_id= INFO z.l.e.CustomTracingAnnotationApp Starting operation -19:09:57.695 [ZScheduler-Worker-9] trace_id=403fe6e9-f666-4688-a609-04813ac26892 user_id=35d36d10-4b64-48fc-bf9d-6b6b37d2f4cc INFO z.l.e.CustomTracingAnnotationApp Starting operation -19:09:58.056 [ZScheduler-Worker-8] trace_id=403fe6e9-f666-4688-a609-04813ac26892 user_id=068a35f2-2633-4404-9522-ffbfabe63730 INFO z.l.e.CustomTracingAnnotationApp Stopping operation -19:09:58.197 [ZScheduler-Worker-10] trace_id=403fe6e9-f666-4688-a609-04813ac26892 user_id=35d36d10-4b64-48fc-bf9d-6b6b37d2f4cc INFO z.l.e.CustomTracingAnnotationApp Stopping operation -19:09:58.202 [ZScheduler-Worker-13] trace_id= user_id= INFO z.l.e.CustomTracingAnnotationApp Done +15:53:20.145 [ZScheduler-Worker-9] [user=1abd8458-aefd-4780-88ec-cccd1310d4c8, trace_id=71436dd4-22d5-4e06-aaa7-f3ff7b108037] INFO z.l.e.CustomTracingAnnotationApp Starting operation +15:53:20.145 [ZScheduler-Worker-13] [user=878689e0-da30-49f8-8923-ed915c00db9c, trace_id=71436dd4-22d5-4e06-aaa7-f3ff7b108037] INFO z.l.e.CustomTracingAnnotationApp Starting operation +15:53:20.688 [ZScheduler-Worker-15] [user=1abd8458-aefd-4780-88ec-cccd1310d4c8, trace_id=71436dd4-22d5-4e06-aaa7-f3ff7b108037] INFO z.l.e.CustomTracingAnnotationApp Stopping operation +15:53:20.688 [ZScheduler-Worker-11] [user=878689e0-da30-49f8-8923-ed915c00db9c, trace_id=71436dd4-22d5-4e06-aaa7-f3ff7b108037] INFO z.l.e.CustomTracingAnnotationApp Stopping operation +15:53:20.691 [ZScheduler-Worker-15] [] INFO z.l.e.CustomTracingAnnotationApp Done ``` diff --git a/docs/slf4j2-bridge.md b/docs/slf4j2-bridge.md index b707f233..c116562f 100644 --- a/docs/slf4j2-bridge.md +++ b/docs/slf4j2-bridge.md @@ -58,6 +58,8 @@ ZIO logging. Enabling both causes circular logging and makes no sense. ## Examples +You can find the source code [here](https://github.com/zio/zio-logging/tree/master/examples) + ### SLF4J bridge with JSON console logger [//]: # (TODO: make snippet type-checked using mdoc) diff --git a/docs/slf4j2.md b/docs/slf4j2.md index 6818b4bd..2648e83e 100644 --- a/docs/slf4j2.md +++ b/docs/slf4j2.md @@ -43,7 +43,7 @@ ZIO.logInfo("Confidential user operation") @@ SLF4J.logMarkerName("CONFIDENTIAL" ## Examples -You can find the source code [here](https://github.com/zio/zio-logging/tree/master/examples/src/main/scala/zio/logging/example) +You can find the source code [here](https://github.com/zio/zio-logging/tree/master/examples) ### SLF4J logger name and annotations @@ -112,69 +112,4 @@ Expected Console Output: 12:13:18.167 [ZScheduler-Worker-13] [trace_id="7826dd28-7e37-4b54-b84d-05399bb6cbeb" user="7b6a1af5-bad2-4846-aa49-138d31b40a24"] INFO zio.logging.example.UserOperation Stopping user operation 12:13:18.167 [ZScheduler-Worker-15] [trace_id="7826dd28-7e37-4b54-b84d-05399bb6cbeb" user="4df9cdbc-e770-4bc9-b884-756e671a6435"] INFO zio.logging.example.UserOperation Stopping user operation 12:13:18.173 [ZScheduler-Worker-13] [] INFO zio.logging.example.Slf4j2SimpleApp Done -``` - -### Custom tracing annotation - -Following application has custom aspect `currentTracingSpanAspect` implementation which taking current span from `Tracing` service -which then is added to logs by log annotation. - -```scala -package zio.logging.example - -import zio.logging.backend.SLF4J -import zio.{ ExitCode, Runtime, Scope, ZIO, ZIOAppDefault, _ } - -import java.util.UUID - -trait Tracing { - def getCurrentSpan(): UIO[String] -} - -final class LiveTracing extends Tracing { - override def getCurrentSpan(): UIO[String] = ZIO.succeed(UUID.randomUUID().toString) -} - -object LiveTracing { - val layer: ULayer[Tracing] = ZLayer.succeed(new LiveTracing) -} - -object CustomTracingAnnotationApp extends ZIOAppDefault { - - private def currentTracingSpanAspect(key: String): ZIOAspect[Nothing, Tracing, Nothing, Any, Nothing, Any] = - new ZIOAspect[Nothing, Tracing, Nothing, Any, Nothing, Any] { - def apply[R <: Tracing, E, A](zio: ZIO[R, E, A])(implicit trace: Trace): ZIO[R, E, A] = - ZIO.serviceWithZIO[Tracing] { tracing => - tracing.getCurrentSpan().flatMap { span => - ZIO.logAnnotate(key, span)(zio) - } - } - } - - override val bootstrap: ZLayer[ZIOAppArgs, Any, Any] = Runtime.removeDefaultLoggers >>> SLF4J.slf4j - - private val users = List.fill(2)(UUID.randomUUID()) - - override def run: ZIO[Scope, Any, ExitCode] = - (for { - _ <- ZIO.foreachPar(users) { uId => - { - ZIO.logInfo("Starting operation") *> - ZIO.sleep(500.millis) *> - ZIO.logInfo("Stopping operation") - } @@ ZIOAspect.annotated("user", uId.toString) - } @@ currentTracingSpanAspect("trace_id") - _ <- ZIO.logInfo("Done") - } yield ExitCode.success).provide(LiveTracing.layer) - -} -``` - -Expected Console Output: -``` -19:09:57.695 [ZScheduler-Worker-9] trace_id= user_id= INFO z.l.e.CustomTracingAnnotationApp Starting operation -19:09:57.695 [ZScheduler-Worker-9] trace_id=403fe6e9-f666-4688-a609-04813ac26892 user_id=35d36d10-4b64-48fc-bf9d-6b6b37d2f4cc INFO z.l.e.CustomTracingAnnotationApp Starting operation -19:09:58.056 [ZScheduler-Worker-8] trace_id=403fe6e9-f666-4688-a609-04813ac26892 user_id=068a35f2-2633-4404-9522-ffbfabe63730 INFO z.l.e.CustomTracingAnnotationApp Stopping operation -19:09:58.197 [ZScheduler-Worker-10] trace_id=403fe6e9-f666-4688-a609-04813ac26892 user_id=35d36d10-4b64-48fc-bf9d-6b6b37d2f4cc INFO z.l.e.CustomTracingAnnotationApp Stopping operation -19:09:58.202 [ZScheduler-Worker-13] trace_id= user_id= INFO z.l.e.CustomTracingAnnotationApp Done -``` +``` \ No newline at end of file diff --git a/examples/slf4j-bridge/src/main/scala/zio/logging/example/Slf4jBridgeExampleApp.scala b/examples/slf4j2-bridge/src/main/scala/zio/logging/example/Slf4jBridgeExampleApp.scala similarity index 100% rename from examples/slf4j-bridge/src/main/scala/zio/logging/example/Slf4jBridgeExampleApp.scala rename to examples/slf4j2-bridge/src/main/scala/zio/logging/example/Slf4jBridgeExampleApp.scala From bedfdfba300dd2cae00ba289c9d0f8531182d5b8 Mon Sep 17 00:00:00 2001 From: Peter Kotula Date: Sun, 5 Feb 2023 16:44:21 +0100 Subject: [PATCH 07/14] ci fix --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 19f4316d..a7fa5360 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -76,7 +76,7 @@ jobs: run: sbt ++${{ matrix.scala }}! testJVM8 - name: Compile additional subprojects if: ${{ ( startsWith(matrix.scala, '2.12.') || startsWith(matrix.scala, '2.13.') ) && endsWith(matrix.java, '.11') }} - run: sbt ++${{ matrix.scala }}! examples/compile benchmarks/compile + run: sbt ++${{ matrix.scala }}! examplesCore/compile examplesJpl/compile examplesSlf4j2Bridge/compile examplesSlf4jLogback/compile examplesSlf4j2Logback/compile examplesSlf4jLog4j/compile benchmarks/compile ci: runs-on: ubuntu-20.04 From 0652b7180d5445f3cd74ed0bf9dfb71b382d0c2c Mon Sep 17 00:00:00 2001 From: Peter Kotula Date: Sun, 5 Feb 2023 16:58:06 +0100 Subject: [PATCH 08/14] examples fix --- .github/workflows/ci.yml | 2 +- build.sbt | 13 +++++++------ .../src/main/resources/log4j2.xml | 2 +- .../scala/zio/logging/example/Slf4jSimpleApp.scala | 0 4 files changed, 9 insertions(+), 8 deletions(-) rename examples/{slf4j-log4j => slf4j2-log4j}/src/main/resources/log4j2.xml (85%) rename examples/{slf4j-log4j => slf4j2-log4j}/src/main/scala/zio/logging/example/Slf4jSimpleApp.scala (100%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a7fa5360..48a15d82 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -76,7 +76,7 @@ jobs: run: sbt ++${{ matrix.scala }}! testJVM8 - name: Compile additional subprojects if: ${{ ( startsWith(matrix.scala, '2.12.') || startsWith(matrix.scala, '2.13.') ) && endsWith(matrix.java, '.11') }} - run: sbt ++${{ matrix.scala }}! examplesCore/compile examplesJpl/compile examplesSlf4j2Bridge/compile examplesSlf4jLogback/compile examplesSlf4j2Logback/compile examplesSlf4jLog4j/compile benchmarks/compile + run: sbt ++${{ matrix.scala }}! examplesCore/compile examplesJpl/compile examplesSlf4j2Bridge/compile examplesSlf4jLogback/compile examplesSlf4j2Logback/compile examplesSlf4j2Log4j/compile benchmarks/compile ci: runs-on: ubuntu-20.04 diff --git a/build.sbt b/build.sbt index 43dbc8b9..a2d63c0a 100644 --- a/build.sbt +++ b/build.sbt @@ -70,7 +70,7 @@ lazy val root = project examplesSlf4j2Bridge, examplesSlf4jLogback, examplesSlf4j2Logback, - examplesSlf4jLog4j, + examplesSlf4j2Log4j, docs ) @@ -229,14 +229,15 @@ lazy val examplesSlf4j2Logback = project ) ) -lazy val examplesSlf4jLog4j = project - .in(file("examples/slf4j-log4j")) - .dependsOn(slf4j) - .settings(stdSettings("zio-logging-examples-slf4j-log4j")) +lazy val examplesSlf4j2Log4j = project + .in(file("examples/slf4j2-log4j")) + .dependsOn(slf4j2) + .settings(stdSettings("zio-logging-examples-slf4j2-log4j")) .settings( publish / skip := true, libraryDependencies ++= Seq( - "org.apache.logging.log4j" % "log4j-slf4j-impl" % "2.17.2" + "org.apache.logging.log4j" % "log4j-slf4j2-impl" % "2.19.0", + "org.apache.logging.log4j" % "log4j-core" % "2.19.0" ) ) diff --git a/examples/slf4j-log4j/src/main/resources/log4j2.xml b/examples/slf4j2-log4j/src/main/resources/log4j2.xml similarity index 85% rename from examples/slf4j-log4j/src/main/resources/log4j2.xml rename to examples/slf4j2-log4j/src/main/resources/log4j2.xml index 4711e91b..0633972e 100644 --- a/examples/slf4j-log4j/src/main/resources/log4j2.xml +++ b/examples/slf4j2-log4j/src/main/resources/log4j2.xml @@ -2,7 +2,7 @@ - + diff --git a/examples/slf4j-log4j/src/main/scala/zio/logging/example/Slf4jSimpleApp.scala b/examples/slf4j2-log4j/src/main/scala/zio/logging/example/Slf4jSimpleApp.scala similarity index 100% rename from examples/slf4j-log4j/src/main/scala/zio/logging/example/Slf4jSimpleApp.scala rename to examples/slf4j2-log4j/src/main/scala/zio/logging/example/Slf4jSimpleApp.scala From a43f330e17bfb65ef9f84fee275886980a86ac44 Mon Sep 17 00:00:00 2001 From: Peter Kotula Date: Tue, 7 Feb 2023 10:07:49 +0100 Subject: [PATCH 09/14] slf4j2 - key-values as list --- .../main/scala/zio/logging/slf4j/SLF4J.scala | 4 +-- .../main/scala/zio/logging/slf4j/SLF4J.scala | 14 ++++----- .../scala/zio/logging/backend/SLF4JSpec.scala | 31 ++++++++++++++----- .../zio/logging/backend/TestAppender.scala | 6 ++-- 4 files changed, 35 insertions(+), 20 deletions(-) diff --git a/slf4j/src/main/scala/zio/logging/slf4j/SLF4J.scala b/slf4j/src/main/scala/zio/logging/slf4j/SLF4J.scala index eb13b071..c56d7594 100644 --- a/slf4j/src/main/scala/zio/logging/slf4j/SLF4J.scala +++ b/slf4j/src/main/scala/zio/logging/slf4j/SLF4J.scala @@ -134,8 +134,8 @@ object SLF4J { override def appendKeyValue(key: String, appendValue: LogAppender => Unit): Unit = { val builder = new StringBuilder() appendValue(LogAppender.unstructured(builder.append(_))) - builder.toString() - mdc.put(key, builder.toString()) + val value = builder.toString() + mdc.put(key, value) () } diff --git a/slf4j2/src/main/scala/zio/logging/slf4j/SLF4J.scala b/slf4j2/src/main/scala/zio/logging/slf4j/SLF4J.scala index 12cb2cf9..4a6a1472 100644 --- a/slf4j2/src/main/scala/zio/logging/slf4j/SLF4J.scala +++ b/slf4j2/src/main/scala/zio/logging/slf4j/SLF4J.scala @@ -77,10 +77,10 @@ object SLF4J { private def logAppender(slf4jLogger: Logger, slf4jMarker: Option[Marker], logLevel: LogLevel): LogAppender = new LogAppender { self => - val message: StringBuilder = new StringBuilder() - val keyValues: scala.collection.mutable.HashMap[String, String] = - new scala.collection.mutable.HashMap[String, String]() - var throwable: Throwable = null + val message: StringBuilder = new StringBuilder() + val keyValues: scala.collection.mutable.ArrayBuffer[(String, String)] = + new scala.collection.mutable.ArrayBuffer[(String, String)]() + var throwable: Throwable = null /** * cause as throwable @@ -106,7 +106,7 @@ object SLF4J { * all key-value into slf4j key-values */ override def appendKeyValue(key: String, value: String): Unit = { - keyValues.put(key, value) + keyValues.addOne((key, value)) () } @@ -116,8 +116,8 @@ object SLF4J { override def appendKeyValue(key: String, appendValue: LogAppender => Unit): Unit = { val builder = new StringBuilder() appendValue(LogAppender.unstructured(builder.append(_))) - builder.toString() - keyValues.put(key, builder.toString()) + val value = builder.toString() + keyValues.addOne((key, value)) () } diff --git a/slf4j2/src/test/scala/zio/logging/backend/SLF4JSpec.scala b/slf4j2/src/test/scala/zio/logging/backend/SLF4JSpec.scala index 61363c15..f674db25 100644 --- a/slf4j2/src/test/scala/zio/logging/backend/SLF4JSpec.scala +++ b/slf4j2/src/test/scala/zio/logging/backend/SLF4JSpec.scala @@ -95,9 +95,11 @@ object SLF4JSpec extends ZIOSpecDefault { test("log only user annotation into key values") { startStop().map { case (_, users) => val loggerOutput = TestAppender.logOutput - startStopAssert(loggerOutput) && assert(loggerOutput.map(_.keyValues.get(LogAnnotation.TraceId.name)))( + startStopAssert(loggerOutput) && assert( + loggerOutput.map(_.keyValues.find(_._1 == LogAnnotation.TraceId.name).map(_._2)) + )( equalTo(Chunk.fill(5)(None)) - ) && assert(loggerOutput.map(_.keyValues.get("user")))( + ) && assert(loggerOutput.map(_.keyValues.find(_._1 == "user").map(_._2)))( equalTo(users.flatMap(u => Chunk.fill(2)(Some(u.toString))) :+ None) ) } @@ -105,11 +107,13 @@ object SLF4JSpec extends ZIOSpecDefault { test("log only trace annotation into key values") { startStop().map { case (traceId, _) => val loggerOutput = TestAppender.logOutput - startStopAssert(loggerOutput) && assert(loggerOutput.map(_.keyValues.get(LogAnnotation.TraceId.name)))( + startStopAssert(loggerOutput) && assert( + loggerOutput.map(_.keyValues.find(_._1 == LogAnnotation.TraceId.name).map(_._2)) + )( equalTo( Chunk.fill(4)(Some(traceId.toString)) :+ None ) - ) && assert(loggerOutput.map(_.keyValues.get("user")))( + ) && assert(loggerOutput.map(_.keyValues.find(_._1 == "user").map(_._2)))( equalTo(Chunk.fill(5)(None)) ) } @@ -118,14 +122,14 @@ object SLF4JSpec extends ZIOSpecDefault { (startStop() @@ zio.logging.loggerName("my-logger")).map { case (traceId, users) => val loggerOutput = TestAppender.logOutput startStopAssert(loggerOutput, "my-logger") && assert( - loggerOutput.map(_.keyValues.get(LogAnnotation.TraceId.name)) + loggerOutput.map(_.keyValues.find(_._1 == LogAnnotation.TraceId.name).map(_._2)) )( equalTo( Chunk.fill(4)(Some(traceId.toString)) :+ None ) - ) && assert(loggerOutput.map(_.keyValues.get("user")))( + ) && assert(loggerOutput.map(_.keyValues.find(_._1 == "user").map(_._2)))( equalTo(users.flatMap(u => Chunk.fill(2)(Some(u.toString))) :+ None) - ) && assert(loggerOutput.map(_.keyValues.contains(zio.logging.loggerNameAnnotationKey)))( + ) && assert(loggerOutput.map(_.keyValues.exists(_._1 == zio.logging.loggerNameAnnotationKey)))( equalTo(Chunk.fill(5)(false)) ) } @@ -183,9 +187,20 @@ object SLF4JSpec extends ZIOSpecDefault { someErrorAssert(loggerOutput, "my-logger") && assertTrue( loggerOutput(0).cause.exists(_.contains("input < 1")) ) && - assertTrue(!loggerOutput(0).keyValues.contains(zio.logging.loggerNameAnnotationKey)) + assertTrue(!loggerOutput(0).keyValues.exists(_._1 == zio.logging.loggerNameAnnotationKey)) } }.provide(loggerLineCause), + test("log multiple key values with same key") { + for { + _ <- ZIO.succeed(TestAppender.reset()) + _ <- ZIO.logInfo("info") @@ ZIOAspect.annotated(LogAnnotation.UserId.name, "u1") @@ LogAnnotation.UserId("u2") + } yield { + val loggerOutput = TestAppender.logOutput + assertTrue( + loggerOutput(0).keyValues.filter(_._1 == LogAnnotation.UserId.name).map(_._2).toSet == Set("u1", "u2") + ) + } + }.provide(loggerDefault), test("log error without cause") { someError().map { _ => val loggerOutput = TestAppender.logOutput diff --git a/slf4j2/src/test/scala/zio/logging/backend/TestAppender.scala b/slf4j2/src/test/scala/zio/logging/backend/TestAppender.scala index 3d7c19ba..2eaa93b4 100644 --- a/slf4j2/src/test/scala/zio/logging/backend/TestAppender.scala +++ b/slf4j2/src/test/scala/zio/logging/backend/TestAppender.scala @@ -33,14 +33,14 @@ object TestAppender { message: String, timestamp: Long, cause: Option[String], - keyValues: Map[String, String] + keyValues: List[(String, String)] ) object LogEntry { def apply(event: ILoggingEvent): LogEntry = { val keyValues = if (event.getKeyValuePairs != null) { - event.getKeyValuePairs.asScala.map(kv => (kv.key, kv.value.toString)).toMap - } else Map.empty[String, String] + event.getKeyValuePairs.asScala.map(kv => (kv.key, kv.value.toString)).toList + } else List.empty[(String, String)] val cause = Option(event.getThrowableProxy).map(_.getMessage) val level = logLevelMapping(event.getLevel) From c50cba02ffd4ee7326e8ce358bf02f766ed01dd5 Mon Sep 17 00:00:00 2001 From: Peter Kotula Date: Tue, 7 Feb 2023 13:14:06 +0100 Subject: [PATCH 10/14] slf4j2 - key-values as list --- slf4j/src/main/scala/zio/logging/slf4j/SLF4J.scala | 3 +-- slf4j2/src/main/scala/zio/logging/slf4j/SLF4J.scala | 12 +++++------- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/slf4j/src/main/scala/zio/logging/slf4j/SLF4J.scala b/slf4j/src/main/scala/zio/logging/slf4j/SLF4J.scala index c56d7594..13cff5a8 100644 --- a/slf4j/src/main/scala/zio/logging/slf4j/SLF4J.scala +++ b/slf4j/src/main/scala/zio/logging/slf4j/SLF4J.scala @@ -134,8 +134,7 @@ object SLF4J { override def appendKeyValue(key: String, appendValue: LogAppender => Unit): Unit = { val builder = new StringBuilder() appendValue(LogAppender.unstructured(builder.append(_))) - val value = builder.toString() - mdc.put(key, value) + mdc.put(key, builder.toString()) () } diff --git a/slf4j2/src/main/scala/zio/logging/slf4j/SLF4J.scala b/slf4j2/src/main/scala/zio/logging/slf4j/SLF4J.scala index 4a6a1472..fcc9ed83 100644 --- a/slf4j2/src/main/scala/zio/logging/slf4j/SLF4J.scala +++ b/slf4j2/src/main/scala/zio/logging/slf4j/SLF4J.scala @@ -77,10 +77,9 @@ object SLF4J { private def logAppender(slf4jLogger: Logger, slf4jMarker: Option[Marker], logLevel: LogLevel): LogAppender = new LogAppender { self => - val message: StringBuilder = new StringBuilder() - val keyValues: scala.collection.mutable.ArrayBuffer[(String, String)] = - new scala.collection.mutable.ArrayBuffer[(String, String)]() - var throwable: Throwable = null + val message = new StringBuilder() + val keyValues = new scala.collection.mutable.ArrayBuffer[(String, String)]() + var throwable: Throwable = null /** * cause as throwable @@ -106,7 +105,7 @@ object SLF4J { * all key-value into slf4j key-values */ override def appendKeyValue(key: String, value: String): Unit = { - keyValues.addOne((key, value)) + keyValues.append((key, value)) () } @@ -116,8 +115,7 @@ object SLF4J { override def appendKeyValue(key: String, appendValue: LogAppender => Unit): Unit = { val builder = new StringBuilder() appendValue(LogAppender.unstructured(builder.append(_))) - val value = builder.toString() - keyValues.addOne((key, value)) + keyValues.append((key, builder.toString())) () } From 020bce913dfc158f30349ab0706f0ba1b2c37434 Mon Sep 17 00:00:00 2001 From: Peter Kotula Date: Tue, 7 Feb 2023 18:55:44 +0100 Subject: [PATCH 11/14] zio 2.0.7 --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index a2d63c0a..c27e72ba 100644 --- a/build.sbt +++ b/build.sbt @@ -22,7 +22,7 @@ inThisBuild( ) ) -val ZioVersion = "2.0.6" +val ZioVersion = "2.0.7" val slf4jVersion = "1.7.36" val slf4j2Version = "2.0.6" val logbackVersion = "1.2.11" From d949b07dd1714d758e903597105a61e7b43457bc Mon Sep 17 00:00:00 2001 From: Peter Kotula Date: Tue, 7 Feb 2023 20:35:09 +0100 Subject: [PATCH 12/14] cause to throwable --- jpl/src/main/scala/zio/logging/backend/JPL.scala | 6 +++++- jpl/src/test/scala/zio/logging/backend/JPLSpec.scala | 10 +++++++--- .../test/scala/zio/logging/backend/TestAppender.scala | 2 +- .../test/scala/zio/logging/backend/TestJPLogger.scala | 2 +- slf4j/src/main/scala/zio/logging/slf4j/SLF4J.scala | 6 +++++- .../src/test/scala/zio/logging/backend/SLF4JSpec.scala | 6 +++--- .../test/scala/zio/logging/backend/TestAppender.scala | 6 +++--- slf4j2/src/main/scala/zio/logging/slf4j/SLF4J.scala | 6 +++++- .../src/test/scala/zio/logging/backend/SLF4JSpec.scala | 4 ++-- .../test/scala/zio/logging/backend/TestAppender.scala | 6 +++--- 10 files changed, 35 insertions(+), 19 deletions(-) diff --git a/jpl/src/main/scala/zio/logging/backend/JPL.scala b/jpl/src/main/scala/zio/logging/backend/JPL.scala index ed6de70d..6c42f35e 100644 --- a/jpl/src/main/scala/zio/logging/backend/JPL.scala +++ b/jpl/src/main/scala/zio/logging/backend/JPL.scala @@ -81,7 +81,11 @@ object JPL { */ override def appendCause(cause: Cause[Any]): Unit = { if (!cause.isEmpty) { - throwable = FiberFailure(cause) + cause match { + case Cause.Die(t, _) => throwable = t + case Cause.Fail(t: Throwable, _) => throwable = t + case _ => throwable = FiberFailure(cause) + } } () } diff --git a/jpl/src/test/scala/zio/logging/backend/JPLSpec.scala b/jpl/src/test/scala/zio/logging/backend/JPLSpec.scala index 1e70986a..6673fea8 100644 --- a/jpl/src/test/scala/zio/logging/backend/JPLSpec.scala +++ b/jpl/src/test/scala/zio/logging/backend/JPLSpec.scala @@ -215,19 +215,23 @@ object JPLSpec extends ZIOSpecDefault { test("log error with cause") { someError().map { _ => val loggerOutput = TestAppender.logOutput - someErrorAssert(loggerOutput) && assertTrue(loggerOutput(0).cause.exists(_.contains("input < 1"))) + someErrorAssert(loggerOutput) && assertTrue(loggerOutput(0).cause.exists(_.getMessage.contains("input < 1"))) } }.provide(loggerLineCause), test("log error with cause with custom logger name - legacy") { (someError() @@ JPL.loggerName("my-logger")).map { _ => val loggerOutput = TestAppender.logOutput - someErrorAssert(loggerOutput, "my-logger") && assertTrue(loggerOutput(0).cause.exists(_.contains("input < 1"))) + someErrorAssert(loggerOutput, "my-logger") && assertTrue( + loggerOutput(0).cause.exists(_.getMessage.contains("input < 1")) + ) } }.provide(loggerLineCause), test("log error with cause with custom logger name") { (someError() @@ logging.loggerName("my-logger")).map { _ => val loggerOutput = TestAppender.logOutput - someErrorAssert(loggerOutput, "my-logger") && assertTrue(loggerOutput(0).cause.exists(_.contains("input < 1"))) + someErrorAssert(loggerOutput, "my-logger") && assertTrue( + loggerOutput(0).cause.exists(_.getMessage.contains("input < 1")) + ) } }.provide(loggerLineCause), test("log error without cause") { diff --git a/jpl/src/test/scala/zio/logging/backend/TestAppender.scala b/jpl/src/test/scala/zio/logging/backend/TestAppender.scala index 2cbf090f..f16e6c15 100644 --- a/jpl/src/test/scala/zio/logging/backend/TestAppender.scala +++ b/jpl/src/test/scala/zio/logging/backend/TestAppender.scala @@ -23,7 +23,7 @@ object TestAppender { logLevel: LogLevel, message: String, timestamp: Long, - cause: Option[String] + cause: Option[Throwable] ) private val logEntriesRef: AtomicReference[Chunk[LogEntry]] = new AtomicReference[Chunk[LogEntry]](Chunk.empty) diff --git a/jpl/src/test/scala/zio/logging/backend/TestJPLogger.scala b/jpl/src/test/scala/zio/logging/backend/TestJPLogger.scala index 77ab0131..8cd631d1 100644 --- a/jpl/src/test/scala/zio/logging/backend/TestJPLogger.scala +++ b/jpl/src/test/scala/zio/logging/backend/TestJPLogger.scala @@ -24,7 +24,7 @@ class TestJPLogger(val name: String) extends System.Logger { logLevelMapping(level), getString(bundle, msg), System.currentTimeMillis(), - Option(thrown).map(_.getMessage) + Option(thrown) ) TestAppender.appendLogEntry(entry) diff --git a/slf4j/src/main/scala/zio/logging/slf4j/SLF4J.scala b/slf4j/src/main/scala/zio/logging/slf4j/SLF4J.scala index 13cff5a8..81bc9dc9 100644 --- a/slf4j/src/main/scala/zio/logging/slf4j/SLF4J.scala +++ b/slf4j/src/main/scala/zio/logging/slf4j/SLF4J.scala @@ -105,7 +105,11 @@ object SLF4J { */ override def appendCause(cause: Cause[Any]): Unit = { if (!cause.isEmpty) { - throwable = FiberFailure(cause) + cause match { + case Cause.Die(t, _) => throwable = t + case Cause.Fail(t: Throwable, _) => throwable = t + case _ => throwable = FiberFailure(cause) + } } () } diff --git a/slf4j/src/test/scala/zio/logging/backend/SLF4JSpec.scala b/slf4j/src/test/scala/zio/logging/backend/SLF4JSpec.scala index c54da4b9..4d82cc89 100644 --- a/slf4j/src/test/scala/zio/logging/backend/SLF4JSpec.scala +++ b/slf4j/src/test/scala/zio/logging/backend/SLF4JSpec.scala @@ -227,14 +227,14 @@ object SLF4JSpec extends ZIOSpecDefault { test("log error with cause") { someError().map { _ => val loggerOutput = TestAppender.logOutput - someErrorAssert(loggerOutput) && assertTrue(loggerOutput(0).cause.exists(_.contains("input < 1"))) + someErrorAssert(loggerOutput) && assertTrue(loggerOutput(0).cause.exists(_.getMessage.contains("input < 1"))) } }.provide(loggerLineCause), test("log error with cause with custom logger name - legacy") { (someError() @@ SLF4J.loggerName("my-logger")).map { _ => val loggerOutput = TestAppender.logOutput someErrorAssert(loggerOutput, "my-logger") && assertTrue( - loggerOutput(0).cause.exists(_.contains("input < 1")) + loggerOutput(0).cause.exists(_.getMessage.contains("input < 1")) ) && assertTrue(!loggerOutput(0).mdc.contains(SLF4J.loggerNameAnnotationKey)) } @@ -243,7 +243,7 @@ object SLF4JSpec extends ZIOSpecDefault { (someError() @@ logging.loggerName("my-logger")).map { _ => val loggerOutput = TestAppender.logOutput someErrorAssert(loggerOutput, "my-logger") && assertTrue( - loggerOutput(0).cause.exists(_.contains("input < 1")) + loggerOutput(0).cause.exists(_.getMessage.contains("input < 1")) ) && assertTrue(!loggerOutput(0).mdc.contains(logging.loggerNameAnnotationKey)) } diff --git a/slf4j/src/test/scala/zio/logging/backend/TestAppender.scala b/slf4j/src/test/scala/zio/logging/backend/TestAppender.scala index 334c70f4..96440309 100644 --- a/slf4j/src/test/scala/zio/logging/backend/TestAppender.scala +++ b/slf4j/src/test/scala/zio/logging/backend/TestAppender.scala @@ -1,7 +1,7 @@ package zio.logging.backend import ch.qos.logback.classic.Level -import ch.qos.logback.classic.spi.ILoggingEvent +import ch.qos.logback.classic.spi.{ ILoggingEvent, IThrowableProxy } import ch.qos.logback.core.AppenderBase import zio.{ Chunk, LogLevel } @@ -32,7 +32,7 @@ object TestAppender { logLevel: LogLevel, message: String, timestamp: Long, - cause: Option[String], + cause: Option[IThrowableProxy], mdc: Map[String, String] ) @@ -44,7 +44,7 @@ object TestAppender { logLevelMapping(event.getLevel), event.getMessage, event.getTimeStamp, - Option(event.getThrowableProxy).map(_.getMessage), + Option(event.getThrowableProxy), event.getMDCPropertyMap.asScala.toMap ) } diff --git a/slf4j2/src/main/scala/zio/logging/slf4j/SLF4J.scala b/slf4j2/src/main/scala/zio/logging/slf4j/SLF4J.scala index fcc9ed83..90e962ac 100644 --- a/slf4j2/src/main/scala/zio/logging/slf4j/SLF4J.scala +++ b/slf4j2/src/main/scala/zio/logging/slf4j/SLF4J.scala @@ -86,7 +86,11 @@ object SLF4J { */ override def appendCause(cause: Cause[Any]): Unit = { if (!cause.isEmpty) { - throwable = FiberFailure(cause) + cause match { + case Cause.Die(t, _) => throwable = t + case Cause.Fail(t: Throwable, _) => throwable = t + case _ => throwable = FiberFailure(cause) + } } () } diff --git a/slf4j2/src/test/scala/zio/logging/backend/SLF4JSpec.scala b/slf4j2/src/test/scala/zio/logging/backend/SLF4JSpec.scala index f674db25..ff457946 100644 --- a/slf4j2/src/test/scala/zio/logging/backend/SLF4JSpec.scala +++ b/slf4j2/src/test/scala/zio/logging/backend/SLF4JSpec.scala @@ -178,14 +178,14 @@ object SLF4JSpec extends ZIOSpecDefault { test("log error with cause") { someError().map { _ => val loggerOutput = TestAppender.logOutput - someErrorAssert(loggerOutput) && assertTrue(loggerOutput(0).cause.exists(_.contains("input < 1"))) + someErrorAssert(loggerOutput) && assertTrue(loggerOutput(0).cause.exists(_.getMessage.contains("input < 1"))) } }.provide(loggerLineCause), test("log error with cause with custom logger name") { (someError() @@ zio.logging.loggerName("my-logger")).map { _ => val loggerOutput = TestAppender.logOutput someErrorAssert(loggerOutput, "my-logger") && assertTrue( - loggerOutput(0).cause.exists(_.contains("input < 1")) + loggerOutput(0).cause.exists(_.getMessage.contains("input < 1")) ) && assertTrue(!loggerOutput(0).keyValues.exists(_._1 == zio.logging.loggerNameAnnotationKey)) } diff --git a/slf4j2/src/test/scala/zio/logging/backend/TestAppender.scala b/slf4j2/src/test/scala/zio/logging/backend/TestAppender.scala index 2eaa93b4..7b081a2e 100644 --- a/slf4j2/src/test/scala/zio/logging/backend/TestAppender.scala +++ b/slf4j2/src/test/scala/zio/logging/backend/TestAppender.scala @@ -1,7 +1,7 @@ package zio.logging.backend import ch.qos.logback.classic.Level -import ch.qos.logback.classic.spi.ILoggingEvent +import ch.qos.logback.classic.spi.{ ILoggingEvent, IThrowableProxy } import ch.qos.logback.core.AppenderBase import zio.{ Chunk, LogLevel } @@ -32,7 +32,7 @@ object TestAppender { logLevel: LogLevel, message: String, timestamp: Long, - cause: Option[String], + cause: Option[IThrowableProxy], keyValues: List[(String, String)] ) @@ -41,7 +41,7 @@ object TestAppender { val keyValues = if (event.getKeyValuePairs != null) { event.getKeyValuePairs.asScala.map(kv => (kv.key, kv.value.toString)).toList } else List.empty[(String, String)] - val cause = Option(event.getThrowableProxy).map(_.getMessage) + val cause = Option(event.getThrowableProxy) val level = logLevelMapping(event.getLevel) LogEntry( From 5fe5070496ac36cbaad7c131312d87795199faeb Mon Sep 17 00:00:00 2001 From: Peter Kotula Date: Thu, 9 Feb 2023 17:35:05 +0100 Subject: [PATCH 13/14] cause to throwable --- jpl/src/main/scala/zio/logging/backend/JPL.scala | 8 ++++---- slf4j/src/main/scala/zio/logging/slf4j/SLF4J.scala | 8 ++++---- slf4j2/src/main/scala/zio/logging/slf4j/SLF4J.scala | 8 ++++---- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/jpl/src/main/scala/zio/logging/backend/JPL.scala b/jpl/src/main/scala/zio/logging/backend/JPL.scala index 6c42f35e..e2ced3e1 100644 --- a/jpl/src/main/scala/zio/logging/backend/JPL.scala +++ b/jpl/src/main/scala/zio/logging/backend/JPL.scala @@ -81,10 +81,10 @@ object JPL { */ override def appendCause(cause: Cause[Any]): Unit = { if (!cause.isEmpty) { - cause match { - case Cause.Die(t, _) => throwable = t - case Cause.Fail(t: Throwable, _) => throwable = t - case _ => throwable = FiberFailure(cause) + val maybeThrowable = (cause.failures.collect { case t: Throwable => t } ++ cause.defects).headOption + maybeThrowable match { + case Some(t) => throwable = t + case None => throwable = FiberFailure(cause) } } () diff --git a/slf4j/src/main/scala/zio/logging/slf4j/SLF4J.scala b/slf4j/src/main/scala/zio/logging/slf4j/SLF4J.scala index 81bc9dc9..620984c6 100644 --- a/slf4j/src/main/scala/zio/logging/slf4j/SLF4J.scala +++ b/slf4j/src/main/scala/zio/logging/slf4j/SLF4J.scala @@ -105,10 +105,10 @@ object SLF4J { */ override def appendCause(cause: Cause[Any]): Unit = { if (!cause.isEmpty) { - cause match { - case Cause.Die(t, _) => throwable = t - case Cause.Fail(t: Throwable, _) => throwable = t - case _ => throwable = FiberFailure(cause) + val maybeThrowable = (cause.failures.collect { case t: Throwable => t } ++ cause.defects).headOption + maybeThrowable match { + case Some(t) => throwable = t + case None => throwable = FiberFailure(cause) } } () diff --git a/slf4j2/src/main/scala/zio/logging/slf4j/SLF4J.scala b/slf4j2/src/main/scala/zio/logging/slf4j/SLF4J.scala index 90e962ac..993b28e9 100644 --- a/slf4j2/src/main/scala/zio/logging/slf4j/SLF4J.scala +++ b/slf4j2/src/main/scala/zio/logging/slf4j/SLF4J.scala @@ -86,10 +86,10 @@ object SLF4J { */ override def appendCause(cause: Cause[Any]): Unit = { if (!cause.isEmpty) { - cause match { - case Cause.Die(t, _) => throwable = t - case Cause.Fail(t: Throwable, _) => throwable = t - case _ => throwable = FiberFailure(cause) + val maybeThrowable = (cause.failures.collect { case t: Throwable => t } ++ cause.defects).headOption + maybeThrowable match { + case Some(t) => throwable = t + case None => throwable = FiberFailure(cause) } } () From ccf67868ee2605f39e944fe51e56f86b6e9ecf7f Mon Sep 17 00:00:00 2001 From: Peter Kotula Date: Fri, 10 Feb 2023 10:48:35 +0100 Subject: [PATCH 14/14] zio 2.0.8 --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index c27e72ba..f8e44c80 100644 --- a/build.sbt +++ b/build.sbt @@ -22,7 +22,7 @@ inThisBuild( ) ) -val ZioVersion = "2.0.7" +val ZioVersion = "2.0.8" val slf4jVersion = "1.7.36" val slf4j2Version = "2.0.6" val logbackVersion = "1.2.11"