From 264fba36d09acaf8128c2e1d95363f9517d480a2 Mon Sep 17 00:00:00 2001 From: Peter Kotula Date: Fri, 26 May 2023 17:57:16 +0200 Subject: [PATCH 01/56] LogFilter types --- .../main/scala/zio/logging/LogFilter.scala | 68 ++++++++++++++----- 1 file changed, 51 insertions(+), 17 deletions(-) diff --git a/core/shared/src/main/scala/zio/logging/LogFilter.scala b/core/shared/src/main/scala/zio/logging/LogFilter.scala index d8e9593a3..0561ee541 100644 --- a/core/shared/src/main/scala/zio/logging/LogFilter.scala +++ b/core/shared/src/main/scala/zio/logging/LogFilter.scala @@ -71,17 +71,11 @@ sealed trait LogFilter[-Message] { self => /** * Returns a new log filter with cached results */ - final def cached: LogFilter[Message] = { - val cache = new java.util.concurrent.ConcurrentHashMap[Value, Boolean]() - LogFilter[Message, self.Value]( - self.group, - v => - cache.computeIfAbsent( - v, - _ => self.predicate(v) - ) - ) - } + final def cached: LogFilter[Message] = + self match { + case LogFilter.Cached(filter) => LogFilter.Cached(filter) + case _ => LogFilter.Cached(self) + } final def contramap[M](f: M => Message): LogFilter[M] = LogFilter[M, self.Value]( group.contramap(f), @@ -136,6 +130,47 @@ sealed trait LogFilter[-Message] { self => object LogFilter { + private[logging] final case class GroupPredicate[M, V]( + logGroup: LogGroup[M, V], + valuePredicate: V => Boolean + ) extends LogFilter[M] { + override type Value = V + + override def group: LogGroup[M, Value] = logGroup + + override def predicate(value: Value): Boolean = valuePredicate(value) + } + + private[logging] final case class Cached[M](filter: LogFilter[M]) extends LogFilter[M] { + + private[logging] val cache = new java.util.concurrent.ConcurrentHashMap[filter.Value, Boolean]() + + override type Value = filter.Value + + override def group: LogGroup[M, Value] = filter.group + + override def predicate(value: Value): Boolean = + cache.computeIfAbsent( + value, + _ => filter.predicate(value) + ) + + def clear(): Unit = + cache.clear() + } + + private[logging] final case class Configured[M, C](config: C, make: C => LogFilter[M]) extends LogFilter[M] { + val filter: LogFilter[M] = make(config) + + override type Value = filter.Value + + override def group: LogGroup[M, Value] = filter.group + + override def predicate(value: Value): Boolean = filter.predicate(value) + + def witConfig(newConfig: C): Configured[M, C] = Configured(newConfig, make) + } + /** * Defines a filter from a list of log-levels specified per tree node * @@ -174,11 +209,7 @@ object LogFilter { def apply[M, V]( group0: LogGroup[M, V], predicate0: V => Boolean - ): LogFilter[M] = new LogFilter[M] { - override type Value = V - override def group: LogGroup[M, V] = group0 - override def predicate(value: Value): Boolean = predicate0(value) - } + ): LogFilter[M] = GroupPredicate(group0, predicate0) def apply[M]( group0: LogGroup[M, Boolean] @@ -333,7 +364,10 @@ object LogFilter { logLevelByGroup[M](rootLevel, LogGroup.loggerName, mappings: _*) def logLevelByGroup[M](group: LogGroup[M, String], config: LogLevelByNameConfig): LogFilter[M] = - logLevelByGroup[M](config.rootLevel, group, config.mappings.toList: _*) + Configured[M, LogLevelByNameConfig]( + config, + config => logLevelByGroup[M](config.rootLevel, group, config.mappings.toList: _*) + ) def logLevelByName[M](config: LogLevelByNameConfig): LogFilter[M] = logLevelByGroup[M](LogGroup.loggerName, config) From 51c0ec186b1c921094b88241a9e13f1fa76e22c0 Mon Sep 17 00:00:00 2001 From: Peter Kotula Date: Sun, 28 May 2023 17:18:54 +0200 Subject: [PATCH 02/56] FilteredLogger --- .../scala/zio/logging/FilteredLogger.scala | 38 +++++++++++++++++++ .../main/scala/zio/logging/LogFilter.scala | 17 +-------- .../src/main/scala/zio/logging/package.scala | 8 ++++ project/MimaSettings.scala | 2 +- 4 files changed, 48 insertions(+), 17 deletions(-) create mode 100644 core/shared/src/main/scala/zio/logging/FilteredLogger.scala diff --git a/core/shared/src/main/scala/zio/logging/FilteredLogger.scala b/core/shared/src/main/scala/zio/logging/FilteredLogger.scala new file mode 100644 index 000000000..112dea5b4 --- /dev/null +++ b/core/shared/src/main/scala/zio/logging/FilteredLogger.scala @@ -0,0 +1,38 @@ +/* + * 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 + +import zio.{ Cause, FiberId, FiberRefs, LogLevel, LogSpan, Trace, ZLogger } + +final case class FilteredLogger[Message, Output](logger: zio.ZLogger[Message, Output], filter: LogFilter[Message]) + extends ZLogger[Message, Option[Output]] { + + override def apply( + trace: Trace, + fiberId: FiberId, + logLevel: LogLevel, + message: () => Message, + cause: Cause[Any], + context: FiberRefs, + spans: List[LogSpan], + annotations: Map[String, String] + ): Option[Output] = + if (filter(trace, fiberId, logLevel, message, cause, context, spans, annotations)) { + Some(logger(trace, fiberId, logLevel, message, cause, context, spans, annotations)) + } else None + + def withFilter(newFilter: LogFilter[Message]): FilteredLogger[Message, Output] = FilteredLogger(logger, newFilter) +} diff --git a/core/shared/src/main/scala/zio/logging/LogFilter.scala b/core/shared/src/main/scala/zio/logging/LogFilter.scala index 0561ee541..8f932d864 100644 --- a/core/shared/src/main/scala/zio/logging/LogFilter.scala +++ b/core/shared/src/main/scala/zio/logging/LogFilter.scala @@ -85,22 +85,7 @@ sealed trait LogFilter[-Message] { self => /** * Returns a version of logger that only logs messages when this filter is satisfied */ - def filter[M <: Message, O](logger: zio.ZLogger[M, O]): zio.ZLogger[M, Option[O]] = - new ZLogger[M, Option[O]] { - override def apply( - trace: Trace, - fiberId: FiberId, - logLevel: LogLevel, - message: () => M, - cause: Cause[Any], - context: FiberRefs, - spans: List[LogSpan], - annotations: Map[String, String] - ): Option[O] = - if (self(trace, fiberId, logLevel, message, cause, context, spans, annotations)) { - Some(logger(trace, fiberId, logLevel, message, cause, context, spans, annotations)) - } else None - } + final def filter[M <: Message, O](logger: zio.ZLogger[M, O]): zio.ZLogger[M, Option[O]] = FilteredLogger(logger, self) /** * The alphanumeric version of the `!` operator. diff --git a/core/shared/src/main/scala/zio/logging/package.scala b/core/shared/src/main/scala/zio/logging/package.scala index 08c578685..d95e879cb 100644 --- a/core/shared/src/main/scala/zio/logging/package.scala +++ b/core/shared/src/main/scala/zio/logging/package.scala @@ -514,4 +514,12 @@ package object logging { def logAnnotate[V: Tag](key: LogAnnotation[V], value: V): ZIO[R, E, A] = self @@ key(value) } + + implicit final class ZLoggerOps[M, O](private val self: ZLogger[M, O]) { + + /** + * Returns a version of logger that only logs messages when this filter is satisfied + */ + def filter(filter: LogFilter[M]): ZLogger[M, Option[O]] = FilteredLogger(self, filter) + } } diff --git a/project/MimaSettings.scala b/project/MimaSettings.scala index 677286ca6..d09b659e3 100644 --- a/project/MimaSettings.scala +++ b/project/MimaSettings.scala @@ -5,7 +5,7 @@ import sbt.Keys.{ name, organization } import sbt._ object MimaSettings { - lazy val bincompatVersionToCompare = "2.1.12" + lazy val bincompatVersionToCompare = "2.1.13" def mimaSettings(failOnProblem: Boolean) = Seq( From 9fbce31d1258d55f082357b5a08112a5724a2854 Mon Sep 17 00:00:00 2001 From: Peter Kotula Date: Sun, 28 May 2023 21:03:28 +0200 Subject: [PATCH 03/56] ReconfigurableLoggerSpec - wip --- .../logging/ReconfigurableLoggerSpec.scala | 113 ++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 core/jvm/src/test/scala/zio/logging/ReconfigurableLoggerSpec.scala diff --git a/core/jvm/src/test/scala/zio/logging/ReconfigurableLoggerSpec.scala b/core/jvm/src/test/scala/zio/logging/ReconfigurableLoggerSpec.scala new file mode 100644 index 000000000..6d7d96f73 --- /dev/null +++ b/core/jvm/src/test/scala/zio/logging/ReconfigurableLoggerSpec.scala @@ -0,0 +1,113 @@ +package zio.logging + +import zio.test._ +import zio.{ + Cause, + Config, + ConfigProvider, + FiberId, + FiberRefs, + LogLevel, + LogSpan, + Queue, + Runtime, + Trace, + UIO, + ZIO, + ZLayer, + ZLogger +} + +import java.util.concurrent.atomic.AtomicReference + +object ReconfigurableLoggerSpec extends ZIOSpecDefault { + + trait ReconfigurableLogger[-Message, +Output, Config] extends ZLogger[Message, Output] { + def reconfigure(config: Config): Unit + + def reconfigureIfChanged(config: Config): Boolean + } + + object ReconfigurableLogger { + def apply[M, O, C]( + config: C, + makeLogger: C => ZLogger[M, O] + ): ReconfigurableLogger[M, O, C] = + new ReconfigurableLogger[M, O, C] { + private val configureLogger: AtomicReference[(C, ZLogger[M, O])] = { + val logger = makeLogger(config) + new AtomicReference[(C, ZLogger[M, O])]((config, logger)) + } + + override def reconfigureIfChanged(config: C): Boolean = + if (configureLogger.get()._1 != config) { + reconfigure(config) + true + } else false + + override def reconfigure(config: C): Unit = { + val logger = makeLogger(config) + configureLogger.set((config, logger)) + } + + override def apply( + trace: Trace, + fiberId: FiberId, + logLevel: LogLevel, + message: () => M, + cause: Cause[Any], + context: FiberRefs, + spans: List[LogSpan], + annotations: Map[String, String] + ): O = + configureLogger.get()._2.apply(trace, fiberId, logLevel, message, cause, context, spans, annotations) + } + + } + + def configuredLogger(queue: zio.Queue[String], configPath: String = "logger"): ZLayer[Any, Config.Error, Unit] = + ZLayer.scoped { + for { + config <- ZIO.config(ConsoleLoggerConfig.config.nested(configPath)) + + logger = ReconfigurableLogger[String, Any, ConsoleLoggerConfig]( + config, + config => + config.format.toLogger.map { line => + zio.Unsafe.unsafe { implicit u => + Runtime.default.unsafe.run(queue.offer(line)) + } + }.filter(config.filter) + ) + + _ <- ZIO.withLoggerScoped(logger) + } yield () + } + + val spec: Spec[Environment, Any] = suite("ReconfigurableLogger")( + test("log") { + + val logFormat = "%message" + + val configProvider: ConfigProvider = ConfigProvider.fromMap( + Map( + "logger/format" -> logFormat, + "logger/filter/rootLevel" -> LogLevel.Info.label, + "logger/filter/mappings/zio.logging.example.LivePingService" -> LogLevel.Debug.label + ), + "/" + ) + + Queue.unbounded[String].flatMap { queue => + (for { + _ <- ZIO.logInfo("info") + elements1 <- queue.takeAll + _ <- ZIO.logInfo("debug") + elements2 <- queue.takeAll + } yield assertTrue(elements1.nonEmpty && elements2.nonEmpty)) + .provide(Runtime.setConfigProvider(configProvider) >>> configuredLogger(queue)) + } + + } + ) +} From 46a0e6cd3aef216a165d89673d4a7772f6f4adf6 Mon Sep 17 00:00:00 2001 From: Peter Kotula Date: Tue, 30 May 2023 19:41:32 +0200 Subject: [PATCH 04/56] ReconfigurableLoggerSpec - wip --- .../logging/ReconfigurableLoggerSpec.scala | 71 +++++++++++++------ 1 file changed, 48 insertions(+), 23 deletions(-) diff --git a/core/jvm/src/test/scala/zio/logging/ReconfigurableLoggerSpec.scala b/core/jvm/src/test/scala/zio/logging/ReconfigurableLoggerSpec.scala index 6d7d96f73..45f661289 100644 --- a/core/jvm/src/test/scala/zio/logging/ReconfigurableLoggerSpec.scala +++ b/core/jvm/src/test/scala/zio/logging/ReconfigurableLoggerSpec.scala @@ -3,6 +3,7 @@ package zio.logging import zio.test._ import zio.{ Cause, + Chunk, Config, ConfigProvider, FiberId, @@ -11,24 +12,27 @@ import zio.{ LogSpan, Queue, Runtime, + Schedule, Trace, UIO, ZIO, ZLayer, ZLogger } - +import zio._ import java.util.concurrent.atomic.AtomicReference object ReconfigurableLoggerSpec extends ZIOSpecDefault { - trait ReconfigurableLogger[-Message, +Output, Config] extends ZLogger[Message, Output] { + sealed trait ReconfigurableLogger[-Message, +Output, Config] extends ZLogger[Message, Output] { + def reconfigure(config: Config): Unit def reconfigureIfChanged(config: Config): Boolean } object ReconfigurableLogger { + def apply[M, O, C]( config: C, makeLogger: C => ZLogger[M, O] @@ -79,35 +83,56 @@ object ReconfigurableLoggerSpec extends ZIOSpecDefault { } }.filter(config.filter) ) - - _ <- ZIO.withLoggerScoped(logger) + _ <- ZIO + .config(ConsoleLoggerConfig.config.nested(configPath)) + .map { newConfig => + logger.reconfigureIfChanged(newConfig) + } + .scheduleFork(Schedule.fixed(200.millis)) + _ <- ZIO.withLoggerScoped(logger) } yield () } val spec: Spec[Environment, Any] = suite("ReconfigurableLogger")( test("log") { - val logFormat = "%message" - - val configProvider: ConfigProvider = ConfigProvider.fromMap( - Map( - "logger/format" -> logFormat, - "logger/filter/rootLevel" -> LogLevel.Info.label, - "logger/filter/mappings/zio.logging.example.LivePingService" -> LogLevel.Debug.label - ), - "/" + val initialProperties = Map( + "logger/format" -> "%message", + "logger/filter/rootLevel" -> LogLevel.Info.label, + "logger/filter/mappings/zio.logging.example.LivePingService" -> LogLevel.Debug.label ) - Queue.unbounded[String].flatMap { queue => - (for { - _ <- ZIO.logInfo("info") - elements1 <- queue.takeAll - _ <- ZIO.logInfo("debug") - elements2 <- queue.takeAll - } yield assertTrue(elements1.nonEmpty && elements2.nonEmpty)) - .provide(Runtime.setConfigProvider(configProvider) >>> configuredLogger(queue)) - } + ZIO + .foreach(initialProperties) { case (k, v) => + TestSystem.putProperty(k, v).as(k -> v) + } + .flatMap { _ => + Queue.unbounded[String].flatMap { queue => + val runTest = + for { + _ <- ZIO.logInfo("info") + _ <- ZIO.logDebug("debug") + elements1 <- queue.takeAll + _ <- TestSystem.putProperty("logger/format", "%level %message") + _ <- ZIO.sleep(500.millis) + _ <- ZIO.logWarning("warn") + elements2 <- queue.takeAll + _ <- TestSystem.putProperty("logger/format", "L: %level M: %message") + _ <- TestSystem.putProperty("logger/filter/rootLevel", LogLevel.Debug.label) + _ <- ZIO.sleep(500.millis) + _ <- ZIO.logDebug("debug") + elements3 <- queue.takeAll + } yield assertTrue( + elements1 == Chunk("info") && elements2 == Chunk("WARN warn") && elements3 == Chunk("L: DEBUG M: debug") + ) + + runTest.provide( + Runtime.removeDefaultLoggers >>> Runtime + .setConfigProvider(ConfigProvider.fromProps("/")) >>> configuredLogger(queue) + ) + } + } - } + } @@ TestAspect.withLiveClock ) } From b0072ab2a168d6347e6a59e349c9717f0720b716 Mon Sep 17 00:00:00 2001 From: Peter Kotula Date: Tue, 30 May 2023 19:56:54 +0200 Subject: [PATCH 05/56] ReconfigurableLogger --- .../ReconfigurableLoggerSpec.scala | 72 ++----------------- .../zio/logging/internal/JsonValidator.scala | 15 ++++ .../internal/ReconfigurableLogger.scala | 66 +++++++++++++++++ .../zio/logging/internal/WriterProvider.scala | 15 ++++ 4 files changed, 101 insertions(+), 67 deletions(-) rename core/jvm/src/test/scala/zio/logging/{ => internal}/ReconfigurableLoggerSpec.scala (63%) create mode 100644 core/shared/src/main/scala/zio/logging/internal/ReconfigurableLogger.scala diff --git a/core/jvm/src/test/scala/zio/logging/ReconfigurableLoggerSpec.scala b/core/jvm/src/test/scala/zio/logging/internal/ReconfigurableLoggerSpec.scala similarity index 63% rename from core/jvm/src/test/scala/zio/logging/ReconfigurableLoggerSpec.scala rename to core/jvm/src/test/scala/zio/logging/internal/ReconfigurableLoggerSpec.scala index 45f661289..7692ca040 100644 --- a/core/jvm/src/test/scala/zio/logging/ReconfigurableLoggerSpec.scala +++ b/core/jvm/src/test/scala/zio/logging/internal/ReconfigurableLoggerSpec.scala @@ -1,74 +1,12 @@ -package zio.logging +package zio.logging.internal +import zio.logging.ConsoleLoggerConfig import zio.test._ -import zio.{ - Cause, - Chunk, - Config, - ConfigProvider, - FiberId, - FiberRefs, - LogLevel, - LogSpan, - Queue, - Runtime, - Schedule, - Trace, - UIO, - ZIO, - ZLayer, - ZLogger -} -import zio._ -import java.util.concurrent.atomic.AtomicReference +import zio.logging._ +import zio.{ Chunk, Config, ConfigProvider, LogLevel, Queue, Runtime, Schedule, ZIO, ZLayer, _ } object ReconfigurableLoggerSpec extends ZIOSpecDefault { - sealed trait ReconfigurableLogger[-Message, +Output, Config] extends ZLogger[Message, Output] { - - def reconfigure(config: Config): Unit - - def reconfigureIfChanged(config: Config): Boolean - } - - object ReconfigurableLogger { - - def apply[M, O, C]( - config: C, - makeLogger: C => ZLogger[M, O] - ): ReconfigurableLogger[M, O, C] = - new ReconfigurableLogger[M, O, C] { - private val configureLogger: AtomicReference[(C, ZLogger[M, O])] = { - val logger = makeLogger(config) - new AtomicReference[(C, ZLogger[M, O])]((config, logger)) - } - - override def reconfigureIfChanged(config: C): Boolean = - if (configureLogger.get()._1 != config) { - reconfigure(config) - true - } else false - - override def reconfigure(config: C): Unit = { - val logger = makeLogger(config) - configureLogger.set((config, logger)) - } - - override def apply( - trace: Trace, - fiberId: FiberId, - logLevel: LogLevel, - message: () => M, - cause: Cause[Any], - context: FiberRefs, - spans: List[LogSpan], - annotations: Map[String, String] - ): O = - configureLogger.get()._2.apply(trace, fiberId, logLevel, message, cause, context, spans, annotations) - } - - } - def configuredLogger(queue: zio.Queue[String], configPath: String = "logger"): ZLayer[Any, Config.Error, Unit] = ZLayer.scoped { for { @@ -94,7 +32,7 @@ object ReconfigurableLoggerSpec extends ZIOSpecDefault { } val spec: Spec[Environment, Any] = suite("ReconfigurableLogger")( - test("log") { + test("log with changed config") { val initialProperties = Map( "logger/format" -> "%message", diff --git a/core/shared/src/main/scala/zio/logging/internal/JsonValidator.scala b/core/shared/src/main/scala/zio/logging/internal/JsonValidator.scala index 8f7ac6f8f..a5b6b679a 100644 --- a/core/shared/src/main/scala/zio/logging/internal/JsonValidator.scala +++ b/core/shared/src/main/scala/zio/logging/internal/JsonValidator.scala @@ -1,3 +1,18 @@ +/* + * 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.internal import scala.annotation.tailrec diff --git a/core/shared/src/main/scala/zio/logging/internal/ReconfigurableLogger.scala b/core/shared/src/main/scala/zio/logging/internal/ReconfigurableLogger.scala new file mode 100644 index 000000000..248e604d4 --- /dev/null +++ b/core/shared/src/main/scala/zio/logging/internal/ReconfigurableLogger.scala @@ -0,0 +1,66 @@ +/* + * 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.internal + +import zio.{ Cause, FiberId, FiberRefs, LogLevel, LogSpan, Trace, ZLogger } + +import java.util.concurrent.atomic.AtomicReference + +private[logging] sealed trait ReconfigurableLogger[-Message, +Output, Config] extends ZLogger[Message, Output] { + + def reconfigure(config: Config): Unit + + def reconfigureIfChanged(config: Config): Boolean +} + +private[logging] object ReconfigurableLogger { + + def apply[M, O, C]( + config: C, + makeLogger: C => ZLogger[M, O] + ): ReconfigurableLogger[M, O, C] = + new ReconfigurableLogger[M, O, C] { + + private val configureLogger: AtomicReference[(C, ZLogger[M, O])] = { + val logger = makeLogger(config) + new AtomicReference[(C, ZLogger[M, O])]((config, logger)) + } + + override def reconfigureIfChanged(config: C): Boolean = + if (configureLogger.get()._1 != config) { + reconfigure(config) + true + } else false + + override def reconfigure(config: C): Unit = { + val logger = makeLogger(config) + configureLogger.set((config, logger)) + } + + override def apply( + trace: Trace, + fiberId: FiberId, + logLevel: LogLevel, + message: () => M, + cause: Cause[Any], + context: FiberRefs, + spans: List[LogSpan], + annotations: Map[String, String] + ): O = + configureLogger.get()._2.apply(trace, fiberId, logLevel, message, cause, context, spans, annotations) + } + +} diff --git a/core/shared/src/main/scala/zio/logging/internal/WriterProvider.scala b/core/shared/src/main/scala/zio/logging/internal/WriterProvider.scala index 0b60ce2f0..d07e22cac 100644 --- a/core/shared/src/main/scala/zio/logging/internal/WriterProvider.scala +++ b/core/shared/src/main/scala/zio/logging/internal/WriterProvider.scala @@ -1,3 +1,18 @@ +/* + * 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.internal import java.io.{ BufferedWriter, FileOutputStream, OutputStreamWriter, Writer } From 8a6ff7af00fbb08cdffb3eaed9afd273796388d6 Mon Sep 17 00:00:00 2001 From: Peter Kotula Date: Wed, 31 May 2023 23:17:44 +0200 Subject: [PATCH 06/56] LogFilter, LogFormat, LoggerNameExtractor, LogGroup - sealed and custom types, configuration - equal --- build.sbt | 3 +- .../internal/ReconfigurableLoggerSpec.scala | 29 +- .../zio/logging/ConsoleLoggerConfig.scala | 4 + .../scala/zio/logging/FileLoggerConfig.scala | 11 + .../main/scala/zio/logging/LogFilter.scala | 40 +- .../main/scala/zio/logging/LogFormat.scala | 370 ++++++++++++------ .../src/main/scala/zio/logging/LogGroup.scala | 215 +++++++--- .../zio/logging/LoggerNameExtractor.scala | 29 +- .../internal/ReconfigurableLogger.scala | 10 +- project/Versions.scala | 1 + 10 files changed, 505 insertions(+), 207 deletions(-) diff --git a/build.sbt b/build.sbt index 2b9c168de..d27644558 100644 --- a/build.sbt +++ b/build.sbt @@ -94,7 +94,8 @@ lazy val core = crossProject(JSPlatform, JVMPlatform) .settings(enableZIO(enableStreaming = true)) .settings( libraryDependencies ++= Seq( - "dev.zio" %%% "zio-parser" % zioParser + "dev.zio" %%% "zio-parser" % zioParser, + "dev.zio" %%% "zio-prelude" % zioPrelude ) ) .jvmSettings( diff --git a/core/jvm/src/test/scala/zio/logging/internal/ReconfigurableLoggerSpec.scala b/core/jvm/src/test/scala/zio/logging/internal/ReconfigurableLoggerSpec.scala index 7692ca040..cbe9d84c0 100644 --- a/core/jvm/src/test/scala/zio/logging/internal/ReconfigurableLoggerSpec.scala +++ b/core/jvm/src/test/scala/zio/logging/internal/ReconfigurableLoggerSpec.scala @@ -71,6 +71,31 @@ object ReconfigurableLoggerSpec extends ZIOSpecDefault { } } - } @@ TestAspect.withLiveClock - ) + }, + test("equals config with same sources") { + + val configPath = "logger" + + val initialProperties = Map( + "logger/format" -> "%message", + "logger/filter/rootLevel" -> LogLevel.Info.label, + "logger/filter/mappings/zio.logging.example.LivePingService" -> LogLevel.Debug.label + ) + + ZIO + .foreach(initialProperties) { case (k, v) => + TestSystem.putProperty(k, v).as(k -> v) + } + .flatMap { _ => + import zio.prelude._ + for { + c1 <- ZIO.config(ConsoleLoggerConfig.config.nested(configPath)) + c2 <- ZIO.config(ConsoleLoggerConfig.config.nested(configPath)) + } yield assertTrue( + c1 === c2 + ) + } + .provide(Runtime.setConfigProvider(ConfigProvider.fromProps("/"))) + } + ) @@ TestAspect.withLiveClock } diff --git a/core/shared/src/main/scala/zio/logging/ConsoleLoggerConfig.scala b/core/shared/src/main/scala/zio/logging/ConsoleLoggerConfig.scala index 1cf6545a1..4f77c2609 100644 --- a/core/shared/src/main/scala/zio/logging/ConsoleLoggerConfig.scala +++ b/core/shared/src/main/scala/zio/logging/ConsoleLoggerConfig.scala @@ -16,6 +16,7 @@ package zio.logging import zio.{ Config, LogLevel } +import zio.prelude._ final case class ConsoleLoggerConfig(format: LogFormat, filter: LogFilter[String]) @@ -34,4 +35,7 @@ object ConsoleLoggerConfig { } } + implicit val equal: Equal[ConsoleLoggerConfig] = Equal.make { (l, r) => + l.format == r.format && l.filter === r.filter + } } diff --git a/core/shared/src/main/scala/zio/logging/FileLoggerConfig.scala b/core/shared/src/main/scala/zio/logging/FileLoggerConfig.scala index 580c67851..e173d6ec9 100644 --- a/core/shared/src/main/scala/zio/logging/FileLoggerConfig.scala +++ b/core/shared/src/main/scala/zio/logging/FileLoggerConfig.scala @@ -16,6 +16,7 @@ package zio.logging import zio._ +import zio.prelude._ import java.net.URI import java.nio.charset.{ Charset, StandardCharsets } @@ -86,4 +87,14 @@ object FileLoggerConfig { } } + implicit val equal: Equal[FileLoggerConfig] = Equal.make { (l, r) => + l.destination == r.destination && + l.charset == r.charset && + l.autoFlushBatchSize == r.autoFlushBatchSize && + l.bufferedIOSize == r.bufferedIOSize && + l.rollingPolicy == r.rollingPolicy && + l.format == r.format && + l.filter === r.filter + } + } diff --git a/core/shared/src/main/scala/zio/logging/LogFilter.scala b/core/shared/src/main/scala/zio/logging/LogFilter.scala index 8f932d864..e9a8b092d 100644 --- a/core/shared/src/main/scala/zio/logging/LogFilter.scala +++ b/core/shared/src/main/scala/zio/logging/LogFilter.scala @@ -15,7 +15,8 @@ */ package zio.logging -import zio.{ Cause, Config, FiberId, FiberRefs, LogLevel, LogSpan, Trace, ZLogger } +import zio.{ Cause, Config, FiberId, FiberRefs, LogLevel, LogSpan, Trace } +import zio.prelude.Equal import scala.annotation.tailrec @@ -73,8 +74,8 @@ sealed trait LogFilter[-Message] { self => */ final def cached: LogFilter[Message] = self match { - case LogFilter.Cached(filter) => LogFilter.Cached(filter) - case _ => LogFilter.Cached(self) + case LogFilter.CachedFilter(filter) => LogFilter.CachedFilter(filter) + case _ => LogFilter.CachedFilter(self) } final def contramap[M](f: M => Message): LogFilter[M] = LogFilter[M, self.Value]( @@ -115,7 +116,16 @@ sealed trait LogFilter[-Message] { self => object LogFilter { - private[logging] final case class GroupPredicate[M, V]( + implicit val equal: Equal[LogFilter[_]] = Equal.make { (l, r) => + (l, r) match { + case (l: GroupPredicateFilter[_, _], r: GroupPredicateFilter[_, _]) => GroupPredicateFilter.equal.equal(l, r) + case (l: CachedFilter[_], r: CachedFilter[_]) => CachedFilter.equal.equal(l, r) + case (l: ConfiguredFilter[_, _], r: ConfiguredFilter[_, _]) => ConfiguredFilter.equal.equal(l, r) + case (l, r) => l == r + } + } + + private[logging] final case class GroupPredicateFilter[M, V]( logGroup: LogGroup[M, V], valuePredicate: V => Boolean ) extends LogFilter[M] { @@ -126,7 +136,11 @@ object LogFilter { override def predicate(value: Value): Boolean = valuePredicate(value) } - private[logging] final case class Cached[M](filter: LogFilter[M]) extends LogFilter[M] { + private[logging] object GroupPredicateFilter { + implicit val equal: Equal[GroupPredicateFilter[_, _]] = Equal.default + } + + private[logging] final case class CachedFilter[M](filter: LogFilter[M]) extends LogFilter[M] { private[logging] val cache = new java.util.concurrent.ConcurrentHashMap[filter.Value, Boolean]() @@ -144,7 +158,11 @@ object LogFilter { cache.clear() } - private[logging] final case class Configured[M, C](config: C, make: C => LogFilter[M]) extends LogFilter[M] { + private[logging] object CachedFilter { + implicit val equal: Equal[CachedFilter[_]] = Equal.default.contramap(_.filter) + } + + private[logging] final case class ConfiguredFilter[M, C](config: C, make: C => LogFilter[M]) extends LogFilter[M] { val filter: LogFilter[M] = make(config) override type Value = filter.Value @@ -153,7 +171,11 @@ object LogFilter { override def predicate(value: Value): Boolean = filter.predicate(value) - def witConfig(newConfig: C): Configured[M, C] = Configured(newConfig, make) + def witConfig(newConfig: C): ConfiguredFilter[M, C] = ConfiguredFilter(newConfig, make) + } + + private[logging] object ConfiguredFilter { + implicit val equal: Equal[ConfiguredFilter[_, _]] = Equal.default.contramap(_.config) } /** @@ -194,7 +216,7 @@ object LogFilter { def apply[M, V]( group0: LogGroup[M, V], predicate0: V => Boolean - ): LogFilter[M] = GroupPredicate(group0, predicate0) + ): LogFilter[M] = GroupPredicateFilter(group0, predicate0) def apply[M]( group0: LogGroup[M, Boolean] @@ -349,7 +371,7 @@ object LogFilter { logLevelByGroup[M](rootLevel, LogGroup.loggerName, mappings: _*) def logLevelByGroup[M](group: LogGroup[M, String], config: LogLevelByNameConfig): LogFilter[M] = - Configured[M, LogLevelByNameConfig]( + ConfiguredFilter[M, LogLevelByNameConfig]( config, config => logLevelByGroup[M](config.rootLevel, group, config.mappings.toList: _*) ) diff --git a/core/shared/src/main/scala/zio/logging/LogFormat.scala b/core/shared/src/main/scala/zio/logging/LogFormat.scala index e41fad254..667e754af 100644 --- a/core/shared/src/main/scala/zio/logging/LogFormat.scala +++ b/core/shared/src/main/scala/zio/logging/LogFormat.scala @@ -31,7 +31,7 @@ import scala.util.Try * timestamp.fixed(32) |-| level |-| label("message", quoted(line)) * }}} */ -trait LogFormat { self => +sealed trait LogFormat { self => import zio.logging.LogFormat.text /** @@ -47,10 +47,7 @@ trait LogFormat { self => * separator between them. */ final def +(other: LogFormat): LogFormat = - LogFormat.make { (builder, trace, fiberId, level, line, cause, context, spans, annotations) => - self.unsafeFormat(builder)(trace, fiberId, level, line, cause, context, spans, annotations) - other.unsafeFormat(builder)(trace, fiberId, level, line, cause, context, spans, annotations) - } + LogFormat.ConcatFormat(self, other) /** * Returns a new log format which concats both formats together with a space @@ -76,44 +73,20 @@ trait LogFormat { self => * Returns a new log format that produces the same as this one, if filter is satisfied */ final def filter[M >: String](filter: LogFilter[M]): LogFormat = - LogFormat.make { (builder, trace, fiberId, level, line, cause, context, spans, annotations) => - if (filter(trace, fiberId, level, line, cause, context, spans, annotations)) { - self.unsafeFormat(builder)(trace, fiberId, level, line, cause, context, spans, annotations) - } - } + LogFormat.FilteredFormat(self, filter) /** * Returns a new log format that produces the same as this one, but with a * space-padded, fixed-width output. Be careful using this operator, as it * destroys all structure, resulting in purely textual log output. */ - final def fixed(size: Int): LogFormat = - LogFormat.make { (builder, trace, fiberId, level, line, cause, context, spans, annotations) => - val tempBuilder = new StringBuilder - val append = LogAppender.unstructured { (line: String) => - tempBuilder.append(line) - () - } - self.unsafeFormat(append)(trace, fiberId, level, line, cause, context, spans, annotations) - - val messageSize = tempBuilder.size - if (messageSize < size) { - builder.appendText(tempBuilder.take(size).appendAll(Array.fill(size - messageSize)(' ')).toString()) - } else { - builder.appendText(tempBuilder.take(size).toString()) - } - } + final def fixed(size: Int): LogFormat = LogFormat.FixedFormat(self, size) /** * Returns a new log format that produces the same as this one, except that * log levels are colored according to the specified mapping. */ - final def highlight(fn: LogLevel => LogColor): LogFormat = - LogFormat.make { (builder, trace, fiberId, level, line, cause, context, spans, annotations) => - builder.appendText(fn(level).ansi) - try self.unsafeFormat(builder)(trace, fiberId, level, line, cause, context, spans, annotations) - finally builder.appendText(LogColor.RESET.ansi) - } + final def highlight(fn: LogLevel => LogColor): LogFormat = LogFormat.HighlightFormat(self, fn) /** * Returns a new log format that produces the same as this one, except that @@ -203,6 +176,225 @@ trait LogFormat { self => object LogFormat { + private[logging] def makeLogger( + fn: ( + LogAppender, + Trace, + FiberId, + LogLevel, + () => String, + Cause[Any], + FiberRefs, + List[LogSpan], + Map[String, String] + ) => Any + )( + builder: LogAppender + ): ZLogger[String, Unit] = 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 = { + fn(builder, trace, fiberId, logLevel, message, cause, context, spans, annotations) + () + } + } + + private[logging] final case class FnFormat( + fn: ( + LogAppender, + Trace, + FiberId, + LogLevel, + () => String, + Cause[Any], + FiberRefs, + List[LogSpan], + Map[String, String] + ) => Any + ) extends LogFormat { + override private[logging] def unsafeFormat(builder: LogAppender): ZLogger[String, Unit] = + makeLogger(fn)(builder) + } + + private[logging] final case class ConcatFormat(first: LogFormat, second: LogFormat) extends LogFormat { + override private[logging] def unsafeFormat(builder: LogAppender): ZLogger[String, Unit] = + makeLogger { (builder, trace, fiberId, level, line, cause, context, spans, annotations) => + first.unsafeFormat(builder)(trace, fiberId, level, line, cause, context, spans, annotations) + second.unsafeFormat(builder)(trace, fiberId, level, line, cause, context, spans, annotations) + () + }(builder) + } + + private[logging] final case class FilteredFormat(format: LogFormat, filter: LogFilter[String]) extends LogFormat { + override private[logging] def unsafeFormat(builder: LogAppender): ZLogger[String, Unit] = + makeLogger { (builder, trace, fiberId, level, line, cause, context, spans, annotations) => + if (filter(trace, fiberId, level, line, cause, context, spans, annotations)) { + format.unsafeFormat(builder)(trace, fiberId, level, line, cause, context, spans, annotations) + } + () + }(builder) + } + + private[logging] final case class HighlightFormat(format: LogFormat, fn: LogLevel => LogColor) extends LogFormat { + override private[logging] def unsafeFormat(builder: LogAppender): ZLogger[String, Unit] = + makeLogger { (builder, trace, fiberId, level, line, cause, context, spans, annotations) => + builder.appendText(fn(level).ansi) + try format.unsafeFormat(builder)(trace, fiberId, level, line, cause, context, spans, annotations) + finally builder.appendText(LogColor.RESET.ansi) + () + }(builder) + } + + private[logging] final case class TextFormat(value: String) extends LogFormat { + override private[logging] def unsafeFormat(builder: LogAppender): ZLogger[String, Unit] = + makeLogger { (builder, _, _, _, _, _, _, _, _) => + builder.appendText(value) + }(builder) + } + + private[logging] final case class TimestampFormat(formatter: DateTimeFormatter) extends LogFormat { + override private[logging] def unsafeFormat(builder: LogAppender): ZLogger[String, Unit] = + makeLogger { (builder, _, _, _, _, _, _, _, _) => + val now = ZonedDateTime.now() + val value = formatter.format(now) + + builder.appendText(value) + }(builder) + } + + private[logging] final case class LabelFormat(label: String, format: LogFormat) extends LogFormat { + override private[logging] def unsafeFormat(builder: LogAppender): ZLogger[String, Unit] = + makeLogger { (builder, trace, fiberId, level, line, cause, context, spans, annotations) => + builder.openKey() + try builder.appendText(label) + finally builder.closeKeyOpenValue() + + try format.unsafeFormat(builder)(trace, fiberId, level, line, cause, context, spans, annotations) + finally builder.closeValue() + }(builder) + } + + private[logging] final case class LoggerNameFormat( + loggerNameExtractor: LoggerNameExtractor, + loggerNameDefault: String + ) extends LogFormat { + override private[logging] def unsafeFormat(builder: LogAppender): ZLogger[String, Unit] = + makeLogger { (builder, trace, _, _, _, _, context, _, annotations) => + val loggerName = loggerNameExtractor(trace, context, annotations).getOrElse(loggerNameDefault) + builder.appendText(loggerName) + }(builder) + } + + private[logging] final case class FixedFormat(format: LogFormat, size: Int) extends LogFormat { + override private[logging] def unsafeFormat(builder: LogAppender): ZLogger[String, Unit] = + makeLogger { (builder, trace, fiberId, level, line, cause, context, spans, annotations) => + val tempBuilder = new StringBuilder + val append = LogAppender.unstructured { (line: String) => + tempBuilder.append(line) + () + } + format.unsafeFormat(append)(trace, fiberId, level, line, cause, context, spans, annotations) + + val messageSize = tempBuilder.size + if (messageSize < size) { + builder.appendText(tempBuilder.take(size).appendAll(Array.fill(size - messageSize)(' ')).toString()) + } else { + builder.appendText(tempBuilder.take(size).toString()) + } + }(builder) + } + + private[logging] final case class AnnotationFormat(name: String) extends LogFormat { + override private[logging] def unsafeFormat(builder: LogAppender): ZLogger[String, Unit] = + makeLogger { (builder, _, _, _, _, _, _, _, annotations) => + annotations.get(name).foreach { value => + builder.appendKeyValue(name, value) + } + }(builder) + } + + private[logging] final case class AnnotationsFormat(excludeKeys: Set[String]) extends LogFormat { + override private[logging] def unsafeFormat(builder: LogAppender): ZLogger[String, Unit] = + makeLogger { (builder, _, _, _, _, _, _, _, annotations) => + builder.appendKeyValues(annotations.filterNot(kv => excludeKeys.contains(kv._1))) + }(builder) + } + + private[logging] final case class LogAnnotationFormat[A](annotation: LogAnnotation[A]) extends LogFormat { + override private[logging] def unsafeFormat(builder: LogAppender): ZLogger[String, Unit] = + makeLogger { (builder, _, _, _, _, _, fiberRefs, _, _) => + fiberRefs + .get(logContext) + .foreach { context => + context.get(annotation).foreach { value => + builder.appendKeyValue(annotation.name, annotation.render(value)) + } + } + }(builder) + } + + private[logging] final case class LogAnnotationsFormat(excludeKeys: Set[String]) extends LogFormat { + override private[logging] def unsafeFormat(builder: LogAppender): ZLogger[String, Unit] = + makeLogger { (builder, _, _, _, _, _, fiberRefs, _, _) => + fiberRefs + .get(logContext) + .foreach { context => + builder.appendKeyValues(context.asMap.filterNot(kv => excludeKeys.contains(kv._1))) + } + () + }(builder) + } + + private[logging] final case class AllAnnotationsFormat(excludeKeys: Set[String]) extends LogFormat { + override private[logging] def unsafeFormat(builder: LogAppender): ZLogger[String, Unit] = + makeLogger { (builder, _, _, _, _, _, fiberRefs, _, annotations) => + val keyValues = annotations.filterNot(kv => excludeKeys.contains(kv._1)).toList ++ fiberRefs + .get(logContext) + .map { context => + context.asMap.filterNot(kv => excludeKeys.contains(kv._1)).toList + } + .getOrElse(Nil) + + builder.appendKeyValues(keyValues) + () + + }(builder) + } + + private[logging] final case class AnyAnnotationFormat(name: String) extends LogFormat { + override private[logging] def unsafeFormat(builder: LogAppender): ZLogger[String, Unit] = + makeLogger { (builder, _, _, _, _, _, fiberRefs, _, annotations) => + annotations + .get(name) + .orElse( + fiberRefs + .get(logContext) + .flatMap(_.get(name)) + ) + .foreach { value => + builder.appendKeyValue(name, value) + } + }(builder) + } + + private[logging] final case class SpanFormat(name: String) extends LogFormat { + override private[logging] def unsafeFormat(builder: LogAppender): ZLogger[String, Unit] = + makeLogger { (builder, _, _, _, _, _, _, spans, _) => + spans.find(_.label == name).foreach { span => + val duration = (java.lang.System.currentTimeMillis() - span.startTime).toString + builder.appendKeyValue(name, s"${duration}ms") + } + }(builder) + } + /** * A `Pattern` is string representation of `LogFormat` */ @@ -597,99 +789,39 @@ object LogFormat { List[LogSpan], Map[String, String] ) => Any - ): LogFormat = { (builder: LogAppender) => - 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 = { - format(builder, trace, fiberId, logLevel, message, cause, context, spans, annotations) - () - } - } - } + ): LogFormat = FnFormat(format) def loggerName(loggerNameExtractor: LoggerNameExtractor, loggerNameDefault: String = "zio-logger"): LogFormat = - LogFormat.make { (builder, trace, _, _, _, _, context, _, annotations) => - val loggerName = loggerNameExtractor(trace, context, annotations).getOrElse(loggerNameDefault) - builder.appendText(loggerName) - } + LoggerNameFormat(loggerNameExtractor, loggerNameDefault) def annotation(name: String): LogFormat = - LogFormat.make { (builder, _, _, _, _, _, _, _, annotations) => - annotations.get(name).foreach { value => - builder.appendKeyValue(name, value) - } - } + AnnotationFormat(name) def logAnnotation[A](ann: LogAnnotation[A]): LogFormat = - LogFormat.make { (builder, _, _, _, _, _, fiberRefs, _, _) => - fiberRefs - .get(logContext) - .foreach { context => - context.get(ann).foreach { value => - builder.appendKeyValue(ann.name, ann.render(value)) - } - } - } + LogAnnotationFormat(ann) def annotation[A](ann: LogAnnotation[A]): LogFormat = logAnnotation(ann) def anyAnnotation(name: String): LogFormat = - LogFormat.make { (builder, _, _, _, _, _, fiberRefs, _, annotations) => - annotations - .get(name) - .orElse( - fiberRefs - .get(logContext) - .flatMap(_.get(name)) - ) - .foreach { value => - builder.appendKeyValue(name, value) - } - } + AnyAnnotationFormat(name) /** * Returns a new log format that appends all annotations to the log output. */ - def annotations: LogFormat = annotations(Set.empty) + val annotations: LogFormat = annotations(Set.empty) def annotations(excludeKeys: Set[String]): LogFormat = - LogFormat.make { (builder, _, _, _, _, _, _, _, annotations) => - builder.appendKeyValues(annotations.filterNot(kv => excludeKeys.contains(kv._1))) - } + AnnotationsFormat(excludeKeys) - def logAnnotations: LogFormat = logAnnotations(Set.empty) + val logAnnotations: LogFormat = logAnnotations(Set.empty) def logAnnotations(excludeKeys: Set[String]): LogFormat = - LogFormat.make { (builder, _, _, _, _, _, fiberRefs, _, _) => - fiberRefs - .get(logContext) - .foreach { context => - builder.appendKeyValues(context.asMap.filterNot(kv => excludeKeys.contains(kv._1))) - } - () - } + LogAnnotationsFormat(excludeKeys) - def allAnnotations: LogFormat = allAnnotations(Set.empty) + val allAnnotations: LogFormat = allAnnotations(Set.empty) - def allAnnotations(excludeKeys: Set[String]): LogFormat = LogFormat.make { - (builder, _, _, _, _, _, fiberRefs, _, annotations) => - val keyValues = annotations.filterNot(kv => excludeKeys.contains(kv._1)).toList ++ fiberRefs - .get(logContext) - .map { context => - context.asMap.filterNot(kv => excludeKeys.contains(kv._1)).toList - } - .getOrElse(Nil) - - builder.appendKeyValues(keyValues) - } + def allAnnotations(excludeKeys: Set[String]): LogFormat = + AllAnnotationsFormat(excludeKeys) def bracketed(inner: LogFormat): LogFormat = bracketStart + inner + bracketEnd @@ -748,15 +880,7 @@ object LogFormat { def ifCauseNonEmpty(format: LogFormat): LogFormat = format.filter(LogFilter.causeNonEmpty) - def label(label: => String, value: LogFormat): LogFormat = - LogFormat.make { (builder, trace, fiberId, logLevel, message, cause, context, spans, annotations) => - builder.openKey() - try builder.appendText(label) - finally builder.closeKeyOpenValue() - - try value.unsafeFormat(builder)(trace, fiberId, logLevel, message, cause, context, spans, annotations) - finally builder.closeValue() - } + def label(label: => String, value: LogFormat): LogFormat = LabelFormat(label, value) val newLine: LogFormat = text(NL) @@ -770,17 +894,12 @@ object LogFormat { * Returns a new log format that appends the specified span to the log output. */ def span(name: String): LogFormat = - LogFormat.make { (builder, _, _, _, _, _, _, spans, _) => - spans.find(_.label == name).foreach { span => - val duration = (java.lang.System.currentTimeMillis() - span.startTime).toString - builder.appendKeyValue(name, s"${duration}ms") - } - } + SpanFormat(name) /** * Returns a new log format that appends all spans to the log output. */ - def spans: LogFormat = + val spans: LogFormat = LogFormat.make { (builder, _, _, _, _, _, _, spans, _) => builder.appendKeyValues(spans.map { span => val duration = (java.lang.System.currentTimeMillis() - span.startTime).toString @@ -788,18 +907,11 @@ object LogFormat { }) } - def text(value: => String): LogFormat = - LogFormat.make { (builder, _, _, _, _, _, _, _, _) => - builder.appendText(value) - } + def text(value: => String): LogFormat = TextFormat(value) val timestamp: LogFormat = timestamp(DateTimeFormatter.ISO_OFFSET_DATE_TIME) - def timestamp(formatter: => DateTimeFormatter): LogFormat = - text { - val now = ZonedDateTime.now() - formatter.format(now) - } + def timestamp(formatter: => DateTimeFormatter): LogFormat = TimestampFormat(formatter) val default: LogFormat = label("timestamp", timestamp.fixed(32)) |-| diff --git a/core/shared/src/main/scala/zio/logging/LogGroup.scala b/core/shared/src/main/scala/zio/logging/LogGroup.scala index 8a20612a1..26755787a 100644 --- a/core/shared/src/main/scala/zio/logging/LogGroup.scala +++ b/core/shared/src/main/scala/zio/logging/LogGroup.scala @@ -17,7 +17,7 @@ package zio.logging import zio.{ Cause, FiberId, FiberRefs, LogLevel, LogSpan, Trace, Zippable } -trait LogGroup[-Message, Out] { self => +sealed trait LogGroup[-Message, Out] { self => def apply( trace: Trace, @@ -39,24 +39,123 @@ trait LogGroup[-Message, Out] { self => other ) - final def contramap[M](f: M => Message): LogGroup[M, Out] = new LogGroup[M, Out] { + final def contramap[M](f: M => Message): LogGroup[M, Out] = LogGroup.ContramapGroup(self, f) + + /** + * Returns new log group whose result is mapped by the specified f function. + */ + final def map[O](f: Out => O): LogGroup[Message, O] = LogGroup.MapGroup(self, f) + + /** + * Combine this log group with specified log group + */ + final def zip[M <: Message, O, Out2]( + other: LogGroup[M, O] + )(implicit zippable: Zippable.Out[Out, O, Out2]): LogGroup[M, Out2] = LogGroup.ZipGroup(self, other) + + /** + * Zips this log group together with the specified log group using the combination functions. + */ + final def zipWith[M <: Message, O, Out2]( + other: LogGroup[M, O] + )(f: (Out, O) => Out2): LogGroup[M, Out2] = LogGroup.ZipWithGroup(self, other, f) + +} + +object LogGroup { + + private[logging] final case class FnGroup[-Message, Out]( + fn: ( + Trace, + FiberId, + LogLevel, + () => Message, + Cause[Any], + FiberRefs, + List[LogSpan], + Map[String, String] + ) => Out + ) extends LogGroup[Message, Out] { override def apply( trace: Trace, fiberId: FiberId, logLevel: LogLevel, - message: () => M, + message: () => Message, cause: Cause[Any], context: FiberRefs, spans: List[LogSpan], annotations: Map[String, String] ): Out = - self(trace, fiberId, logLevel, () => f(message()), cause, context, spans, annotations) + fn(trace, fiberId, logLevel, message, cause, context, spans, annotations) } - /** - * Returns new log group whose result is mapped by the specified f function. - */ - final def map[O](f: Out => O): LogGroup[Message, O] = new LogGroup[Message, O] { + private[logging] final case class LoggerNameExtractorGroup( + loggerNameExtractor: LoggerNameExtractor, + loggerNameDefault: String + ) extends LogGroup[Any, String] { + override def apply( + trace: Trace, + fiberId: FiberId, + logLevel: LogLevel, + message: () => Any, + cause: Cause[Any], + context: FiberRefs, + spans: List[LogSpan], + annotations: Map[String, String] + ): String = + loggerNameExtractor(trace, context, annotations).getOrElse(loggerNameDefault) + } + + private[logging] final case class ConstantGroup[Output]( + constant: Output + ) extends LogGroup[Any, Output] { + override def apply( + trace: Trace, + fiberId: FiberId, + logLevel: LogLevel, + message: () => Any, + cause: Cause[Any], + context: FiberRefs, + spans: List[LogSpan], + annotations: Map[String, String] + ): Output = + constant + } + + private[logging] final case object CauseGroup extends LogGroup[Any, Cause[Any]] { + override def apply( + trace: Trace, + fiberId: FiberId, + logLevel: LogLevel, + message: () => Any, + cause: Cause[Any], + context: FiberRefs, + spans: List[LogSpan], + annotations: Map[String, String] + ): Cause[Any] = + cause + } + + private[logging] final case object LogLevelGroup extends LogGroup[Any, LogLevel] { + override def apply( + trace: Trace, + fiberId: FiberId, + logLevel: LogLevel, + message: () => Any, + cause: Cause[Any], + context: FiberRefs, + spans: List[LogSpan], + annotations: Map[String, String] + ): LogLevel = + logLevel + } + + private[logging] final case class ZipGroup[Message, Out1, Out2, Out]( + first: LogGroup[Message, Out1], + second: LogGroup[Message, Out2] + )(implicit zippable: Zippable.Out[Out1, Out2, Out]) + extends LogGroup[Message, Out] { + override def apply( trace: Trace, fiberId: FiberId, @@ -66,97 +165,97 @@ trait LogGroup[-Message, Out] { self => context: FiberRefs, spans: List[LogSpan], annotations: Map[String, String] - ): O = - f(self(trace, fiberId, logLevel, message, cause, context, spans, annotations)) + ): Out = + zippable.zip( + first(trace, fiberId, logLevel, message, cause, context, spans, annotations), + second(trace, fiberId, logLevel, message, cause, context, spans, annotations) + ) } - /** - * Combine this log group with specified log group - */ - final def zip[M <: Message, O, Out2]( - other: LogGroup[M, O] - )(implicit zippable: Zippable.Out[Out, O, Out2]): LogGroup[M, Out2] = - new LogGroup[M, Out2] { - override def apply( - trace: Trace, - fiberId: FiberId, - logLevel: LogLevel, - message: () => M, - cause: Cause[Any], - context: FiberRefs, - spans: List[LogSpan], - annotations: Map[String, String] - ): Out2 = - zippable.zip( - self(trace, fiberId, logLevel, message, cause, context, spans, annotations), - other(trace, fiberId, logLevel, message, cause, context, spans, annotations) - ) - } + private[logging] final case class ZipWithGroup[Message, Out1, Out2, Out]( + first: LogGroup[Message, Out1], + second: LogGroup[Message, Out2], + fn: (Out1, Out2) => Out + ) extends LogGroup[Message, Out] { - /** - * Zips this log group together with the specified log group using the combination functions. - */ - final def zipWith[M <: Message, O, Out2]( - other: LogGroup[M, O] - )(f: (Out, O) => Out2): LogGroup[M, Out2] = new LogGroup[M, Out2] { override def apply( trace: Trace, fiberId: FiberId, logLevel: LogLevel, - message: () => M, + message: () => Message, cause: Cause[Any], context: FiberRefs, spans: List[LogSpan], annotations: Map[String, String] - ): Out2 = - f( - self(trace, fiberId, logLevel, message, cause, context, spans, annotations), - other(trace, fiberId, logLevel, message, cause, context, spans, annotations) + ): Out = + fn( + first(trace, fiberId, logLevel, message, cause, context, spans, annotations), + second(trace, fiberId, logLevel, message, cause, context, spans, annotations) ) } -} + private[logging] final case class MapGroup[Message, Out1, Out2]( + group: LogGroup[Message, Out1], + fn: Out1 => Out2 + ) extends LogGroup[Message, Out2] { -object LogGroup { + override def apply( + trace: Trace, + fiberId: FiberId, + logLevel: LogLevel, + message: () => Message, + cause: Cause[Any], + context: FiberRefs, + spans: List[LogSpan], + annotations: Map[String, String] + ): Out2 = + fn( + group(trace, fiberId, logLevel, message, cause, context, spans, annotations) + ) + } + + private[logging] final case class ContramapGroup[Message1, Message2, Out]( + group: LogGroup[Message1, Out], + fn: Message2 => Message1 + ) extends LogGroup[Message2, Out] { - def apply[M, O]( - f: (Trace, FiberId, LogLevel, () => M, Cause[Any], FiberRefs, List[LogSpan], Map[String, String]) => O - ): LogGroup[M, O] = new LogGroup[M, O] { override def apply( trace: Trace, fiberId: FiberId, logLevel: LogLevel, - message: () => M, + message: () => Message2, cause: Cause[Any], context: FiberRefs, spans: List[LogSpan], annotations: Map[String, String] - ): O = - f(trace, fiberId, logLevel, message, cause, context, spans, annotations) + ): Out = + group(trace, fiberId, logLevel, () => fn(message()), cause, context, spans, annotations) + } + def apply[M, O]( + fn: (Trace, FiberId, LogLevel, () => M, Cause[Any], FiberRefs, List[LogSpan], Map[String, String]) => O + ): LogGroup[M, O] = FnGroup(fn) + /** * Log group by cause */ - val cause: LogGroup[Any, Cause[Any]] = apply((_, _, _, _, cause, _, _, _) => cause) + val cause: LogGroup[Any, Cause[Any]] = CauseGroup /** * Log group with given constant value */ - def constant[O](value: O): LogGroup[Any, O] = apply((_, _, _, _, _, _, _, _) => value) + def constant[O](value: O): LogGroup[Any, O] = ConstantGroup(value) def fromLoggerNameExtractor( loggerNameExtractor: LoggerNameExtractor, loggerNameDefault: String = "zio-logger" - ): LogGroup[Any, String] = - apply((trace, _, _, _, _, context, _, annotations) => - loggerNameExtractor(trace, context, annotations).getOrElse(loggerNameDefault) - ) + ): LogGroup[Any, String] = LoggerNameExtractorGroup(loggerNameExtractor, loggerNameDefault) /** * Log group by level */ - val logLevel: LogGroup[Any, LogLevel] = apply((_, _, logLevel, _, _, _, _, _) => logLevel) + val logLevel: LogGroup[Any, LogLevel] = LogLevelGroup /** * Log group by logger name diff --git a/core/shared/src/main/scala/zio/logging/LoggerNameExtractor.scala b/core/shared/src/main/scala/zio/logging/LoggerNameExtractor.scala index 31fd4b461..017fea25c 100644 --- a/core/shared/src/main/scala/zio/logging/LoggerNameExtractor.scala +++ b/core/shared/src/main/scala/zio/logging/LoggerNameExtractor.scala @@ -17,7 +17,7 @@ package zio.logging import zio.{ FiberRefs, Trace } -trait LoggerNameExtractor { self => +sealed trait LoggerNameExtractor { self => def apply( trace: Trace, @@ -35,7 +35,7 @@ trait LoggerNameExtractor { self => * The alphanumeric version of the `||` operator. */ final def or(other: LoggerNameExtractor): LoggerNameExtractor = - (trace, context, annotations) => self(trace, context, annotations).orElse(other(trace, context, annotations)) + LoggerNameExtractor.OrExtractor(self, other) /** * Converts this extractor into a log format @@ -52,13 +52,33 @@ trait LoggerNameExtractor { self => object LoggerNameExtractor { + private[logging] final case class FnExtractor(fn: (Trace, FiberRefs, Map[String, String]) => Option[String]) + extends LoggerNameExtractor { + override def apply(trace: Trace, context: FiberRefs, annotations: Map[String, String]): Option[String] = + fn(trace, context, annotations) + } + + private[logging] final case class AnnotationExtractor(name: String) extends LoggerNameExtractor { + override def apply(trace: Trace, context: FiberRefs, annotations: Map[String, String]): Option[String] = + annotations.get(name) + } + + private[logging] final case class OrExtractor(first: LoggerNameExtractor, second: LoggerNameExtractor) + extends LoggerNameExtractor { + + override def apply(trace: Trace, context: FiberRefs, annotations: Map[String, String]): Option[String] = + first(trace, context, annotations).orElse(second(trace, context, annotations)) + } + + def make(fn: (Trace, FiberRefs, Map[String, String]) => Option[String]): LoggerNameExtractor = FnExtractor(fn) + /** * Extractor which take logger name from [[Trace]] * * trace with value ''example.LivePingService.ping(PingService.scala:22)'' * will have ''example.LivePingService'' as logger name */ - val trace: LoggerNameExtractor = (trace, _, _) => + val trace: LoggerNameExtractor = FnExtractor((trace, _, _) => trace match { case Trace(location, _, _) => val last = location.lastIndexOf(".") @@ -68,13 +88,14 @@ object LoggerNameExtractor { Some(name) case _ => None } + ) /** * Extractor which take logger name from annotation * * @param name name of annotation */ - def annotation(name: String): LoggerNameExtractor = (_, _, annotations) => annotations.get(name) + def annotation(name: String): LoggerNameExtractor = AnnotationExtractor(name) /** * Extractor which take logger name from annotation or [[Trace]] if specified annotation is not present diff --git a/core/shared/src/main/scala/zio/logging/internal/ReconfigurableLogger.scala b/core/shared/src/main/scala/zio/logging/internal/ReconfigurableLogger.scala index 248e604d4..0d02d3aab 100644 --- a/core/shared/src/main/scala/zio/logging/internal/ReconfigurableLogger.scala +++ b/core/shared/src/main/scala/zio/logging/internal/ReconfigurableLogger.scala @@ -16,7 +16,7 @@ package zio.logging.internal import zio.{ Cause, FiberId, FiberRefs, LogLevel, LogSpan, Trace, ZLogger } - +import zio.prelude._ import java.util.concurrent.atomic.AtomicReference private[logging] sealed trait ReconfigurableLogger[-Message, +Output, Config] extends ZLogger[Message, Output] { @@ -28,7 +28,7 @@ private[logging] sealed trait ReconfigurableLogger[-Message, +Output, Config] ex private[logging] object ReconfigurableLogger { - def apply[M, O, C]( + def apply[M, O, C: Equal]( config: C, makeLogger: C => ZLogger[M, O] ): ReconfigurableLogger[M, O, C] = @@ -39,11 +39,13 @@ private[logging] object ReconfigurableLogger { new AtomicReference[(C, ZLogger[M, O])]((config, logger)) } - override def reconfigureIfChanged(config: C): Boolean = - if (configureLogger.get()._1 != config) { + override def reconfigureIfChanged(config: C): Boolean = { + val currentConfig = configureLogger.get()._1 + if (currentConfig !== config) { reconfigure(config) true } else false + } override def reconfigure(config: C): Unit = { val logger = makeLogger(config) diff --git a/project/Versions.scala b/project/Versions.scala index 297cdf60b..c2f1d9603 100644 --- a/project/Versions.scala +++ b/project/Versions.scala @@ -9,5 +9,6 @@ object Versions { val zioMetricsConnectorsVersion = "2.0.8" val zioConfig = "4.0.0" val zioParser = "0.1.9" + val zioPrelude = "1.0.0-RC19" val log4jVersion = "2.19.0" } From d93639159faebad79e4043eea93aff52481c7ed3 Mon Sep 17 00:00:00 2001 From: Peter Kotula Date: Thu, 1 Jun 2023 10:52:05 +0200 Subject: [PATCH 07/56] LogFilter, LogFormat, LoggerNameExtractor, LogGroup - sealed and custom types, configuration - equal --- .../zio/logging/ConsoleLoggerConfigSpec.scala | 25 ++++++ .../zio/logging/FileLoggerConfigSpec.scala | 27 ++++++ .../internal/ReconfigurableLoggerSpec.scala | 90 ++++++++----------- .../main/scala/zio/logging/LogFormat.scala | 4 +- 4 files changed, 93 insertions(+), 53 deletions(-) diff --git a/core/jvm/src/test/scala/zio/logging/ConsoleLoggerConfigSpec.scala b/core/jvm/src/test/scala/zio/logging/ConsoleLoggerConfigSpec.scala index 4431a38d2..3602103da 100644 --- a/core/jvm/src/test/scala/zio/logging/ConsoleLoggerConfigSpec.scala +++ b/core/jvm/src/test/scala/zio/logging/ConsoleLoggerConfigSpec.scala @@ -34,6 +34,31 @@ object ConsoleLoggerConfigSpec extends ZIOSpecDefault { assertTrue(true) } }, + test("equals config with same sources") { + + // "%highlight{%timestamp{yyyy-MM-dd'T'HH:mm:ssZ} %fixed{7}{%level} [%fiberId] %name:%line %message %cause}" //FIXME + + val logFormat = + "%highlight{%timestamp %fixed{7}{%level} [%fiberId] %name:%line %message %cause}" + + val configProvider: ConfigProvider = ConfigProvider.fromMap( + Map( + "logger/format" -> logFormat, + "logger/filter/rootLevel" -> LogLevel.Info.label, + "logger/filter/mappings/zio.logging.example.LivePingService" -> LogLevel.Debug.label + ), + "/" + ) + + import zio.prelude._ + for { + c1 <- configProvider.load(ConsoleLoggerConfig.config.nested("logger")) + c2 <- configProvider.load(ConsoleLoggerConfig.config.nested("logger")) + } yield assertTrue( + c1.format == c2.format, + c1 === c2 + ) + }, test("fail on invalid filter config") { val logFormat = "%highlight{%timestamp{yyyy-MM-dd'T'HH:mm:ssZ} %fixed{7}{%level} [%fiberId] %name:%line %message %cause}" diff --git a/core/jvm/src/test/scala/zio/logging/FileLoggerConfigSpec.scala b/core/jvm/src/test/scala/zio/logging/FileLoggerConfigSpec.scala index c1f499775..774a8b2c1 100644 --- a/core/jvm/src/test/scala/zio/logging/FileLoggerConfigSpec.scala +++ b/core/jvm/src/test/scala/zio/logging/FileLoggerConfigSpec.scala @@ -52,6 +52,33 @@ object FileLoggerConfigSpec extends ZIOSpecDefault { assertTrue(loadedConfig.bufferedIOSize.isEmpty) } }, + test("equals config with same sources") { + + val logFormat = + "%highlight{%timestamp %fixed{7}{%level} [%fiberId] %name:%line %message %cause}" + + val configProvider: ConfigProvider = ConfigProvider.fromMap( + Map( + "logger/format" -> logFormat, + "logger/path" -> "file:///tmp/test.log", + "logger/autoFlushBatchSize" -> "2", + "logger/bufferedIOSize" -> "4096", + "logger/rollingPolicy/type" -> "TimeBasedRollingPolicy", + "logger/filter/rootLevel" -> LogLevel.Info.label, + "logger/filter/mappings/zio.logging.example.LivePingService" -> LogLevel.Debug.label + ), + "/" + ) + + import zio.prelude._ + for { + c1 <- configProvider.load(ConsoleLoggerConfig.config.nested("logger")) + c2 <- configProvider.load(ConsoleLoggerConfig.config.nested("logger")) + } yield assertTrue( + c1.format == c2.format, + c1 === c2 + ) + }, test("fail on invalid charset and filter config") { val logFormat = "%highlight{%timestamp{yyyy-MM-dd'T'HH:mm:ssZ} %fixed{7}{%level} [%fiberId] %name:%line %message %cause}" diff --git a/core/jvm/src/test/scala/zio/logging/internal/ReconfigurableLoggerSpec.scala b/core/jvm/src/test/scala/zio/logging/internal/ReconfigurableLoggerSpec.scala index cbe9d84c0..6bb225365 100644 --- a/core/jvm/src/test/scala/zio/logging/internal/ReconfigurableLoggerSpec.scala +++ b/core/jvm/src/test/scala/zio/logging/internal/ReconfigurableLoggerSpec.scala @@ -7,7 +7,11 @@ import zio.{ Chunk, Config, ConfigProvider, LogLevel, Queue, Runtime, Schedule, object ReconfigurableLoggerSpec extends ZIOSpecDefault { - def configuredLogger(queue: zio.Queue[String], configPath: String = "logger"): ZLayer[Any, Config.Error, Unit] = + def configuredLogger( + queue: zio.Queue[String], + reconfigurations: zio.Ref[Int], + configPath: String = "logger" + ): ZLayer[Any, Config.Error, Unit] = ZLayer.scoped { for { config <- ZIO.config(ConsoleLoggerConfig.config.nested(configPath)) @@ -26,6 +30,9 @@ object ReconfigurableLoggerSpec extends ZIOSpecDefault { .map { newConfig => logger.reconfigureIfChanged(newConfig) } + .flatMap { r => + reconfigurations.update(_ + 1).when(r) + } .scheduleFork(Schedule.fixed(200.millis)) _ <- ZIO.withLoggerScoped(logger) } yield () @@ -40,62 +47,43 @@ object ReconfigurableLoggerSpec extends ZIOSpecDefault { "logger/filter/mappings/zio.logging.example.LivePingService" -> LogLevel.Debug.label ) - ZIO - .foreach(initialProperties) { case (k, v) => - TestSystem.putProperty(k, v).as(k -> v) - } - .flatMap { _ => - Queue.unbounded[String].flatMap { queue => - val runTest = - for { - _ <- ZIO.logInfo("info") - _ <- ZIO.logDebug("debug") - elements1 <- queue.takeAll - _ <- TestSystem.putProperty("logger/format", "%level %message") - _ <- ZIO.sleep(500.millis) - _ <- ZIO.logWarning("warn") - elements2 <- queue.takeAll - _ <- TestSystem.putProperty("logger/format", "L: %level M: %message") - _ <- TestSystem.putProperty("logger/filter/rootLevel", LogLevel.Debug.label) - _ <- ZIO.sleep(500.millis) - _ <- ZIO.logDebug("debug") - elements3 <- queue.takeAll - } yield assertTrue( - elements1 == Chunk("info") && elements2 == Chunk("WARN warn") && elements3 == Chunk("L: DEBUG M: debug") - ) - - runTest.provide( - Runtime.removeDefaultLoggers >>> Runtime - .setConfigProvider(ConfigProvider.fromProps("/")) >>> configuredLogger(queue) - ) - } - } - - }, - test("equals config with same sources") { + for { + _ <- ZIO.foreach(initialProperties) { case (k, v) => + TestSystem.putProperty(k, v).as(k -> v) + } - val configPath = "logger" + queue <- Queue.unbounded[String] - val initialProperties = Map( - "logger/format" -> "%message", - "logger/filter/rootLevel" -> LogLevel.Info.label, - "logger/filter/mappings/zio.logging.example.LivePingService" -> LogLevel.Debug.label - ) + reconfigurationsCounter <- Ref.make(0) - ZIO - .foreach(initialProperties) { case (k, v) => - TestSystem.putProperty(k, v).as(k -> v) - } - .flatMap { _ => - import zio.prelude._ + runTest = for { - c1 <- ZIO.config(ConsoleLoggerConfig.config.nested(configPath)) - c2 <- ZIO.config(ConsoleLoggerConfig.config.nested(configPath)) + _ <- ZIO.logInfo("info") + _ <- ZIO.logDebug("debug") + elements1 <- queue.takeAll + _ <- TestSystem.putProperty("logger/format", "%level %message") + _ <- ZIO.sleep(500.millis) + _ <- ZIO.logWarning("warn") + elements2 <- queue.takeAll + _ <- TestSystem.putProperty("logger/format", "L: %level M: %message") + _ <- TestSystem.putProperty("logger/filter/rootLevel", LogLevel.Debug.label) + _ <- ZIO.sleep(500.millis) + _ <- ZIO.logDebug("debug") + elements3 <- queue.takeAll + reconfigured <- reconfigurationsCounter.get } yield assertTrue( - c1 === c2 + elements1 == Chunk("info") && elements2 == Chunk("WARN warn") && elements3 == Chunk( + "L: DEBUG M: debug" + ) && reconfigured == 2 ) - } - .provide(Runtime.setConfigProvider(ConfigProvider.fromProps("/"))) + + result <- + runTest.provide( + Runtime.removeDefaultLoggers >>> Runtime + .setConfigProvider(ConfigProvider.fromProps("/")) >>> configuredLogger(queue, reconfigurationsCounter) + ) + } yield result + } ) @@ TestAspect.withLiveClock } diff --git a/core/shared/src/main/scala/zio/logging/LogFormat.scala b/core/shared/src/main/scala/zio/logging/LogFormat.scala index 667e754af..66ae987c3 100644 --- a/core/shared/src/main/scala/zio/logging/LogFormat.scala +++ b/core/shared/src/main/scala/zio/logging/LogFormat.scala @@ -93,7 +93,7 @@ sealed trait LogFormat { self => * the log output is highlighted. */ final def highlight: LogFormat = - highlight(defaultHighlighter(_)) + highlight(defaultHighlighter) /** * The alphanumeric version of the `|-|` operator. @@ -165,7 +165,7 @@ sealed trait LogFormat { self => builder.toString() } - private def defaultHighlighter(level: LogLevel) = level match { + private val defaultHighlighter: LogLevel => LogColor = { case LogLevel.Error => LogColor.RED case LogLevel.Warning => LogColor.YELLOW case LogLevel.Info => LogColor.CYAN From aba4a2a4a03258545757244fe3348ab2eb8cb2e7 Mon Sep 17 00:00:00 2001 From: Peter Kotula Date: Thu, 1 Jun 2023 18:38:21 +0200 Subject: [PATCH 08/56] LogFilter, LogFormat, LoggerNameExtractor, LogGroup - sealed and custom types, configuration - equal --- .../scala/zio/logging/LogFilterSpec.scala | 31 +++++++ .../main/scala/zio/logging/LogFilter.scala | 84 +++++++++++++++---- 2 files changed, 97 insertions(+), 18 deletions(-) diff --git a/core/jvm/src/test/scala/zio/logging/LogFilterSpec.scala b/core/jvm/src/test/scala/zio/logging/LogFilterSpec.scala index fc1fd26df..adef41c52 100644 --- a/core/jvm/src/test/scala/zio/logging/LogFilterSpec.scala +++ b/core/jvm/src/test/scala/zio/logging/LogFilterSpec.scala @@ -399,6 +399,37 @@ object LogFilterSpec extends ZIOSpecDefault { "**" -> LogLevel.Info ) ) + }, + test("log filters by log level and name from same configuration should be equal") { + + val configProvider = ConfigProvider.fromMap( + Map( + "logger/rootLevel" -> LogLevel.Debug.label, + "logger/mappings/a" -> LogLevel.Info.label, + "logger/mappings/a.b.c" -> LogLevel.Warning.label, + "logger/mappings/e.f" -> LogLevel.Error.label + ), + "/" + ) + + import zio.prelude._ + + for { + f1 <- configProvider + .load(LogFilter.LogLevelByNameConfig.config.nested("logger")) + .map(LogFilter.logLevelByName) + f2 <- configProvider + .load(LogFilter.LogLevelByNameConfig.config.nested("logger")) + .map(LogFilter.logLevelByName) + f3 <- configProvider + .load(LogFilter.LogLevelByNameConfig.config.nested("logger")) + .map(LogFilter.logLevelByName) + .map(_.cached) + f4 <- configProvider + .load(LogFilter.LogLevelByNameConfig.config.nested("logger")) + .map(LogFilter.logLevelByName) + .map(_.cached) + } yield assertTrue(f1 === f2, f3 === f4) } ) } diff --git a/core/shared/src/main/scala/zio/logging/LogFilter.scala b/core/shared/src/main/scala/zio/logging/LogFilter.scala index e9a8b092d..b93271adc 100644 --- a/core/shared/src/main/scala/zio/logging/LogFilter.scala +++ b/core/shared/src/main/scala/zio/logging/LogFilter.scala @@ -60,14 +60,7 @@ sealed trait LogFilter[-Message] { self => * The alphanumeric version of the `&&` operator. */ final def and[M <: Message](other: LogFilter[M]): LogFilter[M] = - LogFilter[M, (self.Value, other.Value)]( - self.group ++ other.group, - v => { - val (v1, v2) = v - - self.predicate(v1) && other.predicate(v2) - } - ) + LogFilter.AndFilter(self, other) /** * Returns a new log filter with cached results @@ -92,20 +85,13 @@ sealed trait LogFilter[-Message] { self => * The alphanumeric version of the `!` operator. */ final def not: LogFilter[Message] = - LogFilter[Message, self.Value](self.group, v => !self.predicate(v)) + LogFilter.NotFilter(self) /** * The alphanumeric version of the `||` operator. */ final def or[M <: Message](other: LogFilter[M]): LogFilter[M] = - LogFilter[M, (self.Value, other.Value)]( - self.group ++ other.group, - v => { - val (v1, v2) = v - - self.predicate(v1) || other.predicate(v2) - } - ) + LogFilter.OrFilter(self, other) /** * Returns a new log filter with negated result @@ -120,6 +106,9 @@ object LogFilter { (l, r) match { case (l: GroupPredicateFilter[_, _], r: GroupPredicateFilter[_, _]) => GroupPredicateFilter.equal.equal(l, r) case (l: CachedFilter[_], r: CachedFilter[_]) => CachedFilter.equal.equal(l, r) + case (l: AndFilter[_], r: AndFilter[_]) => AndFilter.equal.equal(l, r) + case (l: OrFilter[_], r: OrFilter[_]) => OrFilter.equal.equal(l, r) + case (l: NotFilter[_], r: NotFilter[_]) => NotFilter.equal.equal(l, r) case (l: ConfiguredFilter[_, _], r: ConfiguredFilter[_, _]) => ConfiguredFilter.equal.equal(l, r) case (l, r) => l == r } @@ -140,6 +129,63 @@ object LogFilter { implicit val equal: Equal[GroupPredicateFilter[_, _]] = Equal.default } + private[logging] final case class AndFilter[M]( + first: LogFilter[M], + second: LogFilter[M] + ) extends LogFilter[M] { + override type Value = (first.Value, second.Value) + + override val group: LogGroup[M, Value] = first.group ++ second.group + + override def predicate(value: Value): Boolean = { + val (v1, v2) = value + first.predicate(v1) && second.predicate(v2) + } + } + + private[logging] object AndFilter { + implicit val equal: Equal[AndFilter[_]] = Equal.make { (f, s) => + LogFilter.equal.equal(f.first, s.first) && LogFilter.equal.equal(f.second, s.second) + } + } + + private[logging] final case class OrFilter[M]( + first: LogFilter[M], + second: LogFilter[M] + ) extends LogFilter[M] { + override type Value = (first.Value, second.Value) + + override val group: LogGroup[M, Value] = first.group ++ second.group + + override def predicate(value: Value): Boolean = { + val (v1, v2) = value + first.predicate(v1) || second.predicate(v2) + } + } + + private[logging] object OrFilter { + implicit val equal: Equal[OrFilter[_]] = Equal.make { (f, s) => + LogFilter.equal.equal(f.first, s.first) && LogFilter.equal.equal(f.second, s.second) + } + } + + private[logging] final case class NotFilter[M]( + filter: LogFilter[M] + ) extends LogFilter[M] { + override type Value = filter.Value + + override def group: LogGroup[M, Value] = filter.group + + override def predicate(value: Value): Boolean = + !filter.predicate(value) + } + + private[logging] object NotFilter { + implicit val equal: Equal[NotFilter[_]] = Equal.make { (f, s) => + LogFilter.equal.equal(f.filter, s.filter) + } + } + private[logging] final case class CachedFilter[M](filter: LogFilter[M]) extends LogFilter[M] { private[logging] val cache = new java.util.concurrent.ConcurrentHashMap[filter.Value, Boolean]() @@ -159,7 +205,9 @@ object LogFilter { } private[logging] object CachedFilter { - implicit val equal: Equal[CachedFilter[_]] = Equal.default.contramap(_.filter) + implicit val equal: Equal[CachedFilter[_]] = Equal.make { (f, s) => + LogFilter.equal.equal(f.filter, s.filter) + } } private[logging] final case class ConfiguredFilter[M, C](config: C, make: C => LogFilter[M]) extends LogFilter[M] { From 7f646c1d69ea3a83c303f6398c1955f35f888478 Mon Sep 17 00:00:00 2001 From: Peter Kotula Date: Thu, 1 Jun 2023 21:10:53 +0200 Subject: [PATCH 09/56] tests --- .../scala/zio/logging/LogFilterSpec.scala | 108 +++++++++++++++++- .../internal/ReconfigurableLoggerSpec.scala | 8 +- .../main/scala/zio/logging/LogFilter.scala | 2 +- 3 files changed, 112 insertions(+), 6 deletions(-) diff --git a/core/jvm/src/test/scala/zio/logging/LogFilterSpec.scala b/core/jvm/src/test/scala/zio/logging/LogFilterSpec.scala index adef41c52..59ae8a579 100644 --- a/core/jvm/src/test/scala/zio/logging/LogFilterSpec.scala +++ b/core/jvm/src/test/scala/zio/logging/LogFilterSpec.scala @@ -3,7 +3,21 @@ package zio.logging import zio.logging.test.TestService import zio.test.ZTestLogger.LogEntry import zio.test._ -import zio.{ Cause, Chunk, Config, ConfigProvider, FiberId, FiberRefs, LogLevel, LogSpan, Runtime, Trace, ZIO, ZLogger } +import zio.{ + Cause, + Chunk, + Config, + ConfigProvider, + FiberId, + FiberRefs, + LogLevel, + LogSpan, + Runtime, + Trace, + ZIO, + ZIOAspect, + ZLogger +} object LogFilterSpec extends ZIOSpecDefault { @@ -430,6 +444,98 @@ object LogFilterSpec extends ZIOSpecDefault { .map(LogFilter.logLevelByName) .map(_.cached) } yield assertTrue(f1 === f2, f3 === f4) + }, + test("and") { + + val filter = LogFilter.causeNonEmpty.and(LogFilter.logLevel(LogLevel.Info)) + + def testFilter(level: LogLevel, cause: Cause[_], expected: Boolean) = + assertTrue( + filter( + Trace.empty, + FiberId.None, + level, + () => "", + cause, + FiberRefs.empty, + List.empty, + Map.empty + ) == expected + ) + + testFilter(LogLevel.Info, Cause.fail("fail"), true) && testFilter( + LogLevel.Info, + Cause.empty, + false + ) && testFilter(LogLevel.Debug, Cause.fail("fail"), false) + }, + test("or") { + + val filter = LogFilter.causeNonEmpty.or(LogFilter.logLevel(LogLevel.Info)) + + def testFilter(level: LogLevel, cause: Cause[_], expected: Boolean) = + assertTrue( + filter( + Trace.empty, + FiberId.None, + level, + () => "", + cause, + FiberRefs.empty, + List.empty, + Map.empty + ) == expected + ) + + testFilter(LogLevel.Info, Cause.fail("fail"), true) && testFilter(LogLevel.Info, Cause.empty, true) && testFilter( + LogLevel.Debug, + Cause.fail("fail"), + true + ) && testFilter(LogLevel.Debug, Cause.empty, false) + }, + test("not") { + + val filter = LogFilter.causeNonEmpty.and(LogFilter.logLevel(LogLevel.Info)).not + + def testFilter(level: LogLevel, cause: Cause[_], expected: Boolean) = + assertTrue( + filter( + Trace.empty, + FiberId.None, + level, + () => "", + cause, + FiberRefs.empty, + List.empty, + Map.empty + ) == expected + ) + + testFilter(LogLevel.Info, Cause.fail("fail"), false) && testFilter( + LogLevel.Info, + Cause.empty, + true + ) && testFilter(LogLevel.Debug, Cause.fail("fail"), true) + }, + test("cached") { + val logOutputRef = new java.util.concurrent.atomic.AtomicReference[Chunk[LogEntry]](Chunk.empty) + + val filter = LogFilter + .logLevelByGroup( + LogLevel.Info, + LogGroup.loggerName, + "zio.logger1" -> LogLevel.Debug, + "zio.logging.test" -> LogLevel.Warning + ) + .cached + .asInstanceOf[LogFilter.CachedFilter[String]] + + (for { + _ <- ZIO.logDebug("debug") @@ ZIOAspect.annotated(loggerNameAnnotationKey, "zio.logger1") + res1 = filter.cache.get(List("zio", "logger1") -> LogLevel.Debug) + _ <- ZIO.logDebug("debug") @@ ZIOAspect.annotated(loggerNameAnnotationKey, "zio.logger2") + res2 = filter.cache.get(List("zio", "logger2") -> LogLevel.Debug) + } yield assertTrue(res1 == true, res2 == false)).provideLayer(testLogger(logOutputRef, filter)) } ) } diff --git a/core/jvm/src/test/scala/zio/logging/internal/ReconfigurableLoggerSpec.scala b/core/jvm/src/test/scala/zio/logging/internal/ReconfigurableLoggerSpec.scala index 6bb225365..b53628439 100644 --- a/core/jvm/src/test/scala/zio/logging/internal/ReconfigurableLoggerSpec.scala +++ b/core/jvm/src/test/scala/zio/logging/internal/ReconfigurableLoggerSpec.scala @@ -1,8 +1,7 @@ package zio.logging.internal -import zio.logging.ConsoleLoggerConfig +import zio.logging.{ConsoleLoggerConfig, _} import zio.test._ -import zio.logging._ import zio.{ Chunk, Config, ConfigProvider, LogLevel, Queue, Runtime, Schedule, ZIO, ZLayer, _ } object ReconfigurableLoggerSpec extends ZIOSpecDefault { @@ -30,8 +29,8 @@ object ReconfigurableLoggerSpec extends ZIOSpecDefault { .map { newConfig => logger.reconfigureIfChanged(newConfig) } - .flatMap { r => - reconfigurations.update(_ + 1).when(r) + .flatMap { reconfigured => + reconfigurations.update(_ + 1).when(reconfigured) } .scheduleFork(Schedule.fixed(200.millis)) _ <- ZIO.withLoggerScoped(logger) @@ -64,6 +63,7 @@ object ReconfigurableLoggerSpec extends ZIOSpecDefault { _ <- TestSystem.putProperty("logger/format", "%level %message") _ <- ZIO.sleep(500.millis) _ <- ZIO.logWarning("warn") + _ <- ZIO.logDebug("debug") elements2 <- queue.takeAll _ <- TestSystem.putProperty("logger/format", "L: %level M: %message") _ <- TestSystem.putProperty("logger/filter/rootLevel", LogLevel.Debug.label) diff --git a/core/shared/src/main/scala/zio/logging/LogFilter.scala b/core/shared/src/main/scala/zio/logging/LogFilter.scala index b93271adc..e4f6aaea0 100644 --- a/core/shared/src/main/scala/zio/logging/LogFilter.scala +++ b/core/shared/src/main/scala/zio/logging/LogFilter.scala @@ -15,8 +15,8 @@ */ package zio.logging -import zio.{ Cause, Config, FiberId, FiberRefs, LogLevel, LogSpan, Trace } import zio.prelude.Equal +import zio.{ Cause, Config, FiberId, FiberRefs, LogLevel, LogSpan, Trace } import scala.annotation.tailrec From b014b9366985ba715fac3181b8e7db38cf4650fd Mon Sep 17 00:00:00 2001 From: Peter Kotula Date: Thu, 1 Jun 2023 21:21:27 +0200 Subject: [PATCH 10/56] fmt --- .../src/main/scala/zio/logging/ConsoleLoggerConfig.scala | 2 +- .../main/scala/zio/logging/internal/ReconfigurableLogger.scala | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/core/shared/src/main/scala/zio/logging/ConsoleLoggerConfig.scala b/core/shared/src/main/scala/zio/logging/ConsoleLoggerConfig.scala index 4f77c2609..d538f9a21 100644 --- a/core/shared/src/main/scala/zio/logging/ConsoleLoggerConfig.scala +++ b/core/shared/src/main/scala/zio/logging/ConsoleLoggerConfig.scala @@ -15,8 +15,8 @@ */ package zio.logging -import zio.{ Config, LogLevel } import zio.prelude._ +import zio.{ Config, LogLevel } final case class ConsoleLoggerConfig(format: LogFormat, filter: LogFilter[String]) diff --git a/core/shared/src/main/scala/zio/logging/internal/ReconfigurableLogger.scala b/core/shared/src/main/scala/zio/logging/internal/ReconfigurableLogger.scala index 0d02d3aab..3ce509c15 100644 --- a/core/shared/src/main/scala/zio/logging/internal/ReconfigurableLogger.scala +++ b/core/shared/src/main/scala/zio/logging/internal/ReconfigurableLogger.scala @@ -15,8 +15,9 @@ */ package zio.logging.internal -import zio.{ Cause, FiberId, FiberRefs, LogLevel, LogSpan, Trace, ZLogger } import zio.prelude._ +import zio.{ Cause, FiberId, FiberRefs, LogLevel, LogSpan, Trace, ZLogger } + import java.util.concurrent.atomic.AtomicReference private[logging] sealed trait ReconfigurableLogger[-Message, +Output, Config] extends ZLogger[Message, Output] { From 3a79ceed8369e0eeb8c0ce1c0728a6908935b752 Mon Sep 17 00:00:00 2001 From: Peter Kotula Date: Thu, 1 Jun 2023 23:17:20 +0200 Subject: [PATCH 11/56] ReconfigurableLogger --- .../internal/ReconfigurableLoggerSpec.scala | 69 ++++++++----------- .../internal/ReconfigurableLogger.scala | 15 +++- 2 files changed, 42 insertions(+), 42 deletions(-) diff --git a/core/jvm/src/test/scala/zio/logging/internal/ReconfigurableLoggerSpec.scala b/core/jvm/src/test/scala/zio/logging/internal/ReconfigurableLoggerSpec.scala index b53628439..2be346166 100644 --- a/core/jvm/src/test/scala/zio/logging/internal/ReconfigurableLoggerSpec.scala +++ b/core/jvm/src/test/scala/zio/logging/internal/ReconfigurableLoggerSpec.scala @@ -1,6 +1,6 @@ package zio.logging.internal -import zio.logging.{ConsoleLoggerConfig, _} +import zio.logging.{ ConsoleLoggerConfig, _ } import zio.test._ import zio.{ Chunk, Config, ConfigProvider, LogLevel, Queue, Runtime, Schedule, ZIO, ZLayer, _ } @@ -8,32 +8,22 @@ object ReconfigurableLoggerSpec extends ZIOSpecDefault { def configuredLogger( queue: zio.Queue[String], - reconfigurations: zio.Ref[Int], configPath: String = "logger" ): ZLayer[Any, Config.Error, Unit] = ZLayer.scoped { for { - config <- ZIO.config(ConsoleLoggerConfig.config.nested(configPath)) - - logger = ReconfigurableLogger[String, Any, ConsoleLoggerConfig]( - config, - config => - config.format.toLogger.map { line => - zio.Unsafe.unsafe { implicit u => - Runtime.default.unsafe.run(queue.offer(line)) - } - }.filter(config.filter) - ) - _ <- ZIO - .config(ConsoleLoggerConfig.config.nested(configPath)) - .map { newConfig => - logger.reconfigureIfChanged(newConfig) - } - .flatMap { reconfigured => - reconfigurations.update(_ + 1).when(reconfigured) - } - .scheduleFork(Schedule.fixed(200.millis)) - _ <- ZIO.withLoggerScoped(logger) + logger <- ReconfigurableLogger + .make[Config.Error, String, Any, ConsoleLoggerConfig]( + ZIO.config(ConsoleLoggerConfig.config.nested(configPath)), + config => + config.format.toLogger.map { line => + zio.Unsafe.unsafe { implicit u => + Runtime.default.unsafe.run(queue.offer(line)) + } + }.filter(config.filter), + 200.millis + ) + _ <- ZIO.withLoggerScoped(logger) } yield () } @@ -53,34 +43,31 @@ object ReconfigurableLoggerSpec extends ZIOSpecDefault { queue <- Queue.unbounded[String] - reconfigurationsCounter <- Ref.make(0) - runTest = for { - _ <- ZIO.logInfo("info") - _ <- ZIO.logDebug("debug") - elements1 <- queue.takeAll - _ <- TestSystem.putProperty("logger/format", "%level %message") - _ <- ZIO.sleep(500.millis) - _ <- ZIO.logWarning("warn") - _ <- ZIO.logDebug("debug") - elements2 <- queue.takeAll - _ <- TestSystem.putProperty("logger/format", "L: %level M: %message") - _ <- TestSystem.putProperty("logger/filter/rootLevel", LogLevel.Debug.label) - _ <- ZIO.sleep(500.millis) - _ <- ZIO.logDebug("debug") - elements3 <- queue.takeAll - reconfigured <- reconfigurationsCounter.get + _ <- ZIO.logInfo("info") + _ <- ZIO.logDebug("debug") + elements1 <- queue.takeAll + _ <- TestSystem.putProperty("logger/format", "%level %message") + _ <- ZIO.sleep(500.millis) + _ <- ZIO.logWarning("warn") + _ <- ZIO.logDebug("debug") + elements2 <- queue.takeAll + _ <- TestSystem.putProperty("logger/format", "L: %level M: %message") + _ <- TestSystem.putProperty("logger/filter/rootLevel", LogLevel.Debug.label) + _ <- ZIO.sleep(500.millis) + _ <- ZIO.logDebug("debug") + elements3 <- queue.takeAll } yield assertTrue( elements1 == Chunk("info") && elements2 == Chunk("WARN warn") && elements3 == Chunk( "L: DEBUG M: debug" - ) && reconfigured == 2 + ) ) result <- runTest.provide( Runtime.removeDefaultLoggers >>> Runtime - .setConfigProvider(ConfigProvider.fromProps("/")) >>> configuredLogger(queue, reconfigurationsCounter) + .setConfigProvider(ConfigProvider.fromProps("/")) >>> configuredLogger(queue) ) } yield result diff --git a/core/shared/src/main/scala/zio/logging/internal/ReconfigurableLogger.scala b/core/shared/src/main/scala/zio/logging/internal/ReconfigurableLogger.scala index 3ce509c15..204d77306 100644 --- a/core/shared/src/main/scala/zio/logging/internal/ReconfigurableLogger.scala +++ b/core/shared/src/main/scala/zio/logging/internal/ReconfigurableLogger.scala @@ -15,8 +15,8 @@ */ package zio.logging.internal +import zio._ import zio.prelude._ -import zio.{ Cause, FiberId, FiberRefs, LogLevel, LogSpan, Trace, ZLogger } import java.util.concurrent.atomic.AtomicReference @@ -66,4 +66,17 @@ private[logging] object ReconfigurableLogger { configureLogger.get()._2.apply(trace, fiberId, logLevel, message, cause, context, spans, annotations) } + def make[E, M, O, C: Equal]( + loadConfig: => ZIO[Any, E, C], + makeLogger: C => ZLogger[M, O], + duration: Duration = 10.seconds + ): ZIO[Scope, E, ReconfigurableLogger[M, O, C]] = + for { + config <- loadConfig + logger = ReconfigurableLogger[M, O, C](config, makeLogger) + _ <- loadConfig.map { newConfig => + logger.reconfigureIfChanged(newConfig) + }.scheduleFork(Schedule.fixed(duration)) + } yield logger + } From 3eea40d67e86f051a9acbd34280afc573b6b8cac Mon Sep 17 00:00:00 2001 From: Peter Kotula Date: Sun, 4 Jun 2023 21:39:17 +0200 Subject: [PATCH 12/56] ReconfigurableLogger --- .../internal/ReconfigurableLogger.scala | 4 +- .../example/LoggerReconfigureApp.scala | 86 +++++++++++++++++++ 2 files changed, 88 insertions(+), 2 deletions(-) create mode 100644 examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala diff --git a/core/shared/src/main/scala/zio/logging/internal/ReconfigurableLogger.scala b/core/shared/src/main/scala/zio/logging/internal/ReconfigurableLogger.scala index 204d77306..48b075c0e 100644 --- a/core/shared/src/main/scala/zio/logging/internal/ReconfigurableLogger.scala +++ b/core/shared/src/main/scala/zio/logging/internal/ReconfigurableLogger.scala @@ -69,14 +69,14 @@ private[logging] object ReconfigurableLogger { def make[E, M, O, C: Equal]( loadConfig: => ZIO[Any, E, C], makeLogger: C => ZLogger[M, O], - duration: Duration = 10.seconds + interval: Duration = 10.seconds ): ZIO[Scope, E, ReconfigurableLogger[M, O, C]] = for { config <- loadConfig logger = ReconfigurableLogger[M, O, C](config, makeLogger) _ <- loadConfig.map { newConfig => logger.reconfigureIfChanged(newConfig) - }.scheduleFork(Schedule.fixed(duration)) + }.scheduleFork(Schedule.fixed(interval)) } yield logger } diff --git a/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala b/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala new file mode 100644 index 000000000..3ac55f520 --- /dev/null +++ b/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala @@ -0,0 +1,86 @@ +/* + * 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.internal.ReconfigurableLogger +import zio.logging.{ ConsoleLoggerConfig, LogAnnotation } +import zio.{ ExitCode, Runtime, Scope, ZIO, ZIOAppDefault, _ } + +import java.util.UUID + +object LoggerReconfigureApp extends ZIOAppDefault { + + val logFormat = + "%highlight{%timestamp{yyyy-MM-dd'T'HH:mm:ssZ} %fixed{7}{%level} [%fiberId] %name:%line %message %kv{trace_id} %kv{user_id} %cause}" + + def configuredLogger( + loadConfig: => ZIO[Any, Config.Error, ConsoleLoggerConfig] + ): ZLayer[Any, Config.Error, Unit] = + ZLayer.scoped { + for { + logger <- ReconfigurableLogger + .make[Config.Error, String, Any, ConsoleLoggerConfig]( + loadConfig, + config => + config.filter.filter(config.format.toLogger.map { line => + try java.lang.System.out.println(line) + catch { + case t: VirtualMachineError => throw t + case _: Throwable => () + } + }), + 500.millis + ) + _ <- ZIO.withLoggerScoped(logger) + } yield () + } + + override val bootstrap: ZLayer[ZIOAppArgs, Any, Any] = + Runtime.removeDefaultLoggers >>> configuredLogger( + for { + info <- Random.nextBoolean + cfg = Map( + "logger/format" -> logFormat, + "logger/filter/rootLevel" -> (if (info) LogLevel.Info.label else LogLevel.Debug.label) + ) + _ <- Console.printLine(cfg.mkString(", ")).orDie + config <- ConfigProvider.fromMap(cfg, "/").nested("logger").load(ConsoleLoggerConfig.config) + } yield config + ) + + def exec() = + for { + ok <- Random.nextBoolean + traceId <- ZIO.succeed(UUID.randomUUID()) + _ <- ZIO.logDebug("Start") @@ LogAnnotation.TraceId(traceId) + userIds <- ZIO.succeed(List.fill(2)(UUID.randomUUID().toString)) + _ <- ZIO.foreachPar(userIds) { userId => + { + ZIO.logDebug("Starting operation") *> + ZIO.logInfo("OK operation").when(ok) *> + ZIO.logError("Error operation").when(!ok) *> + ZIO.logDebug("Stopping operation") + } @@ LogAnnotation.UserId(userId) + } @@ LogAnnotation.TraceId(traceId) + _ <- ZIO.logDebug("Done") @@ LogAnnotation.TraceId(traceId) + } yield () + + override def run: ZIO[Scope, Any, ExitCode] = + for { + _ <- exec().repeat(Schedule.fixed(500.millis)) + } yield ExitCode.success + +} From 2680f94b9e16270c0671e9f07a1c069ecee61ba9 Mon Sep 17 00:00:00 2001 From: Peter Kotula Date: Tue, 6 Jun 2023 21:48:21 +0200 Subject: [PATCH 13/56] http api - wip --- .../zio/logging/api/http/ApiEndpoints.scala | 48 +++++++++++++++++++ .../zio/logging/api/http/ApiHandlers.scala | 43 +++++++++++++++++ .../scala/zio/logging/api/http/Domain.scala | 31 ++++++++++++ .../zio/logging/api/http/LoggerService.scala | 41 ++++++++++++++++ build.sbt | 13 +++++ .../scala/zio/logging/example/TestApp.scala | 36 ++++++++++++++ project/Versions.scala | 1 + 7 files changed, 213 insertions(+) create mode 100644 api-http/src/main/scala/zio/logging/api/http/ApiEndpoints.scala create mode 100644 api-http/src/main/scala/zio/logging/api/http/ApiHandlers.scala create mode 100644 api-http/src/main/scala/zio/logging/api/http/Domain.scala create mode 100644 api-http/src/main/scala/zio/logging/api/http/LoggerService.scala create mode 100644 examples/slf4j2-logback/src/main/scala/zio/logging/example/TestApp.scala diff --git a/api-http/src/main/scala/zio/logging/api/http/ApiEndpoints.scala b/api-http/src/main/scala/zio/logging/api/http/ApiEndpoints.scala new file mode 100644 index 000000000..5ffc1bd69 --- /dev/null +++ b/api-http/src/main/scala/zio/logging/api/http/ApiEndpoints.scala @@ -0,0 +1,48 @@ +/* + * 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.api.http + +import zio._ +import zio.http._ +import zio.http.codec.PathCodec.{ literal, string } +import zio.http.endpoint._ + +object ApiEndpoints { + + import Domain._ + + def getLoggerConfigurations(rootPath: String) = + Endpoint + .get(literal(rootPath) / literal("logger")) + .out[List[LoggerConfiguration]] + .outError[String](Status.InternalServerError) + + def getLoggerConfiguration(rootPath: String) = + Endpoint + .get(literal(rootPath) / literal("logger") / string("name")) + .out[LoggerConfiguration] + .outError[String](Status.NotFound) + .outError[String](Status.InternalServerError) + + def setLoggerConfiguration(rootPath: String) = + Endpoint + .put(literal(rootPath) / literal("logger") / string("name")) + .in[LogLevel] + .out[LoggerConfiguration] + .outError[String](Status.NotFound) + .outError[String](Status.InternalServerError) + +} diff --git a/api-http/src/main/scala/zio/logging/api/http/ApiHandlers.scala b/api-http/src/main/scala/zio/logging/api/http/ApiHandlers.scala new file mode 100644 index 000000000..c923a10e2 --- /dev/null +++ b/api-http/src/main/scala/zio/logging/api/http/ApiHandlers.scala @@ -0,0 +1,43 @@ +/* + * 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.api.http + +import zio.ZIO + +object ApiHandlers { + + def getLoggerConfigurations(rootPath: String) = + ApiEndpoints + .getLoggerConfigurations(rootPath) + .implement(_ => LoggerService.getLoggerConfigurations().mapError(_ => "Internal Error")) + + def getLoggerConfiguration(rootPath: String) = + ApiEndpoints + .getLoggerConfiguration(rootPath) + .implement { name => + LoggerService.getLoggerConfiguration(name).mapError(_ => "Internal Error").flatMap { + case Some(r) => ZIO.succeed(r) + case None => ZIO.fail("Not Found") + } + } + + def setLoggerConfigurations(rootPath: String) = + ApiEndpoints + .setLoggerConfiguration(rootPath) + .implement { case (name, logLevel) => + LoggerService.setLoggerConfiguration(name, logLevel).mapError(_ => "Internal Error") + } +} diff --git a/api-http/src/main/scala/zio/logging/api/http/Domain.scala b/api-http/src/main/scala/zio/logging/api/http/Domain.scala new file mode 100644 index 000000000..f1dd27df5 --- /dev/null +++ b/api-http/src/main/scala/zio/logging/api/http/Domain.scala @@ -0,0 +1,31 @@ +/* + * 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.api.http + +import zio.LogLevel +import zio.schema.{ DeriveSchema, Schema } + +object Domain { + + implicit val logLevelSchema: Schema[LogLevel] = DeriveSchema.gen[LogLevel] + + final case class LoggerConfiguration(name: String, logLevel: LogLevel) + + object LoggerConfiguration { + implicit val schema: Schema[LoggerConfiguration] = DeriveSchema.gen[LoggerConfiguration] + } + +} diff --git a/api-http/src/main/scala/zio/logging/api/http/LoggerService.scala b/api-http/src/main/scala/zio/logging/api/http/LoggerService.scala new file mode 100644 index 000000000..9b92192ad --- /dev/null +++ b/api-http/src/main/scala/zio/logging/api/http/LoggerService.scala @@ -0,0 +1,41 @@ +/* + * 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.api.http + +import zio.{ LogLevel, ZIO } + +trait LoggerService { + + def getLoggerConfigurations(): ZIO[Any, Throwable, List[Domain.LoggerConfiguration]] + + def getLoggerConfiguration(name: String): ZIO[Any, Throwable, Option[Domain.LoggerConfiguration]] + + def setLoggerConfiguration(name: String, logLevel: LogLevel): ZIO[Any, Throwable, Domain.LoggerConfiguration] +} + +object LoggerService { + def getLoggerConfigurations(): ZIO[LoggerService, Throwable, List[Domain.LoggerConfiguration]] = + ZIO.serviceWithZIO[LoggerService](_.getLoggerConfigurations()) + + def getLoggerConfiguration(name: String): ZIO[LoggerService, Throwable, Option[Domain.LoggerConfiguration]] = + ZIO.serviceWithZIO[LoggerService](_.getLoggerConfiguration(name)) + + def setLoggerConfiguration( + name: String, + logLevel: LogLevel + ): ZIO[LoggerService, Throwable, Domain.LoggerConfiguration] = + ZIO.serviceWithZIO[LoggerService](_.setLoggerConfiguration(name, logLevel)) +} diff --git a/build.sbt b/build.sbt index d27644558..f984077ab 100644 --- a/build.sbt +++ b/build.sbt @@ -77,6 +77,7 @@ lazy val root = project slf4jBridge, slf4j2Bridge, jpl, + apiHttp, benchmarks, examplesCore, examplesJpl, @@ -110,6 +111,18 @@ lazy val coreJS = core.js.settings( libraryDependencies += "io.github.cquiroz" %%% "scala-java-time" % scalaJavaTimeVersion % Test ) +lazy val apiHttp = project + .in(file("api-http")) + .dependsOn(coreJVM) + .settings(stdSettings("zio-logging-api-http", turnCompilerWarningIntoErrors = false)) + .settings(enableZIO()) + .settings(mimaSettings(failOnProblem = true)) + .settings( + libraryDependencies ++= Seq( + "dev.zio" %% "zio-http" % zioHttp + ) + ) + lazy val slf4j = project .in(file("slf4j")) .dependsOn(coreJVM) diff --git a/examples/slf4j2-logback/src/main/scala/zio/logging/example/TestApp.scala b/examples/slf4j2-logback/src/main/scala/zio/logging/example/TestApp.scala new file mode 100644 index 000000000..b0cc4db68 --- /dev/null +++ b/examples/slf4j2-logback/src/main/scala/zio/logging/example/TestApp.scala @@ -0,0 +1,36 @@ +package zio.logging.example + +import zio._ +import zio.Console.printLine +import zio.logging.backend.SLF4J +import zio.logging.consoleLogger +import zio.stream.ZStream + +object TestApp extends ZIOAppDefault { + val logFormat = + "%highlight{%timestamp{yyyy-MM-dd'T'HH:mm:ssZ} %fixed{7}{%level} [%fiberId] %name:%line %message %cause}" + + val configProvider: ConfigProvider = ConfigProvider.fromMap( + Map( + "logger/format" -> logFormat, + "logger/filter/rootLevel" -> "DEBUG" + ), + "/" + ) + + override val bootstrap: ZLayer[ZIOAppArgs, Any, Any] = + Runtime.removeDefaultLoggers >>> Runtime.setConfigProvider(configProvider) >>> consoleLogger() + + def bad(i: Int): Task[Unit] = + if (i == 5) { + ZIO.fail(new Exception("Don't like 5's")) + } else + ZIO.log(s"Got $i") + + override def run: ZIO[Environment with ZIOAppArgs with Scope, Any, Any] = + ZStream + .fromChunks(Chunk(1 to 10: _*)) + .tap(bad) + .runDrain + .fork *> ZIO.never +} diff --git a/project/Versions.scala b/project/Versions.scala index c2f1d9603..8d710e4e1 100644 --- a/project/Versions.scala +++ b/project/Versions.scala @@ -10,5 +10,6 @@ object Versions { val zioConfig = "4.0.0" val zioParser = "0.1.9" val zioPrelude = "1.0.0-RC19" + val zioHttp = "3.0.0-RC2" val log4jVersion = "2.19.0" } From 780cfb2763c84b95832167e161bbbf96a6141e41 Mon Sep 17 00:00:00 2001 From: Peter Kotula Date: Wed, 7 Jun 2023 22:10:59 +0200 Subject: [PATCH 14/56] http api - wip --- .../scala/zio/logging/api/http/ApiEndpoints.scala | 12 ++++++++---- .../scala/zio/logging/api/http/ApiHandlers.scala | 7 ++++++- .../scala/zio/logging/api/http/LoggerService.scala | 1 + .../src/main/scala/zio/logging/LogFormat.scala | 2 -- 4 files changed, 15 insertions(+), 7 deletions(-) diff --git a/api-http/src/main/scala/zio/logging/api/http/ApiEndpoints.scala b/api-http/src/main/scala/zio/logging/api/http/ApiEndpoints.scala index 5ffc1bd69..3cf53e72d 100644 --- a/api-http/src/main/scala/zio/logging/api/http/ApiEndpoints.scala +++ b/api-http/src/main/scala/zio/logging/api/http/ApiEndpoints.scala @@ -17,27 +17,29 @@ package zio.logging.api.http import zio._ import zio.http._ -import zio.http.codec.PathCodec.{ literal, string } +import zio.http.codec.Doc +import zio.http.codec.PathCodec.{literal, string} +import zio.http.endpoint.EndpointMiddleware.None import zio.http.endpoint._ object ApiEndpoints { import Domain._ - def getLoggerConfigurations(rootPath: String) = + def getLoggerConfigurations(rootPath: String): Endpoint[Unit, String, List[LoggerConfiguration], None] = Endpoint .get(literal(rootPath) / literal("logger")) .out[List[LoggerConfiguration]] .outError[String](Status.InternalServerError) - def getLoggerConfiguration(rootPath: String) = + def getLoggerConfiguration(rootPath: String): Endpoint[String, String, LoggerConfiguration, None] = Endpoint .get(literal(rootPath) / literal("logger") / string("name")) .out[LoggerConfiguration] .outError[String](Status.NotFound) .outError[String](Status.InternalServerError) - def setLoggerConfiguration(rootPath: String) = + def setLoggerConfiguration(rootPath: String): Endpoint[(String, LogLevel), String, LoggerConfiguration, None] = Endpoint .put(literal(rootPath) / literal("logger") / string("name")) .in[LogLevel] @@ -45,4 +47,6 @@ object ApiEndpoints { .outError[String](Status.NotFound) .outError[String](Status.InternalServerError) + def doc(rootPath: String): Doc = + getLoggerConfigurations(rootPath).doc + getLoggerConfiguration(rootPath).doc + setLoggerConfiguration(rootPath).doc } diff --git a/api-http/src/main/scala/zio/logging/api/http/ApiHandlers.scala b/api-http/src/main/scala/zio/logging/api/http/ApiHandlers.scala index c923a10e2..c8c401d22 100644 --- a/api-http/src/main/scala/zio/logging/api/http/ApiHandlers.scala +++ b/api-http/src/main/scala/zio/logging/api/http/ApiHandlers.scala @@ -16,6 +16,8 @@ package zio.logging.api.http import zio.ZIO +import zio.http.endpoint.EndpointMiddleware.None +import zio.http.endpoint.Routes object ApiHandlers { @@ -30,7 +32,7 @@ object ApiHandlers { .implement { name => LoggerService.getLoggerConfiguration(name).mapError(_ => "Internal Error").flatMap { case Some(r) => ZIO.succeed(r) - case None => ZIO.fail("Not Found") + case _ => ZIO.fail("Not Found") } } @@ -40,4 +42,7 @@ object ApiHandlers { .implement { case (name, logLevel) => LoggerService.setLoggerConfiguration(name, logLevel).mapError(_ => "Internal Error") } + + def routes(rootPath: String): Routes[LoggerService, String, None] = + getLoggerConfigurations(rootPath) ++ getLoggerConfiguration(rootPath) ++ setLoggerConfigurations(rootPath) } diff --git a/api-http/src/main/scala/zio/logging/api/http/LoggerService.scala b/api-http/src/main/scala/zio/logging/api/http/LoggerService.scala index 9b92192ad..cc32452c5 100644 --- a/api-http/src/main/scala/zio/logging/api/http/LoggerService.scala +++ b/api-http/src/main/scala/zio/logging/api/http/LoggerService.scala @@ -27,6 +27,7 @@ trait LoggerService { } object LoggerService { + def getLoggerConfigurations(): ZIO[LoggerService, Throwable, List[Domain.LoggerConfiguration]] = ZIO.serviceWithZIO[LoggerService](_.getLoggerConfigurations()) diff --git a/core/shared/src/main/scala/zio/logging/LogFormat.scala b/core/shared/src/main/scala/zio/logging/LogFormat.scala index 66ae987c3..90c32dc2b 100644 --- a/core/shared/src/main/scala/zio/logging/LogFormat.scala +++ b/core/shared/src/main/scala/zio/logging/LogFormat.scala @@ -362,10 +362,8 @@ object LogFormat { context.asMap.filterNot(kv => excludeKeys.contains(kv._1)).toList } .getOrElse(Nil) - builder.appendKeyValues(keyValues) () - }(builder) } From 7a3040c7bd41ccd333cd3543dc5965ee4c4b7682 Mon Sep 17 00:00:00 2001 From: Peter Kotula Date: Sat, 10 Jun 2023 13:18:33 +0200 Subject: [PATCH 15/56] http api - wip --- .../zio/logging/api/http/ApiEndpoints.scala | 38 +++++++++++-------- .../zio/logging/api/http/ApiHandlers.scala | 10 ++--- .../scala/zio/logging/api/http/Domain.scala | 22 ++++++++++- 3 files changed, 49 insertions(+), 21 deletions(-) diff --git a/api-http/src/main/scala/zio/logging/api/http/ApiEndpoints.scala b/api-http/src/main/scala/zio/logging/api/http/ApiEndpoints.scala index 3cf53e72d..3c0d08619 100644 --- a/api-http/src/main/scala/zio/logging/api/http/ApiEndpoints.scala +++ b/api-http/src/main/scala/zio/logging/api/http/ApiEndpoints.scala @@ -17,35 +17,43 @@ package zio.logging.api.http import zio._ import zio.http._ -import zio.http.codec.Doc -import zio.http.codec.PathCodec.{literal, string} +import zio.http.codec.{ Doc, HttpCodec } +import zio.http.codec.PathCodec.{ literal, string } import zio.http.endpoint.EndpointMiddleware.None import zio.http.endpoint._ object ApiEndpoints { + import Domain.logLevelSchema - import Domain._ - - def getLoggerConfigurations(rootPath: String): Endpoint[Unit, String, List[LoggerConfiguration], None] = + def getLoggerConfigurations(rootPath: String): Endpoint[Unit, Domain.Error, List[Domain.LoggerConfiguration], None] = Endpoint .get(literal(rootPath) / literal("logger")) - .out[List[LoggerConfiguration]] - .outError[String](Status.InternalServerError) + .out[List[Domain.LoggerConfiguration]] + .outErrors[Domain.Error]( + HttpCodec.error[Domain.Error.Internal](Status.InternalServerError), + HttpCodec.error[Domain.Error.NotFound](Status.NotFound) + ) - def getLoggerConfiguration(rootPath: String): Endpoint[String, String, LoggerConfiguration, None] = + def getLoggerConfiguration(rootPath: String): Endpoint[String, Domain.Error, Domain.LoggerConfiguration, None] = Endpoint .get(literal(rootPath) / literal("logger") / string("name")) - .out[LoggerConfiguration] - .outError[String](Status.NotFound) - .outError[String](Status.InternalServerError) + .out[Domain.LoggerConfiguration] + .outErrors[Domain.Error]( + HttpCodec.error[Domain.Error.Internal](Status.InternalServerError), + HttpCodec.error[Domain.Error.NotFound](Status.NotFound) + ) - def setLoggerConfiguration(rootPath: String): Endpoint[(String, LogLevel), String, LoggerConfiguration, None] = + def setLoggerConfiguration( + rootPath: String + ): Endpoint[(String, LogLevel), Domain.Error, Domain.LoggerConfiguration, None] = Endpoint .put(literal(rootPath) / literal("logger") / string("name")) .in[LogLevel] - .out[LoggerConfiguration] - .outError[String](Status.NotFound) - .outError[String](Status.InternalServerError) + .out[Domain.LoggerConfiguration] + .outErrors[Domain.Error]( + HttpCodec.error[Domain.Error.Internal](Status.InternalServerError), + HttpCodec.error[Domain.Error.NotFound](Status.NotFound) + ) def doc(rootPath: String): Doc = getLoggerConfigurations(rootPath).doc + getLoggerConfiguration(rootPath).doc + setLoggerConfiguration(rootPath).doc diff --git a/api-http/src/main/scala/zio/logging/api/http/ApiHandlers.scala b/api-http/src/main/scala/zio/logging/api/http/ApiHandlers.scala index c8c401d22..9b06a7289 100644 --- a/api-http/src/main/scala/zio/logging/api/http/ApiHandlers.scala +++ b/api-http/src/main/scala/zio/logging/api/http/ApiHandlers.scala @@ -24,15 +24,15 @@ object ApiHandlers { def getLoggerConfigurations(rootPath: String) = ApiEndpoints .getLoggerConfigurations(rootPath) - .implement(_ => LoggerService.getLoggerConfigurations().mapError(_ => "Internal Error")) + .implement(_ => LoggerService.getLoggerConfigurations().mapError(_ => Domain.Error.Internal())) def getLoggerConfiguration(rootPath: String) = ApiEndpoints .getLoggerConfiguration(rootPath) .implement { name => - LoggerService.getLoggerConfiguration(name).mapError(_ => "Internal Error").flatMap { + LoggerService.getLoggerConfiguration(name).mapError(_ => Domain.Error.Internal()).flatMap { case Some(r) => ZIO.succeed(r) - case _ => ZIO.fail("Not Found") + case _ => ZIO.fail(Domain.Error.NotFound()) } } @@ -40,9 +40,9 @@ object ApiHandlers { ApiEndpoints .setLoggerConfiguration(rootPath) .implement { case (name, logLevel) => - LoggerService.setLoggerConfiguration(name, logLevel).mapError(_ => "Internal Error") + LoggerService.setLoggerConfiguration(name, logLevel).mapError(_ => Domain.Error.Internal()) } - def routes(rootPath: String): Routes[LoggerService, String, None] = + def routes(rootPath: String): Routes[LoggerService, Domain.Error, None] = getLoggerConfigurations(rootPath) ++ getLoggerConfiguration(rootPath) ++ setLoggerConfigurations(rootPath) } diff --git a/api-http/src/main/scala/zio/logging/api/http/Domain.scala b/api-http/src/main/scala/zio/logging/api/http/Domain.scala index f1dd27df5..5a25d04d1 100644 --- a/api-http/src/main/scala/zio/logging/api/http/Domain.scala +++ b/api-http/src/main/scala/zio/logging/api/http/Domain.scala @@ -20,7 +20,27 @@ import zio.schema.{ DeriveSchema, Schema } object Domain { - implicit val logLevelSchema: Schema[LogLevel] = DeriveSchema.gen[LogLevel] + sealed trait Error { + def message: String + } + + object Error { + final case class NotFound(message: String = "Not Found") extends Error + + final case class Internal(message: String = "Internal") extends Error + + implicit val notFoundSchema: Schema[Error.NotFound] = DeriveSchema.gen[Error.NotFound] + implicit val internalSchema: Schema[Error.Internal] = DeriveSchema.gen[Error.Internal] + implicit val schema: Schema[Error] = DeriveSchema.gen[Error] + } + + implicit val logLevelSchema: Schema[LogLevel] = { + val levelToLabel: Map[LogLevel, String] = LogLevel.levels.map(level => (level, level.label)).toMap + val labelToLevel: Map[String, LogLevel] = LogLevel.levels.map(level => (level.label, level)).toMap + + Schema[String] + .transformOrFail[LogLevel](v => labelToLevel.get(v).toRight("Failed"), v => levelToLabel.get(v).toRight("Failed")) + } final case class LoggerConfiguration(name: String, logLevel: LogLevel) From 6407859ae9ffab62589edcedf7994ab0a5951da5 Mon Sep 17 00:00:00 2001 From: Peter Kotula Date: Sun, 11 Jun 2023 21:33:29 +0200 Subject: [PATCH 16/56] LogFilter, LogFormat, LoggerNameExtractor, LogGroup - sealed and custom types, configuration - equal --- .../src/main/scala/zio/logging/LogGroup.scala | 32 ++--------------- .../scala/zio/logging/example/TestApp.scala | 36 ------------------- 2 files changed, 2 insertions(+), 66 deletions(-) delete mode 100644 examples/slf4j2-logback/src/main/scala/zio/logging/example/TestApp.scala diff --git a/core/shared/src/main/scala/zio/logging/LogGroup.scala b/core/shared/src/main/scala/zio/logging/LogGroup.scala index 26755787a..eff4e472f 100644 --- a/core/shared/src/main/scala/zio/logging/LogGroup.scala +++ b/core/shared/src/main/scala/zio/logging/LogGroup.scala @@ -122,34 +122,6 @@ object LogGroup { constant } - private[logging] final case object CauseGroup extends LogGroup[Any, Cause[Any]] { - override def apply( - trace: Trace, - fiberId: FiberId, - logLevel: LogLevel, - message: () => Any, - cause: Cause[Any], - context: FiberRefs, - spans: List[LogSpan], - annotations: Map[String, String] - ): Cause[Any] = - cause - } - - private[logging] final case object LogLevelGroup extends LogGroup[Any, LogLevel] { - override def apply( - trace: Trace, - fiberId: FiberId, - logLevel: LogLevel, - message: () => Any, - cause: Cause[Any], - context: FiberRefs, - spans: List[LogSpan], - annotations: Map[String, String] - ): LogLevel = - logLevel - } - private[logging] final case class ZipGroup[Message, Out1, Out2, Out]( first: LogGroup[Message, Out1], second: LogGroup[Message, Out2] @@ -240,7 +212,7 @@ object LogGroup { /** * Log group by cause */ - val cause: LogGroup[Any, Cause[Any]] = CauseGroup + val cause: LogGroup[Any, Cause[Any]] = apply((_, _, _, _, cause, _, _, _) => cause) /** * Log group with given constant value @@ -255,7 +227,7 @@ object LogGroup { /** * Log group by level */ - val logLevel: LogGroup[Any, LogLevel] = LogLevelGroup + val logLevel: LogGroup[Any, LogLevel] = apply((_, _, logLevel, _, _, _, _, _) => logLevel) /** * Log group by logger name diff --git a/examples/slf4j2-logback/src/main/scala/zio/logging/example/TestApp.scala b/examples/slf4j2-logback/src/main/scala/zio/logging/example/TestApp.scala deleted file mode 100644 index b0cc4db68..000000000 --- a/examples/slf4j2-logback/src/main/scala/zio/logging/example/TestApp.scala +++ /dev/null @@ -1,36 +0,0 @@ -package zio.logging.example - -import zio._ -import zio.Console.printLine -import zio.logging.backend.SLF4J -import zio.logging.consoleLogger -import zio.stream.ZStream - -object TestApp extends ZIOAppDefault { - val logFormat = - "%highlight{%timestamp{yyyy-MM-dd'T'HH:mm:ssZ} %fixed{7}{%level} [%fiberId] %name:%line %message %cause}" - - val configProvider: ConfigProvider = ConfigProvider.fromMap( - Map( - "logger/format" -> logFormat, - "logger/filter/rootLevel" -> "DEBUG" - ), - "/" - ) - - override val bootstrap: ZLayer[ZIOAppArgs, Any, Any] = - Runtime.removeDefaultLoggers >>> Runtime.setConfigProvider(configProvider) >>> consoleLogger() - - def bad(i: Int): Task[Unit] = - if (i == 5) { - ZIO.fail(new Exception("Don't like 5's")) - } else - ZIO.log(s"Got $i") - - override def run: ZIO[Environment with ZIOAppArgs with Scope, Any, Any] = - ZStream - .fromChunks(Chunk(1 to 10: _*)) - .tap(bad) - .runDrain - .fork *> ZIO.never -} From 2c1303c4f4aed27ed46aa476b8e53a18aacfc54b Mon Sep 17 00:00:00 2001 From: Peter Kotula Date: Sun, 11 Jun 2023 22:51:41 +0200 Subject: [PATCH 17/56] http api - wip --- .../logging/api/http/ApiHandlersSpec.scala | 74 +++++++++++++++++++ build.sbt | 2 +- .../example/LoggerReconfigureApp.scala | 25 ++++++- 3 files changed, 98 insertions(+), 3 deletions(-) create mode 100644 api-http/src/test/scala/zio/logging/api/http/ApiHandlersSpec.scala diff --git a/api-http/src/test/scala/zio/logging/api/http/ApiHandlersSpec.scala b/api-http/src/test/scala/zio/logging/api/http/ApiHandlersSpec.scala new file mode 100644 index 000000000..6642e8320 --- /dev/null +++ b/api-http/src/test/scala/zio/logging/api/http/ApiHandlersSpec.scala @@ -0,0 +1,74 @@ +package zio.logging.api.http + +import zio.{ ZIO, ZLayer } +import zio.http._ +import zio.http.codec._ +import zio.LogLevel +import zio.test._ + +object ApiHandlersSpec extends ZIOSpecDefault { + + val loggerService = ZLayer.succeed { + new LoggerService { + override def getLoggerConfigurations(): ZIO[Any, Throwable, List[Domain.LoggerConfiguration]] = + ZIO.succeed(Domain.LoggerConfiguration("root", LogLevel.Info) :: Nil) + + override def getLoggerConfiguration(name: String): ZIO[Any, Throwable, Option[Domain.LoggerConfiguration]] = + ZIO.succeed(Some(Domain.LoggerConfiguration(name, LogLevel.Info))) + + override def setLoggerConfiguration( + name: String, + logLevel: LogLevel + ): ZIO[Any, Throwable, Domain.LoggerConfiguration] = + ZIO.succeed(Domain.LoggerConfiguration(name, logLevel)) + } + } + + def spec = suite("ApiHandlersSpec")( + test("get all") { + + val routes = ApiHandlers.routes("example") + + val request = Request.get(URL.decode("/example/logger").toOption.get) + + for { + response <- routes.toApp.runZIO(request) + content <- HttpCodec.content[List[Domain.LoggerConfiguration]].decodeResponse(response) + } yield assertTrue(response.status.isSuccess) && assertTrue( + content == List(Domain.LoggerConfiguration("root", LogLevel.Info)) + ) + }.provideLayer(loggerService), + test("get") { + val routes = ApiHandlers.routes("example") + val request = Request.get(URL.decode("/example/logger/example.Service").toOption.get) + + for { + response <- routes.toApp.runZIO(request) + content <- HttpCodec.content[Domain.LoggerConfiguration].decodeResponse(response) + } yield assertTrue(response.status.isSuccess) && assertTrue( + content == Domain.LoggerConfiguration("example.Service", LogLevel.Info) + ) + }.provideLayer(loggerService), + test("set") { + + import Domain.logLevelSchema + + val routes = ApiHandlers.routes("example") + + for { + request <- ZIO.attempt( + Request + .put( + HttpCodec.content[LogLevel].encodeRequest(LogLevel.Warning).body, + URL.decode("/example/logger/example.Service").toOption.get + ) + ) + response <- routes.toApp.runZIO(request) + content <- HttpCodec.content[Domain.LoggerConfiguration].decodeResponse(response) + } yield assertTrue(response.status.isSuccess) && assertTrue( + content == Domain.LoggerConfiguration("example.Service", LogLevel.Warning) + ) + }.provideLayer(loggerService) + ) + +} diff --git a/build.sbt b/build.sbt index f984077ab..d16d06920 100644 --- a/build.sbt +++ b/build.sbt @@ -206,7 +206,7 @@ lazy val benchmarks = project lazy val examplesCore = project .in(file("examples/core")) - .dependsOn(coreJVM) + .dependsOn(coreJVM, apiHttp) .settings(stdSettings("zio-logging-examples-core", turnCompilerWarningIntoErrors = false)) .settings(enableZIO()) .settings( diff --git a/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala b/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala index 3ac55f520..de941a258 100644 --- a/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala +++ b/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala @@ -18,6 +18,8 @@ package zio.logging.example import zio.logging.internal.ReconfigurableLogger import zio.logging.{ ConsoleLoggerConfig, LogAnnotation } import zio.{ ExitCode, Runtime, Scope, ZIO, ZIOAppDefault, _ } +import zio.http.Server +import zio.logging.api.http.{ ApiHandlers, Domain, LoggerService } import java.util.UUID @@ -78,9 +80,28 @@ object LoggerReconfigureApp extends ZIOAppDefault { _ <- ZIO.logDebug("Done") @@ LogAnnotation.TraceId(traceId) } yield () + val loggerService = ZLayer.succeed { + new LoggerService { + override def getLoggerConfigurations(): ZIO[Any, Throwable, List[Domain.LoggerConfiguration]] = + ZIO.succeed(Domain.LoggerConfiguration("root", LogLevel.Info) :: Nil) + + override def getLoggerConfiguration(name: String): ZIO[Any, Throwable, Option[Domain.LoggerConfiguration]] = + ZIO.succeed(Some(Domain.LoggerConfiguration(name, LogLevel.Info))) + + override def setLoggerConfiguration( + name: String, + logLevel: LogLevel + ): ZIO[Any, Throwable, Domain.LoggerConfiguration] = + ZIO.succeed(Domain.LoggerConfiguration(name, logLevel)) + } + } + + val httpApp = ApiHandlers.routes("example").toApp[LoggerService] + override def run: ZIO[Scope, Any, ExitCode] = - for { + (for { + _ <- Server.serve(httpApp).fork _ <- exec().repeat(Schedule.fixed(500.millis)) - } yield ExitCode.success + } yield ExitCode.success).provide(loggerService ++ Server.default) } From 0e25b5619b20910e0cc615047dd2123848b3b832 Mon Sep 17 00:00:00 2001 From: Peter Kotula Date: Mon, 12 Jun 2023 21:46:21 +0200 Subject: [PATCH 18/56] http api - wip --- .../zio/logging/api/http/ApiEndpoints.scala | 27 +++++--- .../zio/logging/api/http/ApiHandlers.scala | 8 +-- .../logging/api/http/ApiEndpointsSpec.scala | 66 +++++++++++++++++++ .../logging/api/http/ApiHandlersSpec.scala | 6 +- .../example/LoggerReconfigureApp.scala | 2 +- 5 files changed, 93 insertions(+), 16 deletions(-) create mode 100644 api-http/src/test/scala/zio/logging/api/http/ApiEndpointsSpec.scala diff --git a/api-http/src/main/scala/zio/logging/api/http/ApiEndpoints.scala b/api-http/src/main/scala/zio/logging/api/http/ApiEndpoints.scala index 3c0d08619..9b73a4dd1 100644 --- a/api-http/src/main/scala/zio/logging/api/http/ApiEndpoints.scala +++ b/api-http/src/main/scala/zio/logging/api/http/ApiEndpoints.scala @@ -17,7 +17,7 @@ package zio.logging.api.http import zio._ import zio.http._ -import zio.http.codec.{ Doc, HttpCodec } +import zio.http.codec.{ Doc, HttpCodec, PathCodec } import zio.http.codec.PathCodec.{ literal, string } import zio.http.endpoint.EndpointMiddleware.None import zio.http.endpoint._ @@ -25,18 +25,29 @@ import zio.http.endpoint._ object ApiEndpoints { import Domain.logLevelSchema - def getLoggerConfigurations(rootPath: String): Endpoint[Unit, Domain.Error, List[Domain.LoggerConfiguration], None] = + def rootPathCodec(rootPath: Iterable[String]): PathCodec[Unit] = + if (rootPath.isEmpty) { + HttpCodec.empty + } else { + rootPath.map(literal).reduce(_ / _) + } + + def getLoggerConfigurations( + rootPath: Iterable[String] = Iterable.empty + ): Endpoint[Unit, Domain.Error, List[Domain.LoggerConfiguration], None] = Endpoint - .get(literal(rootPath) / literal("logger")) + .get(rootPathCodec(rootPath) / literal("logger")) .out[List[Domain.LoggerConfiguration]] .outErrors[Domain.Error]( HttpCodec.error[Domain.Error.Internal](Status.InternalServerError), HttpCodec.error[Domain.Error.NotFound](Status.NotFound) ) - def getLoggerConfiguration(rootPath: String): Endpoint[String, Domain.Error, Domain.LoggerConfiguration, None] = + def getLoggerConfiguration( + rootPath: Iterable[String] = Iterable.empty + ): Endpoint[String, Domain.Error, Domain.LoggerConfiguration, None] = Endpoint - .get(literal(rootPath) / literal("logger") / string("name")) + .get(rootPathCodec(rootPath) / literal("logger") / string("name")) .out[Domain.LoggerConfiguration] .outErrors[Domain.Error]( HttpCodec.error[Domain.Error.Internal](Status.InternalServerError), @@ -44,10 +55,10 @@ object ApiEndpoints { ) def setLoggerConfiguration( - rootPath: String + rootPath: Iterable[String] = Iterable.empty ): Endpoint[(String, LogLevel), Domain.Error, Domain.LoggerConfiguration, None] = Endpoint - .put(literal(rootPath) / literal("logger") / string("name")) + .put(rootPathCodec(rootPath) / literal("logger") / string("name")) .in[LogLevel] .out[Domain.LoggerConfiguration] .outErrors[Domain.Error]( @@ -55,6 +66,6 @@ object ApiEndpoints { HttpCodec.error[Domain.Error.NotFound](Status.NotFound) ) - def doc(rootPath: String): Doc = + def doc(rootPath: Iterable[String] = Iterable.empty): Doc = getLoggerConfigurations(rootPath).doc + getLoggerConfiguration(rootPath).doc + setLoggerConfiguration(rootPath).doc } diff --git a/api-http/src/main/scala/zio/logging/api/http/ApiHandlers.scala b/api-http/src/main/scala/zio/logging/api/http/ApiHandlers.scala index 9b06a7289..a218c4b68 100644 --- a/api-http/src/main/scala/zio/logging/api/http/ApiHandlers.scala +++ b/api-http/src/main/scala/zio/logging/api/http/ApiHandlers.scala @@ -21,12 +21,12 @@ import zio.http.endpoint.Routes object ApiHandlers { - def getLoggerConfigurations(rootPath: String) = + def getLoggerConfigurations(rootPath: Iterable[String] = Iterable.empty) = ApiEndpoints .getLoggerConfigurations(rootPath) .implement(_ => LoggerService.getLoggerConfigurations().mapError(_ => Domain.Error.Internal())) - def getLoggerConfiguration(rootPath: String) = + def getLoggerConfiguration(rootPath: Iterable[String] = Iterable.empty) = ApiEndpoints .getLoggerConfiguration(rootPath) .implement { name => @@ -36,13 +36,13 @@ object ApiHandlers { } } - def setLoggerConfigurations(rootPath: String) = + def setLoggerConfigurations(rootPath: Iterable[String] = Iterable.empty) = ApiEndpoints .setLoggerConfiguration(rootPath) .implement { case (name, logLevel) => LoggerService.setLoggerConfiguration(name, logLevel).mapError(_ => Domain.Error.Internal()) } - def routes(rootPath: String): Routes[LoggerService, Domain.Error, None] = + def routes(rootPath: Iterable[String] = Iterable.empty): Routes[LoggerService, Domain.Error, None] = getLoggerConfigurations(rootPath) ++ getLoggerConfiguration(rootPath) ++ setLoggerConfigurations(rootPath) } diff --git a/api-http/src/test/scala/zio/logging/api/http/ApiEndpointsSpec.scala b/api-http/src/test/scala/zio/logging/api/http/ApiEndpointsSpec.scala new file mode 100644 index 000000000..c607ff6b0 --- /dev/null +++ b/api-http/src/test/scala/zio/logging/api/http/ApiEndpointsSpec.scala @@ -0,0 +1,66 @@ +package zio.logging.api.http + +import zio.LogLevel +import zio.http.codec._ +import zio.http.codec.PathCodec.literal +import zio.test._ + +object ApiEndpointsSpec extends ZIOSpecDefault { + + def spec = suite("ApiEndpointsSpec")( + test("rootPathCodec") { + def testRootPathCodec(rootPath: Iterable[String], expected: PathCodec[Unit]) = + assertTrue(ApiEndpoints.rootPathCodec(rootPath).encodeRequest(()).url == expected.encodeRequest(()).url) + + testRootPathCodec(Nil, HttpCodec.empty) && testRootPathCodec( + "example" :: Nil, + literal("example") + ) && testRootPathCodec("v1" :: "example" :: Nil, literal("v1") / literal("example")) + }, + test("getLoggerConfigurations") { + + def testPath(rootPath: Iterable[String], expected: PathCodec[Unit]) = + assertTrue( + ApiEndpoints.getLoggerConfigurations(rootPath).input.encodeRequest(()).url == expected.encodeRequest(()).url + ) + + testPath(Nil, literal("logger")) && testPath( + "example" :: Nil, + literal("example") / literal("logger") + ) + }, + test("getLoggerConfiguration") { + + def testPath(rootPath: Iterable[String], expected: PathCodec[Unit]) = + assertTrue( + ApiEndpoints.getLoggerConfiguration(rootPath).input.encodeRequest("my-logger").url == expected + .encodeRequest(()) + .url + ) + + testPath(Nil, literal("logger") / literal("my-logger")) && testPath( + "example" :: Nil, + literal("example") / literal("logger") / literal("my-logger") + ) + }, + test("setLoggerConfigurations") { + + def testPath(rootPath: Iterable[String], expected: PathCodec[Unit]) = + assertTrue( + ApiEndpoints + .setLoggerConfiguration(rootPath) + .input + .encodeRequest(("my-logger", LogLevel.Info)) + .url == expected + .encodeRequest(()) + .url + ) + + testPath(Nil, literal("logger") / literal("my-logger")) && testPath( + "example" :: Nil, + literal("example") / literal("logger") / literal("my-logger") + ) + } + ) + +} diff --git a/api-http/src/test/scala/zio/logging/api/http/ApiHandlersSpec.scala b/api-http/src/test/scala/zio/logging/api/http/ApiHandlersSpec.scala index 6642e8320..aaeb1a734 100644 --- a/api-http/src/test/scala/zio/logging/api/http/ApiHandlersSpec.scala +++ b/api-http/src/test/scala/zio/logging/api/http/ApiHandlersSpec.scala @@ -27,7 +27,7 @@ object ApiHandlersSpec extends ZIOSpecDefault { def spec = suite("ApiHandlersSpec")( test("get all") { - val routes = ApiHandlers.routes("example") + val routes = ApiHandlers.routes("example" :: Nil) val request = Request.get(URL.decode("/example/logger").toOption.get) @@ -39,7 +39,7 @@ object ApiHandlersSpec extends ZIOSpecDefault { ) }.provideLayer(loggerService), test("get") { - val routes = ApiHandlers.routes("example") + val routes = ApiHandlers.routes("example" :: Nil) val request = Request.get(URL.decode("/example/logger/example.Service").toOption.get) for { @@ -53,7 +53,7 @@ object ApiHandlersSpec extends ZIOSpecDefault { import Domain.logLevelSchema - val routes = ApiHandlers.routes("example") + val routes = ApiHandlers.routes("example" :: Nil) for { request <- ZIO.attempt( diff --git a/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala b/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala index de941a258..0f17c5bf5 100644 --- a/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala +++ b/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala @@ -96,7 +96,7 @@ object LoggerReconfigureApp extends ZIOAppDefault { } } - val httpApp = ApiHandlers.routes("example").toApp[LoggerService] + val httpApp = ApiHandlers.routes("example" :: Nil).toApp[LoggerService] override def run: ZIO[Scope, Any, ExitCode] = (for { From 3e5089ae17f465be0a4d2c0f8f396ddaa38f9c41 Mon Sep 17 00:00:00 2001 From: Peter Kotula Date: Tue, 13 Jun 2023 19:09:15 +0200 Subject: [PATCH 19/56] http api - wip --- .../http/{Domain.scala => ApiDomain.scala} | 13 +++-- .../zio/logging/api/http/ApiEndpoints.scala | 50 +++++++++---------- .../zio/logging/api/http/ApiHandlers.scala | 34 ++++++++----- .../zio/logging/api/http/LoggerService.scala | 22 ++++---- .../logging/api/http/ApiEndpointsSpec.scala | 20 ++++---- .../logging/api/http/ApiHandlersSpec.scala | 50 +++++++++---------- .../example/LoggerReconfigureApp.scala | 18 ++++--- 7 files changed, 109 insertions(+), 98 deletions(-) rename api-http/src/main/scala/zio/logging/api/http/{Domain.scala => ApiDomain.scala} (80%) diff --git a/api-http/src/main/scala/zio/logging/api/http/Domain.scala b/api-http/src/main/scala/zio/logging/api/http/ApiDomain.scala similarity index 80% rename from api-http/src/main/scala/zio/logging/api/http/Domain.scala rename to api-http/src/main/scala/zio/logging/api/http/ApiDomain.scala index 5a25d04d1..1b441457b 100644 --- a/api-http/src/main/scala/zio/logging/api/http/Domain.scala +++ b/api-http/src/main/scala/zio/logging/api/http/ApiDomain.scala @@ -18,7 +18,7 @@ package zio.logging.api.http import zio.LogLevel import zio.schema.{ DeriveSchema, Schema } -object Domain { +object ApiDomain { sealed trait Error { def message: String @@ -36,16 +36,19 @@ object Domain { implicit val logLevelSchema: Schema[LogLevel] = { val levelToLabel: Map[LogLevel, String] = LogLevel.levels.map(level => (level, level.label)).toMap - val labelToLevel: Map[String, LogLevel] = LogLevel.levels.map(level => (level.label, level)).toMap + val labelToLevel: Map[String, LogLevel] = levelToLabel.map(_.swap) Schema[String] .transformOrFail[LogLevel](v => labelToLevel.get(v).toRight("Failed"), v => levelToLabel.get(v).toRight("Failed")) } - final case class LoggerConfiguration(name: String, logLevel: LogLevel) + final case class LoggerConfig(name: String, logLevel: LogLevel) - object LoggerConfiguration { - implicit val schema: Schema[LoggerConfiguration] = DeriveSchema.gen[LoggerConfiguration] + object LoggerConfig { + implicit val schema: Schema[LoggerConfig] = DeriveSchema.gen[LoggerConfig] + + def from(value: LoggerService.LoggerConfig): LoggerConfig = + LoggerConfig(value.name, value.logLevel) } } diff --git a/api-http/src/main/scala/zio/logging/api/http/ApiEndpoints.scala b/api-http/src/main/scala/zio/logging/api/http/ApiEndpoints.scala index 9b73a4dd1..da36373a8 100644 --- a/api-http/src/main/scala/zio/logging/api/http/ApiEndpoints.scala +++ b/api-http/src/main/scala/zio/logging/api/http/ApiEndpoints.scala @@ -23,49 +23,49 @@ import zio.http.endpoint.EndpointMiddleware.None import zio.http.endpoint._ object ApiEndpoints { - import Domain.logLevelSchema + import ApiDomain.logLevelSchema - def rootPathCodec(rootPath: Iterable[String]): PathCodec[Unit] = + def rootPathCodec(rootPath: Seq[String]): PathCodec[Unit] = if (rootPath.isEmpty) { HttpCodec.empty } else { rootPath.map(literal).reduce(_ / _) } - def getLoggerConfigurations( - rootPath: Iterable[String] = Iterable.empty - ): Endpoint[Unit, Domain.Error, List[Domain.LoggerConfiguration], None] = + def getLoggerConfigs( + rootPath: Seq[String] = Seq.empty + ): Endpoint[Unit, ApiDomain.Error, List[ApiDomain.LoggerConfig], None] = Endpoint .get(rootPathCodec(rootPath) / literal("logger")) - .out[List[Domain.LoggerConfiguration]] - .outErrors[Domain.Error]( - HttpCodec.error[Domain.Error.Internal](Status.InternalServerError), - HttpCodec.error[Domain.Error.NotFound](Status.NotFound) + .out[List[ApiDomain.LoggerConfig]] + .outErrors[ApiDomain.Error]( + HttpCodec.error[ApiDomain.Error.Internal](Status.InternalServerError), + HttpCodec.error[ApiDomain.Error.NotFound](Status.NotFound) ) - def getLoggerConfiguration( - rootPath: Iterable[String] = Iterable.empty - ): Endpoint[String, Domain.Error, Domain.LoggerConfiguration, None] = + def getLoggerConfig( + rootPath: Seq[String] = Seq.empty + ): Endpoint[String, ApiDomain.Error, ApiDomain.LoggerConfig, None] = Endpoint .get(rootPathCodec(rootPath) / literal("logger") / string("name")) - .out[Domain.LoggerConfiguration] - .outErrors[Domain.Error]( - HttpCodec.error[Domain.Error.Internal](Status.InternalServerError), - HttpCodec.error[Domain.Error.NotFound](Status.NotFound) + .out[ApiDomain.LoggerConfig] + .outErrors[ApiDomain.Error]( + HttpCodec.error[ApiDomain.Error.Internal](Status.InternalServerError), + HttpCodec.error[ApiDomain.Error.NotFound](Status.NotFound) ) - def setLoggerConfiguration( - rootPath: Iterable[String] = Iterable.empty - ): Endpoint[(String, LogLevel), Domain.Error, Domain.LoggerConfiguration, None] = + def setLoggerConfig( + rootPath: Seq[String] = Seq.empty + ): Endpoint[(String, LogLevel), ApiDomain.Error, ApiDomain.LoggerConfig, None] = Endpoint .put(rootPathCodec(rootPath) / literal("logger") / string("name")) .in[LogLevel] - .out[Domain.LoggerConfiguration] - .outErrors[Domain.Error]( - HttpCodec.error[Domain.Error.Internal](Status.InternalServerError), - HttpCodec.error[Domain.Error.NotFound](Status.NotFound) + .out[ApiDomain.LoggerConfig] + .outErrors[ApiDomain.Error]( + HttpCodec.error[ApiDomain.Error.Internal](Status.InternalServerError), + HttpCodec.error[ApiDomain.Error.NotFound](Status.NotFound) ) - def doc(rootPath: Iterable[String] = Iterable.empty): Doc = - getLoggerConfigurations(rootPath).doc + getLoggerConfiguration(rootPath).doc + setLoggerConfiguration(rootPath).doc + def doc(rootPath: Seq[String] = Seq.empty): Doc = + getLoggerConfigs(rootPath).doc + getLoggerConfig(rootPath).doc + setLoggerConfig(rootPath).doc } diff --git a/api-http/src/main/scala/zio/logging/api/http/ApiHandlers.scala b/api-http/src/main/scala/zio/logging/api/http/ApiHandlers.scala index a218c4b68..86b4721ad 100644 --- a/api-http/src/main/scala/zio/logging/api/http/ApiHandlers.scala +++ b/api-http/src/main/scala/zio/logging/api/http/ApiHandlers.scala @@ -21,28 +21,36 @@ import zio.http.endpoint.Routes object ApiHandlers { - def getLoggerConfigurations(rootPath: Iterable[String] = Iterable.empty) = + def getLoggerConfigs(rootPath: Seq[String] = Seq.empty) = ApiEndpoints - .getLoggerConfigurations(rootPath) - .implement(_ => LoggerService.getLoggerConfigurations().mapError(_ => Domain.Error.Internal())) + .getLoggerConfigs(rootPath) + .implement(_ => + LoggerService + .getLoggerConfigs() + .map(_.map(ApiDomain.LoggerConfig.from)) + .mapError(_ => ApiDomain.Error.Internal()) + ) - def getLoggerConfiguration(rootPath: Iterable[String] = Iterable.empty) = + def getLoggerConfig(rootPath: Seq[String] = Seq.empty) = ApiEndpoints - .getLoggerConfiguration(rootPath) + .getLoggerConfig(rootPath) .implement { name => - LoggerService.getLoggerConfiguration(name).mapError(_ => Domain.Error.Internal()).flatMap { - case Some(r) => ZIO.succeed(r) - case _ => ZIO.fail(Domain.Error.NotFound()) + LoggerService.getLoggerConfig(name).mapError(_ => ApiDomain.Error.Internal()).flatMap { + case Some(r) => ZIO.succeed(ApiDomain.LoggerConfig.from(r)) + case _ => ZIO.fail(ApiDomain.Error.NotFound()) } } - def setLoggerConfigurations(rootPath: Iterable[String] = Iterable.empty) = + def setLoggerConfigs(rootPath: Seq[String] = Seq.empty) = ApiEndpoints - .setLoggerConfiguration(rootPath) + .setLoggerConfig(rootPath) .implement { case (name, logLevel) => - LoggerService.setLoggerConfiguration(name, logLevel).mapError(_ => Domain.Error.Internal()) + LoggerService + .setLoggerConfig(name, logLevel) + .map(ApiDomain.LoggerConfig.from) + .mapError(_ => ApiDomain.Error.Internal()) } - def routes(rootPath: Iterable[String] = Iterable.empty): Routes[LoggerService, Domain.Error, None] = - getLoggerConfigurations(rootPath) ++ getLoggerConfiguration(rootPath) ++ setLoggerConfigurations(rootPath) + def routes(rootPath: Seq[String] = Seq.empty): Routes[LoggerService, ApiDomain.Error, None] = + getLoggerConfigs(rootPath) ++ getLoggerConfig(rootPath) ++ setLoggerConfigs(rootPath) } diff --git a/api-http/src/main/scala/zio/logging/api/http/LoggerService.scala b/api-http/src/main/scala/zio/logging/api/http/LoggerService.scala index cc32452c5..bbc5a0612 100644 --- a/api-http/src/main/scala/zio/logging/api/http/LoggerService.scala +++ b/api-http/src/main/scala/zio/logging/api/http/LoggerService.scala @@ -19,24 +19,26 @@ import zio.{ LogLevel, ZIO } trait LoggerService { - def getLoggerConfigurations(): ZIO[Any, Throwable, List[Domain.LoggerConfiguration]] + def getLoggerConfigs(): ZIO[Any, Throwable, List[LoggerService.LoggerConfig]] - def getLoggerConfiguration(name: String): ZIO[Any, Throwable, Option[Domain.LoggerConfiguration]] + def getLoggerConfig(name: String): ZIO[Any, Throwable, Option[LoggerService.LoggerConfig]] - def setLoggerConfiguration(name: String, logLevel: LogLevel): ZIO[Any, Throwable, Domain.LoggerConfiguration] + def setLoggerConfig(name: String, logLevel: LogLevel): ZIO[Any, Throwable, LoggerService.LoggerConfig] } object LoggerService { - def getLoggerConfigurations(): ZIO[LoggerService, Throwable, List[Domain.LoggerConfiguration]] = - ZIO.serviceWithZIO[LoggerService](_.getLoggerConfigurations()) + final case class LoggerConfig(name: String, logLevel: LogLevel) - def getLoggerConfiguration(name: String): ZIO[LoggerService, Throwable, Option[Domain.LoggerConfiguration]] = - ZIO.serviceWithZIO[LoggerService](_.getLoggerConfiguration(name)) + def getLoggerConfigs(): ZIO[LoggerService, Throwable, List[LoggerService.LoggerConfig]] = + ZIO.serviceWithZIO[LoggerService](_.getLoggerConfigs()) - def setLoggerConfiguration( + def getLoggerConfig(name: String): ZIO[LoggerService, Throwable, Option[LoggerService.LoggerConfig]] = + ZIO.serviceWithZIO[LoggerService](_.getLoggerConfig(name)) + + def setLoggerConfig( name: String, logLevel: LogLevel - ): ZIO[LoggerService, Throwable, Domain.LoggerConfiguration] = - ZIO.serviceWithZIO[LoggerService](_.setLoggerConfiguration(name, logLevel)) + ): ZIO[LoggerService, Throwable, LoggerService.LoggerConfig] = + ZIO.serviceWithZIO[LoggerService](_.setLoggerConfig(name, logLevel)) } diff --git a/api-http/src/test/scala/zio/logging/api/http/ApiEndpointsSpec.scala b/api-http/src/test/scala/zio/logging/api/http/ApiEndpointsSpec.scala index c607ff6b0..7291bfb6d 100644 --- a/api-http/src/test/scala/zio/logging/api/http/ApiEndpointsSpec.scala +++ b/api-http/src/test/scala/zio/logging/api/http/ApiEndpointsSpec.scala @@ -9,7 +9,7 @@ object ApiEndpointsSpec extends ZIOSpecDefault { def spec = suite("ApiEndpointsSpec")( test("rootPathCodec") { - def testRootPathCodec(rootPath: Iterable[String], expected: PathCodec[Unit]) = + def testRootPathCodec(rootPath: Seq[String], expected: PathCodec[Unit]) = assertTrue(ApiEndpoints.rootPathCodec(rootPath).encodeRequest(()).url == expected.encodeRequest(()).url) testRootPathCodec(Nil, HttpCodec.empty) && testRootPathCodec( @@ -17,11 +17,11 @@ object ApiEndpointsSpec extends ZIOSpecDefault { literal("example") ) && testRootPathCodec("v1" :: "example" :: Nil, literal("v1") / literal("example")) }, - test("getLoggerConfigurations") { + test("getLoggerConfigs") { - def testPath(rootPath: Iterable[String], expected: PathCodec[Unit]) = + def testPath(rootPath: Seq[String], expected: PathCodec[Unit]) = assertTrue( - ApiEndpoints.getLoggerConfigurations(rootPath).input.encodeRequest(()).url == expected.encodeRequest(()).url + ApiEndpoints.getLoggerConfigs(rootPath).input.encodeRequest(()).url == expected.encodeRequest(()).url ) testPath(Nil, literal("logger")) && testPath( @@ -29,11 +29,11 @@ object ApiEndpointsSpec extends ZIOSpecDefault { literal("example") / literal("logger") ) }, - test("getLoggerConfiguration") { + test("getLoggerConfig") { - def testPath(rootPath: Iterable[String], expected: PathCodec[Unit]) = + def testPath(rootPath: Seq[String], expected: PathCodec[Unit]) = assertTrue( - ApiEndpoints.getLoggerConfiguration(rootPath).input.encodeRequest("my-logger").url == expected + ApiEndpoints.getLoggerConfig(rootPath).input.encodeRequest("my-logger").url == expected .encodeRequest(()) .url ) @@ -43,12 +43,12 @@ object ApiEndpointsSpec extends ZIOSpecDefault { literal("example") / literal("logger") / literal("my-logger") ) }, - test("setLoggerConfigurations") { + test("setLoggerConfigs") { - def testPath(rootPath: Iterable[String], expected: PathCodec[Unit]) = + def testPath(rootPath: Seq[String], expected: PathCodec[Unit]) = assertTrue( ApiEndpoints - .setLoggerConfiguration(rootPath) + .setLoggerConfig(rootPath) .input .encodeRequest(("my-logger", LogLevel.Info)) .url == expected diff --git a/api-http/src/test/scala/zio/logging/api/http/ApiHandlersSpec.scala b/api-http/src/test/scala/zio/logging/api/http/ApiHandlersSpec.scala index aaeb1a734..2bc45a832 100644 --- a/api-http/src/test/scala/zio/logging/api/http/ApiHandlersSpec.scala +++ b/api-http/src/test/scala/zio/logging/api/http/ApiHandlersSpec.scala @@ -10,51 +10,47 @@ object ApiHandlersSpec extends ZIOSpecDefault { val loggerService = ZLayer.succeed { new LoggerService { - override def getLoggerConfigurations(): ZIO[Any, Throwable, List[Domain.LoggerConfiguration]] = - ZIO.succeed(Domain.LoggerConfiguration("root", LogLevel.Info) :: Nil) + override def getLoggerConfigs(): ZIO[Any, Throwable, List[LoggerService.LoggerConfig]] = + ZIO.succeed(LoggerService.LoggerConfig("root", LogLevel.Info) :: Nil) - override def getLoggerConfiguration(name: String): ZIO[Any, Throwable, Option[Domain.LoggerConfiguration]] = - ZIO.succeed(Some(Domain.LoggerConfiguration(name, LogLevel.Info))) + override def getLoggerConfig( + name: String + ): ZIO[Any, Throwable, Option[LoggerService.LoggerConfig]] = + ZIO.succeed(Some(LoggerService.LoggerConfig(name, LogLevel.Info))) - override def setLoggerConfiguration( + override def setLoggerConfig( name: String, logLevel: LogLevel - ): ZIO[Any, Throwable, Domain.LoggerConfiguration] = - ZIO.succeed(Domain.LoggerConfiguration(name, logLevel)) + ): ZIO[Any, Throwable, LoggerService.LoggerConfig] = + ZIO.succeed(LoggerService.LoggerConfig(name, logLevel)) } } def spec = suite("ApiHandlersSpec")( test("get all") { - val routes = ApiHandlers.routes("example" :: Nil) - val request = Request.get(URL.decode("/example/logger").toOption.get) - for { + request <- ZIO.attempt(Request.get(URL.decode("/example/logger").toOption.get)) response <- routes.toApp.runZIO(request) - content <- HttpCodec.content[List[Domain.LoggerConfiguration]].decodeResponse(response) + content <- HttpCodec.content[List[ApiDomain.LoggerConfig]].decodeResponse(response) } yield assertTrue(response.status.isSuccess) && assertTrue( - content == List(Domain.LoggerConfiguration("root", LogLevel.Info)) + content == List(ApiDomain.LoggerConfig("root", LogLevel.Info)) ) - }.provideLayer(loggerService), + }, test("get") { - val routes = ApiHandlers.routes("example" :: Nil) - val request = Request.get(URL.decode("/example/logger/example.Service").toOption.get) - + val routes = ApiHandlers.routes("example" :: Nil) for { + request <- ZIO.attempt(Request.get(URL.decode("/example/logger/example.Service").toOption.get)) response <- routes.toApp.runZIO(request) - content <- HttpCodec.content[Domain.LoggerConfiguration].decodeResponse(response) + content <- HttpCodec.content[ApiDomain.LoggerConfig].decodeResponse(response) } yield assertTrue(response.status.isSuccess) && assertTrue( - content == Domain.LoggerConfiguration("example.Service", LogLevel.Info) + content == ApiDomain.LoggerConfig("example.Service", LogLevel.Info) ) - }.provideLayer(loggerService), + }, test("set") { - - import Domain.logLevelSchema - + import ApiDomain.logLevelSchema val routes = ApiHandlers.routes("example" :: Nil) - for { request <- ZIO.attempt( Request @@ -64,11 +60,11 @@ object ApiHandlersSpec extends ZIOSpecDefault { ) ) response <- routes.toApp.runZIO(request) - content <- HttpCodec.content[Domain.LoggerConfiguration].decodeResponse(response) + content <- HttpCodec.content[ApiDomain.LoggerConfig].decodeResponse(response) } yield assertTrue(response.status.isSuccess) && assertTrue( - content == Domain.LoggerConfiguration("example.Service", LogLevel.Warning) + content == ApiDomain.LoggerConfig("example.Service", LogLevel.Warning) ) - }.provideLayer(loggerService) - ) + } + ).provideLayer(loggerService) } diff --git a/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala b/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala index 0f17c5bf5..1545fd305 100644 --- a/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala +++ b/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala @@ -19,7 +19,7 @@ import zio.logging.internal.ReconfigurableLogger import zio.logging.{ ConsoleLoggerConfig, LogAnnotation } import zio.{ ExitCode, Runtime, Scope, ZIO, ZIOAppDefault, _ } import zio.http.Server -import zio.logging.api.http.{ ApiHandlers, Domain, LoggerService } +import zio.logging.api.http.{ ApiHandlers, ApiDomain, LoggerService } import java.util.UUID @@ -82,17 +82,19 @@ object LoggerReconfigureApp extends ZIOAppDefault { val loggerService = ZLayer.succeed { new LoggerService { - override def getLoggerConfigurations(): ZIO[Any, Throwable, List[Domain.LoggerConfiguration]] = - ZIO.succeed(Domain.LoggerConfiguration("root", LogLevel.Info) :: Nil) + override def getLoggerConfigs(): ZIO[Any, Throwable, List[LoggerService.LoggerConfig]] = + ZIO.succeed(LoggerService.LoggerConfig("root", LogLevel.Info) :: Nil) - override def getLoggerConfiguration(name: String): ZIO[Any, Throwable, Option[Domain.LoggerConfiguration]] = - ZIO.succeed(Some(Domain.LoggerConfiguration(name, LogLevel.Info))) + override def getLoggerConfig( + name: String + ): ZIO[Any, Throwable, Option[LoggerService.LoggerConfig]] = + ZIO.succeed(Some(LoggerService.LoggerConfig(name, LogLevel.Info))) - override def setLoggerConfiguration( + override def setLoggerConfig( name: String, logLevel: LogLevel - ): ZIO[Any, Throwable, Domain.LoggerConfiguration] = - ZIO.succeed(Domain.LoggerConfiguration(name, logLevel)) + ): ZIO[Any, Throwable, LoggerService.LoggerConfig] = + ZIO.succeed(LoggerService.LoggerConfig(name, logLevel)) } } From cc109717aa91adfc4b67cd31849e97b9e8b85598 Mon Sep 17 00:00:00 2001 From: Peter Kotula Date: Wed, 14 Jun 2023 23:21:25 +0200 Subject: [PATCH 20/56] ReconfigurableLogger --- .../main/scala/zio/logging/LogFilter.scala | 85 ++++---- .../internal/ReconfigurableLogger.scala | 18 +- .../example/LoggerReconfigureApp.scala | 191 +++++++++++++++--- 3 files changed, 223 insertions(+), 71 deletions(-) diff --git a/core/shared/src/main/scala/zio/logging/LogFilter.scala b/core/shared/src/main/scala/zio/logging/LogFilter.scala index e4f6aaea0..d79f5030f 100644 --- a/core/shared/src/main/scala/zio/logging/LogFilter.scala +++ b/core/shared/src/main/scala/zio/logging/LogFilter.scala @@ -247,7 +247,17 @@ object LogFilter { * @param rootLevel Minimum log level for the root node * @param mappings List of mappings, nesting defined by dot-separated strings */ - final case class LogLevelByNameConfig(rootLevel: LogLevel, mappings: Map[String, LogLevel]) + final case class LogLevelByNameConfig(rootLevel: LogLevel, mappings: Map[String, LogLevel]) { + + def withRootLevel(rootLevel: LogLevel): LogLevelByNameConfig = + LogLevelByNameConfig(rootLevel, mappings) + + def withMapping(name: String, level: LogLevel): LogLevelByNameConfig = + LogLevelByNameConfig(rootLevel, mappings + (name -> level)) + + def withMappings(mappings: Map[String, LogLevel]): LogLevelByNameConfig = + LogLevelByNameConfig(rootLevel, mappings) + } object LogLevelByNameConfig { @@ -259,6 +269,8 @@ object LogFilter { LogLevelByNameConfig(rootLevel, mappings) } } + + implicit val equal: Equal[LogLevelByNameConfig] = Equal.default } def apply[M, V]( @@ -353,42 +365,10 @@ object LogFilter { val mappingsSorted = mappings.map(splitNameByDotAndLevel.tupled).sorted(nameLevelOrdering) val nameGroup = group.map(splitNameByDot) - @tailrec - def globStarCompare(l: List[String], m: List[String]): Boolean = - (l, m) match { - case (_, Nil) => true - case (Nil, _) => false - case (l @ (_ :: ls), m) => - // try a regular, routesCompare or check if skipping paths (globstar pattern) results in a matching path - l.startsWith(m) || compareRoutes(l, m) || globStarCompare(ls, m) - } - - @tailrec - def anystringCompare(l: String, m: List[String]): Boolean = m match { - case mh :: ms => - val startOfMh = l.indexOfSlice(mh) - if (startOfMh >= 0) anystringCompare(l.drop(startOfMh + mh.size), ms) - else false - case Nil => l.isEmpty() - } - - @tailrec - def compareRoutes(l: List[String], m: List[String]): Boolean = - (l, m) match { - case (_, Nil) => true - case (Nil, _) => false - case (_ :: ls, "*" :: ms) => compareRoutes(ls, ms) - case (l, "**" :: ms) => globStarCompare(l, ms) - case (lh :: ls, mh :: ms) if !mh.contains("*") => - lh == mh && compareRoutes(ls, ms) - case (l @ (lh :: ls), m @ (mh :: ms)) => - anystringCompare(lh, mh.split('*').toList) && compareRoutes(ls, ms) - } - logLevelByGroup[M, List[String]]( rootLevel, nameGroup, - (l, m) => l.startsWith(m) || compareRoutes(l, m), + nameMatcher, mappingsSorted: _* ) } @@ -427,6 +407,43 @@ object LogFilter { def logLevelByName[M](config: LogLevelByNameConfig): LogFilter[M] = logLevelByGroup[M](LogGroup.loggerName, config) + private[logging] val nameMatcher: (List[String], List[String]) => Boolean = (l, m) => { + + @tailrec + def globStarCompare(l: List[String], m: List[String]): Boolean = + (l, m) match { + case (_, Nil) => true + case (Nil, _) => false + case (l @ (_ :: ls), m) => + // try a regular, routesCompare or check if skipping paths (globstar pattern) results in a matching path + l.startsWith(m) || compareRoutes(l, m) || globStarCompare(ls, m) + } + + @tailrec + def anystringCompare(l: String, m: List[String]): Boolean = m match { + case mh :: ms => + val startOfMh = l.indexOfSlice(mh) + if (startOfMh >= 0) anystringCompare(l.drop(startOfMh + mh.size), ms) + else false + case Nil => l.isEmpty() + } + + @tailrec + def compareRoutes(l: List[String], m: List[String]): Boolean = + (l, m) match { + case (_, Nil) => true + case (Nil, _) => false + case (_ :: ls, "*" :: ms) => compareRoutes(ls, ms) + case (l, "**" :: ms) => globStarCompare(l, ms) + case (lh :: ls, mh :: ms) if !mh.contains("*") => + lh == mh && compareRoutes(ls, ms) + case (lh :: ls, mh :: ms) => + anystringCompare(lh, mh.split('*').toList) && compareRoutes(ls, ms) + } + + l.startsWith(m) || compareRoutes(l, m) + } + private[logging] val splitNameByDotAndLevel: (String, LogLevel) => (List[String], LogLevel) = (name, level) => splitNameByDot(name) -> level diff --git a/core/shared/src/main/scala/zio/logging/internal/ReconfigurableLogger.scala b/core/shared/src/main/scala/zio/logging/internal/ReconfigurableLogger.scala index 48b075c0e..7a8ba4dab 100644 --- a/core/shared/src/main/scala/zio/logging/internal/ReconfigurableLogger.scala +++ b/core/shared/src/main/scala/zio/logging/internal/ReconfigurableLogger.scala @@ -22,6 +22,8 @@ import java.util.concurrent.atomic.AtomicReference private[logging] sealed trait ReconfigurableLogger[-Message, +Output, Config] extends ZLogger[Message, Output] { + def config: Config + def reconfigure(config: Config): Unit def reconfigureIfChanged(config: Config): Boolean @@ -30,18 +32,20 @@ private[logging] sealed trait ReconfigurableLogger[-Message, +Output, Config] ex private[logging] object ReconfigurableLogger { def apply[M, O, C: Equal]( - config: C, + initialConfig: C, makeLogger: C => ZLogger[M, O] ): ReconfigurableLogger[M, O, C] = new ReconfigurableLogger[M, O, C] { - private val configureLogger: AtomicReference[(C, ZLogger[M, O])] = { - val logger = makeLogger(config) - new AtomicReference[(C, ZLogger[M, O])]((config, logger)) + private val configuredLogger: AtomicReference[(C, ZLogger[M, O])] = { + val logger = makeLogger(initialConfig) + new AtomicReference[(C, ZLogger[M, O])]((initialConfig, logger)) } + override def config: C = configuredLogger.get()._1 + override def reconfigureIfChanged(config: C): Boolean = { - val currentConfig = configureLogger.get()._1 + val currentConfig = configuredLogger.get()._1 if (currentConfig !== config) { reconfigure(config) true @@ -50,7 +54,7 @@ private[logging] object ReconfigurableLogger { override def reconfigure(config: C): Unit = { val logger = makeLogger(config) - configureLogger.set((config, logger)) + configuredLogger.set((config, logger)) } override def apply( @@ -63,7 +67,7 @@ private[logging] object ReconfigurableLogger { spans: List[LogSpan], annotations: Map[String, String] ): O = - configureLogger.get()._2.apply(trace, fiberId, logLevel, message, cause, context, spans, annotations) + configuredLogger.get()._2.apply(trace, fiberId, logLevel, message, cause, context, spans, annotations) } def make[E, M, O, C: Equal]( diff --git a/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala b/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala index 1545fd305..6d8d12416 100644 --- a/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala +++ b/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala @@ -16,10 +16,10 @@ package zio.logging.example import zio.logging.internal.ReconfigurableLogger -import zio.logging.{ ConsoleLoggerConfig, LogAnnotation } +import zio.logging.{ ConsoleLoggerConfig, LogAnnotation, LogFilter } import zio.{ ExitCode, Runtime, Scope, ZIO, ZIOAppDefault, _ } -import zio.http.Server -import zio.logging.api.http.{ ApiHandlers, ApiDomain, LoggerService } +import zio.http._ +import zio.logging.api.http.{ ApiHandlers, LoggerService } import java.util.UUID @@ -50,18 +50,115 @@ object LoggerReconfigureApp extends ZIOAppDefault { } yield () } - override val bootstrap: ZLayer[ZIOAppArgs, Any, Any] = - Runtime.removeDefaultLoggers >>> configuredLogger( +// override val bootstrap: ZLayer[ZIOAppArgs, Any, Any] = +// Runtime.removeDefaultLoggers >>> configuredLogger( +// for { +// info <- Random.nextBoolean +// cfg = Map( +// "logger/format" -> logFormat, +// "logger/filter/rootLevel" -> (if (info) LogLevel.Info.label else LogLevel.Debug.label) +// ) +// _ <- Console.printLine(cfg.mkString(", ")).orDie +// config <- ConfigProvider.fromMap(cfg, "/").nested("logger").load(ConsoleLoggerConfig.config) +// } yield config +// ) + + class ConfigurableLogger[+Output](logger: ReconfigurableLogger[String, Output, LogFilter.LogLevelByNameConfig]) + extends ZLogger[String, Output] + with LoggerService { + + val rootName = "root" + override def apply( + trace: Trace, + fiberId: FiberId, + logLevel: LogLevel, + message: () => String, + cause: Cause[Any], + context: FiberRefs, + spans: List[LogSpan], + annotations: Map[String, String] + ): Output = logger.apply(trace, fiberId, logLevel, message, cause, context, spans, annotations) + + override def getLoggerConfigs(): ZIO[Any, Throwable, List[LoggerService.LoggerConfig]] = + ZIO.attempt { + val currentConfig = logger.config + + LoggerService.LoggerConfig(rootName, currentConfig.rootLevel) :: currentConfig.mappings + .map(LoggerService.LoggerConfig.tupled) + .toList + } + + override def getLoggerConfig(name: String): ZIO[Any, Throwable, Option[LoggerService.LoggerConfig]] = + ZIO.attempt { + val currentConfig = logger.config + + if (name == rootName) { Some(LoggerService.LoggerConfig(rootName, currentConfig.rootLevel)) } + else { + currentConfig.mappings.collectFirst { + case (n, l) if n == name => LoggerService.LoggerConfig(n, l) + } + } + } + + override def setLoggerConfig(name: String, logLevel: LogLevel): ZIO[Any, Throwable, LoggerService.LoggerConfig] = + ZIO.attempt { + val currentConfig = logger.config + + val newConfig = if (name == rootName) { + currentConfig.withRootLevel(logLevel) + } else { + currentConfig.withMapping(name, logLevel) + } + + logger.reconfigureIfChanged(newConfig) + + LoggerService.LoggerConfig(name, logLevel) + } + } + + def configuredLogger(configPath: String = "logger"): ZLayer[Any, Config.Error, Unit] = + ZLayer.scoped { for { - info <- Random.nextBoolean - cfg = Map( - "logger/format" -> logFormat, - "logger/filter/rootLevel" -> (if (info) LogLevel.Info.label else LogLevel.Debug.label) - ) - _ <- Console.printLine(cfg.mkString(", ")).orDie - config <- ConfigProvider.fromMap(cfg, "/").nested("logger").load(ConsoleLoggerConfig.config) - } yield config - ) + consoleLoggerConfig <- ZIO.config(ConsoleLoggerConfig.config.nested(configPath)) + filterConfig <- ZIO.succeed { + consoleLoggerConfig.filter + .asInstanceOf[LogFilter.ConfiguredFilter[String, LogFilter.LogLevelByNameConfig]] + .config + } + logger <- ZIO.succeed { + val logger = ReconfigurableLogger[String, Any, LogFilter.LogLevelByNameConfig]( + filterConfig, + config => { + val filter = LogFilter.logLevelByName(config) + + filter.filter(consoleLoggerConfig.format.toLogger.map { line => + try java.lang.System.out.println(line) + catch { + case t: VirtualMachineError => throw t + case _: Throwable => () + } + }) + } + ) + + new ConfigurableLogger[Any](logger) + } + + _ <- ZIO.withLoggerScoped(logger) + } yield () + } + + val configProvider: ConfigProvider = ConfigProvider.fromMap( + Map( + "logger/format" -> logFormat, + "logger/filter/rootLevel" -> LogLevel.Info.label, + "logger/filter/mappings/zio.logging.example" -> LogLevel.Debug.label + ), + "/" + ) + + override val bootstrap: ZLayer[Any, Config.Error, Unit] = + Runtime.removeDefaultLoggers >>> Runtime.setConfigProvider(configProvider) >>> configuredLogger() def exec() = for { @@ -80,26 +177,47 @@ object LoggerReconfigureApp extends ZIOAppDefault { _ <- ZIO.logDebug("Done") @@ LogAnnotation.TraceId(traceId) } yield () - val loggerService = ZLayer.succeed { - new LoggerService { - override def getLoggerConfigs(): ZIO[Any, Throwable, List[LoggerService.LoggerConfig]] = - ZIO.succeed(LoggerService.LoggerConfig("root", LogLevel.Info) :: Nil) - - override def getLoggerConfig( - name: String - ): ZIO[Any, Throwable, Option[LoggerService.LoggerConfig]] = - ZIO.succeed(Some(LoggerService.LoggerConfig(name, LogLevel.Info))) - - override def setLoggerConfig( - name: String, - logLevel: LogLevel - ): ZIO[Any, Throwable, LoggerService.LoggerConfig] = - ZIO.succeed(LoggerService.LoggerConfig(name, logLevel)) +// val loggerService = ZLayer.succeed { +// new LoggerService { +// override def getLoggerConfigs(): ZIO[Any, Throwable, List[LoggerService.LoggerConfig]] = +// ZIO.succeed(LoggerService.LoggerConfig("root", LogLevel.Info) :: Nil) +// +// override def getLoggerConfig( +// name: String +// ): ZIO[Any, Throwable, Option[LoggerService.LoggerConfig]] = +// ZIO.succeed(Some(LoggerService.LoggerConfig(name, LogLevel.Info))) +// +// override def setLoggerConfig( +// name: String, +// logLevel: LogLevel +// ): ZIO[Any, Throwable, LoggerService.LoggerConfig] = +// ZIO.succeed(LoggerService.LoggerConfig(name, logLevel)) +// } +// } + + val loggerService: ZLayer[Any, Throwable, LoggerService] = + ZLayer.fromZIO { + for { + fiberRefs <- ZIO.getFiberRefs + + loggerService <- ZIO.attempt { + val loggers = fiberRefs.getOrDefault(FiberRef.currentLoggers) + loggers.collectFirst { case logger: ConfigurableLogger[_] => + logger + }.get + } + + } yield loggerService } - } val httpApp = ApiHandlers.routes("example" :: Nil).toApp[LoggerService] + // Handler +// .html(ApiEndpoints.doc("example" :: Nil).toHtml) +// .toHttp +// .whenPathEq("/example") +// .withDefaultErrorResponse + override def run: ZIO[Scope, Any, ExitCode] = (for { _ <- Server.serve(httpApp).fork @@ -107,3 +225,16 @@ object LoggerReconfigureApp extends ZIOAppDefault { } yield ExitCode.success).provide(loggerService ++ Server.default) } + +/* + + curl 'http://localhost:8080/example/logger' + + curl 'http://localhost:8080/example/logger/root' + + curl 'http://localhost:8080/example/logger/root' + + curl --location --request PUT 'http://localhost:8080/example/logger/root' --header 'Content-Type: application/json' --data-raw '"WARN"' + + curl --location --request PUT 'http://localhost:8080/example/logger/zio.logging.example' --header 'Content-Type: application/json' --data-raw '"WARN"' + */ From fef55764c0ba070ecda0b270c900f1bde13ddf58 Mon Sep 17 00:00:00 2001 From: Peter Kotula Date: Thu, 15 Jun 2023 10:36:11 +0200 Subject: [PATCH 21/56] LoggerConfigurer --- .../zio/logging/api/http/ApiDomain.scala | 3 +- .../zio/logging/api/http/ApiHandlers.scala | 9 +- .../zio/logging/api/http/LoggerService.scala | 44 ------ .../logging/api/http/ApiHandlersSpec.scala | 19 +-- .../zio/logging/ConfigurableLogger.scala | 48 ++++++ .../example/LoggerReconfigureApp.scala | 137 +++++------------- 6 files changed, 101 insertions(+), 159 deletions(-) delete mode 100644 api-http/src/main/scala/zio/logging/api/http/LoggerService.scala create mode 100644 core/shared/src/main/scala/zio/logging/ConfigurableLogger.scala diff --git a/api-http/src/main/scala/zio/logging/api/http/ApiDomain.scala b/api-http/src/main/scala/zio/logging/api/http/ApiDomain.scala index 1b441457b..47b449bd6 100644 --- a/api-http/src/main/scala/zio/logging/api/http/ApiDomain.scala +++ b/api-http/src/main/scala/zio/logging/api/http/ApiDomain.scala @@ -16,6 +16,7 @@ package zio.logging.api.http import zio.LogLevel +import zio.logging.LoggerConfigurer import zio.schema.{ DeriveSchema, Schema } object ApiDomain { @@ -47,7 +48,7 @@ object ApiDomain { object LoggerConfig { implicit val schema: Schema[LoggerConfig] = DeriveSchema.gen[LoggerConfig] - def from(value: LoggerService.LoggerConfig): LoggerConfig = + def from(value: LoggerConfigurer.LoggerConfig): LoggerConfig = LoggerConfig(value.name, value.logLevel) } diff --git a/api-http/src/main/scala/zio/logging/api/http/ApiHandlers.scala b/api-http/src/main/scala/zio/logging/api/http/ApiHandlers.scala index 86b4721ad..c7d1f9bc2 100644 --- a/api-http/src/main/scala/zio/logging/api/http/ApiHandlers.scala +++ b/api-http/src/main/scala/zio/logging/api/http/ApiHandlers.scala @@ -18,6 +18,7 @@ package zio.logging.api.http import zio.ZIO import zio.http.endpoint.EndpointMiddleware.None import zio.http.endpoint.Routes +import zio.logging.LoggerConfigurer object ApiHandlers { @@ -25,7 +26,7 @@ object ApiHandlers { ApiEndpoints .getLoggerConfigs(rootPath) .implement(_ => - LoggerService + LoggerConfigurer .getLoggerConfigs() .map(_.map(ApiDomain.LoggerConfig.from)) .mapError(_ => ApiDomain.Error.Internal()) @@ -35,7 +36,7 @@ object ApiHandlers { ApiEndpoints .getLoggerConfig(rootPath) .implement { name => - LoggerService.getLoggerConfig(name).mapError(_ => ApiDomain.Error.Internal()).flatMap { + LoggerConfigurer.getLoggerConfig(name).mapError(_ => ApiDomain.Error.Internal()).flatMap { case Some(r) => ZIO.succeed(ApiDomain.LoggerConfig.from(r)) case _ => ZIO.fail(ApiDomain.Error.NotFound()) } @@ -45,12 +46,12 @@ object ApiHandlers { ApiEndpoints .setLoggerConfig(rootPath) .implement { case (name, logLevel) => - LoggerService + LoggerConfigurer .setLoggerConfig(name, logLevel) .map(ApiDomain.LoggerConfig.from) .mapError(_ => ApiDomain.Error.Internal()) } - def routes(rootPath: Seq[String] = Seq.empty): Routes[LoggerService, ApiDomain.Error, None] = + def routes(rootPath: Seq[String] = Seq.empty): Routes[LoggerConfigurer, ApiDomain.Error, None] = getLoggerConfigs(rootPath) ++ getLoggerConfig(rootPath) ++ setLoggerConfigs(rootPath) } diff --git a/api-http/src/main/scala/zio/logging/api/http/LoggerService.scala b/api-http/src/main/scala/zio/logging/api/http/LoggerService.scala deleted file mode 100644 index bbc5a0612..000000000 --- a/api-http/src/main/scala/zio/logging/api/http/LoggerService.scala +++ /dev/null @@ -1,44 +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.api.http - -import zio.{ LogLevel, ZIO } - -trait LoggerService { - - def getLoggerConfigs(): ZIO[Any, Throwable, List[LoggerService.LoggerConfig]] - - def getLoggerConfig(name: String): ZIO[Any, Throwable, Option[LoggerService.LoggerConfig]] - - def setLoggerConfig(name: String, logLevel: LogLevel): ZIO[Any, Throwable, LoggerService.LoggerConfig] -} - -object LoggerService { - - final case class LoggerConfig(name: String, logLevel: LogLevel) - - def getLoggerConfigs(): ZIO[LoggerService, Throwable, List[LoggerService.LoggerConfig]] = - ZIO.serviceWithZIO[LoggerService](_.getLoggerConfigs()) - - def getLoggerConfig(name: String): ZIO[LoggerService, Throwable, Option[LoggerService.LoggerConfig]] = - ZIO.serviceWithZIO[LoggerService](_.getLoggerConfig(name)) - - def setLoggerConfig( - name: String, - logLevel: LogLevel - ): ZIO[LoggerService, Throwable, LoggerService.LoggerConfig] = - ZIO.serviceWithZIO[LoggerService](_.setLoggerConfig(name, logLevel)) -} diff --git a/api-http/src/test/scala/zio/logging/api/http/ApiHandlersSpec.scala b/api-http/src/test/scala/zio/logging/api/http/ApiHandlersSpec.scala index 2bc45a832..704aa2a2e 100644 --- a/api-http/src/test/scala/zio/logging/api/http/ApiHandlersSpec.scala +++ b/api-http/src/test/scala/zio/logging/api/http/ApiHandlersSpec.scala @@ -4,25 +4,26 @@ import zio.{ ZIO, ZLayer } import zio.http._ import zio.http.codec._ import zio.LogLevel +import zio.logging.LoggerConfigurer import zio.test._ object ApiHandlersSpec extends ZIOSpecDefault { - val loggerService = ZLayer.succeed { - new LoggerService { - override def getLoggerConfigs(): ZIO[Any, Throwable, List[LoggerService.LoggerConfig]] = - ZIO.succeed(LoggerService.LoggerConfig("root", LogLevel.Info) :: Nil) + val loggerConfigurer = ZLayer.succeed { + new LoggerConfigurer { + override def getLoggerConfigs(): ZIO[Any, Throwable, List[LoggerConfigurer.LoggerConfig]] = + ZIO.succeed(LoggerConfigurer.LoggerConfig("root", LogLevel.Info) :: Nil) override def getLoggerConfig( name: String - ): ZIO[Any, Throwable, Option[LoggerService.LoggerConfig]] = - ZIO.succeed(Some(LoggerService.LoggerConfig(name, LogLevel.Info))) + ): ZIO[Any, Throwable, Option[LoggerConfigurer.LoggerConfig]] = + ZIO.succeed(Some(LoggerConfigurer.LoggerConfig(name, LogLevel.Info))) override def setLoggerConfig( name: String, logLevel: LogLevel - ): ZIO[Any, Throwable, LoggerService.LoggerConfig] = - ZIO.succeed(LoggerService.LoggerConfig(name, logLevel)) + ): ZIO[Any, Throwable, LoggerConfigurer.LoggerConfig] = + ZIO.succeed(LoggerConfigurer.LoggerConfig(name, logLevel)) } } @@ -65,6 +66,6 @@ object ApiHandlersSpec extends ZIOSpecDefault { content == ApiDomain.LoggerConfig("example.Service", LogLevel.Warning) ) } - ).provideLayer(loggerService) + ).provideLayer(loggerConfigurer) } diff --git a/core/shared/src/main/scala/zio/logging/ConfigurableLogger.scala b/core/shared/src/main/scala/zio/logging/ConfigurableLogger.scala new file mode 100644 index 000000000..c4903d0b1 --- /dev/null +++ b/core/shared/src/main/scala/zio/logging/ConfigurableLogger.scala @@ -0,0 +1,48 @@ +package zio.logging + +import zio.{ FiberRef, LogLevel, ZIO, ZLayer, ZLogger } + +trait LoggerConfigurer { + + def getLoggerConfigs(): ZIO[Any, Throwable, List[LoggerConfigurer.LoggerConfig]] + + def getLoggerConfig(name: String): ZIO[Any, Throwable, Option[LoggerConfigurer.LoggerConfig]] + + def setLoggerConfig(name: String, logLevel: LogLevel): ZIO[Any, Throwable, LoggerConfigurer.LoggerConfig] +} + +object LoggerConfigurer { + + final case class LoggerConfig(name: String, logLevel: LogLevel) + + def getLoggerConfigs(): ZIO[LoggerConfigurer, Throwable, List[LoggerConfigurer.LoggerConfig]] = + ZIO.serviceWithZIO[LoggerConfigurer](_.getLoggerConfigs()) + + def getLoggerConfig(name: String): ZIO[LoggerConfigurer, Throwable, Option[LoggerConfigurer.LoggerConfig]] = + ZIO.serviceWithZIO[LoggerConfigurer](_.getLoggerConfig(name)) + + def setLoggerConfig( + name: String, + logLevel: LogLevel + ): ZIO[LoggerConfigurer, Throwable, LoggerConfigurer.LoggerConfig] = + ZIO.serviceWithZIO[LoggerConfigurer](_.setLoggerConfig(name, logLevel)) + + val layer: ZLayer[Any, Throwable, LoggerConfigurer] = + ZLayer.fromZIO { + for { + fiberRefs <- ZIO.getFiberRefs + + loggerService <- ZIO.attempt { + val loggers = fiberRefs.getOrDefault(FiberRef.currentLoggers) + loggers.collectFirst { case logger: ConfigurableLogger[_, _] => + logger.configurer + }.getOrElse(throw new RuntimeException("LoggerConfigurer not found")) + } + } yield loggerService + } +} + +trait ConfigurableLogger[-Message, +Output] extends ZLogger[Message, Output] { + + def configurer: LoggerConfigurer +} diff --git a/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala b/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala index 6d8d12416..24003e17e 100644 --- a/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala +++ b/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala @@ -15,59 +15,22 @@ */ package zio.logging.example +import zio.http.HttpAppMiddleware.basicAuth import zio.logging.internal.ReconfigurableLogger -import zio.logging.{ ConsoleLoggerConfig, LogAnnotation, LogFilter } +import zio.logging.{ ConfigurableLogger, ConsoleLoggerConfig, LogAnnotation, LogFilter, LoggerConfigurer } import zio.{ ExitCode, Runtime, Scope, ZIO, ZIOAppDefault, _ } import zio.http._ -import zio.logging.api.http.{ ApiHandlers, LoggerService } +import zio.logging.api.http.ApiHandlers import java.util.UUID object LoggerReconfigureApp extends ZIOAppDefault { - val logFormat = - "%highlight{%timestamp{yyyy-MM-dd'T'HH:mm:ssZ} %fixed{7}{%level} [%fiberId] %name:%line %message %kv{trace_id} %kv{user_id} %cause}" - - def configuredLogger( - loadConfig: => ZIO[Any, Config.Error, ConsoleLoggerConfig] - ): ZLayer[Any, Config.Error, Unit] = - ZLayer.scoped { - for { - logger <- ReconfigurableLogger - .make[Config.Error, String, Any, ConsoleLoggerConfig]( - loadConfig, - config => - config.filter.filter(config.format.toLogger.map { line => - try java.lang.System.out.println(line) - catch { - case t: VirtualMachineError => throw t - case _: Throwable => () - } - }), - 500.millis - ) - _ <- ZIO.withLoggerScoped(logger) - } yield () - } + class Logger[+Output](logger: ReconfigurableLogger[String, Output, LogFilter.LogLevelByNameConfig]) + extends ConfigurableLogger[String, Output] { -// override val bootstrap: ZLayer[ZIOAppArgs, Any, Any] = -// Runtime.removeDefaultLoggers >>> configuredLogger( -// for { -// info <- Random.nextBoolean -// cfg = Map( -// "logger/format" -> logFormat, -// "logger/filter/rootLevel" -> (if (info) LogLevel.Info.label else LogLevel.Debug.label) -// ) -// _ <- Console.printLine(cfg.mkString(", ")).orDie -// config <- ConfigProvider.fromMap(cfg, "/").nested("logger").load(ConsoleLoggerConfig.config) -// } yield config -// ) - - class ConfigurableLogger[+Output](logger: ReconfigurableLogger[String, Output, LogFilter.LogLevelByNameConfig]) - extends ZLogger[String, Output] - with LoggerService { + override val configurer: LoggerConfigurer = new Configurer(logger) - val rootName = "root" override def apply( trace: Trace, fiberId: FiberId, @@ -79,28 +42,35 @@ object LoggerReconfigureApp extends ZIOAppDefault { annotations: Map[String, String] ): Output = logger.apply(trace, fiberId, logLevel, message, cause, context, spans, annotations) - override def getLoggerConfigs(): ZIO[Any, Throwable, List[LoggerService.LoggerConfig]] = + } + + class Configurer(logger: ReconfigurableLogger[_, _, LogFilter.LogLevelByNameConfig]) extends LoggerConfigurer { + + val rootName = "root" + + override def getLoggerConfigs(): ZIO[Any, Throwable, List[LoggerConfigurer.LoggerConfig]] = ZIO.attempt { val currentConfig = logger.config - LoggerService.LoggerConfig(rootName, currentConfig.rootLevel) :: currentConfig.mappings - .map(LoggerService.LoggerConfig.tupled) + LoggerConfigurer.LoggerConfig(rootName, currentConfig.rootLevel) :: currentConfig.mappings + .map(LoggerConfigurer.LoggerConfig.tupled) .toList } - override def getLoggerConfig(name: String): ZIO[Any, Throwable, Option[LoggerService.LoggerConfig]] = + override def getLoggerConfig(name: String): ZIO[Any, Throwable, Option[LoggerConfigurer.LoggerConfig]] = ZIO.attempt { val currentConfig = logger.config - if (name == rootName) { Some(LoggerService.LoggerConfig(rootName, currentConfig.rootLevel)) } - else { + if (name == rootName) { + Some(LoggerConfigurer.LoggerConfig(rootName, currentConfig.rootLevel)) + } else { currentConfig.mappings.collectFirst { - case (n, l) if n == name => LoggerService.LoggerConfig(n, l) + case (n, l) if n == name => LoggerConfigurer.LoggerConfig(n, l) } } } - override def setLoggerConfig(name: String, logLevel: LogLevel): ZIO[Any, Throwable, LoggerService.LoggerConfig] = + override def setLoggerConfig(name: String, logLevel: LogLevel): ZIO[Any, Throwable, LoggerConfigurer.LoggerConfig] = ZIO.attempt { val currentConfig = logger.config @@ -112,11 +82,11 @@ object LoggerReconfigureApp extends ZIOAppDefault { logger.reconfigureIfChanged(newConfig) - LoggerService.LoggerConfig(name, logLevel) + LoggerConfigurer.LoggerConfig(name, logLevel) } } - def configuredLogger(configPath: String = "logger"): ZLayer[Any, Config.Error, Unit] = + def configurableLogger(configPath: String = "logger"): ZLayer[Any, Config.Error, Unit] = ZLayer.scoped { for { consoleLoggerConfig <- ZIO.config(ConsoleLoggerConfig.config.nested(configPath)) @@ -141,13 +111,16 @@ object LoggerReconfigureApp extends ZIOAppDefault { } ) - new ConfigurableLogger[Any](logger) + new Logger[Any](logger) } _ <- ZIO.withLoggerScoped(logger) } yield () } + val logFormat = + "%highlight{%timestamp{yyyy-MM-dd'T'HH:mm:ssZ} %fixed{7}{%level} [%fiberId] %name:%line %message %kv{trace_id} %kv{user_id} %cause}" + val configProvider: ConfigProvider = ConfigProvider.fromMap( Map( "logger/format" -> logFormat, @@ -158,7 +131,7 @@ object LoggerReconfigureApp extends ZIOAppDefault { ) override val bootstrap: ZLayer[Any, Config.Error, Unit] = - Runtime.removeDefaultLoggers >>> Runtime.setConfigProvider(configProvider) >>> configuredLogger() + Runtime.removeDefaultLoggers >>> Runtime.setConfigProvider(configProvider) >>> configurableLogger() def exec() = for { @@ -177,64 +150,26 @@ object LoggerReconfigureApp extends ZIOAppDefault { _ <- ZIO.logDebug("Done") @@ LogAnnotation.TraceId(traceId) } yield () -// val loggerService = ZLayer.succeed { -// new LoggerService { -// override def getLoggerConfigs(): ZIO[Any, Throwable, List[LoggerService.LoggerConfig]] = -// ZIO.succeed(LoggerService.LoggerConfig("root", LogLevel.Info) :: Nil) -// -// override def getLoggerConfig( -// name: String -// ): ZIO[Any, Throwable, Option[LoggerService.LoggerConfig]] = -// ZIO.succeed(Some(LoggerService.LoggerConfig(name, LogLevel.Info))) -// -// override def setLoggerConfig( -// name: String, -// logLevel: LogLevel -// ): ZIO[Any, Throwable, LoggerService.LoggerConfig] = -// ZIO.succeed(LoggerService.LoggerConfig(name, logLevel)) -// } -// } - - val loggerService: ZLayer[Any, Throwable, LoggerService] = - ZLayer.fromZIO { - for { - fiberRefs <- ZIO.getFiberRefs - - loggerService <- ZIO.attempt { - val loggers = fiberRefs.getOrDefault(FiberRef.currentLoggers) - loggers.collectFirst { case logger: ConfigurableLogger[_] => - logger - }.get - } - - } yield loggerService - } - - val httpApp = ApiHandlers.routes("example" :: Nil).toApp[LoggerService] - - // Handler -// .html(ApiEndpoints.doc("example" :: Nil).toHtml) -// .toHttp -// .whenPathEq("/example") -// .withDefaultErrorResponse + val httpApp = ApiHandlers.routes("example" :: Nil).toApp[LoggerConfigurer] @@ basicAuth("admin", "admin") override def run: ZIO[Scope, Any, ExitCode] = (for { _ <- Server.serve(httpApp).fork _ <- exec().repeat(Schedule.fixed(500.millis)) - } yield ExitCode.success).provide(loggerService ++ Server.default) + } yield ExitCode.success).provide(LoggerConfigurer.layer ++ Server.default) } /* - curl 'http://localhost:8080/example/logger' + curl -u "admin:admin" 'http://localhost:8080/example/logger' + + curl -u "admin:admin" 'http://localhost:8080/example/logger/root' - curl 'http://localhost:8080/example/logger/root' + curl -u "admin:admin" 'http://localhost:8080/example/logger/root' - curl 'http://localhost:8080/example/logger/root' + curl -u "admin:admin" --location --request PUT 'http://localhost:8080/example/logger/root' --header 'Content-Type: application/json' --data-raw '"WARN"' - curl --location --request PUT 'http://localhost:8080/example/logger/root' --header 'Content-Type: application/json' --data-raw '"WARN"' + curl -u "admin:admin" --location --request PUT 'http://localhost:8080/example/logger/zio.logging.example' --header 'Content-Type: application/json' --data-raw '"WARN"' - curl --location --request PUT 'http://localhost:8080/example/logger/zio.logging.example' --header 'Content-Type: application/json' --data-raw '"WARN"' */ From 821a203b2a518b67d3cdc9da2c41b2d5f4b306a9 Mon Sep 17 00:00:00 2001 From: Peter Kotula Date: Fri, 16 Jun 2023 13:39:02 +0200 Subject: [PATCH 22/56] LoggerConfigurer --- .../zio/logging/ConfigurableLogger.scala | 25 ++++++++++++++++++- .../example/LoggerReconfigureApp.scala | 20 +-------------- 2 files changed, 25 insertions(+), 20 deletions(-) diff --git a/core/shared/src/main/scala/zio/logging/ConfigurableLogger.scala b/core/shared/src/main/scala/zio/logging/ConfigurableLogger.scala index c4903d0b1..8ccf69f68 100644 --- a/core/shared/src/main/scala/zio/logging/ConfigurableLogger.scala +++ b/core/shared/src/main/scala/zio/logging/ConfigurableLogger.scala @@ -1,6 +1,6 @@ package zio.logging -import zio.{ FiberRef, LogLevel, ZIO, ZLayer, ZLogger } +import zio.{ Cause, FiberId, FiberRef, FiberRefs, LogLevel, LogSpan, Trace, ZIO, ZLayer, ZLogger } trait LoggerConfigurer { @@ -46,3 +46,26 @@ trait ConfigurableLogger[-Message, +Output] extends ZLogger[Message, Output] { def configurer: LoggerConfigurer } + +object ConfigurableLogger { + + def apply[Message, Output]( + logger: ZLogger[Message, Output], + loggerConfigurer: LoggerConfigurer + ): ConfigurableLogger[Message, Output] = + new ConfigurableLogger[Message, Output] { + + override val configurer: LoggerConfigurer = loggerConfigurer + + override def apply( + trace: Trace, + fiberId: FiberId, + logLevel: LogLevel, + message: () => Message, + cause: Cause[Any], + context: FiberRefs, + spans: List[LogSpan], + annotations: Map[String, String] + ): Output = logger.apply(trace, fiberId, logLevel, message, cause, context, spans, annotations) + } +} diff --git a/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala b/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala index 24003e17e..8e74ce8db 100644 --- a/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala +++ b/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala @@ -26,24 +26,6 @@ import java.util.UUID object LoggerReconfigureApp extends ZIOAppDefault { - class Logger[+Output](logger: ReconfigurableLogger[String, Output, LogFilter.LogLevelByNameConfig]) - extends ConfigurableLogger[String, Output] { - - override val configurer: LoggerConfigurer = new Configurer(logger) - - override def apply( - trace: Trace, - fiberId: FiberId, - logLevel: LogLevel, - message: () => String, - cause: Cause[Any], - context: FiberRefs, - spans: List[LogSpan], - annotations: Map[String, String] - ): Output = logger.apply(trace, fiberId, logLevel, message, cause, context, spans, annotations) - - } - class Configurer(logger: ReconfigurableLogger[_, _, LogFilter.LogLevelByNameConfig]) extends LoggerConfigurer { val rootName = "root" @@ -111,7 +93,7 @@ object LoggerReconfigureApp extends ZIOAppDefault { } ) - new Logger[Any](logger) + ConfigurableLogger(logger, new Configurer(logger)) } _ <- ZIO.withLoggerScoped(logger) From 343b3b52beb0fc4c26efc876bbe6b2eb80f06979 Mon Sep 17 00:00:00 2001 From: Peter Kotula Date: Fri, 16 Jun 2023 23:08:12 +0200 Subject: [PATCH 23/56] LoggerConfigurer --- .../zio/logging/ConfigurableLogger.scala | 104 +++++++++++++++++- .../main/scala/zio/logging/LogFormat.scala | 7 ++ .../example/LoggerReconfigureApp.scala | 74 +++---------- 3 files changed, 118 insertions(+), 67 deletions(-) diff --git a/core/shared/src/main/scala/zio/logging/ConfigurableLogger.scala b/core/shared/src/main/scala/zio/logging/ConfigurableLogger.scala index 8ccf69f68..d37587342 100644 --- a/core/shared/src/main/scala/zio/logging/ConfigurableLogger.scala +++ b/core/shared/src/main/scala/zio/logging/ConfigurableLogger.scala @@ -1,5 +1,21 @@ +/* + * 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 +import zio.logging.internal.ReconfigurableLogger import zio.{ Cause, FiberId, FiberRef, FiberRefs, LogLevel, LogSpan, Trace, ZIO, ZLayer, ZLogger } trait LoggerConfigurer { @@ -49,13 +65,43 @@ trait ConfigurableLogger[-Message, +Output] extends ZLogger[Message, Output] { object ConfigurableLogger { - def apply[Message, Output]( +// def apply[Message, Output]( +// logger: ZLogger[Message, Output], +// loggerConfigurer: LoggerConfigurer +// ): ConfigurableLogger[Message, Output] = +// new ConfigurableLogger[Message, Output] { +// +// override val configurer: LoggerConfigurer = loggerConfigurer +// +// override def apply( +// trace: Trace, +// fiberId: FiberId, +// logLevel: LogLevel, +// message: () => Message, +// cause: Cause[Any], +// context: FiberRefs, +// spans: List[LogSpan], +// annotations: Map[String, String] +// ): Output = logger.apply(trace, fiberId, logLevel, message, cause, context, spans, annotations) +// } + + def make[Message, Output]( logger: ZLogger[Message, Output], - loggerConfigurer: LoggerConfigurer - ): ConfigurableLogger[Message, Output] = - new ConfigurableLogger[Message, Output] { + filterConfig: LogFilter.LogLevelByNameConfig + ): ConfigurableLogger[Message, Option[Output]] = { - override val configurer: LoggerConfigurer = loggerConfigurer + val reconfigurableLogger = ReconfigurableLogger[Message, Option[Output], LogFilter.LogLevelByNameConfig]( + filterConfig, + config => { + val filter = LogFilter.logLevelByName(config) + + filter.filter(logger) + } + ) + + new ConfigurableLogger[Message, Option[Output]] { + + override val configurer: LoggerConfigurer = Configurer(reconfigurableLogger) override def apply( trace: Trace, @@ -66,6 +112,52 @@ object ConfigurableLogger { context: FiberRefs, spans: List[LogSpan], annotations: Map[String, String] - ): Output = logger.apply(trace, fiberId, logLevel, message, cause, context, spans, annotations) + ): Option[Output] = + reconfigurableLogger.apply(trace, fiberId, logLevel, message, cause, context, spans, annotations) } + } + + private case class Configurer(logger: ReconfigurableLogger[_, _, LogFilter.LogLevelByNameConfig]) + extends LoggerConfigurer { + + private val rootName = "root" + + override def getLoggerConfigs(): ZIO[Any, Throwable, List[LoggerConfigurer.LoggerConfig]] = + ZIO.attempt { + val currentConfig = logger.config + + LoggerConfigurer.LoggerConfig(rootName, currentConfig.rootLevel) :: currentConfig.mappings + .map(LoggerConfigurer.LoggerConfig.tupled) + .toList + } + + override def getLoggerConfig(name: String): ZIO[Any, Throwable, Option[LoggerConfigurer.LoggerConfig]] = + ZIO.attempt { + val currentConfig = logger.config + + if (name == rootName) { + Some(LoggerConfigurer.LoggerConfig(rootName, currentConfig.rootLevel)) + } else { + currentConfig.mappings.collectFirst { + case (n, l) if n == name => LoggerConfigurer.LoggerConfig(n, l) + } + } + } + + override def setLoggerConfig(name: String, logLevel: LogLevel): ZIO[Any, Throwable, LoggerConfigurer.LoggerConfig] = + ZIO.attempt { + val currentConfig = logger.config + + val newConfig = if (name == rootName) { + currentConfig.withRootLevel(logLevel) + } else { + currentConfig.withMapping(name, logLevel) + } + + logger.reconfigureIfChanged(newConfig) + + LoggerConfigurer.LoggerConfig(name, logLevel) + } + } + } diff --git a/core/shared/src/main/scala/zio/logging/LogFormat.scala b/core/shared/src/main/scala/zio/logging/LogFormat.scala index 90c32dc2b..fa9f2a93d 100644 --- a/core/shared/src/main/scala/zio/logging/LogFormat.scala +++ b/core/shared/src/main/scala/zio/logging/LogFormat.scala @@ -764,6 +764,13 @@ object LogFormat { def parse(pattern: String): Either[Parser.ParserError[String], Pattern] = syntax.parseString(pattern) + + val config: Config[Pattern] = Config.string.mapOrFail { value => + Pattern.parse(value) match { + case Right(p) => Right(p) + case Left(_) => Left(Config.Error.InvalidData(Chunk.empty, s"Expected a Pattern, but found ${value}")) + } + } } private val NL = System.lineSeparator() diff --git a/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala b/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala index 8e74ce8db..553466834 100644 --- a/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala +++ b/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala @@ -16,7 +16,6 @@ package zio.logging.example import zio.http.HttpAppMiddleware.basicAuth -import zio.logging.internal.ReconfigurableLogger import zio.logging.{ ConfigurableLogger, ConsoleLoggerConfig, LogAnnotation, LogFilter, LoggerConfigurer } import zio.{ ExitCode, Runtime, Scope, ZIO, ZIOAppDefault, _ } import zio.http._ @@ -26,48 +25,6 @@ import java.util.UUID object LoggerReconfigureApp extends ZIOAppDefault { - class Configurer(logger: ReconfigurableLogger[_, _, LogFilter.LogLevelByNameConfig]) extends LoggerConfigurer { - - val rootName = "root" - - override def getLoggerConfigs(): ZIO[Any, Throwable, List[LoggerConfigurer.LoggerConfig]] = - ZIO.attempt { - val currentConfig = logger.config - - LoggerConfigurer.LoggerConfig(rootName, currentConfig.rootLevel) :: currentConfig.mappings - .map(LoggerConfigurer.LoggerConfig.tupled) - .toList - } - - override def getLoggerConfig(name: String): ZIO[Any, Throwable, Option[LoggerConfigurer.LoggerConfig]] = - ZIO.attempt { - val currentConfig = logger.config - - if (name == rootName) { - Some(LoggerConfigurer.LoggerConfig(rootName, currentConfig.rootLevel)) - } else { - currentConfig.mappings.collectFirst { - case (n, l) if n == name => LoggerConfigurer.LoggerConfig(n, l) - } - } - } - - override def setLoggerConfig(name: String, logLevel: LogLevel): ZIO[Any, Throwable, LoggerConfigurer.LoggerConfig] = - ZIO.attempt { - val currentConfig = logger.config - - val newConfig = if (name == rootName) { - currentConfig.withRootLevel(logLevel) - } else { - currentConfig.withMapping(name, logLevel) - } - - logger.reconfigureIfChanged(newConfig) - - LoggerConfigurer.LoggerConfig(name, logLevel) - } - } - def configurableLogger(configPath: String = "logger"): ZLayer[Any, Config.Error, Unit] = ZLayer.scoped { for { @@ -77,24 +34,19 @@ object LoggerReconfigureApp extends ZIOAppDefault { .asInstanceOf[LogFilter.ConfiguredFilter[String, LogFilter.LogLevelByNameConfig]] .config } - logger <- ZIO.succeed { - val logger = ReconfigurableLogger[String, Any, LogFilter.LogLevelByNameConfig]( - filterConfig, - config => { - val filter = LogFilter.logLevelByName(config) - - filter.filter(consoleLoggerConfig.format.toLogger.map { line => - try java.lang.System.out.println(line) - catch { - case t: VirtualMachineError => throw t - case _: Throwable => () - } - }) - } - ) - - ConfigurableLogger(logger, new Configurer(logger)) - } + + logger <- ZIO.succeed { + ConfigurableLogger.make( + consoleLoggerConfig.format.toLogger.map { line => + try java.lang.System.out.println(line) + catch { + case t: VirtualMachineError => throw t + case _: Throwable => () + } + }, + filterConfig + ) + } _ <- ZIO.withLoggerScoped(logger) } yield () From bb6aa44b8829cd62f3f41af346809a1fa27fd0d5 Mon Sep 17 00:00:00 2001 From: Peter Kotula Date: Sat, 17 Jun 2023 20:08:13 +0200 Subject: [PATCH 24/56] ReconfigurableLogger --- .../internal/ReconfigurableLoggerSpec.scala | 4 ++-- .../scala/zio/logging/ConfigurableLogger.scala | 2 +- .../logging/internal/ReconfigurableLogger.scala | 17 ++++++++++------- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/core/jvm/src/test/scala/zio/logging/internal/ReconfigurableLoggerSpec.scala b/core/jvm/src/test/scala/zio/logging/internal/ReconfigurableLoggerSpec.scala index 2be346166..93aa3365e 100644 --- a/core/jvm/src/test/scala/zio/logging/internal/ReconfigurableLoggerSpec.scala +++ b/core/jvm/src/test/scala/zio/logging/internal/ReconfigurableLoggerSpec.scala @@ -15,13 +15,13 @@ object ReconfigurableLoggerSpec extends ZIOSpecDefault { logger <- ReconfigurableLogger .make[Config.Error, String, Any, ConsoleLoggerConfig]( ZIO.config(ConsoleLoggerConfig.config.nested(configPath)), - config => + (config, _) => config.format.toLogger.map { line => zio.Unsafe.unsafe { implicit u => Runtime.default.unsafe.run(queue.offer(line)) } }.filter(config.filter), - 200.millis + Schedule.fixed(200.millis) ) _ <- ZIO.withLoggerScoped(logger) } yield () diff --git a/core/shared/src/main/scala/zio/logging/ConfigurableLogger.scala b/core/shared/src/main/scala/zio/logging/ConfigurableLogger.scala index d37587342..c785ef1ed 100644 --- a/core/shared/src/main/scala/zio/logging/ConfigurableLogger.scala +++ b/core/shared/src/main/scala/zio/logging/ConfigurableLogger.scala @@ -92,7 +92,7 @@ object ConfigurableLogger { val reconfigurableLogger = ReconfigurableLogger[Message, Option[Output], LogFilter.LogLevelByNameConfig]( filterConfig, - config => { + (config, _) => { val filter = LogFilter.logLevelByName(config) filter.filter(logger) diff --git a/core/shared/src/main/scala/zio/logging/internal/ReconfigurableLogger.scala b/core/shared/src/main/scala/zio/logging/internal/ReconfigurableLogger.scala index 7a8ba4dab..32d5b110d 100644 --- a/core/shared/src/main/scala/zio/logging/internal/ReconfigurableLogger.scala +++ b/core/shared/src/main/scala/zio/logging/internal/ReconfigurableLogger.scala @@ -33,27 +33,30 @@ private[logging] object ReconfigurableLogger { def apply[M, O, C: Equal]( initialConfig: C, - makeLogger: C => ZLogger[M, O] + makeLogger: (C, Option[ZLogger[M, O]]) => ZLogger[M, O] ): ReconfigurableLogger[M, O, C] = new ReconfigurableLogger[M, O, C] { private val configuredLogger: AtomicReference[(C, ZLogger[M, O])] = { - val logger = makeLogger(initialConfig) + val logger = makeLogger(initialConfig, None) new AtomicReference[(C, ZLogger[M, O])]((initialConfig, logger)) } override def config: C = configuredLogger.get()._1 override def reconfigureIfChanged(config: C): Boolean = { - val currentConfig = configuredLogger.get()._1 + val (currentConfig, currentLogger) = configuredLogger.get() if (currentConfig !== config) { reconfigure(config) + val logger = makeLogger(config, Some(currentLogger)) + configuredLogger.set((config, logger)) true } else false } override def reconfigure(config: C): Unit = { - val logger = makeLogger(config) + val currentLogger = configuredLogger.get()._2 + val logger = makeLogger(config, Some(currentLogger)) configuredLogger.set((config, logger)) } @@ -72,15 +75,15 @@ private[logging] object ReconfigurableLogger { def make[E, M, O, C: Equal]( loadConfig: => ZIO[Any, E, C], - makeLogger: C => ZLogger[M, O], - interval: Duration = 10.seconds + makeLogger: (C, Option[ZLogger[M, O]]) => ZLogger[M, O], + updateLogger: Schedule[Any, Any, Any] = Schedule.fixed(10.seconds) ): ZIO[Scope, E, ReconfigurableLogger[M, O, C]] = for { config <- loadConfig logger = ReconfigurableLogger[M, O, C](config, makeLogger) _ <- loadConfig.map { newConfig => logger.reconfigureIfChanged(newConfig) - }.scheduleFork(Schedule.fixed(interval)) + }.scheduleFork(updateLogger) } yield logger } From 929daa26a510409a5fba6fcb6b18e53dbc8c973f Mon Sep 17 00:00:00 2001 From: Peter Kotula Date: Mon, 19 Jun 2023 23:24:00 +0200 Subject: [PATCH 25/56] http api - wip --- .../scala/zio/logging/api/http/ApiDomain.scala | 4 ++-- .../scala/zio/logging/api/http/ApiEndpoints.scala | 14 ++++---------- .../zio/logging/api/http/ApiHandlersSpec.scala | 6 +++--- .../scala/zio/logging/ConfigurableLogger.scala | 12 ++++++------ 4 files changed, 15 insertions(+), 21 deletions(-) diff --git a/api-http/src/main/scala/zio/logging/api/http/ApiDomain.scala b/api-http/src/main/scala/zio/logging/api/http/ApiDomain.scala index 47b449bd6..0f611eacb 100644 --- a/api-http/src/main/scala/zio/logging/api/http/ApiDomain.scala +++ b/api-http/src/main/scala/zio/logging/api/http/ApiDomain.scala @@ -43,13 +43,13 @@ object ApiDomain { .transformOrFail[LogLevel](v => labelToLevel.get(v).toRight("Failed"), v => levelToLabel.get(v).toRight("Failed")) } - final case class LoggerConfig(name: String, logLevel: LogLevel) + final case class LoggerConfig(name: String, level: LogLevel) object LoggerConfig { implicit val schema: Schema[LoggerConfig] = DeriveSchema.gen[LoggerConfig] def from(value: LoggerConfigurer.LoggerConfig): LoggerConfig = - LoggerConfig(value.name, value.logLevel) + LoggerConfig(value.name, value.level) } } diff --git a/api-http/src/main/scala/zio/logging/api/http/ApiEndpoints.scala b/api-http/src/main/scala/zio/logging/api/http/ApiEndpoints.scala index da36373a8..945213a20 100644 --- a/api-http/src/main/scala/zio/logging/api/http/ApiEndpoints.scala +++ b/api-http/src/main/scala/zio/logging/api/http/ApiEndpoints.scala @@ -34,14 +34,11 @@ object ApiEndpoints { def getLoggerConfigs( rootPath: Seq[String] = Seq.empty - ): Endpoint[Unit, ApiDomain.Error, List[ApiDomain.LoggerConfig], None] = + ): Endpoint[Unit, ApiDomain.Error.Internal, List[ApiDomain.LoggerConfig], None] = Endpoint .get(rootPathCodec(rootPath) / literal("logger")) .out[List[ApiDomain.LoggerConfig]] - .outErrors[ApiDomain.Error]( - HttpCodec.error[ApiDomain.Error.Internal](Status.InternalServerError), - HttpCodec.error[ApiDomain.Error.NotFound](Status.NotFound) - ) + .outError[ApiDomain.Error.Internal](Status.InternalServerError) def getLoggerConfig( rootPath: Seq[String] = Seq.empty @@ -56,15 +53,12 @@ object ApiEndpoints { def setLoggerConfig( rootPath: Seq[String] = Seq.empty - ): Endpoint[(String, LogLevel), ApiDomain.Error, ApiDomain.LoggerConfig, None] = + ): Endpoint[(String, LogLevel), ApiDomain.Error.Internal, ApiDomain.LoggerConfig, None] = Endpoint .put(rootPathCodec(rootPath) / literal("logger") / string("name")) .in[LogLevel] .out[ApiDomain.LoggerConfig] - .outErrors[ApiDomain.Error]( - HttpCodec.error[ApiDomain.Error.Internal](Status.InternalServerError), - HttpCodec.error[ApiDomain.Error.NotFound](Status.NotFound) - ) + .outError[ApiDomain.Error.Internal](Status.InternalServerError) def doc(rootPath: Seq[String] = Seq.empty): Doc = getLoggerConfigs(rootPath).doc + getLoggerConfig(rootPath).doc + setLoggerConfig(rootPath).doc diff --git a/api-http/src/test/scala/zio/logging/api/http/ApiHandlersSpec.scala b/api-http/src/test/scala/zio/logging/api/http/ApiHandlersSpec.scala index 704aa2a2e..06e02ca74 100644 --- a/api-http/src/test/scala/zio/logging/api/http/ApiHandlersSpec.scala +++ b/api-http/src/test/scala/zio/logging/api/http/ApiHandlersSpec.scala @@ -20,10 +20,10 @@ object ApiHandlersSpec extends ZIOSpecDefault { ZIO.succeed(Some(LoggerConfigurer.LoggerConfig(name, LogLevel.Info))) override def setLoggerConfig( - name: String, - logLevel: LogLevel + name: String, + level: LogLevel ): ZIO[Any, Throwable, LoggerConfigurer.LoggerConfig] = - ZIO.succeed(LoggerConfigurer.LoggerConfig(name, logLevel)) + ZIO.succeed(LoggerConfigurer.LoggerConfig(name, level)) } } diff --git a/core/shared/src/main/scala/zio/logging/ConfigurableLogger.scala b/core/shared/src/main/scala/zio/logging/ConfigurableLogger.scala index c785ef1ed..51d00d3d1 100644 --- a/core/shared/src/main/scala/zio/logging/ConfigurableLogger.scala +++ b/core/shared/src/main/scala/zio/logging/ConfigurableLogger.scala @@ -24,12 +24,12 @@ trait LoggerConfigurer { def getLoggerConfig(name: String): ZIO[Any, Throwable, Option[LoggerConfigurer.LoggerConfig]] - def setLoggerConfig(name: String, logLevel: LogLevel): ZIO[Any, Throwable, LoggerConfigurer.LoggerConfig] + def setLoggerConfig(name: String, level: LogLevel): ZIO[Any, Throwable, LoggerConfigurer.LoggerConfig] } object LoggerConfigurer { - final case class LoggerConfig(name: String, logLevel: LogLevel) + final case class LoggerConfig(name: String, level: LogLevel) def getLoggerConfigs(): ZIO[LoggerConfigurer, Throwable, List[LoggerConfigurer.LoggerConfig]] = ZIO.serviceWithZIO[LoggerConfigurer](_.getLoggerConfigs()) @@ -144,19 +144,19 @@ object ConfigurableLogger { } } - override def setLoggerConfig(name: String, logLevel: LogLevel): ZIO[Any, Throwable, LoggerConfigurer.LoggerConfig] = + override def setLoggerConfig(name: String, level: LogLevel): ZIO[Any, Throwable, LoggerConfigurer.LoggerConfig] = ZIO.attempt { val currentConfig = logger.config val newConfig = if (name == rootName) { - currentConfig.withRootLevel(logLevel) + currentConfig.withRootLevel(level) } else { - currentConfig.withMapping(name, logLevel) + currentConfig.withMapping(name, level) } logger.reconfigureIfChanged(newConfig) - LoggerConfigurer.LoggerConfig(name, logLevel) + LoggerConfigurer.LoggerConfig(name, level) } } From 4c9953e99dda4c83b4bf46ab12db5925e0b98fed Mon Sep 17 00:00:00 2001 From: Peter Kotula Date: Tue, 20 Jun 2023 13:57:45 +0200 Subject: [PATCH 26/56] fmt --- .../scala/zio/logging/api/http/ApiEndpoints.scala | 2 +- .../scala/zio/logging/api/http/ApiHandlers.scala | 15 ++++++++------- .../zio/logging/api/http/ApiHandlersSpec.scala | 4 ++-- .../logging/example/LoggerReconfigureApp.scala | 9 +++++---- 4 files changed, 16 insertions(+), 14 deletions(-) diff --git a/api-http/src/main/scala/zio/logging/api/http/ApiEndpoints.scala b/api-http/src/main/scala/zio/logging/api/http/ApiEndpoints.scala index 945213a20..4c1f2aa86 100644 --- a/api-http/src/main/scala/zio/logging/api/http/ApiEndpoints.scala +++ b/api-http/src/main/scala/zio/logging/api/http/ApiEndpoints.scala @@ -17,8 +17,8 @@ package zio.logging.api.http import zio._ import zio.http._ -import zio.http.codec.{ Doc, HttpCodec, PathCodec } import zio.http.codec.PathCodec.{ literal, string } +import zio.http.codec.{ Doc, HttpCodec, PathCodec } import zio.http.endpoint.EndpointMiddleware.None import zio.http.endpoint._ diff --git a/api-http/src/main/scala/zio/logging/api/http/ApiHandlers.scala b/api-http/src/main/scala/zio/logging/api/http/ApiHandlers.scala index c7d1f9bc2..e33524986 100644 --- a/api-http/src/main/scala/zio/logging/api/http/ApiHandlers.scala +++ b/api-http/src/main/scala/zio/logging/api/http/ApiHandlers.scala @@ -19,37 +19,38 @@ import zio.ZIO import zio.http.endpoint.EndpointMiddleware.None import zio.http.endpoint.Routes import zio.logging.LoggerConfigurer +import zio.logging.api.http.ApiDomain.Error object ApiHandlers { - def getLoggerConfigs(rootPath: Seq[String] = Seq.empty) = + def getLoggerConfigs(rootPath: Seq[String] = Seq.empty): Routes[LoggerConfigurer, Error.Internal, None] = ApiEndpoints .getLoggerConfigs(rootPath) .implement(_ => LoggerConfigurer .getLoggerConfigs() .map(_.map(ApiDomain.LoggerConfig.from)) - .mapError(_ => ApiDomain.Error.Internal()) + .mapError(_ => Error.Internal()) ) - def getLoggerConfig(rootPath: Seq[String] = Seq.empty) = + def getLoggerConfig(rootPath: Seq[String] = Seq.empty): Routes[LoggerConfigurer, Error, None] = ApiEndpoints .getLoggerConfig(rootPath) .implement { name => - LoggerConfigurer.getLoggerConfig(name).mapError(_ => ApiDomain.Error.Internal()).flatMap { + LoggerConfigurer.getLoggerConfig(name).mapError(_ => Error.Internal()).flatMap { case Some(r) => ZIO.succeed(ApiDomain.LoggerConfig.from(r)) - case _ => ZIO.fail(ApiDomain.Error.NotFound()) + case _ => ZIO.fail(Error.NotFound()) } } - def setLoggerConfigs(rootPath: Seq[String] = Seq.empty) = + def setLoggerConfigs(rootPath: Seq[String] = Seq.empty): Routes[LoggerConfigurer, Error.Internal, None] = ApiEndpoints .setLoggerConfig(rootPath) .implement { case (name, logLevel) => LoggerConfigurer .setLoggerConfig(name, logLevel) .map(ApiDomain.LoggerConfig.from) - .mapError(_ => ApiDomain.Error.Internal()) + .mapError(_ => Error.Internal()) } def routes(rootPath: Seq[String] = Seq.empty): Routes[LoggerConfigurer, ApiDomain.Error, None] = diff --git a/api-http/src/test/scala/zio/logging/api/http/ApiHandlersSpec.scala b/api-http/src/test/scala/zio/logging/api/http/ApiHandlersSpec.scala index 06e02ca74..8c99d8cfc 100644 --- a/api-http/src/test/scala/zio/logging/api/http/ApiHandlersSpec.scala +++ b/api-http/src/test/scala/zio/logging/api/http/ApiHandlersSpec.scala @@ -20,8 +20,8 @@ object ApiHandlersSpec extends ZIOSpecDefault { ZIO.succeed(Some(LoggerConfigurer.LoggerConfig(name, LogLevel.Info))) override def setLoggerConfig( - name: String, - level: LogLevel + name: String, + level: LogLevel ): ZIO[Any, Throwable, LoggerConfigurer.LoggerConfig] = ZIO.succeed(LoggerConfigurer.LoggerConfig(name, level)) } diff --git a/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala b/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala index 553466834..7a512710c 100644 --- a/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala +++ b/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala @@ -16,10 +16,10 @@ package zio.logging.example import zio.http.HttpAppMiddleware.basicAuth -import zio.logging.{ ConfigurableLogger, ConsoleLoggerConfig, LogAnnotation, LogFilter, LoggerConfigurer } -import zio.{ ExitCode, Runtime, Scope, ZIO, ZIOAppDefault, _ } import zio.http._ import zio.logging.api.http.ApiHandlers +import zio.logging.{ ConfigurableLogger, ConsoleLoggerConfig, LogAnnotation, LogFilter, LoggerConfigurer } +import zio.{ ExitCode, Runtime, Scope, ZIO, ZIOAppDefault, _ } import java.util.UUID @@ -67,7 +67,7 @@ object LoggerReconfigureApp extends ZIOAppDefault { override val bootstrap: ZLayer[Any, Config.Error, Unit] = Runtime.removeDefaultLoggers >>> Runtime.setConfigProvider(configProvider) >>> configurableLogger() - def exec() = + def exec(): ZIO[Any, Nothing, Unit] = for { ok <- Random.nextBoolean traceId <- ZIO.succeed(UUID.randomUUID()) @@ -84,7 +84,8 @@ object LoggerReconfigureApp extends ZIOAppDefault { _ <- ZIO.logDebug("Done") @@ LogAnnotation.TraceId(traceId) } yield () - val httpApp = ApiHandlers.routes("example" :: Nil).toApp[LoggerConfigurer] @@ basicAuth("admin", "admin") + val httpApp: Http[LoggerConfigurer, Response, Request, Response] = + ApiHandlers.routes("example" :: Nil).toApp[LoggerConfigurer] @@ basicAuth("admin", "admin") override def run: ZIO[Scope, Any, ExitCode] = (for { From a8e0cfb253224a0a05339042c96514939901e8ac Mon Sep 17 00:00:00 2001 From: Peter Kotula Date: Tue, 20 Jun 2023 14:35:51 +0200 Subject: [PATCH 27/56] fix --- .../scala/zio/logging/api/http/ApiEndpointsSpec.scala | 5 +++-- .../test/scala/zio/logging/api/http/ApiHandlersSpec.scala | 8 ++++---- .../src/main/scala/zio/logging/ConfigurableLogger.scala | 6 +++--- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/api-http/src/test/scala/zio/logging/api/http/ApiEndpointsSpec.scala b/api-http/src/test/scala/zio/logging/api/http/ApiEndpointsSpec.scala index 7291bfb6d..a80084618 100644 --- a/api-http/src/test/scala/zio/logging/api/http/ApiEndpointsSpec.scala +++ b/api-http/src/test/scala/zio/logging/api/http/ApiEndpointsSpec.scala @@ -1,13 +1,14 @@ package zio.logging.api.http import zio.LogLevel -import zio.http.codec._ import zio.http.codec.PathCodec.literal +import zio.http.codec._ import zio.test._ +import zio.Scope object ApiEndpointsSpec extends ZIOSpecDefault { - def spec = suite("ApiEndpointsSpec")( + def spec: Spec[Environment with TestEnvironment with Scope,Any] = suite("ApiEndpointsSpec")( test("rootPathCodec") { def testRootPathCodec(rootPath: Seq[String], expected: PathCodec[Unit]) = assertTrue(ApiEndpoints.rootPathCodec(rootPath).encodeRequest(()).url == expected.encodeRequest(()).url) diff --git a/api-http/src/test/scala/zio/logging/api/http/ApiHandlersSpec.scala b/api-http/src/test/scala/zio/logging/api/http/ApiHandlersSpec.scala index 8c99d8cfc..8d3797f4e 100644 --- a/api-http/src/test/scala/zio/logging/api/http/ApiHandlersSpec.scala +++ b/api-http/src/test/scala/zio/logging/api/http/ApiHandlersSpec.scala @@ -1,15 +1,15 @@ package zio.logging.api.http -import zio.{ ZIO, ZLayer } import zio.http._ import zio.http.codec._ -import zio.LogLevel import zio.logging.LoggerConfigurer import zio.test._ +import zio.{LogLevel, ZIO, ZLayer} +import zio.ULayer object ApiHandlersSpec extends ZIOSpecDefault { - val loggerConfigurer = ZLayer.succeed { + val loggerConfigurer: ULayer[LoggerConfigurer] = ZLayer.succeed { new LoggerConfigurer { override def getLoggerConfigs(): ZIO[Any, Throwable, List[LoggerConfigurer.LoggerConfig]] = ZIO.succeed(LoggerConfigurer.LoggerConfig("root", LogLevel.Info) :: Nil) @@ -27,7 +27,7 @@ object ApiHandlersSpec extends ZIOSpecDefault { } } - def spec = suite("ApiHandlersSpec")( + def spec: Spec[Any,Serializable] = suite("ApiHandlersSpec")( test("get all") { val routes = ApiHandlers.routes("example" :: Nil) diff --git a/core/shared/src/main/scala/zio/logging/ConfigurableLogger.scala b/core/shared/src/main/scala/zio/logging/ConfigurableLogger.scala index 51d00d3d1..924d9a17c 100644 --- a/core/shared/src/main/scala/zio/logging/ConfigurableLogger.scala +++ b/core/shared/src/main/scala/zio/logging/ConfigurableLogger.scala @@ -126,9 +126,9 @@ object ConfigurableLogger { ZIO.attempt { val currentConfig = logger.config - LoggerConfigurer.LoggerConfig(rootName, currentConfig.rootLevel) :: currentConfig.mappings - .map(LoggerConfigurer.LoggerConfig.tupled) - .toList + LoggerConfigurer.LoggerConfig(rootName, currentConfig.rootLevel) :: currentConfig.mappings.map { case (n, l) => + LoggerConfigurer.LoggerConfig(n, l) + }.toList } override def getLoggerConfig(name: String): ZIO[Any, Throwable, Option[LoggerConfigurer.LoggerConfig]] = From f9dd304dc495c3e2d356eede2c0a4b4a84fcff1f Mon Sep 17 00:00:00 2001 From: Peter Kotula Date: Wed, 21 Jun 2023 22:24:48 +0200 Subject: [PATCH 28/56] LoggerLayers - wip --- .../main/scala/zio/logging/LoggerLayers.scala | 214 ++++++++++++++++++ .../main/scala/zio/logging/MetricLogger.scala | 38 ++++ .../example/LoggerReconfigureApp.scala | 4 +- 3 files changed, 253 insertions(+), 3 deletions(-) create mode 100644 core/shared/src/main/scala/zio/logging/LoggerLayers.scala create mode 100644 core/shared/src/main/scala/zio/logging/MetricLogger.scala diff --git a/core/shared/src/main/scala/zio/logging/LoggerLayers.scala b/core/shared/src/main/scala/zio/logging/LoggerLayers.scala new file mode 100644 index 000000000..a55eb2b0b --- /dev/null +++ b/core/shared/src/main/scala/zio/logging/LoggerLayers.scala @@ -0,0 +1,214 @@ +/* + * 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 + +import zio.Tag +import zio.{ Queue, Runtime, Scope, UIO, ULayer, ZIO, ZLayer, ZLogger } +import zio.metrics.Metric + +import java.io.PrintStream +import java.nio.charset.Charset +import java.nio.file.Path + +object LoggerLayers { + + def makeConsoleErrLogger(config: ConsoleLoggerConfig): ULayer[ZLogger[String, Any]] = + makePrintStreamLogger(config.format.toLogger, java.lang.System.err, config.filter) + + def makeConsoleErrJsonLogger(config: ConsoleLoggerConfig): ULayer[ZLogger[String, Any]] = + makePrintStreamLogger(config.format.toJsonLogger, java.lang.System.err, config.filter) + + def makeConsoleLogger(config: ConsoleLoggerConfig): ULayer[ZLogger[String, Any]] = + makePrintStreamLogger(config.format.toLogger, java.lang.System.out, config.filter) + + def makeConsoleJsonLogger(config: ConsoleLoggerConfig): ULayer[ZLogger[String, Any]] = + makePrintStreamLogger(config.format.toJsonLogger, java.lang.System.out, config.filter) + + def makePrintStreamLogger( + logger: ZLogger[String, String], + stream: PrintStream, + logFilter: LogFilter[String] + ): ULayer[ZLogger[String, Any]] = ZLayer.succeed(printStreamLogger(logger, stream, logFilter)) + + private def printStreamLogger( + logger: ZLogger[String, String], + stream: PrintStream, + logFilter: LogFilter[String] + ): ZLogger[String, Any] = { + val stringLogger = logFilter.filter(logger.map { line => + try stream.println(line) + catch { + case t: VirtualMachineError => throw t + case _: Throwable => () + } + }) + stringLogger + } + + def makeFileAsyncJsonLogger( + config: FileLoggerConfig + ): ZLayer[Scope, Nothing, ZLogger[String, Any]] = makeFileAsyncLogger( + config.destination, + config.format.toJsonLogger, + config.filter, + config.charset, + config.autoFlushBatchSize, + config.bufferedIOSize, + config.rollingPolicy + ) + + def makeFileAsyncLogger( + config: FileLoggerConfig + ): ZLayer[Scope, Nothing, ZLogger[String, Any]] = + makeFileAsyncLogger( + config.destination, + config.format.toLogger, + config.filter, + config.charset, + config.autoFlushBatchSize, + config.bufferedIOSize, + config.rollingPolicy + ) + + def makeFileAsyncLogger( + destination: Path, + logger: ZLogger[String, String], + logFilter: LogFilter[String], + charset: Charset, + autoFlushBatchSize: Int, + bufferedIOSize: Option[Int], + rollingPolicy: Option[FileLoggerConfig.FileRollingPolicy] + ): ZLayer[Scope, Nothing, ZLogger[String, Any]] = ZLayer.fromZIO { + for { + queue <- Queue.bounded[UIO[Any]](1000) + _ <- queue.take.flatMap(task => task.ignore).forever.forkScoped + } yield fileWriterAsyncLogger( + destination, + logger, + logFilter, + charset, + autoFlushBatchSize, + bufferedIOSize, + queue, + rollingPolicy + ) + } + + private def fileWriterAsyncLogger( + destination: Path, + logger: ZLogger[String, String], + logFilter: LogFilter[String], + charset: Charset, + autoFlushBatchSize: Int, + bufferedIOSize: Option[Int], + queue: Queue[UIO[Any]], + rollingPolicy: Option[FileLoggerConfig.FileRollingPolicy] + ): ZLogger[String, Any] = { + val logWriter = + new zio.logging.internal.FileWriter(destination, charset, autoFlushBatchSize, bufferedIOSize, rollingPolicy) + + val stringLogger: ZLogger[String, Any] = logFilter.filter(logger.map { (line: String) => + zio.Unsafe.unsafe { implicit u => + Runtime.default.unsafe.run(queue.offer(ZIO.succeed { + try logWriter.writeln(line) + catch { + case t: VirtualMachineError => throw t + case _: Throwable => () + } + })) + } + }) + stringLogger + } + + def makeFileJsonLogger(config: FileLoggerConfig): ULayer[ZLogger[String, Any]] = + makeFileLogger( + config.destination, + config.format.toJsonLogger, + config.filter, + config.charset, + config.autoFlushBatchSize, + config.bufferedIOSize, + config.rollingPolicy + ) + + def makeFileLogger(config: FileLoggerConfig): ULayer[ZLogger[String, Any]] = + makeFileLogger( + config.destination, + config.format.toLogger, + config.filter, + config.charset, + config.autoFlushBatchSize, + config.bufferedIOSize, + config.rollingPolicy + ) + + def makeFileLogger( + destination: Path, + logger: ZLogger[String, String], + logFilter: LogFilter[String], + charset: Charset, + autoFlushBatchSize: Int, + bufferedIOSize: Option[Int], + rollingPolicy: Option[FileLoggerConfig.FileRollingPolicy] + ): ULayer[ZLogger[String, Any]] = + ZLayer.succeed( + fileWriterLogger(destination, logger, logFilter, charset, autoFlushBatchSize, bufferedIOSize, rollingPolicy) + ) + + private def fileWriterLogger( + destination: Path, + logger: ZLogger[String, String], + logFilter: LogFilter[String], + charset: Charset, + autoFlushBatchSize: Int, + bufferedIOSize: Option[Int], + rollingPolicy: Option[FileLoggerConfig.FileRollingPolicy] + ): ZLogger[String, Any] = { + val logWriter = + new zio.logging.internal.FileWriter(destination, charset, autoFlushBatchSize, bufferedIOSize, rollingPolicy) + + val stringLogger: ZLogger[String, Any] = logFilter.filter(logger.map { (line: String) => + try logWriter.writeln(line) + catch { + case t: VirtualMachineError => throw t + case _: Throwable => () + } + }) + + stringLogger + } + + def makeMetricLogger(counter: Metric.Counter[Long], logLevelLabel: String): ULayer[MetricLogger] = + ZLayer.succeed(MetricLogger(counter, logLevelLabel)) + + implicit final class ZLoggerLayerOps[-RIn, +E, ROut <: ZLogger[String, Any]: Tag]( + private val self: ZLayer[RIn, E, ROut] + ) { + + def install: ZLayer[RIn, E, Unit] = + self.flatMap { env => + ZLayer.scoped { + ZIO.withLoggerScoped(env.get[ROut]) + } + } + + def installScoped: ZLayer[Scope with RIn, E, Unit] = + self.flatMap { env => + ZLayer.fromZIO(ZIO.withLoggerScoped(env.get[ROut])) + } + } +} diff --git a/core/shared/src/main/scala/zio/logging/MetricLogger.scala b/core/shared/src/main/scala/zio/logging/MetricLogger.scala new file mode 100644 index 000000000..9828fb26e --- /dev/null +++ b/core/shared/src/main/scala/zio/logging/MetricLogger.scala @@ -0,0 +1,38 @@ +/* + * 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 + +import zio.{ Cause, FiberId, FiberRef, FiberRefs, LogLevel, LogSpan, Trace, Unsafe, ZLogger } +import zio.metrics.{ Metric, MetricLabel } + +final case class MetricLogger(counter: Metric.Counter[Long], logLevelLabel: String) extends 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 tags = context.get(FiberRef.currentTags).getOrElse(Set.empty) + counter.unsafe.update(1, tags + MetricLabel(logLevelLabel, logLevel.label.toLowerCase))(Unsafe.unsafe) + () + } + +} diff --git a/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala b/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala index 7a512710c..0ec0c863e 100644 --- a/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala +++ b/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala @@ -18,7 +18,7 @@ package zio.logging.example import zio.http.HttpAppMiddleware.basicAuth import zio.http._ import zio.logging.api.http.ApiHandlers -import zio.logging.{ ConfigurableLogger, ConsoleLoggerConfig, LogAnnotation, LogFilter, LoggerConfigurer } +import zio.logging.{ ConfigurableLogger, ConsoleLoggerConfig, LogAnnotation, LogFilter, LoggerConfigurer, LoggerLayers } import zio.{ ExitCode, Runtime, Scope, ZIO, ZIOAppDefault, _ } import java.util.UUID @@ -101,8 +101,6 @@ object LoggerReconfigureApp extends ZIOAppDefault { curl -u "admin:admin" 'http://localhost:8080/example/logger/root' - curl -u "admin:admin" 'http://localhost:8080/example/logger/root' - curl -u "admin:admin" --location --request PUT 'http://localhost:8080/example/logger/root' --header 'Content-Type: application/json' --data-raw '"WARN"' curl -u "admin:admin" --location --request PUT 'http://localhost:8080/example/logger/zio.logging.example' --header 'Content-Type: application/json' --data-raw '"WARN"' From f9cc5852157b54c93360097d47fa2d055a80df18 Mon Sep 17 00:00:00 2001 From: Peter Kotula Date: Thu, 22 Jun 2023 19:04:17 +0200 Subject: [PATCH 29/56] LoggerLayers - wip --- .../logging/api/http/ApiEndpointsSpec.scala | 2 +- .../logging/api/http/ApiHandlersSpec.scala | 4 +- .../internal/ReconfigurableLoggerSpec.scala | 2 +- .../zio/logging/ConsoleLoggerConfig.scala | 9 ++- .../scala/zio/logging/FileLoggerConfig.scala | 6 ++ .../main/scala/zio/logging/LoggerLayers.scala | 75 ++++++++++++------- .../src/main/scala/zio/logging/package.scala | 16 ++-- .../example/LoggerReconfigureApp.scala | 41 ++++------ 8 files changed, 88 insertions(+), 67 deletions(-) diff --git a/api-http/src/test/scala/zio/logging/api/http/ApiEndpointsSpec.scala b/api-http/src/test/scala/zio/logging/api/http/ApiEndpointsSpec.scala index a80084618..22aec416d 100644 --- a/api-http/src/test/scala/zio/logging/api/http/ApiEndpointsSpec.scala +++ b/api-http/src/test/scala/zio/logging/api/http/ApiEndpointsSpec.scala @@ -8,7 +8,7 @@ import zio.Scope object ApiEndpointsSpec extends ZIOSpecDefault { - def spec: Spec[Environment with TestEnvironment with Scope,Any] = suite("ApiEndpointsSpec")( + def spec: Spec[Environment with TestEnvironment with Scope, Any] = suite("ApiEndpointsSpec")( test("rootPathCodec") { def testRootPathCodec(rootPath: Seq[String], expected: PathCodec[Unit]) = assertTrue(ApiEndpoints.rootPathCodec(rootPath).encodeRequest(()).url == expected.encodeRequest(()).url) diff --git a/api-http/src/test/scala/zio/logging/api/http/ApiHandlersSpec.scala b/api-http/src/test/scala/zio/logging/api/http/ApiHandlersSpec.scala index 8d3797f4e..a31c45065 100644 --- a/api-http/src/test/scala/zio/logging/api/http/ApiHandlersSpec.scala +++ b/api-http/src/test/scala/zio/logging/api/http/ApiHandlersSpec.scala @@ -4,7 +4,7 @@ import zio.http._ import zio.http.codec._ import zio.logging.LoggerConfigurer import zio.test._ -import zio.{LogLevel, ZIO, ZLayer} +import zio.{ LogLevel, ZIO, ZLayer } import zio.ULayer object ApiHandlersSpec extends ZIOSpecDefault { @@ -27,7 +27,7 @@ object ApiHandlersSpec extends ZIOSpecDefault { } } - def spec: Spec[Any,Serializable] = suite("ApiHandlersSpec")( + def spec: Spec[Any, Serializable] = suite("ApiHandlersSpec")( test("get all") { val routes = ApiHandlers.routes("example" :: Nil) diff --git a/core/jvm/src/test/scala/zio/logging/internal/ReconfigurableLoggerSpec.scala b/core/jvm/src/test/scala/zio/logging/internal/ReconfigurableLoggerSpec.scala index 93aa3365e..30ef00050 100644 --- a/core/jvm/src/test/scala/zio/logging/internal/ReconfigurableLoggerSpec.scala +++ b/core/jvm/src/test/scala/zio/logging/internal/ReconfigurableLoggerSpec.scala @@ -14,7 +14,7 @@ object ReconfigurableLoggerSpec extends ZIOSpecDefault { for { logger <- ReconfigurableLogger .make[Config.Error, String, Any, ConsoleLoggerConfig]( - ZIO.config(ConsoleLoggerConfig.config.nested(configPath)), + ConsoleLoggerConfig.load(configPath), (config, _) => config.format.toLogger.map { line => zio.Unsafe.unsafe { implicit u => diff --git a/core/shared/src/main/scala/zio/logging/ConsoleLoggerConfig.scala b/core/shared/src/main/scala/zio/logging/ConsoleLoggerConfig.scala index d538f9a21..756268ed1 100644 --- a/core/shared/src/main/scala/zio/logging/ConsoleLoggerConfig.scala +++ b/core/shared/src/main/scala/zio/logging/ConsoleLoggerConfig.scala @@ -16,7 +16,7 @@ package zio.logging import zio.prelude._ -import zio.{ Config, LogLevel } +import zio.{ Config, LogLevel, ZIO, ZLayer } final case class ConsoleLoggerConfig(format: LogFormat, filter: LogFilter[String]) @@ -38,4 +38,11 @@ object ConsoleLoggerConfig { implicit val equal: Equal[ConsoleLoggerConfig] = Equal.make { (l, r) => l.format == r.format && l.filter === r.filter } + + def load(configPath: String = "logger"): ZIO[Any, Config.Error, ConsoleLoggerConfig] = + ZIO.config(ConsoleLoggerConfig.config.nested(configPath)) + + def make(configPath: String = "logger"): ZLayer[Any, Config.Error, ConsoleLoggerConfig] = + ZLayer.fromZIO(load(configPath)) + } diff --git a/core/shared/src/main/scala/zio/logging/FileLoggerConfig.scala b/core/shared/src/main/scala/zio/logging/FileLoggerConfig.scala index e173d6ec9..ea4cf3482 100644 --- a/core/shared/src/main/scala/zio/logging/FileLoggerConfig.scala +++ b/core/shared/src/main/scala/zio/logging/FileLoggerConfig.scala @@ -97,4 +97,10 @@ object FileLoggerConfig { l.filter === r.filter } + def load(configPath: String = "logger"): ZIO[Any, Config.Error, FileLoggerConfig] = + ZIO.config(FileLoggerConfig.config.nested(configPath)) + + def make(configPath: String = "logger"): ZLayer[Any, Config.Error, FileLoggerConfig] = + ZLayer.fromZIO(load(configPath)) + } diff --git a/core/shared/src/main/scala/zio/logging/LoggerLayers.scala b/core/shared/src/main/scala/zio/logging/LoggerLayers.scala index a55eb2b0b..325264f1d 100644 --- a/core/shared/src/main/scala/zio/logging/LoggerLayers.scala +++ b/core/shared/src/main/scala/zio/logging/LoggerLayers.scala @@ -26,35 +26,61 @@ import java.nio.file.Path object LoggerLayers { def makeConsoleErrLogger(config: ConsoleLoggerConfig): ULayer[ZLogger[String, Any]] = - makePrintStreamLogger(config.format.toLogger, java.lang.System.err, config.filter) + makeSystemErrLogger(config.format.toLogger).project(logger => FilteredLogger(logger, config.filter)) + + def makeConsoleErrLogger: ZLayer[ConsoleLoggerConfig, Nothing, ZLogger[String, Any]] = + ZLayer.environment[ConsoleLoggerConfig].flatMap { env => + makeConsoleErrLogger(env.get[ConsoleLoggerConfig]) + } def makeConsoleErrJsonLogger(config: ConsoleLoggerConfig): ULayer[ZLogger[String, Any]] = - makePrintStreamLogger(config.format.toJsonLogger, java.lang.System.err, config.filter) + makeSystemErrLogger(config.format.toJsonLogger).project(logger => FilteredLogger(logger, config.filter)) + + def makeConsoleErrJsonLogger: ZLayer[ConsoleLoggerConfig, Nothing, ZLogger[String, Any]] = + ZLayer.environment[ConsoleLoggerConfig].flatMap { env => + makeConsoleErrJsonLogger(env.get[ConsoleLoggerConfig]) + } def makeConsoleLogger(config: ConsoleLoggerConfig): ULayer[ZLogger[String, Any]] = - makePrintStreamLogger(config.format.toLogger, java.lang.System.out, config.filter) + makeSystemOutLogger(config.format.toLogger).project(logger => FilteredLogger(logger, config.filter)) + + def makeConsoleLogger: ZLayer[ConsoleLoggerConfig, Nothing, ZLogger[String, Any]] = + ZLayer.environment[ConsoleLoggerConfig].flatMap { env => + makeConsoleLogger(env.get[ConsoleLoggerConfig]) + } def makeConsoleJsonLogger(config: ConsoleLoggerConfig): ULayer[ZLogger[String, Any]] = - makePrintStreamLogger(config.format.toJsonLogger, java.lang.System.out, config.filter) + makeSystemOutLogger(config.format.toJsonLogger).project(logger => FilteredLogger(logger, config.filter)) + + def makeConsoleJsonLogger: ZLayer[ConsoleLoggerConfig, Nothing, ZLogger[String, Any]] = + ZLayer.environment[ConsoleLoggerConfig].flatMap { env => + makeConsoleJsonLogger(env.get[ConsoleLoggerConfig]) + } + + def makeSystemOutLogger( + logger: ZLogger[String, String] + ): ULayer[ZLogger[String, Any]] = makePrintStreamLogger(logger, java.lang.System.out) + + def makeSystemErrLogger( + logger: ZLogger[String, String] + ): ULayer[ZLogger[String, Any]] = makePrintStreamLogger(logger, java.lang.System.err) def makePrintStreamLogger( logger: ZLogger[String, String], - stream: PrintStream, - logFilter: LogFilter[String] - ): ULayer[ZLogger[String, Any]] = ZLayer.succeed(printStreamLogger(logger, stream, logFilter)) + stream: PrintStream + ): ULayer[ZLogger[String, Any]] = ZLayer.succeed(printStreamLogger(logger, stream)) private def printStreamLogger( logger: ZLogger[String, String], - stream: PrintStream, - logFilter: LogFilter[String] + stream: PrintStream ): ZLogger[String, Any] = { - val stringLogger = logFilter.filter(logger.map { line => + val stringLogger = logger.map { line => try stream.println(line) catch { case t: VirtualMachineError => throw t case _: Throwable => () } - }) + } stringLogger } @@ -63,12 +89,11 @@ object LoggerLayers { ): ZLayer[Scope, Nothing, ZLogger[String, Any]] = makeFileAsyncLogger( config.destination, config.format.toJsonLogger, - config.filter, config.charset, config.autoFlushBatchSize, config.bufferedIOSize, config.rollingPolicy - ) + ).project(logger => FilteredLogger(logger, config.filter)) def makeFileAsyncLogger( config: FileLoggerConfig @@ -76,17 +101,15 @@ object LoggerLayers { makeFileAsyncLogger( config.destination, config.format.toLogger, - config.filter, config.charset, config.autoFlushBatchSize, config.bufferedIOSize, config.rollingPolicy - ) + ).project(logger => FilteredLogger(logger, config.filter)) def makeFileAsyncLogger( destination: Path, logger: ZLogger[String, String], - logFilter: LogFilter[String], charset: Charset, autoFlushBatchSize: Int, bufferedIOSize: Option[Int], @@ -98,7 +121,6 @@ object LoggerLayers { } yield fileWriterAsyncLogger( destination, logger, - logFilter, charset, autoFlushBatchSize, bufferedIOSize, @@ -110,7 +132,6 @@ object LoggerLayers { private def fileWriterAsyncLogger( destination: Path, logger: ZLogger[String, String], - logFilter: LogFilter[String], charset: Charset, autoFlushBatchSize: Int, bufferedIOSize: Option[Int], @@ -120,7 +141,7 @@ object LoggerLayers { val logWriter = new zio.logging.internal.FileWriter(destination, charset, autoFlushBatchSize, bufferedIOSize, rollingPolicy) - val stringLogger: ZLogger[String, Any] = logFilter.filter(logger.map { (line: String) => + val stringLogger: ZLogger[String, Any] = logger.map { (line: String) => zio.Unsafe.unsafe { implicit u => Runtime.default.unsafe.run(queue.offer(ZIO.succeed { try logWriter.writeln(line) @@ -130,7 +151,7 @@ object LoggerLayers { } })) } - }) + } stringLogger } @@ -138,41 +159,37 @@ object LoggerLayers { makeFileLogger( config.destination, config.format.toJsonLogger, - config.filter, config.charset, config.autoFlushBatchSize, config.bufferedIOSize, config.rollingPolicy - ) + ).project(logger => FilteredLogger(logger, config.filter)) def makeFileLogger(config: FileLoggerConfig): ULayer[ZLogger[String, Any]] = makeFileLogger( config.destination, config.format.toLogger, - config.filter, config.charset, config.autoFlushBatchSize, config.bufferedIOSize, config.rollingPolicy - ) + ).project(logger => FilteredLogger(logger, config.filter)) def makeFileLogger( destination: Path, logger: ZLogger[String, String], - logFilter: LogFilter[String], charset: Charset, autoFlushBatchSize: Int, bufferedIOSize: Option[Int], rollingPolicy: Option[FileLoggerConfig.FileRollingPolicy] ): ULayer[ZLogger[String, Any]] = ZLayer.succeed( - fileWriterLogger(destination, logger, logFilter, charset, autoFlushBatchSize, bufferedIOSize, rollingPolicy) + fileWriterLogger(destination, logger, charset, autoFlushBatchSize, bufferedIOSize, rollingPolicy) ) private def fileWriterLogger( destination: Path, logger: ZLogger[String, String], - logFilter: LogFilter[String], charset: Charset, autoFlushBatchSize: Int, bufferedIOSize: Option[Int], @@ -181,13 +198,13 @@ object LoggerLayers { val logWriter = new zio.logging.internal.FileWriter(destination, charset, autoFlushBatchSize, bufferedIOSize, rollingPolicy) - val stringLogger: ZLogger[String, Any] = logFilter.filter(logger.map { (line: String) => + val stringLogger: ZLogger[String, Any] = logger.map { (line: String) => try logWriter.writeln(line) catch { case t: VirtualMachineError => throw t case _: Throwable => () } - }) + } stringLogger } diff --git a/core/shared/src/main/scala/zio/logging/package.scala b/core/shared/src/main/scala/zio/logging/package.scala index d95e879cb..f888e5af6 100644 --- a/core/shared/src/main/scala/zio/logging/package.scala +++ b/core/shared/src/main/scala/zio/logging/package.scala @@ -113,7 +113,7 @@ package object logging { def consoleErrJsonLogger(configPath: String = "logger"): ZLayer[Any, Config.Error, Unit] = ZLayer.scoped { for { - config <- ZIO.config(ConsoleLoggerConfig.config.nested(configPath)) + config <- ConsoleLoggerConfig.load(configPath) _ <- ZIO.withLoggerScoped(makeConsoleErrJsonLogger(config)) } yield () } @@ -121,7 +121,7 @@ package object logging { def consoleErrLogger(configPath: String = "logger"): ZLayer[Any, Config.Error, Unit] = ZLayer.scoped { for { - config <- ZIO.config(ConsoleLoggerConfig.config.nested(configPath)) + config <- ConsoleLoggerConfig.load(configPath) _ <- ZIO.withLoggerScoped(makeConsoleErrLogger(config)) } yield () } @@ -146,7 +146,7 @@ package object logging { def consoleJsonLogger(configPath: String = "logger"): ZLayer[Any, Config.Error, Unit] = ZLayer.scoped { for { - config <- ZIO.config(ConsoleLoggerConfig.config.nested(configPath)) + config <- ConsoleLoggerConfig.load(configPath) _ <- ZIO.withLoggerScoped(makeConsoleJsonLogger(config)) } yield () } @@ -157,7 +157,7 @@ package object logging { def consoleLogger(configPath: String = "logger"): ZLayer[Any, Config.Error, Unit] = ZLayer.scoped { for { - config <- ZIO.config(ConsoleLoggerConfig.config.nested(configPath)) + config <- ConsoleLoggerConfig.load(configPath) _ <- ZIO.withLoggerScoped(makeConsoleLogger(config)) } yield () } @@ -296,7 +296,7 @@ package object logging { def fileAsyncJsonLogger(configPath: String = "logger"): ZLayer[Any, Config.Error, Unit] = ZLayer.scoped { for { - config <- ZIO.config(FileLoggerConfig.config.nested(configPath)) + config <- FileLoggerConfig.load(configPath) _ <- makeFileAsyncJsonLogger(config) } yield () } @@ -307,7 +307,7 @@ package object logging { def fileAsyncLogger(configPath: String = "logger"): ZLayer[Any, Config.Error, Unit] = ZLayer.scoped { for { - config <- ZIO.config(FileLoggerConfig.config.nested(configPath)) + config <- FileLoggerConfig.load(configPath) _ <- makeFileAsyncLogger(config) } yield () } @@ -318,7 +318,7 @@ package object logging { def fileJsonLogger(configPath: String = "logger"): ZLayer[Any, Config.Error, Unit] = ZLayer.scoped { for { - config <- ZIO.config(FileLoggerConfig.config.nested(configPath)) + config <- FileLoggerConfig.load(configPath) _ <- ZIO.withLoggerScoped(makeFileJsonLogger(config)) } yield () } @@ -329,7 +329,7 @@ package object logging { def fileLogger(configPath: String = "logger"): ZLayer[Any, Config.Error, Unit] = ZLayer.scoped { for { - config <- ZIO.config(FileLoggerConfig.config.nested(configPath)) + config <- FileLoggerConfig.load(configPath) _ <- ZIO.withLoggerScoped(makeFileLogger(config)) } yield () } diff --git a/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala b/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala index 0ec0c863e..d7c65621c 100644 --- a/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala +++ b/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala @@ -25,32 +25,23 @@ import java.util.UUID object LoggerReconfigureApp extends ZIOAppDefault { - def configurableLogger(configPath: String = "logger"): ZLayer[Any, Config.Error, Unit] = - ZLayer.scoped { - for { - consoleLoggerConfig <- ZIO.config(ConsoleLoggerConfig.config.nested(configPath)) - filterConfig <- ZIO.succeed { - consoleLoggerConfig.filter - .asInstanceOf[LogFilter.ConfiguredFilter[String, LogFilter.LogLevelByNameConfig]] - .config - } - - logger <- ZIO.succeed { - ConfigurableLogger.make( - consoleLoggerConfig.format.toLogger.map { line => - try java.lang.System.out.println(line) - catch { - case t: VirtualMachineError => throw t - case _: Throwable => () - } - }, - filterConfig - ) - } - - _ <- ZIO.withLoggerScoped(logger) - } yield () + def configurableLogger(configPath: String = "logger") = { + import LoggerLayers._ + + ConsoleLoggerConfig.make(configPath).flatMap { env => + val consoleLoggerConfig = env.get[ConsoleLoggerConfig] + + makeSystemOutLogger( + consoleLoggerConfig.format.toLogger + ).project { logger => + val filterConfig = consoleLoggerConfig.filter + .asInstanceOf[LogFilter.ConfiguredFilter[String, LogFilter.LogLevelByNameConfig]] + .config + + ConfigurableLogger.make(logger, filterConfig) + }.install } + } val logFormat = "%highlight{%timestamp{yyyy-MM-dd'T'HH:mm:ssZ} %fixed{7}{%level} [%fiberId] %name:%line %message %kv{trace_id} %kv{user_id} %cause}" From 89bf86f60b82c63d37afcdb2fdb2927ae83cab34 Mon Sep 17 00:00:00 2001 From: Peter Kotula Date: Thu, 22 Jun 2023 23:33:37 +0200 Subject: [PATCH 30/56] LoggerLayers - wip --- .../main/scala/zio/logging/LoggerLayers.scala | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/core/shared/src/main/scala/zio/logging/LoggerLayers.scala b/core/shared/src/main/scala/zio/logging/LoggerLayers.scala index 325264f1d..48ac564b9 100644 --- a/core/shared/src/main/scala/zio/logging/LoggerLayers.scala +++ b/core/shared/src/main/scala/zio/logging/LoggerLayers.scala @@ -95,6 +95,11 @@ object LoggerLayers { config.rollingPolicy ).project(logger => FilteredLogger(logger, config.filter)) + def makeFileAsyncJsonLogger: ZLayer[FileLoggerConfig with Scope, Nothing, ZLogger[String, Any]] = + ZLayer.environment[FileLoggerConfig].flatMap { env => + makeFileAsyncJsonLogger(env.get[FileLoggerConfig]) + } + def makeFileAsyncLogger( config: FileLoggerConfig ): ZLayer[Scope, Nothing, ZLogger[String, Any]] = @@ -107,6 +112,11 @@ object LoggerLayers { config.rollingPolicy ).project(logger => FilteredLogger(logger, config.filter)) + def makeFileAsyncLogger: ZLayer[FileLoggerConfig with Scope, Nothing, ZLogger[String, Any]] = + ZLayer.environment[FileLoggerConfig].flatMap { env => + makeFileAsyncLogger(env.get[FileLoggerConfig]) + } + def makeFileAsyncLogger( destination: Path, logger: ZLogger[String, String], @@ -165,6 +175,11 @@ object LoggerLayers { config.rollingPolicy ).project(logger => FilteredLogger(logger, config.filter)) + def makeFileJsonLogger: ZLayer[FileLoggerConfig, Nothing, ZLogger[String, Any]] = + ZLayer.environment[FileLoggerConfig].flatMap { env => + makeFileJsonLogger(env.get[FileLoggerConfig]) + } + def makeFileLogger(config: FileLoggerConfig): ULayer[ZLogger[String, Any]] = makeFileLogger( config.destination, @@ -175,6 +190,11 @@ object LoggerLayers { config.rollingPolicy ).project(logger => FilteredLogger(logger, config.filter)) + def makeFileLogger: ZLayer[FileLoggerConfig, Nothing, ZLogger[String, Any]] = + ZLayer.environment[FileLoggerConfig].flatMap { env => + makeFileLogger(env.get[FileLoggerConfig]) + } + def makeFileLogger( destination: Path, logger: ZLogger[String, String], From b5434acc8889d2a77f894acb39e65d12eb7096be Mon Sep 17 00:00:00 2001 From: Peter Kotula Date: Sat, 24 Jun 2023 15:30:29 +0200 Subject: [PATCH 31/56] slf4j and jpl loggers as case class --- .../main/scala/zio/logging/backend/JPL.scala | 53 ++++++++++-------- .../main/scala/zio/logging/slf4j/SLF4J.scala | 54 ++++++++++--------- .../main/scala/zio/logging/slf4j/SLF4J.scala | 47 ++++++++-------- 3 files changed, 84 insertions(+), 70 deletions(-) diff --git a/jpl/src/main/scala/zio/logging/backend/JPL.scala b/jpl/src/main/scala/zio/logging/backend/JPL.scala index e2ced3e11..44d6507b3 100644 --- a/jpl/src/main/scala/zio/logging/backend/JPL.scala +++ b/jpl/src/main/scala/zio/logging/backend/JPL.scala @@ -147,30 +147,37 @@ object JPL { loggerName: Trace => String, getJPLogger: String => System.Logger ): ZLogger[String, Unit] = - 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 jpLoggerName = annotations.getOrElse( - JPL.loggerNameAnnotationKey, - annotations.getOrElse(zio.logging.loggerNameAnnotationKey, loggerName(trace)) - ) - val jpLogger = getJPLogger(jpLoggerName) - if (isLogLevelEnabled(jpLogger, logLevel)) { - val appender = logAppender(jpLogger, logLevel) - - format.unsafeFormat(appender)(trace, fiberId, logLevel, message, cause, context, spans, annotations) - appender.closeLogEntry() - } - () + JplLogger(format, loggerName, getJPLogger) + + private[logging] case class JplLogger( + format: LogFormat, + loggerName: Trace => String, + getJPLogger: String => System.Logger + ) extends 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 jpLoggerName = annotations.getOrElse( + JPL.loggerNameAnnotationKey, + annotations.getOrElse(zio.logging.loggerNameAnnotationKey, loggerName(trace)) + ) + val jpLogger = getJPLogger(jpLoggerName) + if (isLogLevelEnabled(jpLogger, logLevel)) { + val appender = logAppender(jpLogger, logLevel) + + format.unsafeFormat(appender)(trace, fiberId, logLevel, message, cause, context, spans, annotations) + appender.closeLogEntry() } + () } + } } diff --git a/slf4j/src/main/scala/zio/logging/slf4j/SLF4J.scala b/slf4j/src/main/scala/zio/logging/slf4j/SLF4J.scala index e11d56444..01dd0d327 100644 --- a/slf4j/src/main/scala/zio/logging/slf4j/SLF4J.scala +++ b/slf4j/src/main/scala/zio/logging/slf4j/SLF4J.scala @@ -256,32 +256,36 @@ object SLF4J { // 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( - SLF4J.loggerNameAnnotationKey, - annotations.getOrElse(logging.loggerNameAnnotationKey, loggerName(trace)) - ) - val slf4jLogger = LoggerFactory.getLogger(slf4jLoggerName) - val slf4jMarkerName = annotations.get(SLF4J.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() - } - () + Slf4jLogger(format, loggerName) + } + + private[logging] case class Slf4jLogger(format: LogFormat, loggerName: Trace => String) + extends 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( + SLF4J.loggerNameAnnotationKey, + annotations.getOrElse(logging.loggerNameAnnotationKey, loggerName(trace)) + ) + val slf4jLogger = LoggerFactory.getLogger(slf4jLoggerName) + val slf4jMarkerName = annotations.get(SLF4J.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/main/scala/zio/logging/slf4j/SLF4J.scala b/slf4j2/src/main/scala/zio/logging/slf4j/SLF4J.scala index 993b28e93..cbf4fec13 100644 --- a/slf4j2/src/main/scala/zio/logging/slf4j/SLF4J.scala +++ b/slf4j2/src/main/scala/zio/logging/slf4j/SLF4J.scala @@ -186,29 +186,32 @@ object SLF4J { // 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(zio.logging.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() - } - () + Slf4jLogger(format, loggerName) + } + + private[logging] case class Slf4jLogger(format: LogFormat, loggerName: Trace => String) + extends 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(zio.logging.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() } + () } } From 2f9fe72dc7d86e81297302bdca1dc857f644db95 Mon Sep 17 00:00:00 2001 From: Peter Kotula Date: Sat, 24 Jun 2023 22:12:07 +0200 Subject: [PATCH 32/56] LoggerLayers - wip --- .../internal/ReconfigurableLogger2Spec.scala | 78 ++++ .../main/scala/zio/logging/LoggerLayers.scala | 366 +++++++++++------- .../internal/ReconfigurableLogger2.scala | 83 ++++ .../example/LoggerReconfigureApp.scala | 41 +- 4 files changed, 420 insertions(+), 148 deletions(-) create mode 100644 core/jvm/src/test/scala/zio/logging/internal/ReconfigurableLogger2Spec.scala create mode 100644 core/shared/src/main/scala/zio/logging/internal/ReconfigurableLogger2.scala diff --git a/core/jvm/src/test/scala/zio/logging/internal/ReconfigurableLogger2Spec.scala b/core/jvm/src/test/scala/zio/logging/internal/ReconfigurableLogger2Spec.scala new file mode 100644 index 000000000..58be8f2e4 --- /dev/null +++ b/core/jvm/src/test/scala/zio/logging/internal/ReconfigurableLogger2Spec.scala @@ -0,0 +1,78 @@ +package zio.logging.internal + +import zio.logging.{ ConsoleLoggerConfig, _ } +import zio.test._ +import zio.{ Chunk, Config, ConfigProvider, LogLevel, Queue, Runtime, Schedule, ZIO, ZLayer, _ } + +object ReconfigurableLogger2Spec extends ZIOSpecDefault { + + def configuredLogger( + queue: zio.Queue[String], + configPath: String = "logger" + ): ZLayer[Any, Config.Error, Unit] = + ZLayer.scoped { + for { + logger <- ReconfigurableLogger2 + .make[Config.Error, String, Any, ConsoleLoggerConfig]( + ConsoleLoggerConfig.load(configPath), + (config, _) => + ZIO.succeed { + config.format.toLogger.map { line => + zio.Unsafe.unsafe { implicit u => + Runtime.default.unsafe.run(queue.offer(line)) + } + }.filter(config.filter) + }, + Schedule.fixed(200.millis) + ) + _ <- ZIO.withLoggerScoped(logger) + } yield () + } + + val spec: Spec[Environment, Any] = suite("ReconfigurableLogger2")( + test("log with changed config") { + + val initialProperties = Map( + "logger/format" -> "%message", + "logger/filter/rootLevel" -> LogLevel.Info.label, + "logger/filter/mappings/zio.logging.example.LivePingService" -> LogLevel.Debug.label + ) + + for { + _ <- ZIO.foreach(initialProperties) { case (k, v) => + TestSystem.putProperty(k, v).as(k -> v) + } + + queue <- Queue.unbounded[String] + + runTest = + for { + _ <- ZIO.logInfo("info") + _ <- ZIO.logDebug("debug") + elements1 <- queue.takeAll + _ <- TestSystem.putProperty("logger/format", "%level %message") + _ <- ZIO.sleep(500.millis) + _ <- ZIO.logWarning("warn") + _ <- ZIO.logDebug("debug") + elements2 <- queue.takeAll + _ <- TestSystem.putProperty("logger/format", "L: %level M: %message") + _ <- TestSystem.putProperty("logger/filter/rootLevel", LogLevel.Debug.label) + _ <- ZIO.sleep(500.millis) + _ <- ZIO.logDebug("debug") + elements3 <- queue.takeAll + } yield assertTrue( + elements1 == Chunk("info") && elements2 == Chunk("WARN warn") && elements3 == Chunk( + "L: DEBUG M: debug" + ) + ) + + result <- + runTest.provide( + Runtime.removeDefaultLoggers >>> Runtime + .setConfigProvider(ConfigProvider.fromProps("/")) >>> configuredLogger(queue) + ) + } yield result + + } + ) @@ TestAspect.withLiveClock +} diff --git a/core/shared/src/main/scala/zio/logging/LoggerLayers.scala b/core/shared/src/main/scala/zio/logging/LoggerLayers.scala index 48ac564b9..7529b4b47 100644 --- a/core/shared/src/main/scala/zio/logging/LoggerLayers.scala +++ b/core/shared/src/main/scala/zio/logging/LoggerLayers.scala @@ -24,51 +24,88 @@ import java.nio.charset.Charset import java.nio.file.Path object LoggerLayers { - - def makeConsoleErrLogger(config: ConsoleLoggerConfig): ULayer[ZLogger[String, Any]] = - makeSystemErrLogger(config.format.toLogger).project(logger => FilteredLogger(logger, config.filter)) - - def makeConsoleErrLogger: ZLayer[ConsoleLoggerConfig, Nothing, ZLogger[String, Any]] = - ZLayer.environment[ConsoleLoggerConfig].flatMap { env => - makeConsoleErrLogger(env.get[ConsoleLoggerConfig]) - } - - def makeConsoleErrJsonLogger(config: ConsoleLoggerConfig): ULayer[ZLogger[String, Any]] = - makeSystemErrLogger(config.format.toJsonLogger).project(logger => FilteredLogger(logger, config.filter)) - - def makeConsoleErrJsonLogger: ZLayer[ConsoleLoggerConfig, Nothing, ZLogger[String, Any]] = - ZLayer.environment[ConsoleLoggerConfig].flatMap { env => - makeConsoleErrJsonLogger(env.get[ConsoleLoggerConfig]) - } - - def makeConsoleLogger(config: ConsoleLoggerConfig): ULayer[ZLogger[String, Any]] = - makeSystemOutLogger(config.format.toLogger).project(logger => FilteredLogger(logger, config.filter)) - - def makeConsoleLogger: ZLayer[ConsoleLoggerConfig, Nothing, ZLogger[String, Any]] = - ZLayer.environment[ConsoleLoggerConfig].flatMap { env => - makeConsoleLogger(env.get[ConsoleLoggerConfig]) - } - - def makeConsoleJsonLogger(config: ConsoleLoggerConfig): ULayer[ZLogger[String, Any]] = - makeSystemOutLogger(config.format.toJsonLogger).project(logger => FilteredLogger(logger, config.filter)) - - def makeConsoleJsonLogger: ZLayer[ConsoleLoggerConfig, Nothing, ZLogger[String, Any]] = - ZLayer.environment[ConsoleLoggerConfig].flatMap { env => - makeConsoleJsonLogger(env.get[ConsoleLoggerConfig]) - } +// +// def makeConsoleErrLogger(config: ConsoleLoggerConfig): ULayer[ZLogger[String, Any]] = +// makeSystemErrLogger(config.format.toLogger).project(logger => FilteredLogger(logger, config.filter)) +// +// def makeConsoleErrLogger: ZLayer[ConsoleLoggerConfig, Nothing, ZLogger[String, Any]] = +// ZLayer.environment[ConsoleLoggerConfig].flatMap { env => +// makeConsoleErrLogger(env.get[ConsoleLoggerConfig]) +// } +// +// def makeConsoleErrJsonLogger(config: ConsoleLoggerConfig): ULayer[ZLogger[String, Any]] = +// makeSystemErrLogger(config.format.toJsonLogger).project(logger => FilteredLogger(logger, config.filter)) +// +// def makeConsoleErrJsonLogger: ZLayer[ConsoleLoggerConfig, Nothing, ZLogger[String, Any]] = +// ZLayer.environment[ConsoleLoggerConfig].flatMap { env => +// makeConsoleErrJsonLogger(env.get[ConsoleLoggerConfig]) +// } +// +// def makeConsoleLogger(config: ConsoleLoggerConfig): ULayer[ZLogger[String, Any]] = +// makeSystemOutLogger(config.format.toLogger).project(logger => FilteredLogger(logger, config.filter)) +// +// def makeConsoleLogger: ZLayer[ConsoleLoggerConfig, Nothing, ZLogger[String, Any]] = +// ZLayer.environment[ConsoleLoggerConfig].flatMap { env => +// makeConsoleLogger(env.get[ConsoleLoggerConfig]) +// } +// +// def makeConsoleJsonLogger(config: ConsoleLoggerConfig): ULayer[ZLogger[String, Any]] = +// makeSystemOutLogger(config.format.toJsonLogger).project(logger => FilteredLogger(logger, config.filter)) +// +// def makeConsoleJsonLogger: ZLayer[ConsoleLoggerConfig, Nothing, ZLogger[String, Any]] = +// ZLayer.environment[ConsoleLoggerConfig].flatMap { env => +// makeConsoleJsonLogger(env.get[ConsoleLoggerConfig]) +// } +// +// def makeSystemOutLogger( +// logger: ZLogger[String, String] +// ): ULayer[ZLogger[String, Any]] = makePrintStreamLogger(logger, java.lang.System.out) +// +// def makeSystemErrLogger( +// logger: ZLogger[String, String] +// ): ULayer[ZLogger[String, Any]] = makePrintStreamLogger(logger, java.lang.System.err) +// +// def makePrintStreamLogger( +// logger: ZLogger[String, String], +// stream: PrintStream +// ): ULayer[ZLogger[String, Any]] = ZLayer.succeed(printStreamLogger(logger, stream)) + + def makeConsoleErrLogger(config: ConsoleLoggerConfig): ZIO[Any, Nothing, ZLogger[String, Any]] = + makeSystemErrLogger(config.format.toLogger).map(logger => FilteredLogger(logger, config.filter)) + + def makeConsoleErrLogger: ZIO[ConsoleLoggerConfig, Nothing, ZLogger[String, Any]] = + ZIO.serviceWithZIO[ConsoleLoggerConfig](makeConsoleErrLogger(_)) + + def makeConsoleErrJsonLogger(config: ConsoleLoggerConfig): ZIO[Any, Nothing, ZLogger[String, Any]] = + makeSystemErrLogger(config.format.toJsonLogger).map(logger => FilteredLogger(logger, config.filter)) + + def makeConsoleErrJsonLogger: ZIO[ConsoleLoggerConfig, Nothing, ZLogger[String, Any]] = + ZIO.serviceWithZIO[ConsoleLoggerConfig](makeConsoleErrJsonLogger(_)) + + def makeConsoleLogger(config: ConsoleLoggerConfig): ZIO[Any, Nothing, ZLogger[String, Any]] = + makeSystemOutLogger(config.format.toLogger).map(logger => FilteredLogger(logger, config.filter)) + + def makeConsoleLogger: ZIO[ConsoleLoggerConfig, Nothing, ZLogger[String, Any]] = + ZIO.serviceWithZIO[ConsoleLoggerConfig](makeConsoleLogger(_)) + + def makeConsoleJsonLogger(config: ConsoleLoggerConfig): ZIO[Any, Nothing, ZLogger[String, Any]] = + makeSystemOutLogger(config.format.toJsonLogger).map(logger => FilteredLogger(logger, config.filter)) + + def makeConsoleJsonLogger: ZIO[ConsoleLoggerConfig, Nothing, ZLogger[String, Any]] = + ZIO.serviceWithZIO[ConsoleLoggerConfig](makeConsoleJsonLogger(_)) def makeSystemOutLogger( logger: ZLogger[String, String] - ): ULayer[ZLogger[String, Any]] = makePrintStreamLogger(logger, java.lang.System.out) + ): ZIO[Any, Nothing, ZLogger[String, Any]] = makePrintStreamLogger(logger, java.lang.System.out) def makeSystemErrLogger( logger: ZLogger[String, String] - ): ULayer[ZLogger[String, Any]] = makePrintStreamLogger(logger, java.lang.System.err) + ): ZIO[Any, Nothing, ZLogger[String, Any]] = makePrintStreamLogger(logger, java.lang.System.err) def makePrintStreamLogger( logger: ZLogger[String, String], stream: PrintStream - ): ULayer[ZLogger[String, Any]] = ZLayer.succeed(printStreamLogger(logger, stream)) + ): ZIO[Any, Nothing, ZLogger[String, Any]] = ZIO.succeed(printStreamLogger(logger, stream)) private def printStreamLogger( logger: ZLogger[String, String], @@ -83,89 +120,131 @@ object LoggerLayers { } stringLogger } - - def makeFileAsyncJsonLogger( - config: FileLoggerConfig - ): ZLayer[Scope, Nothing, ZLogger[String, Any]] = makeFileAsyncLogger( - config.destination, - config.format.toJsonLogger, - config.charset, - config.autoFlushBatchSize, - config.bufferedIOSize, - config.rollingPolicy - ).project(logger => FilteredLogger(logger, config.filter)) - - def makeFileAsyncJsonLogger: ZLayer[FileLoggerConfig with Scope, Nothing, ZLogger[String, Any]] = - ZLayer.environment[FileLoggerConfig].flatMap { env => - makeFileAsyncJsonLogger(env.get[FileLoggerConfig]) - } - - def makeFileAsyncLogger( - config: FileLoggerConfig - ): ZLayer[Scope, Nothing, ZLogger[String, Any]] = - makeFileAsyncLogger( - config.destination, - config.format.toLogger, - config.charset, - config.autoFlushBatchSize, - config.bufferedIOSize, - config.rollingPolicy - ).project(logger => FilteredLogger(logger, config.filter)) - - def makeFileAsyncLogger: ZLayer[FileLoggerConfig with Scope, Nothing, ZLogger[String, Any]] = - ZLayer.environment[FileLoggerConfig].flatMap { env => - makeFileAsyncLogger(env.get[FileLoggerConfig]) - } - - def makeFileAsyncLogger( - destination: Path, - logger: ZLogger[String, String], - charset: Charset, - autoFlushBatchSize: Int, - bufferedIOSize: Option[Int], - rollingPolicy: Option[FileLoggerConfig.FileRollingPolicy] - ): ZLayer[Scope, Nothing, ZLogger[String, Any]] = ZLayer.fromZIO { - for { - queue <- Queue.bounded[UIO[Any]](1000) - _ <- queue.take.flatMap(task => task.ignore).forever.forkScoped - } yield fileWriterAsyncLogger( - destination, - logger, - charset, - autoFlushBatchSize, - bufferedIOSize, - queue, - rollingPolicy - ) - } - - private def fileWriterAsyncLogger( - destination: Path, - logger: ZLogger[String, String], - charset: Charset, - autoFlushBatchSize: Int, - bufferedIOSize: Option[Int], - queue: Queue[UIO[Any]], - rollingPolicy: Option[FileLoggerConfig.FileRollingPolicy] - ): ZLogger[String, Any] = { - val logWriter = - new zio.logging.internal.FileWriter(destination, charset, autoFlushBatchSize, bufferedIOSize, rollingPolicy) - - val stringLogger: ZLogger[String, Any] = logger.map { (line: String) => - zio.Unsafe.unsafe { implicit u => - Runtime.default.unsafe.run(queue.offer(ZIO.succeed { - try logWriter.writeln(line) - catch { - case t: VirtualMachineError => throw t - case _: Throwable => () - } - })) - } - } - stringLogger - } - - def makeFileJsonLogger(config: FileLoggerConfig): ULayer[ZLogger[String, Any]] = +// +// def makeFileAsyncJsonLogger( +// config: FileLoggerConfig +// ): ZLayer[Scope, Nothing, ZLogger[String, Any]] = makeFileAsyncLogger( +// config.destination, +// config.format.toJsonLogger, +// config.charset, +// config.autoFlushBatchSize, +// config.bufferedIOSize, +// config.rollingPolicy +// ).project(logger => FilteredLogger(logger, config.filter)) +// +// def makeFileAsyncJsonLogger: ZLayer[FileLoggerConfig with Scope, Nothing, ZLogger[String, Any]] = +// ZLayer.environment[FileLoggerConfig].flatMap { env => +// makeFileAsyncJsonLogger(env.get[FileLoggerConfig]) +// } +// +// def makeFileAsyncLogger( +// config: FileLoggerConfig +// ): ZLayer[Scope, Nothing, ZLogger[String, Any]] = +// makeFileAsyncLogger( +// config.destination, +// config.format.toLogger, +// config.charset, +// config.autoFlushBatchSize, +// config.bufferedIOSize, +// config.rollingPolicy +// ).project(logger => FilteredLogger(logger, config.filter)) +// +// def makeFileAsyncLogger: ZLayer[FileLoggerConfig with Scope, Nothing, ZLogger[String, Any]] = +// ZLayer.environment[FileLoggerConfig].flatMap { env => +// makeFileAsyncLogger(env.get[FileLoggerConfig]) +// } +// +// def makeFileAsyncLogger( +// destination: Path, +// logger: ZLogger[String, String], +// charset: Charset, +// autoFlushBatchSize: Int, +// bufferedIOSize: Option[Int], +// rollingPolicy: Option[FileLoggerConfig.FileRollingPolicy] +// ): ZLayer[Scope, Nothing, ZLogger[String, Any]] = ZLayer.fromZIO { +// for { +// queue <- Queue.bounded[UIO[Any]](1000) +// _ <- queue.take.flatMap(task => task.ignore).forever.forkScoped +// } yield fileWriterAsyncLogger( +// destination, +// logger, +// charset, +// autoFlushBatchSize, +// bufferedIOSize, +// queue, +// rollingPolicy +// ) +// } +// +// private def fileWriterAsyncLogger( +// destination: Path, +// logger: ZLogger[String, String], +// charset: Charset, +// autoFlushBatchSize: Int, +// bufferedIOSize: Option[Int], +// queue: Queue[UIO[Any]], +// rollingPolicy: Option[FileLoggerConfig.FileRollingPolicy] +// ): ZLogger[String, Any] = { +// val logWriter = +// new zio.logging.internal.FileWriter(destination, charset, autoFlushBatchSize, bufferedIOSize, rollingPolicy) +// +// val stringLogger: ZLogger[String, Any] = logger.map { (line: String) => +// zio.Unsafe.unsafe { implicit u => +// Runtime.default.unsafe.run(queue.offer(ZIO.succeed { +// try logWriter.writeln(line) +// catch { +// case t: VirtualMachineError => throw t +// case _: Throwable => () +// } +// })) +// } +// } +// stringLogger +// } + +// def makeFileJsonLogger(config: FileLoggerConfig): ULayer[ZLogger[String, Any]] = +// makeFileLogger( +// config.destination, +// config.format.toJsonLogger, +// config.charset, +// config.autoFlushBatchSize, +// config.bufferedIOSize, +// config.rollingPolicy +// ).project(logger => FilteredLogger(logger, config.filter)) +// +// def makeFileJsonLogger: ZLayer[FileLoggerConfig, Nothing, ZLogger[String, Any]] = +// ZLayer.environment[FileLoggerConfig].flatMap { env => +// makeFileJsonLogger(env.get[FileLoggerConfig]) +// } +// +// def makeFileLogger(config: FileLoggerConfig): ULayer[ZLogger[String, Any]] = +// makeFileLogger( +// config.destination, +// config.format.toLogger, +// config.charset, +// config.autoFlushBatchSize, +// config.bufferedIOSize, +// config.rollingPolicy +// ).project(logger => FilteredLogger(logger, config.filter)) +// +// def makeFileLogger: ZLayer[FileLoggerConfig, Nothing, ZLogger[String, Any]] = +// ZLayer.environment[FileLoggerConfig].flatMap { env => +// makeFileLogger(env.get[FileLoggerConfig]) +// } +// +// def makeFileLogger( +// destination: Path, +// logger: ZLogger[String, String], +// charset: Charset, +// autoFlushBatchSize: Int, +// bufferedIOSize: Option[Int], +// rollingPolicy: Option[FileLoggerConfig.FileRollingPolicy] +// ): ULayer[ZLogger[String, Any]] = +// ZLayer.succeed( +// fileWriterLogger(destination, logger, charset, autoFlushBatchSize, bufferedIOSize, rollingPolicy) +// ) + + def makeFileJsonLogger(config: FileLoggerConfig): ZIO[Any, Nothing, FilteredLogger[String, Any]] = makeFileLogger( config.destination, config.format.toJsonLogger, @@ -173,14 +252,15 @@ object LoggerLayers { config.autoFlushBatchSize, config.bufferedIOSize, config.rollingPolicy - ).project(logger => FilteredLogger(logger, config.filter)) + ).map(logger => FilteredLogger(logger, config.filter)) - def makeFileJsonLogger: ZLayer[FileLoggerConfig, Nothing, ZLogger[String, Any]] = - ZLayer.environment[FileLoggerConfig].flatMap { env => - makeFileJsonLogger(env.get[FileLoggerConfig]) - } + def makeFileJsonLogger: ZIO[FileLoggerConfig, Nothing, ZLogger[String, Any]] = + ZIO.serviceWithZIO[FileLoggerConfig](makeFileJsonLogger(_)) - def makeFileLogger(config: FileLoggerConfig): ULayer[ZLogger[String, Any]] = + def makeFileLogger: ZIO[FileLoggerConfig, Nothing, ZLogger[String, Any]] = + ZIO.serviceWithZIO[FileLoggerConfig](makeFileLogger(_)) + + def makeFileLogger(config: FileLoggerConfig): ZIO[Any, Nothing, FilteredLogger[String, Any]] = makeFileLogger( config.destination, config.format.toLogger, @@ -188,12 +268,7 @@ object LoggerLayers { config.autoFlushBatchSize, config.bufferedIOSize, config.rollingPolicy - ).project(logger => FilteredLogger(logger, config.filter)) - - def makeFileLogger: ZLayer[FileLoggerConfig, Nothing, ZLogger[String, Any]] = - ZLayer.environment[FileLoggerConfig].flatMap { env => - makeFileLogger(env.get[FileLoggerConfig]) - } + ).map(logger => FilteredLogger(logger, config.filter)) def makeFileLogger( destination: Path, @@ -202,8 +277,8 @@ object LoggerLayers { autoFlushBatchSize: Int, bufferedIOSize: Option[Int], rollingPolicy: Option[FileLoggerConfig.FileRollingPolicy] - ): ULayer[ZLogger[String, Any]] = - ZLayer.succeed( + ): ZIO[Any, Nothing, ZLogger[String, Any]] = + ZIO.succeed( fileWriterLogger(destination, logger, charset, autoFlushBatchSize, bufferedIOSize, rollingPolicy) ) @@ -229,10 +304,13 @@ object LoggerLayers { stringLogger } - def makeMetricLogger(counter: Metric.Counter[Long], logLevelLabel: String): ULayer[MetricLogger] = - ZLayer.succeed(MetricLogger(counter, logLevelLabel)) +// def makeMetricLogger(counter: Metric.Counter[Long], logLevelLabel: String): ULayer[MetricLogger] = +// ZLayer.succeed(MetricLogger(counter, logLevelLabel)) + + def makeMetricLogger(counter: Metric.Counter[Long], logLevelLabel: String): ZIO[Any, Nothing, MetricLogger] = + ZIO.succeed(MetricLogger(counter, logLevelLabel)) - implicit final class ZLoggerLayerOps[-RIn, +E, ROut <: ZLogger[String, Any]: Tag]( + implicit final class ZLoggerLayerLayerOps[-RIn, +E, ROut <: ZLogger[String, Any]: Tag]( private val self: ZLayer[RIn, E, ROut] ) { @@ -248,4 +326,22 @@ object LoggerLayers { ZLayer.fromZIO(ZIO.withLoggerScoped(env.get[ROut])) } } + + implicit final class ZLoggerZIOLayerOps[-RIn, +E, ROut <: ZLogger[String, Any]: Tag]( + private val self: ZIO[RIn, E, ROut] + ) { + + def install: ZLayer[RIn, E, Unit] = + ZLayer.scoped[RIn] { + self.flatMap { logger => + ZIO.withLoggerScoped(logger) + } + } + + def installScoped: ZLayer[Scope with RIn, E, Unit] = + ZLayer.fromZIO(self.flatMap { logger => + ZIO.withLoggerScoped(logger) + }) + + } } diff --git a/core/shared/src/main/scala/zio/logging/internal/ReconfigurableLogger2.scala b/core/shared/src/main/scala/zio/logging/internal/ReconfigurableLogger2.scala new file mode 100644 index 000000000..357d14c31 --- /dev/null +++ b/core/shared/src/main/scala/zio/logging/internal/ReconfigurableLogger2.scala @@ -0,0 +1,83 @@ +/* + * 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.internal + +import zio._ +import zio.prelude._ + +import java.util.concurrent.atomic.AtomicReference + +private[logging] sealed trait ReconfigurableLogger2[-Message, +Output, Config] extends ZLogger[Message, Output] { + + def config: Config + + def underlying: ZLogger[Message, Output] + + private[logging] def set[M <: Message, O >: Output](config: Config, logger: ZLogger[M, O]): Unit +} + +private[logging] object ReconfigurableLogger2 { + + def apply[Message, Output, Config]( + config: Config, + logger: ZLogger[Message, Output] + ): ReconfigurableLogger2[Message, Output, Config] = { + val configuredLogger: AtomicReference[(Config, ZLogger[Message, Output])] = + new AtomicReference[(Config, ZLogger[Message, Output])]((config, logger)) + + new ReconfigurableLogger2[Message, Output, Config] { + + override def config: Config = configuredLogger.get()._1 + + override def underlying: ZLogger[Message, Output] = configuredLogger.get()._2 + + override private[logging] def set[M <: Message, O >: Output](config: Config, logger: ZLogger[M, O]): Unit = + configuredLogger.set((config, logger.asInstanceOf[ZLogger[Message, Output]])) + + override def apply( + trace: Trace, + fiberId: FiberId, + logLevel: LogLevel, + message: () => Message, + cause: Cause[Any], + context: FiberRefs, + spans: List[LogSpan], + annotations: Map[String, String] + ): Output = + configuredLogger.get()._2.apply(trace, fiberId, logLevel, message, cause, context, spans, annotations) + } + } + + def make[E, M, O, C: Equal]( + loadConfig: => ZIO[Any, E, C], + makeLogger: (C, Option[ZLogger[M, O]]) => ZIO[Any, E, ZLogger[M, O]], + updateLogger: Schedule[Any, Any, Any] = Schedule.fixed(10.seconds) + ): ZIO[Scope, E, ReconfigurableLogger2[M, O, C]] = + for { + initialConfig <- loadConfig + initialLogger <- makeLogger(initialConfig, None) + reconfigurableLogger = ReconfigurableLogger2[M, O, C](initialConfig, initialLogger) + _ <- loadConfig.flatMap { newConfig => + val currentConfig = reconfigurableLogger.config + if (currentConfig !== newConfig) { + makeLogger(newConfig, Some(reconfigurableLogger.underlying)).map { newLogger => + reconfigurableLogger.set(newConfig, newLogger) + }.unit + } else ZIO.unit + }.scheduleFork(updateLogger) + } yield reconfigurableLogger + +} diff --git a/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala b/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala index d7c65621c..7979c3950 100644 --- a/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala +++ b/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala @@ -28,19 +28,34 @@ object LoggerReconfigureApp extends ZIOAppDefault { def configurableLogger(configPath: String = "logger") = { import LoggerLayers._ - ConsoleLoggerConfig.make(configPath).flatMap { env => - val consoleLoggerConfig = env.get[ConsoleLoggerConfig] - - makeSystemOutLogger( - consoleLoggerConfig.format.toLogger - ).project { logger => - val filterConfig = consoleLoggerConfig.filter - .asInstanceOf[LogFilter.ConfiguredFilter[String, LogFilter.LogLevelByNameConfig]] - .config - - ConfigurableLogger.make(logger, filterConfig) - }.install - } +// ConsoleLoggerConfig.make(configPath).flatMap { env => +// val consoleLoggerConfig = env.get[ConsoleLoggerConfig] +// +// makeSystemOutLogger( +// consoleLoggerConfig.format.toLogger +// ).project { logger => +// val filterConfig = consoleLoggerConfig.filter +// .asInstanceOf[LogFilter.ConfiguredFilter[String, LogFilter.LogLevelByNameConfig]] +// .config +// +// ConfigurableLogger.make(logger, filterConfig) +// }.install +// } + + ConsoleLoggerConfig + .load(configPath) + .flatMap { consoleLoggerConfig => + makeSystemOutLogger( + consoleLoggerConfig.format.toLogger + ).map { logger => + val filterConfig = consoleLoggerConfig.filter + .asInstanceOf[LogFilter.ConfiguredFilter[String, LogFilter.LogLevelByNameConfig]] + .config + + ConfigurableLogger.make(logger, filterConfig) + } + } + .install } val logFormat = From caf07c13188b66eac8e335ba7707f6571635bd94 Mon Sep 17 00:00:00 2001 From: Peter Kotula Date: Mon, 26 Jun 2023 23:39:07 +0200 Subject: [PATCH 33/56] LoggerLayers - wip --- .../zio/logging/ConfigurableLogger.scala | 82 +++++++++++++- .../main/scala/zio/logging/LoggerLayers.scala | 23 +++- .../internal/ReconfigurableLogger2.scala | 12 +-- .../example/ConfigurableLoggerApp.scala | 100 ++++++++++++++++++ .../example/LoggerReconfigureApp.scala | 98 ++++++----------- 5 files changed, 239 insertions(+), 76 deletions(-) create mode 100644 examples/core/src/main/scala/zio/logging/example/ConfigurableLoggerApp.scala diff --git a/core/shared/src/main/scala/zio/logging/ConfigurableLogger.scala b/core/shared/src/main/scala/zio/logging/ConfigurableLogger.scala index 924d9a17c..dad4319a1 100644 --- a/core/shared/src/main/scala/zio/logging/ConfigurableLogger.scala +++ b/core/shared/src/main/scala/zio/logging/ConfigurableLogger.scala @@ -15,7 +15,7 @@ */ package zio.logging -import zio.logging.internal.ReconfigurableLogger +import zio.logging.internal.{ ReconfigurableLogger, ReconfigurableLogger2 } import zio.{ Cause, FiberId, FiberRef, FiberRefs, LogLevel, LogSpan, Trace, ZIO, ZLayer, ZLogger } trait LoggerConfigurer { @@ -160,4 +160,84 @@ object ConfigurableLogger { } } + def make2[Message, Output]( + logger: ZLogger[Message, Output], + filterConfig: LogFilter.LogLevelByNameConfig + ): ConfigurableLogger[Message, Option[Output]] = { + + val initialLogger = LogFilter.logLevelByName(filterConfig).filter(logger) + + val reconfigurableLogger = ReconfigurableLogger2[Message, Option[Output], LogFilter.LogLevelByNameConfig]( + filterConfig, + initialLogger + ) + + new ConfigurableLogger[Message, Option[Output]] { + + override val configurer: LoggerConfigurer = + Configurer2(filterConfig => LogFilter.logLevelByName(filterConfig).filter(logger), reconfigurableLogger) + + override def apply( + trace: Trace, + fiberId: FiberId, + logLevel: LogLevel, + message: () => Message, + cause: Cause[Any], + context: FiberRefs, + spans: List[LogSpan], + annotations: Map[String, String] + ): Option[Output] = + reconfigurableLogger.apply(trace, fiberId, logLevel, message, cause, context, spans, annotations) + } + } + + private case class Configurer2[M, O]( + makeLogger: LogFilter.LogLevelByNameConfig => ZLogger[M, O], + logger: ReconfigurableLogger2[M, Option[O], LogFilter.LogLevelByNameConfig] + ) extends LoggerConfigurer { + import zio.prelude._ + + private val rootName = "root" + + override def getLoggerConfigs(): ZIO[Any, Throwable, List[LoggerConfigurer.LoggerConfig]] = + ZIO.attempt { + val currentConfig = logger.get._1 + + LoggerConfigurer.LoggerConfig(rootName, currentConfig.rootLevel) :: currentConfig.mappings.map { case (n, l) => + LoggerConfigurer.LoggerConfig(n, l) + }.toList + } + + override def getLoggerConfig(name: String): ZIO[Any, Throwable, Option[LoggerConfigurer.LoggerConfig]] = + ZIO.attempt { + val currentConfig = logger.get._1 + + if (name == rootName) { + Some(LoggerConfigurer.LoggerConfig(rootName, currentConfig.rootLevel)) + } else { + currentConfig.mappings.collectFirst { + case (n, l) if n == name => LoggerConfigurer.LoggerConfig(n, l) + } + } + } + + override def setLoggerConfig(name: String, level: LogLevel): ZIO[Any, Throwable, LoggerConfigurer.LoggerConfig] = + ZIO.attempt { + val currentConfig = logger.get._1 + + val newConfig = if (name == rootName) { + currentConfig.withRootLevel(level) + } else { + currentConfig.withMapping(name, level) + } + + if (currentConfig !== newConfig) { + val newLogger = makeLogger(newConfig) + logger.set(newConfig, newLogger) + } + + LoggerConfigurer.LoggerConfig(name, level) + } + } + } diff --git a/core/shared/src/main/scala/zio/logging/LoggerLayers.scala b/core/shared/src/main/scala/zio/logging/LoggerLayers.scala index 7529b4b47..5d5bce9a7 100644 --- a/core/shared/src/main/scala/zio/logging/LoggerLayers.scala +++ b/core/shared/src/main/scala/zio/logging/LoggerLayers.scala @@ -327,12 +327,30 @@ object LoggerLayers { } } +// implicit final class ZLoggerZIOScopedLayerOps[-RIn, +E, ROut <: ZLogger[String, Any]: Tag]( +// private val self: ZIO[Scope with RIn, E, ROut] +// ) { +// +// def install: ZLayer[RIn, E, Unit] = +// ZLayer.scoped[RIn] { +// self.flatMap { logger => +// ZIO.withLoggerScoped(logger) +// } +// } +// +// def installScoped: ZLayer[Scope with RIn, E, Unit] = +// ZLayer.fromZIO(self.flatMap { logger => +// ZIO.withLoggerScoped(logger) +// }) +// +// } + implicit final class ZLoggerZIOLayerOps[-RIn, +E, ROut <: ZLogger[String, Any]: Tag]( private val self: ZIO[RIn, E, ROut] ) { - def install: ZLayer[RIn, E, Unit] = - ZLayer.scoped[RIn] { + def install[RIn2 <: RIn]: ZLayer[RIn2, E, Unit] = + ZLayer.scoped[RIn2] { self.flatMap { logger => ZIO.withLoggerScoped(logger) } @@ -344,4 +362,5 @@ object LoggerLayers { }) } + } diff --git a/core/shared/src/main/scala/zio/logging/internal/ReconfigurableLogger2.scala b/core/shared/src/main/scala/zio/logging/internal/ReconfigurableLogger2.scala index 357d14c31..62dadbf1a 100644 --- a/core/shared/src/main/scala/zio/logging/internal/ReconfigurableLogger2.scala +++ b/core/shared/src/main/scala/zio/logging/internal/ReconfigurableLogger2.scala @@ -22,9 +22,7 @@ import java.util.concurrent.atomic.AtomicReference private[logging] sealed trait ReconfigurableLogger2[-Message, +Output, Config] extends ZLogger[Message, Output] { - def config: Config - - def underlying: ZLogger[Message, Output] + def get: (Config, ZLogger[Message, Output]) private[logging] def set[M <: Message, O >: Output](config: Config, logger: ZLogger[M, O]): Unit } @@ -40,9 +38,7 @@ private[logging] object ReconfigurableLogger2 { new ReconfigurableLogger2[Message, Output, Config] { - override def config: Config = configuredLogger.get()._1 - - override def underlying: ZLogger[Message, Output] = configuredLogger.get()._2 + override def get: (Config, ZLogger[Message, Output]) = configuredLogger.get() override private[logging] def set[M <: Message, O >: Output](config: Config, logger: ZLogger[M, O]): Unit = configuredLogger.set((config, logger.asInstanceOf[ZLogger[Message, Output]])) @@ -71,9 +67,9 @@ private[logging] object ReconfigurableLogger2 { initialLogger <- makeLogger(initialConfig, None) reconfigurableLogger = ReconfigurableLogger2[M, O, C](initialConfig, initialLogger) _ <- loadConfig.flatMap { newConfig => - val currentConfig = reconfigurableLogger.config + val (currentConfig, currentLogger) = reconfigurableLogger.get if (currentConfig !== newConfig) { - makeLogger(newConfig, Some(reconfigurableLogger.underlying)).map { newLogger => + makeLogger(newConfig, Some(currentLogger)).map { newLogger => reconfigurableLogger.set(newConfig, newLogger) }.unit } else ZIO.unit diff --git a/examples/core/src/main/scala/zio/logging/example/ConfigurableLoggerApp.scala b/examples/core/src/main/scala/zio/logging/example/ConfigurableLoggerApp.scala new file mode 100644 index 000000000..447b0de30 --- /dev/null +++ b/examples/core/src/main/scala/zio/logging/example/ConfigurableLoggerApp.scala @@ -0,0 +1,100 @@ +/* + * 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.http.HttpAppMiddleware.basicAuth +import zio.http._ +import zio.logging.api.http.ApiHandlers +import zio.logging.{ ConfigurableLogger, ConsoleLoggerConfig, LogAnnotation, LogFilter, LoggerConfigurer, LoggerLayers } +import zio.{ ExitCode, Runtime, Scope, ZIO, ZIOAppDefault, _ } + +import java.util.UUID + +object ConfigurableLoggerApp extends ZIOAppDefault { + + def configurableLogger(configPath: String = "logger") = { + import LoggerLayers._ + + ConsoleLoggerConfig + .load(configPath) + .flatMap { consoleLoggerConfig => + makeSystemOutLogger( + consoleLoggerConfig.format.toLogger + ).map { logger => + val filterConfig = consoleLoggerConfig.filter + .asInstanceOf[LogFilter.ConfiguredFilter[String, LogFilter.LogLevelByNameConfig]] + .config + + ConfigurableLogger.make2(logger, filterConfig) + } + } + .install + } + + val logFormat = + "%highlight{%timestamp{yyyy-MM-dd'T'HH:mm:ssZ} %fixed{7}{%level} [%fiberId] %name:%line %message %kv{trace_id} %kv{user_id} %cause}" + + val configProvider: ConfigProvider = ConfigProvider.fromMap( + Map( + "logger/format" -> logFormat, + "logger/filter/rootLevel" -> LogLevel.Info.label, + "logger/filter/mappings/zio.logging.example" -> LogLevel.Debug.label + ), + "/" + ) + + override val bootstrap: ZLayer[Any, Config.Error, Unit] = + Runtime.removeDefaultLoggers >>> Runtime.setConfigProvider(configProvider) >>> configurableLogger() + + def exec(): ZIO[Any, Nothing, Unit] = + for { + ok <- Random.nextBoolean + traceId <- ZIO.succeed(UUID.randomUUID()) + _ <- ZIO.logDebug("Start") @@ LogAnnotation.TraceId(traceId) + userIds <- ZIO.succeed(List.fill(2)(UUID.randomUUID().toString)) + _ <- ZIO.foreachPar(userIds) { userId => + { + ZIO.logDebug("Starting operation") *> + ZIO.logInfo("OK operation").when(ok) *> + ZIO.logError("Error operation").when(!ok) *> + ZIO.logDebug("Stopping operation") + } @@ LogAnnotation.UserId(userId) + } @@ LogAnnotation.TraceId(traceId) + _ <- ZIO.logDebug("Done") @@ LogAnnotation.TraceId(traceId) + } yield () + + val httpApp: Http[LoggerConfigurer, Response, Request, Response] = + ApiHandlers.routes("example" :: Nil).toApp[LoggerConfigurer] @@ basicAuth("admin", "admin") + + override def run: ZIO[Scope, Any, ExitCode] = + (for { + _ <- Server.serve(httpApp).fork + _ <- exec().repeat(Schedule.fixed(500.millis)) + } yield ExitCode.success).provide(LoggerConfigurer.layer ++ Server.default) + +} + +/* + + curl -u "admin:admin" 'http://localhost:8080/example/logger' + + curl -u "admin:admin" 'http://localhost:8080/example/logger/root' + + curl -u "admin:admin" --location --request PUT 'http://localhost:8080/example/logger/root' --header 'Content-Type: application/json' --data-raw '"WARN"' + + curl -u "admin:admin" --location --request PUT 'http://localhost:8080/example/logger/zio.logging.example' --header 'Content-Type: application/json' --data-raw '"WARN"' + + */ diff --git a/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala b/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala index 7979c3950..abc5ff2ff 100644 --- a/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala +++ b/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala @@ -15,63 +15,47 @@ */ package zio.logging.example -import zio.http.HttpAppMiddleware.basicAuth -import zio.http._ -import zio.logging.api.http.ApiHandlers +import zio.logging.internal.ReconfigurableLogger2 import zio.logging.{ ConfigurableLogger, ConsoleLoggerConfig, LogAnnotation, LogFilter, LoggerConfigurer, LoggerLayers } -import zio.{ ExitCode, Runtime, Scope, ZIO, ZIOAppDefault, _ } +import zio.{ Config, ExitCode, Runtime, Scope, ZIO, ZIOAppDefault, _ } import java.util.UUID object LoggerReconfigureApp extends ZIOAppDefault { - def configurableLogger(configPath: String = "logger") = { - import LoggerLayers._ - -// ConsoleLoggerConfig.make(configPath).flatMap { env => -// val consoleLoggerConfig = env.get[ConsoleLoggerConfig] -// -// makeSystemOutLogger( -// consoleLoggerConfig.format.toLogger -// ).project { logger => -// val filterConfig = consoleLoggerConfig.filter -// .asInstanceOf[LogFilter.ConfiguredFilter[String, LogFilter.LogLevelByNameConfig]] -// .config -// -// ConfigurableLogger.make(logger, filterConfig) -// }.install -// } - - ConsoleLoggerConfig - .load(configPath) - .flatMap { consoleLoggerConfig => - makeSystemOutLogger( - consoleLoggerConfig.format.toLogger - ).map { logger => - val filterConfig = consoleLoggerConfig.filter - .asInstanceOf[LogFilter.ConfiguredFilter[String, LogFilter.LogLevelByNameConfig]] - .config - - ConfigurableLogger.make(logger, filterConfig) - } - } - .install - } - val logFormat = "%highlight{%timestamp{yyyy-MM-dd'T'HH:mm:ssZ} %fixed{7}{%level} [%fiberId] %name:%line %message %kv{trace_id} %kv{user_id} %cause}" - val configProvider: ConfigProvider = ConfigProvider.fromMap( - Map( - "logger/format" -> logFormat, - "logger/filter/rootLevel" -> LogLevel.Info.label, - "logger/filter/mappings/zio.logging.example" -> LogLevel.Debug.label - ), - "/" - ) + def configuredLogger( + loadConfig: => ZIO[Any, Config.Error, ConsoleLoggerConfig] + ) = { + import LoggerLayers._ + ZLayer.scoped { + ReconfigurableLogger2 + .make[Config.Error, String, Any, ConsoleLoggerConfig]( + loadConfig, + (config, _) => + makeSystemOutLogger(config.format.toLogger).map { logger => + config.filter.filter(logger) + }, + Schedule.fixed(500.millis) + ) + .flatMap(logger => ZIO.withLoggerScoped(logger)) + } + } - override val bootstrap: ZLayer[Any, Config.Error, Unit] = - Runtime.removeDefaultLoggers >>> Runtime.setConfigProvider(configProvider) >>> configurableLogger() + override val bootstrap: ZLayer[ZIOAppArgs, Any, Any] = + Runtime.removeDefaultLoggers >>> configuredLogger( + for { + info <- Random.nextBoolean + cfg = Map( + "logger/format" -> logFormat, + "logger/filter/rootLevel" -> (if (info) LogLevel.Info.label else LogLevel.Debug.label) + ) + _ <- Console.printLine(cfg.mkString(", ")).orDie + config <- ConfigProvider.fromMap(cfg, "/").nested("logger").load(ConsoleLoggerConfig.config) + } yield config + ) def exec(): ZIO[Any, Nothing, Unit] = for { @@ -90,25 +74,9 @@ object LoggerReconfigureApp extends ZIOAppDefault { _ <- ZIO.logDebug("Done") @@ LogAnnotation.TraceId(traceId) } yield () - val httpApp: Http[LoggerConfigurer, Response, Request, Response] = - ApiHandlers.routes("example" :: Nil).toApp[LoggerConfigurer] @@ basicAuth("admin", "admin") - override def run: ZIO[Scope, Any, ExitCode] = - (for { - _ <- Server.serve(httpApp).fork + for { _ <- exec().repeat(Schedule.fixed(500.millis)) - } yield ExitCode.success).provide(LoggerConfigurer.layer ++ Server.default) + } yield ExitCode.success } - -/* - - curl -u "admin:admin" 'http://localhost:8080/example/logger' - - curl -u "admin:admin" 'http://localhost:8080/example/logger/root' - - curl -u "admin:admin" --location --request PUT 'http://localhost:8080/example/logger/root' --header 'Content-Type: application/json' --data-raw '"WARN"' - - curl -u "admin:admin" --location --request PUT 'http://localhost:8080/example/logger/zio.logging.example' --header 'Content-Type: application/json' --data-raw '"WARN"' - - */ From 999aba99012060667819b4f23c014ac097eb79a1 Mon Sep 17 00:00:00 2001 From: Peter Kotula Date: Tue, 27 Jun 2023 10:04:20 +0200 Subject: [PATCH 34/56] ReconfigurableLogger --- .../internal/ReconfigurableLogger2Spec.scala | 78 ----------------- .../internal/ReconfigurableLoggerSpec.scala | 19 +++-- .../zio/logging/ConfigurableLogger.scala | 85 ++----------------- .../main/scala/zio/logging/LoggerLayers.scala | 25 +----- .../main/scala/zio/logging/MetricLogger.scala | 2 +- .../internal/ReconfigurableLogger.scala | 72 +++++++--------- .../internal/ReconfigurableLogger2.scala | 79 ----------------- .../example/ConfigurableLoggerApp.scala | 2 +- .../example/LoggerReconfigureApp.scala | 26 +++--- 9 files changed, 64 insertions(+), 324 deletions(-) delete mode 100644 core/jvm/src/test/scala/zio/logging/internal/ReconfigurableLogger2Spec.scala delete mode 100644 core/shared/src/main/scala/zio/logging/internal/ReconfigurableLogger2.scala diff --git a/core/jvm/src/test/scala/zio/logging/internal/ReconfigurableLogger2Spec.scala b/core/jvm/src/test/scala/zio/logging/internal/ReconfigurableLogger2Spec.scala deleted file mode 100644 index 58be8f2e4..000000000 --- a/core/jvm/src/test/scala/zio/logging/internal/ReconfigurableLogger2Spec.scala +++ /dev/null @@ -1,78 +0,0 @@ -package zio.logging.internal - -import zio.logging.{ ConsoleLoggerConfig, _ } -import zio.test._ -import zio.{ Chunk, Config, ConfigProvider, LogLevel, Queue, Runtime, Schedule, ZIO, ZLayer, _ } - -object ReconfigurableLogger2Spec extends ZIOSpecDefault { - - def configuredLogger( - queue: zio.Queue[String], - configPath: String = "logger" - ): ZLayer[Any, Config.Error, Unit] = - ZLayer.scoped { - for { - logger <- ReconfigurableLogger2 - .make[Config.Error, String, Any, ConsoleLoggerConfig]( - ConsoleLoggerConfig.load(configPath), - (config, _) => - ZIO.succeed { - config.format.toLogger.map { line => - zio.Unsafe.unsafe { implicit u => - Runtime.default.unsafe.run(queue.offer(line)) - } - }.filter(config.filter) - }, - Schedule.fixed(200.millis) - ) - _ <- ZIO.withLoggerScoped(logger) - } yield () - } - - val spec: Spec[Environment, Any] = suite("ReconfigurableLogger2")( - test("log with changed config") { - - val initialProperties = Map( - "logger/format" -> "%message", - "logger/filter/rootLevel" -> LogLevel.Info.label, - "logger/filter/mappings/zio.logging.example.LivePingService" -> LogLevel.Debug.label - ) - - for { - _ <- ZIO.foreach(initialProperties) { case (k, v) => - TestSystem.putProperty(k, v).as(k -> v) - } - - queue <- Queue.unbounded[String] - - runTest = - for { - _ <- ZIO.logInfo("info") - _ <- ZIO.logDebug("debug") - elements1 <- queue.takeAll - _ <- TestSystem.putProperty("logger/format", "%level %message") - _ <- ZIO.sleep(500.millis) - _ <- ZIO.logWarning("warn") - _ <- ZIO.logDebug("debug") - elements2 <- queue.takeAll - _ <- TestSystem.putProperty("logger/format", "L: %level M: %message") - _ <- TestSystem.putProperty("logger/filter/rootLevel", LogLevel.Debug.label) - _ <- ZIO.sleep(500.millis) - _ <- ZIO.logDebug("debug") - elements3 <- queue.takeAll - } yield assertTrue( - elements1 == Chunk("info") && elements2 == Chunk("WARN warn") && elements3 == Chunk( - "L: DEBUG M: debug" - ) - ) - - result <- - runTest.provide( - Runtime.removeDefaultLoggers >>> Runtime - .setConfigProvider(ConfigProvider.fromProps("/")) >>> configuredLogger(queue) - ) - } yield result - - } - ) @@ TestAspect.withLiveClock -} diff --git a/core/jvm/src/test/scala/zio/logging/internal/ReconfigurableLoggerSpec.scala b/core/jvm/src/test/scala/zio/logging/internal/ReconfigurableLoggerSpec.scala index 30ef00050..671358d91 100644 --- a/core/jvm/src/test/scala/zio/logging/internal/ReconfigurableLoggerSpec.scala +++ b/core/jvm/src/test/scala/zio/logging/internal/ReconfigurableLoggerSpec.scala @@ -9,25 +9,28 @@ object ReconfigurableLoggerSpec extends ZIOSpecDefault { def configuredLogger( queue: zio.Queue[String], configPath: String = "logger" - ): ZLayer[Any, Config.Error, Unit] = + ): ZLayer[Any, Config.Error, Unit] = { ZLayer.scoped { for { logger <- ReconfigurableLogger - .make[Config.Error, String, Any, ConsoleLoggerConfig]( + .make[Any, Config.Error, String, Any, ConsoleLoggerConfig]( ConsoleLoggerConfig.load(configPath), (config, _) => - config.format.toLogger.map { line => - zio.Unsafe.unsafe { implicit u => - Runtime.default.unsafe.run(queue.offer(line)) - } - }.filter(config.filter), + ZIO.succeed { + config.format.toLogger.map { line => + zio.Unsafe.unsafe { implicit u => + Runtime.default.unsafe.run(queue.offer(line)) + } + }.filter(config.filter) + }, Schedule.fixed(200.millis) ) _ <- ZIO.withLoggerScoped(logger) } yield () } + } - val spec: Spec[Environment, Any] = suite("ReconfigurableLogger")( + val spec: Spec[Environment, Any] = suite("ReconfigurableLogger2")( test("log with changed config") { val initialProperties = Map( diff --git a/core/shared/src/main/scala/zio/logging/ConfigurableLogger.scala b/core/shared/src/main/scala/zio/logging/ConfigurableLogger.scala index dad4319a1..caebe0e9f 100644 --- a/core/shared/src/main/scala/zio/logging/ConfigurableLogger.scala +++ b/core/shared/src/main/scala/zio/logging/ConfigurableLogger.scala @@ -15,7 +15,7 @@ */ package zio.logging -import zio.logging.internal.{ ReconfigurableLogger, ReconfigurableLogger2 } +import zio.logging.internal.ReconfigurableLogger import zio.{ Cause, FiberId, FiberRef, FiberRefs, LogLevel, LogSpan, Trace, ZIO, ZLayer, ZLogger } trait LoggerConfigurer { @@ -90,84 +90,9 @@ object ConfigurableLogger { filterConfig: LogFilter.LogLevelByNameConfig ): ConfigurableLogger[Message, Option[Output]] = { - val reconfigurableLogger = ReconfigurableLogger[Message, Option[Output], LogFilter.LogLevelByNameConfig]( - filterConfig, - (config, _) => { - val filter = LogFilter.logLevelByName(config) - - filter.filter(logger) - } - ) - - new ConfigurableLogger[Message, Option[Output]] { - - override val configurer: LoggerConfigurer = Configurer(reconfigurableLogger) - - override def apply( - trace: Trace, - fiberId: FiberId, - logLevel: LogLevel, - message: () => Message, - cause: Cause[Any], - context: FiberRefs, - spans: List[LogSpan], - annotations: Map[String, String] - ): Option[Output] = - reconfigurableLogger.apply(trace, fiberId, logLevel, message, cause, context, spans, annotations) - } - } - - private case class Configurer(logger: ReconfigurableLogger[_, _, LogFilter.LogLevelByNameConfig]) - extends LoggerConfigurer { - - private val rootName = "root" - - override def getLoggerConfigs(): ZIO[Any, Throwable, List[LoggerConfigurer.LoggerConfig]] = - ZIO.attempt { - val currentConfig = logger.config - - LoggerConfigurer.LoggerConfig(rootName, currentConfig.rootLevel) :: currentConfig.mappings.map { case (n, l) => - LoggerConfigurer.LoggerConfig(n, l) - }.toList - } - - override def getLoggerConfig(name: String): ZIO[Any, Throwable, Option[LoggerConfigurer.LoggerConfig]] = - ZIO.attempt { - val currentConfig = logger.config - - if (name == rootName) { - Some(LoggerConfigurer.LoggerConfig(rootName, currentConfig.rootLevel)) - } else { - currentConfig.mappings.collectFirst { - case (n, l) if n == name => LoggerConfigurer.LoggerConfig(n, l) - } - } - } - - override def setLoggerConfig(name: String, level: LogLevel): ZIO[Any, Throwable, LoggerConfigurer.LoggerConfig] = - ZIO.attempt { - val currentConfig = logger.config - - val newConfig = if (name == rootName) { - currentConfig.withRootLevel(level) - } else { - currentConfig.withMapping(name, level) - } - - logger.reconfigureIfChanged(newConfig) - - LoggerConfigurer.LoggerConfig(name, level) - } - } - - def make2[Message, Output]( - logger: ZLogger[Message, Output], - filterConfig: LogFilter.LogLevelByNameConfig - ): ConfigurableLogger[Message, Option[Output]] = { - val initialLogger = LogFilter.logLevelByName(filterConfig).filter(logger) - val reconfigurableLogger = ReconfigurableLogger2[Message, Option[Output], LogFilter.LogLevelByNameConfig]( + val reconfigurableLogger = ReconfigurableLogger[Message, Option[Output], LogFilter.LogLevelByNameConfig]( filterConfig, initialLogger ) @@ -175,7 +100,7 @@ object ConfigurableLogger { new ConfigurableLogger[Message, Option[Output]] { override val configurer: LoggerConfigurer = - Configurer2(filterConfig => LogFilter.logLevelByName(filterConfig).filter(logger), reconfigurableLogger) + Configurer(filterConfig => LogFilter.logLevelByName(filterConfig).filter(logger), reconfigurableLogger) override def apply( trace: Trace, @@ -191,9 +116,9 @@ object ConfigurableLogger { } } - private case class Configurer2[M, O]( + private case class Configurer[M, O]( makeLogger: LogFilter.LogLevelByNameConfig => ZLogger[M, O], - logger: ReconfigurableLogger2[M, Option[O], LogFilter.LogLevelByNameConfig] + logger: ReconfigurableLogger[M, Option[O], LogFilter.LogLevelByNameConfig] ) extends LoggerConfigurer { import zio.prelude._ diff --git a/core/shared/src/main/scala/zio/logging/LoggerLayers.scala b/core/shared/src/main/scala/zio/logging/LoggerLayers.scala index 5d5bce9a7..68bcf9983 100644 --- a/core/shared/src/main/scala/zio/logging/LoggerLayers.scala +++ b/core/shared/src/main/scala/zio/logging/LoggerLayers.scala @@ -15,9 +15,8 @@ */ package zio.logging -import zio.Tag -import zio.{ Queue, Runtime, Scope, UIO, ULayer, ZIO, ZLayer, ZLogger } import zio.metrics.Metric +import zio.{ Queue, Runtime, Scope, Tag, UIO, ULayer, ZIO, ZLayer, ZLogger } import java.io.PrintStream import java.nio.charset.Charset @@ -327,30 +326,12 @@ object LoggerLayers { } } -// implicit final class ZLoggerZIOScopedLayerOps[-RIn, +E, ROut <: ZLogger[String, Any]: Tag]( -// private val self: ZIO[Scope with RIn, E, ROut] -// ) { -// -// def install: ZLayer[RIn, E, Unit] = -// ZLayer.scoped[RIn] { -// self.flatMap { logger => -// ZIO.withLoggerScoped(logger) -// } -// } -// -// def installScoped: ZLayer[Scope with RIn, E, Unit] = -// ZLayer.fromZIO(self.flatMap { logger => -// ZIO.withLoggerScoped(logger) -// }) -// -// } - implicit final class ZLoggerZIOLayerOps[-RIn, +E, ROut <: ZLogger[String, Any]: Tag]( private val self: ZIO[RIn, E, ROut] ) { - def install[RIn2 <: RIn]: ZLayer[RIn2, E, Unit] = - ZLayer.scoped[RIn2] { + def install: ZLayer[RIn, E, Unit] = + ZLayer.scoped[RIn] { self.flatMap { logger => ZIO.withLoggerScoped(logger) } diff --git a/core/shared/src/main/scala/zio/logging/MetricLogger.scala b/core/shared/src/main/scala/zio/logging/MetricLogger.scala index 9828fb26e..10ee509a0 100644 --- a/core/shared/src/main/scala/zio/logging/MetricLogger.scala +++ b/core/shared/src/main/scala/zio/logging/MetricLogger.scala @@ -15,8 +15,8 @@ */ package zio.logging -import zio.{ Cause, FiberId, FiberRef, FiberRefs, LogLevel, LogSpan, Trace, Unsafe, ZLogger } import zio.metrics.{ Metric, MetricLabel } +import zio.{ Cause, FiberId, FiberRef, FiberRefs, LogLevel, LogSpan, Trace, Unsafe, ZLogger } final case class MetricLogger(counter: Metric.Counter[Long], logLevelLabel: String) extends ZLogger[String, Unit] { diff --git a/core/shared/src/main/scala/zio/logging/internal/ReconfigurableLogger.scala b/core/shared/src/main/scala/zio/logging/internal/ReconfigurableLogger.scala index 32d5b110d..445664f10 100644 --- a/core/shared/src/main/scala/zio/logging/internal/ReconfigurableLogger.scala +++ b/core/shared/src/main/scala/zio/logging/internal/ReconfigurableLogger.scala @@ -22,68 +22,58 @@ import java.util.concurrent.atomic.AtomicReference private[logging] sealed trait ReconfigurableLogger[-Message, +Output, Config] extends ZLogger[Message, Output] { - def config: Config + def get: (Config, ZLogger[Message, Output]) - def reconfigure(config: Config): Unit - - def reconfigureIfChanged(config: Config): Boolean + private[logging] def set[M <: Message, O >: Output](config: Config, logger: ZLogger[M, O]): Unit } private[logging] object ReconfigurableLogger { - def apply[M, O, C: Equal]( - initialConfig: C, - makeLogger: (C, Option[ZLogger[M, O]]) => ZLogger[M, O] - ): ReconfigurableLogger[M, O, C] = - new ReconfigurableLogger[M, O, C] { - - private val configuredLogger: AtomicReference[(C, ZLogger[M, O])] = { - val logger = makeLogger(initialConfig, None) - new AtomicReference[(C, ZLogger[M, O])]((initialConfig, logger)) - } + def apply[Message, Output, Config]( + config: Config, + logger: ZLogger[Message, Output] + ): ReconfigurableLogger[Message, Output, Config] = { + val configuredLogger: AtomicReference[(Config, ZLogger[Message, Output])] = + new AtomicReference[(Config, ZLogger[Message, Output])]((config, logger)) - override def config: C = configuredLogger.get()._1 + new ReconfigurableLogger[Message, Output, Config] { - override def reconfigureIfChanged(config: C): Boolean = { - val (currentConfig, currentLogger) = configuredLogger.get() - if (currentConfig !== config) { - reconfigure(config) - val logger = makeLogger(config, Some(currentLogger)) - configuredLogger.set((config, logger)) - true - } else false - } + override def get: (Config, ZLogger[Message, Output]) = configuredLogger.get() - override def reconfigure(config: C): Unit = { - val currentLogger = configuredLogger.get()._2 - val logger = makeLogger(config, Some(currentLogger)) - configuredLogger.set((config, logger)) - } + override private[logging] def set[M <: Message, O >: Output](config: Config, logger: ZLogger[M, O]): Unit = + configuredLogger.set((config, logger.asInstanceOf[ZLogger[Message, Output]])) override def apply( trace: Trace, fiberId: FiberId, logLevel: LogLevel, - message: () => M, + message: () => Message, cause: Cause[Any], context: FiberRefs, spans: List[LogSpan], annotations: Map[String, String] - ): O = + ): Output = configuredLogger.get()._2.apply(trace, fiberId, logLevel, message, cause, context, spans, annotations) } + } - def make[E, M, O, C: Equal]( + def make[R, E, M, O, C: Equal]( loadConfig: => ZIO[Any, E, C], - makeLogger: (C, Option[ZLogger[M, O]]) => ZLogger[M, O], - updateLogger: Schedule[Any, Any, Any] = Schedule.fixed(10.seconds) - ): ZIO[Scope, E, ReconfigurableLogger[M, O, C]] = + makeLogger: (C, Option[ZLogger[M, O]]) => ZIO[R, E, ZLogger[M, O]], + updateLogger: Schedule[R, Any, Any] = Schedule.fixed(10.seconds) + ): ZIO[R, E, ReconfigurableLogger[M, O, C]] = for { - config <- loadConfig - logger = ReconfigurableLogger[M, O, C](config, makeLogger) - _ <- loadConfig.map { newConfig => - logger.reconfigureIfChanged(newConfig) - }.scheduleFork(updateLogger) - } yield logger + initialConfig <- loadConfig + initialLogger <- makeLogger(initialConfig, None) + reconfigurableLogger = ReconfigurableLogger[M, O, C](initialConfig, initialLogger) + _ <- loadConfig.flatMap { newConfig => + val (currentConfig, currentLogger) = reconfigurableLogger.get + if (currentConfig !== newConfig) { + makeLogger(newConfig, Some(currentLogger)).map { newLogger => + reconfigurableLogger.set(newConfig, newLogger) + }.unit + } else ZIO.unit + }.schedule(updateLogger).forkDaemon + } yield reconfigurableLogger } diff --git a/core/shared/src/main/scala/zio/logging/internal/ReconfigurableLogger2.scala b/core/shared/src/main/scala/zio/logging/internal/ReconfigurableLogger2.scala deleted file mode 100644 index 62dadbf1a..000000000 --- a/core/shared/src/main/scala/zio/logging/internal/ReconfigurableLogger2.scala +++ /dev/null @@ -1,79 +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.internal - -import zio._ -import zio.prelude._ - -import java.util.concurrent.atomic.AtomicReference - -private[logging] sealed trait ReconfigurableLogger2[-Message, +Output, Config] extends ZLogger[Message, Output] { - - def get: (Config, ZLogger[Message, Output]) - - private[logging] def set[M <: Message, O >: Output](config: Config, logger: ZLogger[M, O]): Unit -} - -private[logging] object ReconfigurableLogger2 { - - def apply[Message, Output, Config]( - config: Config, - logger: ZLogger[Message, Output] - ): ReconfigurableLogger2[Message, Output, Config] = { - val configuredLogger: AtomicReference[(Config, ZLogger[Message, Output])] = - new AtomicReference[(Config, ZLogger[Message, Output])]((config, logger)) - - new ReconfigurableLogger2[Message, Output, Config] { - - override def get: (Config, ZLogger[Message, Output]) = configuredLogger.get() - - override private[logging] def set[M <: Message, O >: Output](config: Config, logger: ZLogger[M, O]): Unit = - configuredLogger.set((config, logger.asInstanceOf[ZLogger[Message, Output]])) - - override def apply( - trace: Trace, - fiberId: FiberId, - logLevel: LogLevel, - message: () => Message, - cause: Cause[Any], - context: FiberRefs, - spans: List[LogSpan], - annotations: Map[String, String] - ): Output = - configuredLogger.get()._2.apply(trace, fiberId, logLevel, message, cause, context, spans, annotations) - } - } - - def make[E, M, O, C: Equal]( - loadConfig: => ZIO[Any, E, C], - makeLogger: (C, Option[ZLogger[M, O]]) => ZIO[Any, E, ZLogger[M, O]], - updateLogger: Schedule[Any, Any, Any] = Schedule.fixed(10.seconds) - ): ZIO[Scope, E, ReconfigurableLogger2[M, O, C]] = - for { - initialConfig <- loadConfig - initialLogger <- makeLogger(initialConfig, None) - reconfigurableLogger = ReconfigurableLogger2[M, O, C](initialConfig, initialLogger) - _ <- loadConfig.flatMap { newConfig => - val (currentConfig, currentLogger) = reconfigurableLogger.get - if (currentConfig !== newConfig) { - makeLogger(newConfig, Some(currentLogger)).map { newLogger => - reconfigurableLogger.set(newConfig, newLogger) - }.unit - } else ZIO.unit - }.scheduleFork(updateLogger) - } yield reconfigurableLogger - -} diff --git a/examples/core/src/main/scala/zio/logging/example/ConfigurableLoggerApp.scala b/examples/core/src/main/scala/zio/logging/example/ConfigurableLoggerApp.scala index 447b0de30..dd84043ea 100644 --- a/examples/core/src/main/scala/zio/logging/example/ConfigurableLoggerApp.scala +++ b/examples/core/src/main/scala/zio/logging/example/ConfigurableLoggerApp.scala @@ -38,7 +38,7 @@ object ConfigurableLoggerApp extends ZIOAppDefault { .asInstanceOf[LogFilter.ConfiguredFilter[String, LogFilter.LogLevelByNameConfig]] .config - ConfigurableLogger.make2(logger, filterConfig) + ConfigurableLogger.make(logger, filterConfig) } } .install diff --git a/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala b/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala index abc5ff2ff..b3ff8b763 100644 --- a/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala +++ b/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala @@ -15,8 +15,8 @@ */ package zio.logging.example -import zio.logging.internal.ReconfigurableLogger2 -import zio.logging.{ ConfigurableLogger, ConsoleLoggerConfig, LogAnnotation, LogFilter, LoggerConfigurer, LoggerLayers } +import zio.logging.internal.ReconfigurableLogger +import zio.logging.{ ConsoleLoggerConfig, LogAnnotation, LoggerLayers } import zio.{ Config, ExitCode, Runtime, Scope, ZIO, ZIOAppDefault, _ } import java.util.UUID @@ -30,18 +30,16 @@ object LoggerReconfigureApp extends ZIOAppDefault { loadConfig: => ZIO[Any, Config.Error, ConsoleLoggerConfig] ) = { import LoggerLayers._ - ZLayer.scoped { - ReconfigurableLogger2 - .make[Config.Error, String, Any, ConsoleLoggerConfig]( - loadConfig, - (config, _) => - makeSystemOutLogger(config.format.toLogger).map { logger => - config.filter.filter(logger) - }, - Schedule.fixed(500.millis) - ) - .flatMap(logger => ZIO.withLoggerScoped(logger)) - } + ReconfigurableLogger + .make[Any, Config.Error, String, Any, ConsoleLoggerConfig]( + loadConfig, + (config, _) => + makeSystemOutLogger(config.format.toLogger).map { logger => + config.filter.filter(logger) + }, + Schedule.fixed(500.millis) + ) + .install } override val bootstrap: ZLayer[ZIOAppArgs, Any, Any] = From 6c7b154a2ddb4b27bad9bbbe40feaa37aa9b86b5 Mon Sep 17 00:00:00 2001 From: Peter Kotula Date: Wed, 28 Jun 2023 22:07:04 +0200 Subject: [PATCH 35/56] removed deprecated code --- .../main/scala/zio/logging/LogFormat.scala | 4 - .../src/main/scala/zio/logging/package.scala | 184 ------------------ .../main/scala/zio/logging/backend/JPL.scala | 39 +--- .../scala/zio/logging/backend/JPLSpec.scala | 57 ------ .../logging/slf4j/bridge/Slf4jBridge.scala | 27 +-- .../slf4j/bridge/ZioLoggerRuntime.scala | 4 +- .../slf4j/bridge/Slf4jBridgeSpec.scala | 141 ++++---------- .../main/scala/zio/logging/slf4j/SLF4J.scala | 43 +--- .../scala/zio/logging/backend/SLF4JSpec.scala | 64 ------ 9 files changed, 48 insertions(+), 515 deletions(-) diff --git a/core/shared/src/main/scala/zio/logging/LogFormat.scala b/core/shared/src/main/scala/zio/logging/LogFormat.scala index fa9f2a93d..bd07af2a7 100644 --- a/core/shared/src/main/scala/zio/logging/LogFormat.scala +++ b/core/shared/src/main/scala/zio/logging/LogFormat.scala @@ -881,10 +881,6 @@ object LogFormat { } } - @deprecated("use LogFormat.filter", "2.1.2") - def ifCauseNonEmpty(format: LogFormat): LogFormat = - format.filter(LogFilter.causeNonEmpty) - def label(label: => String, value: LogFormat): LogFormat = LabelFormat(label, value) val newLine: LogFormat = text(NL) diff --git a/core/shared/src/main/scala/zio/logging/package.scala b/core/shared/src/main/scala/zio/logging/package.scala index f888e5af6..d27cfd638 100644 --- a/core/shared/src/main/scala/zio/logging/package.scala +++ b/core/shared/src/main/scala/zio/logging/package.scala @@ -62,48 +62,6 @@ package object logging { def loggerName(value: String): ZIOAspect[Nothing, Any, Nothing, Any, Nothing, Any] = ZIOAspect.annotated(loggerNameAnnotationKey, value) - @deprecated("use zio.logging.consoleLogger", "2.1.10") - def console( - format: LogFormat = LogFormat.colored, - logLevel: LogLevel = LogLevel.Info - ): ZLayer[Any, Nothing, Unit] = - console(format, LogFilter.logLevel(logLevel)) - - @deprecated("use zio.logging.consoleLogger", "2.1.10") - def console( - format: LogFormat, - logFilter: LogFilter[String] - ): ZLayer[Any, Nothing, Unit] = - consoleLogger(ConsoleLoggerConfig(format, logFilter)) - - @deprecated("use zio.logging.consoleErrLogger", "2.1.10") - def consoleErr( - format: LogFormat = LogFormat.default, - logLevel: LogLevel = LogLevel.Info - ): ZLayer[Any, Nothing, Unit] = - consoleErr(format, LogFilter.logLevel(logLevel)) - - @deprecated("use zio.logging.consoleErrLogger", "2.1.10") - def consoleErr( - format: LogFormat, - logFilter: LogFilter[String] - ): ZLayer[Any, Nothing, Unit] = - consoleErrLogger(ConsoleLoggerConfig(format, logFilter)) - - @deprecated("use zio.logging.consoleErrJsonLogger", "2.1.10") - def consoleErrJson( - format: LogFormat = LogFormat.default, - logLevel: LogLevel = LogLevel.Info - ): ZLayer[Any, Nothing, Unit] = - consoleErrJson(format, LogFilter.logLevel(logLevel)) - - @deprecated("use zio.logging.consoleErrJsonLogger", "2.1.10") - def consoleErrJson( - format: LogFormat, - logFilter: LogFilter[String] - ): ZLayer[Any, Nothing, Unit] = - consoleErrJsonLogger(ConsoleLoggerConfig(format, logFilter)) - def consoleErrLogger(config: ConsoleLoggerConfig): ZLayer[Any, Nothing, Unit] = Runtime.addLogger(makeConsoleErrLogger(config)) @@ -126,20 +84,6 @@ package object logging { } yield () } - @deprecated("use zio.logging.consoleJsonLogger", "2.1.10") - def consoleJson( - format: LogFormat = LogFormat.default, - logLevel: LogLevel = LogLevel.Info - ): ZLayer[Any, Nothing, Unit] = - consoleJson(format, LogFilter.logLevel(logLevel)) - - @deprecated("use zio.logging.consoleJsonLogger", "2.1.10") - def consoleJson( - format: LogFormat, - logFilter: LogFilter[String] - ): ZLayer[Any, Nothing, Unit] = - consoleJsonLogger(ConsoleLoggerConfig(format, logFilter)) - def consoleJsonLogger(config: ConsoleLoggerConfig): ZLayer[Any, Nothing, Unit] = Runtime.addLogger(makeConsoleJsonLogger(config)) @@ -162,134 +106,6 @@ package object logging { } yield () } - @deprecated("use zio.logging.fileLogger", "2.1.10") - def file( - destination: Path, - format: LogFormat = LogFormat.default, - logLevel: LogLevel = LogLevel.Info, - charset: Charset = StandardCharsets.UTF_8, - autoFlushBatchSize: Int = 1, - bufferedIOSize: Option[Int] = None, - rollingPolicy: Option[FileLoggerConfig.FileRollingPolicy] = None - ): ZLayer[Any, Nothing, Unit] = - file(destination, format, LogFilter.logLevel(logLevel), charset, autoFlushBatchSize, bufferedIOSize, rollingPolicy) - - @deprecated("use zio.logging.fileLogger", "2.1.10") - def file( - destination: Path, - format: LogFormat, - logFilter: LogFilter[String], - charset: Charset, - autoFlushBatchSize: Int, - bufferedIOSize: Option[Int], - rollingPolicy: Option[FileLoggerConfig.FileRollingPolicy] - ): ZLayer[Any, Nothing, Unit] = - fileLogger( - FileLoggerConfig(destination, format, logFilter, charset, autoFlushBatchSize, bufferedIOSize, rollingPolicy) - ) - - @deprecated("use zio.logging.fileAsyncLogger", "2.1.10") - def fileAsync( - destination: Path, - format: LogFormat = LogFormat.default, - logLevel: LogLevel = LogLevel.Info, - charset: Charset = StandardCharsets.UTF_8, - autoFlushBatchSize: Int = 1, - bufferedIOSize: Option[Int] = None, - rollingPolicy: Option[FileLoggerConfig.FileRollingPolicy] = None - ): ZLayer[Any, Nothing, Unit] = - fileAsync( - destination, - format, - LogFilter.logLevel(logLevel), - charset, - autoFlushBatchSize, - bufferedIOSize, - rollingPolicy - ) - - @deprecated("use zio.logging.fileAsyncLogger", "2.1.10") - def fileAsync( - destination: Path, - format: LogFormat, - logFilter: LogFilter[String], - charset: Charset, - autoFlushBatchSize: Int, - bufferedIOSize: Option[Int], - rollingPolicy: Option[FileLoggerConfig.FileRollingPolicy] - ): ZLayer[Any, Nothing, Unit] = - fileAsyncLogger( - FileLoggerConfig(destination, format, logFilter, charset, autoFlushBatchSize, bufferedIOSize, rollingPolicy) - ) - - @deprecated("use zio.logging.fileJsonLogger", "2.1.10") - def fileJson( - destination: Path, - format: LogFormat = LogFormat.default, - logLevel: LogLevel = LogLevel.Info, - charset: Charset = StandardCharsets.UTF_8, - autoFlushBatchSize: Int = 1, - bufferedIOSize: Option[Int] = None, - rollingPolicy: Option[FileLoggerConfig.FileRollingPolicy] = None - ): ZLayer[Any, Nothing, Unit] = - fileJson( - destination, - format, - LogFilter.logLevel(logLevel), - charset, - autoFlushBatchSize, - bufferedIOSize, - rollingPolicy - ) - - @deprecated("use zio.logging.fileJsonLogger", "2.1.10") - def fileJson( - destination: Path, - format: LogFormat, - logFilter: LogFilter[String], - charset: Charset, - autoFlushBatchSize: Int, - bufferedIOSize: Option[Int], - rollingPolicy: Option[FileLoggerConfig.FileRollingPolicy] - ): ZLayer[Any, Nothing, Unit] = - fileJsonLogger( - FileLoggerConfig(destination, format, logFilter, charset, autoFlushBatchSize, bufferedIOSize, rollingPolicy) - ) - - @deprecated("use zio.logging.fileAsyncJsonLogger", "2.1.10") - def fileAsyncJson( - destination: Path, - format: LogFormat = LogFormat.default, - logLevel: LogLevel = LogLevel.Info, - charset: Charset = StandardCharsets.UTF_8, - autoFlushBatchSize: Int = 1, - bufferedIOSize: Option[Int] = None, - rollingPolicy: Option[FileLoggerConfig.FileRollingPolicy] = None - ): ZLayer[Any, Nothing, Unit] = - fileAsyncJson( - destination, - format, - LogFilter.logLevel(logLevel), - charset, - autoFlushBatchSize, - bufferedIOSize, - rollingPolicy - ) - - @deprecated("use zio.logging.fileAsyncJsonLogger", "2.1.10") - def fileAsyncJson( - destination: Path, - format: LogFormat, - logFilter: LogFilter[String], - charset: Charset, - autoFlushBatchSize: Int, - bufferedIOSize: Option[Int], - rollingPolicy: Option[FileLoggerConfig.FileRollingPolicy] - ): ZLayer[Any, Nothing, Unit] = - fileAsyncJsonLogger( - FileLoggerConfig(destination, format, logFilter, charset, autoFlushBatchSize, bufferedIOSize, rollingPolicy) - ) - def fileAsyncJsonLogger(config: FileLoggerConfig): ZLayer[Any, Nothing, Unit] = ZLayer.scoped(makeFileAsyncJsonLogger(config)) diff --git a/jpl/src/main/scala/zio/logging/backend/JPL.scala b/jpl/src/main/scala/zio/logging/backend/JPL.scala index 44d6507b3..a7730c7ad 100644 --- a/jpl/src/main/scala/zio/logging/backend/JPL.scala +++ b/jpl/src/main/scala/zio/logging/backend/JPL.scala @@ -17,20 +17,7 @@ package zio.logging.backend import zio.logging.internal.LogAppender import zio.logging.{ LogFormat, LoggerNameExtractor } -import zio.{ - Cause, - FiberFailure, - FiberId, - FiberRefs, - LogLevel, - LogSpan, - Runtime, - Trace, - ZIOAspect, - ZLayer, - ZLogger, - logging -} +import zio.{ Cause, FiberFailure, FiberId, FiberRefs, LogLevel, LogSpan, Runtime, Trace, ZLayer, ZLogger, logging } object JPL { @@ -45,28 +32,11 @@ object JPL { LogLevel.None -> System.Logger.Level.OFF ) - /** - * log aspect annotation key for JPL logger name - */ - @deprecated("use zio.logging.loggerNameAnnotationKey", "2.1.8") - val loggerNameAnnotationKey = "jpl_logger_name" - /** * default log format for JPL logger */ val logFormatDefault: LogFormat = - LogFormat.allAnnotations(excludeKeys = - Set(JPL.loggerNameAnnotationKey, logging.loggerNameAnnotationKey) - ) + LogFormat.line + LogFormat.cause - - /** - * JPL logger name aspect, by this aspect is possible to change default logger name (default logger name is extracted from [[Trace]]) - * - * annotation key: [[JPL.loggerNameAnnotationKey]] - */ - @deprecated("use zio.logging.loggerName", "2.1.8") - def loggerName(value: String): ZIOAspect[Nothing, Any, Nothing, Any, Nothing, Any] = - ZIOAspect.annotated(loggerNameAnnotationKey, value) + LogFormat.allAnnotations(excludeKeys = Set(logging.loggerNameAnnotationKey)) + LogFormat.line + LogFormat.cause private[backend] def getLoggerName(default: String = "zio-jpl-logger"): Trace => String = trace => LoggerNameExtractor.trace(trace, FiberRefs.empty, Map.empty).getOrElse(default) @@ -165,10 +135,7 @@ object JPL { spans: List[LogSpan], annotations: Map[String, String] ): Unit = { - val jpLoggerName = annotations.getOrElse( - JPL.loggerNameAnnotationKey, - annotations.getOrElse(zio.logging.loggerNameAnnotationKey, loggerName(trace)) - ) + val jpLoggerName = annotations.getOrElse(zio.logging.loggerNameAnnotationKey, loggerName(trace)) val jpLogger = getJPLogger(jpLoggerName) if (isLogLevelEnabled(jpLogger, logLevel)) { val appender = logAppender(jpLogger, logLevel) diff --git a/jpl/src/test/scala/zio/logging/backend/JPLSpec.scala b/jpl/src/test/scala/zio/logging/backend/JPLSpec.scala index 6673fea81..6061c4eaa 100644 --- a/jpl/src/test/scala/zio/logging/backend/JPLSpec.scala +++ b/jpl/src/test/scala/zio/logging/backend/JPLSpec.scala @@ -96,25 +96,6 @@ object JPLSpec extends ZIOSpecDefault { ) } }.provide(loggerDefault), - test("log with custom logger name - legacy") { - val loggerName = "my-logger" - (startStop() @@ JPL.loggerName(loggerName)).map { case (traceId, users) => - val loggerOutput = TestAppender.logOutput - assertTrue(loggerOutput.size == 5) && assertTrue( - loggerOutput.forall(_.loggerName == loggerName) - ) && assertTrue(loggerOutput.forall(_.logLevel == LogLevel.Info)) && assert(loggerOutput.map(_.message))( - equalTo( - Chunk( - s"user=${users(0)} trace_id=$traceId Starting operation", - s"user=${users(0)} trace_id=$traceId Stopping operation", - s"user=${users(1)} trace_id=$traceId Starting operation", - s"user=${users(1)} trace_id=$traceId Stopping operation", - s"Done" - ) - ) - ) - } - }.provide(loggerDefault), test("log with custom logger name") { val loggerName = "my-logger" (startStop() @@ zio.logging.loggerName(loggerName)).map { case (traceId, users) => @@ -152,36 +133,6 @@ object JPLSpec extends ZIOSpecDefault { ) } }.provide(loggerTraceAnnotation), - test("logger name changes - legacy logger name annotation") { - val users = Chunk.fill(2)(UUID.randomUUID()) - for { - traceId <- ZIO.succeed(UUID.randomUUID()) - _ = TestAppender.reset() - _ <- ZIO.logInfo("Start") @@ JPL.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) @@ JPL.loggerName("user-logger") - } @@ LogAnnotation.TraceId(traceId) @@ JPL.loggerName("user-root-logger") - _ <- ZIO.logInfo("Done") @@ JPL.loggerName("root-logger") - } yield { - val loggerOutput = TestAppender.logOutput - assertTrue(loggerOutput.forall(_.logLevel == LogLevel.Info)) && assert(loggerOutput.map(_.loggerName))( - equalTo( - Chunk( - "root-logger", - "user-logger", - "user-logger", - "user-logger", - "user-logger", - "root-logger" - ) - ) - ) - } - }.provide(loggerDefault), test("logger name changes") { val users = Chunk.fill(2)(UUID.randomUUID()) for { @@ -218,14 +169,6 @@ object JPLSpec extends ZIOSpecDefault { 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(_.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 diff --git a/slf4j-bridge/src/main/scala/zio/logging/slf4j/bridge/Slf4jBridge.scala b/slf4j-bridge/src/main/scala/zio/logging/slf4j/bridge/Slf4jBridge.scala index 686782578..a9d354067 100644 --- a/slf4j-bridge/src/main/scala/zio/logging/slf4j/bridge/Slf4jBridge.scala +++ b/slf4j-bridge/src/main/scala/zio/logging/slf4j/bridge/Slf4jBridge.scala @@ -20,41 +20,26 @@ import zio.{ Runtime, Semaphore, Unsafe, ZIO, ZLayer } object Slf4jBridge { - /** - * log annotation key for slf4j logger name - */ - @deprecated("use zio.logging.loggerNameAnnotationKey", "2.1.8") - val loggerNameAnnotationKey: String = "slf4j_logger_name" - /** * initialize SLF4J bridge */ def initialize: ZLayer[Any, Nothing, Unit] = - Runtime.enableCurrentFiber ++ layer(zio.logging.loggerNameAnnotationKey) + Runtime.enableCurrentFiber ++ layer /** * initialize SLF4J bridge without `FiberRef` propagation */ - def initializeWithoutFiberRefPropagation: ZLayer[Any, Nothing, Unit] = layer(zio.logging.loggerNameAnnotationKey) - - /** - * initialize SLF4J bridge, where custom annotation key for logger name may be provided - * this is to achieve backward compatibility where [[Slf4jBridge.loggerNameAnnotationKey]] was used - * - * NOTE: this feature may be removed in future releases - */ - def initialize(nameAnnotationKey: String): ZLayer[Any, Nothing, Unit] = - Runtime.enableCurrentFiber ++ layer(nameAnnotationKey) + def initializeWithoutFiberRefPropagation: ZLayer[Any, Nothing, Unit] = layer private val initLock = Semaphore.unsafe.make(1)(Unsafe.unsafe) - private def layer(nameAnnotationKey: String): ZLayer[Any, Nothing, Unit] = + private def layer: ZLayer[Any, Nothing, Unit] = ZLayer { for { runtime <- ZIO.runtime[Any] - _ <- initLock.withPermit { - ZIO.succeed(ZioLoggerFactory.initialize(new ZioLoggerRuntime(runtime, nameAnnotationKey))) - } + _ <- initLock.withPermit { + ZIO.succeed(ZioLoggerFactory.initialize(new ZioLoggerRuntime(runtime))) + } } yield () } } diff --git a/slf4j-bridge/src/main/scala/zio/logging/slf4j/bridge/ZioLoggerRuntime.scala b/slf4j-bridge/src/main/scala/zio/logging/slf4j/bridge/ZioLoggerRuntime.scala index 44d4941d6..895e851bd 100644 --- a/slf4j-bridge/src/main/scala/zio/logging/slf4j/bridge/ZioLoggerRuntime.scala +++ b/slf4j-bridge/src/main/scala/zio/logging/slf4j/bridge/ZioLoggerRuntime.scala @@ -21,7 +21,7 @@ import org.slf4j.helpers.MessageFormatter import org.slf4j.impl.LoggerRuntime import zio.{ Cause, Fiber, FiberId, FiberRef, FiberRefs, LogLevel, Runtime, Trace, Unsafe } -final class ZioLoggerRuntime(runtime: Runtime[Any], loggerNameAnnotationKey: String) extends LoggerRuntime { +final class ZioLoggerRuntime(runtime: Runtime[Any]) extends LoggerRuntime { override def log( name: String, @@ -44,7 +44,7 @@ final class ZioLoggerRuntime(runtime: Runtime[Any], loggerNameAnnotationKey: Str } val logSpan = zio.LogSpan(name, java.lang.System.currentTimeMillis()) - val loggerName = (loggerNameAnnotationKey -> name) + val loggerName = (zio.logging.loggerNameAnnotationKey -> name) val fiberRefs = currentFiberRefs .updatedAs(fiberId)(FiberRef.currentLogSpan, logSpan :: currentFiberRefs.getOrDefault(FiberRef.currentLogSpan)) diff --git a/slf4j-bridge/src/test/scala/zio/logging/slf4j/bridge/Slf4jBridgeSpec.scala b/slf4j-bridge/src/test/scala/zio/logging/slf4j/bridge/Slf4jBridgeSpec.scala index 86bcb756e..4c725cffa 100644 --- a/slf4j-bridge/src/test/scala/zio/logging/slf4j/bridge/Slf4jBridgeSpec.scala +++ b/slf4j-bridge/src/test/scala/zio/logging/slf4j/bridge/Slf4jBridgeSpec.scala @@ -8,12 +8,12 @@ import zio.{ Cause, Chunk, LogLevel, ZIO, ZIOAspect } object Slf4jBridgeSpec extends ZIOSpecDefault { final case class LogEntry( - span: List[String], - level: LogLevel, - annotations: Map[String, String], - message: String, - cause: Cause[Any] - ) + span: List[String], + level: LogLevel, + annotations: Map[String, String], + message: String, + cause: Cause[Any] + ) override def spec = suite("Slf4jBridge")( @@ -27,77 +27,6 @@ object Slf4jBridgeSpec extends ZIOSpecDefault { } } yield assertCompletes }, - test("logs through slf4j - legacy logger name annotation key") { - val testFailure = new RuntimeException("test error") - for { - _ <- - (for { - logger <- ZIO.attempt(org.slf4j.LoggerFactory.getLogger("test.logger")) - _ <- ZIO.logSpan("span")(ZIO.attempt(logger.debug("test debug message"))) @@ ZIOAspect - .annotated("trace_id", "tId") - _ <- ZIO.attempt(logger.warn("hello {}", "world")) @@ ZIOAspect.annotated("user_id", "uId") - _ <- ZIO.attempt(logger.warn("{}..{}..{} ... go!", "3", "2", "1")) - _ <- ZIO.attempt(logger.warn("warn cause", testFailure)) - _ <- ZIO.attempt(logger.error("error", testFailure)) - _ <- ZIO.attempt(logger.error("error", null)) - } yield ()).exit - output <- ZTestLogger.logOutput - lines = output.map { logEntry => - LogEntry( - logEntry.spans.map(_.label), - logEntry.logLevel, - logEntry.annotations, - logEntry.message(), - logEntry.cause - ) - } - } yield assertTrue( - lines == Chunk( - LogEntry( - List("test.logger", "span"), - LogLevel.Debug, - Map(Slf4jBridge.loggerNameAnnotationKey -> "test.logger", "trace_id" -> "tId"), - "test debug message", - Cause.empty - ), - LogEntry( - List("test.logger"), - LogLevel.Warning, - Map(Slf4jBridge.loggerNameAnnotationKey -> "test.logger", "user_id" -> "uId"), - "hello world", - Cause.empty - ), - LogEntry( - List("test.logger"), - LogLevel.Warning, - Map(Slf4jBridge.loggerNameAnnotationKey -> "test.logger"), - "3..2..1 ... go!", - Cause.empty - ), - LogEntry( - List("test.logger"), - LogLevel.Warning, - Map(Slf4jBridge.loggerNameAnnotationKey -> "test.logger"), - "warn cause", - Cause.die(testFailure) - ), - LogEntry( - List("test.logger"), - LogLevel.Error, - Map(Slf4jBridge.loggerNameAnnotationKey -> "test.logger"), - "error", - Cause.die(testFailure) - ), - LogEntry( - List("test.logger"), - LogLevel.Error, - Map(Slf4jBridge.loggerNameAnnotationKey -> "test.logger"), - "error", - Cause.empty - ) - ) - ) - }.provide(Slf4jBridge.initialize(Slf4jBridge.loggerNameAnnotationKey)), test("Implements Logger#getName") { for { logger <- ZIO.attempt(org.slf4j.LoggerFactory.getLogger("zio.test.logger")) @@ -112,25 +41,25 @@ object Slf4jBridgeSpec extends ZIOSpecDefault { val testFailure = new RuntimeException("test error") for { _ <- (for { - logger <- ZIO.attempt(org.slf4j.LoggerFactory.getLogger("test.logger")) - _ <- ZIO.logSpan("span")(ZIO.attempt(logger.debug("test debug message"))) @@ ZIOAspect - .annotated("trace_id", "tId") - _ <- ZIO.attempt(logger.warn("hello {}", "world")) @@ ZIOAspect.annotated("user_id", "uId") - _ <- ZIO.attempt(logger.warn("{}..{}..{} ... go!", "3", "2", "1")) - _ <- ZIO.attempt(logger.warn("warn cause", testFailure)) - _ <- ZIO.attempt(logger.error("error", testFailure)) - _ <- ZIO.attempt(logger.error("error", null)) - } yield ()).exit + logger <- ZIO.attempt(org.slf4j.LoggerFactory.getLogger("test.logger")) + _ <- ZIO.logSpan("span")(ZIO.attempt(logger.debug("test debug message"))) @@ ZIOAspect + .annotated("trace_id", "tId") + _ <- ZIO.attempt(logger.warn("hello {}", "world")) @@ ZIOAspect.annotated("user_id", "uId") + _ <- ZIO.attempt(logger.warn("{}..{}..{} ... go!", "3", "2", "1")) + _ <- ZIO.attempt(logger.warn("warn cause", testFailure)) + _ <- ZIO.attempt(logger.error("error", testFailure)) + _ <- ZIO.attempt(logger.error("error", null)) + } yield ()).exit output <- ZTestLogger.logOutput lines = output.map { logEntry => - LogEntry( - logEntry.spans.map(_.label), - logEntry.logLevel, - logEntry.annotations, - logEntry.message(), - logEntry.cause - ) - } + LogEntry( + logEntry.spans.map(_.label), + logEntry.logLevel, + logEntry.annotations, + logEntry.message(), + logEntry.cause + ) + } } yield assertTrue( lines == Chunk( LogEntry( @@ -181,20 +110,20 @@ object Slf4jBridgeSpec extends ZIOSpecDefault { test("logs through slf4j without fiber ref propagation") { for { _ <- (for { - logger <- ZIO.attempt(org.slf4j.LoggerFactory.getLogger("test.logger")) - _ <- ZIO.attempt(logger.debug("test debug message")) @@ ZIOAspect.annotated("trace_id", "tId") - _ <- ZIO.attempt(logger.warn("hello {}", "world")) @@ ZIOAspect.annotated("user_id", "uId") - } yield ()).exit + logger <- ZIO.attempt(org.slf4j.LoggerFactory.getLogger("test.logger")) + _ <- ZIO.attempt(logger.debug("test debug message")) @@ ZIOAspect.annotated("trace_id", "tId") + _ <- ZIO.attempt(logger.warn("hello {}", "world")) @@ ZIOAspect.annotated("user_id", "uId") + } yield ()).exit output <- ZTestLogger.logOutput lines = output.map { logEntry => - LogEntry( - logEntry.spans.map(_.label), - logEntry.logLevel, - logEntry.annotations, - logEntry.message(), - logEntry.cause - ) - } + LogEntry( + logEntry.spans.map(_.label), + logEntry.logLevel, + logEntry.annotations, + logEntry.message(), + logEntry.cause + ) + } } yield assertTrue( lines == Chunk( LogEntry( diff --git a/slf4j/src/main/scala/zio/logging/slf4j/SLF4J.scala b/slf4j/src/main/scala/zio/logging/slf4j/SLF4J.scala index 01dd0d327..03d74da06 100644 --- a/slf4j/src/main/scala/zio/logging/slf4j/SLF4J.scala +++ b/slf4j/src/main/scala/zio/logging/slf4j/SLF4J.scala @@ -37,12 +37,6 @@ import java.util object SLF4J { - /** - * log annotation key for slf4j logger name - */ - @deprecated("use zio.logging.loggerNameAnnotationKey", "2.1.8") - val loggerNameAnnotationKey = "slf4j_logger_name" - /** * log annotation key for slf4j marker name */ @@ -53,18 +47,9 @@ object SLF4J { */ val logFormatDefault: LogFormat = LogFormat.allAnnotations(excludeKeys = - Set(SLF4J.loggerNameAnnotationKey, SLF4J.logMarkerNameAnnotationKey, logging.loggerNameAnnotationKey) + Set(SLF4J.logMarkerNameAnnotationKey, logging.loggerNameAnnotationKey) ) + 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("use zio.logging.loggerName", "2.1.8") - def loggerName(value: String): ZIOAspect[Nothing, Any, Nothing, Any, Nothing, Any] = - ZIOAspect.annotated(loggerNameAnnotationKey, value) - /** * slf4j marker name aspect * @@ -201,27 +186,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 @@ -272,10 +236,7 @@ object SLF4J { spans: List[LogSpan], annotations: Map[String, String] ): Unit = { - val slf4jLoggerName = annotations.getOrElse( - SLF4J.loggerNameAnnotationKey, - annotations.getOrElse(logging.loggerNameAnnotationKey, loggerName(trace)) - ) + val slf4jLoggerName = annotations.getOrElse(logging.loggerNameAnnotationKey, loggerName(trace)) val slf4jLogger = LoggerFactory.getLogger(slf4jLoggerName) val slf4jMarkerName = annotations.get(SLF4J.logMarkerNameAnnotationKey) val slf4jMarker = slf4jMarkerName.map(n => MarkerFactory.getMarker(n)) diff --git a/slf4j/src/test/scala/zio/logging/backend/SLF4JSpec.scala b/slf4j/src/test/scala/zio/logging/backend/SLF4JSpec.scala index 4d82cc898..5dacbd322 100644 --- a/slf4j/src/test/scala/zio/logging/backend/SLF4JSpec.scala +++ b/slf4j/src/test/scala/zio/logging/backend/SLF4JSpec.scala @@ -114,20 +114,6 @@ object SLF4JSpec extends ZIOSpecDefault { ) } }.provide(loggerTraceAnnotation), - test("log all annotations into MDC with custom logger name - legacy") { - (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("log all annotations into MDC with custom logger name") { (startStop() @@ logging.loggerName("my-logger")).map { case (traceId, users) => val loggerOutput = TestAppender.logOutput @@ -142,47 +128,6 @@ object SLF4JSpec extends ZIOSpecDefault { ) } }.provide(loggerDefault), - test("logger name changes - legacy logger name annotation") { - 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("logger name changes") { val users = Chunk.fill(2)(UUID.randomUUID()) for { @@ -230,15 +175,6 @@ object SLF4JSpec extends ZIOSpecDefault { 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(_.getMessage.contains("input < 1")) - ) && - assertTrue(!loggerOutput(0).mdc.contains(SLF4J.loggerNameAnnotationKey)) - } - }.provide(loggerLineCause), test("log error with cause with custom logger name") { (someError() @@ logging.loggerName("my-logger")).map { _ => val loggerOutput = TestAppender.logOutput From dcdcc3716b83436fc0699e6031d8c3cd0a40aefb Mon Sep 17 00:00:00 2001 From: Peter Kotula Date: Wed, 28 Jun 2023 22:16:20 +0200 Subject: [PATCH 36/56] ReconfigurableLogger --- .../{internal => }/ReconfigurableLoggerSpec.scala | 6 ++---- .../main/scala/zio/logging/ConfigurableLogger.scala | 1 - .../logging/{internal => }/ReconfigurableLogger.scala | 10 +++++----- .../zio/logging/example/LoggerReconfigureApp.scala | 3 +-- 4 files changed, 8 insertions(+), 12 deletions(-) rename core/jvm/src/test/scala/zio/logging/{internal => }/ReconfigurableLoggerSpec.scala (95%) rename core/shared/src/main/scala/zio/logging/{internal => }/ReconfigurableLogger.scala (87%) diff --git a/core/jvm/src/test/scala/zio/logging/internal/ReconfigurableLoggerSpec.scala b/core/jvm/src/test/scala/zio/logging/ReconfigurableLoggerSpec.scala similarity index 95% rename from core/jvm/src/test/scala/zio/logging/internal/ReconfigurableLoggerSpec.scala rename to core/jvm/src/test/scala/zio/logging/ReconfigurableLoggerSpec.scala index 671358d91..d2999cbb0 100644 --- a/core/jvm/src/test/scala/zio/logging/internal/ReconfigurableLoggerSpec.scala +++ b/core/jvm/src/test/scala/zio/logging/ReconfigurableLoggerSpec.scala @@ -1,6 +1,5 @@ -package zio.logging.internal +package zio.logging -import zio.logging.{ ConsoleLoggerConfig, _ } import zio.test._ import zio.{ Chunk, Config, ConfigProvider, LogLevel, Queue, Runtime, Schedule, ZIO, ZLayer, _ } @@ -9,7 +8,7 @@ object ReconfigurableLoggerSpec extends ZIOSpecDefault { def configuredLogger( queue: zio.Queue[String], configPath: String = "logger" - ): ZLayer[Any, Config.Error, Unit] = { + ): ZLayer[Any, Config.Error, Unit] = ZLayer.scoped { for { logger <- ReconfigurableLogger @@ -28,7 +27,6 @@ object ReconfigurableLoggerSpec extends ZIOSpecDefault { _ <- ZIO.withLoggerScoped(logger) } yield () } - } val spec: Spec[Environment, Any] = suite("ReconfigurableLogger2")( test("log with changed config") { diff --git a/core/shared/src/main/scala/zio/logging/ConfigurableLogger.scala b/core/shared/src/main/scala/zio/logging/ConfigurableLogger.scala index caebe0e9f..581bac78c 100644 --- a/core/shared/src/main/scala/zio/logging/ConfigurableLogger.scala +++ b/core/shared/src/main/scala/zio/logging/ConfigurableLogger.scala @@ -15,7 +15,6 @@ */ package zio.logging -import zio.logging.internal.ReconfigurableLogger import zio.{ Cause, FiberId, FiberRef, FiberRefs, LogLevel, LogSpan, Trace, ZIO, ZLayer, ZLogger } trait LoggerConfigurer { diff --git a/core/shared/src/main/scala/zio/logging/internal/ReconfigurableLogger.scala b/core/shared/src/main/scala/zio/logging/ReconfigurableLogger.scala similarity index 87% rename from core/shared/src/main/scala/zio/logging/internal/ReconfigurableLogger.scala rename to core/shared/src/main/scala/zio/logging/ReconfigurableLogger.scala index 445664f10..50cf96478 100644 --- a/core/shared/src/main/scala/zio/logging/internal/ReconfigurableLogger.scala +++ b/core/shared/src/main/scala/zio/logging/ReconfigurableLogger.scala @@ -13,18 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package zio.logging.internal +package zio.logging import zio._ import zio.prelude._ import java.util.concurrent.atomic.AtomicReference -private[logging] sealed trait ReconfigurableLogger[-Message, +Output, Config] extends ZLogger[Message, Output] { +sealed trait ReconfigurableLogger[-Message, +Output, Config] extends ZLogger[Message, Output] { def get: (Config, ZLogger[Message, Output]) - private[logging] def set[M <: Message, O >: Output](config: Config, logger: ZLogger[M, O]): Unit + def set[M <: Message, O >: Output](config: Config, logger: ZLogger[M, O]): Unit } private[logging] object ReconfigurableLogger { @@ -40,7 +40,7 @@ private[logging] object ReconfigurableLogger { override def get: (Config, ZLogger[Message, Output]) = configuredLogger.get() - override private[logging] def set[M <: Message, O >: Output](config: Config, logger: ZLogger[M, O]): Unit = + override def set[M <: Message, O >: Output](config: Config, logger: ZLogger[M, O]): Unit = configuredLogger.set((config, logger.asInstanceOf[ZLogger[Message, Output]])) override def apply( @@ -58,7 +58,7 @@ private[logging] object ReconfigurableLogger { } def make[R, E, M, O, C: Equal]( - loadConfig: => ZIO[Any, E, C], + loadConfig: => ZIO[R, E, C], makeLogger: (C, Option[ZLogger[M, O]]) => ZIO[R, E, ZLogger[M, O]], updateLogger: Schedule[R, Any, Any] = Schedule.fixed(10.seconds) ): ZIO[R, E, ReconfigurableLogger[M, O, C]] = diff --git a/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala b/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala index b3ff8b763..81f2c2982 100644 --- a/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala +++ b/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala @@ -15,8 +15,7 @@ */ package zio.logging.example -import zio.logging.internal.ReconfigurableLogger -import zio.logging.{ ConsoleLoggerConfig, LogAnnotation, LoggerLayers } +import zio.logging.{ ConsoleLoggerConfig, LogAnnotation, LoggerLayers, ReconfigurableLogger } import zio.{ Config, ExitCode, Runtime, Scope, ZIO, ZIOAppDefault, _ } import java.util.UUID From 553e82f7e5c1034757f8a7c7a65df02aa6116433 Mon Sep 17 00:00:00 2001 From: Peter Kotula Date: Wed, 28 Jun 2023 23:09:46 +0200 Subject: [PATCH 37/56] LoggerLayers --- .../logging/api/http/ApiEndpointsSpec.scala | 3 +- .../logging/api/http/ApiHandlersSpec.scala | 3 +- .../main/scala/zio/logging/LoggerLayers.scala | 227 +++++++++++---- .../src/main/scala/zio/logging/package.scala | 274 +----------------- .../example/ConfigurableLoggerApp.scala | 14 +- .../example/LoggerReconfigureApp.scala | 6 +- 6 files changed, 180 insertions(+), 347 deletions(-) diff --git a/api-http/src/test/scala/zio/logging/api/http/ApiEndpointsSpec.scala b/api-http/src/test/scala/zio/logging/api/http/ApiEndpointsSpec.scala index 22aec416d..b4656c5db 100644 --- a/api-http/src/test/scala/zio/logging/api/http/ApiEndpointsSpec.scala +++ b/api-http/src/test/scala/zio/logging/api/http/ApiEndpointsSpec.scala @@ -1,10 +1,9 @@ package zio.logging.api.http -import zio.LogLevel import zio.http.codec.PathCodec.literal import zio.http.codec._ import zio.test._ -import zio.Scope +import zio.{ LogLevel, Scope } object ApiEndpointsSpec extends ZIOSpecDefault { diff --git a/api-http/src/test/scala/zio/logging/api/http/ApiHandlersSpec.scala b/api-http/src/test/scala/zio/logging/api/http/ApiHandlersSpec.scala index a31c45065..6a1d861be 100644 --- a/api-http/src/test/scala/zio/logging/api/http/ApiHandlersSpec.scala +++ b/api-http/src/test/scala/zio/logging/api/http/ApiHandlersSpec.scala @@ -4,8 +4,7 @@ import zio.http._ import zio.http.codec._ import zio.logging.LoggerConfigurer import zio.test._ -import zio.{ LogLevel, ZIO, ZLayer } -import zio.ULayer +import zio.{ LogLevel, ULayer, ZIO, ZLayer } object ApiHandlersSpec extends ZIOSpecDefault { diff --git a/core/shared/src/main/scala/zio/logging/LoggerLayers.scala b/core/shared/src/main/scala/zio/logging/LoggerLayers.scala index 68bcf9983..38ec3c76b 100644 --- a/core/shared/src/main/scala/zio/logging/LoggerLayers.scala +++ b/core/shared/src/main/scala/zio/logging/LoggerLayers.scala @@ -16,13 +16,94 @@ package zio.logging import zio.metrics.Metric -import zio.{ Queue, Runtime, Scope, Tag, UIO, ULayer, ZIO, ZLayer, ZLogger } +import zio.{ Config, Queue, Runtime, Scope, Tag, UIO, ZIO, ZLayer, ZLogger } import java.io.PrintStream import java.nio.charset.Charset import java.nio.file.Path -object LoggerLayers { +private[logging] trait LoggerLayers { + + private[logging] val logLevelMetricLabel = "level" + + private[logging] val loggedTotalMetric = Metric.counter(name = "zio_log_total") + + val logMetrics: ZLayer[Any, Nothing, Unit] = + makeMetricLogger(loggedTotalMetric, logLevelMetricLabel).install + + def logMetricsWith(name: String, logLevelLabel: String): ZLayer[Any, Nothing, Unit] = + makeMetricLogger(Metric.counter(name), logLevelLabel).install + + def consoleErrLogger(config: ConsoleLoggerConfig): ZLayer[Any, Nothing, Unit] = + makeConsoleErrLogger(config).install + + def consoleErrJsonLogger(config: ConsoleLoggerConfig): ZLayer[Any, Nothing, Unit] = + makeConsoleErrJsonLogger(config).install + + def consoleErrJsonLogger(configPath: String = "logger"): ZLayer[Any, Config.Error, Unit] = + ConsoleLoggerConfig.load(configPath).flatMap(makeConsoleErrJsonLogger).install + + def consoleErrLogger(configPath: String = "logger"): ZLayer[Any, Config.Error, Unit] = + ConsoleLoggerConfig.load(configPath).flatMap(makeConsoleErrLogger).install + + def consoleJsonLogger(config: ConsoleLoggerConfig): ZLayer[Any, Nothing, Unit] = + makeConsoleJsonLogger(config).install + + def consoleJsonLogger(configPath: String = "logger"): ZLayer[Any, Config.Error, Unit] = + ConsoleLoggerConfig.load(configPath).flatMap(makeConsoleJsonLogger).install + + def consoleLogger(config: ConsoleLoggerConfig): ZLayer[Any, Nothing, Unit] = + makeConsoleLogger(config).install + + def consoleLogger(configPath: String = "logger"): ZLayer[Any, Config.Error, Unit] = + ConsoleLoggerConfig.load(configPath).flatMap(makeConsoleLogger).install + + def fileAsyncJsonLogger(config: FileLoggerConfig): ZLayer[Any, Nothing, Unit] = + ZLayer.scoped { + for { + logger <- makeFileAsyncJsonLogger(config) + _ <- ZIO.withLoggerScoped(logger) + } yield () + } + + def fileAsyncJsonLogger(configPath: String = "logger"): ZLayer[Any, Config.Error, Unit] = + ZLayer.scoped { + for { + config <- FileLoggerConfig.load(configPath) + logger <- makeFileAsyncJsonLogger(config) + _ <- ZIO.withLoggerScoped(logger) + } yield () + } + + def fileAsyncLogger(config: FileLoggerConfig): ZLayer[Any, Nothing, Unit] = + ZLayer.scoped { + for { + logger <- makeFileAsyncLogger(config) + _ <- ZIO.withLoggerScoped(logger) + } yield () + } + + def fileAsyncLogger(configPath: String = "logger"): ZLayer[Any, Config.Error, Unit] = + ZLayer.scoped { + for { + config <- FileLoggerConfig.load(configPath) + logger <- makeFileAsyncLogger(config) + _ <- ZIO.withLoggerScoped(logger) + } yield () + } + + def fileJsonLogger(config: FileLoggerConfig): ZLayer[Any, Nothing, Unit] = + makeFileJsonLogger(config).install + + def fileJsonLogger(configPath: String = "logger"): ZLayer[Any, Config.Error, Unit] = + FileLoggerConfig.load(configPath).flatMap(makeFileJsonLogger).install + + def fileLogger(config: FileLoggerConfig): ZLayer[Any, Nothing, Unit] = + makeFileLogger(config).install + + def fileLogger(configPath: String = "logger"): ZLayer[Any, Config.Error, Unit] = + FileLoggerConfig.load(configPath).flatMap(makeFileLogger).install + // // def makeConsoleErrLogger(config: ConsoleLoggerConfig): ULayer[ZLogger[String, Any]] = // makeSystemErrLogger(config.format.toLogger).project(logger => FilteredLogger(logger, config.filter)) @@ -72,27 +153,15 @@ object LoggerLayers { def makeConsoleErrLogger(config: ConsoleLoggerConfig): ZIO[Any, Nothing, ZLogger[String, Any]] = makeSystemErrLogger(config.format.toLogger).map(logger => FilteredLogger(logger, config.filter)) - def makeConsoleErrLogger: ZIO[ConsoleLoggerConfig, Nothing, ZLogger[String, Any]] = - ZIO.serviceWithZIO[ConsoleLoggerConfig](makeConsoleErrLogger(_)) - def makeConsoleErrJsonLogger(config: ConsoleLoggerConfig): ZIO[Any, Nothing, ZLogger[String, Any]] = makeSystemErrLogger(config.format.toJsonLogger).map(logger => FilteredLogger(logger, config.filter)) - def makeConsoleErrJsonLogger: ZIO[ConsoleLoggerConfig, Nothing, ZLogger[String, Any]] = - ZIO.serviceWithZIO[ConsoleLoggerConfig](makeConsoleErrJsonLogger(_)) - def makeConsoleLogger(config: ConsoleLoggerConfig): ZIO[Any, Nothing, ZLogger[String, Any]] = makeSystemOutLogger(config.format.toLogger).map(logger => FilteredLogger(logger, config.filter)) - def makeConsoleLogger: ZIO[ConsoleLoggerConfig, Nothing, ZLogger[String, Any]] = - ZIO.serviceWithZIO[ConsoleLoggerConfig](makeConsoleLogger(_)) - def makeConsoleJsonLogger(config: ConsoleLoggerConfig): ZIO[Any, Nothing, ZLogger[String, Any]] = makeSystemOutLogger(config.format.toJsonLogger).map(logger => FilteredLogger(logger, config.filter)) - def makeConsoleJsonLogger: ZIO[ConsoleLoggerConfig, Nothing, ZLogger[String, Any]] = - ZIO.serviceWithZIO[ConsoleLoggerConfig](makeConsoleJsonLogger(_)) - def makeSystemOutLogger( logger: ZLogger[String, String] ): ZIO[Any, Nothing, ZLogger[String, Any]] = makePrintStreamLogger(logger, java.lang.System.out) @@ -175,31 +244,73 @@ object LoggerLayers { // ) // } // -// private def fileWriterAsyncLogger( -// destination: Path, -// logger: ZLogger[String, String], -// charset: Charset, -// autoFlushBatchSize: Int, -// bufferedIOSize: Option[Int], -// queue: Queue[UIO[Any]], -// rollingPolicy: Option[FileLoggerConfig.FileRollingPolicy] -// ): ZLogger[String, Any] = { -// val logWriter = -// new zio.logging.internal.FileWriter(destination, charset, autoFlushBatchSize, bufferedIOSize, rollingPolicy) -// -// val stringLogger: ZLogger[String, Any] = logger.map { (line: String) => -// zio.Unsafe.unsafe { implicit u => -// Runtime.default.unsafe.run(queue.offer(ZIO.succeed { -// try logWriter.writeln(line) -// catch { -// case t: VirtualMachineError => throw t -// case _: Throwable => () -// } -// })) -// } -// } -// stringLogger -// } + + def makeFileAsyncJsonLogger(config: FileLoggerConfig): ZIO[Scope, Nothing, FilteredLogger[String, Any]] = + makeFileAsyncLogger( + config.destination, + config.format.toJsonLogger, + config.charset, + config.autoFlushBatchSize, + config.bufferedIOSize, + config.rollingPolicy + ).map(logger => FilteredLogger(logger, config.filter)) + + def makeFileAsyncLogger(config: FileLoggerConfig): ZIO[Scope, Nothing, FilteredLogger[String, Any]] = + makeFileAsyncLogger( + config.destination, + config.format.toLogger, + config.charset, + config.autoFlushBatchSize, + config.bufferedIOSize, + config.rollingPolicy + ).map(logger => FilteredLogger(logger, config.filter)) + + def makeFileAsyncLogger( + destination: Path, + logger: ZLogger[String, String], + charset: Charset, + autoFlushBatchSize: Int, + bufferedIOSize: Option[Int], + rollingPolicy: Option[FileLoggerConfig.FileRollingPolicy] + ): ZIO[Scope, Nothing, ZLogger[String, Any]] = + for { + queue <- Queue.bounded[UIO[Any]](1000) + _ <- queue.take.flatMap(task => task.ignore).forever.forkScoped + } yield fileWriterAsyncLogger( + destination, + logger, + charset, + autoFlushBatchSize, + bufferedIOSize, + queue, + rollingPolicy + ) + + private def fileWriterAsyncLogger( + destination: Path, + logger: ZLogger[String, String], + charset: Charset, + autoFlushBatchSize: Int, + bufferedIOSize: Option[Int], + queue: Queue[UIO[Any]], + rollingPolicy: Option[FileLoggerConfig.FileRollingPolicy] + ): ZLogger[String, Any] = { + val logWriter = + new zio.logging.internal.FileWriter(destination, charset, autoFlushBatchSize, bufferedIOSize, rollingPolicy) + + val stringLogger: ZLogger[String, Any] = logger.map { (line: String) => + zio.Unsafe.unsafe { implicit u => + Runtime.default.unsafe.run(queue.offer(ZIO.succeed { + try logWriter.writeln(line) + catch { + case t: VirtualMachineError => throw t + case _: Throwable => () + } + })) + } + } + stringLogger + } // def makeFileJsonLogger(config: FileLoggerConfig): ULayer[ZLogger[String, Any]] = // makeFileLogger( @@ -253,12 +364,6 @@ object LoggerLayers { config.rollingPolicy ).map(logger => FilteredLogger(logger, config.filter)) - def makeFileJsonLogger: ZIO[FileLoggerConfig, Nothing, ZLogger[String, Any]] = - ZIO.serviceWithZIO[FileLoggerConfig](makeFileJsonLogger(_)) - - def makeFileLogger: ZIO[FileLoggerConfig, Nothing, ZLogger[String, Any]] = - ZIO.serviceWithZIO[FileLoggerConfig](makeFileLogger(_)) - def makeFileLogger(config: FileLoggerConfig): ZIO[Any, Nothing, FilteredLogger[String, Any]] = makeFileLogger( config.destination, @@ -309,22 +414,22 @@ object LoggerLayers { def makeMetricLogger(counter: Metric.Counter[Long], logLevelLabel: String): ZIO[Any, Nothing, MetricLogger] = ZIO.succeed(MetricLogger(counter, logLevelLabel)) - implicit final class ZLoggerLayerLayerOps[-RIn, +E, ROut <: ZLogger[String, Any]: Tag]( - private val self: ZLayer[RIn, E, ROut] - ) { - - def install: ZLayer[RIn, E, Unit] = - self.flatMap { env => - ZLayer.scoped { - ZIO.withLoggerScoped(env.get[ROut]) - } - } - - def installScoped: ZLayer[Scope with RIn, E, Unit] = - self.flatMap { env => - ZLayer.fromZIO(ZIO.withLoggerScoped(env.get[ROut])) - } - } +// implicit final class ZLoggerLayerLayerOps[-RIn, +E, ROut <: ZLogger[String, Any]: Tag]( +// private val self: ZLayer[RIn, E, ROut] +// ) { +// +// def install: ZLayer[RIn, E, Unit] = +// self.flatMap { env => +// ZLayer.scoped { +// ZIO.withLoggerScoped(env.get[ROut]) +// } +// } +// +// def installScoped: ZLayer[Scope with RIn, E, Unit] = +// self.flatMap { env => +// ZLayer.fromZIO(ZIO.withLoggerScoped(env.get[ROut])) +// } +// } implicit final class ZLoggerZIOLayerOps[-RIn, +E, ROut <: ZLogger[String, Any]: Tag]( private val self: ZIO[RIn, E, ROut] diff --git a/core/shared/src/main/scala/zio/logging/package.scala b/core/shared/src/main/scala/zio/logging/package.scala index d27cfd638..5d3d99945 100644 --- a/core/shared/src/main/scala/zio/logging/package.scala +++ b/core/shared/src/main/scala/zio/logging/package.scala @@ -15,13 +15,7 @@ */ package zio -import zio.metrics.{ Metric, MetricLabel } - -import java.io.PrintStream -import java.nio.charset.{ Charset, StandardCharsets } -import java.nio.file.Path - -package object logging { +package object logging extends LoggerLayers { /** * The [[logContext]] fiber reference is used to store typed, structured log @@ -50,10 +44,6 @@ package object logging { */ val loggerNameAnnotationKey = "logger_name" - private[logging] val logLevelMetricLabel = "level" - - private[logging] val loggedTotalMetric = Metric.counter(name = "zio_log_total") - /** * Logger name aspect, by this aspect is possible to set logger name (in general, logger name is extracted from [[Trace]]) * @@ -62,268 +52,6 @@ package object logging { def loggerName(value: String): ZIOAspect[Nothing, Any, Nothing, Any, Nothing, Any] = ZIOAspect.annotated(loggerNameAnnotationKey, value) - def consoleErrLogger(config: ConsoleLoggerConfig): ZLayer[Any, Nothing, Unit] = - Runtime.addLogger(makeConsoleErrLogger(config)) - - def consoleErrJsonLogger(config: ConsoleLoggerConfig): ZLayer[Any, Nothing, Unit] = - Runtime.addLogger(makeConsoleErrJsonLogger(config)) - - def consoleErrJsonLogger(configPath: String = "logger"): ZLayer[Any, Config.Error, Unit] = - ZLayer.scoped { - for { - config <- ConsoleLoggerConfig.load(configPath) - _ <- ZIO.withLoggerScoped(makeConsoleErrJsonLogger(config)) - } yield () - } - - def consoleErrLogger(configPath: String = "logger"): ZLayer[Any, Config.Error, Unit] = - ZLayer.scoped { - for { - config <- ConsoleLoggerConfig.load(configPath) - _ <- ZIO.withLoggerScoped(makeConsoleErrLogger(config)) - } yield () - } - - def consoleJsonLogger(config: ConsoleLoggerConfig): ZLayer[Any, Nothing, Unit] = - Runtime.addLogger(makeConsoleJsonLogger(config)) - - def consoleJsonLogger(configPath: String = "logger"): ZLayer[Any, Config.Error, Unit] = - ZLayer.scoped { - for { - config <- ConsoleLoggerConfig.load(configPath) - _ <- ZIO.withLoggerScoped(makeConsoleJsonLogger(config)) - } yield () - } - - def consoleLogger(config: ConsoleLoggerConfig): ZLayer[Any, Nothing, Unit] = - Runtime.addLogger(makeConsoleLogger(config)) - - def consoleLogger(configPath: String = "logger"): ZLayer[Any, Config.Error, Unit] = - ZLayer.scoped { - for { - config <- ConsoleLoggerConfig.load(configPath) - _ <- ZIO.withLoggerScoped(makeConsoleLogger(config)) - } yield () - } - - def fileAsyncJsonLogger(config: FileLoggerConfig): ZLayer[Any, Nothing, Unit] = - ZLayer.scoped(makeFileAsyncJsonLogger(config)) - - def fileAsyncJsonLogger(configPath: String = "logger"): ZLayer[Any, Config.Error, Unit] = - ZLayer.scoped { - for { - config <- FileLoggerConfig.load(configPath) - _ <- makeFileAsyncJsonLogger(config) - } yield () - } - - def fileAsyncLogger(config: FileLoggerConfig): ZLayer[Any, Nothing, Unit] = - ZLayer.scoped(makeFileAsyncLogger(config)) - - def fileAsyncLogger(configPath: String = "logger"): ZLayer[Any, Config.Error, Unit] = - ZLayer.scoped { - for { - config <- FileLoggerConfig.load(configPath) - _ <- makeFileAsyncLogger(config) - } yield () - } - - def fileJsonLogger(config: FileLoggerConfig): ZLayer[Any, Nothing, Unit] = - Runtime.addLogger(makeFileJsonLogger(config)) - - def fileJsonLogger(configPath: String = "logger"): ZLayer[Any, Config.Error, Unit] = - ZLayer.scoped { - for { - config <- FileLoggerConfig.load(configPath) - _ <- ZIO.withLoggerScoped(makeFileJsonLogger(config)) - } yield () - } - - def fileLogger(config: FileLoggerConfig): ZLayer[Any, Nothing, Unit] = - Runtime.addLogger(makeFileLogger(config)) - - def fileLogger(configPath: String = "logger"): ZLayer[Any, Config.Error, Unit] = - ZLayer.scoped { - for { - config <- FileLoggerConfig.load(configPath) - _ <- ZIO.withLoggerScoped(makeFileLogger(config)) - } yield () - } - - val logMetrics: ZLayer[Any, Nothing, Unit] = - Runtime.addLogger(makeMetricLogger(loggedTotalMetric, logLevelMetricLabel)) - - def logMetricsWith(name: String, logLevelLabel: String): ZLayer[Any, Nothing, Unit] = - Runtime.addLogger(makeMetricLogger(Metric.counter(name), logLevelLabel)) - - private def makeConsoleErrLogger(config: ConsoleLoggerConfig): ZLogger[String, Any] = - makeConsoleLogger(config.format.toLogger, java.lang.System.err, config.filter) - - private def makeConsoleErrJsonLogger(config: ConsoleLoggerConfig): ZLogger[String, Any] = - makeConsoleLogger(config.format.toJsonLogger, java.lang.System.err, config.filter) - - private def makeConsoleLogger(config: ConsoleLoggerConfig): ZLogger[String, Any] = - makeConsoleLogger(config.format.toLogger, java.lang.System.out, config.filter) - - private def makeConsoleJsonLogger(config: ConsoleLoggerConfig): ZLogger[String, Any] = - makeConsoleLogger(config.format.toJsonLogger, java.lang.System.out, config.filter) - - private def makeConsoleLogger( - logger: ZLogger[String, String], - stream: PrintStream, - logFilter: LogFilter[String] - ): ZLogger[String, Any] = { - - val stringLogger = logFilter.filter(logger.map { line => - try stream.println(line) - catch { - case t: VirtualMachineError => throw t - case _: Throwable => () - } - }) - stringLogger - } - - private def makeFileAsyncJsonLogger( - config: FileLoggerConfig - ): ZIO[Scope, Nothing, Unit] = makeFileAsyncLogger( - config.destination, - config.format.toJsonLogger, - config.filter, - config.charset, - config.autoFlushBatchSize, - config.bufferedIOSize, - config.rollingPolicy - ) - - private def makeFileAsyncLogger( - config: FileLoggerConfig - ): ZIO[Scope, Nothing, Unit] = makeFileAsyncLogger( - config.destination, - config.format.toLogger, - config.filter, - config.charset, - config.autoFlushBatchSize, - config.bufferedIOSize, - config.rollingPolicy - ) - - private def makeFileAsyncLogger( - destination: Path, - logger: ZLogger[String, String], - logFilter: LogFilter[String], - charset: Charset, - autoFlushBatchSize: Int, - bufferedIOSize: Option[Int], - rollingPolicy: Option[FileLoggerConfig.FileRollingPolicy] - ): ZIO[Scope, Nothing, Unit] = - for { - queue <- Queue.bounded[UIO[Any]](1000) - stringLogger = - makeFileAsyncLogger( - destination, - logger, - logFilter, - charset, - autoFlushBatchSize, - bufferedIOSize, - queue, - rollingPolicy - ) - _ <- ZIO.withLoggerScoped(stringLogger) - _ <- queue.take.flatMap(task => task.ignore).forever.forkScoped - } yield () - - private def makeFileAsyncLogger( - destination: Path, - logger: ZLogger[String, String], - logFilter: LogFilter[String], - charset: Charset, - autoFlushBatchSize: Int, - bufferedIOSize: Option[Int], - queue: Queue[UIO[Any]], - rollingPolicy: Option[FileLoggerConfig.FileRollingPolicy] - ): ZLogger[String, Any] = { - val logWriter = - new zio.logging.internal.FileWriter(destination, charset, autoFlushBatchSize, bufferedIOSize, rollingPolicy) - - val stringLogger: ZLogger[String, Any] = logFilter.filter(logger.map { (line: String) => - zio.Unsafe.unsafe { implicit u => - Runtime.default.unsafe.run(queue.offer(ZIO.succeed { - try logWriter.writeln(line) - catch { - case t: VirtualMachineError => throw t - case _: Throwable => () - } - })) - } - }) - stringLogger - } - - private def makeFileJsonLogger(config: FileLoggerConfig): ZLogger[String, Any] = - makeFileLogger( - config.destination, - config.format.toJsonLogger, - config.filter, - config.charset, - config.autoFlushBatchSize, - config.bufferedIOSize, - config.rollingPolicy - ) - - private def makeFileLogger(config: FileLoggerConfig): ZLogger[String, Any] = - makeFileLogger( - config.destination, - config.format.toLogger, - config.filter, - config.charset, - config.autoFlushBatchSize, - config.bufferedIOSize, - config.rollingPolicy - ) - - private def makeFileLogger( - destination: Path, - logger: ZLogger[String, String], - logFilter: LogFilter[String], - charset: Charset, - autoFlushBatchSize: Int, - bufferedIOSize: Option[Int], - rollingPolicy: Option[FileLoggerConfig.FileRollingPolicy] - ): ZLogger[String, Any] = { - val logWriter = - new zio.logging.internal.FileWriter(destination, charset, autoFlushBatchSize, bufferedIOSize, rollingPolicy) - - val stringLogger: ZLogger[String, Any] = logFilter.filter(logger.map { (line: String) => - try logWriter.writeln(line) - catch { - case t: VirtualMachineError => throw t - case _: Throwable => () - } - }) - - stringLogger - } - - private def makeMetricLogger(counter: Metric.Counter[Long], logLevelLabel: String): ZLogger[String, Unit] = - 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 tags = context.get(FiberRef.currentTags).getOrElse(Set.empty) - counter.unsafe.update(1, tags + MetricLabel(logLevelLabel, logLevel.label.toLowerCase))(Unsafe.unsafe) - () - } - } - val removeDefaultLoggers: ZLayer[Any, Nothing, Unit] = Runtime.removeDefaultLoggers implicit final class LogAnnotationZIOSyntax[R, E, A](private val self: ZIO[R, E, A]) { diff --git a/examples/core/src/main/scala/zio/logging/example/ConfigurableLoggerApp.scala b/examples/core/src/main/scala/zio/logging/example/ConfigurableLoggerApp.scala index dd84043ea..5ed7b6d69 100644 --- a/examples/core/src/main/scala/zio/logging/example/ConfigurableLoggerApp.scala +++ b/examples/core/src/main/scala/zio/logging/example/ConfigurableLoggerApp.scala @@ -18,16 +18,21 @@ package zio.logging.example import zio.http.HttpAppMiddleware.basicAuth import zio.http._ import zio.logging.api.http.ApiHandlers -import zio.logging.{ ConfigurableLogger, ConsoleLoggerConfig, LogAnnotation, LogFilter, LoggerConfigurer, LoggerLayers } +import zio.logging.{ + ConfigurableLogger, + ConsoleLoggerConfig, + LogAnnotation, + LogFilter, + LoggerConfigurer, + makeSystemOutLogger +} import zio.{ ExitCode, Runtime, Scope, ZIO, ZIOAppDefault, _ } import java.util.UUID object ConfigurableLoggerApp extends ZIOAppDefault { - def configurableLogger(configPath: String = "logger") = { - import LoggerLayers._ - + def configurableLogger(configPath: String = "logger") = ConsoleLoggerConfig .load(configPath) .flatMap { consoleLoggerConfig => @@ -42,7 +47,6 @@ object ConfigurableLoggerApp extends ZIOAppDefault { } } .install - } val logFormat = "%highlight{%timestamp{yyyy-MM-dd'T'HH:mm:ssZ} %fixed{7}{%level} [%fiberId] %name:%line %message %kv{trace_id} %kv{user_id} %cause}" diff --git a/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala b/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala index 81f2c2982..25b921ae4 100644 --- a/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala +++ b/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala @@ -15,7 +15,7 @@ */ package zio.logging.example -import zio.logging.{ ConsoleLoggerConfig, LogAnnotation, LoggerLayers, ReconfigurableLogger } +import zio.logging.{ ConsoleLoggerConfig, LogAnnotation, ReconfigurableLogger, makeSystemOutLogger } import zio.{ Config, ExitCode, Runtime, Scope, ZIO, ZIOAppDefault, _ } import java.util.UUID @@ -27,8 +27,7 @@ object LoggerReconfigureApp extends ZIOAppDefault { def configuredLogger( loadConfig: => ZIO[Any, Config.Error, ConsoleLoggerConfig] - ) = { - import LoggerLayers._ + ) = ReconfigurableLogger .make[Any, Config.Error, String, Any, ConsoleLoggerConfig]( loadConfig, @@ -39,7 +38,6 @@ object LoggerReconfigureApp extends ZIOAppDefault { Schedule.fixed(500.millis) ) .install - } override val bootstrap: ZLayer[ZIOAppArgs, Any, Any] = Runtime.removeDefaultLoggers >>> configuredLogger( From b36de964464497e70b4f1172d4f53ed92ad3f228 Mon Sep 17 00:00:00 2001 From: Peter Kotula Date: Fri, 30 Jun 2023 17:41:03 +0200 Subject: [PATCH 38/56] LoggerLayers --- .../main/scala/zio/logging/LoggerLayers.scala | 203 ++---------------- .../zio/logging/ReconfigurableLogger.scala | 2 +- 2 files changed, 13 insertions(+), 192 deletions(-) diff --git a/core/shared/src/main/scala/zio/logging/LoggerLayers.scala b/core/shared/src/main/scala/zio/logging/LoggerLayers.scala index 38ec3c76b..a21bc6108 100644 --- a/core/shared/src/main/scala/zio/logging/LoggerLayers.scala +++ b/core/shared/src/main/scala/zio/logging/LoggerLayers.scala @@ -59,38 +59,16 @@ private[logging] trait LoggerLayers { ConsoleLoggerConfig.load(configPath).flatMap(makeConsoleLogger).install def fileAsyncJsonLogger(config: FileLoggerConfig): ZLayer[Any, Nothing, Unit] = - ZLayer.scoped { - for { - logger <- makeFileAsyncJsonLogger(config) - _ <- ZIO.withLoggerScoped(logger) - } yield () - } + makeFileAsyncJsonLogger(config).installUnscoped def fileAsyncJsonLogger(configPath: String = "logger"): ZLayer[Any, Config.Error, Unit] = - ZLayer.scoped { - for { - config <- FileLoggerConfig.load(configPath) - logger <- makeFileAsyncJsonLogger(config) - _ <- ZIO.withLoggerScoped(logger) - } yield () - } + FileLoggerConfig.load(configPath).flatMap(makeFileAsyncJsonLogger).installUnscoped def fileAsyncLogger(config: FileLoggerConfig): ZLayer[Any, Nothing, Unit] = - ZLayer.scoped { - for { - logger <- makeFileAsyncLogger(config) - _ <- ZIO.withLoggerScoped(logger) - } yield () - } + makeFileAsyncLogger(config).installUnscoped def fileAsyncLogger(configPath: String = "logger"): ZLayer[Any, Config.Error, Unit] = - ZLayer.scoped { - for { - config <- FileLoggerConfig.load(configPath) - logger <- makeFileAsyncLogger(config) - _ <- ZIO.withLoggerScoped(logger) - } yield () - } + FileLoggerConfig.load(configPath).flatMap(makeFileAsyncLogger).installUnscoped def fileJsonLogger(config: FileLoggerConfig): ZLayer[Any, Nothing, Unit] = makeFileJsonLogger(config).install @@ -104,52 +82,6 @@ private[logging] trait LoggerLayers { def fileLogger(configPath: String = "logger"): ZLayer[Any, Config.Error, Unit] = FileLoggerConfig.load(configPath).flatMap(makeFileLogger).install -// -// def makeConsoleErrLogger(config: ConsoleLoggerConfig): ULayer[ZLogger[String, Any]] = -// makeSystemErrLogger(config.format.toLogger).project(logger => FilteredLogger(logger, config.filter)) -// -// def makeConsoleErrLogger: ZLayer[ConsoleLoggerConfig, Nothing, ZLogger[String, Any]] = -// ZLayer.environment[ConsoleLoggerConfig].flatMap { env => -// makeConsoleErrLogger(env.get[ConsoleLoggerConfig]) -// } -// -// def makeConsoleErrJsonLogger(config: ConsoleLoggerConfig): ULayer[ZLogger[String, Any]] = -// makeSystemErrLogger(config.format.toJsonLogger).project(logger => FilteredLogger(logger, config.filter)) -// -// def makeConsoleErrJsonLogger: ZLayer[ConsoleLoggerConfig, Nothing, ZLogger[String, Any]] = -// ZLayer.environment[ConsoleLoggerConfig].flatMap { env => -// makeConsoleErrJsonLogger(env.get[ConsoleLoggerConfig]) -// } -// -// def makeConsoleLogger(config: ConsoleLoggerConfig): ULayer[ZLogger[String, Any]] = -// makeSystemOutLogger(config.format.toLogger).project(logger => FilteredLogger(logger, config.filter)) -// -// def makeConsoleLogger: ZLayer[ConsoleLoggerConfig, Nothing, ZLogger[String, Any]] = -// ZLayer.environment[ConsoleLoggerConfig].flatMap { env => -// makeConsoleLogger(env.get[ConsoleLoggerConfig]) -// } -// -// def makeConsoleJsonLogger(config: ConsoleLoggerConfig): ULayer[ZLogger[String, Any]] = -// makeSystemOutLogger(config.format.toJsonLogger).project(logger => FilteredLogger(logger, config.filter)) -// -// def makeConsoleJsonLogger: ZLayer[ConsoleLoggerConfig, Nothing, ZLogger[String, Any]] = -// ZLayer.environment[ConsoleLoggerConfig].flatMap { env => -// makeConsoleJsonLogger(env.get[ConsoleLoggerConfig]) -// } -// -// def makeSystemOutLogger( -// logger: ZLogger[String, String] -// ): ULayer[ZLogger[String, Any]] = makePrintStreamLogger(logger, java.lang.System.out) -// -// def makeSystemErrLogger( -// logger: ZLogger[String, String] -// ): ULayer[ZLogger[String, Any]] = makePrintStreamLogger(logger, java.lang.System.err) -// -// def makePrintStreamLogger( -// logger: ZLogger[String, String], -// stream: PrintStream -// ): ULayer[ZLogger[String, Any]] = ZLayer.succeed(printStreamLogger(logger, stream)) - def makeConsoleErrLogger(config: ConsoleLoggerConfig): ZIO[Any, Nothing, ZLogger[String, Any]] = makeSystemErrLogger(config.format.toLogger).map(logger => FilteredLogger(logger, config.filter)) @@ -188,62 +120,6 @@ private[logging] trait LoggerLayers { } stringLogger } -// -// def makeFileAsyncJsonLogger( -// config: FileLoggerConfig -// ): ZLayer[Scope, Nothing, ZLogger[String, Any]] = makeFileAsyncLogger( -// config.destination, -// config.format.toJsonLogger, -// config.charset, -// config.autoFlushBatchSize, -// config.bufferedIOSize, -// config.rollingPolicy -// ).project(logger => FilteredLogger(logger, config.filter)) -// -// def makeFileAsyncJsonLogger: ZLayer[FileLoggerConfig with Scope, Nothing, ZLogger[String, Any]] = -// ZLayer.environment[FileLoggerConfig].flatMap { env => -// makeFileAsyncJsonLogger(env.get[FileLoggerConfig]) -// } -// -// def makeFileAsyncLogger( -// config: FileLoggerConfig -// ): ZLayer[Scope, Nothing, ZLogger[String, Any]] = -// makeFileAsyncLogger( -// config.destination, -// config.format.toLogger, -// config.charset, -// config.autoFlushBatchSize, -// config.bufferedIOSize, -// config.rollingPolicy -// ).project(logger => FilteredLogger(logger, config.filter)) -// -// def makeFileAsyncLogger: ZLayer[FileLoggerConfig with Scope, Nothing, ZLogger[String, Any]] = -// ZLayer.environment[FileLoggerConfig].flatMap { env => -// makeFileAsyncLogger(env.get[FileLoggerConfig]) -// } -// -// def makeFileAsyncLogger( -// destination: Path, -// logger: ZLogger[String, String], -// charset: Charset, -// autoFlushBatchSize: Int, -// bufferedIOSize: Option[Int], -// rollingPolicy: Option[FileLoggerConfig.FileRollingPolicy] -// ): ZLayer[Scope, Nothing, ZLogger[String, Any]] = ZLayer.fromZIO { -// for { -// queue <- Queue.bounded[UIO[Any]](1000) -// _ <- queue.take.flatMap(task => task.ignore).forever.forkScoped -// } yield fileWriterAsyncLogger( -// destination, -// logger, -// charset, -// autoFlushBatchSize, -// bufferedIOSize, -// queue, -// rollingPolicy -// ) -// } -// def makeFileAsyncJsonLogger(config: FileLoggerConfig): ZIO[Scope, Nothing, FilteredLogger[String, Any]] = makeFileAsyncLogger( @@ -312,48 +188,6 @@ private[logging] trait LoggerLayers { stringLogger } -// def makeFileJsonLogger(config: FileLoggerConfig): ULayer[ZLogger[String, Any]] = -// makeFileLogger( -// config.destination, -// config.format.toJsonLogger, -// config.charset, -// config.autoFlushBatchSize, -// config.bufferedIOSize, -// config.rollingPolicy -// ).project(logger => FilteredLogger(logger, config.filter)) -// -// def makeFileJsonLogger: ZLayer[FileLoggerConfig, Nothing, ZLogger[String, Any]] = -// ZLayer.environment[FileLoggerConfig].flatMap { env => -// makeFileJsonLogger(env.get[FileLoggerConfig]) -// } -// -// def makeFileLogger(config: FileLoggerConfig): ULayer[ZLogger[String, Any]] = -// makeFileLogger( -// config.destination, -// config.format.toLogger, -// config.charset, -// config.autoFlushBatchSize, -// config.bufferedIOSize, -// config.rollingPolicy -// ).project(logger => FilteredLogger(logger, config.filter)) -// -// def makeFileLogger: ZLayer[FileLoggerConfig, Nothing, ZLogger[String, Any]] = -// ZLayer.environment[FileLoggerConfig].flatMap { env => -// makeFileLogger(env.get[FileLoggerConfig]) -// } -// -// def makeFileLogger( -// destination: Path, -// logger: ZLogger[String, String], -// charset: Charset, -// autoFlushBatchSize: Int, -// bufferedIOSize: Option[Int], -// rollingPolicy: Option[FileLoggerConfig.FileRollingPolicy] -// ): ULayer[ZLogger[String, Any]] = -// ZLayer.succeed( -// fileWriterLogger(destination, logger, charset, autoFlushBatchSize, bufferedIOSize, rollingPolicy) -// ) - def makeFileJsonLogger(config: FileLoggerConfig): ZIO[Any, Nothing, FilteredLogger[String, Any]] = makeFileLogger( config.destination, @@ -408,30 +242,10 @@ private[logging] trait LoggerLayers { stringLogger } -// def makeMetricLogger(counter: Metric.Counter[Long], logLevelLabel: String): ULayer[MetricLogger] = -// ZLayer.succeed(MetricLogger(counter, logLevelLabel)) - def makeMetricLogger(counter: Metric.Counter[Long], logLevelLabel: String): ZIO[Any, Nothing, MetricLogger] = ZIO.succeed(MetricLogger(counter, logLevelLabel)) -// implicit final class ZLoggerLayerLayerOps[-RIn, +E, ROut <: ZLogger[String, Any]: Tag]( -// private val self: ZLayer[RIn, E, ROut] -// ) { -// -// def install: ZLayer[RIn, E, Unit] = -// self.flatMap { env => -// ZLayer.scoped { -// ZIO.withLoggerScoped(env.get[ROut]) -// } -// } -// -// def installScoped: ZLayer[Scope with RIn, E, Unit] = -// self.flatMap { env => -// ZLayer.fromZIO(ZIO.withLoggerScoped(env.get[ROut])) -// } -// } - - implicit final class ZLoggerZIOLayerOps[-RIn, +E, ROut <: ZLogger[String, Any]: Tag]( + implicit final class ZLoggerZIOLayerOps[RIn, +E, ROut <: ZLogger[String, Any]: Tag]( private val self: ZIO[RIn, E, ROut] ) { @@ -442,6 +256,13 @@ private[logging] trait LoggerLayers { } } + def installUnscoped[RIn2](implicit ev: RIn2 with Scope =:= RIn): ZLayer[RIn2, E, Unit] = + ZLayer.scoped[RIn2] { + self.asInstanceOf[ZIO[RIn2 with Scope, E, ROut]].flatMap { logger => + ZIO.withLoggerScoped(logger) + } + } + def installScoped: ZLayer[Scope with RIn, E, Unit] = ZLayer.fromZIO(self.flatMap { logger => ZIO.withLoggerScoped(logger) diff --git a/core/shared/src/main/scala/zio/logging/ReconfigurableLogger.scala b/core/shared/src/main/scala/zio/logging/ReconfigurableLogger.scala index 50cf96478..501162f6e 100644 --- a/core/shared/src/main/scala/zio/logging/ReconfigurableLogger.scala +++ b/core/shared/src/main/scala/zio/logging/ReconfigurableLogger.scala @@ -27,7 +27,7 @@ sealed trait ReconfigurableLogger[-Message, +Output, Config] extends ZLogger[Mes def set[M <: Message, O >: Output](config: Config, logger: ZLogger[M, O]): Unit } -private[logging] object ReconfigurableLogger { +object ReconfigurableLogger { def apply[Message, Output, Config]( config: Config, From 7d7a13f557dcf7f9d88fbda9078c2b3551beb22c Mon Sep 17 00:00:00 2001 From: Peter Kotula Date: Tue, 4 Jul 2023 15:27:52 +0200 Subject: [PATCH 39/56] ReconfigurableLogger --- .../src/main/scala/zio/logging/ReconfigurableLogger.scala | 4 ++-- core/shared/src/main/scala/zio/logging/package.scala | 5 +++-- .../scala/zio/logging/example/LoggerReconfigureApp.scala | 4 ++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/core/shared/src/main/scala/zio/logging/ReconfigurableLogger.scala b/core/shared/src/main/scala/zio/logging/ReconfigurableLogger.scala index 501162f6e..920ec06ba 100644 --- a/core/shared/src/main/scala/zio/logging/ReconfigurableLogger.scala +++ b/core/shared/src/main/scala/zio/logging/ReconfigurableLogger.scala @@ -61,7 +61,7 @@ object ReconfigurableLogger { loadConfig: => ZIO[R, E, C], makeLogger: (C, Option[ZLogger[M, O]]) => ZIO[R, E, ZLogger[M, O]], updateLogger: Schedule[R, Any, Any] = Schedule.fixed(10.seconds) - ): ZIO[R, E, ReconfigurableLogger[M, O, C]] = + ): ZIO[R with Scope, E, ReconfigurableLogger[M, O, C]] = for { initialConfig <- loadConfig initialLogger <- makeLogger(initialConfig, None) @@ -73,7 +73,7 @@ object ReconfigurableLogger { reconfigurableLogger.set(newConfig, newLogger) }.unit } else ZIO.unit - }.schedule(updateLogger).forkDaemon + }.schedule(updateLogger).forkScoped } yield reconfigurableLogger } diff --git a/core/shared/src/main/scala/zio/logging/package.scala b/core/shared/src/main/scala/zio/logging/package.scala index 5d3d99945..9e25cec1f 100644 --- a/core/shared/src/main/scala/zio/logging/package.scala +++ b/core/shared/src/main/scala/zio/logging/package.scala @@ -59,11 +59,12 @@ package object logging extends LoggerLayers { self @@ key(value) } - implicit final class ZLoggerOps[M, O](private val self: ZLogger[M, O]) { + + implicit final class ZLoggerOps[-Message, +Output](private val self: ZLogger[Message, Output]) { /** * Returns a version of logger that only logs messages when this filter is satisfied */ - def filter(filter: LogFilter[M]): ZLogger[M, Option[O]] = FilteredLogger(self, filter) + def filter[M <: Message](filter: LogFilter[M]): ZLogger[M, Option[Output]] = FilteredLogger(self, filter) } } diff --git a/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala b/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala index 25b921ae4..ba2a64bbb 100644 --- a/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala +++ b/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala @@ -27,7 +27,7 @@ object LoggerReconfigureApp extends ZIOAppDefault { def configuredLogger( loadConfig: => ZIO[Any, Config.Error, ConsoleLoggerConfig] - ) = + ): ZLayer[Any, Config.Error, Unit] = ReconfigurableLogger .make[Any, Config.Error, String, Any, ConsoleLoggerConfig]( loadConfig, @@ -37,7 +37,7 @@ object LoggerReconfigureApp extends ZIOAppDefault { }, Schedule.fixed(500.millis) ) - .install + .installUnscoped override val bootstrap: ZLayer[ZIOAppArgs, Any, Any] = Runtime.removeDefaultLoggers >>> configuredLogger( From 5d8c3c57a64d6f2252dc26826c9f5074e4da9db4 Mon Sep 17 00:00:00 2001 From: Peter Kotula Date: Tue, 11 Jul 2023 15:19:17 +0200 Subject: [PATCH 40/56] fmt fixes --- core/shared/src/main/scala/zio/logging/package.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/core/shared/src/main/scala/zio/logging/package.scala b/core/shared/src/main/scala/zio/logging/package.scala index 9e25cec1f..374631989 100644 --- a/core/shared/src/main/scala/zio/logging/package.scala +++ b/core/shared/src/main/scala/zio/logging/package.scala @@ -59,7 +59,6 @@ package object logging extends LoggerLayers { self @@ key(value) } - implicit final class ZLoggerOps[-Message, +Output](private val self: ZLogger[Message, Output]) { /** From 1a74af4bc12990f86af2653c13a415e4c92422ee Mon Sep 17 00:00:00 2001 From: Peter Kotula Date: Tue, 11 Jul 2023 21:17:55 +0200 Subject: [PATCH 41/56] Configs updates --- .../scala/zio/logging/FilterBenchmarks.scala | 65 ++++++++++--------- .../logging/ReconfigurableLoggerSpec.scala | 2 +- .../zio/logging/ConsoleLoggerConfig.scala | 15 +++-- .../scala/zio/logging/FileLoggerConfig.scala | 10 +-- .../main/scala/zio/logging/LogFilter.scala | 7 ++ .../main/scala/zio/logging/LoggerLayers.scala | 16 ++--- .../example/ConfigurableLoggerApp.scala | 6 +- .../example/LoggerReconfigureApp.scala | 2 +- .../example/Slf4jBridgeExampleApp.scala | 4 +- .../slf4j/bridge/Slf4jBridgeExampleApp.scala | 4 +- .../slf4j/bridge/Slf4jBridgeExampleApp.scala | 4 +- 11 files changed, 75 insertions(+), 60 deletions(-) diff --git a/benchmarks/src/main/scala/zio/logging/FilterBenchmarks.scala b/benchmarks/src/main/scala/zio/logging/FilterBenchmarks.scala index 038530d36..3cd0b3111 100644 --- a/benchmarks/src/main/scala/zio/logging/FilterBenchmarks.scala +++ b/benchmarks/src/main/scala/zio/logging/FilterBenchmarks.scala @@ -14,7 +14,7 @@ class FilterBenchmarks { val runtime = Runtime.default val unfilteredLogging: ZLayer[Any, Nothing, Unit] = - Runtime.removeDefaultLoggers >>> consoleLogger(ConsoleLoggerConfig(LogFormat.default, LogFilter.acceptAll)) + Runtime.removeDefaultLoggers >>> makeSystemOutLogger(LogFormat.default.toLogger).install val handWrittenFilteredLogging: ZLayer[Any, Nothing, Unit] = { val loggerNameGroup: LogGroup[Any, String] = LoggerNameExtractor.loggerNameAnnotationOrTrace.toLogGroup() @@ -37,40 +37,45 @@ class FilterBenchmarks { } } ) - Runtime.removeDefaultLoggers >>> consoleLogger(ConsoleLoggerConfig(LogFormat.default, filter)) + Runtime.removeDefaultLoggers >>> makeSystemOutLogger(LogFormat.default.toLogger) + .map(logger => FilteredLogger(logger, filter)) + .install } - val filterByLogLevelAndNameLogging: ZLayer[Any, Nothing, Unit] = - Runtime.removeDefaultLoggers >>> consoleLogger( - ConsoleLoggerConfig( - LogFormat.default, - LogFilter.logLevelByName( - LogLevel.Debug, - "a.b.c" -> LogLevel.Info, - "a.b.d" -> LogLevel.Warning, - "e" -> LogLevel.Info, - "f.g" -> LogLevel.Error, - "f" -> LogLevel.Info - ) + val filterByLogLevelAndNameLogging: ZLayer[Any, Nothing, Unit] = { + val filter = LogFilter + .LogLevelByNameConfig( + LogLevel.Debug, + "a.b.c" -> LogLevel.Info, + "a.b.d" -> LogLevel.Warning, + "e" -> LogLevel.Info, + "f.g" -> LogLevel.Error, + "f" -> LogLevel.Info ) - ) + .toFilter + + Runtime.removeDefaultLoggers >>> makeSystemOutLogger(LogFormat.default.toLogger) + .map(logger => FilteredLogger(logger, filter)) + .install + } - val cachedFilterByLogLevelAndNameLogging: ZLayer[Any, Nothing, Unit] = - Runtime.removeDefaultLoggers >>> consoleLogger( - ConsoleLoggerConfig( - LogFormat.default, - LogFilter - .logLevelByName( - LogLevel.Debug, - "a.b.c" -> LogLevel.Info, - "a.b.d" -> LogLevel.Warning, - "e" -> LogLevel.Info, - "f.g" -> LogLevel.Error, - "f" -> LogLevel.Info - ) - .cached + val cachedFilterByLogLevelAndNameLogging: ZLayer[Any, Nothing, Unit] = { + val filter = LogFilter + .LogLevelByNameConfig( + LogLevel.Debug, + "a.b.c" -> LogLevel.Info, + "a.b.d" -> LogLevel.Warning, + "e" -> LogLevel.Info, + "f.g" -> LogLevel.Error, + "f" -> LogLevel.Info ) - ) + .toFilter + .cached + + Runtime.removeDefaultLoggers >>> makeSystemOutLogger(LogFormat.default.toLogger) + .map(logger => FilteredLogger(logger, filter)) + .install + } val names: List[String] = List( "a", diff --git a/core/jvm/src/test/scala/zio/logging/ReconfigurableLoggerSpec.scala b/core/jvm/src/test/scala/zio/logging/ReconfigurableLoggerSpec.scala index d2999cbb0..2a7712a23 100644 --- a/core/jvm/src/test/scala/zio/logging/ReconfigurableLoggerSpec.scala +++ b/core/jvm/src/test/scala/zio/logging/ReconfigurableLoggerSpec.scala @@ -20,7 +20,7 @@ object ReconfigurableLoggerSpec extends ZIOSpecDefault { zio.Unsafe.unsafe { implicit u => Runtime.default.unsafe.run(queue.offer(line)) } - }.filter(config.filter) + }.filter(config.toFilter) }, Schedule.fixed(200.millis) ) diff --git a/core/shared/src/main/scala/zio/logging/ConsoleLoggerConfig.scala b/core/shared/src/main/scala/zio/logging/ConsoleLoggerConfig.scala index 756268ed1..f4c0ed824 100644 --- a/core/shared/src/main/scala/zio/logging/ConsoleLoggerConfig.scala +++ b/core/shared/src/main/scala/zio/logging/ConsoleLoggerConfig.scala @@ -16,21 +16,26 @@ package zio.logging import zio.prelude._ -import zio.{ Config, LogLevel, ZIO, ZLayer } +import zio.{ Config, ZIO, ZLayer } -final case class ConsoleLoggerConfig(format: LogFormat, filter: LogFilter[String]) +final case class ConsoleLoggerConfig( + format: LogFormat, + filter: LogFilter.LogLevelByNameConfig +) { + def toFilter[M]: LogFilter[M] = filter.toFilter +} object ConsoleLoggerConfig { - val default: ConsoleLoggerConfig = ConsoleLoggerConfig(LogFormat.default, LogFilter.logLevel(LogLevel.Info)) + val default: ConsoleLoggerConfig = ConsoleLoggerConfig(LogFormat.default, LogFilter.LogLevelByNameConfig.default) val config: Config[ConsoleLoggerConfig] = { val formatConfig = LogFormat.config.nested("format").withDefault(LogFormat.default) val filterConfig = LogFilter.LogLevelByNameConfig.config.nested("filter") - (formatConfig ++ filterConfig).map { case (format, filterConfig) => + (formatConfig ++ filterConfig).map { case (format, filter) => ConsoleLoggerConfig( format, - LogFilter.logLevelByName(filterConfig) + filter ) } } diff --git a/core/shared/src/main/scala/zio/logging/FileLoggerConfig.scala b/core/shared/src/main/scala/zio/logging/FileLoggerConfig.scala index ea4cf3482..ba612dadb 100644 --- a/core/shared/src/main/scala/zio/logging/FileLoggerConfig.scala +++ b/core/shared/src/main/scala/zio/logging/FileLoggerConfig.scala @@ -26,12 +26,14 @@ import scala.util.{ Failure, Success, Try } final case class FileLoggerConfig( destination: Path, format: LogFormat, - filter: LogFilter[String], + filter: LogFilter.LogLevelByNameConfig, charset: Charset = StandardCharsets.UTF_8, autoFlushBatchSize: Int = 1, bufferedIOSize: Option[Int] = None, rollingPolicy: Option[FileLoggerConfig.FileRollingPolicy] = None -) +) { + def toFilter[M]: LogFilter[M] = filter.toFilter +} object FileLoggerConfig { @@ -74,11 +76,11 @@ object FileLoggerConfig { val rollingPolicyConfig = FileRollingPolicy.config.nested("rollingPolicy").optional (pathConfig ++ formatConfig ++ filterConfig ++ charsetConfig ++ autoFlushBatchSizeConfig ++ bufferedIOSizeConfig ++ rollingPolicyConfig).map { - case (path, format, filterConfig, charset, autoFlushBatchSize, bufferedIOSize, rollingPolicy) => + case (path, format, filter, charset, autoFlushBatchSize, bufferedIOSize, rollingPolicy) => FileLoggerConfig( path, format, - LogFilter.logLevelByName(filterConfig), + filter, charset, autoFlushBatchSize, bufferedIOSize, diff --git a/core/shared/src/main/scala/zio/logging/LogFilter.scala b/core/shared/src/main/scala/zio/logging/LogFilter.scala index d79f5030f..607d796b4 100644 --- a/core/shared/src/main/scala/zio/logging/LogFilter.scala +++ b/core/shared/src/main/scala/zio/logging/LogFilter.scala @@ -257,10 +257,17 @@ object LogFilter { def withMappings(mappings: Map[String, LogLevel]): LogLevelByNameConfig = LogLevelByNameConfig(rootLevel, mappings) + + def toFilter[M]: LogFilter[M] = LogFilter.logLevelByName(this) } object LogLevelByNameConfig { + def apply(rootLevel: LogLevel, mappings: (String, LogLevel)*): LogLevelByNameConfig = + LogLevelByNameConfig(rootLevel, mappings.toMap) + + val default: LogLevelByNameConfig = LogLevelByNameConfig(LogLevel.Info, Map.empty[String, LogLevel]) + val config: Config[LogLevelByNameConfig] = { val rootLevelConfig = Config.logLevel.nested("rootLevel").withDefault(LogLevel.Info) val mappingsConfig = Config.table("mappings", Config.logLevel).withDefault(Map.empty) diff --git a/core/shared/src/main/scala/zio/logging/LoggerLayers.scala b/core/shared/src/main/scala/zio/logging/LoggerLayers.scala index a21bc6108..bfa614207 100644 --- a/core/shared/src/main/scala/zio/logging/LoggerLayers.scala +++ b/core/shared/src/main/scala/zio/logging/LoggerLayers.scala @@ -83,16 +83,16 @@ private[logging] trait LoggerLayers { FileLoggerConfig.load(configPath).flatMap(makeFileLogger).install def makeConsoleErrLogger(config: ConsoleLoggerConfig): ZIO[Any, Nothing, ZLogger[String, Any]] = - makeSystemErrLogger(config.format.toLogger).map(logger => FilteredLogger(logger, config.filter)) + makeSystemErrLogger(config.format.toLogger).map(logger => FilteredLogger(logger, config.toFilter)) def makeConsoleErrJsonLogger(config: ConsoleLoggerConfig): ZIO[Any, Nothing, ZLogger[String, Any]] = - makeSystemErrLogger(config.format.toJsonLogger).map(logger => FilteredLogger(logger, config.filter)) + makeSystemErrLogger(config.format.toJsonLogger).map(logger => FilteredLogger(logger, config.toFilter)) def makeConsoleLogger(config: ConsoleLoggerConfig): ZIO[Any, Nothing, ZLogger[String, Any]] = - makeSystemOutLogger(config.format.toLogger).map(logger => FilteredLogger(logger, config.filter)) + makeSystemOutLogger(config.format.toLogger).map(logger => FilteredLogger(logger, config.toFilter)) def makeConsoleJsonLogger(config: ConsoleLoggerConfig): ZIO[Any, Nothing, ZLogger[String, Any]] = - makeSystemOutLogger(config.format.toJsonLogger).map(logger => FilteredLogger(logger, config.filter)) + makeSystemOutLogger(config.format.toJsonLogger).map(logger => FilteredLogger(logger, config.toFilter)) def makeSystemOutLogger( logger: ZLogger[String, String] @@ -129,7 +129,7 @@ private[logging] trait LoggerLayers { config.autoFlushBatchSize, config.bufferedIOSize, config.rollingPolicy - ).map(logger => FilteredLogger(logger, config.filter)) + ).map(logger => FilteredLogger(logger, config.toFilter)) def makeFileAsyncLogger(config: FileLoggerConfig): ZIO[Scope, Nothing, FilteredLogger[String, Any]] = makeFileAsyncLogger( @@ -139,7 +139,7 @@ private[logging] trait LoggerLayers { config.autoFlushBatchSize, config.bufferedIOSize, config.rollingPolicy - ).map(logger => FilteredLogger(logger, config.filter)) + ).map(logger => FilteredLogger(logger, config.toFilter)) def makeFileAsyncLogger( destination: Path, @@ -196,7 +196,7 @@ private[logging] trait LoggerLayers { config.autoFlushBatchSize, config.bufferedIOSize, config.rollingPolicy - ).map(logger => FilteredLogger(logger, config.filter)) + ).map(logger => FilteredLogger(logger, config.toFilter)) def makeFileLogger(config: FileLoggerConfig): ZIO[Any, Nothing, FilteredLogger[String, Any]] = makeFileLogger( @@ -206,7 +206,7 @@ private[logging] trait LoggerLayers { config.autoFlushBatchSize, config.bufferedIOSize, config.rollingPolicy - ).map(logger => FilteredLogger(logger, config.filter)) + ).map(logger => FilteredLogger(logger, config.toFilter)) def makeFileLogger( destination: Path, diff --git a/examples/core/src/main/scala/zio/logging/example/ConfigurableLoggerApp.scala b/examples/core/src/main/scala/zio/logging/example/ConfigurableLoggerApp.scala index 5ed7b6d69..45e0b40d0 100644 --- a/examples/core/src/main/scala/zio/logging/example/ConfigurableLoggerApp.scala +++ b/examples/core/src/main/scala/zio/logging/example/ConfigurableLoggerApp.scala @@ -39,11 +39,7 @@ object ConfigurableLoggerApp extends ZIOAppDefault { makeSystemOutLogger( consoleLoggerConfig.format.toLogger ).map { logger => - val filterConfig = consoleLoggerConfig.filter - .asInstanceOf[LogFilter.ConfiguredFilter[String, LogFilter.LogLevelByNameConfig]] - .config - - ConfigurableLogger.make(logger, filterConfig) + ConfigurableLogger.make(logger, consoleLoggerConfig.filter) } } .install diff --git a/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala b/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala index ba2a64bbb..abfd67912 100644 --- a/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala +++ b/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala @@ -33,7 +33,7 @@ object LoggerReconfigureApp extends ZIOAppDefault { loadConfig, (config, _) => makeSystemOutLogger(config.format.toLogger).map { logger => - config.filter.filter(logger) + config.toFilter.filter(logger) }, Schedule.fixed(500.millis) ) diff --git a/examples/slf4j2-bridge/src/main/scala/zio/logging/example/Slf4jBridgeExampleApp.scala b/examples/slf4j2-bridge/src/main/scala/zio/logging/example/Slf4jBridgeExampleApp.scala index d5bebd11b..1c03d6bb3 100644 --- a/examples/slf4j2-bridge/src/main/scala/zio/logging/example/Slf4jBridgeExampleApp.scala +++ b/examples/slf4j2-bridge/src/main/scala/zio/logging/example/Slf4jBridgeExampleApp.scala @@ -25,7 +25,7 @@ object Slf4jBridgeExampleApp extends ZIOAppDefault { private val slf4jLogger = org.slf4j.LoggerFactory.getLogger("SLF4J-LOGGER") - private val logFilter: LogFilter[String] = LogFilter.logLevelByName( + private val logFilterConfig = LogFilter.LogLevelByNameConfig( LogLevel.Info, "zio.logging.slf4j" -> LogLevel.Debug, "SLF4J-LOGGER" -> LogLevel.Warning @@ -40,7 +40,7 @@ object Slf4jBridgeExampleApp extends ZIOAppDefault { ) + LogFormat.logAnnotation(LogAnnotation.UserId) + LogFormat.logAnnotation( LogAnnotation.TraceId ) + LogFormat.default, - logFilter + logFilterConfig ) ) >+> Slf4jBridge.initialize diff --git a/slf4j-bridge/src/test/scala/zio/logging/slf4j/bridge/Slf4jBridgeExampleApp.scala b/slf4j-bridge/src/test/scala/zio/logging/slf4j/bridge/Slf4jBridgeExampleApp.scala index dd503c7bf..2f5b11986 100644 --- a/slf4j-bridge/src/test/scala/zio/logging/slf4j/bridge/Slf4jBridgeExampleApp.scala +++ b/slf4j-bridge/src/test/scala/zio/logging/slf4j/bridge/Slf4jBridgeExampleApp.scala @@ -9,7 +9,7 @@ object Slf4jBridgeExampleApp extends ZIOAppDefault { private val slf4jLogger = org.slf4j.LoggerFactory.getLogger("SLF4J-LOGGER") - private val logFilter: LogFilter[String] = LogFilter.logLevelByName( + private val logFilterConfig = LogFilter.LogLevelByNameConfig( LogLevel.Info, "zio.logging.slf4j" -> LogLevel.Debug, "SLF4J-LOGGER" -> LogLevel.Warning @@ -24,7 +24,7 @@ object Slf4jBridgeExampleApp extends ZIOAppDefault { ) + LogFormat.logAnnotation(LogAnnotation.UserId) + LogFormat.logAnnotation( LogAnnotation.TraceId ) + LogFormat.default, - logFilter + logFilterConfig ) ) >+> Slf4jBridge.initialize diff --git a/slf4j2-bridge/src/test/scala/zio/logging/slf4j/bridge/Slf4jBridgeExampleApp.scala b/slf4j2-bridge/src/test/scala/zio/logging/slf4j/bridge/Slf4jBridgeExampleApp.scala index 3b46b7ffc..928cd06bf 100644 --- a/slf4j2-bridge/src/test/scala/zio/logging/slf4j/bridge/Slf4jBridgeExampleApp.scala +++ b/slf4j2-bridge/src/test/scala/zio/logging/slf4j/bridge/Slf4jBridgeExampleApp.scala @@ -9,7 +9,7 @@ object Slf4jBridgeExampleApp extends ZIOAppDefault { private val slf4jLogger = org.slf4j.LoggerFactory.getLogger("SLF4J-LOGGER") - private val logFilter: LogFilter[String] = LogFilter.logLevelByName( + private val logFilterConfig = LogFilter.LogLevelByNameConfig( LogLevel.Info, "zio.logging.slf4j" -> LogLevel.Debug, "SLF4J-LOGGER" -> LogLevel.Warning @@ -24,7 +24,7 @@ object Slf4jBridgeExampleApp extends ZIOAppDefault { ) + LogFormat.logAnnotation(LogAnnotation.UserId) + LogFormat.logAnnotation( LogAnnotation.TraceId ) + LogFormat.default, - logFilter + logFilterConfig ) ) >+> Slf4jBridge.initialize From 15a7015e87951022c1635992ca152682b8629d1b Mon Sep 17 00:00:00 2001 From: Peter Kotula Date: Fri, 14 Jul 2023 11:44:30 +0200 Subject: [PATCH 42/56] Configs updates --- .../logging/ReconfigurableLoggerSpec.scala | 5 +-- .../zio/logging/ConsoleLoggerConfig.scala | 10 +++-- .../scala/zio/logging/FileLoggerConfig.scala | 8 ++-- .../main/scala/zio/logging/LoggerLayers.scala | 37 ++++++++++--------- .../src/main/scala/zio/logging/package.scala | 2 + .../example/ConfigurableLoggerApp.scala | 4 +- .../example/LoggerReconfigureApp.scala | 7 +--- 7 files changed, 39 insertions(+), 34 deletions(-) diff --git a/core/jvm/src/test/scala/zio/logging/ReconfigurableLoggerSpec.scala b/core/jvm/src/test/scala/zio/logging/ReconfigurableLoggerSpec.scala index 2a7712a23..5ca3eee8e 100644 --- a/core/jvm/src/test/scala/zio/logging/ReconfigurableLoggerSpec.scala +++ b/core/jvm/src/test/scala/zio/logging/ReconfigurableLoggerSpec.scala @@ -6,14 +6,13 @@ import zio.{ Chunk, Config, ConfigProvider, LogLevel, Queue, Runtime, Schedule, object ReconfigurableLoggerSpec extends ZIOSpecDefault { def configuredLogger( - queue: zio.Queue[String], - configPath: String = "logger" + queue: zio.Queue[String] ): ZLayer[Any, Config.Error, Unit] = ZLayer.scoped { for { logger <- ReconfigurableLogger .make[Any, Config.Error, String, Any, ConsoleLoggerConfig]( - ConsoleLoggerConfig.load(configPath), + ConsoleLoggerConfig.load(), (config, _) => ZIO.succeed { config.format.toLogger.map { line => diff --git a/core/shared/src/main/scala/zio/logging/ConsoleLoggerConfig.scala b/core/shared/src/main/scala/zio/logging/ConsoleLoggerConfig.scala index f4c0ed824..50489a5e4 100644 --- a/core/shared/src/main/scala/zio/logging/ConsoleLoggerConfig.scala +++ b/core/shared/src/main/scala/zio/logging/ConsoleLoggerConfig.scala @@ -16,7 +16,7 @@ package zio.logging import zio.prelude._ -import zio.{ Config, ZIO, ZLayer } +import zio.{ Config, NonEmptyChunk, ZIO, ZLayer } final case class ConsoleLoggerConfig( format: LogFormat, @@ -44,10 +44,12 @@ object ConsoleLoggerConfig { l.format == r.format && l.filter === r.filter } - def load(configPath: String = "logger"): ZIO[Any, Config.Error, ConsoleLoggerConfig] = - ZIO.config(ConsoleLoggerConfig.config.nested(configPath)) + def load(configPath: NonEmptyChunk[String] = loggerConfigPath): ZIO[Any, Config.Error, ConsoleLoggerConfig] = + ZIO.config(ConsoleLoggerConfig.config.nested(configPath.head, configPath.tail: _*)) - def make(configPath: String = "logger"): ZLayer[Any, Config.Error, ConsoleLoggerConfig] = + def make( + configPath: NonEmptyChunk[String] = loggerConfigPath + ): ZLayer[Any, Config.Error, ConsoleLoggerConfig] = ZLayer.fromZIO(load(configPath)) } diff --git a/core/shared/src/main/scala/zio/logging/FileLoggerConfig.scala b/core/shared/src/main/scala/zio/logging/FileLoggerConfig.scala index ba612dadb..ab2c91d02 100644 --- a/core/shared/src/main/scala/zio/logging/FileLoggerConfig.scala +++ b/core/shared/src/main/scala/zio/logging/FileLoggerConfig.scala @@ -99,10 +99,12 @@ object FileLoggerConfig { l.filter === r.filter } - def load(configPath: String = "logger"): ZIO[Any, Config.Error, FileLoggerConfig] = - ZIO.config(FileLoggerConfig.config.nested(configPath)) + def load(configPath: NonEmptyChunk[String] = loggerConfigPath): ZIO[Any, Config.Error, FileLoggerConfig] = + ZIO.config(FileLoggerConfig.config.nested(configPath.head, configPath.tail: _*)) - def make(configPath: String = "logger"): ZLayer[Any, Config.Error, FileLoggerConfig] = + def make( + configPath: NonEmptyChunk[String] = loggerConfigPath + ): ZLayer[Any, Config.Error, FileLoggerConfig] = ZLayer.fromZIO(load(configPath)) } diff --git a/core/shared/src/main/scala/zio/logging/LoggerLayers.scala b/core/shared/src/main/scala/zio/logging/LoggerLayers.scala index bfa614207..f00ad2ab8 100644 --- a/core/shared/src/main/scala/zio/logging/LoggerLayers.scala +++ b/core/shared/src/main/scala/zio/logging/LoggerLayers.scala @@ -16,7 +16,7 @@ package zio.logging import zio.metrics.Metric -import zio.{ Config, Queue, Runtime, Scope, Tag, UIO, ZIO, ZLayer, ZLogger } +import zio.{ Config, NonEmptyChunk, Queue, Runtime, Scope, Tag, UIO, ZIO, ZLayer, ZLogger } import java.io.PrintStream import java.nio.charset.Charset @@ -40,59 +40,59 @@ private[logging] trait LoggerLayers { def consoleErrJsonLogger(config: ConsoleLoggerConfig): ZLayer[Any, Nothing, Unit] = makeConsoleErrJsonLogger(config).install - def consoleErrJsonLogger(configPath: String = "logger"): ZLayer[Any, Config.Error, Unit] = + def consoleErrJsonLogger(configPath: NonEmptyChunk[String] = loggerConfigPath): ZLayer[Any, Config.Error, Unit] = ConsoleLoggerConfig.load(configPath).flatMap(makeConsoleErrJsonLogger).install - def consoleErrLogger(configPath: String = "logger"): ZLayer[Any, Config.Error, Unit] = + def consoleErrLogger(configPath: NonEmptyChunk[String] = loggerConfigPath): ZLayer[Any, Config.Error, Unit] = ConsoleLoggerConfig.load(configPath).flatMap(makeConsoleErrLogger).install def consoleJsonLogger(config: ConsoleLoggerConfig): ZLayer[Any, Nothing, Unit] = makeConsoleJsonLogger(config).install - def consoleJsonLogger(configPath: String = "logger"): ZLayer[Any, Config.Error, Unit] = + def consoleJsonLogger(configPath: NonEmptyChunk[String] = loggerConfigPath): ZLayer[Any, Config.Error, Unit] = ConsoleLoggerConfig.load(configPath).flatMap(makeConsoleJsonLogger).install def consoleLogger(config: ConsoleLoggerConfig): ZLayer[Any, Nothing, Unit] = makeConsoleLogger(config).install - def consoleLogger(configPath: String = "logger"): ZLayer[Any, Config.Error, Unit] = + def consoleLogger(configPath: NonEmptyChunk[String] = loggerConfigPath): ZLayer[Any, Config.Error, Unit] = ConsoleLoggerConfig.load(configPath).flatMap(makeConsoleLogger).install def fileAsyncJsonLogger(config: FileLoggerConfig): ZLayer[Any, Nothing, Unit] = makeFileAsyncJsonLogger(config).installUnscoped - def fileAsyncJsonLogger(configPath: String = "logger"): ZLayer[Any, Config.Error, Unit] = + def fileAsyncJsonLogger(configPath: NonEmptyChunk[String] = loggerConfigPath): ZLayer[Any, Config.Error, Unit] = FileLoggerConfig.load(configPath).flatMap(makeFileAsyncJsonLogger).installUnscoped def fileAsyncLogger(config: FileLoggerConfig): ZLayer[Any, Nothing, Unit] = makeFileAsyncLogger(config).installUnscoped - def fileAsyncLogger(configPath: String = "logger"): ZLayer[Any, Config.Error, Unit] = + def fileAsyncLogger(configPath: NonEmptyChunk[String] = loggerConfigPath): ZLayer[Any, Config.Error, Unit] = FileLoggerConfig.load(configPath).flatMap(makeFileAsyncLogger).installUnscoped def fileJsonLogger(config: FileLoggerConfig): ZLayer[Any, Nothing, Unit] = makeFileJsonLogger(config).install - def fileJsonLogger(configPath: String = "logger"): ZLayer[Any, Config.Error, Unit] = + def fileJsonLogger(configPath: NonEmptyChunk[String] = loggerConfigPath): ZLayer[Any, Config.Error, Unit] = FileLoggerConfig.load(configPath).flatMap(makeFileJsonLogger).install def fileLogger(config: FileLoggerConfig): ZLayer[Any, Nothing, Unit] = makeFileLogger(config).install - def fileLogger(configPath: String = "logger"): ZLayer[Any, Config.Error, Unit] = + def fileLogger(configPath: NonEmptyChunk[String] = loggerConfigPath): ZLayer[Any, Config.Error, Unit] = FileLoggerConfig.load(configPath).flatMap(makeFileLogger).install def makeConsoleErrLogger(config: ConsoleLoggerConfig): ZIO[Any, Nothing, ZLogger[String, Any]] = - makeSystemErrLogger(config.format.toLogger).map(logger => FilteredLogger(logger, config.toFilter)) + makeSystemErrLogger(config.format.toLogger).filter(config.toFilter) def makeConsoleErrJsonLogger(config: ConsoleLoggerConfig): ZIO[Any, Nothing, ZLogger[String, Any]] = - makeSystemErrLogger(config.format.toJsonLogger).map(logger => FilteredLogger(logger, config.toFilter)) + makeSystemErrLogger(config.format.toJsonLogger).filter(config.toFilter) def makeConsoleLogger(config: ConsoleLoggerConfig): ZIO[Any, Nothing, ZLogger[String, Any]] = - makeSystemOutLogger(config.format.toLogger).map(logger => FilteredLogger(logger, config.toFilter)) + makeSystemOutLogger(config.format.toLogger).filter(config.toFilter) def makeConsoleJsonLogger(config: ConsoleLoggerConfig): ZIO[Any, Nothing, ZLogger[String, Any]] = - makeSystemOutLogger(config.format.toJsonLogger).map(logger => FilteredLogger(logger, config.toFilter)) + makeSystemOutLogger(config.format.toJsonLogger).filter(config.toFilter) def makeSystemOutLogger( logger: ZLogger[String, String] @@ -129,7 +129,7 @@ private[logging] trait LoggerLayers { config.autoFlushBatchSize, config.bufferedIOSize, config.rollingPolicy - ).map(logger => FilteredLogger(logger, config.toFilter)) + ).filter(config.toFilter) def makeFileAsyncLogger(config: FileLoggerConfig): ZIO[Scope, Nothing, FilteredLogger[String, Any]] = makeFileAsyncLogger( @@ -139,7 +139,7 @@ private[logging] trait LoggerLayers { config.autoFlushBatchSize, config.bufferedIOSize, config.rollingPolicy - ).map(logger => FilteredLogger(logger, config.toFilter)) + ).filter(config.toFilter) def makeFileAsyncLogger( destination: Path, @@ -196,7 +196,7 @@ private[logging] trait LoggerLayers { config.autoFlushBatchSize, config.bufferedIOSize, config.rollingPolicy - ).map(logger => FilteredLogger(logger, config.toFilter)) + ).filter(config.toFilter) def makeFileLogger(config: FileLoggerConfig): ZIO[Any, Nothing, FilteredLogger[String, Any]] = makeFileLogger( @@ -206,7 +206,7 @@ private[logging] trait LoggerLayers { config.autoFlushBatchSize, config.bufferedIOSize, config.rollingPolicy - ).map(logger => FilteredLogger(logger, config.toFilter)) + ).filter(config.toFilter) def makeFileLogger( destination: Path, @@ -249,6 +249,9 @@ private[logging] trait LoggerLayers { private val self: ZIO[RIn, E, ROut] ) { + def filter(filter: LogFilter[String]): ZIO[RIn, E, FilteredLogger[String, Any]] = + self.map(logger => FilteredLogger(logger, filter)) + def install: ZLayer[RIn, E, Unit] = ZLayer.scoped[RIn] { self.flatMap { logger => diff --git a/core/shared/src/main/scala/zio/logging/package.scala b/core/shared/src/main/scala/zio/logging/package.scala index 374631989..4b829b334 100644 --- a/core/shared/src/main/scala/zio/logging/package.scala +++ b/core/shared/src/main/scala/zio/logging/package.scala @@ -44,6 +44,8 @@ package object logging extends LoggerLayers { */ val loggerNameAnnotationKey = "logger_name" + val loggerConfigPath: NonEmptyChunk[String] = NonEmptyChunk("logger") + /** * Logger name aspect, by this aspect is possible to set logger name (in general, logger name is extracted from [[Trace]]) * diff --git a/examples/core/src/main/scala/zio/logging/example/ConfigurableLoggerApp.scala b/examples/core/src/main/scala/zio/logging/example/ConfigurableLoggerApp.scala index 45e0b40d0..530860de0 100644 --- a/examples/core/src/main/scala/zio/logging/example/ConfigurableLoggerApp.scala +++ b/examples/core/src/main/scala/zio/logging/example/ConfigurableLoggerApp.scala @@ -22,8 +22,8 @@ import zio.logging.{ ConfigurableLogger, ConsoleLoggerConfig, LogAnnotation, - LogFilter, LoggerConfigurer, + loggerConfigPath, makeSystemOutLogger } import zio.{ ExitCode, Runtime, Scope, ZIO, ZIOAppDefault, _ } @@ -32,7 +32,7 @@ import java.util.UUID object ConfigurableLoggerApp extends ZIOAppDefault { - def configurableLogger(configPath: String = "logger") = + def configurableLogger(configPath: NonEmptyChunk[String] = loggerConfigPath) = ConsoleLoggerConfig .load(configPath) .flatMap { consoleLoggerConfig => diff --git a/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala b/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala index abfd67912..847fa5bf8 100644 --- a/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala +++ b/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala @@ -15,7 +15,7 @@ */ package zio.logging.example -import zio.logging.{ ConsoleLoggerConfig, LogAnnotation, ReconfigurableLogger, makeSystemOutLogger } +import zio.logging.{ ConsoleLoggerConfig, LogAnnotation, ReconfigurableLogger, _ } import zio.{ Config, ExitCode, Runtime, Scope, ZIO, ZIOAppDefault, _ } import java.util.UUID @@ -31,10 +31,7 @@ object LoggerReconfigureApp extends ZIOAppDefault { ReconfigurableLogger .make[Any, Config.Error, String, Any, ConsoleLoggerConfig]( loadConfig, - (config, _) => - makeSystemOutLogger(config.format.toLogger).map { logger => - config.toFilter.filter(logger) - }, + (config, _) => makeSystemOutLogger(config.format.toLogger).filter(config.filter.toFilter), Schedule.fixed(500.millis) ) .installUnscoped From dc87ea408f69f157168cde6c327b2fafa8eaba68 Mon Sep 17 00:00:00 2001 From: Peter Kotula Date: Fri, 14 Jul 2023 14:49:19 +0200 Subject: [PATCH 43/56] filter --- .../src/main/scala/zio/logging/FilterBenchmarks.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/benchmarks/src/main/scala/zio/logging/FilterBenchmarks.scala b/benchmarks/src/main/scala/zio/logging/FilterBenchmarks.scala index 3cd0b3111..e1106ae5c 100644 --- a/benchmarks/src/main/scala/zio/logging/FilterBenchmarks.scala +++ b/benchmarks/src/main/scala/zio/logging/FilterBenchmarks.scala @@ -38,7 +38,7 @@ class FilterBenchmarks { } ) Runtime.removeDefaultLoggers >>> makeSystemOutLogger(LogFormat.default.toLogger) - .map(logger => FilteredLogger(logger, filter)) + .filter(filter) .install } @@ -55,7 +55,7 @@ class FilterBenchmarks { .toFilter Runtime.removeDefaultLoggers >>> makeSystemOutLogger(LogFormat.default.toLogger) - .map(logger => FilteredLogger(logger, filter)) + .filter(filter) .install } @@ -73,7 +73,7 @@ class FilterBenchmarks { .cached Runtime.removeDefaultLoggers >>> makeSystemOutLogger(LogFormat.default.toLogger) - .map(logger => FilteredLogger(logger, filter)) + .filter(filter) .install } From 67063b842fe430dfc32c1d74e9ca778d799b8d52 Mon Sep 17 00:00:00 2001 From: Peter Kotula Date: Sun, 13 Aug 2023 20:21:33 +0200 Subject: [PATCH 44/56] moved http api to examples --- build.sbt | 16 ++-------------- .../scala/zio/logging/api/http/ApiDomain.scala | 0 .../zio/logging/api/http/ApiEndpoints.scala | 0 .../scala/zio/logging/api/http/ApiHandlers.scala | 0 .../zio/logging/api/http/ApiEndpointsSpec.scala | 0 .../zio/logging/api/http/ApiHandlersSpec.scala | 0 6 files changed, 2 insertions(+), 14 deletions(-) rename {api-http => examples/core}/src/main/scala/zio/logging/api/http/ApiDomain.scala (100%) rename {api-http => examples/core}/src/main/scala/zio/logging/api/http/ApiEndpoints.scala (100%) rename {api-http => examples/core}/src/main/scala/zio/logging/api/http/ApiHandlers.scala (100%) rename {api-http => examples/core}/src/test/scala/zio/logging/api/http/ApiEndpointsSpec.scala (100%) rename {api-http => examples/core}/src/test/scala/zio/logging/api/http/ApiHandlersSpec.scala (100%) diff --git a/build.sbt b/build.sbt index d16d06920..291c1b01c 100644 --- a/build.sbt +++ b/build.sbt @@ -77,7 +77,6 @@ lazy val root = project slf4jBridge, slf4j2Bridge, jpl, - apiHttp, benchmarks, examplesCore, examplesJpl, @@ -111,18 +110,6 @@ lazy val coreJS = core.js.settings( libraryDependencies += "io.github.cquiroz" %%% "scala-java-time" % scalaJavaTimeVersion % Test ) -lazy val apiHttp = project - .in(file("api-http")) - .dependsOn(coreJVM) - .settings(stdSettings("zio-logging-api-http", turnCompilerWarningIntoErrors = false)) - .settings(enableZIO()) - .settings(mimaSettings(failOnProblem = true)) - .settings( - libraryDependencies ++= Seq( - "dev.zio" %% "zio-http" % zioHttp - ) - ) - lazy val slf4j = project .in(file("slf4j")) .dependsOn(coreJVM) @@ -206,13 +193,14 @@ lazy val benchmarks = project lazy val examplesCore = project .in(file("examples/core")) - .dependsOn(coreJVM, apiHttp) + .dependsOn(coreJVM) .settings(stdSettings("zio-logging-examples-core", turnCompilerWarningIntoErrors = false)) .settings(enableZIO()) .settings( publish / skip := true, libraryDependencies ++= Seq( "dev.zio" %% "zio-metrics-connectors" % zioMetricsConnectorsVersion, + "dev.zio" %% "zio-http" % zioHttp, "dev.zio" %% "zio-config-typesafe" % zioConfig ) ) diff --git a/api-http/src/main/scala/zio/logging/api/http/ApiDomain.scala b/examples/core/src/main/scala/zio/logging/api/http/ApiDomain.scala similarity index 100% rename from api-http/src/main/scala/zio/logging/api/http/ApiDomain.scala rename to examples/core/src/main/scala/zio/logging/api/http/ApiDomain.scala diff --git a/api-http/src/main/scala/zio/logging/api/http/ApiEndpoints.scala b/examples/core/src/main/scala/zio/logging/api/http/ApiEndpoints.scala similarity index 100% rename from api-http/src/main/scala/zio/logging/api/http/ApiEndpoints.scala rename to examples/core/src/main/scala/zio/logging/api/http/ApiEndpoints.scala diff --git a/api-http/src/main/scala/zio/logging/api/http/ApiHandlers.scala b/examples/core/src/main/scala/zio/logging/api/http/ApiHandlers.scala similarity index 100% rename from api-http/src/main/scala/zio/logging/api/http/ApiHandlers.scala rename to examples/core/src/main/scala/zio/logging/api/http/ApiHandlers.scala diff --git a/api-http/src/test/scala/zio/logging/api/http/ApiEndpointsSpec.scala b/examples/core/src/test/scala/zio/logging/api/http/ApiEndpointsSpec.scala similarity index 100% rename from api-http/src/test/scala/zio/logging/api/http/ApiEndpointsSpec.scala rename to examples/core/src/test/scala/zio/logging/api/http/ApiEndpointsSpec.scala diff --git a/api-http/src/test/scala/zio/logging/api/http/ApiHandlersSpec.scala b/examples/core/src/test/scala/zio/logging/api/http/ApiHandlersSpec.scala similarity index 100% rename from api-http/src/test/scala/zio/logging/api/http/ApiHandlersSpec.scala rename to examples/core/src/test/scala/zio/logging/api/http/ApiHandlersSpec.scala From 3abe4c4770a3cf5ce36143da5b3d02c2111da9d9 Mon Sep 17 00:00:00 2001 From: Peter Kotula Date: Sun, 15 Oct 2023 17:57:05 +0200 Subject: [PATCH 45/56] fmt fix --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index 291c1b01c..71b0dc03b 100644 --- a/build.sbt +++ b/build.sbt @@ -200,7 +200,7 @@ lazy val examplesCore = project publish / skip := true, libraryDependencies ++= Seq( "dev.zio" %% "zio-metrics-connectors" % zioMetricsConnectorsVersion, - "dev.zio" %% "zio-http" % zioHttp, + "dev.zio" %% "zio-http" % zioHttp, "dev.zio" %% "zio-config-typesafe" % zioConfig ) ) From 051f282517ceb4e280ae6d5c4e2cc334c3fce7d9 Mon Sep 17 00:00:00 2001 From: Peter Kotula Date: Sun, 15 Oct 2023 19:53:26 +0200 Subject: [PATCH 46/56] ConfigurableLogger - moved to examples --- .../zio/logging/ConfigurableLogger.scala | 40 +++++-------------- .../example/ConfigurableLoggerApp.scala | 22 +++++----- 2 files changed, 19 insertions(+), 43 deletions(-) rename {core/shared => examples/core}/src/main/scala/zio/logging/ConfigurableLogger.scala (78%) diff --git a/core/shared/src/main/scala/zio/logging/ConfigurableLogger.scala b/examples/core/src/main/scala/zio/logging/ConfigurableLogger.scala similarity index 78% rename from core/shared/src/main/scala/zio/logging/ConfigurableLogger.scala rename to examples/core/src/main/scala/zio/logging/ConfigurableLogger.scala index 581bac78c..6ff7d85df 100644 --- a/core/shared/src/main/scala/zio/logging/ConfigurableLogger.scala +++ b/examples/core/src/main/scala/zio/logging/ConfigurableLogger.scala @@ -15,7 +15,7 @@ */ package zio.logging -import zio.{ Cause, FiberId, FiberRef, FiberRefs, LogLevel, LogSpan, Trace, ZIO, ZLayer, ZLogger } +import zio.{ Cause, FiberId, FiberRefs, LogLevel, LogSpan, Trace, ZIO, ZLayer, ZLogger } trait LoggerConfigurer { @@ -44,16 +44,14 @@ object LoggerConfigurer { val layer: ZLayer[Any, Throwable, LoggerConfigurer] = ZLayer.fromZIO { - for { - fiberRefs <- ZIO.getFiberRefs - - loggerService <- ZIO.attempt { - val loggers = fiberRefs.getOrDefault(FiberRef.currentLoggers) - loggers.collectFirst { case logger: ConfigurableLogger[_, _] => - logger.configurer - }.getOrElse(throw new RuntimeException("LoggerConfigurer not found")) - } - } yield loggerService + ZIO.loggers.flatMap { loggers => + loggers.collectFirst { case logger: ConfigurableLogger[_, _] => + logger.configurer + } match { + case Some(value) => ZIO.succeed(value) + case None => ZIO.fail(new RuntimeException("LoggerConfigurer not found")) + } + } } } @@ -64,26 +62,6 @@ trait ConfigurableLogger[-Message, +Output] extends ZLogger[Message, Output] { object ConfigurableLogger { -// def apply[Message, Output]( -// logger: ZLogger[Message, Output], -// loggerConfigurer: LoggerConfigurer -// ): ConfigurableLogger[Message, Output] = -// new ConfigurableLogger[Message, Output] { -// -// override val configurer: LoggerConfigurer = loggerConfigurer -// -// override def apply( -// trace: Trace, -// fiberId: FiberId, -// logLevel: LogLevel, -// message: () => Message, -// cause: Cause[Any], -// context: FiberRefs, -// spans: List[LogSpan], -// annotations: Map[String, String] -// ): Output = logger.apply(trace, fiberId, logLevel, message, cause, context, spans, annotations) -// } - def make[Message, Output]( logger: ZLogger[Message, Output], filterConfig: LogFilter.LogLevelByNameConfig diff --git a/examples/core/src/main/scala/zio/logging/example/ConfigurableLoggerApp.scala b/examples/core/src/main/scala/zio/logging/example/ConfigurableLoggerApp.scala index 530860de0..d7fbd2cd7 100644 --- a/examples/core/src/main/scala/zio/logging/example/ConfigurableLoggerApp.scala +++ b/examples/core/src/main/scala/zio/logging/example/ConfigurableLoggerApp.scala @@ -30,6 +30,16 @@ import zio.{ ExitCode, Runtime, Scope, ZIO, ZIOAppDefault, _ } import java.util.UUID +/* + curl -u "admin:admin" 'http://localhost:8080/example/logger' + + curl -u "admin:admin" 'http://localhost:8080/example/logger/root' + + curl -u "admin:admin" --location --request PUT 'http://localhost:8080/example/logger/root' --header 'Content-Type: application/json' --data-raw '"WARN"' + + curl -u "admin:admin" --location --request PUT 'http://localhost:8080/example/logger/zio.logging.example' --header 'Content-Type: application/json' --data-raw '"WARN"' + + */ object ConfigurableLoggerApp extends ZIOAppDefault { def configurableLogger(configPath: NonEmptyChunk[String] = loggerConfigPath) = @@ -86,15 +96,3 @@ object ConfigurableLoggerApp extends ZIOAppDefault { } yield ExitCode.success).provide(LoggerConfigurer.layer ++ Server.default) } - -/* - - curl -u "admin:admin" 'http://localhost:8080/example/logger' - - curl -u "admin:admin" 'http://localhost:8080/example/logger/root' - - curl -u "admin:admin" --location --request PUT 'http://localhost:8080/example/logger/root' --header 'Content-Type: application/json' --data-raw '"WARN"' - - curl -u "admin:admin" --location --request PUT 'http://localhost:8080/example/logger/zio.logging.example' --header 'Content-Type: application/json' --data-raw '"WARN"' - - */ From 75ffc5868f929176d52587c809f73b2f388d8070 Mon Sep 17 00:00:00 2001 From: Peter Kotula Date: Sat, 28 Oct 2023 16:40:03 +0200 Subject: [PATCH 47/56] doc updates --- .../src/main/scala/zio/logging/package.scala | 2 -- docs/console-logger.md | 2 +- docs/file-logger.md | 2 +- docs/jpl.md | 9 ++++- docs/slf4j1-bridge.md | 33 ++++++++++--------- docs/slf4j1.md | 8 +++++ docs/slf4j2-bridge.md | 28 ++++++++-------- .../example/Slf4jBridgeExampleApp.scala | 17 +++++----- .../slf4j/bridge/Slf4jBridgeExampleApp.scala | 17 +++++----- .../slf4j/bridge/Slf4jBridgeExampleApp.scala | 17 +++++----- 10 files changed, 74 insertions(+), 61 deletions(-) diff --git a/core/shared/src/main/scala/zio/logging/package.scala b/core/shared/src/main/scala/zio/logging/package.scala index 4b829b334..ccc03f802 100644 --- a/core/shared/src/main/scala/zio/logging/package.scala +++ b/core/shared/src/main/scala/zio/logging/package.scala @@ -54,8 +54,6 @@ package object logging extends LoggerLayers { def loggerName(value: String): ZIOAspect[Nothing, Any, Nothing, Any, Nothing, Any] = ZIOAspect.annotated(loggerNameAnnotationKey, value) - val removeDefaultLoggers: ZLayer[Any, Nothing, Unit] = Runtime.removeDefaultLoggers - implicit final class LogAnnotationZIOSyntax[R, E, A](private val self: ZIO[R, E, A]) { def logAnnotate[V: Tag](key: LogAnnotation[V], value: V): ZIO[R, E, A] = self @@ key(value) diff --git a/docs/console-logger.md b/docs/console-logger.md index 310310ab2..8debda568 100644 --- a/docs/console-logger.md +++ b/docs/console-logger.md @@ -11,7 +11,7 @@ import zio.{ ConfigProvider, Runtime } val configProvider: ConfigProvider = ??? -val logger = Runtime.removeDefaultLoggers >>> Runtime.setConfigProvider(configProvider) >>> consoleLogger(configPath = "logger") +val logger = Runtime.removeDefaultLoggers >>> Runtime.setConfigProvider(configProvider) >>> consoleLogger() ``` logger layer with given configuration: diff --git a/docs/file-logger.md b/docs/file-logger.md index 7e51274f5..867ea5d52 100644 --- a/docs/file-logger.md +++ b/docs/file-logger.md @@ -11,7 +11,7 @@ import zio.{ ConfigProvider, Runtime } val configProvider: ConfigProvider = ??? -val logger = Runtime.removeDefaultLoggers >>> Runtime.setConfigProvider(configProvider) >>> fileLogger(configPath = "logger") +val logger = Runtime.removeDefaultLoggers >>> Runtime.setConfigProvider(configProvider) >>> fileLogger() ``` logger layer with given configuration: diff --git a/docs/jpl.md b/docs/jpl.md index eb116fbe1..0b5ee3047 100644 --- a/docs/jpl.md +++ b/docs/jpl.md @@ -91,4 +91,11 @@ Oct 28, 2022 1:47:02 PM zio.logging.backend.JPL$$anon$1 $anonfun$closeLogEntry$1 INFO: user=59c114fd-676d-4df9-a5a0-b8e132468fbf trace_id=7d3e3b84-dd3b-44ff-915a-04fb2d135e28 Stopping user operation Oct 28, 2022 1:47:02 PM zio.logging.backend.JPL$$anon$1 $anonfun$closeLogEntry$1 INFO: Done -``` \ No newline at end of file +``` + +## Feature changes + +### Version 2.2.0 + +Deprecated log annotation with key `jpl_logger_name` (`JPL.loggerNameAnnotationKey`) removed, +only common log annotation with key `logger_name` (`zio.logging.loggerNameAnnotationKey`) for logger name is supported now. diff --git a/docs/slf4j1-bridge.md b/docs/slf4j1-bridge.md index 90fc80e59..f1c9375e4 100644 --- a/docs/slf4j1-bridge.md +++ b/docs/slf4j1-bridge.md @@ -53,9 +53,10 @@ ZIO logging. Enabling both causes circular logging and makes no sense. [//]: # (TODO: make snippet type-checked using mdoc) ```scala -package zio.logging.slf4j.bridge +package zio.logging.example -import zio.logging._ +import zio.logging.slf4j.bridge.Slf4jBridge +import zio.logging.{ ConsoleLoggerConfig, LogAnnotation, LogFilter, LogFormat, LoggerNameExtractor, consoleJsonLogger } import zio.{ ExitCode, LogLevel, Runtime, Scope, ZIO, ZIOAppArgs, ZIOAppDefault, ZLayer } import java.util.UUID @@ -64,23 +65,16 @@ object Slf4jBridgeExampleApp extends ZIOAppDefault { private val slf4jLogger = org.slf4j.LoggerFactory.getLogger("SLF4J-LOGGER") - private val logFilter: LogFilter[String] = LogFilter.logLevelByName( - LogLevel.Info, - "zio.logging.slf4j" -> LogLevel.Debug, - "SLF4J-LOGGER" -> LogLevel.Warning - ) + private val logFormat = LogFormat.label( + "name", + LoggerNameExtractor.loggerNameAnnotationOrTrace.toLogFormat() + ) + LogFormat.logAnnotation(LogAnnotation.UserId) + LogFormat.logAnnotation( + LogAnnotation.TraceId + ) + LogFormat.default override val bootstrap: ZLayer[ZIOAppArgs, Any, Any] = Runtime.removeDefaultLoggers >>> consoleJsonLogger( - ConsoleLoggerConfig( - LogFormat.label( - "name", - LoggerNameExtractor.loggerNameAnnotationOrTrace.toLogFormat() - ) + LogFormat.logAnnotation(LogAnnotation.UserId) + LogFormat.logAnnotation( - LogAnnotation.TraceId - ) + LogFormat.default, - logFilter - ) + ConsoleLoggerConfig(logFormat, logFilterConfig) ) >+> Slf4jBridge.initialize private val uuids = List.fill(2)(UUID.randomUUID()) @@ -145,3 +139,10 @@ val logFilter: LogFilter[String] = LogFilter.logLevelByGroup( "SLF4J-LOGGER" -> LogLevel.Warning ) ``` + +### Version 2.2.0 + +Deprecated log annotation with key `slf4j_logger_name` (`Slf4jBridge.loggerNameAnnotationKey`) removed, +only common log annotation with key `logger_name` (`zio.logging.loggerNameAnnotationKey`) for logger name is supported now. + + diff --git a/docs/slf4j1.md b/docs/slf4j1.md index 6a2712641..8db60dd60 100644 --- a/docs/slf4j1.md +++ b/docs/slf4j1.md @@ -185,3 +185,11 @@ Expected Console Output: 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 ``` + +## Feature changes + +### Version 2.2.0 + +Deprecated log annotation with key `slf4j_logger_name` (`SLF4J.loggerNameAnnotationKey`) removed, +only common log annotation with key `logger_name` (`zio.logging.loggerNameAnnotationKey`) for logger name is supported now. + diff --git a/docs/slf4j2-bridge.md b/docs/slf4j2-bridge.md index 5b4f08b96..ec3e097b6 100644 --- a/docs/slf4j2-bridge.md +++ b/docs/slf4j2-bridge.md @@ -33,11 +33,13 @@ may be used to get logger name from log annotation or ZIO Trace. This logger name extractor is used by default in log filter, which applying log filtering by defined logger name and level: ```scala -val logFilter: LogFilter[String] = LogFilter.logLevelByName( +val logFilterConfig = LogFilter.LogLevelByNameConfig( LogLevel.Info, "zio.logging.slf4j" -> LogLevel.Debug, "SLF4J-LOGGER" -> LogLevel.Warning ) + +val logFilter: LogFilter[String] = logFilterConfig.toFilter ```
@@ -65,9 +67,10 @@ You can find the source code [here](https://github.com/zio/zio-logging/tree/mast [//]: # (TODO: make snippet type-checked using mdoc) ```scala -package zio.logging.slf4j.bridge +package zio.logging.example -import zio.logging._ +import zio.logging.slf4j.bridge.Slf4jBridge +import zio.logging.{ ConsoleLoggerConfig, LogAnnotation, LogFilter, LogFormat, LoggerNameExtractor, consoleJsonLogger } import zio.{ ExitCode, LogLevel, Runtime, Scope, ZIO, ZIOAppArgs, ZIOAppDefault, ZLayer } import java.util.UUID @@ -76,23 +79,22 @@ object Slf4jBridgeExampleApp extends ZIOAppDefault { private val slf4jLogger = org.slf4j.LoggerFactory.getLogger("SLF4J-LOGGER") - private val logFilter: LogFilter[String] = LogFilter.logLevelByName( + private val logFilterConfig = LogFilter.LogLevelByNameConfig( LogLevel.Info, "zio.logging.slf4j" -> LogLevel.Debug, "SLF4J-LOGGER" -> LogLevel.Warning ) + private val logFormat = LogFormat.label( + "name", + LoggerNameExtractor.loggerNameAnnotationOrTrace.toLogFormat() + ) + LogFormat.logAnnotation(LogAnnotation.UserId) + LogFormat.logAnnotation( + LogAnnotation.TraceId + ) + LogFormat.default + override val bootstrap: ZLayer[ZIOAppArgs, Any, Any] = Runtime.removeDefaultLoggers >>> consoleJsonLogger( - ConsoleLoggerConfig( - LogFormat.label( - "name", - LoggerNameExtractor.loggerNameAnnotationOrTrace.toLogFormat() - ) + LogFormat.logAnnotation(LogAnnotation.UserId) + LogFormat.logAnnotation( - LogAnnotation.TraceId - ) + LogFormat.default, - logFilter - ) + ConsoleLoggerConfig(logFormat, logFilterConfig) ) >+> Slf4jBridge.initialize private val uuids = List.fill(2)(UUID.randomUUID()) diff --git a/examples/slf4j2-bridge/src/main/scala/zio/logging/example/Slf4jBridgeExampleApp.scala b/examples/slf4j2-bridge/src/main/scala/zio/logging/example/Slf4jBridgeExampleApp.scala index 1c03d6bb3..69de5ad26 100644 --- a/examples/slf4j2-bridge/src/main/scala/zio/logging/example/Slf4jBridgeExampleApp.scala +++ b/examples/slf4j2-bridge/src/main/scala/zio/logging/example/Slf4jBridgeExampleApp.scala @@ -31,17 +31,16 @@ object Slf4jBridgeExampleApp extends ZIOAppDefault { "SLF4J-LOGGER" -> LogLevel.Warning ) + private val logFormat = LogFormat.label( + "name", + LoggerNameExtractor.loggerNameAnnotationOrTrace.toLogFormat() + ) + LogFormat.logAnnotation(LogAnnotation.UserId) + LogFormat.logAnnotation( + LogAnnotation.TraceId + ) + LogFormat.default + override val bootstrap: ZLayer[ZIOAppArgs, Any, Any] = Runtime.removeDefaultLoggers >>> consoleJsonLogger( - ConsoleLoggerConfig( - LogFormat.label( - "name", - LoggerNameExtractor.loggerNameAnnotationOrTrace.toLogFormat() - ) + LogFormat.logAnnotation(LogAnnotation.UserId) + LogFormat.logAnnotation( - LogAnnotation.TraceId - ) + LogFormat.default, - logFilterConfig - ) + ConsoleLoggerConfig(logFormat, logFilterConfig) ) >+> Slf4jBridge.initialize private val uuids = List.fill(2)(UUID.randomUUID()) diff --git a/slf4j-bridge/src/test/scala/zio/logging/slf4j/bridge/Slf4jBridgeExampleApp.scala b/slf4j-bridge/src/test/scala/zio/logging/slf4j/bridge/Slf4jBridgeExampleApp.scala index 2f5b11986..489d8b447 100644 --- a/slf4j-bridge/src/test/scala/zio/logging/slf4j/bridge/Slf4jBridgeExampleApp.scala +++ b/slf4j-bridge/src/test/scala/zio/logging/slf4j/bridge/Slf4jBridgeExampleApp.scala @@ -15,17 +15,16 @@ object Slf4jBridgeExampleApp extends ZIOAppDefault { "SLF4J-LOGGER" -> LogLevel.Warning ) + private val logFormat = LogFormat.label( + "name", + LoggerNameExtractor.loggerNameAnnotationOrTrace.toLogFormat() + ) + LogFormat.logAnnotation(LogAnnotation.UserId) + LogFormat.logAnnotation( + LogAnnotation.TraceId + ) + LogFormat.default + override val bootstrap: ZLayer[ZIOAppArgs, Any, Any] = Runtime.removeDefaultLoggers >>> consoleJsonLogger( - ConsoleLoggerConfig( - LogFormat.label( - "name", - LoggerNameExtractor.loggerNameAnnotationOrTrace.toLogFormat() - ) + LogFormat.logAnnotation(LogAnnotation.UserId) + LogFormat.logAnnotation( - LogAnnotation.TraceId - ) + LogFormat.default, - logFilterConfig - ) + ConsoleLoggerConfig(logFormat, logFilterConfig) ) >+> Slf4jBridge.initialize private val uuids = List.fill(2)(UUID.randomUUID()) diff --git a/slf4j2-bridge/src/test/scala/zio/logging/slf4j/bridge/Slf4jBridgeExampleApp.scala b/slf4j2-bridge/src/test/scala/zio/logging/slf4j/bridge/Slf4jBridgeExampleApp.scala index 928cd06bf..06c7ff166 100644 --- a/slf4j2-bridge/src/test/scala/zio/logging/slf4j/bridge/Slf4jBridgeExampleApp.scala +++ b/slf4j2-bridge/src/test/scala/zio/logging/slf4j/bridge/Slf4jBridgeExampleApp.scala @@ -15,17 +15,16 @@ object Slf4jBridgeExampleApp extends ZIOAppDefault { "SLF4J-LOGGER" -> LogLevel.Warning ) + private val logFormat = LogFormat.label( + "name", + LoggerNameExtractor.loggerNameAnnotationOrTrace.toLogFormat() + ) + LogFormat.logAnnotation(LogAnnotation.UserId) + LogFormat.logAnnotation( + LogAnnotation.TraceId + ) + LogFormat.default + override val bootstrap: ZLayer[ZIOAppArgs, Any, Any] = Runtime.removeDefaultLoggers >>> consoleJsonLogger( - ConsoleLoggerConfig( - LogFormat.label( - "name", - LoggerNameExtractor.loggerNameAnnotationOrTrace.toLogFormat() - ) + LogFormat.logAnnotation(LogAnnotation.UserId) + LogFormat.logAnnotation( - LogAnnotation.TraceId - ) + LogFormat.default, - logFilterConfig - ) + ConsoleLoggerConfig(logFormat, logFilterConfig) ) >+> Slf4jBridge.initialize private val uuids = List.fill(2)(UUID.randomUUID()) From fee64523bb07c6c722c45b9183f66301a5116b33 Mon Sep 17 00:00:00 2001 From: Peter Kotula Date: Thu, 23 Nov 2023 17:21:11 +0100 Subject: [PATCH 48/56] merge fixes --- .../logging/slf4j/bridge/Slf4jBridge.scala | 6 +- .../slf4j/bridge/Slf4jBridgeSpec.scala | 70 +++++++++---------- 2 files changed, 38 insertions(+), 38 deletions(-) diff --git a/slf4j-bridge/src/main/scala/zio/logging/slf4j/bridge/Slf4jBridge.scala b/slf4j-bridge/src/main/scala/zio/logging/slf4j/bridge/Slf4jBridge.scala index a9d354067..463dde8bb 100644 --- a/slf4j-bridge/src/main/scala/zio/logging/slf4j/bridge/Slf4jBridge.scala +++ b/slf4j-bridge/src/main/scala/zio/logging/slf4j/bridge/Slf4jBridge.scala @@ -37,9 +37,9 @@ object Slf4jBridge { ZLayer { for { runtime <- ZIO.runtime[Any] - _ <- initLock.withPermit { - ZIO.succeed(ZioLoggerFactory.initialize(new ZioLoggerRuntime(runtime))) - } + _ <- initLock.withPermit { + ZIO.succeed(ZioLoggerFactory.initialize(new ZioLoggerRuntime(runtime))) + } } yield () } } diff --git a/slf4j-bridge/src/test/scala/zio/logging/slf4j/bridge/Slf4jBridgeSpec.scala b/slf4j-bridge/src/test/scala/zio/logging/slf4j/bridge/Slf4jBridgeSpec.scala index 4c725cffa..d03247d84 100644 --- a/slf4j-bridge/src/test/scala/zio/logging/slf4j/bridge/Slf4jBridgeSpec.scala +++ b/slf4j-bridge/src/test/scala/zio/logging/slf4j/bridge/Slf4jBridgeSpec.scala @@ -8,12 +8,12 @@ import zio.{ Cause, Chunk, LogLevel, ZIO, ZIOAspect } object Slf4jBridgeSpec extends ZIOSpecDefault { final case class LogEntry( - span: List[String], - level: LogLevel, - annotations: Map[String, String], - message: String, - cause: Cause[Any] - ) + span: List[String], + level: LogLevel, + annotations: Map[String, String], + message: String, + cause: Cause[Any] + ) override def spec = suite("Slf4jBridge")( @@ -41,25 +41,25 @@ object Slf4jBridgeSpec extends ZIOSpecDefault { val testFailure = new RuntimeException("test error") for { _ <- (for { - logger <- ZIO.attempt(org.slf4j.LoggerFactory.getLogger("test.logger")) - _ <- ZIO.logSpan("span")(ZIO.attempt(logger.debug("test debug message"))) @@ ZIOAspect - .annotated("trace_id", "tId") - _ <- ZIO.attempt(logger.warn("hello {}", "world")) @@ ZIOAspect.annotated("user_id", "uId") - _ <- ZIO.attempt(logger.warn("{}..{}..{} ... go!", "3", "2", "1")) - _ <- ZIO.attempt(logger.warn("warn cause", testFailure)) - _ <- ZIO.attempt(logger.error("error", testFailure)) - _ <- ZIO.attempt(logger.error("error", null)) - } yield ()).exit + logger <- ZIO.attempt(org.slf4j.LoggerFactory.getLogger("test.logger")) + _ <- ZIO.logSpan("span")(ZIO.attempt(logger.debug("test debug message"))) @@ ZIOAspect + .annotated("trace_id", "tId") + _ <- ZIO.attempt(logger.warn("hello {}", "world")) @@ ZIOAspect.annotated("user_id", "uId") + _ <- ZIO.attempt(logger.warn("{}..{}..{} ... go!", "3", "2", "1")) + _ <- ZIO.attempt(logger.warn("warn cause", testFailure)) + _ <- ZIO.attempt(logger.error("error", testFailure)) + _ <- ZIO.attempt(logger.error("error", null)) + } yield ()).exit output <- ZTestLogger.logOutput lines = output.map { logEntry => - LogEntry( - logEntry.spans.map(_.label), - logEntry.logLevel, - logEntry.annotations, - logEntry.message(), - logEntry.cause - ) - } + LogEntry( + logEntry.spans.map(_.label), + logEntry.logLevel, + logEntry.annotations, + logEntry.message(), + logEntry.cause + ) + } } yield assertTrue( lines == Chunk( LogEntry( @@ -110,20 +110,20 @@ object Slf4jBridgeSpec extends ZIOSpecDefault { test("logs through slf4j without fiber ref propagation") { for { _ <- (for { - logger <- ZIO.attempt(org.slf4j.LoggerFactory.getLogger("test.logger")) - _ <- ZIO.attempt(logger.debug("test debug message")) @@ ZIOAspect.annotated("trace_id", "tId") - _ <- ZIO.attempt(logger.warn("hello {}", "world")) @@ ZIOAspect.annotated("user_id", "uId") - } yield ()).exit + logger <- ZIO.attempt(org.slf4j.LoggerFactory.getLogger("test.logger")) + _ <- ZIO.attempt(logger.debug("test debug message")) @@ ZIOAspect.annotated("trace_id", "tId") + _ <- ZIO.attempt(logger.warn("hello {}", "world")) @@ ZIOAspect.annotated("user_id", "uId") + } yield ()).exit output <- ZTestLogger.logOutput lines = output.map { logEntry => - LogEntry( - logEntry.spans.map(_.label), - logEntry.logLevel, - logEntry.annotations, - logEntry.message(), - logEntry.cause - ) - } + LogEntry( + logEntry.spans.map(_.label), + logEntry.logLevel, + logEntry.annotations, + logEntry.message(), + logEntry.cause + ) + } } yield assertTrue( lines == Chunk( LogEntry( From 07ed580523f2a162d68592f97e035689f041f0fe Mon Sep 17 00:00:00 2001 From: Peter Kotula Date: Sun, 24 Dec 2023 19:03:48 +0100 Subject: [PATCH 49/56] dep. upgrades --- build.sbt | 6 +++--- project/Versions.scala | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build.sbt b/build.sbt index 71b0dc03b..7d3682475 100644 --- a/build.sbt +++ b/build.sbt @@ -199,9 +199,9 @@ lazy val examplesCore = project .settings( publish / skip := true, libraryDependencies ++= Seq( - "dev.zio" %% "zio-metrics-connectors" % zioMetricsConnectorsVersion, - "dev.zio" %% "zio-http" % zioHttp, - "dev.zio" %% "zio-config-typesafe" % zioConfig + "dev.zio" %% "zio-metrics-connectors-prometheus" % zioMetricsConnectorsVersion, + "dev.zio" %% "zio-http" % zioHttp, + "dev.zio" %% "zio-config-typesafe" % zioConfig ) ) diff --git a/project/Versions.scala b/project/Versions.scala index 8d710e4e1..f2910aed3 100644 --- a/project/Versions.scala +++ b/project/Versions.scala @@ -6,7 +6,7 @@ object Versions { val scalaCollectionCompatVersion = "2.11.0" val logstashLogbackEncoderVersion = "6.6" val scalaJavaTimeVersion = "2.5.0" - val zioMetricsConnectorsVersion = "2.0.8" + val zioMetricsConnectorsVersion = "2.3.1" val zioConfig = "4.0.0" val zioParser = "0.1.9" val zioPrelude = "1.0.0-RC19" From 725dfff5138700a85f3bf14b5d22cc8c0637f37e Mon Sep 17 00:00:00 2001 From: Peter Kotula Date: Sun, 24 Dec 2023 19:26:07 +0100 Subject: [PATCH 50/56] LoggerReconfigureApp with logger.conf --- examples/core/src/main/resources/logger.conf | 8 ++++++++ .../logging/example/LoggerReconfigureApp.scala | 18 +++++++----------- 2 files changed, 15 insertions(+), 11 deletions(-) create mode 100644 examples/core/src/main/resources/logger.conf diff --git a/examples/core/src/main/resources/logger.conf b/examples/core/src/main/resources/logger.conf new file mode 100644 index 000000000..5c65f06c8 --- /dev/null +++ b/examples/core/src/main/resources/logger.conf @@ -0,0 +1,8 @@ +logger { + format = "%highlight{%timestamp{yyyy-MM-dd'T'HH:mm:ssZ} %fixed{7}{%level} [%fiberId] %name:%line %message %kv{trace_id} %kv{user_id} %cause}" + filter { + rootLevel = "INFO" + mappings { + } + } +} \ No newline at end of file diff --git a/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala b/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala index 847fa5bf8..0b80946f5 100644 --- a/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala +++ b/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala @@ -15,6 +15,8 @@ */ package zio.logging.example +import com.typesafe.config.ConfigFactory +import zio.config.typesafe.TypesafeConfigProvider import zio.logging.{ ConsoleLoggerConfig, LogAnnotation, ReconfigurableLogger, _ } import zio.{ Config, ExitCode, Runtime, Scope, ZIO, ZIOAppDefault, _ } @@ -22,9 +24,6 @@ import java.util.UUID object LoggerReconfigureApp extends ZIOAppDefault { - val logFormat = - "%highlight{%timestamp{yyyy-MM-dd'T'HH:mm:ssZ} %fixed{7}{%level} [%fiberId] %name:%line %message %kv{trace_id} %kv{user_id} %cause}" - def configuredLogger( loadConfig: => ZIO[Any, Config.Error, ConsoleLoggerConfig] ): ZLayer[Any, Config.Error, Unit] = @@ -39,14 +38,11 @@ object LoggerReconfigureApp extends ZIOAppDefault { override val bootstrap: ZLayer[ZIOAppArgs, Any, Any] = Runtime.removeDefaultLoggers >>> configuredLogger( for { - info <- Random.nextBoolean - cfg = Map( - "logger/format" -> logFormat, - "logger/filter/rootLevel" -> (if (info) LogLevel.Info.label else LogLevel.Debug.label) - ) - _ <- Console.printLine(cfg.mkString(", ")).orDie - config <- ConfigProvider.fromMap(cfg, "/").nested("logger").load(ConsoleLoggerConfig.config) - } yield config + config <- ZIO.succeed(ConfigFactory.load("logger.conf")) + _ <- Console.printLine(config.getConfig("logger")).orDie + loggerConfig <- + TypesafeConfigProvider.fromTypesafeConfig(config).nested("logger").load(ConsoleLoggerConfig.config) + } yield loggerConfig ) def exec(): ZIO[Any, Nothing, Unit] = From 89d097cf74b6fe67ae1149b54e7f1cb3a0a0e83a Mon Sep 17 00:00:00 2001 From: Peter Kotula Date: Mon, 25 Dec 2023 11:32:19 +0100 Subject: [PATCH 51/56] benchmarks, docs --- .../scala/zio/logging/FilterBenchmarks.scala | 44 +++++- docs/reconfigurable-logger.md | 137 ++++++++++++++++++ docs/sidebars.js | 1 + .../example/LoggerReconfigureApp.scala | 2 +- 4 files changed, 176 insertions(+), 8 deletions(-) create mode 100644 docs/reconfigurable-logger.md diff --git a/benchmarks/src/main/scala/zio/logging/FilterBenchmarks.scala b/benchmarks/src/main/scala/zio/logging/FilterBenchmarks.scala index e1106ae5c..f31f9b19a 100644 --- a/benchmarks/src/main/scala/zio/logging/FilterBenchmarks.scala +++ b/benchmarks/src/main/scala/zio/logging/FilterBenchmarks.scala @@ -1,7 +1,7 @@ package zio.logging import org.openjdk.jmh.annotations._ -import zio.{ LogLevel, Runtime, Unsafe, ZIO, ZLayer } +import zio.{ ConfigProvider, LogLevel, Runtime, Unsafe, ZIO, ZLayer } import java.util.concurrent.TimeUnit import scala.util.Random @@ -77,6 +77,31 @@ class FilterBenchmarks { .install } + val reconfigurableFilterByLogLevelAndNameLogging: ZLayer[Any, Nothing, Unit] = { + val logFormat = + "%label{timestamp}{%fixed{32}{%timestamp}} %label{level}{%level} %label{thread}{%fiberId} %label{message}{%message} %label{cause}{%cause}" + + val configProvider: ConfigProvider = ConfigProvider.fromMap( + Map( + "logger/format" -> logFormat, + "logger/filter/rootLevel" -> LogLevel.Debug.label, + "logger/filter/mappings/a.b.c" -> LogLevel.Info.label, + "logger/filter/mappings/a.b.d" -> LogLevel.Warning.label, + "logger/filter/mappings/e" -> LogLevel.Info.label, + "logger/filter/mappings/f.g" -> LogLevel.Error.label, + "logger/filter/mappings/f" -> LogLevel.Info.label + ), + "/" + ) + + Runtime.removeDefaultLoggers >>> Runtime.setConfigProvider(configProvider) >>> ReconfigurableLogger + .make[Any, Nothing, String, Any, ConsoleLoggerConfig]( + ConsoleLoggerConfig.load().orDie, + (config, _) => makeConsoleLogger(config) + ) + .installUnscoped[Any] + } + val names: List[String] = List( "a", "a.b", @@ -113,15 +138,16 @@ class FilterBenchmarks { } /** - * 2022/10/28 Initial results + * 2023/12/25 Initial results * * jmh:run -i 3 -wi 3 -f1 -t1 .*FilterBenchmarks.* * - * Benchmark Mode Cnt Score Error Units - * FilterBenchmarks.cachedFilterByLogLevelAndNameLog thrpt 3 16623.054 ± 15855.331 ops/s - * FilterBenchmarks.filterByLogLevelAndNameLog thrpt 3 18048.598 ± 3868.976 ops/s - * FilterBenchmarks.handWrittenFilterLog thrpt 3 16352.488 ± 2316.372 ops/s - * FilterBenchmarks.noFilteringLog thrpt 3 15104.002 ± 3857.108 ops/s + * Benchmark Mode Cnt Score Error Units + * FilterBenchmarks.cachedFilterByLogLevelAndNameLog thrpt 3 15098.312 ± 4204.210 ops/s + * FilterBenchmarks.filterByLogLevelAndNameLog thrpt 3 13100.786 ± 2017.585 ops/s + * FilterBenchmarks.handWrittenFilterLog thrpt 3 10864.716 ± 482.042 ops/s + * FilterBenchmarks.noFilteringLog thrpt 3 8813.185 ± 10371.239 ops/s + * FilterBenchmarks.reconfigurableFilterByLogLevelAndNameLog thrpt 3 3334.433 ± 216.060 ops/s */ @Benchmark @@ -140,4 +166,8 @@ class FilterBenchmarks { def cachedFilterByLogLevelAndNameLog(): Unit = testLoggingWith(cachedFilterByLogLevelAndNameLogging) + @Benchmark + def reconfigurableFilterByLogLevelAndNameLog(): Unit = + testLoggingWith(reconfigurableFilterByLogLevelAndNameLogging) + } diff --git a/docs/reconfigurable-logger.md b/docs/reconfigurable-logger.md new file mode 100644 index 000000000..2aa797a0c --- /dev/null +++ b/docs/reconfigurable-logger.md @@ -0,0 +1,137 @@ +--- +id: reconfigurable-logger +title: "Reconfigurable Logger" +--- + +`ReconfigurableLogger` is adding support for updating logger configuration in application runtime. + +logger layer with configuration from config provider (example with [Console Logger)](console-logger.md)): + + +```scala +import zio.logging.{ consoleLogger, ConsoleLoggerConfig, ReconfigurableLogger } +import zio._ + +val configProvider: ConfigProvider = ??? + +val logger = Runtime.removeDefaultLoggers >>> Runtime.setConfigProvider(configProvider) >>> ReconfigurableLogger + .make[Any, Nothing, String, Any, ConsoleLoggerConfig]( + ConsoleLoggerConfig.load().orDie, // loading current configuration + (config, _) => makeConsoleLogger(config), // make logger from loaded configuration + Schedule.fixed(5.second) // default is 10 seconds + ) + .installUnscoped[Any] +``` + +`ReconfigurableLogger`, based on given `Schedule` and load configuration function, will recreate logger if configuration changed. + +## Examples + +You can find the source code [here](https://github.com/zio/zio-logging/tree/master/examples) + + +### Colorful Console Logger With Reconfiguration In Runtime + +[//]: # (TODO: make snippet type-checked using mdoc) + +By default, root level configuration is `INFO`, when root level configuration is changed to `DEBUG`, other logger messages should be in output. + +Configuration: + +``` +logger { + format = "%highlight{%timestamp{yyyy-MM-dd'T'HH:mm:ssZ} %fixed{7}{%level} [%fiberId] %name:%line %message %kv{trace_id} %kv{user_id} %cause}" + filter { + rootLevel = "DEBUG" + } +} +``` + +Application: + +```scala +package zio.logging.example + +import com.typesafe.config.ConfigFactory +import zio.config.typesafe.TypesafeConfigProvider +import zio.logging.{ ConsoleLoggerConfig, LogAnnotation, ReconfigurableLogger, _ } +import zio.{ Config, ExitCode, Runtime, Scope, ZIO, ZIOAppDefault, _ } + +import java.util.UUID + +object LoggerReconfigureApp extends ZIOAppDefault { + + def configuredLogger( + loadConfig: => ZIO[Any, Config.Error, ConsoleLoggerConfig] + ): ZLayer[Any, Config.Error, Unit] = + ReconfigurableLogger + .make[Any, Config.Error, String, Any, ConsoleLoggerConfig]( + loadConfig, + (config, _) => makeConsoleLogger(config), + Schedule.fixed(500.millis) + ) + .installUnscoped + + override val bootstrap: ZLayer[ZIOAppArgs, Any, Any] = + Runtime.removeDefaultLoggers >>> configuredLogger( + for { + config <- ZIO.succeed(ConfigFactory.load("logger.conf")) + _ <- Console.printLine(config.getConfig("logger")).orDie + loggerConfig <- + TypesafeConfigProvider.fromTypesafeConfig(config).nested("logger").load(ConsoleLoggerConfig.config) + } yield loggerConfig + ) + + def exec(): ZIO[Any, Nothing, Unit] = + for { + ok <- Random.nextBoolean + traceId <- ZIO.succeed(UUID.randomUUID()) + _ <- ZIO.logDebug("Start") @@ LogAnnotation.TraceId(traceId) + userIds <- ZIO.succeed(List.fill(2)(UUID.randomUUID().toString)) + _ <- ZIO.foreachPar(userIds) { userId => + { + ZIO.logDebug("Starting operation") *> + ZIO.logInfo("OK operation").when(ok) *> + ZIO.logError("Error operation").when(!ok) *> + ZIO.logDebug("Stopping operation") + } @@ LogAnnotation.UserId(userId) + } @@ LogAnnotation.TraceId(traceId) + _ <- ZIO.logDebug("Done") @@ LogAnnotation.TraceId(traceId) + } yield () + + override def run: ZIO[Scope, Any, ExitCode] = + for { + _ <- exec().repeat(Schedule.fixed(500.millis)) + } yield ExitCode.success + +} +``` + +Expected console output: + +``` +Config(SimpleConfigObject({"filter":{"mappings":{},"rootLevel":"INFO"},"format":"%highlight{%timestamp{yyyy-MM-dd'T'HH:mm:ssZ} %fixed{7}{%level} [%fiberId] %name:%line %message %kv{trace_id} %kv{user_id} %cause}"})) +2023-12-25T11:11:27+0100 ERROR [zio-fiber-106] zio.logging.example.LoggerReconfigureApp:58 Error operation trace_id=18788cfd-5efe-488b-8549-c69f374110ea user_id=b31bb7a0-f8d1-4d7d-8215-1c980ec0e986 +2023-12-25T11:11:27+0100 ERROR [zio-fiber-107] zio.logging.example.LoggerReconfigureApp:58 Error operation trace_id=18788cfd-5efe-488b-8549-c69f374110ea user_id=2c390abc-17a7-413d-9d4a-668215727fbd +Config(SimpleConfigObject({"filter":{"mappings":{},"rootLevel":"INFO"},"format":"%highlight{%timestamp{yyyy-MM-dd'T'HH:mm:ssZ} %fixed{7}{%level} [%fiberId] %name:%line %message %kv{trace_id} %kv{user_id} %cause}"})) +2023-12-25T11:11:28+0100 ERROR [zio-fiber-108] zio.logging.example.LoggerReconfigureApp:58 Error operation trace_id=865905f4-9548-44b6-bd60-9b2d30da95f4 user_id=6abecac0-b867-4f50-a699-c344d2548b79 +2023-12-25T11:11:28+0100 ERROR [zio-fiber-109] zio.logging.example.LoggerReconfigureApp:58 Error operation trace_id=865905f4-9548-44b6-bd60-9b2d30da95f4 user_id=b7f73c21-2dc6-4707-8bdd-96cfd7500e26 +Config(SimpleConfigObject({"filter":{"mappings":{},"rootLevel":"DEBUG"},"format":"%highlight{%timestamp{yyyy-MM-dd'T'HH:mm:ssZ} %fixed{7}{%level} [%fiberId] %name:%line %message %kv{trace_id} %kv{user_id} %cause}"})) +2023-12-25T11:11:28+0100 DEBUG [zio-fiber-5] zio.logging.example.LoggerReconfigureApp:52 Start trace_id=44ac195b-9e17-42d8-b8ff-264a52275855 +2023-12-25T11:11:28+0100 DEBUG [zio-fiber-110] zio.logging.example.LoggerReconfigureApp:56 Starting operation trace_id=44ac195b-9e17-42d8-b8ff-264a52275855 user_id=a976cb5b-d035-488f-9664-3761a07fb2d9 +2023-12-25T11:11:28+0100 DEBUG [zio-fiber-111] zio.logging.example.LoggerReconfigureApp:56 Starting operation trace_id=44ac195b-9e17-42d8-b8ff-264a52275855 user_id=ed23b03a-00e5-4495-8a0d-092eee0a7479 +2023-12-25T11:11:28+0100 INFO [zio-fiber-110] zio.logging.example.LoggerReconfigureApp:57 OK operation trace_id=44ac195b-9e17-42d8-b8ff-264a52275855 user_id=a976cb5b-d035-488f-9664-3761a07fb2d9 +2023-12-25T11:11:28+0100 INFO [zio-fiber-111] zio.logging.example.LoggerReconfigureApp:57 OK operation trace_id=44ac195b-9e17-42d8-b8ff-264a52275855 user_id=ed23b03a-00e5-4495-8a0d-092eee0a7479 +2023-12-25T11:11:28+0100 DEBUG [zio-fiber-111] zio.logging.example.LoggerReconfigureApp:59 Stopping operation trace_id=44ac195b-9e17-42d8-b8ff-264a52275855 user_id=ed23b03a-00e5-4495-8a0d-092eee0a7479 +2023-12-25T11:11:28+0100 DEBUG [zio-fiber-110] zio.logging.example.LoggerReconfigureApp:59 Stopping operation trace_id=44ac195b-9e17-42d8-b8ff-264a52275855 user_id=a976cb5b-d035-488f-9664-3761a07fb2d9 +2023-12-25T11:11:28+0100 DEBUG [zio-fiber-5] zio.logging.example.LoggerReconfigureApp:62 Done trace_id=44ac195b-9e17-42d8-b8ff-264a52275855 +Config(SimpleConfigObject({"filter":{"mappings":{},"rootLevel":"DEBUG"},"format":"%highlight{%timestamp{yyyy-MM-dd'T'HH:mm:ssZ} %fixed{7}{%level} [%fiberId] %name:%line %message %kv{trace_id} %kv{user_id} %cause}"})) +2023-12-25T11:11:29+0100 DEBUG [zio-fiber-5] zio.logging.example.LoggerReconfigureApp:52 Start trace_id=f613ff34-8d8a-49e8-b58c-5fa954f3bc79 +2023-12-25T11:11:29+0100 DEBUG [zio-fiber-113] zio.logging.example.LoggerReconfigureApp:56 Starting operation trace_id=f613ff34-8d8a-49e8-b58c-5fa954f3bc79 user_id=0c513b80-3fc6-4aa9-a805-11e658805296 +2023-12-25T11:11:29+0100 DEBUG [zio-fiber-112] zio.logging.example.LoggerReconfigureApp:56 Starting operation trace_id=f613ff34-8d8a-49e8-b58c-5fa954f3bc79 user_id=aa296f97-6503-4633-ac78-16094f00ceca +2023-12-25T11:11:29+0100 ERROR [zio-fiber-113] zio.logging.example.LoggerReconfigureApp:58 Error operation trace_id=f613ff34-8d8a-49e8-b58c-5fa954f3bc79 user_id=0c513b80-3fc6-4aa9-a805-11e658805296 +2023-12-25T11:11:29+0100 ERROR [zio-fiber-112] zio.logging.example.LoggerReconfigureApp:58 Error operation trace_id=f613ff34-8d8a-49e8-b58c-5fa954f3bc79 user_id=aa296f97-6503-4633-ac78-16094f00ceca +2023-12-25T11:11:29+0100 DEBUG [zio-fiber-113] zio.logging.example.LoggerReconfigureApp:59 Stopping operation trace_id=f613ff34-8d8a-49e8-b58c-5fa954f3bc79 user_id=0c513b80-3fc6-4aa9-a805-11e658805296 +2023-12-25T11:11:29+0100 DEBUG [zio-fiber-112] zio.logging.example.LoggerReconfigureApp:59 Stopping operation trace_id=f613ff34-8d8a-49e8-b58c-5fa954f3bc79 user_id=aa296f97-6503-4633-ac78-16094f00ceca +2023-12-25T11:11:29+0100 DEBUG [zio-fiber-5] zio.logging.example.LoggerReconfigureApp:62 Done trace_id=f613ff34-8d8a-49e8-b58c-5fa954f3bc79 +``` diff --git a/docs/sidebars.js b/docs/sidebars.js index 3fde2ec5e..cf12c2599 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -10,6 +10,7 @@ const sidebars = { 'log-filter', 'console-logger', 'file-logger', + 'reconfigurable-logger', 'jpl', 'slf4j2', 'slf4j1', diff --git a/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala b/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala index 0b80946f5..16476c92b 100644 --- a/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala +++ b/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala @@ -30,7 +30,7 @@ object LoggerReconfigureApp extends ZIOAppDefault { ReconfigurableLogger .make[Any, Config.Error, String, Any, ConsoleLoggerConfig]( loadConfig, - (config, _) => makeSystemOutLogger(config.format.toLogger).filter(config.filter.toFilter), + (config, _) => makeConsoleLogger(config), Schedule.fixed(500.millis) ) .installUnscoped From 818216304a9c2f6792dc2060799b9413fb621802 Mon Sep 17 00:00:00 2001 From: Peter Kotula Date: Mon, 25 Dec 2023 12:06:23 +0100 Subject: [PATCH 52/56] zio-http updates --- .../zio/logging/api/http/ApiEndpoints.scala | 18 +++---- .../zio/logging/api/http/ApiHandlers.scala | 51 ++++++++++--------- .../example/ConfigurableLoggerApp.scala | 5 +- .../logging/api/http/ApiEndpointsSpec.scala | 24 +++++---- .../logging/api/http/ApiHandlersSpec.scala | 10 ++-- project/Versions.scala | 2 +- 6 files changed, 59 insertions(+), 51 deletions(-) diff --git a/examples/core/src/main/scala/zio/logging/api/http/ApiEndpoints.scala b/examples/core/src/main/scala/zio/logging/api/http/ApiEndpoints.scala index 4c1f2aa86..feb6f12ce 100644 --- a/examples/core/src/main/scala/zio/logging/api/http/ApiEndpoints.scala +++ b/examples/core/src/main/scala/zio/logging/api/http/ApiEndpoints.scala @@ -21,30 +21,29 @@ import zio.http.codec.PathCodec.{ literal, string } import zio.http.codec.{ Doc, HttpCodec, PathCodec } import zio.http.endpoint.EndpointMiddleware.None import zio.http.endpoint._ +import zio.logging.api.http.ApiDomain.Error object ApiEndpoints { import ApiDomain.logLevelSchema def rootPathCodec(rootPath: Seq[String]): PathCodec[Unit] = if (rootPath.isEmpty) { - HttpCodec.empty + PathCodec.empty } else { rootPath.map(literal).reduce(_ / _) } def getLoggerConfigs( rootPath: Seq[String] = Seq.empty - ): Endpoint[Unit, ApiDomain.Error.Internal, List[ApiDomain.LoggerConfig], None] = - Endpoint - .get(rootPathCodec(rootPath) / literal("logger")) + ): Endpoint[Unit, Unit, Error.Internal, List[ApiDomain.LoggerConfig], None] = + Endpoint(Method.GET / rootPathCodec(rootPath) / literal("logger")) .out[List[ApiDomain.LoggerConfig]] .outError[ApiDomain.Error.Internal](Status.InternalServerError) def getLoggerConfig( rootPath: Seq[String] = Seq.empty - ): Endpoint[String, ApiDomain.Error, ApiDomain.LoggerConfig, None] = - Endpoint - .get(rootPathCodec(rootPath) / literal("logger") / string("name")) + ): Endpoint[String, String, Error, ApiDomain.LoggerConfig, None] = + Endpoint(Method.GET / rootPathCodec(rootPath) / literal("logger") / string("name")) .out[ApiDomain.LoggerConfig] .outErrors[ApiDomain.Error]( HttpCodec.error[ApiDomain.Error.Internal](Status.InternalServerError), @@ -53,9 +52,8 @@ object ApiEndpoints { def setLoggerConfig( rootPath: Seq[String] = Seq.empty - ): Endpoint[(String, LogLevel), ApiDomain.Error.Internal, ApiDomain.LoggerConfig, None] = - Endpoint - .put(rootPathCodec(rootPath) / literal("logger") / string("name")) + ): Endpoint[String, (String, LogLevel), Error.Internal, ApiDomain.LoggerConfig, None] = + Endpoint(Method.PUT / rootPathCodec(rootPath) / literal("logger") / string("name")) .in[LogLevel] .out[ApiDomain.LoggerConfig] .outError[ApiDomain.Error.Internal](Status.InternalServerError) diff --git a/examples/core/src/main/scala/zio/logging/api/http/ApiHandlers.scala b/examples/core/src/main/scala/zio/logging/api/http/ApiHandlers.scala index e33524986..4784936bf 100644 --- a/examples/core/src/main/scala/zio/logging/api/http/ApiHandlers.scala +++ b/examples/core/src/main/scala/zio/logging/api/http/ApiHandlers.scala @@ -15,44 +15,49 @@ */ package zio.logging.api.http -import zio.ZIO -import zio.http.endpoint.EndpointMiddleware.None -import zio.http.endpoint.Routes +import zio.http.{ Handler, Route, Routes } import zio.logging.LoggerConfigurer import zio.logging.api.http.ApiDomain.Error +import zio.{ LogLevel, ZIO } object ApiHandlers { - def getLoggerConfigs(rootPath: Seq[String] = Seq.empty): Routes[LoggerConfigurer, Error.Internal, None] = + def getLoggerConfigs(rootPath: Seq[String] = Seq.empty): Route[LoggerConfigurer, Nothing] = ApiEndpoints .getLoggerConfigs(rootPath) - .implement(_ => - LoggerConfigurer - .getLoggerConfigs() - .map(_.map(ApiDomain.LoggerConfig.from)) - .mapError(_ => Error.Internal()) - ) + .implement { + Handler.fromFunctionZIO[Unit] { _ => + LoggerConfigurer + .getLoggerConfigs() + .map(_.map(ApiDomain.LoggerConfig.from)) + .mapError(_ => Error.Internal()) + } + } - def getLoggerConfig(rootPath: Seq[String] = Seq.empty): Routes[LoggerConfigurer, Error, None] = + def getLoggerConfig(rootPath: Seq[String] = Seq.empty): Route[LoggerConfigurer, Nothing] = ApiEndpoints .getLoggerConfig(rootPath) - .implement { name => - LoggerConfigurer.getLoggerConfig(name).mapError(_ => Error.Internal()).flatMap { - case Some(r) => ZIO.succeed(ApiDomain.LoggerConfig.from(r)) - case _ => ZIO.fail(Error.NotFound()) + .implement { + Handler.fromFunctionZIO[String] { name => + LoggerConfigurer.getLoggerConfig(name).mapError(_ => Error.Internal()).flatMap { + case Some(r) => ZIO.succeed(ApiDomain.LoggerConfig.from(r)) + case _ => ZIO.fail(Error.NotFound()) + } } } - def setLoggerConfigs(rootPath: Seq[String] = Seq.empty): Routes[LoggerConfigurer, Error.Internal, None] = + def setLoggerConfigs(rootPath: Seq[String] = Seq.empty): Route[LoggerConfigurer, Nothing] = ApiEndpoints .setLoggerConfig(rootPath) - .implement { case (name, logLevel) => - LoggerConfigurer - .setLoggerConfig(name, logLevel) - .map(ApiDomain.LoggerConfig.from) - .mapError(_ => Error.Internal()) + .implement { + Handler.fromFunctionZIO[(String, LogLevel)] { case (name, logLevel) => + LoggerConfigurer + .setLoggerConfig(name, logLevel) + .map(ApiDomain.LoggerConfig.from) + .mapError(_ => Error.Internal()) + } } - def routes(rootPath: Seq[String] = Seq.empty): Routes[LoggerConfigurer, ApiDomain.Error, None] = - getLoggerConfigs(rootPath) ++ getLoggerConfig(rootPath) ++ setLoggerConfigs(rootPath) + def routes(rootPath: Seq[String] = Seq.empty): Routes[LoggerConfigurer, Nothing] = + Routes(getLoggerConfigs(rootPath), getLoggerConfig(rootPath), setLoggerConfigs(rootPath)) } diff --git a/examples/core/src/main/scala/zio/logging/example/ConfigurableLoggerApp.scala b/examples/core/src/main/scala/zio/logging/example/ConfigurableLoggerApp.scala index d7fbd2cd7..708539b85 100644 --- a/examples/core/src/main/scala/zio/logging/example/ConfigurableLoggerApp.scala +++ b/examples/core/src/main/scala/zio/logging/example/ConfigurableLoggerApp.scala @@ -15,7 +15,6 @@ */ package zio.logging.example -import zio.http.HttpAppMiddleware.basicAuth import zio.http._ import zio.logging.api.http.ApiHandlers import zio.logging.{ @@ -86,8 +85,8 @@ object ConfigurableLoggerApp extends ZIOAppDefault { _ <- ZIO.logDebug("Done") @@ LogAnnotation.TraceId(traceId) } yield () - val httpApp: Http[LoggerConfigurer, Response, Request, Response] = - ApiHandlers.routes("example" :: Nil).toApp[LoggerConfigurer] @@ basicAuth("admin", "admin") + val httpApp: HttpApp[LoggerConfigurer] = + ApiHandlers.routes("example" :: Nil).toHttpApp @@ Middleware.basicAuth("admin", "admin") override def run: ZIO[Scope, Any, ExitCode] = (for { diff --git a/examples/core/src/test/scala/zio/logging/api/http/ApiEndpointsSpec.scala b/examples/core/src/test/scala/zio/logging/api/http/ApiEndpointsSpec.scala index b4656c5db..8aa5c949a 100644 --- a/examples/core/src/test/scala/zio/logging/api/http/ApiEndpointsSpec.scala +++ b/examples/core/src/test/scala/zio/logging/api/http/ApiEndpointsSpec.scala @@ -10,9 +10,13 @@ object ApiEndpointsSpec extends ZIOSpecDefault { def spec: Spec[Environment with TestEnvironment with Scope, Any] = suite("ApiEndpointsSpec")( test("rootPathCodec") { def testRootPathCodec(rootPath: Seq[String], expected: PathCodec[Unit]) = - assertTrue(ApiEndpoints.rootPathCodec(rootPath).encodeRequest(()).url == expected.encodeRequest(()).url) + assertTrue( + ApiEndpoints.rootPathCodec(rootPath).encode(()).getOrElse(zio.http.Path.empty) == expected + .encode(()) + .getOrElse(zio.http.Path.empty) + ) - testRootPathCodec(Nil, HttpCodec.empty) && testRootPathCodec( + testRootPathCodec(Nil, PathCodec.empty) && testRootPathCodec( "example" :: Nil, literal("example") ) && testRootPathCodec("v1" :: "example" :: Nil, literal("v1") / literal("example")) @@ -21,7 +25,9 @@ object ApiEndpointsSpec extends ZIOSpecDefault { def testPath(rootPath: Seq[String], expected: PathCodec[Unit]) = assertTrue( - ApiEndpoints.getLoggerConfigs(rootPath).input.encodeRequest(()).url == expected.encodeRequest(()).url + ApiEndpoints.getLoggerConfigs(rootPath).input.encodeRequest(()).path == expected + .encode(()) + .getOrElse(zio.http.Path.empty) ) testPath(Nil, literal("logger")) && testPath( @@ -33,9 +39,9 @@ object ApiEndpointsSpec extends ZIOSpecDefault { def testPath(rootPath: Seq[String], expected: PathCodec[Unit]) = assertTrue( - ApiEndpoints.getLoggerConfig(rootPath).input.encodeRequest("my-logger").url == expected - .encodeRequest(()) - .url + ApiEndpoints.getLoggerConfig(rootPath).input.encodeRequest("my-logger").path == expected + .encode(()) + .getOrElse(zio.http.Path.empty) ) testPath(Nil, literal("logger") / literal("my-logger")) && testPath( @@ -51,9 +57,9 @@ object ApiEndpointsSpec extends ZIOSpecDefault { .setLoggerConfig(rootPath) .input .encodeRequest(("my-logger", LogLevel.Info)) - .url == expected - .encodeRequest(()) - .url + .path == expected + .encode(()) + .getOrElse(zio.http.Path.empty) ) testPath(Nil, literal("logger") / literal("my-logger")) && testPath( diff --git a/examples/core/src/test/scala/zio/logging/api/http/ApiHandlersSpec.scala b/examples/core/src/test/scala/zio/logging/api/http/ApiHandlersSpec.scala index 6a1d861be..275ed35bf 100644 --- a/examples/core/src/test/scala/zio/logging/api/http/ApiHandlersSpec.scala +++ b/examples/core/src/test/scala/zio/logging/api/http/ApiHandlersSpec.scala @@ -32,7 +32,7 @@ object ApiHandlersSpec extends ZIOSpecDefault { for { request <- ZIO.attempt(Request.get(URL.decode("/example/logger").toOption.get)) - response <- routes.toApp.runZIO(request) + response <- routes.toHttpApp.runZIO(request) content <- HttpCodec.content[List[ApiDomain.LoggerConfig]].decodeResponse(response) } yield assertTrue(response.status.isSuccess) && assertTrue( content == List(ApiDomain.LoggerConfig("root", LogLevel.Info)) @@ -42,7 +42,7 @@ object ApiHandlersSpec extends ZIOSpecDefault { val routes = ApiHandlers.routes("example" :: Nil) for { request <- ZIO.attempt(Request.get(URL.decode("/example/logger/example.Service").toOption.get)) - response <- routes.toApp.runZIO(request) + response <- routes.toHttpApp.runZIO(request) content <- HttpCodec.content[ApiDomain.LoggerConfig].decodeResponse(response) } yield assertTrue(response.status.isSuccess) && assertTrue( content == ApiDomain.LoggerConfig("example.Service", LogLevel.Info) @@ -55,11 +55,11 @@ object ApiHandlersSpec extends ZIOSpecDefault { request <- ZIO.attempt( Request .put( - HttpCodec.content[LogLevel].encodeRequest(LogLevel.Warning).body, - URL.decode("/example/logger/example.Service").toOption.get + URL.decode("/example/logger/example.Service").toOption.get, + HttpCodec.content[LogLevel].encodeRequest(LogLevel.Warning).body ) ) - response <- routes.toApp.runZIO(request) + response <- routes.toHttpApp.runZIO(request) content <- HttpCodec.content[ApiDomain.LoggerConfig].decodeResponse(response) } yield assertTrue(response.status.isSuccess) && assertTrue( content == ApiDomain.LoggerConfig("example.Service", LogLevel.Warning) diff --git a/project/Versions.scala b/project/Versions.scala index f2910aed3..c26f4ddf3 100644 --- a/project/Versions.scala +++ b/project/Versions.scala @@ -10,6 +10,6 @@ object Versions { val zioConfig = "4.0.0" val zioParser = "0.1.9" val zioPrelude = "1.0.0-RC19" - val zioHttp = "3.0.0-RC2" + val zioHttp = "3.0.0-RC4" val log4jVersion = "2.19.0" } From cef3251dabb4aecdf1f1e472ae89a7793ca64bd5 Mon Sep 17 00:00:00 2001 From: Peter Kotula Date: Mon, 25 Dec 2023 12:23:36 +0100 Subject: [PATCH 53/56] ConfigurableLoggerApp --- .../zio/logging/example/ConfigurableLoggerApp.scala | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/examples/core/src/main/scala/zio/logging/example/ConfigurableLoggerApp.scala b/examples/core/src/main/scala/zio/logging/example/ConfigurableLoggerApp.scala index 708539b85..200fe572b 100644 --- a/examples/core/src/main/scala/zio/logging/example/ConfigurableLoggerApp.scala +++ b/examples/core/src/main/scala/zio/logging/example/ConfigurableLoggerApp.scala @@ -23,7 +23,7 @@ import zio.logging.{ LogAnnotation, LoggerConfigurer, loggerConfigPath, - makeSystemOutLogger + makeConsoleLogger } import zio.{ ExitCode, Runtime, Scope, ZIO, ZIOAppDefault, _ } @@ -44,11 +44,9 @@ object ConfigurableLoggerApp extends ZIOAppDefault { def configurableLogger(configPath: NonEmptyChunk[String] = loggerConfigPath) = ConsoleLoggerConfig .load(configPath) - .flatMap { consoleLoggerConfig => - makeSystemOutLogger( - consoleLoggerConfig.format.toLogger - ).map { logger => - ConfigurableLogger.make(logger, consoleLoggerConfig.filter) + .flatMap { config => + makeConsoleLogger(config).map { logger => + ConfigurableLogger.make(logger, config.filter) } } .install From 21f975ec0d812fd4d3ac97e6b56ac6b166ee8228 Mon Sep 17 00:00:00 2001 From: Peter Kotula Date: Tue, 26 Dec 2023 12:59:55 +0100 Subject: [PATCH 54/56] examples and docs updates --- docs/reconfigurable-logger.md | 183 +++++++++++++++--- docs/slf4j1-bridge.md | 2 +- docs/slf4j2-bridge.md | 2 +- examples/core/src/main/resources/logger.conf | 1 + .../zio/logging/api/http/ApiEndpoints.scala | 13 +- .../example/ConfigurableLoggerApp.scala | 27 +-- .../example/LoggerReconfigureApp.scala | 3 +- project/Versions.scala | 2 +- 8 files changed, 173 insertions(+), 60 deletions(-) diff --git a/docs/reconfigurable-logger.md b/docs/reconfigurable-logger.md index 2aa797a0c..d78da4cf7 100644 --- a/docs/reconfigurable-logger.md +++ b/docs/reconfigurable-logger.md @@ -5,7 +5,7 @@ title: "Reconfigurable Logger" `ReconfigurableLogger` is adding support for updating logger configuration in application runtime. -logger layer with configuration from config provider (example with [Console Logger)](console-logger.md)): +logger layer with configuration from `ConfigProvider` (example with [Console Logger)](console-logger.md)): ```scala @@ -25,16 +25,18 @@ val logger = Runtime.removeDefaultLoggers >>> Runtime.setConfigProvider(configPr `ReconfigurableLogger`, based on given `Schedule` and load configuration function, will recreate logger if configuration changed. +**NOTE:** consider if you need this feature in your application, as there may be some performance impacts (see [benchmarks](https://github.com/zio/zio-logging/blob/master/benchmarks/src/main/scala/zio/logging/FilterBenchmarks.scala)). + ## Examples You can find the source code [here](https://github.com/zio/zio-logging/tree/master/examples) -### Colorful Console Logger With Reconfiguration In Runtime +### Console Logger With Re-configuration From Configuration File In Runtime [//]: # (TODO: make snippet type-checked using mdoc) -By default, root level configuration is `INFO`, when root level configuration is changed to `DEBUG`, other logger messages should be in output. +Example of application where logger configuration is updated at runtime when logger configuration file is changed. Configuration: @@ -42,7 +44,10 @@ Configuration: logger { format = "%highlight{%timestamp{yyyy-MM-dd'T'HH:mm:ssZ} %fixed{7}{%level} [%fiberId] %name:%line %message %kv{trace_id} %kv{user_id} %cause}" filter { - rootLevel = "DEBUG" + rootLevel = "INFO" + mappings { + "zio.logging.example" = "DEBUG" + } } } ``` @@ -77,8 +82,7 @@ object LoggerReconfigureApp extends ZIOAppDefault { for { config <- ZIO.succeed(ConfigFactory.load("logger.conf")) _ <- Console.printLine(config.getConfig("logger")).orDie - loggerConfig <- - TypesafeConfigProvider.fromTypesafeConfig(config).nested("logger").load(ConsoleLoggerConfig.config) + loggerConfig <- ConsoleLoggerConfig.load().withConfigProvider(TypesafeConfigProvider.fromTypesafeConfig(config)) } yield loggerConfig ) @@ -107,31 +111,148 @@ object LoggerReconfigureApp extends ZIOAppDefault { } ``` -Expected console output: +When configuration for `logger/filter/mappings/zio.logging.example` change from `DEBUG` to `WARN`: + +``` +Config(SimpleConfigObject({"filter":{"mappings":{"zio.logging.example":"DEBUG"},"rootLevel":"INFO"},"format":"%highlight{%timestamp{yyyy-MM-dd'T'HH:mm:ssZ} %fixed{7}{%level} [%fiberId] %name:%line %message %kv{trace_id} %kv{user_id} %cause}"})) +2023-12-26T10:10:26+0100 DEBUG [zio-fiber-5] zio.logging.example.LoggerReconfigureApp:51 Start trace_id=87ead38c-8b42-43ea-9905-039d0263026d +2023-12-26T10:10:26+0100 DEBUG [zio-fiber-36] zio.logging.example.LoggerReconfigureApp:55 Starting operation trace_id=87ead38c-8b42-43ea-9905-039d0263026d user_id=dfa05247-ec27-46f7-a4e0-bb86f2d501e9 +2023-12-26T10:10:26+0100 DEBUG [zio-fiber-37] zio.logging.example.LoggerReconfigureApp:55 Starting operation trace_id=87ead38c-8b42-43ea-9905-039d0263026d user_id=19693c77-0896-4fae-a830-67d5fe370b05 +2023-12-26T10:10:26+0100 ERROR [zio-fiber-36] zio.logging.example.LoggerReconfigureApp:57 Error operation trace_id=87ead38c-8b42-43ea-9905-039d0263026d user_id=dfa05247-ec27-46f7-a4e0-bb86f2d501e9 +2023-12-26T10:10:26+0100 ERROR [zio-fiber-37] zio.logging.example.LoggerReconfigureApp:57 Error operation trace_id=87ead38c-8b42-43ea-9905-039d0263026d user_id=19693c77-0896-4fae-a830-67d5fe370b05 +2023-12-26T10:10:26+0100 DEBUG [zio-fiber-36] zio.logging.example.LoggerReconfigureApp:58 Stopping operation trace_id=87ead38c-8b42-43ea-9905-039d0263026d user_id=dfa05247-ec27-46f7-a4e0-bb86f2d501e9 +2023-12-26T10:10:26+0100 DEBUG [zio-fiber-37] zio.logging.example.LoggerReconfigureApp:58 Stopping operation trace_id=87ead38c-8b42-43ea-9905-039d0263026d user_id=19693c77-0896-4fae-a830-67d5fe370b05 +2023-12-26T10:10:26+0100 DEBUG [zio-fiber-5] zio.logging.example.LoggerReconfigureApp:61 Done trace_id=87ead38c-8b42-43ea-9905-039d0263026d +2023-12-26T10:10:26+0100 DEBUG [zio-fiber-5] zio.logging.example.LoggerReconfigureApp:51 Start trace_id=c6d4c770-8db1-4ea8-91eb-548c6a99a90c +2023-12-26T10:10:26+0100 DEBUG [zio-fiber-39] zio.logging.example.LoggerReconfigureApp:55 Starting operation trace_id=c6d4c770-8db1-4ea8-91eb-548c6a99a90c user_id=a62a153f-6a91-491e-8c97-bab94186f0a2 +2023-12-26T10:10:26+0100 DEBUG [zio-fiber-38] zio.logging.example.LoggerReconfigureApp:55 Starting operation trace_id=c6d4c770-8db1-4ea8-91eb-548c6a99a90c user_id=8eeb6442-80a9-40e5-b97d-a12876702a65 +2023-12-26T10:10:26+0100 ERROR [zio-fiber-39] zio.logging.example.LoggerReconfigureApp:57 Error operation trace_id=c6d4c770-8db1-4ea8-91eb-548c6a99a90c user_id=a62a153f-6a91-491e-8c97-bab94186f0a2 +2023-12-26T10:10:26+0100 ERROR [zio-fiber-38] zio.logging.example.LoggerReconfigureApp:57 Error operation trace_id=c6d4c770-8db1-4ea8-91eb-548c6a99a90c user_id=8eeb6442-80a9-40e5-b97d-a12876702a65 +2023-12-26T10:10:26+0100 DEBUG [zio-fiber-39] zio.logging.example.LoggerReconfigureApp:58 Stopping operation trace_id=c6d4c770-8db1-4ea8-91eb-548c6a99a90c user_id=a62a153f-6a91-491e-8c97-bab94186f0a2 +2023-12-26T10:10:26+0100 DEBUG [zio-fiber-38] zio.logging.example.LoggerReconfigureApp:58 Stopping operation trace_id=c6d4c770-8db1-4ea8-91eb-548c6a99a90c user_id=8eeb6442-80a9-40e5-b97d-a12876702a65 +2023-12-26T10:10:26+0100 DEBUG [zio-fiber-5] zio.logging.example.LoggerReconfigureApp:61 Done trace_id=c6d4c770-8db1-4ea8-91eb-548c6a99a90c +Config(SimpleConfigObject({"filter":{"mappings":{"zio.logging.example":"WARN"},"rootLevel":"INFO"},"format":"%highlight{%timestamp{yyyy-MM-dd'T'HH:mm:ssZ} %fixed{7}{%level} [%fiberId] %name:%line %message %kv{trace_id} %kv{user_id} %cause}"})) +2023-12-26T10:10:27+0100 ERROR [zio-fiber-40] zio.logging.example.LoggerReconfigureApp:57 Error operation trace_id=e2d8bbb4-5ad0-4952-b035-03da7689ab56 user_id=0f6452da-3f7e-40ff-b8b4-6b4c731903fb +2023-12-26T10:10:27+0100 ERROR [zio-fiber-41] zio.logging.example.LoggerReconfigureApp:57 Error operation trace_id=e2d8bbb4-5ad0-4952-b035-03da7689ab56 user_id=c4a86b38-90d7-4bb6-9f49-73bc5701e1ef +``` + +### Console Logger With Configuration By Http APIs + +[//]: # (TODO: make snippet type-checked using mdoc) + +Example of application where logger configuration can be changed by Http APIs. + +Logger configurations APIs: +* get logger configurations + ```bash + curl -u "admin:admin" 'http://localhost:8080/example/logger' + ``` +* get `root` logger configuration + ```bash + curl -u "admin:admin" 'http://localhost:8080/example/logger/root' + ``` +* set `root` logger configuration + ```bash + curl -u "admin:admin" --location --request PUT 'http://localhost:8080/example/logger/root' --header 'Content-Type: application/json' --data-raw '"WARN"' + ``` +* get `zio.logging.example` logger configuration + ```bash + curl -u "admin:admin" --location --request PUT 'http://localhost:8080/example/logger/zio.logging.example' --header 'Content-Type: application/json' --data-raw '"WARN"' + ``` + +Configuration: + +``` +logger { + format = "%highlight{%timestamp{yyyy-MM-dd'T'HH:mm:ssZ} %fixed{7}{%level} [%fiberId] %name:%line %message %kv{trace_id} %kv{user_id} %cause}" + filter { + rootLevel = "INFO" + mappings { + "zio.logging.example" = "DEBUG" + } + } +} +``` + +Application: + +```scala +package zio.logging.example + +import com.typesafe.config.ConfigFactory +import zio.config.typesafe.TypesafeConfigProvider +import zio.http._ +import zio.logging.api.http.ApiHandlers +import zio.logging.{ ConfigurableLogger, ConsoleLoggerConfig, LogAnnotation, LoggerConfigurer, makeConsoleLogger } +import zio.{ ExitCode, Runtime, Scope, ZIO, ZIOAppDefault, _ } + +import java.util.UUID + +object ConfigurableLoggerApp extends ZIOAppDefault { + + def configurableLogger() = + ConsoleLoggerConfig + .load() + .flatMap { config => + makeConsoleLogger(config).map { logger => + ConfigurableLogger.make(logger, config.filter) + } + } + .install + + val configProvider: ConfigProvider = TypesafeConfigProvider.fromTypesafeConfig(ConfigFactory.load("logger.conf")) + + override val bootstrap: ZLayer[Any, Config.Error, Unit] = + Runtime.removeDefaultLoggers >>> Runtime.setConfigProvider(configProvider) >>> configurableLogger() + + def exec(): ZIO[Any, Nothing, Unit] = + for { + ok <- Random.nextBoolean + traceId <- ZIO.succeed(UUID.randomUUID()) + _ <- ZIO.logDebug("Start") @@ LogAnnotation.TraceId(traceId) + userIds <- ZIO.succeed(List.fill(2)(UUID.randomUUID().toString)) + _ <- ZIO.foreachPar(userIds) { userId => + { + ZIO.logDebug("Starting operation") *> + ZIO.logInfo("OK operation").when(ok) *> + ZIO.logError("Error operation").when(!ok) *> + ZIO.logDebug("Stopping operation") + } @@ LogAnnotation.UserId(userId) + } @@ LogAnnotation.TraceId(traceId) + _ <- ZIO.logDebug("Done") @@ LogAnnotation.TraceId(traceId) + } yield () + + val httpApp: HttpApp[LoggerConfigurer] = + ApiHandlers.routes("example" :: Nil).toHttpApp @@ Middleware.basicAuth("admin", "admin") + + override def run: ZIO[Scope, Any, ExitCode] = + (for { + _ <- Server.serve(httpApp).fork + _ <- exec().repeat(Schedule.fixed(500.millis)) + } yield ExitCode.success).provide(LoggerConfigurer.layer ++ Server.default) + +} +``` + +**NOTE:** `ConfigurableLogger` and `ApiHandlers` are currently implemented in examples, +it will be considered in the future, if they will be moved to official `zio-logging` implementation +(once there will be official stable `zio-http` release). +If you like to use them in your app, you can copy them. + +When configuration for `logger/filter/mappings/zio.logging.example` change from `DEBUG` to `WARN`: + +```bash +curl -u "admin:admin" --location --request PUT 'http://localhost:8080/example/logger/zio.logging.example' --header 'Content-Type: application/json' --data-raw '"WARN"' +``` ``` -Config(SimpleConfigObject({"filter":{"mappings":{},"rootLevel":"INFO"},"format":"%highlight{%timestamp{yyyy-MM-dd'T'HH:mm:ssZ} %fixed{7}{%level} [%fiberId] %name:%line %message %kv{trace_id} %kv{user_id} %cause}"})) -2023-12-25T11:11:27+0100 ERROR [zio-fiber-106] zio.logging.example.LoggerReconfigureApp:58 Error operation trace_id=18788cfd-5efe-488b-8549-c69f374110ea user_id=b31bb7a0-f8d1-4d7d-8215-1c980ec0e986 -2023-12-25T11:11:27+0100 ERROR [zio-fiber-107] zio.logging.example.LoggerReconfigureApp:58 Error operation trace_id=18788cfd-5efe-488b-8549-c69f374110ea user_id=2c390abc-17a7-413d-9d4a-668215727fbd -Config(SimpleConfigObject({"filter":{"mappings":{},"rootLevel":"INFO"},"format":"%highlight{%timestamp{yyyy-MM-dd'T'HH:mm:ssZ} %fixed{7}{%level} [%fiberId] %name:%line %message %kv{trace_id} %kv{user_id} %cause}"})) -2023-12-25T11:11:28+0100 ERROR [zio-fiber-108] zio.logging.example.LoggerReconfigureApp:58 Error operation trace_id=865905f4-9548-44b6-bd60-9b2d30da95f4 user_id=6abecac0-b867-4f50-a699-c344d2548b79 -2023-12-25T11:11:28+0100 ERROR [zio-fiber-109] zio.logging.example.LoggerReconfigureApp:58 Error operation trace_id=865905f4-9548-44b6-bd60-9b2d30da95f4 user_id=b7f73c21-2dc6-4707-8bdd-96cfd7500e26 -Config(SimpleConfigObject({"filter":{"mappings":{},"rootLevel":"DEBUG"},"format":"%highlight{%timestamp{yyyy-MM-dd'T'HH:mm:ssZ} %fixed{7}{%level} [%fiberId] %name:%line %message %kv{trace_id} %kv{user_id} %cause}"})) -2023-12-25T11:11:28+0100 DEBUG [zio-fiber-5] zio.logging.example.LoggerReconfigureApp:52 Start trace_id=44ac195b-9e17-42d8-b8ff-264a52275855 -2023-12-25T11:11:28+0100 DEBUG [zio-fiber-110] zio.logging.example.LoggerReconfigureApp:56 Starting operation trace_id=44ac195b-9e17-42d8-b8ff-264a52275855 user_id=a976cb5b-d035-488f-9664-3761a07fb2d9 -2023-12-25T11:11:28+0100 DEBUG [zio-fiber-111] zio.logging.example.LoggerReconfigureApp:56 Starting operation trace_id=44ac195b-9e17-42d8-b8ff-264a52275855 user_id=ed23b03a-00e5-4495-8a0d-092eee0a7479 -2023-12-25T11:11:28+0100 INFO [zio-fiber-110] zio.logging.example.LoggerReconfigureApp:57 OK operation trace_id=44ac195b-9e17-42d8-b8ff-264a52275855 user_id=a976cb5b-d035-488f-9664-3761a07fb2d9 -2023-12-25T11:11:28+0100 INFO [zio-fiber-111] zio.logging.example.LoggerReconfigureApp:57 OK operation trace_id=44ac195b-9e17-42d8-b8ff-264a52275855 user_id=ed23b03a-00e5-4495-8a0d-092eee0a7479 -2023-12-25T11:11:28+0100 DEBUG [zio-fiber-111] zio.logging.example.LoggerReconfigureApp:59 Stopping operation trace_id=44ac195b-9e17-42d8-b8ff-264a52275855 user_id=ed23b03a-00e5-4495-8a0d-092eee0a7479 -2023-12-25T11:11:28+0100 DEBUG [zio-fiber-110] zio.logging.example.LoggerReconfigureApp:59 Stopping operation trace_id=44ac195b-9e17-42d8-b8ff-264a52275855 user_id=a976cb5b-d035-488f-9664-3761a07fb2d9 -2023-12-25T11:11:28+0100 DEBUG [zio-fiber-5] zio.logging.example.LoggerReconfigureApp:62 Done trace_id=44ac195b-9e17-42d8-b8ff-264a52275855 -Config(SimpleConfigObject({"filter":{"mappings":{},"rootLevel":"DEBUG"},"format":"%highlight{%timestamp{yyyy-MM-dd'T'HH:mm:ssZ} %fixed{7}{%level} [%fiberId] %name:%line %message %kv{trace_id} %kv{user_id} %cause}"})) -2023-12-25T11:11:29+0100 DEBUG [zio-fiber-5] zio.logging.example.LoggerReconfigureApp:52 Start trace_id=f613ff34-8d8a-49e8-b58c-5fa954f3bc79 -2023-12-25T11:11:29+0100 DEBUG [zio-fiber-113] zio.logging.example.LoggerReconfigureApp:56 Starting operation trace_id=f613ff34-8d8a-49e8-b58c-5fa954f3bc79 user_id=0c513b80-3fc6-4aa9-a805-11e658805296 -2023-12-25T11:11:29+0100 DEBUG [zio-fiber-112] zio.logging.example.LoggerReconfigureApp:56 Starting operation trace_id=f613ff34-8d8a-49e8-b58c-5fa954f3bc79 user_id=aa296f97-6503-4633-ac78-16094f00ceca -2023-12-25T11:11:29+0100 ERROR [zio-fiber-113] zio.logging.example.LoggerReconfigureApp:58 Error operation trace_id=f613ff34-8d8a-49e8-b58c-5fa954f3bc79 user_id=0c513b80-3fc6-4aa9-a805-11e658805296 -2023-12-25T11:11:29+0100 ERROR [zio-fiber-112] zio.logging.example.LoggerReconfigureApp:58 Error operation trace_id=f613ff34-8d8a-49e8-b58c-5fa954f3bc79 user_id=aa296f97-6503-4633-ac78-16094f00ceca -2023-12-25T11:11:29+0100 DEBUG [zio-fiber-113] zio.logging.example.LoggerReconfigureApp:59 Stopping operation trace_id=f613ff34-8d8a-49e8-b58c-5fa954f3bc79 user_id=0c513b80-3fc6-4aa9-a805-11e658805296 -2023-12-25T11:11:29+0100 DEBUG [zio-fiber-112] zio.logging.example.LoggerReconfigureApp:59 Stopping operation trace_id=f613ff34-8d8a-49e8-b58c-5fa954f3bc79 user_id=aa296f97-6503-4633-ac78-16094f00ceca -2023-12-25T11:11:29+0100 DEBUG [zio-fiber-5] zio.logging.example.LoggerReconfigureApp:62 Done trace_id=f613ff34-8d8a-49e8-b58c-5fa954f3bc79 +2023-12-26T10:49:27+0100 DEBUG [zio-fiber-73] zio.logging.example.ConfigurableLoggerApp:62 Starting operation trace_id=dcf30228-dc00-4c1f-ab94-20c9f8116045 user_id=5d35778f-78ff-48a8-aa6a-73114ec719b5 +2023-12-26T10:49:27+0100 DEBUG [zio-fiber-72] zio.logging.example.ConfigurableLoggerApp:62 Starting operation trace_id=dcf30228-dc00-4c1f-ab94-20c9f8116045 user_id=9e17bd97-aa18-4b09-a423-9de28241a20b +2023-12-26T10:49:27+0100 INFO [zio-fiber-73] zio.logging.example.ConfigurableLoggerApp:63 OK operation trace_id=dcf30228-dc00-4c1f-ab94-20c9f8116045 user_id=5d35778f-78ff-48a8-aa6a-73114ec719b5 +2023-12-26T10:49:27+0100 INFO [zio-fiber-72] zio.logging.example.ConfigurableLoggerApp:63 OK operation trace_id=dcf30228-dc00-4c1f-ab94-20c9f8116045 user_id=9e17bd97-aa18-4b09-a423-9de28241a20b +2023-12-26T10:49:27+0100 DEBUG [zio-fiber-73] zio.logging.example.ConfigurableLoggerApp:65 Stopping operation trace_id=dcf30228-dc00-4c1f-ab94-20c9f8116045 user_id=5d35778f-78ff-48a8-aa6a-73114ec719b5 +2023-12-26T10:49:27+0100 DEBUG [zio-fiber-72] zio.logging.example.ConfigurableLoggerApp:65 Stopping operation trace_id=dcf30228-dc00-4c1f-ab94-20c9f8116045 user_id=9e17bd97-aa18-4b09-a423-9de28241a20b +2023-12-26T10:49:27+0100 DEBUG [zio-fiber-4] zio.logging.example.ConfigurableLoggerApp:68 Done trace_id=dcf30228-dc00-4c1f-ab94-20c9f8116045 +2023-12-26T10:49:28+0100 ERROR [zio-fiber-77] zio.logging.example.ConfigurableLoggerApp:64 Error operation trace_id=7da8765e-2e27-42c6-8834-16d15d21c72c user_id=4395d188-5971-4839-a721-278d07a2881b +2023-12-26T10:49:28+0100 ERROR [zio-fiber-78] zio.logging.example.ConfigurableLoggerApp:64 Error operation trace_id=7da8765e-2e27-42c6-8834-16d15d21c72c user_id=27af6873-2f7b-4b9a-ad2b-6bd12479cace ``` diff --git a/docs/slf4j1-bridge.md b/docs/slf4j1-bridge.md index f1c9375e4..df921d357 100644 --- a/docs/slf4j1-bridge.md +++ b/docs/slf4j1-bridge.md @@ -42,7 +42,7 @@ val logger = Runtime.removeDefaultLoggers >>> consoleJsonLogger() >+> Slf4jBridg ```
-**NOTE** You should either use `zio-logging-slf4j` to send all ZIO logs to an SLF4j provider (such as logback, log4j etc) OR `zio-logging-slf4j-bridge` to send all SLF4j logs to +**NOTE:** You should either use `zio-logging-slf4j` to send all ZIO logs to an SLF4j provider (such as logback, log4j etc) OR `zio-logging-slf4j-bridge` to send all SLF4j logs to ZIO logging. Enabling both causes circular logging and makes no sense. diff --git a/docs/slf4j2-bridge.md b/docs/slf4j2-bridge.md index ec3e097b6..7ddddf5ce 100644 --- a/docs/slf4j2-bridge.md +++ b/docs/slf4j2-bridge.md @@ -54,7 +54,7 @@ val logger = Runtime.removeDefaultLoggers >>> consoleJsonLogger() >+> Slf4jBridg
-**NOTE** You should either use `zio-logging-slf4j` to send all ZIO logs to an SLF4j provider (such as logback, log4j etc) OR `zio-logging-slf4j-bridge` to send all SLF4j logs to +**NOTE:** You should either use `zio-logging-slf4j` to send all ZIO logs to an SLF4j provider (such as logback, log4j etc) OR `zio-logging-slf4j-bridge` to send all SLF4j logs to ZIO logging. Enabling both causes circular logging and makes no sense. diff --git a/examples/core/src/main/resources/logger.conf b/examples/core/src/main/resources/logger.conf index 5c65f06c8..0db7b21fe 100644 --- a/examples/core/src/main/resources/logger.conf +++ b/examples/core/src/main/resources/logger.conf @@ -3,6 +3,7 @@ logger { filter { rootLevel = "INFO" mappings { + "zio.logging.example" = "DEBUG" } } } \ No newline at end of file diff --git a/examples/core/src/main/scala/zio/logging/api/http/ApiEndpoints.scala b/examples/core/src/main/scala/zio/logging/api/http/ApiEndpoints.scala index feb6f12ce..cc22f605f 100644 --- a/examples/core/src/main/scala/zio/logging/api/http/ApiEndpoints.scala +++ b/examples/core/src/main/scala/zio/logging/api/http/ApiEndpoints.scala @@ -18,9 +18,10 @@ package zio.logging.api.http import zio._ import zio.http._ import zio.http.codec.PathCodec.{ literal, string } -import zio.http.codec.{ Doc, HttpCodec, PathCodec } +import zio.http.codec.{ HttpCodec, PathCodec } import zio.http.endpoint.EndpointMiddleware.None import zio.http.endpoint._ +import zio.http.endpoint.openapi.{ OpenAPI, OpenAPIGen } import zio.logging.api.http.ApiDomain.Error object ApiEndpoints { @@ -58,6 +59,12 @@ object ApiEndpoints { .out[ApiDomain.LoggerConfig] .outError[ApiDomain.Error.Internal](Status.InternalServerError) - def doc(rootPath: Seq[String] = Seq.empty): Doc = - getLoggerConfigs(rootPath).doc + getLoggerConfig(rootPath).doc + setLoggerConfig(rootPath).doc + def openAPI(rootPath: Seq[String] = Seq.empty): OpenAPI = + OpenAPIGen.fromEndpoints( + title = "Logger Configurations API", + version = "1.0", + getLoggerConfigs(rootPath), + getLoggerConfig(rootPath), + setLoggerConfig(rootPath) + ) } diff --git a/examples/core/src/main/scala/zio/logging/example/ConfigurableLoggerApp.scala b/examples/core/src/main/scala/zio/logging/example/ConfigurableLoggerApp.scala index 200fe572b..0fb86fb17 100644 --- a/examples/core/src/main/scala/zio/logging/example/ConfigurableLoggerApp.scala +++ b/examples/core/src/main/scala/zio/logging/example/ConfigurableLoggerApp.scala @@ -15,16 +15,11 @@ */ package zio.logging.example +import com.typesafe.config.ConfigFactory +import zio.config.typesafe.TypesafeConfigProvider import zio.http._ import zio.logging.api.http.ApiHandlers -import zio.logging.{ - ConfigurableLogger, - ConsoleLoggerConfig, - LogAnnotation, - LoggerConfigurer, - loggerConfigPath, - makeConsoleLogger -} +import zio.logging.{ ConfigurableLogger, ConsoleLoggerConfig, LogAnnotation, LoggerConfigurer, makeConsoleLogger } import zio.{ ExitCode, Runtime, Scope, ZIO, ZIOAppDefault, _ } import java.util.UUID @@ -41,9 +36,9 @@ import java.util.UUID */ object ConfigurableLoggerApp extends ZIOAppDefault { - def configurableLogger(configPath: NonEmptyChunk[String] = loggerConfigPath) = + def configurableLogger() = ConsoleLoggerConfig - .load(configPath) + .load() .flatMap { config => makeConsoleLogger(config).map { logger => ConfigurableLogger.make(logger, config.filter) @@ -51,17 +46,7 @@ object ConfigurableLoggerApp extends ZIOAppDefault { } .install - val logFormat = - "%highlight{%timestamp{yyyy-MM-dd'T'HH:mm:ssZ} %fixed{7}{%level} [%fiberId] %name:%line %message %kv{trace_id} %kv{user_id} %cause}" - - val configProvider: ConfigProvider = ConfigProvider.fromMap( - Map( - "logger/format" -> logFormat, - "logger/filter/rootLevel" -> LogLevel.Info.label, - "logger/filter/mappings/zio.logging.example" -> LogLevel.Debug.label - ), - "/" - ) + val configProvider: ConfigProvider = TypesafeConfigProvider.fromTypesafeConfig(ConfigFactory.load("logger.conf")) override val bootstrap: ZLayer[Any, Config.Error, Unit] = Runtime.removeDefaultLoggers >>> Runtime.setConfigProvider(configProvider) >>> configurableLogger() diff --git a/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala b/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala index 16476c92b..2652ae84a 100644 --- a/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala +++ b/examples/core/src/main/scala/zio/logging/example/LoggerReconfigureApp.scala @@ -40,8 +40,7 @@ object LoggerReconfigureApp extends ZIOAppDefault { for { config <- ZIO.succeed(ConfigFactory.load("logger.conf")) _ <- Console.printLine(config.getConfig("logger")).orDie - loggerConfig <- - TypesafeConfigProvider.fromTypesafeConfig(config).nested("logger").load(ConsoleLoggerConfig.config) + loggerConfig <- ConsoleLoggerConfig.load().withConfigProvider(TypesafeConfigProvider.fromTypesafeConfig(config)) } yield loggerConfig ) diff --git a/project/Versions.scala b/project/Versions.scala index c26f4ddf3..a10afb07d 100644 --- a/project/Versions.scala +++ b/project/Versions.scala @@ -9,7 +9,7 @@ object Versions { val zioMetricsConnectorsVersion = "2.3.1" val zioConfig = "4.0.0" val zioParser = "0.1.9" - val zioPrelude = "1.0.0-RC19" + val zioPrelude = "1.0.0-RC21" val zioHttp = "3.0.0-RC4" val log4jVersion = "2.19.0" } From e3c09c637d1acb513c5d09aae2f5ddad156694bb Mon Sep 17 00:00:00 2001 From: Peter Kotula Date: Tue, 26 Dec 2023 18:11:08 +0100 Subject: [PATCH 55/56] benchmarks --- .../scala/zio/logging/FilterBenchmarks.scala | 92 ++++++++----------- 1 file changed, 36 insertions(+), 56 deletions(-) diff --git a/benchmarks/src/main/scala/zio/logging/FilterBenchmarks.scala b/benchmarks/src/main/scala/zio/logging/FilterBenchmarks.scala index f31f9b19a..cd4edbcac 100644 --- a/benchmarks/src/main/scala/zio/logging/FilterBenchmarks.scala +++ b/benchmarks/src/main/scala/zio/logging/FilterBenchmarks.scala @@ -1,7 +1,7 @@ package zio.logging import org.openjdk.jmh.annotations._ -import zio.{ ConfigProvider, LogLevel, Runtime, Unsafe, ZIO, ZLayer } +import zio.{ LogLevel, Runtime, Unsafe, ZIO, ZLayer } import java.util.concurrent.TimeUnit import scala.util.Random @@ -42,65 +42,40 @@ class FilterBenchmarks { .install } - val filterByLogLevelAndNameLogging: ZLayer[Any, Nothing, Unit] = { - val filter = LogFilter - .LogLevelByNameConfig( - LogLevel.Debug, - "a.b.c" -> LogLevel.Info, - "a.b.d" -> LogLevel.Warning, - "e" -> LogLevel.Info, - "f.g" -> LogLevel.Error, - "f" -> LogLevel.Info - ) - .toFilter + val filterConfig: LogFilter.LogLevelByNameConfig = LogFilter.LogLevelByNameConfig( + LogLevel.Debug, + "a.b.c" -> LogLevel.Info, + "a.b.d" -> LogLevel.Warning, + "e" -> LogLevel.Info, + "f.g" -> LogLevel.Error, + "f" -> LogLevel.Info + ) + val filterByLogLevelAndNameLogging: ZLayer[Any, Nothing, Unit] = Runtime.removeDefaultLoggers >>> makeSystemOutLogger(LogFormat.default.toLogger) - .filter(filter) + .filter(filterConfig.toFilter) .install - } - - val cachedFilterByLogLevelAndNameLogging: ZLayer[Any, Nothing, Unit] = { - val filter = LogFilter - .LogLevelByNameConfig( - LogLevel.Debug, - "a.b.c" -> LogLevel.Info, - "a.b.d" -> LogLevel.Warning, - "e" -> LogLevel.Info, - "f.g" -> LogLevel.Error, - "f" -> LogLevel.Info - ) - .toFilter - .cached + val cachedFilterByLogLevelAndNameLogging: ZLayer[Any, Nothing, Unit] = Runtime.removeDefaultLoggers >>> makeSystemOutLogger(LogFormat.default.toLogger) - .filter(filter) + .filter(filterConfig.toFilter.cached) .install - } - val reconfigurableFilterByLogLevelAndNameLogging: ZLayer[Any, Nothing, Unit] = { - val logFormat = - "%label{timestamp}{%fixed{32}{%timestamp}} %label{level}{%level} %label{thread}{%fiberId} %label{message}{%message} %label{cause}{%cause}" - - val configProvider: ConfigProvider = ConfigProvider.fromMap( - Map( - "logger/format" -> logFormat, - "logger/filter/rootLevel" -> LogLevel.Debug.label, - "logger/filter/mappings/a.b.c" -> LogLevel.Info.label, - "logger/filter/mappings/a.b.d" -> LogLevel.Warning.label, - "logger/filter/mappings/e" -> LogLevel.Info.label, - "logger/filter/mappings/f.g" -> LogLevel.Error.label, - "logger/filter/mappings/f" -> LogLevel.Info.label - ), - "/" - ) + val reconfigurableFilterByLogLevelAndNameLogging: ZLayer[Any, Nothing, Unit] = + Runtime.removeDefaultLoggers >>> ReconfigurableLogger + .make[Any, Nothing, String, Any, ConsoleLoggerConfig]( + ZIO.succeed(ConsoleLoggerConfig(LogFormat.default, filterConfig)), + (config, _) => makeSystemOutLogger(config.format.toLogger).filter(config.toFilter) + ) + .installUnscoped[Any] - Runtime.removeDefaultLoggers >>> Runtime.setConfigProvider(configProvider) >>> ReconfigurableLogger + val reconfigurableCachedFilterByLogLevelAndNameLogging: ZLayer[Any, Nothing, Unit] = + Runtime.removeDefaultLoggers >>> ReconfigurableLogger .make[Any, Nothing, String, Any, ConsoleLoggerConfig]( - ConsoleLoggerConfig.load().orDie, - (config, _) => makeConsoleLogger(config) + ZIO.succeed(ConsoleLoggerConfig(LogFormat.default, filterConfig)), + (config, _) => makeSystemOutLogger(config.format.toLogger).filter(config.toFilter.cached) ) .installUnscoped[Any] - } val names: List[String] = List( "a", @@ -138,16 +113,17 @@ class FilterBenchmarks { } /** - * 2023/12/25 Initial results + * 2023/12/26 Initial results * * jmh:run -i 3 -wi 3 -f1 -t1 .*FilterBenchmarks.* * - * Benchmark Mode Cnt Score Error Units - * FilterBenchmarks.cachedFilterByLogLevelAndNameLog thrpt 3 15098.312 ± 4204.210 ops/s - * FilterBenchmarks.filterByLogLevelAndNameLog thrpt 3 13100.786 ± 2017.585 ops/s - * FilterBenchmarks.handWrittenFilterLog thrpt 3 10864.716 ± 482.042 ops/s - * FilterBenchmarks.noFilteringLog thrpt 3 8813.185 ± 10371.239 ops/s - * FilterBenchmarks.reconfigurableFilterByLogLevelAndNameLog thrpt 3 3334.433 ± 216.060 ops/s + * Benchmark Mode Cnt Score Error Units + * FilterBenchmarks.cachedFilterByLogLevelAndNameLog thrpt 3 14795.547 ± 1372.850 ops/s + * FilterBenchmarks.filterByLogLevelAndNameLog thrpt 3 15093.994 ± 1230.494 ops/s + * FilterBenchmarks.handWrittenFilterLog thrpt 3 13157.888 ± 10193.287 ops/s + * FilterBenchmarks.noFilteringLog thrpt 3 11043.746 ± 230.514 ops/s + * FilterBenchmarks.reconfigurableCachedFilterByLogLevelAndNameLog thrpt 3 7532.412 ± 415.760 ops/s + * FilterBenchmarks.reconfigurableFilterByLogLevelAndNameLog thrpt 3 7482.096 ± 628.534 ops/s */ @Benchmark @@ -170,4 +146,8 @@ class FilterBenchmarks { def reconfigurableFilterByLogLevelAndNameLog(): Unit = testLoggingWith(reconfigurableFilterByLogLevelAndNameLogging) + @Benchmark + def reconfigurableCachedFilterByLogLevelAndNameLog(): Unit = + testLoggingWith(reconfigurableCachedFilterByLogLevelAndNameLogging) + } From a3c6c78a598259f4477e8ec1b4c6907bbad3972b Mon Sep 17 00:00:00 2001 From: Peter Kotula Date: Thu, 18 Jan 2024 22:17:28 +0100 Subject: [PATCH 56/56] LoggerFactory typo fix --- .../src/main/java/zio/logging/slf4j/bridge/LoggerFactory.java | 2 +- .../src/main/scala/zio/logging/slf4j/bridge/Slf4jBridge.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/slf4j2-bridge/src/main/java/zio/logging/slf4j/bridge/LoggerFactory.java b/slf4j2-bridge/src/main/java/zio/logging/slf4j/bridge/LoggerFactory.java index f2aea9252..b655f3dd5 100644 --- a/slf4j2-bridge/src/main/java/zio/logging/slf4j/bridge/LoggerFactory.java +++ b/slf4j2-bridge/src/main/java/zio/logging/slf4j/bridge/LoggerFactory.java @@ -28,7 +28,7 @@ final class LoggerFactory implements ILoggerFactory { private LoggerRuntime runtime = null; - void attacheRuntime(LoggerRuntime runtime) { + void attachRuntime(LoggerRuntime runtime) { this.runtime = runtime; } diff --git a/slf4j2-bridge/src/main/scala/zio/logging/slf4j/bridge/Slf4jBridge.scala b/slf4j2-bridge/src/main/scala/zio/logging/slf4j/bridge/Slf4jBridge.scala index 716662978..e10b85321 100644 --- a/slf4j2-bridge/src/main/scala/zio/logging/slf4j/bridge/Slf4jBridge.scala +++ b/slf4j2-bridge/src/main/scala/zio/logging/slf4j/bridge/Slf4jBridge.scala @@ -40,7 +40,7 @@ object Slf4jBridge { org.slf4j.LoggerFactory .getILoggerFactory() .asInstanceOf[LoggerFactory] - .attacheRuntime(new ZioLoggerRuntime(runtime)) + .attachRuntime(new ZioLoggerRuntime(runtime)) ) } } yield ()