博客摘录 Java中的日志框架总结

日志框架

Java中现有的日志框架:

  1. 日志门面:JCL(Jakarta Common Logging)、SLF
  2. 日志实现:JUL(Java Util Logging)、log4j、logback、log4j2

logbacklog4j 功能更加强大,性能更加好;log4j2logback 功能相似,但前者的性能更好

1、JUL

1.1 JUL介绍

JUL:全称 Java Util Logging,它是 Java 原生的日志框架,使用时不需要引入第三方类库,相对其它日志框架使用方便、学习简单,能够在小型应用中灵活使用。

架构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XLFQIjbC-1682563442313)(images/c4448535e6a34d8a9e99e094004a31f3.png)]

  • Loggers:记录器。应用程序通过获取 Logger 对象,调用其 API 来发布日志信息。Logger 对象通常是应用程序访问日志系统的入口程序;
  • Appenders:也被称为 Handlers。每个 Logger 都会关联一组 Handlers。 Logger 会将日志交给关联的 Handlers 处理,由 Handlers 负责将日志做记录。 Handlers 在此是抽象的。其具体的实现决定了日志记录的位置可以是控制台、文件等;
  • Layouts:也被称为 Formatters。它负责对日志事件中的数据进行转换和格式化。Layouts 决定了数据在一条日志记录中的最终形式;
  • Level:每条日志消息都有一个关联的日志级别。该级别粗略指导了日志消息的重要性和紧迫。可以将 Level 和 Loggers、Appenders 做关联,以便于我们过滤消息;
  • Filters:过滤器。根据需要定制哪些消息会被记录,哪些消息会被放过。

【总结】:用户使用 Logger 来进行日志记录,Logger 持有多个 Handlers,日志的输出操作是由 Handler 完成。在 Handler 输出日志前,会经过 Filters 的过滤,判断哪些日志级别放行,哪些日志级别拦截。Handler 会将日志内容输出到指定位置(控制台/文件)。Handler 在输出日志时会使用 Layout,将输出内容进行排版。

1.2 使用案例
public class JulTest {

	public static final String CALSS_NAME = "com.zzc.sbexp.log.jul.JulTest";

    @Test
    public void testQuick() {
        Logger logger = Logger.getLogger(CALSS_NAME);
        // 输出日志 日志级别:info
        logger.info("hello jul");

        // 通过方法输出
        logger.log(Level.INFO, "hello jul");

        // 通过占位符输出变量值
        String name = "zzc";
        Integer age = 24;
        logger.log(Level.INFO, "用户信息:{0}, {1}", new Object[]{name, age});
    }

}
1.3 日志级别

日志级别从高至低:offserverwarninginfoconfigfinefinerfinestall

如果是:off,那么就是关闭了日志级别输出;
如果是:all,那么就是开启了日志级别输出。

Jul 默认级别是 info,只要日志级别高于 info,都会输出。

自定义日志级别

步骤:

  1. 关闭系统默认配置
  2. 创建一个 Handler。我这里是输出到控制台和磁盘,所以是 ConsoleHandlerFileHandler
  3. 创建一个 Formatter
  4. HandlerFormatter 进行关联;
  5. Handler 添加到 Logger 中。
@Test
public void testLogConfig() throws IOException {
    Logger logger = Logger.getLogger(CALSS_NAME);
    // 关闭系统默认配置
    logger.setUseParentHandlers(false);
    // 创建控制台输出Handler
    ConsoleHandler consoleHandler = new ConsoleHandler();
    SimpleFormatter simpleFormatter = new SimpleFormatter();
    // 关联
    consoleHandler.setFormatter(simpleFormatter);
    // 创建文件输出Handler
    FileHandler fileHandler = new FileHandler("logs.log");
    fileHandler.setFormatter(simpleFormatter);

    logger.addHandler(consoleHandler);
    logger.addHandler(fileHandler);
    // 配置日志级别
    logger.setLevel(Level.ALL);
    consoleHandler.setLevel(Level.ALL);
    fileHandler.setLevel(Level.WARNING);

    logger.severe("server");
    logger.warning("warning");
    logger.info("info");
}

【注意】:logs.log 的父级路径必须得存在,否则,会报错。

自定义配置文件修改 RootLogger 的默认配置

在 resource 路径下添加一个配置文件 logging.properties,其内容为:

#RootLogger默认的处理器,可以配置多个,所有非手动解除父日志的子日志都将使用这些处理器
handlers= java.util.logging.ConsoleHandler,java.util.logging.FileHandler

#RootLogger的日志级别(默认INFO),所有的Handler都受限于此日志级别,Handler的日志级别可以比RootLogger的日志级别
.level = INFO

java.util.logging.FileHandler.pattern = E:/logs/java%u.log
#单个日志文件大小,单位是bit,1024bit即为1kb
java.util.logging.FileHandler.limit = 1024 * 1024 * 10
#日志文件数量
java.util.logging.FileHandler.count = 1
#是否以追加方式添加日志内容
java.util.logging.FileHandler.append = true
java.util.logging.FileHandler.encoding = UTF-8
java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter

java.util.logging.ConsoleHandler.level = ALL
java.util.logging.ConsoleHandler.encoding = UTF-8
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter

代码:

@Test
public void testLogProperties() throws IOException {
    // 通过类加载加载配置文件
    InputStream in = JulTest.class.getClassLoader().getResourceAsStream("logging.properties");
    // 创建LogManger加载配置文件
    LogManager logManager = LogManager.getLogManager();
    logManager.readConfiguration(in);
    Logger logger = Logger.getLogger(CALSS_NAME);

    logger.severe("server");
    logger.warning("warning");
    logger.info("info");
    logger.finest("finest");
}

自定义配置文件添加自定义的 Logger

在 resource 路径下添加一个配置文件 logging.properties,其内容为:

...

my.logger.handlers = java.util.logging.ConsoleHandler
my.logger.level = INFO
my.logger.useParentHandlers = false

代码:

@Test
public void testMyLogProperties() throws IOException {
    // 通过类加载加载配置文件
    InputStream in = JulTest.class.getClassLoader().getResourceAsStream("logging.properties");
    // 创建LogManger加载配置文件
    LogManager logManager = LogManager.getLogManager();
    logManager.readConfiguration(in);
    Logger logger = Logger.getLogger("my.logger");

    logger.severe("server");
    logger.warning("warning");
    logger.info("info");
    logger.finest("finest");
}

