DOC: Updated logging documentation. See #1467

This commit is contained in:
Patrik Nordwall 2011-12-13 12:33:29 +01:00
parent d7840fe9cc
commit 0239106fcc
9 changed files with 282 additions and 52 deletions

View file

@ -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:
* <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 =
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:
* <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))
@ -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 {

View 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"
}
}

View file

@ -0,0 +1,5 @@
package akka.docs.event
import org.scalatest.junit.JUnitSuite
class LoggingDocTest extends LoggingDocTestBase with JUnitSuite

View file

@ -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
}

View file

@ -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.

View file

@ -7,7 +7,6 @@ General
jmm
message-send-semantics
configuration
event-handler
slf4j
logging
addressing
supervision

View 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>

View file

@ -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>