Logback学习笔记
Logback的目标是作为Log4j的继任者。Logback使用模块化的架构,主要分为三个部分:logback-core、logback-classic、logback-access。
模块logback-classic作为Log4j的继任者,它做了根本性的改进。该模块实现了SLF4J API,你可以方便的切换到其它日志框架。模块logback-access和Servlet容器集成,提供HTTP访问日志的功能。在logback-core的基础上,你可以方便的轻易的构建自己的模块。
Logback比起Log4j的优势包括:
- 在某些关键的执行路径上,提供高于Log4j十倍的性能,同时具有比Log4j更低的内存占用
- logback-classic直接实现了SLF4J API,而Log4J需要经过适配才能使用SLF4J
- 支持XML和Groovy两种配置风格
- 在配置文件修改后,实时的重新加载,不需要重启应用
- 优雅的重IO错误中恢复:FileAppender及其子类(例如RollingFileAppender)能在文件服务器宕机恢复后,自动重连,不需要重启应用
- 自动移除旧日志归档,你可以设置TimeBasedRollingPolicy、SizeAndTimeBasedFNATP的maxHistory属性,以控制日志文件的数量
- 自动压缩日志归档文件,RollingFileAppender 能够在Rollover时自动、异步的压缩日志文件
- Prudent Mode:支持多个JVM中运行的FileAppender安全的向同一个文件进行写入
- 配置文件的条件式处理,你可以在配置文件中添加 <if>等元素
- 过滤器,Logback提供比Log4j更加强大的过滤器机制
- SiftingAppender,可以基于任何运行时属性来分离日志。例如,根据用户会话来分割日志,每个会话产生一个日志文件
- 异常栈增强 —— 支持软件包信息,例如 struts-1.2.9.jar:1.2.9
1 2 3 4 5 6 7 8 9 10 |
<dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.25</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.3</version> </dependency> |
你可以通过编程,或者指定配置文件,来配置Logback。默认配置行为如下:
- 尝试寻找类路径下的 logback-test.xml
- 如果找不到,尝试寻找logback.groovy
- 如果找不到,尝试寻找logback.xml
- 尝试寻找类路径下META-INF/services/ch.qos.logback.classic.spi.Configurator指定的配置类,获得的全限定名称用于配置Logback
- 如果以上步骤都失败,使用 BasicConfigurator,直接打印到控制台
注意,配置文件位置可以通过系统属性 -Dlogback.configurationFile=/path/to/config.xml指定。
这是根配置元素:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
<!-- debug设置为true,打印Logback状态信息 scan设置为true,自动扫描配置文件的修改,并重新载入 scanPeriod 默认每分钟扫描一次,如果不指定单位,则单位ms packagingData="true" 在栈信息中附加包信息,1.1.4以后默认禁用 --> <configuration debug="true" scan="true" scanPeriod="30 seconds" packagingData="true"> <!-- 设置LoggerContext名称 --> <contextName>logbacktesst</contextName> <!-- 定义属性 --> <property name="USER_HOME" value="/home/alex"/> <!-- 从JNDI获取属性 --> <insertFromJNDI env-entry-name="java:comp/env/userhome" as="USER_HOME"/> <!-- 将当前时间定义为属性--> <timestamp key="bySecond" datePattern="yyyyMMdd'T'HHmmss"/> <!-- 下面的子元素单独阐述 --> <include /> <if /> <appender /> <logger /> </configuration> |
用于包含其它文件中的配置信息:
1 |
<include file="http://dev.gmem.cc/logback/basic.xml"/> |
被包含的文件,格式必须如下:
1 2 3 4 |
<!-- 必须以include为根元素 --> <included> <appender name="includedConsole" class="ch.qos.logback.core.ConsoleAppender" /> </included> |
用于条件化配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<if condition='property("HOSTNAME").contains("zircon")'> <then> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%d %-5level %logger{35} - %msg %n</pattern> </encoder> </appender> <root> <appender-ref ref="STDOUT"/> </root> </then> <else> </else> </if> |
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<!-- 根日志器 --> <root level="debug"> <!-- 设置为OFF则关闭根日志器 --> <!-- 每个日志器都可以引用多个Appender --> <appender-ref ref="STDOUT"/> <appender-ref ref="FILE"/> </root> <!-- 自定义日志器,以及其最低级别--> <logger name="cc.gmem" level="INFO"/> <!-- 默认情况下,每个日志器会调用自己的Appender,也会使用所有祖先的Appender --> <!-- 设置additivity为false,则不使用任何祖先Appender --> <logger name="cc.gmem.study.logback.LogbackTest" level="DEBUG" additivity="false"> <appender-ref ref="FILE"/> </logger> |
定义日志追加器,负责把LogEvent输出到目的地。
1 2 3 4 5 6 7 8 9 10 |
<!-- 输出到控制台的Appender --> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> </encoder> <!-- 也可以输出到System.err --> <target>System.out</target> <!-- 设置为true则可以在Windows下支持彩色输出,需要依赖org.fusesource.jansi:jansi:1.9 --> <withJansi>false</withJansi> </appender> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<!-- 输出到文件的Appender --> <appender name="FILE" class="ch.qos.logback.core.FileAppender"> <!-- 可以引用定义的属性,进行变量替换 --> <file>${USER_HOME}logbacktest.log</file> <!-- 唯一性文件名 --> <file>log-${bySecond}.txt</file> <encoder> <pattern>%date %level [%thread] %logger{10} [%file:%line] %msg%n</pattern> </encoder> <!-- 默认true,是否附加到文件的尾部,设置为false则文件的原有内容被清除 --> <append>true</append> <!-- 默认true,设置为false可以大大提升日志吞吐量,但是程序崩溃可能丢失日志 --> <immediateFlush>false</immediateFlush> <!-- 如果设置为true则日志被安全的写入文件,即使存在其它JVM的FileAppender在使用此文件 --> <prudent>false</prudent> </appender> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
<!-- 滚动文件Appender --> <appender name="RFILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>logFile.log</file> <!-- 滚动策略,基于时间滚动 --> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <!-- 文件名模式: %d{yyyy-MM-dd-HH, UTC} %d{yyyy-MM-dd} --> <!-- 每日滚动出一个文件--> <fileNamePattern>logFile.%d{yyyy-MM-dd}.log</fileNamePattern> <!-- 最多保留的日志文件数量,老的日志自动移除 --> <!-- 保留30天的日志历史 --> <maxHistory>30</maxHistory> <!-- 日志占用空间限制,如果超过限制,从最老的文件开始删除,删除是异步的 --> <!-- 最多占用3GB --> <totalSizeCap>3GB</totalSizeCap> <!-- 如果设置为true,则在启动时移除归档日志 --> <cleanHistoryOnStart>false</cleanHistoryOnStart> </rollingPolicy> <!-- 滚动策略,基于时间 + 尺寸 --> <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"> <!-- 每日滚定,%i为文件计数,初始值0 --> <fileNamePattern>mylog-%d{yyyy-MM-dd}.%i.txt</fileNamePattern> <!-- 单个文件最多100MB,保留60天历史,最大占用空间20GB --> <maxFileSize>100MB</maxFileSize> <maxHistory>60</maxHistory> <totalSizeCap>20GB</totalSizeCap> </rollingPolicy> <!-- 触发策略:负责确定何时执行rollover --> <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy"> <!-- 如果当前文件超过5MB,则触发Appender进行是否需要rollover的判断 --> <maxFileSize>5MB</maxFileSize> </triggeringPolicy> <encoder> <pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern> </encoder> </appender> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<!-- 连接到远程日志服务器,推送日志 --> <!-- 通过套接字发送日志 --> <appender name="SOCKET" class="ch.qos.logback.classic.net.SocketAppender"> <remoteHost>${host}</remoteHost> <port>${port}</port> <reconnectionDelay>10000</reconnectionDelay> <includeCallerData>${includeCallerData}</includeCallerData> </appender> <!-- 通过SSL发送日志 --> <appender name="SOCKETSSL" class="ch.qos.logback.classic.net.SSLSocketAppender"> <remoteHost>${host}</remoteHost> <port>${port}</port> <reconnectionDelay>10000</reconnectionDelay> <ssl> <trustStore> <location>${truststore}</location> <password>${password}</password> </trustStore> </ssl> </appender> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<!-- 创建一个日志服务器并监听,向所有连接到此服务器的客户端推送日志 --> <!-- 在客户端连接之前产生的日志,简单的丢弃 --> <appender name="SERVER" class="ch.qos.logback.classic.net.server.ServerSocketAppender"> <port>${port}</port> <includeCallerData>${includeCallerData}</includeCallerData> </appender> <appender name="SERVERSSL" class="ch.qos.logback.classic.net.server.SSLServerSocketAppender"> <port>${port}</port> <includeCallerData>${includeCallerData}</includeCallerData> <ssl> <keyStore> <location>${keystore}</location> <password>${password}</password> </keyStore> </ssl> </appender> |
1 2 3 4 5 6 7 8 9 |
<!-- 将日志记录到数据库 --> <appender name="DB" class="ch.qos.logback.classic.db.DBAppender"> <connectionSource class="ch.qos.logback.core.db.DriverManagerConnectionSource"> <driverClass>com.mysql.jdbc.Driver</driverClass> <url>jdbc:mysql://host_name:3306/datebase_name</url> <user>username</user> <password>password</password> </connectionSource> </appender> |
1 2 3 4 5 6 |
<!-- 写到Syslog中 --> <appender name="SYSLOG" class="ch.qos.logback.classic.net.SyslogAppender"> <syslogHost>remote_home</syslogHost> <facility>AUTH</facility> <suffixPattern>[%thread] %logger %msg</suffixPattern> </appender> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<!-- 根据特定的运行时属性,对日志文件进行分割 --> <appender name="SIFT" class="ch.qos.logback.classic.sift.SiftingAppender"> <!-- 根据运行时属性userid进行分割 --> <discriminator class="ch.qos.logback.classic.sift.MDCBasedDiscriminator"> <key>userid</key> <defaultValue>unknown</defaultValue> </discriminator> <sift> <appender name="FILE-${userid}" class="ch.qos.logback.core.FileAppender"> <!-- 运行时属性可以随意引用 --> <file>${userid}.log</file> <append>false</append> <layout class="ch.qos.logback.classic.PatternLayout"> <pattern>%d [%thread] %level %mdc %logger{35} - %msg%n</pattern> </layout> </appender> </sift> </appender> |
配合此Appender的Java代码:
1 2 3 4 |
logger.debug("Application started"); // 设置运行时属性 MDC.put("userid", "Alice"); logger.debug("Alice says hello"); |
此日志追加器能够异步的记录ILoggingEvent,它本身仅仅作为事件分发器,必须引用其它Appender负责实际的日志记录。
AsyncAppender在BlockingQueue中对日志进行缓冲,它会创建一个专门的工作线程,从队列的头部获取日志,并分发给单个子Appender。默认情况下,队列用量超过80%后,AsyncAppender会丢弃TRACE, DEBUG,INFO级别的日志信息。
队列的默认容量为256,如果队列满了,调用logger.***的应用程序线程会阻塞。
你必须在关闭JVM进程之前,明确的关闭AsyncAppender,以防止日志事件丢失(未来得及分发)。你可以显式调用:
1 2 |
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory(); loggerContext.stop(); |
或者使用Java的ShutdownHook:
1 2 3 |
<configuration debug="true"> <shutdownHook class="ch.qos.logback.core.hook.DelayingShutdownHook" /> </configuration> |
AsyncAppender配置示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender"> <!-- 队列容量 --> <queueSize>256</queueSize> <!-- 仅剩多少容量时,开始丢弃不重要的日志,设置为0则不丢弃 --> <discardingThreshold>20</discardingThreshold> <!-- 当LoggerContext停止时,AsyncAppender.stop()等待多长时间,让日志被刷出到appender-ref --> <maxFlushTime>1000</maxFlushTime> <!-- 如果设置为true,队列满了会直接丢弃信息,而不是阻塞 --> <neverBlock>false</neverBlock> <!-- 实际负责日志记录的Appender --> <appender-ref ref="FILE" /> </appender> |
此接口负责格式化日志事件:
1 2 3 4 5 6 7 8 |
public interface Layout<E> extends ContextAware, LifeCycle { String doLayout(E event); String getFileHeader(); String getPresentationHeader(); String getFileFooter(); String getPresentationFooter(); String getContentType(); } |
你可以注册自己实现的Layout:
1 2 3 4 5 |
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder"> <layout class="cc.gmem.study.logback.JsonLayout" /> </encoder> </appender> |
这是一个灵活的Layout,通过一个字符串指定输出格式。
转换符 | 说明 | ||
c{length} lo{length} logger{length} |
原始日志事件的Logger名称
|
||
C{length} class{length} |
调用日志的代码所属类的全限定名称 | ||
contextName cn |
LoggerContext的名称 | ||
d{pattern} date{pattern} d{pattern, timezone} date{pattern, timezone} |
日志事件的名称:
|
||
F / file | 调用日志的代码的Java源文件名称 | ||
caller{depth} | 调用日志的代码的位置信息 | ||
L / line | 调用日志的代码的行号 | ||
m / msg / message | 日志内容 | ||
M / method | 调用日志的代码所在的方法 | ||
n | 依赖于平台的换行符 | ||
p / le / level | 日志级别 | ||
r / relative | 从JVM启动到日志发生时,流逝的毫秒数 | ||
t / thread | 产生日志事件的线程名 | ||
X{key:-defaultVal} mdc{key:-defaultVal} |
产生日志线程关联的关联MDC(mapped diagnostic context)中的值 | ||
ex{depth} exception{depth} throwable{depth} |
输出关联到日志事件的调用栈信息 | ||
xEx{depth} xException{depth} xThrowable{depth} |
类似上面,外加包信息 |
格式化修饰符,下面是一些修是%logger的例子:
修饰符 | 说明 |
%20logger | 如果宽度不足20,左侧补空白 |
%-20logger | 如果宽度不足20,右侧补空白 |
%.30logger | 如果宽度超过30,从左侧截断 |
%.-30logger | 如果宽度超过30,从右侧截断 |
%20.30logger | 如果宽度不足20,左侧补空白;如果宽度超过30,从左侧截断 |
支持颜色代码:%black", "%red", "%green","%yellow","%blue", "%magenta","%cyan", "%white", "%gray", "%boldRed","%boldGreen", "%boldYellow", "%boldBlue", "%boldMagenta""%boldCyan", "%boldWhite" 和 "%highlight" 。
Pattern示例:
1 2 3 4 |
[%thread] %highlight(%-5level) %cyan(%logger{15}) - %msg %n # 输出 [main] WARN c.l.TrivialMain - a warning message 0 [main] DEBUG c.l.TrivialMain - hello world number1 |
ch.qos.logback.access.PatternLayout扩展了以下转换符:
转换符 | 说明 |
a / remoteIP | 远程IP地址 |
A / localIP | 本地IP地址 |
b / B / bytesSent | 响应长度 |
h / clientHost | 远程主机名 |
H / protocol | 请求协议 |
reqParameter{paramName} | 输出指定请求参数 |
reqAttribute{attributeName} | 输出指定的请求属性 |
i{header} / header{header} | 输出指定请求头 |
responseHeader{header} | 输出指定响应头 |
reqCookie{cookie} | 输出指定Cookie值 |
m / requestMethod | 请求的HTTP方法 |
r / requestURL | 请求URL |
s / statusCode | 状态码 |
D / elapsedTime | 处理请求消耗的时间,单位毫秒 |
T / elapsedSeconds | 处理请求消耗的时间,单位秒 |
t / date | 输出时间,例如%t{dd MMM yyyy ;HH:mm:ss,SSS} |
u / user | 输出远程用户 |
q / queryString | 输出查询字符串,包括前缀? |
U / requestURI | 请求的URI |
S / sessionID | SessionID |
v / server | 服务器名称 |
I / threadName | 处理请求的线程名称 |
localPort | 本地端口 |
编码器,负责将日志事件转换为字节数组,并写入到输出流。
JSON格式编码器,用于配合Logstash,示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <encoder class="net.logstash.logback.encoder.LogstashEncoder"> <!-- 对输出的JSON进行格式化 --> <jsonGeneratorDecorator class="net.logstash.logback.decorate.PrettyPrintingJsonGeneratorDecorator"/> <!-- 时间输出为UNIX时间戳 --> <timestampPattern>[UNIX_TIMESTAMP_AS_NUMBER]</timestampPattern> <!-- 对字段进行重命名 --> <fieldNames> <timestamp>instant</timestamp> <logger>loggerName</logger> <thread>thread</thread> <stackTrace>thrown</stackTrace> <!-- 丢弃字段 --> <version>[ignore]</version> <levelValue>[ignore]</levelValue> </fieldNames> </encoder> <target>System.out</target> </appender> |
当前版本可以和JDK7 + Tomcat 7.0.62配合使用:
- 将logback-access.jar与logback-core.jar复制到$TOMCAT_HOME/lib/目录下
- 修改配置文件:
123<!-- 添加在Engine或Host元素内部 --><!-- quiet进制打印默认的状态信息 --><ValveclassName="ch.qos.logback.access.tomcat.LogbackValve" quiet="true" filename="logback-access.xml"/> -
logback默认会在$TOMCAT_HOME/conf下查找配置文件logback-access.xml,配置示例:
123456789<configuration><statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener" /><appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"><encoder><pattern>%h %l %u %user %date "%r" %s %b</pattern></encoder></appender><appender-ref ref="STDOUT" /></configuration>
重启Tomcat即可。
Leave a Reply