2、Log4j

2.1 Log4j 介绍

Log4jApache 下的一款开源的日志框架,通过在项目中使用 log4j,我们可以控制日志信息输出的位置(控制台、文件、数据库);也可以控制每一条日志的输出格式,通过定义日志的输出级别,可以更灵活的控制日志的输出过程,方便项目的调试。

Log4j 组件

Log4j 主要由 Loggers(日志记录器)、Appenders(输出端)、Layout(日志格式化器)组成。

  1. Loggers:控制日志的输出级别与日志是否输出

Loggers:日志记录器,负责处理日志记录。实例的命名就是类的全限定名。Logger 的名字大小写敏感。其命名有继承机制。如:org.apache.commons 的 logger 会继承 org.apache 的 logger。

Log4j 中,有一个特殊的 logger,叫 root,它是所有的 logger 的根。root logger 可以通过 Logger.getRootLogger() 方法获取

  1. Appenders:指定日志的输出方式

Appenders 用来指定日志输出到哪个地方,可以同时指定日志的输出目的地(控制台、文件)。常用的目的地有以下几种:

  • ConsoleAppender:将日志输出到控制台
  • FileAppender:将日志输出到文件中
  • DailyRollingFileAppender:将日志输出到一个日志文件,并且每天输出到一个新的文件
  • RollingFileAppender:将日志输出到一个日志文件,并且指定文件的尺寸,当文件大小达到指定尺寸时,会自动把文件改名,同时,产生一个新的文件
  • JDBCAppender:将日志保存到数据库
  1. Layout:控制台日志信息的输出格式

Layouts· 用于控制日志输出内容的格式,让我们可以使用各种需要的格式输出日志。常用的 Layouts:

HTML Layout:格式化日志输出为 HTML 表格形式
Simple Layout:简单的日志输出格式化。打印的日志格式为(info - message)
Pattern Layout:最强大的格式化器。可以根据自定义格式输出日志,如果没有指定转换格式,则使用默认的转换格式

2.2 使用案例

新建一个 SpringBoot 工程,引入 log4j 依赖:

<dependency>
	<groupId>log4j</groupId>
	<artifactId>log4j</artifactId>
	<version>1.2.17</version>
</dependency>
public class Log4jTest {

    @Test
    public void testQuick() {
        Logger logger = Logger.getLogger(Log4jTest.class);
        logger.info("info");
    }

}

执行后,查看控制台:

由此可知,需要进行系统配置。一般用配置文件进行配置,这里暂时先用代码进行配置。

@Test
public void testQuick() {
    BasicConfigurator.configure();
    Logger logger = Logger.getLogger(Log4jTest.class);
    logger.info("info");
}

这样,日志信息就能正常输出。

2.3 日志级别

fatal:严重错误。一般会造成系统崩溃并终止运行
error:错误信息。不会影响系统运行
warn:警告信息。可能会发生问题
info:运行信息。数据连接、网络连接、IO操作等等
debug:调试信息。一般在开发中使用,记录程序变量参数传递信息等等
trace:追踪信息。记录程序所有的流程信息

Log4j 默认级别是 debug,只要日志级别高于 debug,都会输出。

2.4 使用配置文件

resources 下新建一个配置文件 log4j.properties

# 指定 RootLogger 顶级父元素默认配置信息
# 指定日志级别=info,使用的 appender 是 console
log4j.rootLogger = info,console
# 指定控制台日志输出的 appender
log4j.appender.console = org.apache.log4j.ConsoleAppender
# 指定消息格式 layout
log4j.appender.console.layout = org.apache.log4j.SimpleLayout

在代码中使用:

@Test
public void testProperties() {
    Logger logger = Logger.getLogger(Log4jTest.class);
    logger.fatal("fatal");
    logger.error("error");
    logger.warn("warn");
    logger.info("info");
    logger.debug("debug");
    logger.trace("trace");
}
  1. 自定义配置 Appender 的格式

log4j.properties 配置文件中,我们定义了日志输出级别与输出端,在输出端中分别配置日志的输出格式:

# 指定 RootLogger 顶级父元素默认配置信息
# 指定日志级别=info,使用的 appender 是 console
log4j.rootLogger = info,console
# 指定控制台日志输出的 appender
log4j.appender.console = org.apache.log4j.ConsoleAppender
# 指定消息格式 layout
log4j.appender.console.layout = org.apache.log4j.PatternLayout
#指定消息格式的内容
log4j.appender.console.layout.conversionPattern = [%p]%r  %l %d{yyyy-MM-dd HH:mm:ss} %m%n

log4j 采用类似 C 语言的 printf() 函数的打印格式格式化日志信息,具体的占位符及其含义如下:

%m=输出代码中指定的信息
%p=输出优先级
%n=换行符
%r=输出自应用启动到输出该 log 信息耗费的毫秒数
%c=输出打印语句所属的类的全限定名
%t=输出产生该日志的线程全名
%d=输出服务器当前时间。默认为 ISO8601。可指定格式:%d{yyyy年MM月dd日 HH:mm:ss}
%l=输出日志发生位置。包括:类名、线程名及在代码中的行数
%F=输出日志消息产生时所在的文件名称
%L=输出代码中的行号
%%=输出一个 % 字符

可以在 % 与字符之间加上修饰符来控制最小宽度,最大宽度和文本的对其方式。如:

%5c=输出类的全限定名,最小宽度是5,类名小于5,默认情况下右对齐
%-5c=输出类的全限定名,最小宽度是5,类名小于5,“-” 指定左对齐,会有空格
%.5c=输出类的全限定名,最大宽度是5,类名大于5,就会将左边多出的字符截掉;小于5,不会有空格
%20.30c%=类名小于20,补空格、右对齐;大于30,就会将左边多出的字符截掉
  1. 自定义配置文件的 FileAppender

log4j.properties:将日志信息输出到文件 FileAppender

# 指定 RootLogger 顶级父元素默认配置信息
# 指定日志级别=info,使用的 appender 是 console
log4j.rootLogger = info,console,rollFile
# 指定控制台日志输出的 appender
log4j.appender.console = org.apache.log4j.ConsoleAppender
# 指定消息格式 layout
log4j.appender.console.layout = org.apache.log4j.PatternLayout
# 指定消息格式的内容
log4j.appender.console.layout.conversionPattern = [%p]%r  %l %d{yyyy-MM-dd HH:mm:ss} %m%n

