[关闭]
@xtccc 2016-11-07T08:29:38.000000Z 字数 9074 阅读 4176

Logging

给我写信
GitHub

此处输入图片的描述

开发技巧



参考:



1. Log4j2


Log4j2是在Log4j的基础上演进而来的。

1.1 直接使用Log4j2

代码

  1. package cn.gridx.log4j2.examples
  2. import org.apache.logging.log4j.LogManager;
  3. object FirstUsage {
  4. def main(args: Array[String]): Unit = {
  5. // 等价于 LogManager.getLogger(this.getClass)
  6. val logger = LogManager.getLogger()
  7. for (i <- 0 until 20) {
  8. logger.info("hello log4j2")
  9. logger.error("错误")
  10. logger.info("信息")
  11. logger.debug("好了")
  12. logger.trace("OK")
  13. }
  14. }
  15. }



依赖

  1. dependencies {
  2. compile 'org.apache.logging.log4j:log4j-api:2.6.1',
  3. 'org.apache.logging.log4j:log4j-core:2.6.1'
  4. }

运行时也需要这两个依赖包


log4j2.prperties

  1. status = info
  2. rootLogger.level = info
  3. rootLogger.appenderRef.console.ref = Console # 输出到控制台
  4. rootLogger.appenderRef.rolling.ref = RollingFile # 输出到rolling文件
  5. appender.console.type = Console
  6. appender.console.name = Console
  7. appender.console.layout.type = PatternLayout
  8. appender.console.layout.pattern = %highlight{%-8p [%d{yyyy-MM-dd HH:mm:ss}] %C %t %L: %m%n} # highlight可以对日志分颜色高亮显示
  9. appender.rolling.type = RollingFile
  10. appender.rolling.name = RollingFile
  11. appender.rolling.fileName = mylogs/etl.log
  12. appender.rolling.filePattern = mylogs/etl-%d{yyyy-MM-dd}.log.%i
  13. appender.rolling.layout.type = PatternLayout
  14. appender.rolling.layout.pattern = %-8p [%d{yyyy-MM-dd HH:mm:ss}] %C %t %L: %m%n
  15. appender.rolling.policies.type = SizeBasedTriggeringPolicy
  16. appender.rolling.policies.size=5MB
  17. appender.rolling.strategy.type = DefaultRolloverStrategy
  18. appender.rolling.strategy.max = 500



运行

  1. java -cp \
  2. build/libs/log4j2-1.0-RELEASE.jar:./conf:jars/ \
  3. cn.gridx.log4j2.examples.FirstUsage

其中, 配置文件log4j2.prperties位于目录conf中,jars目录下则包含所需的依赖包。

配置文件的语法,与Log4J是不一样的,具体参考 Configuration with Properties

注意:配置文件log4j2.prperties必须位于classpath中,这里使用参数-cp指定的。不能直接写成配置文件的路径,而是要写成配置文件所在的目录,否则会报错 :

ERROR StatusLogger No log4j2 configuration file found. Using default configuration: logging only errors to the console

也可以不把配置文件放在classpath中,而是直接放在项目的resources目录下,让它存在于构件好的JAR包中。


1.2 通过SLF4J使用Log4j2

SLF4J是一个facade,我们可以在代码中直接使用SLF4J的API,但是让实际的日志由Log4j2来处理。

使用方法很简单,在运行Java app时,将以下的几个包放入classpath中:

log4j-api-2.6.1.jar
log4j-core-2.6.1.jar
slf4j-api-1.7.21.jar
log4j-slf4j-impl-2.6.1.jar

可以参考 : Log4j 2 SLF4J Binding

这样,我们在需要输出日志时,只需要使用SLF4J的API即可:

  1. val logger = LoggerFactory.getLogger(this.getClass)


1.3 Additivity

使用additivity特性,我们可以把某个包下面的所有日志单独输出到一个指定的地方。

假设我们的需求是:

将属于cn.gridx.log4j2.examples.additivity包的程序的日志输出到一个单独的目录mylogs/additivity,输出级别为WARN;其他程序产生的日志输出到另一个目录mylogs/system,同时输出到控制台上,输出级别为INFO。


