一、认识Logback
1.1、Logback概述
官方网站:https://logback.qos.ch/index.html
Logback:同样是log4j创始人离开Apache之后设计的开源日志组件,其性能比Log4j要好。
logback分为三个模块:logback-core,logback-classic和logback-access
logback-core:为其他两个模块奠定基础
logback-classic:它是log4j的一个改良版本,同时它完整实现了slf4j API。
logback-access:访问模块与Servlet容器集成提供通过Http来访问日志的功能。
现如今日志框架都是通过slf4j日志门面来搭建日志系统的,在大多引入日志实现框架中都包含了slf4j-api的依赖,之后主要通过修改配置文件以及pom.xml。
1.2、日志等级(五个)
package ch.qos.logback.classic; public final class Level implements Serializable { public static final Level OFF = new Level(2147483647, "OFF"); public static final Level ERROR = new Level(40000, "ERROR"); public static final Level WARN = new Level(30000, "WARN"); public static final Level INFO = new Level(20000, "INFO"); public static final Level DEBUG = new Level(10000, "DEBUG"); public static final Level TRACE = new Level(5000, "TRACE"); public static final Level ALL = new Level(-2147483648, "ALL"); ... }
日志级别排序为:TRACE< DEBUG<INFO< WARN< ERROR。默认日志等级为Debug,其中OFF与ALL作为日志开关。
1.3、相关组件
在logback中也包含三个组件Logger、Appender以及Layout,与Log4j大同小异:
Logger:日志记录器,把它关联到应用的对应的context上后,主要用于存放日志对象,也可以定义日志类型、级别。
Appender:用于指定日志输出的目的地,目的地可以是控制台、文件、数据库等等。
Layout:负责把事件转换成字符串,格式化的日志信息的输出,在Logback中Layout对象被封装在encoder中。
二、Logback配置文件(XML)
提前准备
在使用logback日志框架会默认读取以下类型的配置文件:
logback.groovy
logback-test.xml
logback.xml
若是均不存在会采用默认配置,日志等级为DEBUG,默认输出到console。
一般创建logback.xml(Maven项目放置到resource下),需要提前准备内容如下:
<?xml version="1.0" encoding="UTF-8"?> <configuration> ....相关配置信息 </configuration>
下面部分内容直接就写相关配置信息并不包含上面提前准备内容!!!
RootLogger设置
root基本設置
<!-- 设置root的日志等级为ALL --> <root level="ALL"> <!-- 指定appender引用,这里例如引用下面的appender --> <appender-ref ref="console"/> </root>
配置键值对以及进行获取对应键的值
<!-- ①配置一对键值对,其name可被引用--> <property name="pattern" value="%-5level %d{yyyy-MM-dd HH:mm:ss.SSS} %c %M %L [%thread] %m%n"/> <!-- 配置appender过程中引用对应键值对 --> <appender name="console" class="ch.qos.logback.core.ConsoleAppender"> <!-- 设置自定义格式布局 --> <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> <!-- ******************** --> <!-- ②注意这里使用${name}包裹能够引用property中name对应的value --> <pattern>${pattern}</pattern> <!-- ******************** --> </encoder> </appender>
注意看①②,在②中引用预先配置的property,使用${}获取其中的value值
日志输出格式
<!-- 日志输出格式: %-5level:日志等级 %d{yyyy-MM-dd HH:mm:ss.SSS}:日期 %c:类的完整名称 %M:method %L:行号 %thread:线程名称 %m或者%msg:信息 %n:换行 --> <!--配置一对键值对 日志等级(值为靠左对齐5个位子) 日期 类完整名 方法 行号 线程名 信息及换行--> <property name="pattern" value="%-5level %d{yyyy-MM-dd HH:mm:ss.SSS} %c %M %L [%thread] %m%n"/>
格式输出大致如图。
Appender
介绍
ConsoleAppender:输出到屏幕。
FileAppender:输出到指定文件。
RollingFileAppender:可定时创建文件以及按文件大小进行切割。
ConsoleAppender
目的:输出到屏幕上。
实例演示
配置logback.xml:
<?xml version="1.0" encoding="UTF-8"?> <configuration> <!-- 可见root基本设置中的格式介绍 --> <property name="pattern" value="%-5level %d{yyyy-MM-dd HH:mm:ss.SSS} %c %M %L [%thread] %m%n"/> <!-- ******************** --> <!-- 配置ConsoleAppender --> <appender name="console" class="ch.qos.logback.core.ConsoleAppender"> <!-- 输出流对象 --> <target>System.err</target> <!-- 日志格式配置 --> <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> <!-- 设置输出格式引用上面的键值对及输出的格式 --> <pattern>${pattern}</pattern> </encoder> </appender> <!-- ******************** --> <!-- 设置root,引用appender --> <root level="ALL"> <appender-ref ref="console"/> <appender-ref ref="file"/> </root> </configuration>
运行程序:
import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class LogTest { //获取Logger实例 public static final Logger LOGGER = LoggerFactory.getLogger(LogTest.class); public static void main(String[] args) { //为了效果明显循环10000次 for (int i = 0; i < 10000; i++) { LOGGER.error("error"); LOGGER.warn("warn"); LOGGER.info("info"); LOGGER.debug("debug"); LOGGER.trace("trace"); } } }
源码初探
查看下源码中对应的配置属性:
第9行:encoder是ConsoleAppender的父类OutputStreamAppender中有setEncoder()设置方法俩设置Encoder
第11行:对应在PatternLayoutEncoder的父类PatternLayoutEncoderBase中有setPattern()设置方法所以可以在对应标签中设置pattern。
FileAppender(自定义格式与HTML)
目的:日志输出到文件中。
针对于FileAppender其中append = true(可追加)、FileSize bufferSize = new FileSize(8192);(缓冲大小)。
FileAppender本身类中包含setFile(),其父类OutputStreamAppender包含setEncoder。
输出到文件其格式为自定义格式(PatternLayoutEncoder)
<!-- 配置路径以及日志格式 --> <property name="log_dir" value="d:/logs"/> <property name="pattern" value="%-5level %d{yyyy-MM-dd HH:mm:ss.SSS} %c %M %L [%thread] %m%n"/> <!-- ******************** --> <!-- 配置fileAppender --> <appender name="file" class="ch.qos.logback.core.FileAppender"> <!-- 配置文件路径 --> <file>${log_dir}/logback.log</file> <!-- 日志格式设置 --> <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> <pattern>${pattern}</pattern> </encoder> </appender> <!-- ******************** --> <!-- 设置root的日志等级为ALL,以及引用上面的name为console的appender --> <root level="ALL"> <!-- 这里console只用来表示root标签可设置多个appender,无引用上面的意思 --> <appender-ref ref="console"/> <appender-ref ref="file"/> </root>
输出程序可见下面的3.2,对应的键值对设置无非对应指定类或其父类的相关set方法可见源码。
输出到文件其格式为HTML格式(LayoutWrappingEncoder中包含Layout为HTMLLayout)
<!-- 配置路径以及日志格式 --> <property name="log_dir" value="d:/logs"/> <property name="pattern" value="%-5level %d{yyyy-MM-dd HH:mm:ss.SSS} %c %M %L [%thread] %m%n"/> <!-- ******************** --> <!-- 配置HTML格式的Appender --> <appender name="htmlFile" class="ch.qos.logback.core.FileAppender"> <!-- 配置文件路径 --> <file>${log_dir}/logback.html</file> <!-- 日志格式设置 --> <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder"> <!-- 设置了HTMLLayout的layout --> <layout class="ch.qos.logback.classic.html.HTMLLayout"> <!-- 注意:输出格式为HTML时,不需要有空格或者其他符号或者换行,否则输出格式会有空格不美观 --> <pattern>%-5level%d{yyyy-MM-dd HH:mm:ss.SSS}%c%M%L%thread%m</pattern> </layout> </encoder> </appender> <!-- ******************** --> <!-- 设置root的日志等级为ALL,引用console和 --> <root level="ALL"> <!-- 这里console只用来表示root标签可设置多个appender,无引用上面的意思 --> <appender-ref ref="console"/> <appender-ref ref="htmlFile"/> </root>
测试程序见3.2,对应标签设置无非就是对应set方法。
在ch.qos.logback.core.encoder.LayoutWrappingEncoder中包含layout可设置对应的htmllayout。
RollingFileAppender
目的:可定时创建文件以及按文件大小进行切割。
配置详情
RollingFileAppender类实现了FileAppender,即具有FileAppender可设置的属性内容如Encoder、file(输出路径)
<!-- 设置路径键值对 --> <property name="log_dir" value="d:/logs"/> <!-- ******************** --> <!-- 日志拆分和归档的appender对象 --> <appender name="rollingFile" class="ch.qos.logback.core.rolling.RollingFileAppender"> <!-- 配置文件路径 --> <file>${log_dir}/roll_logback.log</file> <!-- 自定义日志格式设置 --> <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> <pattern>${pattern}</pattern> </encoder> <!-- 指定日志文件拆分与压缩规则 --> <RollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"> <!-- 通过指定压缩文件名称来确定分割文件方式,这里是设置为年月日 可精确到毫秒:%d{yyyy-MM-dd-hh-mm-ss-SSS} --> <fileNamePattern>${log_dir}/rolling.%d{yyyy-MM-dd-hh-mm-ss}.log%i.gz</fileNamePattern> <!-- 文件拆分大小,超过1MB即拆分 --> <maxFileSize>1MB</maxFileSize> </RollingPolicy> </appender> <!-- ******************** --> <!-- 设置root的日志等级为ALL,引用appender --> <root level="ALL"> <appender-ref ref="rollingFile"/> </root>
第13行:设置的日志文件拆分与压缩规则类,其包含两个属性fileNamePattern可确定分割文件的名称以及分割的时间(分割上面file文件),maxFileSize可设置单个文件的大小,这里最大为1MB。
测试程序:
import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class LogTest { //获取Logger实例 public static final Logger LOGGER = LoggerFactory.getLogger(LogTest.class); public static void main(String[] args) { //为了效果明显循环10000次 for (int i = 0; i < 10000; i++) { LOGGER.error("error"); LOGGER.warn("warn"); LOGGER.info("info"); LOGGER.debug("debug"); LOGGER.trace("trace"); } } }
原理分析
首先看该类的层级图,注意到其继承了FileAppender,那么就可以知道一定有file、Encoder属性可设置。
看该类中的属性及set方法:
public class RollingFileAppender<E> extends FileAppender<E> { File currentlyActiveFile RollingPolicy rollingPolicy; //两个set方法 @Override public void setFile(String file) { if (file != null && ((triggeringPolicy != null) || (rollingPolicy != null))) { addError("File property must be set before any triggeringPolicy or rollingPolicy properties"); addError(MORE_INFO_PREFIX + RFA_LATE_FILE_URL); } super.setFile(file); } public void setRollingPolicy(RollingPolicy policy) { rollingPolicy = policy; if (rollingPolicy instanceof TriggeringPolicy) { triggeringPolicy = (TriggeringPolicy<E>) policy; } } }
我们分析一下RollingPolicy:其是一个接口,我们可以使用它的实现类SizeAndTimeBasedRollingPolicy
public class SizeAndTimeBasedRollingPolicy<E> extends TimeBasedRollingPolicy<E> { //设置单个文件最大容量 FileSize maxFileSize; } //注意其父类,见1 public class TimeBasedRollingPolicy<E> extends RollingPolicyBase implements TriggeringPolicy<E> { } public abstract class RollingPolicyBase extends ContextAwareBase implements RollingPolicy { //设置文件自定义格式化名 FileNamePattern fileNamePattern; public void setFileNamePattern(String fnp) { fileNamePatternStr = fnp; } }
Filter
介绍
目的:用来进行过滤指定日志等级的日志记录。
Filter可设置在任意的appender中,如下:
EvaluatorFilter:临界过滤器,滤掉低于指定临界值的日志。
LevelFilter:级别过滤器,对特定某个级别的日志进行过滤。
LevelFilter
配置说明
配置LevelFilter的三个属性介绍:
level:设置过滤的日志等级。
onMatch:匹配日志等级时执行操作。
onMismatch:不匹配日志等级时执行操作。
其中onMatch、onMismatch对应表示使用的是指定的枚举值(三个):
DENY:日志将立即被抛弃不再经过其他过滤器。(否定含义)
NEUTRAL:有序列表里的下个过滤器过接着处理日志;(该级别既不处理,也不抛弃,相当于没有任何处理,日志会被保存下来并在本appender被执行)(中立的)
ACCEPT:日志会被立即处理,不再经过剩余过滤器。(通过含义)
实例使用
要求:只将error日志等级输出到文件中。
logback.xml配置如下:
<?xml version="1.0" encoding="UTF-8"?> <configuration> <!-- 配置fileAppender --> <appender name="file" class="ch.qos.logback.core.FileAppender"> <!-- 配置文件路径 --> <file>${log_dir}/logback.log</file> <!-- 日志格式设置 --> <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> <pattern>${pattern}</pattern> </encoder> <!-- ******************** --> <!-- 配置日志等级过滤器:LevelFilter --> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <!-- 指定日志等级 --> <level>error</level> <!-- 若是>=error(匹配)直接通过不过滤 --> <onMatch>ACCEPT</onMatch> <!-- 若是<error(不匹配)过滤掉 --> <onMismatch>DENY</onMismatch> </filter> <!-- ******************** --> </appender> <!-- 设置root的日志等级为ALL,以及引用上面的name为console的appender --> <root level="ALL"> <appender-ref ref="file"/> </root> </configuration>
测试程序:
import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class LogTest { //获取Logger实例 public static final Logger LOGGER = LoggerFactory.getLogger(LogTest.class); public static void main(String[] args) { for (int i = 0; i < 10000; i++) { LOGGER.error("error"); LOGGER.warn("warn"); LOGGER.info("info"); LOGGER.debug("debug"); LOGGER.trace("trace"); } } }
原理分析
上面配置的是LevelFilter类,我们看下该类包含的属性:
可以看到LevelFilter类中包含level属性及set方法。
LevelFilter父类AbstractMatcherFilter包含了onMatch、onMismatch属性及set方法。
可以看到其默认值为FilterReply.NEUTRAL对应的是枚举类,我们再去看下枚举类:
对应着三个实例,对应含义见上。
异步日志配置
介绍
我们之前打印日志使用的都是同步日志,而对于高并发低延迟要求的系统中我们可以设置异步。
异步:打印日志时候是把打印任务放入内存队列后就直接返回了,而不像同步日志一样会每次直接进行写磁盘。
实例使用
对FileAppender使用异步日志,logback.xml配置如下:
<?xml version="1.0" encoding="UTF-8"?> <configuration> <!-- 配置fileAppender --> <appender name="file" class="ch.qos.logback.core.FileAppender"> <!-- 配置文件路径 --> <file>${log_dir}/logback.log</file> <!-- 日志格式设置 --> <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> <pattern>${pattern}</pattern> </encoder> <!-- 配置日志等级过滤器:LevelFilter --> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <!-- 指定日志等级 --> <level>error</level> <!-- 若是>=error(匹配)直接通过不过滤 --> <onMatch>ACCEPT</onMatch> <!-- 若是<error(不匹配)过滤掉 --> <onMismatch>DENY</onMismatch> </filter> </appender> <!-- 设置异步日志 --> <appender name="async" class="ch.qos.logback.classic.AsyncAppender"> <!--设为0表示队列达到80%,也不丢弃任务--> <discardingThreshold>0</discardingThreshold> <!--队列的深度,该值会影响性能,默认256,这里设置1024--> <queueSize>1024</queueSize> <!--队列满了直接丢弃要写的消息--> <neverBlock>true</neverBlock> <!--One and only one appender may be attached to AsyncAppender,添加多个的话后面的会被忽略--> <appender-ref ref="file"/> </appender> <!-- 设置root的日志等级为ALL,以及引用上面的name为console的appender --> <root level="ALL"> <appender-ref ref="file"/> </root> </configuration>
这里的配置信息是查看的一篇博客。
测试程序:
import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class LogTest { //获取Logger实例 public static final Logger LOGGER = LoggerFactory.getLogger(LogTest.class); public static void main(String[] args) { for (int i = 0; i < 10000; i++) { LOGGER.error("error"); LOGGER.warn("warn"); LOGGER.info("info"); LOGGER.debug("debug"); LOGGER.trace("trace"); } } }
说明:通常对于在进行高并发情况下使用异步日志!
源码初探
通过使用appender为AsyncAppender来引用配置文件中定义的appender来达到异步效果,首先看下AsyncAppender的继承关系:
AsyncAppender类中包含一个配置属性:
AsyncAppender的父类AsyncAppenderBase包含的配置属性:
对应属性描述如下:
<!--可设置属性介绍--> <!--队列的深度,该值会影响性能,默认256--> <queueSize>512</queueSize> <!--设为0表示队列达到80%,也不丢弃任务--> <discardingThreshold>0</discardingThreshold> <!--日志上下文关闭后,AsyncAppender继续执行写任务的时间,单位毫秒--> <maxFlushTime>1000</maxFlushTime> <!--队列满了直接丢弃要写的消息--> <neverBlock>true</neverBlock> <!--是否包含调用方的信息,false则无法打印类名方法名行号等--> <includeCallerData>true</includeCallerData> <appender-ref ref="file"/>
其中需要知道的是在异步日志器中属性包含了BlockingQueue接口即队列,该接口有多个实现类:
在AsyncAppenderBase的start()方法,默认获取了ArrayBlockingQueue这个实现类:
许多配置信息其实间接的与这个队列实现类有关!!!这里暂时了解即可,不再进行深入探究。
自定义Logger
介绍
目的:根据不同的业务需求时可进行自定义Logger,例如自己定义的消息使用自定义输出到控制台,对于系统的消息可输出到日志文件。可通过使用LoggerFactory.getLogger(指定类或包名);来获取指定配置的logger实例。
使用方式
<!--自定义logger additivity:表示是否从 rootLogger继承配置--> <logger name="xyz.changlu" level="debug" additivity="false"> <appender-ref ref="console"/> </logger>
这里使用<logger>标签来作为自定义标签。
additivity:设置一个布尔值表示是否要继承RootLogger。