# 指定控制台日志输出的 appender
log4j.appender.file = org.apache.log4j.FileAppender
# 指定消息格式 layout
log4j.appender.file.layout = org.apache.log4j.PatternLayout
# 指定消息格式的内容
log4j.appender.file.layout.conversionPattern = [%p]%r  %l %d{yyyy-MM-dd HH:mm:ss} %m%n
# 指定日志文件的保存路径
log4j.appender.file.file = E:/temp/logs.log
# 指定日志文件的字符集
log4j.appender.file.encoding = UTF-8

log4j.properties:按照文件大小进行拆分

# 指定 RootLogger 顶级父元素默认配置信息
# 指定日志级别=info,使用的 appender 是 console
log4j.rootLogger = info,console,rollFile
# 指定控制台日志输出的 appender
log4j.appender.console = org.apache.log4j.ConsoleAppender
# 指定消息格式 layout
log4j.appender.console.layout = org.apache.log4j.PatternLayout
# 指定消息格式的内容
log4j.appender.console.layout.conversionPattern = [%p]%r  %l %d{yyyy-MM-dd HH:mm:ss} %m%n

# 按照文件大小进行拆分的 appender 对象
log4j.appender.rollFile = org.apache.log4j.RollingFileAppender
# 指定消息格式 layout
log4j.appender.rollFile.layout = org.apache.log4j.PatternLayout
# 指定消息格式的内容
log4j.appender.rollFile.layout.conversionPattern = [%p]%r  %l %d{yyyy-MM-dd HH:mm:ss} %m%n
# 指定日志文件的保存路径
log4j.appender.rollFile.file = E:/temp/logs.log
# 指定日志文件的字符集
log4j.appender.rollFile.encoding = UTF-8
# 指定日志文件内容的大小
log4j.appender.rollFile.maxFileSize = 1MB
# 指定日志文件的数量
log4j.appender.rollFile.maxBackupIndex = 10

log4j.properties:按照时间大小进行拆分

# 指定 RootLogger 顶级父元素默认配置信息
# 指定日志级别=info,使用的 appender 是 console
log4j.rootLogger = info,dailyFile

# 按照时间大小进行拆分的 appender 对象
log4j.appender.dailyFile = org.apache.log4j.DailyRollingFileAppender
# 指定消息格式 layout
log4j.appender.dailyFile.layout = org.apache.log4j.PatternLayout
# 指定消息格式的内容
log4j.appender.dailyFile.layout.conversionPattern = [%p]%r  %l %d{yyyy-MM-dd HH:mm:ss} %m%n
# 指定日志文件的保存路径
log4j.appender.dailyFile.file = E:/temp/logs.log
# 指定日志文件的字符集
log4j.appender.dailyFile.encoding = UTF-8
# 指定日志文件拆分的规则
log4j.appender.dailyFile.datePattern = '.'yyyy-MM-dd-HH-mm-ss
  1. 自定义 Logger

log4j.properties

# 指定 RootLogger 顶级父元素默认配置信息
# 指定日志级别=info,使用的 appender 是 console
log4j.rootLogger = trace,console

# 自定义 logger,也会继承RootLogger,所以也会输出到控制台
log4j.logger.com.zzc.sbexp.log.log4j = info,file
# apache 的logger,只有error级别及以上才会输出到控制台,并不会输出到文件
log4j.logger.org.apache = error

...

运行后:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4vFapvhx-1682563411050)(images/849957d3337b405fbd8f769138e01e03.png)]

前言

JUL 用起来简单、方便,不需要引入第三方依赖,适合小型应用。Log4j 同样适用起来简单,但它配置更为强大。那么,我们该如何选择这两个日志框架呢?其实,这根据我们项目需求而定的。

举个简单例子:在项目初期,功能简单,我们可以使用比较简单的日志框架 JUL。因为,它使用起来灵活,不需要导入第三方依赖。随着功能的日益完善,当初的日志框架无法满足现在的需求,那么就得进行日志的升级了,如:切换成 Log4j。

如果从 JUL 切换成 Log4j,那么代码一定会受到影响,且之前记录的日志信息也得修改(改动大),这并不是我们程序员希望看到的。为了解决这一问题,Apache 组织站出来了,它将当时主流的日志框架(JULLog4j)统一一套 API。那么,后期在软件开发阶段需要关联一套统一的 API 就可以操作某一个日志实现框架的具体日志记录了。即:日志框架修改了,但这套 API 是没变的。而这套 API 就是指 JCL

3、JCL

3.1 JCL介绍

JCL:全称 Jakarta Common Logging ,是 Apache 提供的一个通用日志 API。

它为 “所有的 Java 日志实现”提供了一个统一的接口,它自身也提供了一个日志的实现,但功能非常弱(impleLog)。所以,一般不会单独使用它。它允许开发者使用不同的具体日志实现工具:Log4j、JUL

JCL 有两个基本的抽象类:Log、LogFactory(负责创建 Log)

3.2 使用案例

新建一个 Maven 工程,引入一个 commons 依赖,POM 文件:

<dependency>
	<groupId>commons-logging</groupId>
	<artifactId>commons-logging</artifactId>
	<version>1.2</version>
</dependency>
public class JclTest {

    @Test
    public void testQuick() {
        Log log = LogFactory.getLog(JclTest.class);
        log.info("info");
        log.error("error");
    }

}

运行后:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cqydFSkm-1682563411050)(images/c400cb88b15c40239b38e8f876411d71.png)]

这种格式,由于没有导入日志的实现依赖,就使用默认的 JDK14 Logger(JUL)

导入Log4j

<dependency>
  <groupId>log4j</groupId>
  <artifactId>log4j</artifactId>
  <version>1.2.17</version>
</dependency>

运行后:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PUmGJcSW-1682563411051)(images/d60fc10269c4463dae17e2cf8a851ef9.png)]

弹出警告:Log4j 没有系统配置。来,咱们加上 log4j.properties 配置文件

# 指定 RootLogger 顶级父元素默认配置信息
# 指定日志级别=info,使用的 appender 是 console
log4j.rootLogger = trace,console

