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