diff --git a/akka-actor-tests/src/test/scala/akka/actor/DeathWatchSpec.scala b/akka-actor-tests/src/test/scala/akka/actor/DeathWatchSpec.scala
index 59fe72cc07..cd6dc58129 100644
--- a/akka-actor-tests/src/test/scala/akka/actor/DeathWatchSpec.scala
+++ b/akka-actor-tests/src/test/scala/akka/actor/DeathWatchSpec.scala
@@ -33,7 +33,7 @@ trait DeathWatchSpec { this: AkkaSpec with ImplicitSender with DefaultTimeout
}
"notify with one Terminated message when an Actor is stopped" in {
- val terminal = system.actorOf(Props(context ⇒ { case _ ⇒ }))
+ val terminal = system.actorOf(Props.empty)
startWatching(terminal) ! "hallo"
expectMsg("hallo") // this ensures that the DaemonMsgWatch has been received before we send the PoisonPill
@@ -43,7 +43,7 @@ trait DeathWatchSpec { this: AkkaSpec with ImplicitSender with DefaultTimeout
}
"notify with one Terminated message when an Actor is already dead" in {
- val terminal = system.actorOf(Props(context ⇒ { case _ ⇒ }))
+ val terminal = system.actorOf(Props.empty)
terminal ! PoisonPill
@@ -52,7 +52,7 @@ trait DeathWatchSpec { this: AkkaSpec with ImplicitSender with DefaultTimeout
}
"notify with all monitors with one Terminated message when an Actor is stopped" in {
- val terminal = system.actorOf(Props(context ⇒ { case _ ⇒ }))
+ val terminal = system.actorOf(Props.empty)
val monitor1, monitor2, monitor3 = startWatching(terminal)
terminal ! PoisonPill
@@ -67,7 +67,7 @@ trait DeathWatchSpec { this: AkkaSpec with ImplicitSender with DefaultTimeout
}
"notify with _current_ monitors with one Terminated message when an Actor is stopped" in {
- val terminal = system.actorOf(Props(context ⇒ { case _ ⇒ }))
+ val terminal = system.actorOf(Props.empty)
val monitor1, monitor3 = startWatching(terminal)
val monitor2 = system.actorOf(Props(new Actor {
context.watch(terminal)
diff --git a/akka-actor-tests/src/test/scala/akka/actor/dispatch/ActorModelSpec.scala b/akka-actor-tests/src/test/scala/akka/actor/dispatch/ActorModelSpec.scala
index b0d831dc77..fb75ab5593 100644
--- a/akka-actor-tests/src/test/scala/akka/actor/dispatch/ActorModelSpec.scala
+++ b/akka-actor-tests/src/test/scala/akka/actor/dispatch/ActorModelSpec.scala
@@ -208,8 +208,9 @@ object ActorModelSpec {
await(deadline)(stats.restarts.get() == restarts)
} catch {
case e ⇒
- system.eventStream.publish(Error(e, Option(dispatcher).toString,
- if (dispatcher ne null) dispatcher.getClass else this.getClass,
+ system.eventStream.publish(Error(e,
+ Option(dispatcher).toString,
+ (Option(dispatcher) getOrElse this).getClass,
"actual: " + stats + ", required: InterceptorStats(susp=" + suspensions +
",res=" + resumes + ",reg=" + registers + ",unreg=" + unregisters +
",recv=" + msgsReceived + ",proc=" + msgsProcessed + ",restart=" + restarts))
diff --git a/akka-actor-tests/src/test/scala/akka/event/LoggingReceiveSpec.scala b/akka-actor-tests/src/test/scala/akka/event/LoggingReceiveSpec.scala
index 6d524729dd..bcfb9c391b 100644
--- a/akka-actor-tests/src/test/scala/akka/event/LoggingReceiveSpec.scala
+++ b/akka-actor-tests/src/test/scala/akka/event/LoggingReceiveSpec.scala
@@ -59,7 +59,7 @@ class LoggingReceiveSpec extends WordSpec with BeforeAndAfterEach with BeforeAnd
}
val log = LoggingReceive("funky")(r)
log.isDefinedAt("hallo")
- expectMsg(1 second, Logging.Debug("funky", classOf[String], "received unhandled message hallo"))
+ expectMsg(1 second, Logging.Debug("funky", classOf[DummyClassForStringSources], "received unhandled message hallo"))
}
}
diff --git a/akka-actor/src/main/scala/akka/event/Logging.scala b/akka-actor/src/main/scala/akka/event/Logging.scala
index 33c4b1339e..07a1da1da5 100644
--- a/akka-actor/src/main/scala/akka/event/Logging.scala
+++ b/akka-actor/src/main/scala/akka/event/Logging.scala
@@ -168,15 +168,85 @@ trait LoggingBus extends ActorEventBus {
}
+/**
+ * This trait defines the interface to be provided by a “log source formatting
+ * rule” as used by [[akka.event.Logging]]’s `apply`/`create` method.
+ *
+ * See the companion object for default implementations.
+ *
+ * Example:
+ * {{{
+ * trait MyType { // as an example
+ * def name: String
+ * }
+ *
+ * implicit val myLogSourceType: LogSource[MyType] = new LogSource {
+ * def genString(a: MyType) = a.name
+ * }
+ *
+ * class MyClass extends MyType {
+ * val log = Logging(eventStream, this) // will use "hallo" as logSource
+ * def name = "hallo"
+ * }
+ * }}}
+ *
+ * The second variant is used for including the actor system’s address:
+ * {{{
+ * trait MyType { // as an example
+ * def name: String
+ * }
+ *
+ * implicit val myLogSourceType: LogSource[MyType] = new LogSource {
+ * def genString(a: MyType) = a.name
+ * def genString(a: MyType, s: ActorSystem) = a.name + "," + s
+ * }
+ *
+ * class MyClass extends MyType {
+ * val sys = ActorSyste("sys")
+ * val log = Logging(sys, this) // will use "hallo,akka://sys" as logSource
+ * def name = "hallo"
+ * }
+ * }}}
+ *
+ * The default implementation of the second variant will just call the first.
+ */
trait LogSource[-T] {
def genString(t: T): String
def genString(t: T, system: ActorSystem): String = genString(t)
+ def getClazz(t: T): Class[_] = t.getClass
}
+/**
+ * This is a “marker” class which is inserted as originator class into
+ * [[akka.event.LogEvent]] when the string representation was supplied
+ * directly.
+ */
+class DummyClassForStringSources
+
+/**
+ * This object holds predefined formatting rules for log sources.
+ *
+ * In case an [[akka.actor.ActorSystem]] is provided, the following apply:
+ *
+ * - [[akka.actor.Actor]] and [[akka.actor.ActorRef]] will be represented by their absolute physical path
+ * - providing a `String` as source will append "()" and use the result
+ * - providing a `Class` will extract its simple name, append "()" and use the result
+ * - anything else gives compile error unless implicit [[akka.event.LogSource]] is in scope for it
+ *
+ *
+ * In case a [[akka.event.LoggingBus]] is provided, the following apply:
+ *
+ * - [[akka.actor.Actor]] and [[akka.actor.ActorRef]] will be represented by their absolute physical path
+ * - providing a `String` as source will be used as is
+ * - providing a `Class` will extract its simple name
+ * - anything else gives compile error unless implicit [[akka.event.LogSource]] is in scope for it
+ *
+ */
object LogSource {
implicit val fromString: LogSource[String] = new LogSource[String] {
def genString(s: String) = s
override def genString(s: String, system: ActorSystem) = s + "(" + system + ")"
+ override def getClazz(s: String) = classOf[DummyClassForStringSources]
}
implicit val fromActor: LogSource[Actor] = new LogSource[Actor] {
@@ -191,29 +261,54 @@ object LogSource {
val fromClass: LogSource[Class[_]] = new LogSource[Class[_]] {
def genString(c: Class[_]) = simpleName(c)
override def genString(c: Class[_], system: ActorSystem) = simpleName(c) + "(" + system + ")"
+ override def getClazz(c: Class[_]) = c
}
implicit def fromAnyClass[T]: LogSource[Class[T]] = fromClass.asInstanceOf[LogSource[Class[T]]]
- def apply[T: LogSource](o: T): String = implicitly[LogSource[T]].genString(o)
+ /**
+ * Convenience converter access: given an implicit `LogSource`, generate the
+ * string representation and originating class.
+ */
+ def apply[T: LogSource](o: T): (String, Class[_]) = {
+ val ls = implicitly[LogSource[T]]
+ (ls.genString(o), ls.getClazz(o))
+ }
- def apply[T: LogSource](o: T, system: ActorSystem): String = implicitly[LogSource[T]].genString(o, system)
+ /**
+ * Convenience converter access: given an implicit `LogSource` and
+ * [[akka.actor.ActorSystem]], generate the string representation and
+ * originating class.
+ */
+ def apply[T: LogSource](o: T, system: ActorSystem): (String, Class[_]) = {
+ val ls = implicitly[LogSource[T]]
+ (ls.genString(o, system), ls.getClazz(o))
+ }
- def fromAnyRef(o: AnyRef): String =
+ /**
+ * construct string representation for any object according to
+ * rules above with fallback to its `Class`’s simple name.
+ */
+ def fromAnyRef(o: AnyRef): (String, Class[_]) =
o match {
- case c: Class[_] ⇒ fromClass.genString(c)
- case a: Actor ⇒ fromActor.genString(a)
- case a: ActorRef ⇒ fromActorRef.genString(a)
- case s: String ⇒ s
- case x ⇒ simpleName(x)
+ case c: Class[_] ⇒ apply(c)
+ case a: Actor ⇒ apply(a)
+ case a: ActorRef ⇒ apply(a)
+ case s: String ⇒ apply(s)
+ case x ⇒ (simpleName(x), x.getClass)
}
- def fromAnyRef(o: AnyRef, system: ActorSystem): String =
+ /**
+ * construct string representation for any object according to
+ * rules above (including the actor system’s address) with fallback to its
+ * `Class`’s simple name.
+ */
+ def fromAnyRef(o: AnyRef, system: ActorSystem): (String, Class[_]) =
o match {
- case c: Class[_] ⇒ fromClass.genString(c, system)
- case a: Actor ⇒ fromActor.genString(a, system)
- case a: ActorRef ⇒ fromActorRef.genString(a, system)
- case s: String ⇒ fromString.genString(s, system)
- case x ⇒ simpleName(x) + "(" + system + ")"
+ case c: Class[_] ⇒ apply(c)
+ case a: Actor ⇒ apply(a)
+ case a: ActorRef ⇒ apply(a)
+ case s: String ⇒ apply(s)
+ case x ⇒ (simpleName(x) + "(" + system + ")", x.getClass)
}
}
@@ -322,140 +417,79 @@ object Logging {
/**
* Obtain LoggingAdapter for the given actor system and source object. This
- * will use the system’s event stream.
+ * will use the system’s event stream and include the system’s address in the
+ * log source string.
*
- * The source is used to identify the source of this logging channel and must have
- * a corresponding implicit LogSource[T] instance in scope; by default these are
- * provided for Class[_], Actor, ActorRef and String types. By these, the source
- * object is translated to a String according to the following rules:
- *
- * - if it is an Actor or ActorRef, its path is used
- * - in case of a String it is used as is
- * - in case of a class an approximation of its simpleName
- *
- and in all other cases the simpleName of its class
- *
- *
- * You can add your own rules quite easily:
+ * Do not use this if you want to supply a log category string (like
+ * “com.example.app.whatever”) unaltered, supply `system.eventStream` in this
+ * case or use
*
* {{{
- * trait MyType { // as an example
- * def name: String
- * }
- *
- * implicit val myLogSourceType: LogSource[MyType] = new LogSource {
- * def genString(a: MyType) = a.name
- * }
- *
- * class MyClass extends MyType {
- * val log = Logging(eventStream, this) // will use "hallo" as logSource
- * def name = "hallo"
- * }
+ * Logging(system, this.getClass)
* }}}
+ *
+ * The source is used to identify the source of this logging channel and
+ * must have a corresponding implicit LogSource[T] instance in scope; by
+ * default these are provided for Class[_], Actor, ActorRef and String types.
+ * See the companion object of [[akka.event.LogSource]] for details.
+ *
+ * You can add your own rules quite easily, see [[akka.event.LogSource]].
*/
- def apply[T: LogSource](system: ActorSystem, logSource: T): LoggingAdapter =
- new BusLogging(system.eventStream, LogSource(logSource, system), logSource.getClass)
+ def apply[T: LogSource](system: ActorSystem, logSource: T): LoggingAdapter = {
+ val (str, clazz) = LogSource(logSource, system)
+ new BusLogging(system.eventStream, str, clazz)
+ }
/**
* Obtain LoggingAdapter for the given logging bus and source object.
*
- * The source is used to identify the source of this logging channel and must have
- * a corresponding implicit LogSource[T] instance in scope; by default these are
- * provided for Class[_], Actor, ActorRef and String types. By these, the source
- * object is translated to a String according to the following rules:
- *
- * - if it is an Actor or ActorRef, its path is used
- * - in case of a String it is used as is
- * - in case of a class an approximation of its simpleName
- *
- and in all other cases the simpleName of its class
- *
+ * The source is used to identify the source of this logging channel and
+ * must have a corresponding implicit LogSource[T] instance in scope; by
+ * default these are provided for Class[_], Actor, ActorRef and String types.
+ * See the companion object of [[akka.event.LogSource]] for details.
*
- * You can add your own rules quite easily:
- *
- * {{{
- * trait MyType { // as an example
- * def name: String
- * }
- *
- * implicit val myLogSourceType: LogSource[MyType] = new LogSource {
- * def genString(a: MyType) = a.name
- * }
- *
- * class MyClass extends MyType {
- * val log = Logging(eventStream, this) // will use "hallo" as logSource
- * def name = "hallo"
- * }
- * }}}
+ * You can add your own rules quite easily, see [[akka.event.LogSource]].
*/
- def apply[T: LogSource](bus: LoggingBus, logSource: T): LoggingAdapter =
- new BusLogging(bus, implicitly[LogSource[T]].genString(logSource), logSource.getClass)
+ def apply[T: LogSource](bus: LoggingBus, logSource: T): LoggingAdapter = {
+ val (str, clazz) = LogSource(logSource)
+ new BusLogging(bus, str, clazz)
+ }
/**
* Obtain LoggingAdapter for the given actor system and source object. This
- * will use the system’s event stream.
+ * will use the system’s event stream and include the system’s address in the
+ * log source string.
*
- * The source is used to identify the source of this logging channel and must have
- * a corresponding implicit LogSource[T] instance in scope; by default these are
- * provided for Class[_], Actor, ActorRef and String types. By these, the source
- * object is translated to a String according to the following rules:
- *
- * - if it is an Actor or ActorRef, its path is used
- * - in case of a String it is used as is
- * - in case of a class an approximation of its simpleName
- *
- and in all other cases the simpleName of its class
- *
- *
- * You can add your own rules quite easily:
+ * Do not use this if you want to supply a log category string (like
+ * “com.example.app.whatever”) unaltered, supply `system.eventStream` in this
+ * case or use
*
* {{{
- * trait MyType { // as an example
- * def name: String
- * }
- *
- * implicit val myLogSourceType: LogSource[MyType] = new LogSource {
- * def genString(a: MyType) = a.name
- * }
- *
- * class MyClass extends MyType {
- * val log = Logging(eventStream, this) // will use "hallo" as logSource
- * def name = "hallo"
- * }
+ * Logging.getLogger(system, this.getClass());
* }}}
+ *
+ * The source is used to identify the source of this logging channel and
+ * must have a corresponding implicit LogSource[T] instance in scope; by
+ * default these are provided for Class[_], Actor, ActorRef and String types.
+ * See the companion object of [[akka.event.LogSource]] for details.
*/
- def getLogger(system: ActorSystem, logSource: AnyRef): LoggingAdapter = apply(system, LogSource.fromAnyRef(logSource, system))
+ def getLogger(system: ActorSystem, logSource: AnyRef): LoggingAdapter = {
+ val (str, clazz) = LogSource.fromAnyRef(logSource, system)
+ new BusLogging(system.eventStream, str, clazz)
+ }
/**
- * Obtain LoggingAdapter for the given logging bus and source object. This
- * will use the system’s event stream.
+ * Obtain LoggingAdapter for the given logging bus and source object.
*
- * The source is used to identify the source of this logging channel and must have
- * a corresponding implicit LogSource[T] instance in scope; by default these are
- * provided for Class[_], Actor, ActorRef and String types. By these, the source
- * object is translated to a String according to the following rules:
- *
- * - if it is an Actor or ActorRef, its path is used
- * - in case of a String it is used as is
- * - in case of a class an approximation of its simpleName
- *
- and in all other cases the simpleName of its class
- *
- *
- * You can add your own rules quite easily:
- *
- * {{{
- * trait MyType { // as an example
- * def name: String
- * }
- *
- * implicit val myLogSourceType: LogSource[MyType] = new LogSource {
- * def genString(a: MyType) = a.name
- * }
- *
- * class MyClass extends MyType {
- * val log = Logging(eventStream, this) // will use "hallo" as logSource
- * def name = "hallo"
- * }
- * }}}
+ * The source is used to identify the source of this logging channel and
+ * must have a corresponding implicit LogSource[T] instance in scope; by
+ * default these are provided for Class[_], Actor, ActorRef and String types.
+ * See the companion object of [[akka.event.LogSource]] for details.
*/
- //def getLogger(bus: LoggingBus, logSource: AnyRef): LoggingAdapter = apply(bus, LogSource.fromAnyRef(logSource))
+ def getLogger(bus: LoggingBus, logSource: AnyRef): LoggingAdapter = {
+ val (str, clazz) = LogSource.fromAnyRef(logSource)
+ new BusLogging(bus, str, clazz)
+ }
/**
* Artificial exception injected into Error events if no Throwable is
diff --git a/akka-actor/src/main/scala/akka/event/LoggingReceive.scala b/akka-actor/src/main/scala/akka/event/LoggingReceive.scala
index bb5a282856..27d829de5e 100644
--- a/akka-actor/src/main/scala/akka/event/LoggingReceive.scala
+++ b/akka-actor/src/main/scala/akka/event/LoggingReceive.scala
@@ -36,7 +36,8 @@ object LoggingReceive {
class LoggingReceive(source: AnyRef, r: Receive)(implicit system: ActorSystem) extends Receive {
def isDefinedAt(o: Any) = {
val handled = r.isDefinedAt(o)
- system.eventStream.publish(Debug(LogSource.fromAnyRef(source), source.getClass, "received " + (if (handled) "handled" else "unhandled") + " message " + o))
+ val (str, clazz) = LogSource.fromAnyRef(source)
+ system.eventStream.publish(Debug(str, clazz, "received " + (if (handled) "handled" else "unhandled") + " message " + o))
handled
}
def apply(o: Any): Unit = r(o)
diff --git a/akka-docs/java/logging.rst b/akka-docs/java/logging.rst
index aee644c175..ffee92d00e 100644
--- a/akka-docs/java/logging.rst
+++ b/akka-docs/java/logging.rst
@@ -17,8 +17,13 @@ as illustrated in this example:
.. includecode:: code/akka/docs/event/LoggingDocTestBase.java
:include: imports,my-actor
-The second parameter to the ``Logging.getLogger`` is the source of this logging channel.
-The source object is translated to a String according to the following rules:
+The first parameter to ``Logging.getLogger`` could also be any
+:class:`LoggingBus`, specifically ``system.eventStream()``; in the demonstrated
+case, the actor system’s address is included in the ``akkaSource``
+representation of the log source (see `Logging Thread and Akka Source in MDC`_)
+while in the second case this is not automatically done. The second parameter
+to ``Logging.getLogger`` is the source of this logging channel. The source
+object is translated to a String according to the following rules:
* if it is an Actor or ActorRef, its path is used
* in case of a String it is used as is
@@ -28,6 +33,13 @@ The source object is translated to a String according to the following rules:
The log message may contain argument placeholders ``{}``, which will be substituted if the log level
is enabled.
+The Java :class:`Class` of the log source is also included in the generated
+:class:`LogEvent`. In case of a simple string this is replaced with a “marker”
+class :class:`akka.event.DummyClassForStringSources` in order to allow special
+treatment of this case, e.g. in the SLF4J event listener which will then use
+the string instead of the class’ name for looking up the logger instance to
+use.
+
Event Handler
=============
@@ -96,6 +108,12 @@ With Logback the thread name is available with ``%X{sourceThread}`` specifier wi
+.. note::
+
+ It will probably be a good idea to use the ``sourceThread`` MDC value also in
+ non-Akka parts of the application in order to have this property consistently
+ available in the logs.
+
Another helpful facility is that Akka captures the actor’s address when
instantiating a logger within it, meaning that the full instance identification
is available for associating log messages e.g. with members of a router. This
diff --git a/akka-docs/scala/code/akka/docs/event/LoggingDocSpec.scala b/akka-docs/scala/code/akka/docs/event/LoggingDocSpec.scala
index c3c070d374..652c36af3f 100644
--- a/akka-docs/scala/code/akka/docs/event/LoggingDocSpec.scala
+++ b/akka-docs/scala/code/akka/docs/event/LoggingDocSpec.scala
@@ -47,6 +47,24 @@ object LoggingDocSpec {
}
//#my-event-listener
+ //#my-source
+ import akka.event.LogSource
+ import akka.actor.ActorSystem
+
+ object MyType {
+ implicit val logSource: LogSource[AnyRef] = new LogSource[AnyRef] {
+ def genString(o: AnyRef): String = o.getClass.getName
+ override def getClazz(o: AnyRef): Class[_] = o.getClass
+ }
+ }
+
+ class MyType(system: ActorSystem) {
+ import MyType._
+ import akka.event.Logging
+
+ val log = Logging(system, this)
+ }
+ //#my-source
}
class LoggingDocSpec extends AkkaSpec {
diff --git a/akka-docs/scala/logging.rst b/akka-docs/scala/logging.rst
index f4272c5da0..debafcedc5 100644
--- a/akka-docs/scala/logging.rst
+++ b/akka-docs/scala/logging.rst
@@ -22,6 +22,8 @@ For convenience you can mixin the ``log`` member into actors, instead of definin
.. code-block:: scala
class MyActor extends Actor with akka.actor.ActorLogging {
+ ...
+ }
The second parameter to the ``Logging`` is the source of this logging channel.
The source object is translated to a String according to the following rules:
@@ -29,17 +31,46 @@ The source object is translated to a String according to the following rules:
* if it is an Actor or ActorRef, its path is used
* in case of a String it is used as is
* in case of a class an approximation of its simpleName
- * and in all other cases the simpleName of its class
+ * and in all other cases a compile error occurs unless and implicit
+ :class:`LogSource[T]` is in scope for the type in question.
The log message may contain argument placeholders ``{}``, which will be substituted if the log level
is enabled.
+Translating Log Source to String and Class
+------------------------------------------
+
+The rules for translating the source object to the source string and class
+which are inserted into the :class:`LogEvent` during runtime are implemented
+using implicit parameters and thus fully customizable: simply create your own
+instance of :class:`LogSource[T]` and have it in scope when creating the
+logger.
+
+.. includecode:: code/akka/docs/event/LoggingDocSpec.scala#my-source
+
+This example creates a log source which mimics traditional usage of Java
+loggers, which are based upon the originating object’s class name as log
+category. The override of :meth:`getClazz` is only included for demonstration
+purposes as it contains exactly the default behavior.
+
+.. note::
+
+ You may also create the string representation up front and pass that in as
+ the log source, but be aware that then the :class:`Class[_]` which will be
+ put in the :class:`LogEvent` is
+ :class:`akka.event.DummyClassForStringSources`.
+
+ The SLF4J event listener treats this case specially (using the actual string
+ to look up the logger instance to use instead of the class’ name), and you
+ might want to do this also in case you implement your own loggin adapter.
+
Event Handler
=============
-Logging is performed asynchronously through an event bus. You can configure which event handlers that should
-subscribe to the logging events. That is done using the 'event-handlers' element in the :ref:`configuration`.
-Here you can also define the log level.
+Logging is performed asynchronously through an event bus. You can configure
+which event handlers that should subscribe to the logging events. That is done
+using the ``event-handlers`` element in the :ref:`configuration`. Here you can
+also define the log level.
.. code-block:: ruby
@@ -50,7 +81,8 @@ Here you can also define the log level.
loglevel = "DEBUG"
}
-The default one logs to STDOUT and is registered by default. It is not intended to be used for production. There is also an :ref:`slf4j-scala`
+The default one logs to STDOUT and is registered by default. It is not intended
+to be used for production. There is also an :ref:`slf4j-scala`
event handler available in the 'akka-slf4j' module.
Example of creating a listener:
@@ -58,7 +90,6 @@ Example of creating a listener:
.. includecode:: code/akka/docs/event/LoggingDocSpec.scala
:include: my-event-listener
-
.. _slf4j-scala:
SLF4J
@@ -98,6 +129,12 @@ With Logback the thread name is available with ``%X{sourceThread}`` specifier wi
+.. note::
+
+ It will probably be a good idea to use the ``sourceThread`` MDC value also in
+ non-Akka parts of the application in order to have this property consistently
+ available in the logs.
+
Another helpful facility is that Akka captures the actor’s address when
instantiating a logger within it, meaning that the full instance identification
is available for associating log messages e.g. with members of a router. This
diff --git a/akka-slf4j/src/main/scala/akka/event/slf4j/SLF4J.scala b/akka-slf4j/src/main/scala/akka/event/slf4j/SLF4J.scala
index 4831d78270..91a6cd7bf2 100644
--- a/akka-slf4j/src/main/scala/akka/event/slf4j/SLF4J.scala
+++ b/akka-slf4j/src/main/scala/akka/event/slf4j/SLF4J.scala
@@ -8,6 +8,7 @@ import org.slf4j.{ Logger ⇒ SLFLogger, LoggerFactory ⇒ SLFLoggerFactory }
import org.slf4j.MDC
import akka.event.Logging._
import akka.actor._
+import akka.event.DummyClassForStringSources
/**
* Base trait for all classes that wants to be able use the SLF4J logging infrastructure.
@@ -19,7 +20,10 @@ trait SLF4JLogging {
object Logger {
def apply(logger: String): SLFLogger = SLFLoggerFactory getLogger logger
- def apply(logClass: Class[_]): SLFLogger = SLFLoggerFactory getLogger logClass
+ def apply(logClass: Class[_], logSource: String): SLFLogger = logClass match {
+ case c if c == classOf[DummyClassForStringSources] ⇒ apply(logSource)
+ case _ ⇒ SLFLoggerFactory getLogger logClass
+ }
def root: SLFLogger = apply(SLFLogger.ROOT_LOGGER_NAME)
}
@@ -39,24 +43,24 @@ class Slf4jEventHandler extends Actor with SLF4JLogging {
case event @ Error(cause, logSource, logClass, message) ⇒
withMdc(logSource, event.thread.getName) {
cause match {
- case Error.NoCause ⇒ Logger(logClass).error(message.toString)
- case _ ⇒ Logger(logClass).error(message.toString, cause)
+ case Error.NoCause ⇒ Logger(logClass, logSource).error(message.toString)
+ case _ ⇒ Logger(logClass, logSource).error(message.toString, cause)
}
}
case event @ Warning(logSource, logClass, message) ⇒
withMdc(logSource, event.thread.getName) {
- Logger(logClass).warn("{}", message.asInstanceOf[AnyRef])
+ Logger(logClass, logSource).warn("{}", message.asInstanceOf[AnyRef])
}
case event @ Info(logSource, logClass, message) ⇒
withMdc(logSource, event.thread.getName) {
- Logger(logClass).info("{}", message.asInstanceOf[AnyRef])
+ Logger(logClass, logSource).info("{}", message.asInstanceOf[AnyRef])
}
case event @ Debug(logSource, logClass, message) ⇒
withMdc(logSource, event.thread.getName) {
- Logger(logClass).debug("{}", message.asInstanceOf[AnyRef])
+ Logger(logClass, logSource).debug("{}", message.asInstanceOf[AnyRef])
}
case InitializeLogger(_) ⇒