目录
jdk log
package java.util.logging;
/**
* All methods on Logger are multi-thread safe.
*
* @since 1.4
*/
public class Logger {
//....
}
commons log
http://commons.apache.org/proper/commons-logging/
slf4j
Simple Logging Facade for Java
slf4j是门面模式的典型应用,引入一个适配层,由适配层决定使用哪一种日志系统,而调用端只需要做的事情就是打印日志而不需要关心如何打印日志,slf4j或者commons-logging就是这种适配层。
slf4j只是一个日志标准,并不是日志系统的具体实现。
log4j
1
Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件,甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等;我们也可以控制每一条日志的输出格式;通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。
依赖
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-log4j12 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.25</version>
</dependency>
配置文件样例
### set log levels ###
log4j.rootLogger = DEBUG,stdout,D,E
# 配置日志信息输出目的地
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
# Target是输出目的地的目标
log4j.appender.stdout.Target = System.out
# 指定日志消息的输出最低层次
log4j.appender.stdout.Threshold = INFO
# 定义名为stdout的输出端的layout类型
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
# 如果使用pattern布局就要指定的打印信息的具体格式ConversionPattern
log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss} %l%m%n
# 名字为D的对应日志处理
log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
# File是输出目的地的文件名
log4j.appender.D.File = LOG//app_debug.log
#false:默认值是true,即将消息增加到指定文件中,false指将消息覆盖指定的文件内容
log4j.appender.D.Append = true
log4j.appender.D.Threshold = DEBUG
log4j.appender.D.layout = org.apache.log4j.TTCCLayout
# 名字为E的对应日志处理
log4j.appender.E = org.apache.log4j.DailyRollingFileAppender
log4j.appender.E.File = LOG//app_error.log
log4j.appender.E.Append = true
log4j.appender.E.Threshold = ERROR
log4j.appender.E.layout = org.apache.log4j.PatternLayout
log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
logback
依赖
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>${logback.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
</dependency>
配置文件样例
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- 彩色日志格式 -->
<property name="CONSOLE_LOG_PATTERN"
value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
<!-- 彩色日志依赖的渲染类 -->
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/>
<conversionRule conversionWord="wex"
converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/>
<conversionRule conversionWord="wEx"
converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!-- encoders are assigned the type
ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
</encoder>
</appender>
<appender name="ROLLING" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/dams.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- rollover daily -->
<fileNamePattern>logs/dams-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<!-- each file should be at most 100MB, keep 5 days worth of history, but at most 20GB -->
<maxFileSize>100MB</maxFileSize>
<maxHistory>5</maxHistory>
<totalSizeCap>20GB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>%-4relative [%thread] %-5level %logger{35} - %msg %n</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="STDOUT" />
<appender-ref ref="ROLLING" />
</root>
</configuration>
log4j2
2
依赖
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.24</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-1.2-api</artifactId>
<version>2.11.0</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.11.0</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.11.0</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.11.0</version>
</dependency>
不可同时使用log4j-slf4j-impl和log4j-to-slf4j,否则会引发循环依赖。详情见log4j官方文档。
如果使用异步日志,需要加入disruptor依赖
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>3.4.2</version>
</dependency>
log4j2的AsyncLogger本身的逻辑采用了缓冲区思想,使用的是disruptor框架来实现一个环形无锁队列
配置文件样例
<?xml version="1.0" encoding="UTF-8"?>
<!-- monitorInterval配置成一个正整数,则每隔这么久的时间(秒),log4j2会刷新一次配置。如果不配置则不会动态刷新 -->
<Configuration status="INFO" monitorInterval="30">
<Properties>
<!-- 应用需要修改为合适的log路径 -->
<Property name="baseLogDir">logs</Property>
<Property name="pattern">%d{yyyyMMdd-HHmmss.SSS} [%level] %c{1} - %msg%n</Property>
</Properties>
<!-- 先定义所有的appender -->
<Appenders>
<!-- 这个输出控制台的配置 -->
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout>
<Pattern>${pattern}</Pattern>
</PatternLayout>
</Console>
<!-- 系统日志,可以作为root logger的appender,供打印一些中间件的日志 -->
<RollingRandomAccessFile name="SYS_APPENDER" fileName="${baseLogDir}/server.log"
filePattern="${baseLogDir}/server.log.%d{yyyyMMddHH}.%i.gz">
<PatternLayout>
<Pattern>${pattern}</Pattern>
</PatternLayout>
<Policies>
<SizeBasedTriggeringPolicy size="200MB" />
<TimeBasedTriggeringPolicy interval="1" modulate="true" />
</Policies>
<Filters>
<ThresholdFilter level="INFO" onMatch="ACCEPT" onMismatch="DENY" />
</Filters>
<!-- max=6标识一小时内最多产生6个日志文件 -->
<DefaultRolloverStrategy max="6">
<!-- 对于指定的路径下的指定后缀的文件,只保留1天的日志文件,那么最多会有24小时*6个日志文件 -->
<Delete basePath="${baseLogDir}" maxDepth="1">
<IfFileName glob="*.gz" />
<IfLastModified age="1d" />
</Delete>
</DefaultRolloverStrategy>
</RollingRandomAccessFile>
<!-- 应用info日志 -->
<RollingRandomAccessFile name="APPINFO_APPENDER" fileName="${baseLogDir}/appinfo.log"
filePattern="${baseLogDir}/appinfo.log.%d{yyyyMMddHH}.%i.gz">
<PatternLayout>
<Pattern>${pattern}</Pattern>
</PatternLayout>
<Policies>
<SizeBasedTriggeringPolicy size="500MB" />
<TimeBasedTriggeringPolicy interval="1" modulate="true" />
</Policies>
<Filters>
<!-- 当前appender只打印info日志,warn及以上日志忽略,由后面的appender决定是否需要打印 -->
<ThresholdFilter level="WARN" onMatch="DENY" onMismatch="NEUTRAL" />
<ThresholdFilter level="INFO" onMatch="ACCEPT" onMismatch="DENY" />
</Filters>
<!-- max=20标识一小时内最多产生20个日志文件 -->
<DefaultRolloverStrategy max="20">
<!-- 对于指定的路径下的指定后缀的文件,只保留3天的日志文件,那么最多会有3天*24小时*20个日志文件 -->
<!-- 注意应用需要根据业务需求和磁盘大小评估需要保留的日志个数,对于500M的日志文件来说,要根据应用日志的情况,观察单个日志压缩后文件大小,并计算总大小需要的空间 -->
<Delete basePath="${baseLogDir}" maxDepth="1">
<IfFileName glob="*.gz" />
<IfLastModified age="3d" />
</Delete>
</DefaultRolloverStrategy>
</RollingRandomAccessFile>
<!-- 应用错误日志 -->
<RollingRandomAccessFile name="APPERROR_APPENDER" fileName="${baseLogDir}/apperror.log"
filePattern="${baseLogDir}/apperror.log.%d{yyyyMMddHH}.%i.gz">
<PatternLayout>
<Pattern>${pattern}</Pattern>
</PatternLayout>
<Policies>
<SizeBasedTriggeringPolicy size="500MB" />
<TimeBasedTriggeringPolicy interval="1" modulate="true" />
</Policies>
<Filters>
<ThresholdFilter level="WARN" onMatch="ACCEPT" onMismatch="DENY" />
</Filters>
<!-- max=10标识一小时内最多产生10个日志文件 -->
<DefaultRolloverStrategy max="10">
<!-- 对于指定的路径下的指定后缀的文件,只保留3天的日志文件,那么最多会有3*24小时*10个日志文件 -->
<Delete basePath="${baseLogDir}" maxDepth="1">
<IfFileName glob="*.gz" />
<IfLastModified age="3d" />
</Delete>
</DefaultRolloverStrategy>
</RollingRandomAccessFile>
</Appenders>
<Loggers>
<!-- root是默认的logger,也就是公共的logger,供记录一些不常打印的系统参数或者其他组件参数 -->
<AsyncRoot level="WARN">
<AppenderRef ref="Console" />
<AppenderRef ref="SYS_APPENDER" />
</AsyncRoot>
<!-- 常打印的应用日志,建议独立配置,并采用异步模式。name根据实际的包名修改,生产环境中additivity建议设置为false以避免在root logger中重复打印 -->
<AsyncLogger name="com.unionpay" level="INFO" includeLocation="false" additivity="false">
<AppenderRef ref="APPINFO_APPENDER" />
<AppenderRef ref="APPERROR_APPENDER" />
</AsyncLogger>
</Loggers>
</Configuration>
启动
//代码上:
System.setProperty("log4j.configurationFile", System.getProperty("user.dir") + "/src/test/java/log4j2.xml");
//脚本上:
-Dlog4j.configurationFile="${path}"/conf/log4j2.xml
How do I shut down log4j2 in code?
Normally there is no need to do this manually. Each LoggerContext registers a shutdown hook that takes care of releasing resources when the JVM exits (unless system property log4j.shutdownHookEnabled is set to false). Web applications should include the log4j-web module in their classpath which disables the shutdown hook but instead cleans up log4j resources when the web application is stopped.
However, if you need to manually shut down Log4j, you can do so as in the below example. Note that there is an optional parameter for specifying which LoggerContext to shut down.
<?xml version="1.0" encoding="UTF-8"?>
<configuration status="info" shutdownHook="disable">
<!-- ... -->
</configuration>
import org.apache.logging.log4j.LogManager;
// ...
LogManager.shutdown();
配置文件读取变量
- date – 可以按照特定的格式插入当前日期/时间
- env–可以读取系统的环境变量
<File name="log" fileName="${env:MYAPP_HOME}/log/test.log" append="false">
<PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/>
</File>
- jvmrunargs – 可以读取jvm启动时设定的运行参数,但是不能读main函数的参数,在Android系统上也不能用
- sys – 读取的是 system properties的属性
<File name="log" fileName="${sys:MYAPP_HOME}/log/test.log" append="false">
<PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/>
</File>
异步日志输出
3
log4j2的异步形式大概分为两种:全异步和同步异步混合
Asynchronous Loggers for Low-Latency Logging
-
全异步
实现方式:将系统属性log4j2.contextSelector设置 为
org.apache.logging.log4j.core.async.AsyncLoggerContextSelector
,即
System.setProperty("log4j2.contextSelector", "org.apache.logging.log4j.core.async.AsyncLoggerContextSelector");
或者在启动时设置:
-Dlog4j2.contextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector
-
同步异步混合
相比起全异步,混合异步可能会花费更多的性能
默认情况下,异步日志记录器不会将location信息传递给I/O线程,如果你的layouts或custom过滤器需要location信息,你需要在所有相关日志记录器(包括根日志记录器)的配置中设置
includeLocation=true
<AsyncLogger name="com.foo.Bar" level="trace" includeLocation="true">
<AppenderRef ref="RandomAccessFile"/>
</AsyncLogger>
可以仅error日志输出location信息