Skip to content

SLF4J provide logging backend that stores everything in MDC  #445

@domdorn

Description

@domdorn

This corresponds to zio/zio#6673
I'm using SLF4J and the LogStashEncoder. I'm trying to put all fields/spans into MDCFields and just keep the real message in the message field. The only way I was able to accomplish this was by creating my own implementation of the SLF4J backend like

  private def slf4jLogger(
                           rootLoggerName: ZTraceElement => String,
                           logLevel: LogLevel,
                           format: LogFormat
                         ): ZLogger[String, Unit] =
    new ZLogger[String, Unit] {
      val formatLogger: ZLogger[String, Option[String]] =
        format.toLogger.filterLogLevel(_ >= logLevel)

      override def apply(
                          trace: ZTraceElement,
                          fiberId: FiberId,
                          logLevel: LogLevel,
                          message: () => String,
                          cause: Cause[Any],
                          context: Map[FiberRef[_], Any],
                          spans: List[LogSpan],
                          annotations: Map[String, String]
                        ): Unit =
        formatLogger(trace, fiberId, logLevel, message, cause, context, spans, annotations).foreach { message =>
          val slf4jLogger = LoggerFactory.getLogger(rootLoggerName(trace))

          val now = System.currentTimeMillis()
          val newAnnotations = spans.map(span => span.label -> (now-span.startTime).toString).toMap ++ annotations ++ Map(
            "fiberId" -> fiberId.threadName.toString
          )

          val previous =
            if (newAnnotations.nonEmpty) {
              val previous =
                Some(Option(MDC.getCopyOfContextMap).getOrElse(java.util.Collections.emptyMap[String, String]()))
              MDC.setContextMap(newAnnotations.asJava)
              previous
            } else None

          try logLevel match {
            case LogLevel.All     => if (slf4jLogger.isTraceEnabled) slf4jLogger.trace(message)
            case LogLevel.Debug   => if (slf4jLogger.isDebugEnabled) slf4jLogger.debug(message)
            case LogLevel.Info    => if (slf4jLogger.isInfoEnabled) slf4jLogger.info(message)
            case LogLevel.Warning => if (slf4jLogger.isWarnEnabled) slf4jLogger.warn(message)
            case LogLevel.Error   => if (slf4jLogger.isErrorEnabled) slf4jLogger.error(message)
            case LogLevel.Fatal   => if (slf4jLogger.isErrorEnabled) slf4jLogger.error(message)
            case LogLevel.None    => ()
            case _                => ()
          } finally previous.foreach(MDC.setContextMap)
        }
    }

In general, when using SLF4J and one of the underlying backends, I want everything stored in fields so that I can leverage the logger-architecture of (in this case) Logback, where I can simply log a json string or create my custom logformat like for every other project.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions