跳至主要內容

slf4j+logback

chanchaw大约 9 分钟languagejava

配置文件

每日+大小生成日志文件

每天都会生成一个日志文件,如果当前超过10MB,则会生成当日的第二个文件,初次试验在 showabe 项目中

<!--  2024年7月17日 16:34:38 修改为按照日期和文件大小生成日志文件,弃用上面 appender  -->
<appender name="dynamicScheduleTask" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
        <!-- rollover daily -->
        <FileNamePattern>${log.path}/dynamicScheduleTask.%d{yyyyMMdd}.%i.log</FileNamePattern>
        <!-- each file should be at most 10MB, keep 30 days worth of history, but at most 20GB -->
        <maxFileSize>10MB</maxFileSize>
        <maxHistory>30</maxHistory>
        <!-- 文件达到1G后删除旧的日志文件 -->
        <totalSizeCap>1GB</totalSizeCap>
    </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>
</appender>
<logger name="dynamicScheduleTask" level="info" additivity="false">
    <appender-ref ref="dynamicScheduleTask" />
</logger>

根节点

<configuration scan="true" scanPeriod="60 seconds" debug="false">

scan : 当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true。 scanPeriod : 设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。 debug : 当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态,默认值为false。

子节点 - logger

在根节点 configuration 下使用子节点 logger,其中 name 可以设置为包路径或者类的全路径,当指定为 sql 的类并指定为 DEBUG 时可打印带有参数的 sql 语句。其下的子节点可指定使用的具体的 appender ,additivity 表示是否向上级 logger 传递打印日志。

<logger name="org.hibernate.type.descriptor.sql.BasicBinder" level="TRACE" />
<logger name="org.hibernate.type.descriptor.sql.BasicExtractor" level="DEBUG" />
<logger name="org.hibernate.SQL" level="DEBUG" />
<logger name="org.hibernate.engine.QueryParameters" level="DEBUG" />
<logger name="org.hibernate.engine.query.HQLQueryPlan" level="DEBUG" />

<!--myibatis log configure-->
<logger name="com.apache.ibatis" level="TRACE"/>
<logger name="java.sql.Connection" level="DEBUG"/>
<logger name="java.sql.Statement" level="DEBUG"/>
<logger name="java.sql.PreparedStatement" level="DEBUG"/>

<logger name="com.xx.XXController" level="WARN" additivity="false">
	<appender-ref ref="console"/>
</logger>

子节点 - root

该节点是必选节点,可同时使用多个 appender,level 表示日志级别

    <root level="DEBUG">
        <appender-ref ref="STDOUT" />
        <appender-ref ref="FILE"/>
    </root>

子节点 - property

用于自定义变量

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">

    <!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径-->
    <property name="LOG_HOME" value="C:/logs" />

    <!--控制台日志, 控制台输出 -->
    <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>

    <!--文件日志, 按照每天生成日志文件 -->
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--日志文件输出的文件名-->
            <FileNamePattern>${LOG_HOME}/Logback.log.%d{yyyy-MM-dd}.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>
        <!--日志文件最大的大小-->
        <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
            <MaxFileSize>10MB</MaxFileSize>
        </triggeringPolicy>
    </appender>
  ......

案例

异步日志

配置文件如下

<!-- 异步日志 -->
<appender name="testAsyncAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>${log.path}/asyncLog.log</file>
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
        <!--日志文件输出的文件名-->
        <FileNamePattern>${log.path}/asyncLog.%d{yyyy-MM-dd}.log</FileNamePattern>
        <MaxHistory>30</MaxHistory>
        <!-- 文件达到1G后删除旧的日志文件 -->
        <totalSizeCap>1GB</totalSizeCap>
    </rollingPolicy>
    <encoder>
        <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
        <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger{50} - [%thread] - %msg%n</pattern>
    </encoder>
    <!--日志文件最大的大小-->
    <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
        <MaxFileSize>10MB</MaxFileSize>
    </triggeringPolicy>