那么,我们的配置文件可以写成:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <Configuration status="WARN">
  3. <Appenders>
  4. <Console name="Console" target="SYSTEM_OUT">
  5. <PatternLayout pattern="%highlight{%-8p [%d{yyyy-MM-dd HH:mm:ss}] %c %L: %m%n}"/>
  6. </Console>
  7. <RollingFile name="RollingFile_additivity"
  8. fileName="mylogs/additivity/log.out"
  9. filePattern="mylogs/additivity/log.out.%i">
  10. <PatternLayout>
  11. <pattern>%-8p [%d{yyyy-MM-dd HH:mm:ss}] %c %L: %m%n</pattern>
  12. </PatternLayout>
  13. <SizeBasedTriggeringPolicy size="1KB" />
  14. <DefaultRolloverStrategy max="20"/>
  15. </RollingFile>
  16. <RollingFile name="RollingFile_system"
  17. fileName="mylogs/system/log.out"
  18. filePattern="mylogs/system/log.out.%i">
  19. <PatternLayout>
  20. <pattern>%-8p [%d{yyyy-MM-dd HH:mm:ss}] %c %L: %m%n</pattern>
  21. </PatternLayout>
  22. <SizeBasedTriggeringPolicy size="1KB" />
  23. <DefaultRolloverStrategy max="20"/>
  24. </RollingFile>
  25. </Appenders>
  26. <Loggers>
  27. <!-- 将包"cn.gridx.log4j2.examples.additivity"下的程序产生的所有日志写入到RollingFile_additivity中 -->
  28. <Logger name="cn.gridx.log4j2.examples.additivity" level="warn" additivity="false">
  29. <AppenderRef ref="RollingFile_additivity"/>
  30. </Logger>
  31. <!-- 将其他的程序都写入到Console和RollingFile_system -->
  32. <Root level="info">
  33. <AppenderRef ref="Console"/>
  34. <AppenderRef ref="RollingFile_system"/>
  35. </Root>
  36. </Loggers>
  37. </Configuration>



1.4 常见问题

1.4.1 Multiple SLF4J bindings

在编译和构建时,我们并不需要用到SLF4J或者log4j2的依赖(除了在编译source code中的LoggerFactoryclass需要引入org.slf4j:slf4j-log4j12:1.7.21这个依赖),因此,我们在运行app时,往往通过java -cp来传入以下4个Jar包:

log4j-api-2.6.1.jar
log4j-core-2.6.1.jar
slf4j-api-1.7.21.jar
log4j-slf4j-impl-2.6.1.jar



但经常会遇到异常:

SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/Users/SNAPSHOT-all.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/Users/lib/log4j-slf4j-impl-2.6.1.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [org.slf4j.impl.Log4jLoggerFactory]



这说明,在classpath中存在多个slf4j的实现,需要去掉不需要的。我们通过-cp传递过去的那4个Jar包肯定是正确的,说明我们自己生成的Jar包SNAPSHOT-all.jar中含有了slf4f的实现,也许是由其他依赖间接引入的。

我们在打包时,把org/slf4j这个package去掉即可。

  1. shadowJar {
  2. exclude('org/slf4j/**')
  3. }
  4. 或者
  5. jar {
  6. exclude('org/slf4j/**')
  7. }




2. Logback


Logback 是新一代的日志组件,在Akka系统中被推荐使用。

2.1 用法

添加依赖

  1. dependencies {
  2. compile 'org.slf4j:slf4j-api:1.7.10',
  3. 'ch.qos.logback:logback-classic:1.1.3',
  4. 'ch.qos.logback:logback-core:1.1.3'
  5. }



配置logback.xml
在resources目录下,或者是在classpath中,添加logback.xml文件:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <configuration>
  3. <appender name="FILE" class="ch.qos.logback.core.FileAppender">
  4. <file>log.out</file>
  5. <append>true</append>
  6. <encoder>
  7. <pattern>%date{MM/dd HH:mm:ss} %-5level %logger{35} |- %msg%n</pattern>
  8. </encoder>
  9. </appender>
  10. <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
  11. <target>System.out</target>
  12. <encoder>
  13. <pattern>%date{MM/dd HH:mm:ss} %-5level %logger{35} |- %msg%n</pattern>
  14. </encoder>
  15. </appender>
  16. <logger name="akka" level="INFO" />
  17. <root level="INFO">
  18. <appender-ref ref="FILE"/>
  19. <appender-ref ref="CONSOLE"/>
  20. </root>
  21. </configuration>



调用logback

  1. import org.slf4j.LoggerFactory
  2. val log = LoggerFactory.getLogger(this.getClass)
  3. log.debug("DEBUG")
  4. log.info("Info")
  5. log.warn("WARN")
  6. log.error("Error")


2.2 彩色日志 (highlight)

使用highlight conversion word
可以通过highlight 这个 conversion word来让不同level的日志有不同的颜色,只需要在logback.xml里面配置即可。

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <configuration>
  3. <appender name="Console" class="ch.qos.logback.core.ConsoleAppender">
  4. <encoder>
  5. <pattern>%date{MM/dd HH:mm:ss} %highlight(%-5level) %logger{35} |- %highlight(%msg%n)</pattern>
  6. </encoder>
  7. </appender>
  8. <logger name="akka" level="DEBUG" />
  9. <root level="DEBUG">
  10. <appender-ref ref="Console"/>
  11. </root>
  12. </configuration>



自定义高亮的颜色
可以通过自定义logback layout来实现自定义的颜色。

1. 创建一个Converter class

  1. package logback.layouts.myhighlight;
  2. import ch.qos.logback.classic.Level;
  3. import ch.qos.logback.classic.spi.ILoggingEvent;
  4. import ch.qos.logback.core.pattern.color.ForegroundCompositeConverterBase;
  5. import static ch.qos.logback.core.pattern.color.ANSIConstants.*;
  6. /**
  7. * Created by tao on 10/9/16.
  8. */
  9. public class MyHighLightConverter extends ForegroundCompositeConverterBase<ILoggingEvent> {
  10. @Override
  11. protected String getForegroundColorCode(ILoggingEvent event) {
  12. Level level = event.getLevel();
  13. switch (level.toInt()) {
  14. case Level.ERROR_INT:
  15. return BOLD + RED_FG;
  16. case Level.WARN_INT:
  17. return RED_FG;
  18. case Level.INFO_INT:
  19. return YELLOW_FG;
  20. default:
  21. return DEFAULT_FG;
  22. }
  23. }
  24. }

然后在logback.xml中声明并使用它:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <configuration>
  3. <conversionRule conversionWord="myhighlight"
  4. converterClass="logback.layouts.myhighlight.MyHighLightConverter"/>
  5. <appender name="Console" class="ch.qos.logback.core.ConsoleAppender">
  6. <encoder>
  7. <pattern>%date{MM/dd HH:mm:ss} %myhighlight(%-5level) %logger{35} |- %myhighlight(%msg%n)</pattern>
  8. </encoder>
  9. </appender>
  10. <logger name="akka" level="DEBUG" />
  11. <root level="DEBUG">
  12. <appender-ref ref="Console"/>
  13. </root>
  14. </configuration>




2.3 过滤器

如果我们想把满足给定条件的日志去掉不显示,则可以使用过滤器。例如,我们想把满足下列三个条件的日志屏蔽掉:

  1. 该日志由类akka.remote.ReliableDeliverySupervisor产生;
  2. 该日志的消息以“Association with”开头;
  3. 该日志的消息包含“Caused by: [Connection refused”;

则我们可以在logback.xml中添加一个filter:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <configuration>
  3. <conversionRule conversionWord="customhighlight"
  4. converterClass="com.gridx.pipeline.logback.layouts.CustomHighLightConverter"/>
  5. <appender name="FILE" class="ch.qos.logback.core.FileAppender">
  6. <file>../logs/master.log</file>
  7. <append>true</append>
  8. <encoder>
  9. <pattern>%date{MM/dd HH:mm:ss} %customhighlight(%-5level) %logger{35} |- %customhighlight(%msg%n)</pattern>
  10. </encoder>
  11. <filter class="ch.qos.logback.core.filter.EvaluatorFilter">
  12. <evaluator>
  13. <expression>
  14. if(logger.equals("akka.remote.ReliableDeliverySupervisor") &amp;&amp;
  15. formattedMessage.startsWith("Association with") &amp;&amp;
  16. formattedMessage.contains("Caused by: [Connection refused"))
  17. return true;
  18. else
  19. return false;
  20. </expression>
  21. </evaluator>
  22. <OnMatch>DENY</OnMatch>
  23. <OnDismatch>NEUTRAL</OnDismatch>
  24. </filter>
  25. </appender>
  26. <logger name="akka" level="INFO" />
  27. <root level="INFO">
  28. <appender-ref ref="FILE"/>
  29. </root>
  30. </configuration>

我们实际上是在XML中直接插入了Java代码,注意Java中的&在XML中需要用&amp;来代替。


2.4 Rolling File Appender

通过使用ch.qos.logback.core.rolling.RollingFileAppender,可以使得每个log文件的size不超过某个设置的值,如果log不断地产生,并且目前的log file size即将达到预设的大小时,新产生的日志会被写入到一个新生成的log file中。

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <configuration>
  3. <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
  4. <file>../logs/akka-master.log</file>
  5. <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
  6. <fileNamePattern>../logs/akka-master.%i.log</fileNamePattern>
  7. <minIndex>1</minIndex>
  8. <maxIndex>10</maxIndex>
  9. </rollingPolicy>
  10. <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
  11. <maxFileSize>3MB</maxFileSize>
  12. </triggeringPolicy>
  13. <encoder>
  14. <pattern>%date{MM/dd HH:mm:ss} %customhighlight(%-5level) %logger{35} |- %customhighlight(%msg%n)</pattern>
  15. </encoder>
  16. </appender>
  17. <logger name="akka" level="INFO" />
  18. <root level="INFO">
  19. <appender-ref ref="FILE"/>
  20. </root>
  21. </configuration>
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注