I have an Akka Actor that makes a call to MyObject.foo()
. MyObject
is not an Actor. How do I setup Logging in it? With an Actor it\'s simple, because
simply create your own logger:
private val log = LoggerFactory.getLogger(YourClass.getClass)
Actually I would redirect Akka logging to slf4j and use this API directly in all unrelated classes. First add this to your configuration:
akka {
event-handlers = ["akka.event.slf4j.Slf4jEventHandler"]
loglevel = "DEBUG"
}
Then choose some SLF4J implementation, I suggest logback. In your actors continue using ActorLogging
trait. In other classes simply rely on SLF4J API - or even better - try out slf4s facade around SLF4J.
Tip: try out the following logging pattern in Logback:
<pattern>%d{HH:mm:ss.SSS} | %-5level | %thread | %X{akkaSource} | %logger{1} | %m%n%rEx</pattern>
The %X{akkaSource}
will print actor path when available (just like standard logging).
As has been mentioned, you're spoiled for options for non-actor logging within an actor system. I am going to attempt to provide a set of heuristics to help you determine how you should route logging for your work.
You're welcome to mix and match the above behaviors as necessary to meet your requirements. For example, you might choose to bind to SLF4J for libraries and use Akka logging for everything else. Just note that mixing blocking and non-blocking logging could cause race conditions where causes (logged async via an actor) are logged after their effects (logged sync directly).
According to the latest (currently version 2.6.9) logging documentation using a Logger obtained from org.slf4j.LoggerFactory
is perfectly fine, and is actually the recommended way to log outside an actor. I copy hereafter the exact wording.
It’s perfectly fine to use a Logger retrieved via org.slf4j.LoggerFactory, but then the logging events will not include the akkaSource MDC value. This is the recommend way when logging outside of an actor, including logging from Future callbacks.
I also provide hereafter a snippet based on the example
val log = LoggerFactory.getLogger("com.mypackage.MyObject")
Future {
// do something
"result"
}.onComplete {
case Success(result) => log.info("Success!: {}", result)
case Failure(exc) => log.error("Failure!", exc)
}
In order to minimize performance penalties by logging one can configure an asynchronous appender for the SLF4J backend. Logback is the recommended logging backend.
dependencies {
compile group: 'ch.qos.logback', name: 'logback-classic', version: '1.2.3'
}
A starting point for configuration of logback.xml for production:
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>myapp.log</file>
<immediateFlush>false</immediateFlush>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>myapp_%d{yyyy-MM-dd}.log</fileNamePattern>
</rollingPolicy>
<encoder>
<pattern>[%date{ISO8601}] [%level] [%logger] [%marker] [%thread] - %msg MDC: {%mdc}%n</pattern>
</encoder>
</appender>
<appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
<queueSize>8192</queueSize>
<neverBlock>true</neverBlock>
<appender-ref ref="FILE" />
</appender>
<root level="INFO">
<appender-ref ref="ASYNC"/>
</root>
Logging generally means IO and locks, which can slow down the operations of your code if it was performed synchronously.
The configurations shown above are the ones provided by the AKKA logging documentation. The documentation provides more information and can be found here
Using Akka 2.2.1, I was able to put this into my App to get logging outside of an actor:
import akka.event.Logging
val system = ActorSystem("HelloSystem", ConfigFactory.load.getConfig("akka"))
val log = Logging.getLogger(system, this)
log.info("Hi!")
This seems like a simpler solution for unifying an application's logging.
I've now settled on simply passing my central logging system around through DI constructor injection (Guice). And in my classes that do logging regularly (where asynchronicity is important), I take the injected ActorSystem and call the
this.log = akka.event.Logging.getLogger(actorSystem, this);
in the classes constructor.