目录
一步到位springboot目录
gitee:https://gitee.com/chaitou/leilema.git
前言
相信很多为接触过实际开发项目的小伙伴一般都没打过日志,就算有可能也是随便log
一下,并没有深入的学习过。甚至会打出下面这样的日志,如果还是学生还能原谅,如果已经工作了,赶紧学习一下本篇回去趁人还没发现摸摸的把日志改了吧…
logger.error(e);
logger.error(e.getMessage);
logger.error("错误信息:" + e);
框架选型
JCL
、SLF4j
、Jboss-logging
、Log4j
、Log4j2
、Logback
、JUL
这么多框架,多多少少你得听说过一点吧,其实日志框架分为3部分:日志门面
、日志适配器
、日子库
- 日志门面:
JCL
、SLF4j
、Jboss-logging
这些框架都是日志门面。门面设计模式是面向对象的一种设计模式,类似JDBC,也就是说这些货本身自己不干活
,就是一套接口规范,让调用者不需要关心日志底层具体是什么框架在干活 - 日志库:
Log4j
、Log4j2
、Logback
、JUL
都是日志库,也就是真实干活的人 - 日志适配器:它是解决
日志门面
和日志库
接口不兼容的,一般配套的都是兼容的
所以我们的目标是挑选一个日志门面
,再挑选一个日子库
,搭配使用。先挑选日志库,、Log4j(有升级版就不用旧版了)
Log4j2
、Logback
、。剩下JUL(太简陋了没人用)
Log4j2
、Logback
怎么选呢?Log4j2
是Apache写的一套框架,由于太优秀太过复杂,因此也比较小众(优秀也有错…),因此我们选择Logback
,简单易用又足够用
话外,其实log4j
的升级版就是logback
,log4j
、logback
、slf4j
其实是同一个作者,太牛逼了…而log4j2
则是Apache
官方写的,名字给人家取了。那么门面模式
选谁呢?那肯定是选slf4j
了,毕竟是一个人写的,怎么说也得适配的好一些
集成框架
1. 引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-loggin</artifactId>
</dependency>
不好意思这一步都省了,因为不仅仅是我们这么选,springboot
官方也是这么选的,所以当我们引入spring-boot-starter
的时候,就默认帮我们引入logback
、slf4j
,真是贴心的小棉袄呢,可以看下依赖就知道了
2. 配置
配置就相当讲究了,就比如我们公司要求,所有日志必须保存180天,每个文件最大50mb,超过的另外取一个文件。info
、warn
、error
必须分开。毕竟跟学生项目不同,公司是有审计要求的,不能胡来,那么我就按这个标准配置一下
- 这么复杂的需求,肯定是要另取一个配置文件来配置了,因此我们再
application-dev.yml
和application-pro.yml
中分别指明logback
配置文件的目录,这里以dev
为例
logging:
config: classpath:logback-dev.xml
- 既然告诉springboot,日志配置文件叫
logback-dev.xml
,所以我们在/resource
目录下新建一个logback-dev.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="true">
<property name="CONSOLE_LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger{50} - %msg%n"/>
<property name="LOG_HOME" value="./log"/>
<!--输出到控制台-->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
<!--<charset>utf8</charset>-->
</encoder>
</appender>
<!--info 级别的日志-->
<!-- 按照每天生成日志文件 -->
<appender name="INFO" class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFO</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
</encoder>
<file>${LOG_HOME}/info.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!--日志文件输出的文件名-->
<fileNamePattern>${LOG_HOME}/achiveLog/info-%d{yyyy-MM-dd}_%i.log</fileNamePattern>
<maxFileSize>50MB</maxFileSize>
<!--日志文件保留天数-->
<MaxHistory>180</MaxHistory>
</rollingPolicy>
</appender>
<!--WARN 级别的日志-->
<appender name="WARN" class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>WARN</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
</encoder>
<file>${LOG_HOME}/warn.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/achiveLog/warn-%d{yyyy-MM-dd}_%i.log</fileNamePattern>
<maxFileSize>50MB</maxFileSize>
<MaxHistory>180</MaxHistory>
</rollingPolicy>
</appender>
<!--ERROR 级别的日志-->
<appender name="ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
</encoder>
<file>${LOG_HOME}/error.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/achiveLog/error-%d{yyyy-MM-dd}_%i.log</fileNamePattern>
<maxFileSize>50MB</maxFileSize>
<MaxHistory>180</MaxHistory>
</rollingPolicy>
</appender>
<!-- 日志输出级别 -->
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="INFO"/>
<appender-ref ref="WARN"/>
<appender-ref ref="ERROR"/>
</root>
</configuration>
再强调一遍,配置文件不要背!吃饱了撑的才去背这个东西!抄下来,然后读懂,改一改就好了!用久了自然而然就能写出来了,但是没必要刻意去背,没有意义的
首先看一下根节点root
,这个level="INFO"
指的是日志登记,按日志级别从低到高分为TRACE
< DEBUG
< INFO
< WARN
< ERROR
< FATAL
,我们这边指定了INFO
,也就是说DEBUG
的日志是不会被输出出来的
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="INFO"/>
<appender-ref ref="WARN"/>
<appender-ref ref="ERROR"/>
</root>
根节点有4个直接点,挑一个做一下解释
<!--WARN 级别的日志-->
<appender name="WARN" class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>WARN</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
</encoder>
<file>${LOG_HOME}/warn.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/achiveLog/warn-%d{yyyy-MM-dd}_%i.log</fileNamePattern>
<maxFileSize>50MB</maxFileSize>
<MaxHistory>180</MaxHistory>
</rollingPolicy>
</appender>
filter
:用于过滤日志level
:匹配等级是WARN
onMatch
:当匹配时ACCEPT
onMismatch
:不匹配时DENY
,不匹配的过滤掉
因此只过滤出level
为warn
级别的日志,其他的日志信息经过这个节点会被过滤掉,也就不会进入到后续流程了。如果你不写onMatch
、onMismatch
标签,那么error
级别的因为大于warn
,也会进入到后续流程
而我们说的后续流程就是下面的标签。这些标签指明过滤出来的数据应该放到控制台
还是放到文件
中
file
:这些过滤出来的数据需要被写入到文件中fileNamePattern
:文件的目录和命名格式warn-yyyy-mm-dd_1.log
maxFileSize
:文件最大50MBMaxHistory
:保留18天
3. 使用
- 第一步肯定是定好日志输出级别,比如在开发情况下,想看到更多信息,因此可以用
DEBUG
级别。而生产环境下则应该开启INFO
级别,避免无效的日志打印
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="INFO"/>
<appender-ref ref="WARN"/>
<appender-ref ref="ERROR"/>
</root>
- 考虑好异常属于什么级别的异常,再打日志。想好是
log.error
还是log.warn
比如业务异常往往通过用户引导就可以恢复的,那么就只打
WARN
级别。而ERROR
级别的日志一旦出现,就需要人工介入,因此要定期检查ERROR
日志排查问题。这段是《阿里Java开发手册》写的,这里自我检讨一下,之前把业务异常打成了ERROR
级别,应该打成WARN
级别
- 因为我们用了
lombok
插件,因此使用@Slf4j
注解后便可以log.error
打日志了。如果没有用lombok
的同学则需要写private static final Logger log = LoggerFactory.getLogger(Xxx.class);
private static final Logger log
之所以定义成static
是因为其与当前类绑定,避免每次new一个对象都创建一次,造成资源浪费《阿里Java开发规范》p152
除了这些,常见的错误还有这样打日志的,这种写法使用到的是重载的方法,也就是说实际上调用的方法是log.error(String msg)
,这样做会导致异常堆栈信息的丢失!这是不能容忍的,排查问题异常困难
log.error(e);
log.error(e.getMessage);
log.error("错误信息:" + e);
正确的写法应该是以下代码,想深入了解的同学可以参考java日志常见误区
logger.error("第x部分出错 ", e);
打完收工看结果
根目录下生成了3个日志文件
其中error
指有error
日志,其他的也同理,历史文件放在achiveLog
里面。模拟了一个业务异常,此时error
中可以看到日志(这里应该用warn
的,阿里说的,代码还没改过来)
2020-04-19 17:51:51.982 ERROR c.b.l.freamwork.advice.ControllerExceptionAdvice - 订单号不存在:1999
com.bugpool.leilema.freamwork.exception.APIException: 订单号不存在:1999
at com.bugpool.leilema.order.service.impl.OrderMasterServiceImpl.cancel(OrderMasterServiceImpl.java:90)
...