# 指定控制台日志输出的 appender
log4j.appender.console = org.apache.log4j.ConsoleAppender
# 指定消息格式 layout
log4j.appender.console.layout = org.apache.log4j.PatternLayout
# 指定消息格式的内容
log4j.appender.console.layout.conversionPattern = [%p]%r  %l %d{yyyy-MM-dd HH:mm:ss} %m%n

运行后:
在这里插入图片描述

这种日志格式,就是使用了 log4j

看,修改日志的实现,不需要修改代码。这就是面向接口设计的理念。

3.3 为什么使用日志门面
  1. 面向接口开发,不再依赖具体的实现类,减少代码的耦合
  2. 项目通过导入不同的日志实现类,可以灵活地切换日志框架
  3. 统一 API,方便开发者学习
  4. 统一配置,便于项目日志的管理
3.4 JCL原理

根据上面案例我们知道:当我们没有引入日志的实现的依赖时,它会使用日志的默认实现 JUL;但当我们引入了 log4j 后,自然就使用 log4j 日志。那么,jcl 到底是如何做到的呢?

1、 通过 LogFactory 动态地加载 Log 实现

在这里插入图片描述

接口 Log 有 4 个实现类:JDK14LoggerLog4jLoggerJdk13LumberjackLoggerSimpleLog

通过 LogFactory 动态地获取 Log(哪个实现类)

2、日志门面支持的日志实现数组

private static final String[] classesToDiscover = {
        "org.apache.commons.logging.impl.Log4JLogger",
        "org.apache.commons.logging.impl.Jdk14Logger",
        "org.apache.commons.logging.impl.Jdk13LumberjackLogger",
        "org.apache.commons.logging.impl.SimpleLog"
};

注意:这个数组中的元素的顺序!!第一个是: Log4JLogger

3、获取具体日志的实现

for(int i=0; i<classesToDiscover.length && result == null; ++i) {
    result = createLogFromClass(classesToDiscover[i], logCategory, true);
}

循环 jcl 中已经实现了的 4 个类,然后调用方法 createLogFromClass()

重点看方法 createLogFromClass()

// 只看重点逻辑
private Log createLogFromClass(String logAdapterClassName,
                               String logCategory,
                               boolean affectState)
    throws LogConfigurationException {
    Class c;
    Log logAdapter = null;
    for (;;) {
	    try {
	        c = Class.forName(logAdapterClassName, true, currentCL);
	    } catch (NoClassDefFoundError e) {
	    	...
	    	break;
	    }
	    constructor = c.getConstructor(logConstructorSignature);
	    Object o = constructor.newInstance(params);
	    if (o instanceof Log) {
            logAdapterClass = c;
            logAdapter = (Log) o;
            break;
        }
    }
    return logAdapter;
}

这个方法的逻辑:通过 Class.forName(String className) 来加载 Log 的实现类(来自于 classesToDiscover 数组),如果此类存在,则通过反射创建其实例并返回;如果不存在,则直接返回。

再看看 for 循环中,如果返回的结果不为 null,则跳出循环,继续往下执行;否则,继续循环。

那么,当我们没有引入 log4j 的依赖时,也是存在 Log4JLogger 的(jcl 的实现类)。并且,它是位于要循环的数组中的第一个元素,但是,它并不会加载成功。因为 Log4JLogger 类中引入了 log4j 的依赖,依赖于 log4j。如:import org.apache.log4j.Logger; 所以,在调用 Class.forName(String className) 时会失败。然后继续循环,尝试加载第二个元素 Jdk14Logger,这个依赖就存在 jcl 依赖中,所以,它就会加载成功。

没有引入 log4j 依赖时,org.apache.commons.logging.impl.Log4JLogger 类:
在这里插入图片描述

那么,当我们引入 log4j 依赖时,org.apache.commons.logging.impl.Log4JLogger 类就显得可用了,所以,它会一次加载成功,不用循环数组

JCL 只支持以上4种实现类,如果还想支持其它三方日志框架,就得修改 jcl 源码了。难道使用起来这么困难吗?其实不然,早在2014年,jcl 就被 apache 组织给淘汰了,后期就推出了更加优秀的日志门面框架。

4、SLF4j

4.1 SLF介绍

SLF 主要是为了给 java 日志访问提供一套标准、规范的 API 框架。其主要意义在于提供接口,具体的实现可以交由其它日志框架。如:logbacklog4jlog4j2

当然,SLF 自己也提供了较为简单的实现,但一般很少用。对于一般的 java 项目而言,日志框架会选择 slf4j-api 作为门面,配上具体的实现框架(log4j、logback 等。即: slf + logback、slf + log4j2),中间使用桥接器完成桥接。SLF 是目前市面上最流行的日志门面。现在的项目中,基本上都是使用 SLF 作为日志系统。

SLF 日志门面主要提供两大功能:

  1. 日志框架的绑定
  2. 日志框架的桥接
4.2 使用案例

新建一个 Maven 工程,引入一个 slf4j-api 依赖,POM 文件:

<!--slf日志门面-->
<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-api</artifactId>
  <version>1.7.26</version>
</dependency>
<!--slf内置简单的实现-->
<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-simple</artifactId>
  <version>1.7.21</version>
</dependency>
public class SlfTest {

    public static final Logger LOGGER = LoggerFactory.getLogger(SlfTest.class);

    @Test
    public void testQuick() {
        LOGGER.error("error");
        LOGGER.warn("warn");
        LOGGER.info("info");
        LOGGER.debug("debug");
        LOGGER.trace("trace");

		// 使用占位符
        String name = "zzc";
        Integer age = 24;
        LOGGER.info("用户信息:{}, {}", name, age);
    }

}

运行后,结果如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5GCrMaJa-1682563411055)(images/9e3356b00ca84f5f931a416593b3a29a.png)]

4.3 SLF的日志绑定

上面的案例是使用了 SLF 绑定了 简单的日志实现。那么,SLF 是如何绑定其它的主流日志框架呢?SLF4J 官网的用户手册 上有详细的介绍:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JRKK7KVb-1682563411055)(images/a884877819d74aacb43a4db7a1cfe0ff.png)]

上面这副图就表明了 SLF 是如何绑定其它的主流日志框架。

