日志级别:
Log4j 中日志级别有8种: 从低至高依次为 ALL、TRACE、DEBUG、INFO、WARN、ERROR、FATAL、OFF。
Log4j 建议只使用四个级别,优先级从低至高分别是DEBUG、INFO、WARN、ERROR。
日志框架演化:
- Log4j
Apache Log4j 是一种基于 Java 的日志记录工具,它是 Apache 软件基金会的一个项目。在 jdk1.3 之前,还没有现成的日志框架,Java 工程师只能使用原始的 System.out.println(), System.err.println() 或者 e.printStackTrace()。通过把 debug 日志写到 StdOut 流,错误日志写到 ErrOut 流,以此记录应用程序的运行状态。这种原始的日志记录方式缺陷明显,不仅无法实现定制化,而且日志的输出粒度不够细。鉴于此,1999 年,大牛 Ceki Gülcü 创建了 Log4j 项目,并几乎成为了 Java 日志框架的实际标准。
- JUL
Log4j 作为 Apache 基金会的一员,Apache 希望将 Log4j 引入 jdk,不过被 sun 公司拒绝了。随后,sun 模仿 Log4j,在 jdk1.4 中引入了 JUL(java.util.logging)。
- Commons Logging
为了解耦日志接口与实现,2002 年 Apache 推出了 JCL(Jakarta Commons Logging),也就是 Commons Logging。Commons Logging 定义了一套日志接口,具体实现则由 Log4j 或 JUL 来完成。Commons Logging 基于动态绑定来实现日志的记录,在使用时只需要用它定义的接口编码即可,程序运行时会使用 ClassLoader 寻找和载入底层的日志库,因此可以自由选择由 log4j 或 JUL 来实现日志功能。
- Slf4j&Logback
大牛 Ceki Gülcü 与 Apache 基金会关于 Commons-Logging 制定的标准存在分歧,后来,Ceki Gülcü 离开 Apache 并先后创建了 Slf4j 和 Logback 两个项目。Slf4j 是一个日志门面,只提供接口,可以支持 Logback、JUL、log4j 等日志实现,Logback 提供具体的实现,它相较于 log4j 有更快的执行速度和更完善的功能。
- Log4j 2
为了维护在 Java 日志江湖的地位,防止 JCL、Log4j 被 Slf4j、Logback 组合取代 ,2014 年 Apache 推出了 Log4j 2。Log4j 2 与 log4j 不兼容,经过大量深度优化,其性能显著提升。
日志门面与日志系统:
slf4j(全称是Simple Loging Facade For Java)是一个为Java程序提供日志输出的统一接口,并不是一个具体的日志实现方案,就好像我们经常使用的JDBC一样,只是一种规则而已。因此单独的slf4j是不能工作的,它必须搭配其他具体的日志实现方案,比如apache的org.apache.log4j.Logger,jdk自带的java.util.logging.Logger等等。
commons-logging和slf4j一样都是日志的接口, log4j,logback等等才是日志的真正实现。当我们调用接口时,接口的工厂会自动寻找恰当的实现,返回一个实现的实例给我服务。这些过程都是透明化的,用户不需要进行任何操作!
日志框架的使用选择:
Logback 和 Slf4j 是同一个作者,其兼容性不言而喻。
依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
spring-boot 集成 logback,spring-boot-starter-logging依赖包括日志门面和日志系统,并且做了连接适配。额外只需要在yml或properties配置文件中添加如下配置:
logging:
config: classpath:logback-cbssproxy.xml #日志配置文件地址
#config: http://${nacos.config.server-addr}/nacos/v1/cs/configs?group=cbss&tenant=${nacos.config.namespace}&dataId=logback-spring.xml
level:
com.eastcom: debug #指定包路径下的日志输出级别,默认info,排查问题时可改为debug
org.springframework.cloud.bus: debug #指定特定类的日志级别,方便查看问题
logback.xml文件具体配置示例:
示例1:
<?xml version="1.0" encoding="UTF-8"?>
<!-- debug:打印logback内部日志信息,实时查看logback的运行状态,默认为false -->
<!-- scan:配置文件如果发生改变,是否被重新加载,默认为true。 -->
<!-- scanPeriod:设置检测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒,默认的时间间隔为1分钟,默认为true。 -->
<configuration debug="false" scan="false" scanPeriod="30 seconds">
<!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径,可以使用系统变量 此处设置为程序运行目录-->
<!-- <property name="LOG_HOME" value="${app.home}/log" /> -->
<property name="LOG_HOME" value="logdir"/>
<!-- 时间戳定义,timeReference:使用日志产生日期为时间基准 -->
<timestamp key="byDay" datePattern="yyyy-MM-dd" timeReference="contextBirth"/>
<springProperty scope="context" name="springApplicationName" source="spring.application.name"/>
<contextName>${springApplicationName}</contextName>
<!-- 引入springboot的控制台日志输出格式-彩色便于分析 -->
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度,%msg:日志消息,%n是换行符 -->
<property name="CONSOLE_LOG_PATTERN_1"
value="%d{yyyy-MM-dd} %d{HH:mm:ss.SSS} %-5level [%thread] [%X{traceId}] %logger{36} - %msg%n"/>
<property name="CONSOLE_LOG_PATTERN_2"
value="%d{yyyy-MM-dd} %d{HH:mm:ss.SSS} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} [%15.15thread] [%X{traceId}] %clr(%-40.40logger{39}){cyan} %clr(:){faint} %msg%n"/>
<property name="CONSOLE_LOG_PATTERN_3"
value="${CONSOLE_LOG_PATTERN:-%clr(%d{${LOG_DATEFORMAT_PATTERN:-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}}"/>
<!-- 控制台输出,生产环境将请stdout去掉 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${CONSOLE_LOG_PATTERN_2}</pattern>
</encoder>
</appender>
<!-- file for all -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 日志输出文件 -->
<file>${LOG_HOME}/logback_${springApplicationName}_${byDay}.log</file>
<!-- 追加日志到原文件结尾 -->
<append>true</append>
<!-- timebasedrollingpolicy:演示时间和大小为基础的日志文件归档 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 归档的日志文件的路径,例如今天是2013-12-21日志,当前写的日志文件路径为file节点指定。 -->
<!--可以将此文件与file指定文件路径设置为不同路径,从而将当前日志文件或归档日志文件置不同的目录。 -->
<!--而2013-12-21的日志文件在由fileNamePattern指定。%d{yyyy-MM-dd}指定日期格式,%i指定索引 -->
<!-- 文件滚动日期格式:每天:.YYYY-MM-dd(默认);每星期:.YYYY-ww;每月:.YYYY-MM -->
<!-- 每隔半天:.YYYY-MM-dd-a;每小时:.YYYY-MM-dd-HH;每分钟:.YYYY-MM-dd-HH-mm -->
<fileNamePattern>${LOG_HOME}/logback_${springApplicationName}_%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<!-- 控制归档文件的最大数量的保存,删除旧的文件,默认单位天数 -->
<maxHistory>7</maxHistory>
<!-- 设置当前日志的文件的大小,决定日志翻滚 -->
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<!-- 除按日志记录之外,还配置了日志文件不能超过10M(默认),若超过10M,日志文件会以索引0开始, -->
<maxFileSize>10MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<encoder>
<!-- <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n</pattern> -->
<pattern>%d{yyyy-MM-dd} %d{HH:mm:ss.SSS} [%thread] [%X{traceId}] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!--输出到logstash的appender-->
<appender name="LOGSTASH" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
<!--可以访问的logstash日志收集端口-->
<destination>10.8.29.171:9252</destination>
<encoder charset="UTF-8" class="net.logstash.logback.encoder.LogstashEncoder">
<!-- 在elasticsearch的index中追加applicationName字段 -->
<customFields>{"applicationName":"ahsa"}</customFields>
</encoder>
</appender>
<!-- 输出至 logStash 的意愿日志-->
<logger name="logStashWishLog" level="INFO" additivity="true">
<appender-ref ref="LOGSTASH"/>
</logger>
<!-- 输出至 logStash 的策略日志-->
<logger name="logStashStrategyLog" level="INFO" additivity="true">
<appender-ref ref="LOGSTASH"/>
</logger>
<logger name="com.eastcom.*" level="INFO" additivity="false">
<appender-ref ref="FILE"/>
</logger>
<root level="INFO">
<appender-ref ref="STDOUT"/>
<appender-ref ref="FILE"/>
</root>
</configuration>
示例2:
<?xml version="1.0" encoding="UTF-8"?>
<!-- debug:打印logback内部日志信息,实时查看logback的运行状态,默认为false -->
<!-- scan:配置文件如果发生改变,是否被重新加载,默认为true。 -->
<!-- scanPeriod:设置检测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒,默认的时间间隔为1分钟,默认为true。 -->
<configuration debug="false" scan="false" scanPeriod="30 seconds">
<contextName>Application</contextName>
<!-- 时间戳定义,timeReference:使用日志产生日期为时间基准 -->
<timestamp key="byDay" datePattern="yyyy-MM-dd" timeReference="contextBirth" />
<!-- 定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径,可以使用系统变量 -->
<!-- <property name="LOG_HOME" value="${app.home}/log" /> -->
<!-- 日志会放在项目根路径下的 logdir 目录 -->
<property name="LOG_HOME" value="logdir" />
<!-- 控制台输出,生产环境将请stdout去掉 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度,%msg:日志消息,%n是换行符 -->
<pattern>
%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
</pattern>
</encoder>
</appender>
<!-- file for all 打印所有日志 -->
<appender name="FILE-ALL" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 日志输出文件 -->
<file>${LOG_HOME}/LoggingBack-${byDay}.log</file>
<!-- 追加日志到原文件结尾 -->
<append>true</append>
<!-- timebasedrollingpolicy:演示时间和大小为基础的日志文件归档 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 归档的日志文件的路径,例如今天是2013-12-21日志,当前写的日志文件路径为file节点指定。 -->
<!--可以将此文件与file指定文件路径设置为不同路径,从而将当前日志文件或归档日志文件置不同的目录。 -->
<!--而2013-12-21的日志文件在由fileNamePattern指定。%d{yyyy-MM-dd}指定日期格式,%i指定索引 -->
<!-- 文件滚动日期格式:每天:.YYYY-MM-dd(默认);每星期:.YYYY-ww;每月:.YYYY-MM -->
<!-- 每隔半天:.YYYY-MM-dd-a;每小时:.YYYY-MM-dd-HH;每分钟:.YYYY-MM-dd-HH-mm -->
<fileNamePattern>${LOG_HOME}/log-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<!-- 控制归档文件的最大数量的保存,删除旧的文件,默认单位天数 -->
<maxHistory>7</maxHistory>
<!-- 设置当前日志的文件的大小,决定日志翻滚 -->
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<!-- 除按日志记录之外,还配置了日志文件不能超过10M(默认),若超过10M,日志文件会以索引0开始, -->
<maxFileSize>10MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n
</pattern>
</encoder>
</appender>
<!-- file for info 只打印 info 级别的日志(通过过滤器实现)-->
<appender name="FILE-INFO" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 这里添加一个过滤器 -->
<file>${LOG_HOME}/LoggingBack-info.log</file>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFO</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/LOG-INFO-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<maxHistory>7</maxHistory>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>10MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n
</pattern>
</encoder>
</appender>
<!-- file for error 只打印 error 级别的日志-->
<appender name="FILE-ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_HOME}/LoggingBack-error.log</file>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/LOG-ERROR-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<maxHistory>7</maxHistory>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>10MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n
</pattern>
</encoder>
</appender>
<!--输出到logstash的appender-->
<appender name="LOGSTASH" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
<!--可以访问的logstash日志收集端口-->
<destination>10.8.29.171:9252</destination>
<encoder charset="UTF-8" class="net.logstash.logback.encoder.LogstashEncoder">
<!-- 在elasticsearch的index中追加applicationName字段 -->
<customFields>{"applicationName":"ahsa"}</customFields>
</encoder>
</appender>
<!-- 输出至 logStash 的意愿日志-->
<logger name="logStashWishLog" level="INFO" additivity="true">
<appender-ref ref="LOGSTASH"/>
</logger>
<!-- 输出至 logStash 的策略日志-->
<logger name="logStashStrategyLog" level="INFO" additivity="true">
<appender-ref ref="LOGSTASH"/>
</logger>
<!--
业务日志级别:项目com.eastcom包下的所有日志输出到以下appender。
additivity 为false代表日志在以下 appender 不会重复
-->
<!-- <logger name="com.eastcom" level="INFO" additivity="false">
<appender-ref ref="STDOUT" />
<appender-ref ref="FILE" />
<appender-ref ref="FILE-INFO" />
<appender-ref ref="FILE-ERROR" />
</logger> -->
<!-- additivity 为false 表示不向 root 标签传递日志打印信息,即日志不会重复打印。
为true,则 root 标签下的 appender 都会重复打印该日志-->
<logger name="playLog" level="INFO" additivity="true">
<appender-ref ref="playLog" />
</logger>
<!-- root,所有logger标签的父类,若 logger标签中的additivity=false,则日志不重复输出,以子类为准。 -->
<root level="INFO">
<appender-ref ref="STDOUT" />
<appender-ref ref="FILE" />
<appender-ref ref="FILE-INFO" />
<appender-ref ref="FILE-ERROR" />
</root>
</configuration>
示例3:(最新)
TimeBasedRollingPolicy 和 SizeAndTimeBasedFNATP 合并为 SizeAndTimeBasedRollingPolicy,SizeAndTimeBasedFNATP 已弃用。
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 日志输出文件 -->
<file>${LOG_HOME}/logback_${springApplicationName}_${byDay}.log</file>
<!-- 追加日志到原文件结尾 -->
<append>true</append>
<!-- timebasedrollingpolicy:演示时间和大小为基础的日志文件归档 -->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- 归档的日志文件的路径,例如今天是2013-12-21日志,当前写的日志文件路径为file节点指定。 -->
<!--可以将此文件与file指定文件路径设置为不同路径,从而将当前日志文件或归档日志文件置不同的目录。 -->
<!--而2013-12-21的日志文件在由fileNamePattern指定。%d{yyyy-MM-dd}指定日期格式,%i指定索引 -->
<!-- 文件滚动日期格式:每天:.YYYY-MM-dd(默认);每星期:.YYYY-ww;每月:.YYYY-MM -->
<!-- 每隔半天:.YYYY-MM-dd-a;每小时:.YYYY-MM-dd-HH;每分钟:.YYYY-MM-dd-HH-mm -->
<fileNamePattern>${LOG_HOME}/logback_${springApplicationName}_%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<!-- 控制归档文件的最大数量的保存,删除旧的文件,默认单位天数 -->
<maxHistory>7</maxHistory>
<!-- 设置当前日志的文件的大小,决定日志翻滚 -->
<!-- 除按日志记录之外,还配置了日志文件不能超过10M(默认),若超过10M,日志文件会以索引0开始, -->
<maxFileSize>1KB</maxFileSize>
</rollingPolicy>
<encoder>
<!-- <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n</pattern> -->
<pattern>%d{yyyy-MM-dd} %d{HH:mm:ss.SSS} [%thread] [%X{traceId}] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
LoggerFactory.getLogger("name") 和 LoggerFactory.getLogger(类.class) 的区别:
使用LoggerFactory.getLogger("name") 获取日志,需要在 name属性中填写需要获取的日志的名称。
注意:若logback.xml文件中未配置appender和logger,获者只添加了appender,而没有该名称的logger标签,则日志未指定输出目的地,按root指定输出。
若只添加了logger,并且里面指定了未添加的appender,则启动时会报找不到appender的错误。
<logger name="name" level="INFO" additivity="true">
<appender-ref ref="LOGSTASH"/>
</logger>
使用LoggerFactory.getLogger(类.class) 获取日志,需要在 name属性中填写该类的全限定名称。注意:若该类的包路径发生改变时,对应的日志也要做改动。
<logger name="com.ryxx.User" level="INFO" additivity="true">
<appender-ref ref="LOGSTASH"/>
</logger>
appender标签:
作用:
①控制打印日志的地方、②打印日志的输出格式。
root标签:
是所有logger的祖先元素,所有的logger都继承与root元素,相当于java中的object对象。
每一个logger标签都可以指定一个级别(TRACE,DEBUG,INFO,WARN,ERROR五种,他们是在 ch.qos.logback.classic.Level class中定义的)。
如果一个指定的logger标签没有指定规则的话,就会继承离他最近的祖先的级别。
因为所有的logger都是继承root标签,所以为了确保所有的logger都有一个级别,root标签有一个默认的级别,默认值为DEBUG。
logger标签:
概述:
会继承距离自己最近的 root 标签。
任何一个logger对象都有五种级别,所以就对应有五种打印日志方法:logger.info(字符串),logger.debug(字符串)。
如果使用logger.info(字符串)的话,那么logger打印级别就是INFO。
有效级别是最低级别,高于有效级别的信息都能打印出来,低于有效级别都不能打印出来。
结构:
属性:
name:必填属性,含义是指定包名或者类名的全路径(包含包名)。若为类表示范围只是该类,若为包名,则表示该包以下的全部类。
level:选填属性,可以是8种日志级别中任何一个,并且不区分大小写。如果没有写的话就会继承离他最近的祖先的级别。
additivity:选填属性,是否向上级loger传递打印信息。默认为true(累加)。若不需要将该处的日志累加到 root ,设置为false。
子标签:
appender-ref(一个或多个):
属性:
ref:必填属性,值是被引用的appender的name。
示例:
<!-- additivity 为false 表示不向 root 标签传递日志打印信息,即日志不会重复打印。
为true,则 root 标签下的 appender 都会重复打印该日志-->
<logger name="playLog" level="INFO" additivity="true">
<appender-ref ref="playLog" />
</logger>
<!-- root,所有logger标签的父类,若 logger标签中的additivity=false,则日志不重复输出,以子类为准。 -->
<root level="INFO">
<appender-ref ref="STDOUT" />
<appender-ref ref="FILE" />
<appender-ref ref="FILE-INFO" />
<appender-ref ref="FILE-ERROR" />
</root>
参考: