logback基础知识
logback介绍
参考:https://logback.qos.ch/manual/introduction.html
Logback是由log4j创始人Ceki Gülcü设计的另一个开源日志组件,官方网站: http://logback.qos.ch。The logback manual链接地址为:https://logback.qos.ch/manual/index.html
它当前分为下面几个模块:
logback-core:其它两个模块的基础模块
logback-classic:它是log4j的一个改良版本,同时它完整实现了slf4j API,使你可以很方便地在logback和其它日志系统如log4j或JDK中的java.util.logging进行切换。
logback-access:访问模块与Servlet容器(如tomcat和jetty)集成,提供通过Http来访问日志的功能。
注意:你可以自己在logback-core之上轻松构建自己的模块
日志API比我们使用System.out.println高级在哪里呢?
就比如打印到控制台。通过logback,可以控制消息的格式、消息记录的位置,并且可以根据消息类型和级别来记录消息。如果我不想看到某些级别的消息,直接设置一下就可以了。以上这些都是System.out.println做不到的。
logback的结构
参考:https://logback.qos.ch/manual/architecture.html
Logback基于三个主要类:Logger,Appender和Layout。 这三种类型的组件协同工作,使开发人员能够根据消息类型和级别记录消息,并在运行时控制这些消息的格式以及报告的位置。
logger context
logger context:各个logger 都被关联到一个 LoggerContext,LoggerContext负责制造logger,也负责以树结构排列各logger。其他所有logger也通过org.slf4j.LoggerFactory 类的静态方法getLogger取得。 getLogger方法以 logger名称为参数。用同一名字调用LoggerFactory.getLogger 方法所得到的永远都是同一个logger对象的引用。
logger
logger:用来设置某一个包或具体的某一个类的日志打印级别、以及指定<appender>。
作为日志的记录器,把它关联到应用的对应的context上后,主要用于存放日志对象,也可以定义日志类型、级别。
<logger>有一个name属性,一个可选的level和一个可选的addtivity属性,可以包含零个或多个<appender-ref>元素,标识这个appender将会添加到这个logger中。
root
子节点<root>:它也是<logger>元素,但是它是根logger,是所有<logger>的上级。只有一个level属性,因为name已经被命名为"root",且已经是最上级了。
level: 用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL和OFF,不能设置为INHERITED或者同义词NULL。 默认是DEBUG。
appender
appender:主要用于指定日志输出的目的地,目的地可以是控制台、文件、远程套接字服务器、 MySQL、 Oracle和其他数据库、 JMS和远程UNIX Syslog daemons。
给定logger的每个启用的日志记录请求都将转发到该logger中的所有appender以及层次结构中较高的appender。例如,如果将控制台appender添加到根记录器,则所有启用的日志记录请求将至少在控制台上打印。 如果另外将文件appender添加到记录器(例如L),则对L和L的子项启用的记录请求将打印在文件和控制台上。 通过将logger的additivity标志设置为false,可以覆盖此默认行为,以便不再添加appender累积。
有两个必要属性name和class。name指定appender名称,class指定appender的全限定名。
ConsoleAppender 把日志输出到控制台
FileAppender:将日志输出到文件,适合java se程序
RollingFileAppender:滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将日志记录到其他文件。适合java web程序
layouts
用户通常不仅要定制输出目的地,还要定制输出格式。 这是通过将layouts与appender相关联来实现的。 layouts负责根据用户的意愿格式化日志记录请求,而appender负责将格式化的输出发送到其目的地。 PatternLayout是标准logback分发的一部分,它允许用户根据类似于C语言printf函数的转换模式指定输出格式。
PatternLayoutEncoder
logback日志记录几个原则
遵循继承体系
1) 如果给定的logger没有分配级别,那么它将从具有指定级别的最近祖先继承一个级别
2) 如果所有logger都有一个指定的级别值,级别继承不起作用。
为logger分配级别
logger 可以被分配级别:级别包括:TRACE、DEBUG、INFO、WARN 和 ERROR,定义于ch.qos.logback.classic.Level类。如果 logger没有被分配级别,那么它将从有被分配级别的最近的祖先那里继承级别。root logger 默认级别是 DEBUG。
打印方法决定记录请求的级别
打印方法决定记录请求的级别:例如,如果 LOGGER 是一个 logger 实例,那么,语句 LOGGER.info("..")是一条级别为 INFO的记录语句。记录请求的级别在高于或等于其 logger 的有效级别时被称为被启用,否则,称为被禁用。记录请求级别为 p,其 logger的有效级别为 q,只有则当 p>=q时,该请求才会被执行。该规则是 logback 的核心。级别排序为: TRACE < DEBUG < INFO < WARN < ERROR。
实际上,logback输出日志是从子节点开始,子节点如果有输出源直接输入。如果无,判断配置的addtivity,是否向上级传递,即是否向root传递,传递则采用root的输出源,否则不输出日志。
例如假设我们不再有兴趣查看属于“chapters.configuration”包的任何组件的任何DEBUG消息。
代码
在chapters.configuration类中LOGGER.debug(‘xxx’);
配置文件
<logger name="chapters.configuration" level="INFO"/>
注意,基本选择规则取决于被调用的LOGGER实例的有效级别,而不是附加了appender的logger的级别。 logback将首先确定是否启用了日志记录语句,如果启用,它将调用logger层次结构中找到的appender,而不管它们的级别如何。
配置文件格式
参考:https://logback.qos.ch/manual/configuration.html
配置configuration
配置文件的基本结构可以描述为<configuration>元素,包含零个或多个<appender>元素,后跟零个或多个<logger>元素,后跟最多一个<root>元素。
根节点<configuration>,包含下面三个属性
scan: 当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true。
scanPeriod: 设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。
debug: 当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。
Logback可以使用内置状态系统报告有关其内部状态的信息。 在logback的生命周期中发生的重要事件可以通过名为StatusManager的组件进行访问。 暂时,让我们通过调用StatusPrinter类的静态print()方法指示logback打印其内部状态。
// print internal state
LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
StatusPrinter.print(lc);
可以看到其内部执行情况
12:49:22.203 [main] DEBUG chapters.introduction.HelloWorld2 - Hello world.
12:49:22,076 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback.groovy]
12:49:22,078 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback-test.xml]
12:49:22,093 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback.xml]
12:49:22,093 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Setting up default configuration.
配置property
子节点<property>
将默认配置文件的位置指定为系统属性
<property resource="db.properties" />
配置contextName
子节点<contextName>:用来设置上下文名称,每个logger都关联到logger上下文,默认上下文名称为default。但可以使用<contextName>设置成其他名字,用于区分不同应用程序使用同一日志文件(在pattern中使用%contextName)的记录。
配置logger
使用<logger>元素配置记录器。 <logger>元素只接受一个必需的name属性,一个可选的level属性和一个可选的additivity属性,允许值为true或false。 level属性的值允许一个不区分大小写的字符串值TRACE,DEBUG,INFO,WARN,ERROR,ALL或OFF。
<logger>元素可以包含零个或多个<appender-ref>元素; 这样引用的每个appender都被添加到指定的logger中。
<root>元素配置根记录器。 它支持单个属性,即level属性。 它不允许任何其他属性,因为additivity标志不适用于根记录器。 此外,由于根记录器已被命名为“ROOT”,因此它也不允许使用name属性。 level属性的值可以是不区分大小写的字符串TRACE,DEBUG,INFO,WARN,ERROR,ALL或OFF之一。
与<logger>元素类似,<root>元素可以包含零个或多个<appender-ref>元素; 这样引用的每个appender都被添加到根记录器中。
配置appender
appender使用<appender>元素配置,该元素采用两个必需属性name和class。 name属性指定appender的名称,而class属性指定要实例化的appender类的完全限定名称。 <appender>元素可以包含零个或一个<layout>元素,零个或多个<encoder>元素以及零个或多个<filter>元素。
LevelFilter根据精确的级别匹配过滤事件。 如果事件的级别等于配置的级别,则筛选器接受或拒绝该事件,具体取决于onMatch和onMismatch属性的配置。
例如下面这个appender,只显示error信息
<appender name="ERROR-OUT" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--日志文件输出的文件名 -->
<fileNamePattern>${log.path}/%d{yyyyMMdd}/monitor-web.%d{yyyy-MM-dd}.error.log</fileNamePattern>
<!--日志文件保留天数 -->
<MaxHistory>30</MaxHistory>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} -
%msg%n
</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
默认情况下,appender是累积的:记录器将记录到自身附加的appender(如果有的话)以及附加到其祖先的所有appender。 因此,将相同的appender附加到多个记录器将导致日志记录输出重复。
解决方法为将指定logger的additivity标志设置为false,以使其日志记录输出将发送到名为FILE的appender,但不会发送到层次结构中较高的任何appender。
springmvc中实际使用
1. 配置maven依赖
注意:Logback-classic模块还需要存在slf4j-api.jar和logback-core.jar。而slf4j是一个单独的项目。
<properties>
<logback.version>1.1.3</logback.version>
</properties>
<!-- logback日志配置开始 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>${logback.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-access</artifactId>
<version>${logback.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
<version>1.7.12</version>
</dependency>
<dependency>
<groupId>org.logback-extensions</groupId>
<artifactId>logback-ext-spring</artifactId>
<version>0.1.2</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.12</version>
</dependency>
<!-- logback日志配置结束 -->
2. 在web.xml中配置logback
<!--logback配置开始-->
<!--尽管后面servlet也配置了contextConfigLocation,但这里是不能省略的-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<context-param>
<param-name>logbackConfigLocation</param-name>
<param-value>classpath:logback.xml</param-value>
</context-param>
<!--自定义监听器-->
<listener>
<listener-class>ch.qos.logback.ext.spring.web.LogbackConfigListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
<!--logback配置结束-->
3. 在classpath下新建logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<!-- 属性描述
scan:设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true
scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。
debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。 -->
<configuration debug="false">
<!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径 -->
<property resource="config.properties"/>
<!-- 控制台输出日志 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符 -->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} -
%msg%n
</pattern>
</encoder>
</appender>
<!-- 按文件大小区分的滚动ALL-OUT日志 -->
<appender name="ALL-OUT" class="ch.qos.logback.core.rolling.RollingFileAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符 -->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} -%msg%n</pattern>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<FileNamePattern>${log.path}/%d{yyyyMMdd}/monitor-web.%d{yyyy-MM-dd}-%i.log</FileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>50MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
</appender>
<!-- 按照每天生成INFO-OUT日志文件 -->
<appender name="INFO-OUT" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--日志文件输出的文件名 -->
<FileNamePattern>${log.path}/%d{yyyyMMdd}/monitor-web.%d{yyyy-MM-dd}.info.log</FileNamePattern>
<!--日志文件保留天数 -->
<MaxHistory>30</MaxHistory>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符 -->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} -%msg%n</pattern>
</encoder>
<!--在多数的Log工具中,级别是可以传递,例如如果指定了日志输出级别为DEBUG, 那么INFO、ERROR级别的log也会出现在日志文件。
这种默认给程序的调试带来了很多的麻烦。通过配置Filter 来严格控制日志输入级别-->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFO</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 按照每天生成WARN-OUT日志文件 -->
<appender name="WARN-OUT" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--日志文件输出的文件名 -->
<FileNamePattern>${log.path}/%d{yyyyMMdd}/monitor-web.%d{yyyy-MM-dd}.warn.log</FileNamePattern>
<!--日志文件保留天数 -->
<MaxHistory>30</MaxHistory>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符 -->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} -%msg%n</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>WARN</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 按照每天生成ERROR-OUT日志文件 -->
<appender name="ERROR-OUT" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--日志文件输出的文件名 -->
<fileNamePattern>${log.path}/%d{yyyyMMdd}/monitor-web.%d{yyyy-MM-dd}.error.log</fileNamePattern>
<!--日志文件保留天数 -->
<MaxHistory>30</MaxHistory>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} -
%msg%n
</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!--logback设置第三方包中的日志显示-->
<logger name="org.springframework" level="INFO"/>
<logger name="net.sf.ehcache" level="WARN"/>
<logger name="org.apache" level="INFO"/>
<logger name="org.quartz.core" level="INFO"/>
<logger name="org.springframework.web.servlet.view.JstlView" level="DEBUG"/>
<logger name="httpclient.wire.content" level="WARN"/>
<logger name="org.springframework.web.util.NestedServletException" level="FATAL"/>
<logger name="com.mchange.v2" level="INFO"/>
<!--JdbcTemplate的日志输出器 -->
<logger name="org.springframework.jdbc.core.StatementCreatorUtils"
additivity="false" level="TRACE">
<appender-ref ref="STDOUT"/>
</logger>
<logger name="org.springframework.jdbc.core.springframework.jdbc.core.BeanPropertyRowMapper"
additivity="false" level="INFO">
<appender-ref ref="STDOUT"/>
</logger>
<logger name="org.springframework.jdbc.core" additivity="true">
<level value="INFO"/>
<appender-ref ref="STDOUT"/>
</logger>
<logger name="java.sql.Connection" additivity="true">
<level value="DEBUG"/>
<appender-ref ref="STDOUT"/>
</logger>
<logger name="java.sql.Statement" additivity="true">
<level value="DEBUG"/>
<appender-ref ref="STDOUT"/>
</logger>
<logger name="java.sql.PreparedStatement" additivity="true">
<level value="DEBUG"/>
<appender-ref ref="STDOUT"/>
</logger>
<logger name="java.sql.ResultSet" additivity="true">
<level value="DEBUG"/>
<appender-ref ref="STDOUT"/>
</logger>
<logger name="java.sql.ResultSet" additivity="true">
<level value="DEBUG"/>
<appender-ref ref="STDOUT"/>
</logger>
<!-- 日志输出级别 -->
<root level="DEBUG">
<appender-ref ref="STDOUT"/>
<appender-ref ref="ALL-OUT"/>
<appender-ref ref="INFO-OUT"/>
<appender-ref ref="WARN-OUT"/>
<appender-ref ref="ERROR-OUT"/>
</root>
<!--日志异步到数据库 -->
</configuration>
4. 使用,在Class类中写
private static final Logger logger = LoggerFactory.getLogger(SessionManager.class);
logger.debug("使用单用户单点登录模式");
我们说方法包含一个级别为DEBUG的日志语句,并带有消息“使用单用户单点登录模式”。
请注意,上面的示例不引用任何logback类。在大多数情况下,就日志记录而言,您的类只需要导入SLF4J类,因此,绝大多数(如果不是全部)类将使用SLF4J API,并且将忽略logback的存在。
总结
总结来说,logback可以实现以下需求
1. 如果appender不设置filter level,那么默认是debug,基本上可以把所有的日志都记录下来
2. 通过为appender设置filter level,可以将某个级别的日志过滤出来
3. 通过设置logger level级别,可以启用或关闭指定类的日志记录功能
4. 通过设置logger addtivity=false,避免logger和root重复记录日志功能