Search code examples
scalalogbackscala-catslogstash-logback-encodercats-effect

logback.xml appender not used with cats IOApp


I have a scala app which uses the AWS Kinesis Client Library.

I am using logback with the logstash encoder to format the logs from my app and the KCL as JSON.

My App is also written using cats.effects.IO.

import cats.effects._

object Main extends App {

  run(args.toList).unsafeRunSync

  def run(args: List[String]): IO[ExitCode] = { .. }

}

When the above code runs, logs from my app and from the KCL are correctly formatted through my JSON appender.

The problem arises when I try to use cats.effects.IOApp:

import cats.effects._

object Main extends IOApp {

  def run(args: List[String]): IO[ExitCode] = { .. }

}

When this version runs, the logs from my app are still correctly formatted through my JSON appender, but the logs from the KCL revert back to the default basic logger.

I have narrowed this down to the use of Fiber under the hood, and can reproduce the problem if I use run(args.toList).start.flatMap(_.join).unsafeRunSync which is essentially what IOApp is doing under the hood. I am running on the JVM, so this is the code that is running under the hood.

My logback.xml:

  <appender name="json" class="ch.qos.logback.core.ConsoleAppender">
    <encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
      <providers>
        <pattern>
          <pattern>
            {
              "level": "%level",
              "message": "%message"
            }
          </pattern>
        </pattern>
        <nestedField>
          <fieldName>properties</fieldName>
          <providers>
            <timestamp>
              <fieldName>utcTimestamp</fieldName>
              <pattern>yyyy-MM-dd'T'HH:mm:ss'Z'</pattern>
              <timeZone>UTC</timeZone>
            </timestamp>
            <arguments/>
          </providers>
        </nestedField>
        <stackTrace/>
      </providers>
    </encoder>
  </appender>

  <root level="info">
    <appender-ref ref="json" />
  </root>
</configuration>

Solution

  • Relying on the auto discovery of the logback.xml when there are multiple conflicting dependencies on the class path means there is no guarantee as to which will load first.

    Since there is already a dependency for:

    libraryDependencies += "org.slf4j" % "jcl-over-slf4j" % "1.7.21"
    

    You just need to exclude the commons logger:

    excludeDependencies += "commons-logging" % "commons-logging"
    

    This way the KCL is forced to use the correct logger instead of having to choose.