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 I can just mixin ActorLogging. In MyObject, I don't have access to context.system. Do I create an akka.event.Logging
with AkkaSystem() and then what for the LogSource implicit?
问题:
回答1:
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).
回答2:
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.
回答3:
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.
回答4:
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 can use a logger (log4j 1.x, logback, log4j 2.x) directly in both actor and non-actor code.
- This tightly couples your code to a logger implementation. This is fine if it's your code, not to be used elsewhere, but not fine if you're building a library or intend to open source your work.
- If you do this, you gain no benefits from the actor system. Logging calls may become blocking calls, depending on how you have your logger set up, and thus this is frowned upon wherever performance or control over back pressure are important concerns.
- Because actor code (along with services it can consume) can operate on many different threads, some traditional logging activities such as the use of a threadlocal MDC (Mapped Diagnostic Context) can result in bizarre race conditions and context swtiching with logs output from messages that pass from actor to actor. Activities such as swapping MDCs onto messages before sending them may become necessary to preserve context between actor and non actor code.
- To capture ActorSystem events such as dead letters and supervision, you may need to write a logging adapter and specify it in your application.conf. These are pretty straightforward.
- You can use the SLF4J facade for both actor and non-actor logging.
- You are no longer coupled to a logger impl and what's more your services aren't coupled to akka. This is the best option for portability.
- You may inherit blocking behavior from your log framework.
- You may have to manage MDCs
- To capture ActorSystem events you'll need to specify "akka.event.slf4j.Slf4jLogger" in your application.conf
- You'll need to include an slf4j provider jar on the classpath to route slf4j log events to your chosen logger
- You can use Akka's Logging as your facade in both Actor and non-actor code
- You aren't coupled to a logger impl OR to slf4j, but you're coupled to a version of akka. This is probably a requirement of your system anyway, but for libraries it might reduce portability.
- You have to pass around an actor system to act as the "bus" for loggers. Tight coupling to a working actor system reduces portability further. (Within an app I usually build a little LoggingViaActorSystem trait with an implicit or global ActorSystem, which makes it easier to deal with this in code but not across dependencies).
- Non-blocking aynchronous logging is guaranteed, even if your logger doesn't support them. Causal consistency of logging is likely due to the use of a single consumer mailbox. However, memory safety and back pressure are not (I believe Akka logging uses an unbounded mailbox) --
- There are options such as use of a DiagnosticLoggingAdapter for avoiding the complexity of managing your own MDCs as work passes from actor to actor. Consistency should be preserved even as non-actor code mutates these MDCs.
- Logging is not likely to be available during an out-of-memory crash, and is sensitive to thread starvation on the default dispatcher
- You'll need to specify your chosen logger in application.conf unless you're interested in logging to standard out
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).
回答5:
simply create your own logger:
private val log = LoggerFactory.getLogger(YourClass.getClass)