Merge pull request #150 from jboner/wip-1467-logging-docs-patriknw

DOC: Updated logging documentation. See #1467
This commit is contained in:
patriknw 2011-12-13 05:49:49 -08:00
commit 237f6c3d53
13 changed files with 376 additions and 153 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

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

@ -1,101 +0,0 @@
.. _event-handler:
Event Handler
=============
There is an Event Handler which takes the place of a logging system in Akka:
.. code-block:: scala
akka.event.EventHandler
You can configure which event handlers should be registered at boot time. 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 (EventHandler$DefaultListener logs to STDOUT)
event-handlers = ["akka.event.EventHandler$DefaultListener"]
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 from Scala (from Java you just have to create an 'UntypedActor' and create a handler for these messages):
.. code-block:: scala
val errorHandlerEventListener = Actor.actorOf(new Actor {
self.dispatcher = EventHandler.EventHandlerDispatcher
def receive = {
case EventHandler.Error(cause, instance, message) => ...
case EventHandler.Warning(instance, message) => ...
case EventHandler.Info(instance, message) => ...
case EventHandler.Debug(instance, message) => ...
case genericEvent => ...
}
})
To add the listener:
.. code-block:: scala
EventHandler.addListener(errorHandlerEventListener)
To remove the listener:
.. code-block:: scala
EventHandler.removeListener(errorHandlerEventListener)
To log an event:
.. code-block:: scala
EventHandler.notify(EventHandler.Error(exception, this, message))
EventHandler.notify(EventHandler.Warning(this, message))
EventHandler.notify(EventHandler.Info(this, message))
EventHandler.notify(EventHandler.Debug(this, message))
EventHandler.notify(object)
You can also use one of the direct methods (for a bit better performance):
.. code-block:: scala
EventHandler.error(exception, this, message)
EventHandler.error(this, message)
EventHandler.warning(this, message)
EventHandler.info(this, message)
EventHandler.debug(this, message)
The event handler allows you to send an arbitrary object to the handler which you can handle in your event handler listener. The default listener prints it's toString String out to STDOUT.
.. code-block:: scala
EventHandler.notify(anyRef)
The methods take a call-by-name parameter for the message to avoid object allocation and execution if level is disabled. The following formatting function will not be evaluated if level is INFO, WARNING, or ERROR.
.. code-block:: scala
EventHandler.debug(this, "Processing took %s ms".format(duration))
From Java you need to nest the call in an if statement to achieve the same thing.
.. code-block:: java
if (EventHandler.isDebugEnabled()) {
EventHandler.debug(this, String.format("Processing took %s ms", duration));
}

View file

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

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>

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

@ -8,6 +8,7 @@ Java API
untyped-actors
typed-actors
logging
futures
dataflow
stm

View file

@ -0,0 +1,97 @@
.. _logging-java:
################
Logging (Java)
################
.. sidebar:: Contents
.. contents:: :local:
How to Log
==========
Create a ``LoggingAdapter`` and use the ``error``, ``warning``, ``info``, or ``debug`` methods,
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:
* 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-java`
event handler available in the 'akka-slf4j' module.
Example of creating a listener:
.. includecode:: code/akka/docs/event/LoggingDocTestBase.java
:include: imports,imports-listener,my-event-listener
.. _slf4j-java:
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:: xml
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.0.0</version>
<scope>runtime</scope>
</dependency>
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

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

@ -8,6 +8,7 @@ Scala API
actors
typed-actors
logging
futures
dataflow
agents

View file

@ -0,0 +1,99 @@
.. _logging-scala:
#################
Logging (Scala)
#################
.. sidebar:: Contents
.. contents:: :local:
How to Log
==========
Create a ``LoggingAdapter`` and use the ``error``, ``warning``, ``info``, or ``debug`` methods,
as illustrated in this example:
.. includecode:: code/akka/docs/event/LoggingDocSpec.scala
:include: my-actor
For convenience you can mixin the ``log`` member into actors, instead of defining it as above.
.. 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:
* 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-scala`
event handler available in the 'akka-slf4j' module.
Example of creating a listener:
.. includecode:: code/akka/docs/event/LoggingDocSpec.scala
:include: my-event-listener
.. _slf4j-scala:
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>