diff --git a/akka-actor/src/main/scala/akka/event/Logging.scala b/akka-actor/src/main/scala/akka/event/Logging.scala index d6e71aa586..0425b4c661 100644 --- a/akka-actor/src/main/scala/akka/event/Logging.scala +++ b/akka-actor/src/main/scala/akka/event/Logging.scala @@ -281,19 +281,34 @@ object Logging { val debugFormat = "[DEBUG] [%s] [%s] [%s] %s".intern /** - * Obtain LoggingAdapter for the given application and source object. The - * source is used to identify the source of this logging channel and must have + * Obtain LoggingAdapter for the given event stream (system) and source object. + * Note that there is an implicit conversion from [[akka.actor.ActorSystem]] + * to [[akka.event.LoggingBus]]. + * + * The source is used to identify the source of this logging channel and must have * a corresponding LogSource[T] instance in scope; by default these are - * provided for Class[_], Actor, ActorRef and String types. + * provided for Class[_], Actor, ActorRef and String types. The source + * object is translated to a String according to the following rules: + * */ def apply[T: LogSource](eventStream: LoggingBus, logSource: T): LoggingAdapter = new BusLogging(eventStream, implicitly[LogSource[T]].genString(logSource)) /** - * Java API: Obtain LoggingAdapter for the given application and source object. The - * source object is used to identify the source of this logging channel; if it is - * an Actor or ActorRef, its address is used, in case of a class an approximation of - * its simpleName and in all other cases the simpleName of its class. + * Java API: Obtain LoggingAdapter for the given system and source object. The + * source object is used to identify the source of this logging channel. The source + * object is translated to a String according to the following rules: + * */ def getLogger(system: ActorSystem, logSource: AnyRef): LoggingAdapter = apply(system.eventStream, LogSource.fromAnyRef(logSource)) @@ -354,6 +369,11 @@ object Logging { */ case object LoggerInitialized + /** + * Java API to create a LoggerInitialized message. + */ + def loggerInitialized() = LoggerInitialized + class LoggerInitializationException(msg: String) extends AkkaException(msg) trait StdOutLogger { diff --git a/akka-docs/general/code/ConfigDocSpec.scala b/akka-docs/general/code/akka/docs/config/ConfigDocSpec.scala similarity index 100% rename from akka-docs/general/code/ConfigDocSpec.scala rename to akka-docs/general/code/akka/docs/config/ConfigDocSpec.scala diff --git a/akka-docs/general/code/akka/docs/event/LoggingDocSpec.scala b/akka-docs/general/code/akka/docs/event/LoggingDocSpec.scala new file mode 100644 index 0000000000..3d355bccb2 --- /dev/null +++ b/akka-docs/general/code/akka/docs/event/LoggingDocSpec.scala @@ -0,0 +1,59 @@ +package akka.docs.event + +import akka.actor.ActorSystem +import akka.testkit.AkkaSpec +import akka.actor.Actor +import akka.actor.Props + +object LoggingDocSpec { + + //#my-actor + import akka.event.Logging + + class MyActor extends Actor { + val log = Logging(context.system, this) + override def preStart() = { + log.debug("Starting") + } + override def preRestart(reason: Throwable, message: Option[Any]) { + log.error(reason, "Restarting due to [{}] when processing [{}]", + reason.getMessage, message.getOrElse("")) + } + def receive = { + case "test" ⇒ log.info("Received test") + case x ⇒ log.warning("Received unknown message: {}", x) + } + } + //#my-actor + + //#my-event-listener + import akka.event.Logging.InitializeLogger + import akka.event.Logging.LoggerInitialized + import akka.event.Logging.Error + import akka.event.Logging.Warning + import akka.event.Logging.Info + import akka.event.Logging.Debug + + class MyEventListener extends Actor { + def receive = { + case InitializeLogger(_) ⇒ sender ! LoggerInitialized + case Error(cause, logSource, message) ⇒ // ... + case Warning(logSource, message) ⇒ // ... + case Info(logSource, message) ⇒ // ... + case Debug(logSource, message) ⇒ // ... + } + } + //#my-event-listener + +} + +class LoggingDocSpec extends AkkaSpec { + + import LoggingDocSpec.MyActor + + "use a logging actor" in { + val myActor = system.actorOf(Props(new MyActor)) + myActor ! "test" + } + +} diff --git a/akka-docs/general/code/akka/docs/event/LoggingDocTest.scala b/akka-docs/general/code/akka/docs/event/LoggingDocTest.scala new file mode 100644 index 0000000000..2a55bdb81a --- /dev/null +++ b/akka-docs/general/code/akka/docs/event/LoggingDocTest.scala @@ -0,0 +1,5 @@ +package akka.docs.event + +import org.scalatest.junit.JUnitSuite + +class LoggingDocTest extends LoggingDocTestBase with JUnitSuite diff --git a/akka-docs/general/code/akka/docs/event/LoggingDocTestBase.java b/akka-docs/general/code/akka/docs/event/LoggingDocTestBase.java new file mode 100644 index 0000000000..669caa14b1 --- /dev/null +++ b/akka-docs/general/code/akka/docs/event/LoggingDocTestBase.java @@ -0,0 +1,86 @@ +package akka.docs.event; + +//#imports +import akka.event.Logging; +import akka.event.LoggingAdapter; + +//#imports + +//#imports-listener +import akka.event.Logging.InitializeLogger; +import akka.event.Logging.Error; +import akka.event.Logging.Warning; +import akka.event.Logging.Info; +import akka.event.Logging.Debug; + +//#imports-listener + +import org.junit.Test; + +import scala.Option; +import static org.junit.Assert.*; + +import akka.actor.ActorSystem; +import akka.actor.ActorRef; +import akka.actor.UntypedActor; +import akka.actor.UntypedActorFactory; + +public class LoggingDocTestBase { + + @Test + public void useLoggingActor() { + ActorSystem system = ActorSystem.create("MySystem"); + ActorRef myActor = system.actorOf(new UntypedActorFactory() { + public UntypedActor create() { + return new MyActor(); + } + }); + myActor.tell("test"); + system.stop(); + } + + //#my-actor + class MyActor extends UntypedActor { + LoggingAdapter log = Logging.getLogger(getContext().system(), this); + + @Override + public void preStart() { + log.debug("Starting"); + } + + @Override + public void preRestart(Throwable reason, Option message) { + log.error(reason, "Restarting due to [{}] when processing [{}]", reason.getMessage(), + message.isDefined() ? message.get() : ""); + } + + public void onReceive(Object message) { + if (message.equals("test")) { + log.info("Received test"); + } else { + log.warning("Received unknown message: {}", message); + } + } + } + + //#my-actor + + //#my-event-listener + class MyEventListener extends UntypedActor { + public void onReceive(Object message) { + if (message instanceof InitializeLogger) { + getSender().tell(Logging.loggerInitialized()); + } else if (message instanceof Error) { + // ... + } else if (message instanceof Warning) { + // ... + } else if (message instanceof Info) { + // ... + } else if (message instanceof Debug) { + // ... + } + } + } + //#my-event-listener + +} diff --git a/akka-docs/general/configuration.rst b/akka-docs/general/configuration.rst index 9328e19561..96467312bd 100644 --- a/akka-docs/general/configuration.rst +++ b/akka-docs/general/configuration.rst @@ -47,7 +47,7 @@ with ``/``. ``-Dconfig.resource=/dev.conf`` will load the ``dev.conf`` from the You may also specify and parse the configuration programmatically in other ways when instantiating the ``ActorSystem``. -.. includecode:: code/ConfigDocSpec.scala +.. includecode:: code/akka/docs/config/ConfigDocSpec.scala :include: imports,custom-config The ``ConfigFactory`` provides several methods to parse the configuration from various sources. diff --git a/akka-docs/general/index.rst b/akka-docs/general/index.rst index f3cc0d2a7d..4853061fbc 100644 --- a/akka-docs/general/index.rst +++ b/akka-docs/general/index.rst @@ -7,7 +7,6 @@ General jmm message-send-semantics configuration - event-handler - slf4j + logging addressing supervision diff --git a/akka-docs/general/logging.rst b/akka-docs/general/logging.rst new file mode 100644 index 0000000000..bdcf54c1cf --- /dev/null +++ b/akka-docs/general/logging.rst @@ -0,0 +1,103 @@ +.. _logging: + +######### + Logging +######### + +.. sidebar:: Contents + + .. contents:: :local: + +How to Log +========== + +Create a ``LoggingAdapter`` and use the ``error``, ``warning``, ``info``, or ``debug`` methods, +as illustrated in this example in Scala: + +.. includecode:: code/akka/docs/event/LoggingDocSpec.scala + :include: my-actor + +Corresponding example in Java: + +.. includecode:: code/akka/docs/event/LoggingDocTestBase.java + :include: imports,my-actor + +The second parameter to the ``Logging`` or ``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 + * in case of a class an approximation of its simpleName + * and in all other cases the simpleName of its class + +The log message may contain argument placeholders ``{}``, which will be substituted if the log level +is enabled. + +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. + +.. code-block:: ruby + + akka { + # Event handlers to register at boot time (Logging$DefaultLogger logs to STDOUT) + event-handlers = ["akka.event.Logging$DefaultLogger"] + loglevel = "DEBUG" # Options: ERROR, WARNING, INFO, 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` +event handler available in the 'akka-slf4j' module. + +Example of creating a listener in Scala: + +.. includecode:: code/akka/docs/event/LoggingDocSpec.scala + :include: my-event-listener + +Corresponding example in Java: + +.. includecode:: code/akka/docs/event/LoggingDocTestBase.java + :include: imports,imports-listener,my-event-listener + + +.. _slf4j: + +SLF4J +===== + +Akka provides an event handler for `SL4FJ `_. This module is available in the 'akka-slf4j.jar'. +It has one single dependency; the slf4j-api jar. In runtime you also need a SLF4J backend, we recommend `Logback `_: + + .. code-block:: scala + + lazy val logback = "ch.qos.logback" % "logback-classic" % "1.0.0" % "runtime" + + +You need to enable the Slf4jEventHandler in the 'event-handlers' element in +the :ref:`configuration`. Here you can also define the log level of the event bus. +More fine grained log levels can be defined in the configuration of the SLF4J backend +(e.g. logback.xml). The String representation of the source object that is used when +creating the ``LoggingAdapter`` correspond to the name of the SL4FJ logger. + +.. code-block:: ruby + + akka { + event-handlers = ["akka.event.slf4j.Slf4jEventHandler"] + loglevel = "DEBUG" + } + +Logging thread in MDC +--------------------- + +Since the logging is done asynchronously the thread in which the logging was performed is captured in +Mapped Diagnostic Context (MDC) with attribute name ``sourceThread``. +With Logback the thread name is available with ``%X{sourceThread}`` specifier within the pattern layout configuration:: + + + + %date{ISO8601} %-5level %logger{36} %X{sourceThread} - %msg%n + + + diff --git a/akka-docs/general/slf4j.rst b/akka-docs/general/slf4j.rst deleted file mode 100644 index 296cbb7b48..0000000000 --- a/akka-docs/general/slf4j.rst +++ /dev/null @@ -1,42 +0,0 @@ -.. _slf4j: - -SLF4J -===== - -This module is available in the 'akka-slf4j.jar'. It has one single dependency; the slf4j-api jar. In runtime you -also need a SLF4J backend, we recommend `Logback `_: - - .. code-block:: scala - - lazy val logback = "ch.qos.logback" % "logback-classic" % "1.0.0" % "runtime" - - -Event Handler -------------- - -This module includes a SLF4J Event Handler that works with Akka's standard Event Handler. You enabled it in the 'event-handlers' element in -the :ref:`configuration`. Here you can also define the log level. - -.. code-block:: ruby - - akka { - event-handlers = ["akka.event.slf4j.Slf4jEventHandler"] - loglevel = "DEBUG" - } - -Read more about how to use the :ref:`event-handler`. - -Logging thread in MDC ---------------------- - -Since the logging is done asynchronously the thread in which the logging was performed is captured in -Mapped Diagnostic Context (MDC) with attribute name ``sourceThread``. -With Logback the thread name is available with ``%X{sourceThread}`` specifier within the pattern layout configuration:: - - - - %date{ISO8601} %-5level %logger{36} %X{sourceThread} - %msg%n - - - - \ No newline at end of file