DOC: Updated logging documentation. See #1467
This commit is contained in:
parent
d7840fe9cc
commit
0239106fcc
9 changed files with 282 additions and 52 deletions
|
|
@ -281,19 +281,34 @@ object Logging {
|
||||||
val debugFormat = "[DEBUG] [%s] [%s] [%s] %s".intern
|
val debugFormat = "[DEBUG] [%s] [%s] [%s] %s".intern
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Obtain LoggingAdapter for the given application and source object. The
|
* Obtain LoggingAdapter for the given event stream (system) and source object.
|
||||||
* source is used to identify the source of this logging channel and must have
|
* 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
|
* 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:
|
||||||
|
* <ul>
|
||||||
|
* <li>if it is an Actor or ActorRef, its path is used</li>
|
||||||
|
* <li>in case of a String it is used as is</li>
|
||||||
|
* <li>in case of a class an approximation of its simpleName
|
||||||
|
* <li>and in all other cases the simpleName of its class</li>
|
||||||
|
* </ul>
|
||||||
*/
|
*/
|
||||||
def apply[T: LogSource](eventStream: LoggingBus, logSource: T): LoggingAdapter =
|
def apply[T: LogSource](eventStream: LoggingBus, logSource: T): LoggingAdapter =
|
||||||
new BusLogging(eventStream, implicitly[LogSource[T]].genString(logSource))
|
new BusLogging(eventStream, implicitly[LogSource[T]].genString(logSource))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Java API: Obtain LoggingAdapter for the given application and source object. The
|
* Java API: Obtain LoggingAdapter for the given system and source object. The
|
||||||
* source object is used to identify the source of this logging channel; if it is
|
* source object is used to identify the source of this logging channel. The source
|
||||||
* an Actor or ActorRef, its address is used, in case of a class an approximation of
|
* object is translated to a String according to the following rules:
|
||||||
* its simpleName and in all other cases the simpleName of its class.
|
* <ul>
|
||||||
|
* <li>if it is an Actor or ActorRef, its path is used</li>
|
||||||
|
* <li>in case of a String it is used as is</li>
|
||||||
|
* <li>in case of a class an approximation of its simpleName
|
||||||
|
* <li>and in all other cases the simpleName of its class</li>
|
||||||
|
* </ul>
|
||||||
*/
|
*/
|
||||||
def getLogger(system: ActorSystem, logSource: AnyRef): LoggingAdapter = apply(system.eventStream, LogSource.fromAnyRef(logSource))
|
def getLogger(system: ActorSystem, logSource: AnyRef): LoggingAdapter = apply(system.eventStream, LogSource.fromAnyRef(logSource))
|
||||||
|
|
||||||
|
|
@ -354,6 +369,11 @@ object Logging {
|
||||||
*/
|
*/
|
||||||
case object LoggerInitialized
|
case object LoggerInitialized
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Java API to create a LoggerInitialized message.
|
||||||
|
*/
|
||||||
|
def loggerInitialized() = LoggerInitialized
|
||||||
|
|
||||||
class LoggerInitializationException(msg: String) extends AkkaException(msg)
|
class LoggerInitializationException(msg: String) extends AkkaException(msg)
|
||||||
|
|
||||||
trait StdOutLogger {
|
trait StdOutLogger {
|
||||||
|
|
|
||||||
59
akka-docs/general/code/akka/docs/event/LoggingDocSpec.scala
Normal file
59
akka-docs/general/code/akka/docs/event/LoggingDocSpec.scala
Normal file
|
|
@ -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"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
package akka.docs.event
|
||||||
|
|
||||||
|
import org.scalatest.junit.JUnitSuite
|
||||||
|
|
||||||
|
class LoggingDocTest extends LoggingDocTestBase with JUnitSuite
|
||||||
|
|
@ -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<Object> 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
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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
|
You may also specify and parse the configuration programmatically in other ways when instantiating
|
||||||
the ``ActorSystem``.
|
the ``ActorSystem``.
|
||||||
|
|
||||||
.. includecode:: code/ConfigDocSpec.scala
|
.. includecode:: code/akka/docs/config/ConfigDocSpec.scala
|
||||||
:include: imports,custom-config
|
:include: imports,custom-config
|
||||||
|
|
||||||
The ``ConfigFactory`` provides several methods to parse the configuration from various sources.
|
The ``ConfigFactory`` provides several methods to parse the configuration from various sources.
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ General
|
||||||
jmm
|
jmm
|
||||||
message-send-semantics
|
message-send-semantics
|
||||||
configuration
|
configuration
|
||||||
event-handler
|
logging
|
||||||
slf4j
|
|
||||||
addressing
|
addressing
|
||||||
supervision
|
supervision
|
||||||
|
|
|
||||||
103
akka-docs/general/logging.rst
Normal file
103
akka-docs/general/logging.rst
Normal file
|
|
@ -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 <http://www.slf4j.org/>`_. 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 <http://logback.qos.ch/>`_:
|
||||||
|
|
||||||
|
.. 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::
|
||||||
|
|
||||||
|
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||||
|
<layout>
|
||||||
|
<pattern>%date{ISO8601} %-5level %logger{36} %X{sourceThread} - %msg%n</pattern>
|
||||||
|
</layout>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
|
@ -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 <http://logback.qos.ch/>`_:
|
|
||||||
|
|
||||||
.. 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::
|
|
||||||
|
|
||||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
|
||||||
<layout>
|
|
||||||
<pattern>%date{ISO8601} %-5level %logger{36} %X{sourceThread} - %msg%n</pattern>
|
|
||||||
</layout>
|
|
||||||
</appender>
|
|
||||||
|
|
||||||
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue