前情
在开发过程中,总会遇到很多的bug,有一些bug可以通过本地复现,debug来查找原因。但是大部分的bug总是突然出现,并且难以在本地环境重现。这个时候日志就是最好的查找问题的工具。当然,前提是,系统开发过程中有留有‘痕迹’。
虽然日志本事有分为几级:TRACE, DEBUG, INFO, WARN, ERROR等。当然有些日志工具的级别更多。但是使用过程中,想要对日志追根溯源,大量的日志合并的一起,也是不容易查看的。这个时候对日志文件的处理就显得比较重要了。
以下是介绍logback使用的整理,以及示例。
logback简介
logback 继承自 log4j,它建立在有十年工业经验的日志系统之上。它比其它所有的日志系统更快并且更小,包含了许多独特并且有用的特性。
他主要分为下面三个部分:
- logback-core:其它两个模块的基础模块
- logback-classic:它是log4j的一个改良版本,同时它完整实现了slf4j API使你可以很方便地更换成其它日志系统如log4j或JDK14 Logging
- logback-access:访问模块与Servlet容器集成提供通过Http来访问日志的功能
配置详解
logback可以通过编程或者配置 XML 脚本或者 Groovy 格式的方式来配置 logback。对于已经使用 log4j 的用户可以通过这个工具(http://logback.qos.ch/translator/)来把 log4j.properties 转换为 logback.xml。
Logback 构建在三个主要的类上:Logger,Appender 和 Layouts。这三个不同类型的组件一起作用能够让开发者根据消息的类型以及日志的级别来打印日志。
Logger
一个 Logger 被当作为一个实体,它们的命名是大小写敏感的,并且遵循以下规则:
命名层次结构
如果一个 logger 的名字加上一个 . 作为另一个 logger 名字的前缀,那么该 logger 就是另一个 logger 的祖先。如果一个 logger 与另一个 logger 之间没有其它的 logger ,则该 logger 就是另一个 logger 的父级。
例如:名为 com.foo 的 logger 是名为 com.foo.Bar 的 logger 的父级。名为 java 的 logger 是名为 java.util 的父级,是名为 java.util.Vector 的祖先。
root 是 logger 层次结构的最高层。
每个Logger有一个需要打印日志的等级,在logback中日志的等级采用继承的机制,即子logger将继承父logger的打印等级。root的等级默认为debug。
也就是说,在所有log没有配置打印等级的时候,默认都是debug的使用等级。
Appender
logback 允许日志在多个地方进行输出。站在 logback 的角度来说,输出目的地叫做 appender。appender 包括console、file、remote socket server、MySQL、PostgreSQL、Oracle 或者其它的数据库、JMS、remote UNIX Syslog daemons 中。
一个 logger 可以有多个 appender。
对于给定的 logger,每一个允许输出的日志都会被转发到该 logger 的所有 appender 中去。换句话说,appender 从 logger 的层级结构中去继承叠加性。
例如:如果 root logger 添加了一个 console appender,所有允许输出的日志至少会在控制台打印出来。如果再给一个 logger 添加了一个 file appender和 console appender,那么这个 logger 以及他的子集 都可以在文件和控制台打印日志。并且 控制台的日志会打印两遍。
这是因为所有的logger都是 root的子集,可以通过设置这个logger的 additivity = false 来改写默认的设置,这样 appender 将不再具有叠加性。
layout
通常,用户既想自定义日志的输出地,也想自定义日志的输出格式。通过给 appender 添加一个 layout 可以做到。layout 的作用是将日志格式化,而 appender 的作用是将格式化后的日志输出到指定的目的地。
PatternLayout 能够根据用户指定的格式来格式化日志,类似于 C 语言的 printf 函数。
对应在xml中的pattern标签。
pattern支持的格式如下图所示:
例:%d{yyyy-MM-dd HH:mm:ss} %p [%F:%L] [%t] - <%m>%n
日志时间 日志级别 [java文件:行号] [线程名] - <日志信息>\r\n(换行符)
示例
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true">
<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
<Target>System.out</Target>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} %p [%F:%L] [%t] - <%m>%n
</pattern>
</encoder>
</appender>
<appender name="out"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<append>true</append>
<!--输出文件地址-->
<file>logs/service.log</file>
<encoder>
<!--控制日志的输出格式-->
<pattern>%d{yyyy-MM-dd HH:mm:ss} %p [%F:%L] [%t] - <%m>%n
</pattern>
</encoder>
<!--输出文件切割方式-->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>logs/service.log.%d{.yyyy-MM-dd}</fileNamePattern>
<maxHistory>6</maxHistory>
</rollingPolicy>
</appender>
<appender name="sdk"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<append>true</append>
<file>logs/sdk.log</file>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} %p [%F:%L] [%t] - <%m>%n
</pattern>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>logs/sdk.log.%d{.yyyy-MM-dd}</fileNamePattern>
<maxHistory>6</maxHistory>
</rollingPolicy>
</appender>
<logger name="com.xx.xxx" level="DEBUG" additivity="false">
<appender-ref ref="sdk" />
<appender-ref ref="stdout" />
</logger>
<logger name="com.xx" level="DEBUG">
<appender-ref ref="out" />
</logger>
<root level="INFO">
<appender-ref ref="stdout" />
</root>
</configuration>
以上是测试时使用的日志配置:
使用的场景是,引入一个SDK(com.xx.xxx)来开发自己的程序,SDK在执行中需要进行交互,因此需要DEBUG日志来监控交互状态。但是在测试时,不想影响开发程序日志的查看。
名称为stdout的appender 是在控制台的输出,例如eclipse的控制台。
输出的格式为:日志时间 日志级别 [java文件:行号] [线程名] - <日志信息>\r\n(换行符)
名称为out的appender,是一个文件日志输出,输出文件地址为{程序目录}/logs/service.log,
输出的格式为:日志时间 日志级别 [java文件:行号] [线程名] - <日志信息>\r\n(换行符)
按照时间来切割文件,当天的日志只放在service.log 第二天将前天的文件复制到/logs/service.log…{时间}
名称为sdk的appender,是一个文件日志输出,输出文件地址为{程序目录}/logs/sdk.log,
输出的格式为:日志时间 日志级别 [java文件:行号] [线程名] - <日志信息>\r\n(换行符)
按照时间来切割文件,当天的日志只放在sdk.log 第二天将前天的文件复制到/logs/sdk.log…{时间}
名称为com.xx.xxx的Logger 指定以com.xx.xxx下的类(sdk)打印的DEBUG以上等级的日志都会输出到sdk 和stdout 的appender
即sdk.log的日志文件,和系统控制台.且由于配置additivity=“false”,此logger所属的日志将不再在父logger中打印。即root下的out appender不会 打印出sdk的日志。
名称为com.xx的Logger 指定以com.xx下的类(sdk)打印的DEBUG以上等级的日志都会输出到out 和stdout 的appender
即service.log的日志文件,由于未配置additivity,默认为true。本 logger的日志可以打印在父logger的append里。
即stdout appender还是会打印出com.xx下的日志。
名称为root 的logget 指定其他所有的INFO以上的日志 输出到stdout 的appender中。
最后的显示结果是:
- sdk.log 将打印sdk中的DEBUG以上等级的日志。
- service.log 将打印com.xx(除sdk)的DEBUG以上等级的日志。
- 控制台 将打印所有(除sdk)的INFO以上的日志
也可以将不同等级的日志输出到对应的文档,要根据实际开发情况来进行配置。