AKKA的日志: slf4j,logback和其他
AKKA的日志: slf4j,logback和其他
作为一个“搞kernel的”,对日志的理解不过是printk的EMERG,INFO,DEBUG等各种level,关键时刻还是得dump内存,上gcc单步跟踪。但在到处是异步并发,远程分布式通信的jdk世界,日志成了定位问题最重要甚至是唯一的手段。在akka上尤为如此。
akka日志的官方文档http://doc.akka.io/docs/akka/current/scala/logging.html
akka日志功能是基于slf4j构建的。对于不熟悉java的人,slf4j,log4j,logback等基本上是这样一个关系:SLF4J是一套log接口,java.util.logging, logback, log4j等是具体的实现,而logback已逐渐取代log4j成为事实标准。
所以要使用akka的日志,除了akka-slf4j还需增加logback依赖。
libraryDependencies ++= Seq( "com.typesafe.akka" %% "akka-actor" % akkaVersion, "com.typesafe.akka" %% "akka-contrib" % akkaVersion, "com.typesafe.akka" %% "akka-testkit" % akkaVersion, "com.typesafe.akka" %% "akka-slf4j" % akkaVersion, "org.scalatest" %% "scalatest" % "2.2.4" % "test", "ch.qos.logback" % "logback-classic" % "1.1.3", "commons-io" % "commons-io" % "2.4" % "test")
actor系统中记录日志的三种方法
1 通过ActorLogging记录日志
akka提供了ActorLogging这个trait,方便在actor中记录日志。
class MasterActor extends Actor with akka.actor.ActorLogging{ def receive = { case _ => log.debug("debug log") log.info("info log") log.warning("warning log") log.error("error log") }}
2 通过akka.event.Logging记录日志
ActorLogging这个trait只能mix到Actor类上。
在其他非actor类上,如果能访问到actor系统,可利用它的event stream进行log。
import akka.event.Loggingval log = Logging(system.eventStream, "log prefix:")log.debug("debug log")
也可直接使用ActorSystem内置的LoggingAdapter。
val system = akka.actor.ActorSystem()system.log.error("log from ActorSystem")
3 直接通过slf4j访问logback记录日志
import org.slf4j.LoggerFactoryval log = LoggerFactory.getLogger(getClass)log.debug("Hello Logger!")
这3种log方式因为用的都是同一个logback实例,所以输出是统一的,区别是akka提供的log接口能够在记录时自动带上actor地址等信息,能极大的方便定位问题。
logback的配置
上面说了半天好像和printk的level级别没有太大差别,java的log系统最强大的地方在于它的可配置性。
如下面这个logback配置,可把ERROR及以上级别的打印输出到akka.log文件,同时把DEBUG及以上级别的打印输出到控制台。还可以配置输出格式,自动在log内容上附带一些上下文信息,如%X{akkaSource}
这个变量会解析为发起log的具体actor地址,这在系统上百万actor并发时,将成为跟踪问题重要线索。如果输出到日志文件,还可控制文件体积的最大值,选择原有内容是追加还是覆盖。
<configuration> <appender name="FILE" class="ch.qos.logback.core.FileAppender"> <file>akka.log</file> <filter class="ch.qos.logback.classic.filter.ThresholdFilter"> <level>ERROR</level> </filter> <append>false</append> <!-- encoders are assigned the type ch.qos.logback.classic.encoder.PatternLayoutEncoder by default --> <encoder> <!--<pattern>%date{ISO8601} %-5level %logger{36} %X{sourceThread} - %msg%n</pattern>--> <pattern>%date{ISO8601} %-5level %logger{36} %X{akkaSource} - %msg%n</pattern> </encoder> </appender> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%date{ISO8601} %-5level %logger{36} %X{akkaSource} - %msg%n</pattern> <!--<pattern>%X{akkaTimestamp} %-5level %logger{36} %X{akkaSource} - %msg%n</pattern>--> </encoder> </appender> <root level="DEBUG"> <appender-ref ref="STDOUT" /> <appender-ref ref="FILE" /> </root></configuration>
日志的每种输出方式对应一个appender,除了上面用到的ConsoleAppender和FileAppender,还有通过网络发送日志到远程日志服务器的Appender,用户也可自定义appender,如日志云服务提供商loggly就有自己的Appender。分布式消息系统kafka也能通过Appender直接在某个topic上接收log日志。
更多语法参考:http://logback.qos.ch/manual/appenders.html
最后推荐一篇LinkedIn工程师关于日志的长文The Log: What every software engineer should know about real-time data’s unifying abstraction,当然他说的日志已经超出了简单记录调试信息的范畴了,但其中一些观点很有意思,如数据库其实是一种特殊形式的日志,按照这个思路所谓大数据其实就是如何翻日志了,日志将是整个系统最重要的资产。
相关代码已提交github, 欢迎fork交流。