springboot集成日志框架

目录

一步到位springboot目录
gitee:https://gitee.com/chaitou/leilema.git

前言

相信很多为接触过实际开发项目的小伙伴一般都没打过日志,就算有可能也是随便log一下,并没有深入的学习过。甚至会打出下面这样的日志,如果还是学生还能原谅,如果已经工作了,赶紧学习一下本篇回去趁人还没发现摸摸的把日志改了吧…

logger.error(e);
logger.error(e.getMessage);
logger.error("错误信息:" + e);

框架选型

JCLSLF4jJboss-loggingLog4jLog4j2LogbackJUL这么多框架,多多少少你得听说过一点吧,其实日志框架分为3部分:日志门面日志适配器日子库

  1. 日志门面:JCLSLF4jJboss-logging这些框架都是日志门面。门面设计模式是面向对象的一种设计模式,类似JDBC,也就是说这些货本身自己不干活,就是一套接口规范,让调用者不需要关心日志底层具体是什么框架在干活
  2. 日志库:Log4jLog4j2LogbackJUL都是日志库,也就是真实干活的人
  3. 日志适配器:它是解决日志门面日志库接口不兼容的,一般配套的都是兼容的

所以我们的目标是挑选一个日志门面,再挑选一个日子库,搭配使用。先挑选日志库,Log4j(有升级版就不用旧版了)Log4j2LogbackJUL(太简陋了没人用)。剩下Log4j2Logback怎么选呢?Log4j2是Apache写的一套框架,由于太优秀太过复杂,因此也比较小众(优秀也有错…),因此我们选择Logback,简单易用又足够用

话外,其实log4j的升级版就是logbacklog4jlogbackslf4j其实是同一个作者,太牛逼了…而log4j2则是Apache官方写的,名字给人家取了。那么门面模式选谁呢?那肯定是选slf4j了,毕竟是一个人写的,怎么说也得适配的好一些

集成框架

1. 引入依赖

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-loggin</artifactId>
</dependency>

不好意思这一步都省了,因为不仅仅是我们这么选,springboot官方也是这么选的,所以当我们引入spring-boot-starter的时候,就默认帮我们引入logbackslf4j,真是贴心的小棉袄呢,可以看下依赖就知道了
依赖

2. 配置

配置就相当讲究了,就比如我们公司要求,所有日志必须保存180天,每个文件最大50mb,超过的另外取一个文件。infowarnerror必须分开。毕竟跟学生项目不同,公司是有审计要求的,不能胡来,那么我就按这个标准配置一下

  1. 这么复杂的需求,肯定是要另取一个配置文件来配置了,因此我们再application-dev.ymlapplication-pro.yml中分别指明logback配置文件的目录,这里以dev为例
logging:
  config: classpath:logback-dev.xml
  1. 既然告诉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,不匹配的过滤掉

因此只过滤出levelwarn级别的日志,其他的日志信息经过这个节点会被过滤掉,也就不会进入到后续流程了。如果你不写onMatchonMismatch标签,那么error级别的因为大于warn,也会进入到后续流程

而我们说的后续流程就是下面的标签。这些标签指明过滤出来的数据应该放到控制台还是放到文件

  • file:这些过滤出来的数据需要被写入到文件中
  • fileNamePattern:文件的目录和命名格式warn-yyyy-mm-dd_1.log
  • maxFileSize:文件最大50MB
  • MaxHistory:保留18天

3. 使用

  1. 第一步肯定是定好日志输出级别,比如在开发情况下,想看到更多信息,因此可以用DEBUG级别。而生产环境下则应该开启INFO级别,避免无效的日志打印
	<root level="INFO">
        <appender-ref ref="CONSOLE"/>
        <appender-ref ref="INFO"/>
        <appender-ref ref="WARN"/>
        <appender-ref ref="ERROR"/>
  	</root>
  1. 考虑好异常属于什么级别的异常,再打日志。想好是log.error还是log.warn

比如业务异常往往通过用户引导就可以恢复的,那么就只打WARN级别。而ERROR级别的日志一旦出现,就需要人工介入,因此要定期检查ERROR日志排查问题。这段是《阿里Java开发手册》写的,这里自我检讨一下,之前把业务异常打成了ERROR级别,应该打成WARN级别

  1. 因为我们用了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)
...
发布了58 篇原创文章 · 获赞 281 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/chaitoudaren/article/details/105617533