</appender>
<appender name="asyncAppender" class="ch.qos.logback.classic.AsyncAppender">
    <appender-ref ref="testAsyncAppender" />
    <!-- 设置异步阻塞队列的大小,为了不丢失日志建议设置的大一些,单机压测时100000是没问题的,应该不用担心OOM -->
    <queueSize>10000</queueSize>
    <!-- 设置丢弃DEBUG、TRACE、INFO日志的阀值,不丢失 -->
    <discardingThreshold>0</discardingThreshold>
    <!-- 设置队列入队时非阻塞,当队列满时会直接丢弃日志,但是对性能提升极大 -->
    <neverBlock>true</neverBlock>
</appender>
<logger name="asyncLog" level="info" additivity="false">
    <appender-ref ref="asyncAppender" />
</logger>

在 java 中使用上面的 logger

private static final Logger asyncLog = LoggerFactory.getLogger("asyncLog");
public JsonResult selectByPrimaryKey(@PathVariable Integer pk){
    asyncLog.info("异步日志查询之前");
    BillApplyMoney billApplyMoney = service.selectByPrimaryKey(pk);
    asyncLog.info("异步日志查询之后");
    System.out.println("线程ID和名称分别是:" + Thread.currentThread().getId() + " / " + Thread.currentThread().getName());
    return JsonResult.ok(billApplyMoney);
}

相较于之前的同步日志,是在原来的 appender 上又套了一层异步 appender,之前的 logger 要使用新创建的异步 appender。需要注意的是日志输出中的线程名称和项目内打印的线程名称是一样的,原因的日志框架将当前业务逻辑所在的线程ID以及名称打包传送输出到日志文件中,所以看到两处的线程好像是同一个线程(日志输出中不打印异步日志自己的线程)

配置文件

在项目 resources 下创建名称为 logback-spring.xml 的配置文件

单独控制台输出

<!-- 下面的 debug="false" 表示不打印logback自身的日志 -->
<configuration debug="false">
    <!-- 默认的控制台日志输出,一般生产环境都是后台启动,这个没太大作用 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <Pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{80} - %msg%n</Pattern>
          	<charset>UTF-8</charset>
        </encoder>
    </appender>

    <root level="info">
        <appender-ref ref="STDOUT"/>
    </root>
</configuration>

滚动文件的配置

<appender name="dataTransmitter" class="ch.qos.logback.core.rolling.RollingFileAppender">
  <file>${log.path}/dataTransmitter.log</file>
  <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
    <!--日志文件输出的文件名-->
    <FileNamePattern>${log.path}/dataTransmitter.%d{yyyy-MM-dd}.log</FileNamePattern>
    <MaxHistory>30</MaxHistory>
  </rollingPolicy>
  <encoder>
    <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
    <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger{50} - %msg%n</pattern>
  </encoder>
  <!--日志文件最大的大小-->
  <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
    <MaxFileSize>30MB</MaxFileSize>
  </triggeringPolicy>
</appender>

完整案例

slf4j+logback
slf4j+logback

使用

最简单使用

在类头上使用注解 @slf4j,就可以在代码中使用了,如下图

slf4j+logback01
slf4j+logback01
<configuration>
  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
      <Pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{80} - %msg%n</Pattern>
    </encoder>
  </appender>

  <root level="info">
    <appender-ref ref="STDOUT"/>
  </root>
</configuration>

之后在业务类中使用注解 @Slf4j ,然后使用 log 是将日志打印在 console 中

按照业务逻辑收集打印

logback-spring.xml 在根节点下声明 appender 节点,代码如下

<appender name="hulfProductEnter" class="ch.qos.logback.core.rolling.RollingFileAppender">
  <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
    <!--日志文件输出的文件名-->
    <FileNamePattern>${LOG_HOME}/hulfProductEnter.log.%d{yyyy-MM-dd}.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} %msg%n</pattern>
  </encoder>
  <!--日志文件最大的大小-->
  <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
    <MaxFileSize>10MB</MaxFileSize>
  </triggeringPolicy>
</appender>

同时在 logback-spring.xml 的根节点下声明 logger,第一行代码的 hulfProductEnter 表示日志配置的名称 第二行的 hulfProductEnter 表示使用的 appender 节点的名称,这里关联到上面的 appender

  <logger name="hulfProductEnter" level="info" additivity="false">
      <appender-ref ref="hulfProductEnter" />
  </logger>

