diff --git a/akka-actor/src/main/scala/akka/event/Logging.scala b/akka-actor/src/main/scala/akka/event/Logging.scala index ff17673478..86ac09c7c2 100644 --- a/akka-actor/src/main/scala/akka/event/Logging.scala +++ b/akka-actor/src/main/scala/akka/event/Logging.scala @@ -8,6 +8,7 @@ import java.util.concurrent.atomic.AtomicInteger import akka.actor.ActorSystem.Settings import akka.actor._ +import akka.annotation.{ DoNotInherit, InternalApi } import akka.dispatch.RequiresMessageQueue import akka.event.Logging._ import akka.util.ReentrantGuard @@ -1403,7 +1404,9 @@ trait DiagnosticLoggingAdapter extends LoggingAdapter { def clearMDC(): Unit = mdc(emptyMDC) } -final class LogMarker(val name: String) +/** DO NOT INHERIT: Class is open only for use by akka-slf4j*/ +@DoNotInherit +class LogMarker(val name: String) object LogMarker { /** The Marker is internally transferred via MDC using using this key */ private[akka] final val MDCKey = "marker" diff --git a/akka-docs/src/main/paradox/scala/logging.md b/akka-docs/src/main/paradox/scala/logging.md index 89da73f4f9..a9bb43201a 100644 --- a/akka-docs/src/main/paradox/scala/logging.md +++ b/akka-docs/src/main/paradox/scala/logging.md @@ -559,6 +559,16 @@ A more advanced (including most Akka added information) example pattern would be %date{ISO8601} level=[%level] marker=[%marker] logger=[%logger] akkaSource=[%X{akkaSource}] sourceActorSystem=[%X{sourceActorSystem}] sourceThread=[%X{sourceThread}] mdc=[ticket-#%X{ticketNumber}: %X{ticketDesc}] - msg=[%msg]%n----%n ``` +#### Using SLF4J's Markers + +It is also possible to use the `org.slf4j.Marker` with the `LoggingAdapter` when using slf4j. + +Since the akka-actor library avoids depending on any specific logging library, the support for this is included in `akka-slf4j`, +which provides the `Slf4jLogMarker` type which can be passed in as first argument instead of the logging framework agnostic LogMarker +type from `akka-actor`. The most notable difference between the two is that slf4j's Markers can have child markers, so one can +rely more information using them rather than just a single string. + + ## java.util.logging diff --git a/akka-slf4j/src/main/scala/akka/event/slf4j/Slf4jLogger.scala b/akka-slf4j/src/main/scala/akka/event/slf4j/Slf4jLogger.scala index 9bb62ca3f8..8573e54765 100644 --- a/akka-slf4j/src/main/scala/akka/event/slf4j/Slf4jLogger.scala +++ b/akka-slf4j/src/main/scala/akka/event/slf4j/Slf4jLogger.scala @@ -7,12 +7,9 @@ package akka.event.slf4j import org.slf4j.{ MDC, Marker, MarkerFactory, Logger ⇒ SLFLogger, LoggerFactory ⇒ SLFLoggerFactory } import akka.event.Logging._ import akka.actor._ -import akka.event.DummyClassForStringSources +import akka.event.{ LogMarker, _ } import akka.util.Helpers -import akka.event.LoggingFilter -import akka.event.EventStream import akka.dispatch.RequiresMessageQueue -import akka.event.LoggerMessageQueueSemantics /** * Base trait for all classes that wants to be able use the SLF4J logging infrastructure. @@ -111,8 +108,12 @@ class Slf4jLogger extends Actor with SLF4JLogging with RequiresMessageQueue[Logg private final def markerIfPresent(event: LogEvent): Marker = event match { - case m: LogEventWithMarker ⇒ MarkerFactory.getMarker(m.marker.name) - case _ ⇒ null + case m: LogEventWithMarker ⇒ + m.marker match { + case slf4jMarker: Slf4jLogMarker ⇒ slf4jMarker.marker + case marker ⇒ MarkerFactory.getMarker(marker.name) + } + case _ ⇒ null } /** @@ -141,3 +142,14 @@ class Slf4jLoggingFilter(settings: ActorSystem.Settings, eventStream: EventStrea def isDebugEnabled(logClass: Class[_], logSource: String) = (eventStream.logLevel >= DebugLevel) && Logger(logClass, logSource).isDebugEnabled } + +/** Wraps [[org.slf4j.Marker]] */ +final class Slf4jLogMarker(val marker: org.slf4j.Marker) extends LogMarker(name = marker.getName) + +/** Factory for creating [[LogMarker]] that wraps [[org.slf4j.Marker]] */ +object Slf4jLogMarker { + def apply(marker: org.slf4j.Marker): Slf4jLogMarker = new Slf4jLogMarker(marker) + + /** Java API */ + def create(marker: org.slf4j.Marker): Slf4jLogMarker = apply(marker) +} diff --git a/akka-slf4j/src/test/scala/akka/event/slf4j/Slf4jLoggerSpec.scala b/akka-slf4j/src/test/scala/akka/event/slf4j/Slf4jLoggerSpec.scala index 415dd8d781..2ec9435e12 100644 --- a/akka-slf4j/src/test/scala/akka/event/slf4j/Slf4jLoggerSpec.scala +++ b/akka-slf4j/src/test/scala/akka/event/slf4j/Slf4jLoggerSpec.scala @@ -13,6 +13,7 @@ import ch.qos.logback.core.OutputStreamAppender import java.io.ByteArrayOutputStream import org.scalatest.BeforeAndAfterEach +import org.slf4j.{ Marker, MarkerFactory } object Slf4jLoggerSpec { @@ -27,6 +28,7 @@ object Slf4jLoggerSpec { """ final case class StringWithMDC(s: String, mdc: Map[String, Any]) + final case class StringWithSlf4jMarkerMDC(s: String, marker: Marker) final case class StringWithMarker(s: String, marker: LogMarker) final class LogProducer extends Actor with DiagnosticActorLogging { @@ -38,6 +40,8 @@ object Slf4jLoggerSpec { log.error(e, e.getMessage) case (s: String, x: Int, y: Int) ⇒ log.info(s, x, y) + case StringWithSlf4jMarkerMDC(s, m) ⇒ + markLog.info(Slf4jLogMarker(m), s) case StringWithMDC(s, mdc) ⇒ log.mdc(mdc) log.info(s) @@ -110,6 +114,17 @@ class Slf4jLoggerSpec extends AkkaSpec(Slf4jLoggerSpec.config) with BeforeAndAft s should include("msg=[security-wise interesting message]") } + "log info with slf4j marker" in { + val slf4jMarker = MarkerFactory.getMarker("SLF") + slf4jMarker.add(MarkerFactory.getMarker("ADDED")) // slf4j markers can have children + producer ! StringWithSlf4jMarkerMDC("security-wise interesting message", slf4jMarker) + + awaitCond(outputString.contains("----"), 5 seconds) + val s = outputString + s should include("marker=[SLF [ ADDED ]]") + s should include("msg=[security-wise interesting message]") + } + "put custom MDC values when specified" in { producer ! StringWithMDC("Message with custom MDC values", Map("ticketNumber" → 3671, "ticketDesc" → "Custom MDC Values"))