Skip to main content
Version: 2.0.x

Reconfigurable Logger

ReconfigurableLogger is adding support for updating logger configuration in application runtime.

logger layer with configuration from ConfigProvider (example with Console Logger)):

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.

NOTE: consider if you need this feature in your application, as there may be some performance impacts (see benchmarks).

Examples

You can find the source code here

Console Logger With Re-configuration From Configuration File In Runtime

Example of application where logger configuration is updated at runtime when logger configuration file is changed.

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:

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 <- ConsoleLoggerConfig.load().withConfigProvider(TypesafeConfigProvider.fromTypesafeConfig(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

}

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

Example of application where logger configuration can be changed by Http APIs.

Logger configurations APIs:

  • get logger configurations
     curl -u "admin:admin" 'http://localhost:8080/example/logger'
  • get root logger configuration
     curl -u "admin:admin" 'http://localhost:8080/example/logger/root'
  • set root logger configuration
     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
     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:

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:

curl -u "admin:admin" --location --request PUT 'http://localhost:8080/example/logger/zio.logging.example' --header 'Content-Type: application/json' --data-raw '"WARN"'
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