@xtccc
2016-11-07T08:29:38.000000Z
字数 9074
阅读 4176
开发技巧
参考:
Log4j2是在Log4j的基础上演进而来的。
代码
package cn.gridx.log4j2.examples
import org.apache.logging.log4j.LogManager;
object FirstUsage {
def main(args: Array[String]): Unit = {
// 等价于 LogManager.getLogger(this.getClass)
val logger = LogManager.getLogger()
for (i <- 0 until 20) {
logger.info("hello log4j2")
logger.error("错误")
logger.info("信息")
logger.debug("好了")
logger.trace("OK")
}
}
}
依赖
dependencies {
compile 'org.apache.logging.log4j:log4j-api:2.6.1',
'org.apache.logging.log4j:log4j-core:2.6.1'
}
运行时也需要这两个依赖包
log4j2.prperties
status = info
rootLogger.level = info
rootLogger.appenderRef.console.ref = Console # 输出到控制台
rootLogger.appenderRef.rolling.ref = RollingFile # 输出到rolling文件
appender.console.type = Console
appender.console.name = Console
appender.console.layout.type = PatternLayout
appender.console.layout.pattern = %highlight{%-8p [%d{yyyy-MM-dd HH:mm:ss}] %C %t %L: %m%n} # highlight可以对日志分颜色高亮显示
appender.rolling.type = RollingFile
appender.rolling.name = RollingFile
appender.rolling.fileName = mylogs/etl.log
appender.rolling.filePattern = mylogs/etl-%d{yyyy-MM-dd}.log.%i
appender.rolling.layout.type = PatternLayout
appender.rolling.layout.pattern = %-8p [%d{yyyy-MM-dd HH:mm:ss}] %C %t %L: %m%n
appender.rolling.policies.type = SizeBasedTriggeringPolicy
appender.rolling.policies.size=5MB
appender.rolling.strategy.type = DefaultRolloverStrategy
appender.rolling.strategy.max = 500
运行
java -cp \
build/libs/log4j2-1.0-RELEASE.jar:./conf:jars/ \
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包中。
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即可:
val logger = LoggerFactory.getLogger(this.getClass)
使用additivity特性,我们可以把某个包下面的所有日志单独输出到一个指定的地方。
假设我们的需求是:
将属于
cn.gridx.log4j2.examples.additivity
包的程序的日志输出到一个单独的目录mylogs/additivity
,输出级别为WARN;其他程序产生的日志输出到另一个目录mylogs/system
,同时输出到控制台上,输出级别为INFO。
那么,我们的配置文件可以写成:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%highlight{%-8p [%d{yyyy-MM-dd HH:mm:ss}] %c %L: %m%n}"/>
</Console>
<RollingFile name="RollingFile_additivity"
fileName="mylogs/additivity/log.out"
filePattern="mylogs/additivity/log.out.%i">
<PatternLayout>
<pattern>%-8p [%d{yyyy-MM-dd HH:mm:ss}] %c %L: %m%n</pattern>
</PatternLayout>
<SizeBasedTriggeringPolicy size="1KB" />
<DefaultRolloverStrategy max="20"/>
</RollingFile>
<RollingFile name="RollingFile_system"
fileName="mylogs/system/log.out"
filePattern="mylogs/system/log.out.%i">
<PatternLayout>
<pattern>%-8p [%d{yyyy-MM-dd HH:mm:ss}] %c %L: %m%n</pattern>
</PatternLayout>
<SizeBasedTriggeringPolicy size="1KB" />
<DefaultRolloverStrategy max="20"/>
</RollingFile>
</Appenders>
<Loggers>
<!-- 将包"cn.gridx.log4j2.examples.additivity"下的程序产生的所有日志写入到RollingFile_additivity中 -->
<Logger name="cn.gridx.log4j2.examples.additivity" level="warn" additivity="false">
<AppenderRef ref="RollingFile_additivity"/>
</Logger>
<!-- 将其他的程序都写入到Console和RollingFile_system -->
<Root level="info">
<AppenderRef ref="Console"/>
<AppenderRef ref="RollingFile_system"/>
</Root>
</Loggers>
</Configuration>
在编译和构建时,我们并不需要用到SLF4J或者log4j2的依赖(除了在编译source code中的LoggerFactory
class需要引入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去掉即可。
shadowJar {
exclude('org/slf4j/**')
}
或者
jar {
exclude('org/slf4j/**')
}
Logback 是新一代的日志组件,在Akka系统中被推荐使用。
添加依赖
dependencies {
compile 'org.slf4j:slf4j-api:1.7.10',
'ch.qos.logback:logback-classic:1.1.3',
'ch.qos.logback:logback-core:1.1.3'
}
配置logback.xml
在resources目录下,或者是在classpath中,添加logback.xml文件:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>log.out</file>
<append>true</append>
<encoder>
<pattern>%date{MM/dd HH:mm:ss} %-5level %logger{35} |- %msg%n</pattern>
</encoder>
</appender>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<target>System.out</target>
<encoder>
<pattern>%date{MM/dd HH:mm:ss} %-5level %logger{35} |- %msg%n</pattern>
</encoder>
</appender>
<logger name="akka" level="INFO" />
<root level="INFO">
<appender-ref ref="FILE"/>
<appender-ref ref="CONSOLE"/>
</root>
</configuration>
调用logback
import org.slf4j.LoggerFactory
val log = LoggerFactory.getLogger(this.getClass)
log.debug("DEBUG")
log.info("Info")
log.warn("WARN")
log.error("Error")
使用highlight conversion word
可以通过highlight 这个 conversion word来让不同level的日志有不同的颜色,只需要在logback.xml里面配置即可。
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="Console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%date{MM/dd HH:mm:ss} %highlight(%-5level) %logger{35} |- %highlight(%msg%n)</pattern>
</encoder>
</appender>
<logger name="akka" level="DEBUG" />
<root level="DEBUG">
<appender-ref ref="Console"/>
</root>
</configuration>
自定义高亮的颜色
可以通过自定义logback layout来实现自定义的颜色。
1. 创建一个Converter class
package logback.layouts.myhighlight;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.pattern.color.ForegroundCompositeConverterBase;
import static ch.qos.logback.core.pattern.color.ANSIConstants.*;
/**
* Created by tao on 10/9/16.
*/
public class MyHighLightConverter extends ForegroundCompositeConverterBase<ILoggingEvent> {
@Override
protected String getForegroundColorCode(ILoggingEvent event) {
Level level = event.getLevel();
switch (level.toInt()) {
case Level.ERROR_INT:
return BOLD + RED_FG;
case Level.WARN_INT:
return RED_FG;
case Level.INFO_INT:
return YELLOW_FG;
default:
return DEFAULT_FG;
}
}
}
然后在logback.xml中声明并使用它:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<conversionRule conversionWord="myhighlight"
converterClass="logback.layouts.myhighlight.MyHighLightConverter"/>
<appender name="Console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%date{MM/dd HH:mm:ss} %myhighlight(%-5level) %logger{35} |- %myhighlight(%msg%n)</pattern>
</encoder>
</appender>
<logger name="akka" level="DEBUG" />
<root level="DEBUG">
<appender-ref ref="Console"/>
</root>
</configuration>
如果我们想把满足给定条件的日志去掉不显示,则可以使用过滤器。例如,我们想把满足下列三个条件的日志屏蔽掉:
akka.remote.ReliableDeliverySupervisor
产生;则我们可以在logback.xml中添加一个filter:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<conversionRule conversionWord="customhighlight"
converterClass="com.gridx.pipeline.logback.layouts.CustomHighLightConverter"/>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>../logs/master.log</file>
<append>true</append>
<encoder>
<pattern>%date{MM/dd HH:mm:ss} %customhighlight(%-5level) %logger{35} |- %customhighlight(%msg%n)</pattern>
</encoder>
<filter class="ch.qos.logback.core.filter.EvaluatorFilter">
<evaluator>
<expression>
if(logger.equals("akka.remote.ReliableDeliverySupervisor") &&
formattedMessage.startsWith("Association with") &&
formattedMessage.contains("Caused by: [Connection refused"))
return true;
else
return false;
</expression>
</evaluator>
<OnMatch>DENY</OnMatch>
<OnDismatch>NEUTRAL</OnDismatch>
</filter>
</appender>
<logger name="akka" level="INFO" />
<root level="INFO">
<appender-ref ref="FILE"/>
</root>
</configuration>
我们实际上是在XML中直接插入了Java代码,注意Java中的&
在XML中需要用&
来代替。
通过使用ch.qos.logback.core.rolling.RollingFileAppender
,可以使得每个log文件的size不超过某个设置的值,如果log不断地产生,并且目前的log file size即将达到预设的大小时,新产生的日志会被写入到一个新生成的log file中。
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>../logs/akka-master.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
<fileNamePattern>../logs/akka-master.%i.log</fileNamePattern>
<minIndex>1</minIndex>
<maxIndex>10</maxIndex>
</rollingPolicy>
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<maxFileSize>3MB</maxFileSize>
</triggeringPolicy>
<encoder>
<pattern>%date{MM/dd HH:mm:ss} %customhighlight(%-5level) %logger{35} |- %customhighlight(%msg%n)</pattern>
</encoder>
</appender>
<logger name="akka" level="INFO" />
<root level="INFO">
<appender-ref ref="FILE"/>
</root>
</configuration>