spring boot内置:Logback
文章目录
- spring boot内置:Logback
- 一:Logback强在哪?
- 二:简单使用
- 三:把 log4j 转成 logback
- 四:日志门面SLF4J
- 1:什么是SLF4J
- 2:SLF4J 解决了什么痛点
- 3:SLF4J 比 Log4J 强在哪
 
 
一:Logback强在哪?
- 非常自然地实现了 SLF4J,不需要像 Log4j 和 JUL 那样加一个适配层

- 是Spring Boot默认的[一旦某款工具库成为了默认选项,那就说明这款工具已经超过了其他竞品]
- 支持自动重新加载配置文件,不需要另外创建扫描线程来监视
- 性能大大的提升
logback手册:https://github.com/itwanger/logback-chinese-manual
二:简单使用
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.2.3</version>
</dependency>
Maven 会自动导入另外两个依赖:

package org.example.open_source.log;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
 * <p>
 * 功能描述:logback demo test
 * </p>
 *
 * @author cui haida
 * @date 2024/04/05/21:02
 */
public class LogbackDemo {
    static Logger logger = LoggerFactory.getLogger(LogbackDemo.class);
    public static void main(String[] args) {
        logger.info("info");
        logger.debug("debug");
        logger.error("error");
        logger.warn("warn");
    }
}

Logger 和 LoggerFactory 都来自 SLF4J,所以如果项目是从 Log4j + SLF4J 切换到 Logback 的话,此时的代码是零改动的。
在没有配置文件的情况下,一切都是默认的,Logback 的日志信息会输出到控制台。
可以通过 StatusPrinter 来打印 Logback 的内部信息:
LoggerContext lc = (LoggerContext)LoggerFactory.getILoggerFactory();
StatusPrinter.print(lc);

