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"))