如果 Java 应用中需要使用日志记录的话,则首先需要引入 slf4j-api.jar 的依赖,统一日志接口。并且,它需要引入具体的实现,共有三种情况:

  1. 没有引入具体的实现:那么,日志功能将不能起作用
  2. 引入了一类实现(上图的蓝色框:slf4j-logbackslf4j-simpleslf4j-nop),由于它们的设计比 SLF 要晚,所以默认遵循 SLF 的规范,只需要导入它们的依赖就可使用
  3. 引入了另一类实现(log4jjdk14),它们的设计比 SLF 要早,在设计之初,并没有遵循 SLF 的规范,无法直接进行绑定。所以,需要添加一个适配层 Adaptation layer。通过适配器进行适配,从而间接地遵循了 SLF 的规范。

使用 SLF 的日志绑定流程:

1、添加 slf4j-api 的依赖
2、使用 slf4j 的 API 在项目中进行统一的日志记录
3、 绑定具体的日志实现

- 绑定了已经实现sfl4j的日志框架,直接添加对应的依赖
- 绑定了没有实现slf4j的日志框架,先添加日志的适配器,再添加实现类的依赖

4、slf4j 有且仅有一个日志实现框架的绑定(如果出现多个,默认使用第一个依赖日志实现)

接下来就对剩下的日志实现进行案例演示

logback:

<!--logback-->
<dependency>
   <groupId>ch.qos.logback</groupId>
   <artifactId>logback-classic</artifactId>
   <version>1.2.3</version>
 </dependency>

logback-classic 包含了 logback-core 的依赖。

【注意】:引入了 logback-classic 的依赖后,得去掉的 slf4j-simple 的依赖(入门案例时引入的)

入门案例代码不变,运行后:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-suYIDbYx-1682563411056)(images/0c2b891abc6b451b8fcffcdecf893658.png)]

nop:

slf4j-nop 是日志的开关,引入它后,表示系统关闭日志系统

<!-- nop 日志的开关-->
<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-nop</artifactId>
  <version>1.7.2</version>
</dependency>

运行后,控制台并不会输出日志信息

log4j:

<!--绑定log4j实现,需要导入适配器-->
<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-log4j12</artifactId>
  <version>1.7.25</version>
</dependency>
<dependency>
  <groupId>log4j</groupId>
  <artifactId>log4j</artifactId>
  <version>1.2.17</version>
</dependency>

需要引入 log4j.properties,这里就拿之前的就行。

运行结果如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iQz2wHvg-1682563411056)(images/9061c19846f8425586332fa293d824ea.png)]

jdk14:

<!--绑定jdk14实现-->
<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-jdk14</artifactId>
  <version>1.7.25</version>
</dependency>

运行结果如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gWwz7F0M-1682563411057)(images/d64259c38419483884a4292b34b942d6.png)]

4.4 SLF的日志绑定原理

LoggerFactoryLogger 是根据 LoggerFactory 获取的实例

public static Logger getLogger(String name) {
    ILoggerFactory iLoggerFactory = getILoggerFactory();
    return iLoggerFactory.getLogger(name);
}

LoggerFactory#getILoggerFactory():获取具体的工厂实例

public static ILoggerFactory getILoggerFactory() {
    if (INITIALIZATION_STATE == 0) {
    	//...
        performInitialization();
    }

    switch(INITIALIZATION_STATE) {
    //...
    case 3:
        return StaticLoggerBinder.getSingleton().getLoggerFactory();
    //...
}

LoggerFactory#bind():绑定所有的实现类

private static final void bind() {
    String msg;
    try {
        Set<URL> staticLoggerBinderPathSet = null;
        if (!isAndroid()) {
        	// 1.找出所有的日志实现
            staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();
            // 2.如果实现类大于1,则会记录下来
            reportMultipleBindingAmbiguity(staticLoggerBinderPathSet);
        }
		
		// 3.进行日志实现类org.slf4j.impl.StaticLoggerBinder.class加载
        StaticLoggerBinder.getSingleton();
        INITIALIZATION_STATE = 3;
        reportActualBinding(staticLoggerBinderPathSet);
        //...
    } catch (NoClassDefFoundError var2) {
        // ...
    }
}

说明:

  1. findPossibleStaticLoggerBinderPathSet();:通过类加载器在类路径下(包括 jar 包)查找包含org.slf4j.impl.StaticLoggerBinder.class 类的路径。即:日志实现的 jar 就包含
  2. reportMultipleBindingAmbiguity():如果引入了多于1个的日志实现,则会记录下来
  3. StaticLoggerBinder.getSingleton();:调用此方法时,会导致类加载,并返回日志实现的单例实例(即使引入了多个日志实现的类,那也只会返回同一个实例)。
4.5 SLF 的日志桥接器

Java 工程中使用的日志框架依赖于 SLF 以外的 API。为了使日志框架看上去是 SLF API 的实现,将使用 SLF 附带了几个桥接模块。这些模块将对 Log4j、jcl 的调用重定向,就好像它们是对 SLF API 一样。

桥接解决的项目中日志的遗留问题,当系统中存在之前的日志 API,可以通过桥接转换到 SLF 的实现。

步骤:

  1. 先去除之前老的日志框架的依赖
  2. 添加 SLF 提供的桥接组件
  3. 为项目添加 SLF 的具体实现

案例 ------ log4j:

假若之前,工程中使用的是 log4j

<dependency>
  <groupId>log4j</groupId>
  <artifactId>log4j</artifactId>
  <version>1.2.17</version>
</dependency>

然后,在类路径下添加:log4j.properties

添加测试代码

public class Log4jTest {

    @Test
    public void testQuick() {
        Logger logger = Logger.getLogger(com.zzc.log.log4j.Log4jTest.class);
        logger.fatal("fatal");
        logger.error("error");
        logger.warn("warn");
        logger.info("info");
        logger.debug("debug");
        logger.trace("trace");
    }

}

运行结果后:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MArx5zGw-1682563411057)(images/9f71ae9d55c04d0da4e0fcf1df2a2172.png)]

现在,我需要在工程中使用 SLF API(SLF + logback),而不使用 log4j

引入依赖:

<!--slf日志门面-->
<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-api</artifactId>
  <version>1.7.26</version>