业务代码中做下面声明,之后就可以使用 logger.info(xxx); 进行打印日志了 这种方法可以在多个类中声明同一个 appender 的日志对象,将多个类的多个方法的逻辑收集打印到一个日志文件中。(不需要在 root 节点下添加该名称的 appender)

// 导入下面两个包
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private static final Logger logger = LoggerFactory.getLogger("hulfProductEnter");

每个类分别打印

logger # name 后面填写类名称,可以是自定义的类也可以是各个框架的类,其下属子节点 appender-ref 填写 logback-spring.xml 中的 appender 节点属性 name 的名称,这种情况下只要在对应的类头上使用注解 @Slf4j - 声明式日志

<logger name="com.xdf.data_transmitter.utils.DataTransmitter" level="info" additivity="false">
  <appender-ref ref="dataTransmitter" />
</logger>

使用 SpringBoot 配置的参数

从springboot配置变量中获取key为source的值,并重命名为 name 属性指定的名称

<springProperty scope="context" name="LOG_FILE_MAX_SIZE" source="logging.file.max-size" defaultValue="100MB" />
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <maxFileSize>${LOG_FILE_MAX_SIZE}</maxFileSize>
</rollingPolicy>

多日志文件

logger+@slf4j

配置文件中使用 logger 子节点

<logger name="com.xdf.zipperdye.aop.MybatisAspect" level="info" additivity="false">
    <appender-ref ref="sql"/>
</logger>

同时该指定的类 com.xdf.zipperdye.aop.MybatisAspect 类上使用注解 @slf4j ,注意使用中的 level 要和配置文件中的 logger 节点的 level 一致。

root节点引用+代码指定

不需要使用子节点 logger,只要将 appender.name="sql" 放入 root 节点,系统启动后就会向该 appender 指定的文件写入日志,同时要在业务类中使用代码

private static final Logger log = LoggerFactory.getLogger("sql");

指定使用的日志文件。注意像下面代码单独为sql指定 level 是无效的,仍然会遵循 root 的 level

<root level="debug">
  <appender-ref ref="zipperdye" />
  <appender-ref ref="sql" level="info"/>
</root>

这样会导致主日志文件和子日志文件 sql.log 打印同样的内容

为java.sql设定debug

或者使用最简单的方法,将主日志文件设定 level=info,保证打印最少的日志,然后再制作如下面代码一样的两个 logger 运行在 debug 模式下,这样会打印所有的 sql 语句。

<root level="info">
    <appender-ref ref="zipperdye" />
</root>

<logger name="java.sql.Statement" level="DEBUG"/>
<logger name="java.sql.PreparedStatement" level="DEBUG"/>

这样有一点不好是将 sql 打印在主日志文件中了。

打印源码文件名和行号

文件 logback-spring.xml 中如下代码打印出的效果是:
[2024-02-03 12:07:56.013][INFO main][ESLUtils.java#81]: 配置项esl.init=0,不初始化esl系统

<!-- 使用本日志的类:ESLUtils -->
<appender name="esl" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>${log.path}/esl.log</file>
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
        <!--日志文件输出的文件名-->
        <FileNamePattern>${log.path}/esl.%d{yyyy-MM-dd}.log</FileNamePattern>
        <MaxHistory>30</MaxHistory>
    </rollingPolicy>
    <encoder>
        <!--格式化输出:
            %logger{0} - 输出日志的logger名称,例如代码 <logger name="esl工具" level="info" additivity="false"> 中的 "esl工具"
            %d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符
        -->
        <pattern>[%d{yyyy-MM-dd HH:mm:ss.SSS}][%-5level %thread][%F#%L]: %msg%n</pattern>
    </encoder>
    <!--日志文件最大的大小-->
    <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
        <MaxFileSize>30MB</MaxFileSize>
    </triggeringPolicy>
</appender>
<logger name="esl" level="info" additivity="false">
    <appender-ref ref="esl" />
</logger>