我有一个 Akka Actor 可以调用MyObject.foo()
. MyObject
不是演员。如何设置登录?使用 Actor 很简单,因为我可以混合 ActorLogging。在 MyObject 中,我无权访问 context.system。我是否akka.event.Logging
使用 AkkaSystem() 创建一个,然后为 LogSource 隐式创建什么?
6 回答
实际上我会将 Akka 日志重定向到slf4j并在所有不相关的类中直接使用这个 API。首先将其添加到您的配置中:
akka {
event-handlers = ["akka.event.slf4j.Slf4jEventHandler"]
loglevel = "DEBUG"
}
然后选择一些 SLF4J 实现,我建议logback。在你的演员继续使用ActorLogging
特质。在其他类中,只需依赖 SLF4J API - 甚至更好 - 尝试围绕 SLF4J的slf4s外观。
提示:在 Logback 中尝试以下日志模式:
<pattern>%d{HH:mm:ss.SSS} | %-5level | %thread | %X{akkaSource} | %logger{1} | %m%n%rEx</pattern>
将%X{akkaSource}
在可用时打印参与者路径(就像标准日志记录一样)。
使用 Akka 2.2.1,我能够将其放入我的应用程序中,以便在演员之外进行日志记录:
import akka.event.Logging
val system = ActorSystem("HelloSystem", ConfigFactory.load.getConfig("akka"))
val log = Logging.getLogger(system, this)
log.info("Hi!")
这似乎是统一应用程序日志记录的更简单的解决方案。
如前所述,您会被参与者系统中的非参与者日志记录选项宠坏。我将尝试提供一组启发式方法来帮助您确定应该如何为您的工作路由日志记录。
- 您可以在参与者和非参与者代码中直接使用记录器(log4j 1.x、logback、log4j 2.x)。
- 这将您的代码与记录器实现紧密耦合。如果是你的代码,这很好,不要在其他地方使用,但如果你正在构建一个库或打算开源你的工作,那就不好了。
- 如果你这样做,你不会从演员系统中获得任何好处。记录调用可能会变成阻塞调用,这取决于你如何设置你的记录器,因此在性能或对背压的控制是重要问题的地方,这是不受欢迎的。
- 由于参与者代码(连同它可以使用的服务)可以在许多不同的线程上运行,一些传统的日志记录活动,例如使用线程本地 MDC(映射诊断上下文)可能会导致奇怪的竞争条件和上下文与来自消息的日志输出切换从演员到演员。诸如在发送消息之前将 MDC 交换到消息上之类的活动可能成为保留参与者和非参与者代码之间的上下文的必要条件。
- 要捕获死信和监督等 ActorSystem 事件,您可能需要编写一个日志适配器并在 application.conf 中指定它。这些非常简单。
- 您可以将 SLF4J 外观用于参与者和非参与者日志记录。
- 您不再与 logger impl 耦合,而且您的服务也不再与 akka 耦合。这是便携性的最佳选择。
- 你可以从你的日志框架继承阻塞行为。
- 您可能需要管理 MDC
- 要捕获 ActorSystem 事件,您需要在 application.conf 中指定“akka.event.slf4j.Slf4jLogger”
- 您需要在类路径中包含一个 slf4j 提供程序 jar,以将 slf4j 日志事件路由到您选择的记录器
- 您可以在 Actor 和非 Actor 代码中使用 Akka 的 Logging 作为您的外观
- 您没有与 logger impl 或 slf4j 耦合,但您与 akka 的一个版本耦合。无论如何,这可能是您系统的要求,但对于库来说,它可能会降低可移植性。
- 你必须传递一个actor系统来充当记录器的“总线”。与工作参与者系统的紧密耦合进一步降低了可移植性。(在应用程序中,我通常构建一个带有隐式或全局 ActorSystem 的小 LoggingViaActorSystem trait,这使得在代码中更容易处理这个问题,而不是跨依赖项)。
- 保证非阻塞异步日志记录,即使您的记录器不支持它们。日志记录的因果一致性可能是由于使用了单个消费者邮箱。但是,内存安全和背压不是(我相信 Akka logging 使用的是无界邮箱)——
- 有一些选项,例如使用DiagnosticLoggingAdapter来避免在工作从参与者传递到参与者时管理您自己的 MDC 的复杂性。即使非参与者代码改变了这些 MDC,也应该保持一致性。
- 在内存不足崩溃期间日志不太可能可用,并且对默认调度程序上的线程饥饿很敏感
- 您需要在 application.conf 中指定您选择的记录器,除非您有兴趣登录到标准输出
欢迎您根据需要混合和匹配上述行为以满足您的要求。例如,您可以选择为库绑定到 SLF4J 并为其他所有内容使用 Akka 日志记录。请注意,混合阻塞和非阻塞日志记录可能会导致竞争条件,其中原因(通过参与者记录的异步)在其影响(直接记录同步)之后被记录。
我现在已经决定通过 DI 构造函数注入 (Guice) 简单地传递我的中央日志记录系统。在我定期进行日志记录的课程中(异步性很重要),我采用注入的 ActorSystem 并调用
this.log = akka.event.Logging.getLogger(actorSystem, this);
在类构造函数中。
根据最新的(当前版本 2.6.9)日志记录文档,使用从中获得的 Loggerorg.slf4j.LoggerFactory
非常好,实际上是推荐的在参与者之外登录的方式。我在下文中复制了确切的措辞。
使用通过 org.slf4j.LoggerFactory 检索的 Logger 非常好,但是日志事件将不包括 akkaSource MDC 值。这是在 actor 外部登录时推荐的方式,包括从 Future 回调中登录。
我还在下文中提供了一个基于示例的片段
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)
}
为了通过记录最小化性能损失,可以为 SLF4J 后端配置一个异步附加程序。Logback 是推荐的日志记录后端。
dependencies {
compile group: 'ch.qos.logback', name: 'logback-classic', version: '1.2.3'
}
为生产配置 logback.xml 的起点:
<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>
日志记录通常意味着 IO 和锁,如果同步执行,它们会减慢代码的操作。
上面显示的配置是 AKKA 日志记录文档提供的配置。该文档提供了更多信息,可以在此处找到
只需创建自己的记录器:
private val log = LoggerFactory.getLogger(YourClass.getClass)