</dependency>
<!--logback-->
<dependency>
  <groupId>ch.qos.logback</groupId>
  <artifactId>logback-classic</artifactId>
  <version>1.2.3</version>
</dependency>
<!--配置log4j的桥接器-->
<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>log4j-over-slf4j</artifactId>
  <version>1.7.24</version>
</dependency>

测试代码不变

运行结果如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4OW83Bht-1682563411057)(images/9c44b526a1d1466898940722c6af834e.png)]

输出格式:logback(用的它的实现)

【注意】:桥接器和适配器不能一起使用。否则,就是出现 栈溢出。

5、logback

5.1 logback介绍

logback 是由 log4j 创始人设计的另一个开源日志组件,性能比 log4j

logback 主要分为 3 个模块:

  • logback-core:其它两个模块的基础模块
  • logback-classic:它是 log4j 的一个改良版本,同时它完整实现了 slf4j API
  • logback-access:访问模块与 Servlet 容器集成提供通过 http 来访问日志的功能
5.2 使用案例
<!--slf日志门面-->
<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-api</artifactId>
  <version>1.7.26</version>
</dependency>
<!--logback-->
<dependency>
  <groupId>ch.qos.logback</groupId>
  <artifactId>logback-classic</artifactId>
  <version>1.2.3</version>
</dependency>
public class LogbackTest {

    public static final Logger LOGGER = LoggerFactory.getLogger(LogbackTest.class);

    @Test
    public void testQuick() {
        LOGGER.error("error");
        LOGGER.warn("warn");
        LOGGER.info("info");
        LOGGER.debug("debug");
        LOGGER.trace("trace");
    }

}

运行结果如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-m8TWnN7N-1682563411058)(images/92b1d185b0b94862a2a23e960bf22192.png)]

表明:logback 的默认级别是 debug

5.3 配置文件

logback 依次读取以下类型的配置文件:

  • logback.groovy
  • logback-test.xml
  • logback.xml

如果均不存在,则采用默认配置

  1. logback 组件之间的关系
  • Logger:日志记录器。把它关联到应用的对应的 context 上后,主要用于存放日志对象,也可以定义日志类型、级别
  • Appender:用于指定日志输出的目的地。目的地可以是控制台、文件、数据库等
  • Layout:负责把事件转换成字符串,格式化日志信息输出。在 logback 中,Layout 对象被封装成 encoder 中
  1. 控制台 Appender

控制台 Appender:将日志信息输出到控制台,并且可以设置一些日志信息。

在 resources 下新建一个 logback.xml 配置文件

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <!--配置集中管理属性-->
    <property name="pattern" value="[%-5level] %d{yyyy-mm-dd HH:mm:ss.SSS} %c %M %L [%thread] %m%n"></property>

    <!--
        %-5level:日志级别。向左对齐
        %d{yyyy-mm-dd HH:mm:ss.SSS}:日期(格式化)
        %c:方法名称
        %M:类名
        %thread:线程名
        %m:日志信息
        %n:换行
    -->
    <!--控制台日志输出的 Appender-->
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <!--控制输出流对象,默认为 System.out,可修改为 System.err-->
        <target>System.err</target>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${pattern}</pattern>
        </encoder>
    </appender>

    <!--root logger 配置-->
    <root level="ALL">
        <appender-ref ref="console"></appender-ref>
    </root>
    
</configuration>

配置了配置文件后,运行结果如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ib0O9LOM-1682563411058)(images/1fba74abd5194225932fadb02eb977b0.png)]

  1. 文件 Appender

文件 Appender:将日志信息存放到日志文件中

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <!--配置集中管理属性-->
    <property name="pattern" value="[%-5level] %d{yyyy-mm-dd HH:mm:ss.SSS} %c %M %L [%thread] %m%n"></property>
    <property name="log_dir" value="E:/temp/logs"></property>

    <!--控制台日志输出的 Appender-->
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <!--控制输出流对象,默认为 System.out,可修改为 System.err-->
        <target>System.err</target>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${pattern}</pattern>
        </encoder>
    </appender>

    <!--文件日志输出的 Appender-->
    <appender name="file" class="ch.qos.logback.core.FileAppender">
        <!--日志保存路径-->
        <file>${log_dir}/logback.log</file>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${pattern}</pattern>
        </encoder>
    </appender>

    <!--root logger 配置-->
    <root level="ALL">
        <appender-ref ref="console"></appender-ref>
        <appender-ref ref="file" />
    </root>
</configuration>

当然,如果更想直观地看日志,可将日志文件设置成 html 格式:

<!--html 文件格式-->
<appender name="htmlFile" class="ch.qos.logback.core.FileAppender">
    <!--日志保存路径-->
    <file>${log_dir}/logback.html</file>
    <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
        <layout class="ch.qos.logback.classic.html.HTMLLayout">
            <pattern>%level%d{yyyy-mm-dd HH:mm:ss.SSS}%c%M%L%thread%m</pattern>
        </layout>
    </encoder>
</appender>
  1. 日志拆分和归档压缩的 Appender

日志文件较大时,我们不能让日志信息都存储在同一个日志文件里面,所以,我们要将日志信息进行拆分。

<!--日志拆分和归档压缩的 Appender-->
<appender name="rollFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <!--日志保存路径-->
    <file>${log_dir}/roll_logback.log</file>
    <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
        <pattern>${pattern}</pattern>
    </encoder>
    <!--指定拆分规则-->
    <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
        <!--按照时间和压缩格式声明拆分的文件名-->
        <fileNamePattern>${log_dir}/rolling.%d{yyyy-MM-dd-HH-mm-ss}.log%i.gz</fileNamePattern>
        <!--按照文件大小拆分-->
        <maxFileSize>1MB</maxFileSize>
    </rollingPolicy>
</appender>

运行结果如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-A2PA0Jbu-1682563411058)(images/83284e7b587c49dcb633f6e1a1a51baf.png)]

