I have a program using com.monovore.decline and org.typelevel.log4cats.
app.scala
//> using scala 3
//> using resourceDir ./resources
//> using dep org.typelevel::log4cats-slf4j:2.6.0
//> using dep ch.qos.logback:logback-classic:1.4.8
//> using dep org.typelevel::cats-core:2.9.0
//> using dep org.typelevel::cats-effect:3.5.1
//> using dep com.monovore::decline:2.4.1
//> using dep com.monovore::decline-effect:2.4.1
package myapp
import cats.effect.{ExitCode, IO, Sync}
import cats.implicits.*
import org.typelevel.log4cats.slf4j.Slf4jFactory
import org.typelevel.log4cats.{LoggerFactory, SelfAwareStructuredLogger}
import com.monovore.decline.effect.CommandIOApp
import com.monovore.decline.Opts
import org.typelevel.log4cats.syntax.*
class LoggingConfigurator
extends ch.qos.logback.core.spi.ContextAwareBase
with ch.qos.logback.classic.spi.Configurator {
override final def configure(
loggerContext: ch.qos.logback.classic.LoggerContext,
): ch.qos.logback.classic.spi.Configurator.ExecutionStatus = {
val encoder = new ch.qos.logback.classic.encoder.JsonEncoder
encoder.setContext(loggerContext)
encoder.start()
val appender = new ch.qos.logback.core.ConsoleAppender[ch.qos.logback.classic.spi.ILoggingEvent]
appender.setContext(loggerContext)
appender.setTarget("System.err")
appender.setEncoder(encoder)
appender.start()
val logger = loggerContext.getLogger("ROOT")
logger.addAppender(appender)
logger.setLevel(ch.qos.logback.classic.Level.DEBUG)
logger.setAdditive(false)
ch.qos.logback.classic.spi.Configurator.ExecutionStatus.DO_NOT_INVOKE_NEXT_IF_ANY
}
}
trait Logging[F[_]: Sync] {
private given LoggerFactory[F] = Slf4jFactory.create[F]
inline protected given SelfAwareStructuredLogger[F] = LoggerFactory[F].getLogger
}
object App extends CommandIOApp("myapp", "an app", true, "0.0.1") with Logging[IO] {
override final def main: Opts[IO[ExitCode]] =
Opts.unit map { _ =>
for {
_ <- debug"Hello, world!"
} yield ExitCode.Success
}
}
resources/META-INF/services/ch.qos.logback.classic.spi.Configurator
myapp.LoggingConfigurator
When I run this program, I expect to see JSON logs. But I see plain text (default ConsoleAppender
with default PatternLayoutEncoder
) instead:
$ scala-cli .
16:57:51.531 [io-compute-5] DEBUG myapp.App -- Hello, world!
I hope it's clear that I was expecting something like:
$ scala-cli .
{"timestamp": "2023-06-24T16:57:51.531+0500", "level": "DEBUG", "name": "myapp.App", "msg": "Hello, world!"}
What am I doing wrong?
EDIT: It seems I misunderstood usage of resources/META-INF/services/...
. I have edited the code snippet above to my latest attempt, but it still doesn't work as I expect so there is something else I am misunderstanding.