一般来说,我们会在本地环境中配置 logback-test.xml,在生产环境下配置 logback.xml。
<configuration debug="true">
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <!-- encoder将日志变成字节数组并转到输出流 -->
        <encoder>
            <!-- pattern指定格式,各个参数的意义见下图 -->
            <pattern>%d{HH:mm:ss.SSS} %relative [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>
    <root level="debug">
        <appender-ref ref="STDOUT" />
    </root>
</configuration>
Logback 的配置文件非常灵活
最基本的结构为 <configuration> 元素,包含 0 或多个 <appender> 元素,其后跟 0 或多个 <logger> 元素,其后再跟最多只能存在一个的 <root> 元素。
appender

root
只支持一个属性——level,值可以为:TRACE、DEBUG、INFO、WARN、ERROR、ALL、OFF。
appender-ref 用来指定具体的 appender
查看内部状态信息
可以在代码中通过 StatusPrinter 来打印 Logback 内部状态信息
也可以通过在 configuration 上开启 debug 来打印内部状态信息
自动重载配置
之前提到 Logback 很强的一个功能就是支持自动重载配置,那想要启用这个功能也非常简单,只需要在 configuration 元素上添加 scan=true 即可。
<configuration scan="true">
    //...
</configuration>
默认情况下,扫描的时间间隔是一分钟一次。
如果想要调整时间间隔,可以通过 scanPeriod 属性进行调整,单位可以是毫秒(milliseconds)、秒(seconds)、分钟(minutes)或者小时(hours)。
下面这个示例指定的时间间隔是 30 秒:
<configuration scan="true" scanPeriod="30 seconds">
   ...
</configuration>
注意:如果指定了时间间隔,没有指定时间单位,默认的时间单位为毫秒。
当设置 scan=true 后,Logback 会起一个 ReconfigureOnChangeTask 的任务来监视配置文件的变化。
三:把 log4j 转成 logback
如果你的项目以前用的 Log4j,那么可以通过下面这个网址把 log4j.properties 转成 logback-test.xml:
http://logback.qos.ch/translator/

将之前的那个log4j.peoperties拿到之后,将会转成下面的xml
<?xml version="1.0" encoding="UTF-8"?>
<!--  Built in 2024-04-05 01:26 by logback-translator            -->
<!--                                                             -->
<!--  For sponsoring SLF4J/logback/reload4j projects see         -->
<!--    https://github.com/sponsors/qos-ch                       -->
<!--                                                             -->
<!--  Contact sales(at)qos.ch about licensing this tool or       -->
<!--  custom development.-->
<configuration>
    <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
        <Target>System.out</Target>
        <encoder>
            <!-- 问题1:Logback 中没有 %l -->
            <pattern>[%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n</pattern>
        </encoder>
    </appender>
    <!-- 问题2,滚动策略? -->
    <appender name="D" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!--See http://logback.qos.ch/manual/appenders.html#RollingFileAppender-->
        <!--and http://logback.qos.ch/manual/appenders.html#TimeBasedRollingPolicy-->
        <!--for further documentation-->
        <Append>true</Append>
        <File>debug.log</File>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n</pattern>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>DEBUG</level>
        </filter>
    </appender>
    <appender name="E" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!--See http://logback.qos.ch/manual/appenders.html#RollingFileAppender-->
        <!--and http://logback.qos.ch/manual/appenders.html#TimeBasedRollingPolicy-->
        <!--for further documentation-->
        <Append>true</Append>
        <File>error.log</File>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n</pattern>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>ERROR</level>
        </filter>
    </appender>
    <root level="debug">
        <appender-ref ref="stdout"/>
        <appender-ref ref="D"/>
        <appender-ref ref="E"/>
    </root>
</configuration>
来看下面这个 RollingFileAppender 配置:
<appender name="D" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>debug.log</file>
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
        <!-- 按天滚动 -->
        <fileNamePattern>debug.%d{yyyy-MM-dd}.log</fileNamePattern>
        <!-- 保存 30 天的历史记录,最大大小为 30GB -->
        <maxHistory>30</maxHistory>
        <totalSizeCap>3GB</totalSizeCap>
    </rollingPolicy>
    <encoder>
        <pattern>%relative [%thread] %level %logger{35} - %msg%n</pattern>
    </encoder>
</appender>
基于按天滚动的文件策略,最多保留 30 天,最大大小为 30G。
SizeAndTimeBasedRollingPolicy  比 TimeBasedRollingPolicy 多了一个日志文件大小设定的属性:maxFileSize,其他完全一样。
基于我们对 RollingPolicy 的了解,可以把 logback-test.xml 的内容调整为以下内容:
<configuration>
    <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
        <Target>System.out</Target>
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} [%thread] %level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>
    <appender name="D" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <Append>true</Append>
        <File>debug.log</File>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 按天轮转 -->
            <fileNamePattern>debug.%d{yyyy-MM-dd}.log</fileNamePattern>
            <!-- 保存 30 天的历史记录,最大大小为 30GB -->
            <maxHistory>30</maxHistory>
            <totalSizeCap>3GB</totalSizeCap>
        </rollingPolicy>
        <encoder>
            <pattern>%relative [%thread] %-5level %logger{35} - %msg%n</pattern>
        </encoder>
    </appender>
    <appender name="E" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <File>error.log</File>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 按天轮转 -->
            <fileNamePattern>error.%d{yyyy-MM-dd}.log</fileNamePattern>
            <!-- 保存 30 天的历史记录,最大大小为 30GB -->
            <maxHistory>30</maxHistory>
            <totalSizeCap>3GB</totalSizeCap>
        </rollingPolicy>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n</pattern>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>ERROR</level>
        </filter>
    </appender>
    <root level="debug">
        <appender-ref ref="stdout"/>
        <appender-ref ref="D"/>
        <appender-ref ref="E"/>
    </root>
</configuration>
四:日志门面SLF4J
1:什么是SLF4J
SLF4J 是 Simple Logging Facade for Java 的缩写(for≈4),也就是简易的日志门面
以外观模式(Facade pattern,一种设计模式,为子系统中的一组接口提供一个统一的高层接口,使得子系统更容易使用)实现
支持 java.util.logging、Log4J 和 Logback。
SLF4J 的作者就是 Log4J 和 Logback 的作者


2:SLF4J 解决了什么痛点
春秋战国的时候,每个国家都有自己的货币,用别国的货币也不合适,对吧?那在发生贸易的时候就比较麻烦了,货币不统一,就没法直接交易,因为货币可能不等价。
那秦始皇统一六国后,就推出了新的货币政策,全国都用一种货币,那之前的问题就解决掉了。
你看,同样的道理,日志系统有 JUL、JCL,Ceki Gulcu 自己又写了 2 种,Log4j 和 Logback
各有各的优缺点,再加上使用者千千万,萝卜白菜各有所爱,这就导致不同的应用可能会用不同的日志系统。
假设我们正在开发一套系统,打算用 SLF4J 作为门面,Log4j 作为日志系统
我们在项目中使用了 A 框架,而 A 框架的门面是 JCL,日志系统是 JUL,那就相等于要维护两套日志系统,对吧?

这就难受了!
Ceki Gulcu 想到了这个问题,并且帮我们解决了!来看 SLF4J 官网给出的解决方案。

- 使用 jcl-over-slf4j.jar替换 commons-logging.jar
- 引入 jul-to-slf4j.jar
3:SLF4J 比 Log4J 强在哪
SLF4J 除了解决掉以上的痛点,帮助我们的应用程序独立于任何特定的日志系统,还有一个非常牛逼的功能,那就是 SLF4J 在打印日志的时候使用了占位符 {},
它有点类似于 String 类的 format() 方法(使用 %s 等填充参数),但更加便捷,这在很大程度上提高了程序的性能。
众所周知,字符串是不可变的,字符串拼接会创建很多不必要的字符串对象,极大的消耗了内存空间。但 Log4J 在打印带参数的日志时,只能使用字符串拼接的方式:
String name = "张三";
int age = 18;
logger.debug(name + ",年纪:" + age + ",hello world");
非常笨重,但加入了 SLF4J 后,这个问题迎刃而解。我们来看一下在 Log4j 项目中加入 SLF4J 的详细的步骤。
第一步,把 log4j 的依赖替换为 slf4j-log4j12(Maven 会自动引入 slf4j-api.jar 和 log4j.jar):
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.7.25</version>
</dependency>
第二步,在 resources 目录下创建 log4j.properties 文件
# config rootLogger
log4j.rootLogger = debug,stdout,D,E
# config logger stdout
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n
# config logger debug
# if the log level is debug, the log will be output to debug.log
log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File = debug.log
log4j.appender.D.Append = true
log4j.appender.D.Threshold = DEBUG
log4j.appender.D.layout = org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n
# config logger error
# if the log level is error, the log will be output to error.log
log4j.appender.E = org.apache.log4j.DailyRollingFileAppender
log4j.appender.E.File =error.log
log4j.appender.E.Append = true
log4j.appender.E.Threshold = ERROR
log4j.appender.E.layout = org.apache.log4j.PatternLayout
log4j.appender.E.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n
第三步,新建测试类:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
 * @author cuihaida
 */
public class Log4jSLF4JDemo {
    private static final Logger logger = LoggerFactory.getLogger(Log4jSLF4JDemo.class);
    public static void main(String[] args) {
        logger.debug("{},hello world","张三");
    }
}
看到了吧,使用占位符要比“+”操作符方便的多。并且此时不再需要 isDebugEnabled() 先进行判断,debug() 方法会在字符串拼接之前执行。
如果只是 Log4J 的话,会先进行字符串拼接,再执行 debug() 方法
这也就意味着,如果日志系统的级别不是 DEBUG,就会多执行了字符串拼接的操作,白白浪费了性能。



