日志文件拆分规则:日志压缩文件按照秒拆分(精确到秒),当日志文件超过 1 MB 时,日志文件会再次进行拆分,且日志文件名中的 .log%i.gz 中的 i 就会自加。

  1. 日志级别过滤器

    <!--日志拆分和归档压缩的 Appender-->
    <appender name="rollFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!--日志保存路径-->
        <file>${log_dir}/roll_logback.log</file>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${pattern}</pattern>
        </encoder>
        <!--指定拆分规则-->
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <!--按照时间和压缩格式声明拆分的文件名-->
            <fileNamePattern>${log_dir}/rolling.%d{yyyy-MM-dd-HH-mm-ss}.log%i.gz</fileNamePattern>
            <!--按照文件大小拆分-->
            <maxFileSize>1MB</maxFileSize>
        </rollingPolicy>
    
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>
    

    其中,

    <filter class="ch.qos.logback.classic.filter.LevelFilter">
        <level>ERROR</level>
        <onMatch>ACCEPT</onMatch>
        <onMismatch>DENY</onMismatch>
    </filter>
    

    表示:只记录 ERROR 级别的日志信息

  2. 异步日志

默认情况下,程序运行时,程序和日志记录是同步的。使用异步日志的话,就可以使程序和日志记录不是同步。

<!--异步日志-->
<appender name="asyc" class="ch.qos.logback.classic.AsyncAppender">
    <!--指定某个具体的 appender-->
    <appender-ref ref="rollFile"></appender-ref>
</appender>

<!--root logger 配置-->
<root level="ALL">
    <appender-ref ref="console"></appender-ref>
    <appender-ref ref="asyc" />
</root>
  1. 自定义 logger
<!--root logger 配置-->
<root level="ALL">
    <appender-ref ref="console" />
    <appender-ref ref="async" />
</root>

<!--自定义Logger对象-->
<logger name="com.zzc.log.logback" level="info" additivity="false">
    <appender-ref ref="console" />
</logger>

上述标签含义:自定义 Logger 对象没有继承 rootLogger 对象,但它用到了 console appender,并且只输出 info 级别以上的日志信息。

logger 标签的属性说明:

  • name:测试类 LogbackTest 所在的类名
  • level:控制日志级别
  • additivity:自定义 logger 对象是否继承 rootLogger 对象的功能。为 false,表示不继承 root logger 的功能

6、log4j2

6.1 log4j2介绍

log4j2log4j 的升级版,参考了 logback 的优秀的设计,修复了一些问题,因此,带来了一些重大的提升

6.2 使用案例

目前市面上主流的日志门面是 slf4j。虽然 log4j2 也是日志门面,但它的实现功能非常强大,性能优越。所以,大家一般还是将 log4j2 看做是日志的实现。slf4j + log4j2 是未来趋势。

新建一个 Maven 工程,引入依赖,POM 文件:

<!--log4j2的日志门面-->
<dependency>
  <groupId>org.apache.logging.log4j</groupId>
  <artifactId>log4j-api</artifactId>
  <version>2.11.1</version>
</dependency>
<!--log4j2的日志实现-->
<dependency>
  <groupId>org.apache.logging.log4j</groupId>
  <artifactId>log4j-core</artifactId>
  <version>2.10.0</version>
</dependency>
public class Log4j2Test {

    public static final Logger LOGGER = LogManager.getLogger(Log4j2Test.class);

    @Test
    public void testQuick() {
        LOGGER.fatal("fatal");
        LOGGER.error("error");
        LOGGER.warn("warn");
        LOGGER.info("info");
        LOGGER.debug("debug");
        LOGGER.trace("trace");
    }

}

运行结果后:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cQ2coiRw-1682563411059)(images/d922e816284a49fbbaca343e06cce9f4.png)]

表明:log4j2 的日志默认级别为 error

slf4j + log4j2

<!--slf日志门面-->
<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-api</artifactId>
  <version>1.7.26</version>
</dependency>
<!--使用log4j2的适配器进行绑定-->
<dependency>
  <groupId>org.apache.logging.log4j</groupId>
  <artifactId>log4j-slf4j-impl</artifactId>
  <version>2.10.0</version>
</dependency>

<!--log4j2的日志门面-->
<dependency>
  <groupId>org.apache.logging.log4j</groupId>
  <artifactId>log4j-api</artifactId>
  <version>2.11.1</version>
</dependency>
<!--log4j2的日志实现-->
<dependency>
  <groupId>org.apache.logging.log4j</groupId>
  <artifactId>log4j-core</artifactId>
  <version>2.10.0</version>
</dependency>
6.3 配置文件

log4j2 默认加载 classpath 下的 log4j2.xml 文件中的配置

<?xml version="1.0" encoding="utf-8" ?>
<!--
    status=warn:日志框架本身的输出日志级别
    monitorInterval="5":自动加载配置文件的间隔时间,不低于5秒
-->
<Configuration status="warn" monitorInterval="5">
    <!--配置集中管理属性-->
    <properties>
        <property name="LOG_HOME" value="E:/temp/logs"></property>
    </properties>

    <Appenders>
        <Console name="console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] [%-5level] %c{36}:%L --- %m%n"></PatternLayout>
        </Console>

        <File name="file" fileName="${LOG_HOME}/myfile.log">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] [%-5level] %c{36}:%L --- %m%n"></PatternLayout>
        </File>
        <!--使用随机读写流-->
        <RandomAccessFile name="accessFile" fileName="${LOG_HOME}/myAcclog.log">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] [%-5level] %c{36}:%L --- %m%n"></PatternLayout>
        </RandomAccessFile>
        <!--按照一定规则拆分-->
        <RollingFile name="rollingFile" fileName="${LOG_HOME}/myRolllog.log"
                     filePattern="${LOG_HOME}/$${date:yyyy-MM-dd}/myRolllog-%d{yyyy-MM-dd-HH-mm}-%i.log">
            <ThresholdFilter level="debug" onMatch="ACCEPT" onMismatch="DENY" />
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] [%-5level] %c{36}:%L --- %m%n"></PatternLayout>
            <!--拆分规则-->
            <Policies>
                <!--系统启动时,触发并启动拆分规则,生成新的日志文件-->
                <OnStartupTriggeringPolicy />
                <!--按照文件大小拆分-->
                <SizeBasedTriggeringPolicy size="10 MB" />
                <!--按照时间节点拆分,规则由filePattern定义-->
                <TimeBasedTriggeringPolicy />
            </Policies>
            <!--在同一个目录下,文件的个数限定30个,超过后覆盖旧的日志信息-->
            <DefaultRolloverStrategy max="30" />
        </RollingFile>
    </Appenders>

    <Loggers>
        <Root level="info">
            <AppenderRef ref="console" />
        </Root>
    </Loggers>

</Configuration>
6.4 异步日志

log4j2 最大的特点就是 异步日志,其性能的提升主要也是从异步日志中受益。

log4j2 提供了两种异步方式:AsyncAppenderAsyncLogger(常用)

【注意】:配置异步日志,需要引入依赖:

<dependency>
  <groupId>com.lmax</groupId>
  <artifactId>disruptor</artifactId>
  <version>3.3.4</version>
</dependency>

  1. AsyncAppender

    <Configuration>
    	...
    	<File name="file" fileName="${LOG_HOME}/myfile.log">
    	    <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] [%-5level] %c{36}:%L --- %m%n"></PatternLayout>
    	</File>
    	
    	<!--异步日志配置-->
    	<Async name="Async">
    	    <AppenderRef ref="file" />
    	</Async>
    
    	<Loggers>
            <Root level="info">
                <AppenderRef ref="console" />
                <AppenderRef ref="Async" />
            </Root>
        </Loggers>
    </Configuration>
    
    

    运行代码后,结果如下:
    在这里插入图片描述

  2. AsyncAppender
    AsyncAppender 才是 log4j2 的重头戏,也是官方推荐的方式。有两种方式:全局异步、混合异步。

全局异步

全局异步:所有的日志都是异步地记录,在配置文件上不做任何修改。只需要添加一个 log4j2.component.properties 配置文件。

Log4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector

混合异步

混合异步:可以在应用中同时使用同步日志和异步日志,这使得日志的配置更加灵活

修改 log4j2.xml

<Configuration>
	...
	<Loggers>
        <!--自定义Logger对象 includeLocation="false" 关闭行号信息-->
        <AsyncLogger name="com.zzc.log.log4j2" level="info" includeLocation="false" additivity="false">
            <AppenderRef ref="console" />
        </AsyncLogger>

        <Root level="info">
            <AppenderRef ref="console" />
            <AppenderRef ref="Async" />
        </Root>
    </Loggers>
</Configuration>

运行代码,结果如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2IXww43F-1682563411059)(images/24439c4520b54057928cadd97e9cb9cc.png)]

表明:没有显示行号。

7. SpringBoot 中的日志

SpringBoot 日志也是开发中常用的日志系统。SpringBoot 默认就是使用 SLF4J 作为日志门面,logback 作为日志实现来记录日志。

7.1 SpringBoot 中的日志设计

SpringBoot 中的日志:

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

依赖结构图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JnJThRaW-1682563411060)(images/2226c93f0e8a4d25856dd20041777f47.png)]

总结:

  1. SpringBoot 底层使用 logback 作为日志实现
  2. 使用了 SLF4J 作为日志门面
  3. JUL 也转换为了 SLF4J
  4. 也可以使用 Log4j2 作为日志门面,但是最终也是通过 SLF4J 调用 logback
7.2 SpringBoot 中使用日志

1.测试

@RunWith(SpringRunner.class)
@SpringBootTest
public class ApplicationTests {

	private static final Logger LOGGER = LoggerFactory.getLogger(ApplicationTests.class);

	@Test
	public void contextLoads() {
		LOGGER.error("error");
		LOGGER.warn("warn");
		LOGGER.info("info");
		LOGGER.debug("debug");
		LOGGER.trace("trace");
	}

}

运行结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gfBhfboz-1682563411060)(images/7eb2d7b0b7f94e799f51f454b5578c2d.png)]

SpringBoot 中的默认日志级别:info

2.配置文件:

application.properties

# 指定自定义 Logger 对象日志级别
logging.level.com.zzc.sbexp=debug
# 指定控制台输出信息格式
logging.pattern.console=[%-5level] %d{yyyy-MM-dd HH:mm:ss} %c [%thread] === %m%n
# 指定日志文件存放的目录,文件名默认为 spring.log
logging.file=E://temp/springboot.log
# 指定日志文件消息格式
logging.pattern.file=[%-5level] %d{yyyy-MM-dd HH:mm:ss} %c [%thread] === %m%n

运行结果如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0GC5AYal-1682563411060)(images/cd94dc0859414f2499b4df98e3b72d47.png)]

3.指定日志配置文件:

给类路径下放上每个日志框架自己的配置文件;SpringBoot 就不使用默认配置的了。

日志框架配置文件
logbacklogback.xml、logback-spring.xml
log4j2log4j2.xml、log4j2-spring.xml
jullogging.properties

logback.xml 直接就被 SpringBoot 识别了。

logback.xml

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <!--配置集中管理属性-->
    <property name="pattern" value="[%-5level] %d{yyyy-mm-dd HH:mm:ss.SSS} %c %M %L [%thread] --- %m%n"></property>
    <property name="log_dir" value="E:/temp/logs"></property>

    <!--控制台日志输出的 Appender-->
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <!--控制输出流对象,默认为 System.out,可修改为 System.err-->
        <target>System.err</target>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${pattern}</pattern>
        </encoder>
    </appender>

    <!--自定义Logger对象-->
    <logger name="com.zzc.sbexp" level="info" additivity="false">
        <appender-ref ref="console" />
    </logger>
</configuration>

运行代码后:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-L1Xwnlhw-1682563411061)(images/e4d0c720bce947fbaadd633e4178604f.png)]

4.切换日志配置文件:

将日志框架切换为 log4j2

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-web</artifactId>
	<exclusions>
		<!--排除logback-->
		<exclusion>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-logging</artifactId>
		</exclusion>
	</exclusions>
</dependency>
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>

log4j2.xml

<?xml version="1.0" encoding="utf-8" ?>

<Configuration status="warn" monitorInterval="5">
    <!--配置集中管理属性-->
    <properties>
        <property name="LOG_HOME" value="E:/temp/logs"></property>
    </properties>

    <Appenders>
        <Console name="console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] [%-5level] %c{36}:%L --- %m%n"></PatternLayout>
        </Console>
    </Appenders>

    <Loggers>
        <Root level="info">
            <AppenderRef ref="console" />
        </Root>
    </Loggers>

</Configuration>

8.使用logback输出mybaits日志

在配置文件种增加自定义logger

<!--mybatis 日志-->
<logger name="com.apache.ibatis" level="debug"/>
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值