Java日志框架

笔记学习原视频(B站):全面深入学习多种java日志框架

目前常见日志框架有:

  • JUL
  • Logback
  • log4j
  • log4j2

目前常见的日志门面(统一的日志API):

  • JCL
  • Slf4j

一、 老技术(基本都弃用了)

之所以要了解这种废弃的技术一是方便了解整体的发展历史,清楚技术替换的缘由,二是方便了解日志系统的基本原理。

日志门面技术:JCL

日志框架:

  • JDK原生的JUL
  • ApacheLog4J

1 JUL 日志框架

JUL,全称Java Util Logging,是Java平台自带的原生日志框架。它不需要额外引入第三方库,因此在小型应用或快速开发项目中非常受欢迎。

JUL日志组件

  • Loggers:为日志记录器,日志对象,通常为应用程序访问日志框架的入口。
  • Appenders:也叫Handlers为处理器,用于处理输出,比如输出到控制台还是文件等。
  • Layouts:也叫Formatters,负责将日志中的数据进行转换和格式化。决定了数据在一条日志记录中的最终形式。
  • Level:日志级别,Trace < Debug < Info < Warn < Error < Fatal < Off
  • Filters:过滤器,根据需要定制哪些记录会被过滤,哪些会被记录。

1.1 快速入门

JUL(Java Util Logger)这是Java自带的日志框架,不需要引入任何依赖。

public class JulDemo {  // 1. 获取日志记录器  public static final Logger logger = Logger.getLogger("com.clcao.log.JulDemo");  public static void main(String[] args) {  // 2. 日志记录输出  logger.info("info信息");  // 3. 通用方法进行日志记录  logger.log(Level.INFO,"Level Info msg");  // 4. 通过占位符,输出变量信息  String name = "张三";  int age = 18;  logger.log(Level.INFO,"用户信息,name = {0},age = {1}",new Object[]{name,age});  }  
}

注意这里的包为java.util.logging下的包。

输出大概长这样👇
在这里插入图片描述

1.2 日志级别

java.util.logging.Level有定义:

OFF, SEVERE, WARNING, INFO, CONFIG, FINE, FINER, FINEST, ALL

默认级别INFO

@Test  
public void testLevel(){  Logger logger = Logger.getLogger("com.clcao.log.JulTest");  // 日志级别  logger.severe("SEVERE");  logger.warning("WARNING");  logger.info("INFO");    // JUL 默认级别  logger.config("CONFIG");  logger.fine("FINE");  logger.finer("FINER");  logger.finest("FINEST");  
}
1.2.1 自定义日志级别
@Test  
public void testSetLevel(){  Logger logger = Logger.getLogger("com.clcao.log.JulTest");  // 关闭原有的日志级别  logger.setUseParentHandlers(false);  // 创建处理器对象,在 Logback 里边对应 appender    ConsoleHandler consoleHandler = new ConsoleHandler();  // 创建格式化对象 Formatter    SimpleFormatter formatter = new SimpleFormatter();  // 关联  consoleHandler.setFormatter(formatter);  logger.addHandler(consoleHandler);  // 设置日志级别  consoleHandler.setLevel(Level.ALL);  logger.setLevel(Level.ALL);  // 日志级别  logger.severe("SEVERE");  logger.warning("WARNING");  logger.info("INFO");    // JUL 默认级别  logger.config("CONFIG");  logger.fine("FINE");  logger.finer("FINER");  logger.finest("FINEST");  
}
1.2.2 文件日志输出

在上面的基础上追加处理器FileHandler

// 文件日志输出  
String path = "logs/jul.log";  
File file = new File(path);  
System.out.println(file.getParentFile());  
if (!file.getParentFile().exists()) {  System.out.println(file.getParentFile().mkdirs());  
}  FileHandler fileHandler = new FileHandler(path);  fileHandler.setFormatter(formatter);  
logger.addHandler(fileHandler);

1.3 父子容器

日志打印默认继承父容器日志级别。

@Test  
public void testParent(){  Logger logger1 = Logger.getLogger("com");  // 父为:java.util.logging.LogManager$RootLoggerLogger logger2 = Logger.getLogger("com.clcao");  // 父为 logger1Logger logger3 = Logger.getLogger("com.clcao.log");   // 父为 logger2System.out.println(logger3.getParent() == logger2);  System.out.println(logger2.getParent() == logger1);  System.out.println(logger1.getParent());  // 设置日志级别  ConsoleHandler consoleHandler = new ConsoleHandler();  SimpleFormatter simpleFormatter = new SimpleFormatter();  consoleHandler.setFormatter(simpleFormatter);  logger2.setLevel(Level.ALL);  consoleHandler.setLevel(Level.ALL);  logger2.addHandler(consoleHandler);  // 关闭父容器  logger2.setUseParentHandlers(false);  // 如果不设置false会继承父级别 logger1==>默认日志级别为 INFOprintLog(logger2);  
}

1.4 配置文件

默认由日志管理器LogManager读取配置文件,位置:System.getProperty("java.home")下的/conf/logging.properties

当前为:/Library/Java/JavaVirtualMachines/jdk-17.0.2.jdk/Contents/Home/conf/logging.properties

JDK安装目录下的conf目录,自带的配置如下:

logging.properties(去掉了相关注释)


# 默认处理器对象,向控制台输出
handlers= java.util.logging.ConsoleHandler  # 顶级 LoggerManager 没有名称,所以为 .level 默认级别为 INFO
.level= INFO  # 日志文件输出路径设置
# %h当前用户名 %u数字取值
java.util.logging.FileHandler.pattern = %h/java%u.log  
# 最多 50000 条记录
java.util.logging.FileHandler.limit = 50000 
# 日志文件数量最大为 1
java.util.logging.FileHandler.count = 1  java.util.logging.FileHandler.maxLocks = 100  
java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter  # Limit the messages that are printed on the console to INFO and above.  
java.util.logging.ConsoleHandler.level = INFO  
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter  
读取配置文件
@Test  
public void testConfig() throws IOException {  Logger logger = Logger.getLogger("com.clcao");  logger.addHandler(new FileHandler());  // 读取配置文件  InputStream is = JulTest.class.getClassLoader().getResourceAsStream("logging.properties");  // 创建 LogManager 对象 读取配置文件  LogManager logManager = LogManager.getLogManager();  logManager.readConfiguration(is);  printLog(logger);  
}

如果创建了FileHandler文件日志处理器,则会根据配置文件的设置,生成文件日志,默认配置为java.util.logging.FileHandler.pattern = %h/java%u.log。因此在当前用户目录下存在文件:java0.log

配置日志文件输出

handlers= java.util.logging.ConsoleHandler,java.util.logging.FileHandler

日志文件
在这里插入图片描述

XML格式
在这里插入图片描述

配置文件详解
# RootLogger 顶级父元素的默认日志处理器,如果要日志文件输出则:java.util.logging.FileHandler
handlers= java.util.logging.ConsoleHandler# RootLogger 顶级父元素日志级别
.level= ALL  # 自定义 Logger 使用
com.clcao.handlers = java.util.logging.ConsoleHandler
com.clcao.level = CONFIG# 关闭默认设置
com.clcao.useParentHandlers = false# 日志文件输出路径设置
# %h当前用户名 %u数字取值
java.util.logging.FileHandler.pattern = %h/java%u.log  
# 最多 50000 条记录
java.util.logging.FileHandler.limit = 50000 
# 日志文件数量最大为 1
java.util.logging.FileHandler.count = 1   java.util.logging.FileHandler.maxLocks = 100  
# 指定文件日志输出的格式对象,可以替换为 SimpleFormatter
java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter  
# 指定日志文件追加
java.util.logging.FileHandler.append = true# 向控制台输出的日志对象 ConsoleHandler
# 控制台输出对象日志级别
java.util.logging.ConsoleHandler.level = ALL  
# 指定 handler 对象的日志消息格式对象
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
# 指定 handler 对象的字符集
java.util.logging.ConsoleHandler.encoding = UTF-8# 指定日志消息格式
java.util.logging.SimpleFormatter.format = %4$s: %5$s [%1$tc]%n

1.5 执行原理和流程

在这里插入图片描述


2 Log4J 日志框架

Log4JApache软件基金会下的一个开源项目,主要用于Java应用程序中的日志记录。

Log4J最早在1996年被创建,随后经过多次的改进和增强,逐渐成为了Java日志领域的重要框架之一。需要注意的是,随着技术的发展,Log4J已经发展到了Log4J2,但这里主要介绍的是Log4J(通常指的是Log4J 1.x版本)。

官网:Apache Log4J

Log4J技术特点

  1. 灵活的配置Log4J允许通过配置文件(如log4j.propertieslog4j.xml)来灵活地控制日志信息的输出目的地(如控制台、文件、GUI组件等)、输出格式以及日志级别等,而无需修改应用程序的代码。

  2. 多输出源支持Log4J支持将日志信息输出到多个目的地,包括但不限于控制台、文件、套接口服务器、NT的事件记录器、UNIX Syslog守护进程等。

  3. 日志级别控制Log4J定义了多种日志级别(如OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL等),允许开发者根据日志信息的重要性来选择性地输出日志,从而有效地控制日志信息的生成和输出。

  4. 线程安全Log4J是线程安全的,这意味着它可以在多线程环境中安全地使用,而不会出现数据竞争或不一致的问题。

  5. 性能优异Log4J在日志记录方面表现出色,具有较高的性能和可靠性,能够满足大规模应用程序的日志记录需求。

  6. 扩展性强Log4J提供了丰富的API和扩展点,允许开发者根据自己的需求来定制和扩展日志记录功能。

  7. 支持多种语言:虽然Log4J最初是为Java设计的,但随着时间的推移,它已经被孵化出了支持C、C++、C#、Perl、Python、Ruby等多种语言的子框架或接口,使得多语言分布式系统能够使用统一的日志组件模块。

  8. 易于集成Log4J易于与Java应用程序集成,并且提供了丰富的第三方扩展,可以方便地将其集成到J2EE、JINI甚至SNMP等应用中。

2.1 快速入门

导入依赖

<dependency>  <groupId>log4j</groupId>  <artifactId>log4j</artifactId>  <version>1.2.17</version>  
</dependency>
  • log4j:整个日志框架的总称,包含核心组件、API和可能的集成库。
  • log4j-coreLog4j框架的核心组件,提供日志记录和输出的具体实现。
  • log4j-apiLog4jAPI接口部分,定义了一系列用于日志记录的接口。
  • log4j-jul(或类似功能的库):与JUL集成的库,使得Log4j能够兼容和集成JDK自带的日志框架。

快速入门

@Test  
public void testQuick(){  // 初始化配置信息,否则会报错  BasicConfigurator.configure();  // 1. 获取日志记录器  Logger logger = Logger.getLogger(Log4JTest.class);  Logger rootLogger = Logger.getRootLogger();  System.out.println(rootLogger.getLevel());  // 2. 日志记录输出  logger.info("INFO");  // 3. 日志级别  logger.log(Level.OFF,"OFF");  logger.log(Level.FATAL,"FATAL");  logger.log(Level.ERROR,"ERROR");  logger.log(Level.WARN,"WARN");  logger.log(Level.INFO,"INFO");  logger.log(Level.DEBUG,"DEBUG");  logger.log(Level.TRACE,"TRACE");  
}

输出长这样👇
在这里插入图片描述

2.1.1 Log4J日志组件
  • Loggers:日志记录器,控制日志的输出级别和是否输出
  • Appenders:输出端,跟JUL中的handlers处理器一个意思,指定日志的输出方式(控制台、文件)
  • Layout:日志格式化器,等价于JUL中的Formatter,控制日志信息输出的格式。

Log4J有一个特殊的loggerroot,他是所有logger的根,意味着所有的logger直接或者间接继承自root,可以通过Logger.getRootLogger(); 方法获取,默认级别为DEBUG

Appenders
输出端类型作用
ConsoleAppender将日志输出到控制台
FileAppender将日志输出到文件
DailyRollingFileAppender将日志输出到文件,并且每天输出一个新文件
RollingFileAppender将日志输出到文件,并且指定文件的大小,当文件达到指定大小时候改名生成新的文件
JDBCAppender将日志信息保存到数据库中
Layouts
格式化器类型作用
HTMLLayout格式化日志输出为HTML格式
SimpleLayout简单的格式化输出,打印格式为(info-message)
PatternLayout最强大的格式化器,自定义格式化输出,如果没有则使用默认格式
简单配置文件

必须在类路径下,文件名为log4j.properties

log4j.rootLogger = trace,console  
log4j.appender.console = org.apache.log4j.ConsoleAppender  
log4j.appender.console.layout = org.apache.log4j.SimpleLayout

使用

 @Test  public void testQuick(){  // 初始化配置信息,否则会报错  
//        BasicConfigurator.configure();  // 注释掉初始化信息,改用读取配置文件 ==> log4j.properties  // 1. 获取日志记录器  Logger logger = Logger.getLogger(Log4JTest.class);  Logger rootLogger = Logger.getRootLogger();  System.out.println(rootLogger.getLevel());  // 2. 日志记录输出  logger.info("INFO");  // 3. 日志级别  logger.log(Level.OFF,"OFF");  logger.log(Level.FATAL,"FATAL");  logger.log(Level.ERROR,"ERROR");  logger.log(Level.WARN,"WARN");  logger.log(Level.INFO,"INFO");  logger.log(Level.DEBUG,"DEBUG");  logger.log(Level.TRACE,"TRACE");  }

输出
在这里插入图片描述

2.1.2 内置日志

开启

// 开启 log4j 内置日志  
LogLog.setInternalDebugging(true);

2.2 配置文件详解

log4j.properties

# 指定 RootLogger 顶级父元素的日志设置  
# 指定顶级父元素的日志级别 trace, 输出位置为 console 控制台,需要指定下面的appender,比如下面的 file | rollingFile | dailyRollingFilelog4j.rootLogger = trace,logDB  
# 指定日志输出的 Appender对象  
log4j.appender.console = org.apache.log4j.ConsoleAppender  
# 指定日志输出的格式化器 HTMLLayout | PatternLayout | xm.XMLLayoutlog4j.appender.console.layout = org.apache.log4j.PatternLayout  
# 指定消息格式的内容  
log4j.appender.console.layout.conversionPattern = [%p]%r %c %t %F %l %d{yyyy-MM-dd HH:mm:ss.SSS} - %m%n  # %m    输出代码中指定的信息  
# %p    输出优先级, DEBUG、INFO 等  
# %n    换行符(Windows 换行符为`\r\n`,Unix 换行符为`\n`)  
# %r    输出自应用启动到输出该条日志所消耗的时间毫秒数  
# %c    输出打印该日志的类全限定名  
# %t    输出该日志的线程名  
# %d    输出服务器的当前时间,默认为ISO8601,也可以指定格式,比如:%d{yyyy-MM-dd HH:mm:ss}  
# %l    输出日志时间发生的位置,包括类名、线程、及在代码中的行数。比如:Test.main(Test.java:10)  
# %F    输出日志信息产生所在的文件名称  
# %L    输出代码中的行号  
# %%    输出一个 `%` 符号  ################################## 普通 FileAppender ################################ 指定日志文件输出的 Appender 对象  
log4j.appender.file = org.apache.log4j.FileAppender  
# 指定消息格式器对象 Layoutlog4j.appender.file.layout = org.apache.log4j.PatternLayout  
# 指定消息格式的内容  
log4j.appender.file.layout.conversionPattern = [%p]%r %c %t %F %l %d{yyyy-MM-dd HH:mm:ss.SSS} - %m%n  
# 指定日志文件的保存路径  
log4j.appender.file.file = logs/log4j.log  
# 指定日志的字符集  
log4j.appender.file.encoding = UTF-8  ################################## 日志回滚 RollingFileAppender ####################### 指定日志文件输出的 Appender 对象  
log4j.appender.rollingFile = org.apache.log4j.RollingFileAppender  
# 指定消息格式器对象 Layoutlog4j.appender.rollingFile.layout = org.apache.log4j.PatternLayout  
# 指定消息格式的内容  
log4j.appender.rollingFile.layout.conversionPattern = [%p]%r %c %t %F %l %d{yyyy-MM-dd HH:mm:ss.SSS} - %m%n  
# 指定日志文件的保存路径  
log4j.appender.rollingFile.file = logs/log4j.log  
# 指定日志的字符集  
log4j.appender.rollingFile.encoding = UTF-8  
# 指定日志文件的内容大小  
log4j.appender.rollingFile.maxFileSize = 1MB  
# 指定日志文件的数量  
log4j.appender.rollingFile.maxBackupIndex = 10  ################################## 时间规则 DailyRollingFileAppender ####################### 指定日志文件输出的 Appender 对象  
log4j.appender.dailyRollingFile = org.apache.log4j.DailyRollingFileAppender  
# 指定消息格式器对象 Layoutlog4j.appender.dailyRollingFile.layout = org.apache.log4j.PatternLayout  
# 指定消息格式的内容  
log4j.appender.dailyRollingFile.layout.conversionPattern = [%p]%r %c %t %F %l %d{yyyy-MM-dd HH:mm:ss.SSS} - %m%n  
# 指定日志文件的保存路径  
log4j.appender.dailyRollingFile.file = logs/log4j.log  
# 指定日志的字符集  
log4j.appender.dailyRollingFile.encoding = UTF-8  
# 指定拆分规则  
log4j.appender.dailyRollingFile.datePattern = yyyy-MM-dd-HH-mm-ss  ################################## 数据库 JDBCAppender ######################log4j.appender.logDB = org.apache.log4j.jdbc.JDBCAppender  
log4j.appender.logDB.layout = org.apache.log4j.PatternLayout  
log4j.appender.logDB.Driver = com.mysql.jdbc.Driver  
log4j.appender.logDB.URL = jdbc:mysql://localhost:3306/clcao  
log4j.appender.logDB.User = root  
log4j.appender.logDB.Password = ccl@2023  
log4j.appender.logDB.Sql = INSERT INTO log(project_name,create_date,level,category,file_name,thread_name,line,all_category,message)\  values('clcao','%d{yyyy-MM-dd HH:mm:ss}','%p','%c','%F','%t','%L','%l','%m')
JDBCAppender

建表语句

CREATE TABLE IF NOT EXISTS `log`(log_id int(11) NOT NULL AUTO_INCREMENT,`project_name` varchar(255) DEFAULT NULL COMMENT '项目名',`create_date` varchar(255) DEFAULT NULL COMMENT '创建时间',`levle` varchar(255) DEFAULT NULL COMMENT '日志级别',`category` varchar(255) DEFAULT NULL COMMENT '所在类的全名',`file_name` varchar(255) DEFAULT NULL COMMENT '出书日志信息产生时所在的文件名称',`thread_name` varchar(255) DEFAULT NULL COMMENT '线程名',`line` varchar(255) DEFAULT NULL COMMENT '行号',`all_category` varchar(255) DEFAULT NULL COMMENT '日志事件发生位置',`message` varchar(4000) DEFAULT NULL COMMENT '输出代码中指定的消息',PRIMARY KEY (`log_id`)
);

对应的设置👇

################################## 数据库 JDBCAppender ######################log4j.appender.logDB = org.apache.log4j.jdbc.JDBCAppender  
log4j.appender.logDB.layout = org.apache.log4j.PatternLayout  
log4j.appender.logDB.Driver = com.mysql.jdbc.Driver  
log4j.appender.logDB.URL = jdbc:mysql://localhost:3306/clcao  
log4j.appender.logDB.User = root  
log4j.appender.logDB.Password = ccl@2023  
log4j.appender.logDB.Sql = INSERT INTO log(project_name,create_date,level,category,file_name,thread_name,line,all_category,message)\  values('clcao','%d{yyyy-MM-dd HH:mm:ss}','%p','%c','%F','%t','%L','%l','%m')
自定义logger日志

配置文件添加

# 指定 RootLogger 顶级父元素的日志设置  
# 指定顶级父元素的日志级别 trace, 输出位置为 console 控制台,需要指定下面的appender,比如下面的 file | rollingFile | dailyRollingFile
log4j.rootLogger = trace,rollingFile  # 自定义 logger 日志  
log4j.logger.com.clcao = info,file

对比顶级父元素的日志设置。自定义 logger意味着指定某个包下产生的日志记录方式,而顶级父元素root记录的是整个项目。

顶级父元素 Root Logger
  • 定义root logger 是 Log4j 层次结构中的顶级节点。它不对应于应用程序中的任何特定类,而是作为所有其他 logger 的默认父节点。
  • 配置:在 Log4j 的配置中,root logger 的配置通常指定了全局的日志级别和输出目的地(如控制台、文件等)。如果没有为特定的 logger 指定配置,它将继承 root logger 的配置。
  • 作用root logger 的设置可以作为应用程序日志记录行为的“兜底”配置。它确保了即使应用程序中没有显式配置任何 logger,日志记录仍然可以按照某种预定的方式工作。

3 JCL 日志门面

JCL(Jakarta Commons Logging)是Apache提供的一个通用日志API,也称为Commons Logging。它主要作为一个日志门面(Facade)存在,允许开发者在编写代码时使用统一的日志接口,而在实际运行时,则可以根据项目依赖的日志框架来动态选择具体的日志实现。这样做的好处是提高了代码的灵活性和可维护性,使得开发者可以在不修改代码的情况下,通过简单的配置来切换不同的日志实现框架

在这里插入图片描述

3.1 快速入门

@Test  
public void testQuick(){  // 获取日志记录器  Log log = LogFactory.getLog(JCLTest.class);  // 日志记录  log.info("Hello JCL");  // 日志级别  log.fatal("fatal");  log.error("error");  log.warn("warn");  log.info("info");  log.debug("debug");  log.trace("trace");  
}

如果没有导入Log4J的依赖,则使用JDK默认实现,即JUL框架打印日志,如果导入了Log4J依赖则使用Log4J的实现。

这就是JCL作为日志门面技术的好处,做到了统一API的目的。

3.2 JCL原理

支持的日志实现
在这里插入图片描述

LogFactoryImpl.java

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

这里定义了支持的几种实现;其中Log4J的常量为:org.apache.commons.logging.impl.Log4JLogger

通过预定义实现类,进行查找,如果找到类存在则创建实例对象,从而做到根据依赖得到不同是实现的目的,如果存在多个实现技术,则按照数组顺序执行。

但是JCL只支持一些简单的如上面定义的一些技术,像现在主流的LogbackLog4J2等都不支持,并且无法扩展,因此=======》@Deprecated! 算是弃用的技术了。取而代之的门面技术是slf4j


二、 主流新技术

再谈日志门面和日志框架技术

在这里插入图片描述

日志框架出现的历史顺序:
Log4J -> JUL -> JCL -> Slf4j -> Logback -> Log4J2

1 SLF4J日志门面

SLF4J(Simple Logging Facade for Java)是一个为各种日志框架提供简单抽象(或称为门面)的Java库。它允许你在后端使用任何日志框架(如Logback、log4j等),而你的应用程序代码则无需修改即可在这些日志框架之间切换。SLF4J本身不提供日志实现,它只是一个抽象层,用于绑定到具体的日志框架上。

1.1 快速入门

添加依赖

<!--slf4j日志门面-->
<dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>2.0.13</version> 
</dependency><!--内置的简单实现-->
<dependency><groupId>org.slf4j</groupId><artifactId>slf4j-simple</artifactId><version>2.0.13</version><scope>test</scope>
</dependency>

使用

public class Slf4jTest {  private static final Logger LOGGER = LoggerFactory.getLogger(Slf4jTest.class);  @Test  public void testQuick(){  // 日志记录  LOGGER.info("Hello SLF4J!!!");  // 日志级别  LOGGER.error("error");  LOGGER.warn("warn");  LOGGER.info("info");  LOGGER.debug("debug");  LOGGER.trace("trace");  // 使用占位符输出日志信息  String name = "Tom";  int age = 19;  LOGGER.info("用户信息:name={},age={}",name,age);  // 将系统的异常信息输出  try {  int a = 1/0;  } catch (Exception e) {  LOGGER.error("出现异常:", e);  }  }  
}

1.2 绑定日志实现

slf4j门面与日志实现框架技术官网图
在这里插入图片描述

场景1: 只引入slf4j-api.jar依赖,没有引入任何其他实现,默认输出 /dev/null 也就是不会进行日志记录。

场景2: 引入遵循了SLF4J规范的实现依赖,比如logback(logback-classic.jar、logback-core.jar)slf4j-simple.jar,则可以直接使用。

场景3: 对于较早的JULlog4j没有遵循规范,需要引入一个适配包,比如:slf4j-reload4j.jar适配LOG4J框架和slf4j-jdk14.jar适配JUL日志框架。

场景4: 引入slf4j-nop.jar日志开关,将日志功能关闭(不能引入其他的实现)。

存在多个实现
<!--内置的简单实现-->  
<dependency>  <groupId>org.slf4j</groupId>  <artifactId>slf4j-simple</artifactId>  <version>2.0.13</version>  <scope>test</scope>  
</dependency>  <!--logback日志框架-->  
<dependency>  <groupId>ch.qos.logback</groupId>  <artifactId>logback-classic</artifactId>  <version>1.4.11</version>  
</dependency>

在这里插入图片描述

存在多个实现,会优先使用第一个!

绑定需要适配包的日志框架(log4j)

引入依赖

<!--适配log4j-->
<dependency><groupId>org.slf4j</groupId><artifactId>slf4j-reload4j</artifactId><version>2.0.13</version><scope>test</scope>
</dependency>
<!--LOG4J日志框架-->  
<dependency>  <groupId>log4j</groupId>  <artifactId>log4j</artifactId>  <version>1.2.17</version>  
</dependency>

在这里插入图片描述

绑定日志实现原理

在这里插入图片描述

核心在于使用JDK自带的ServiceLoader类动态加载具体的实现类。

日志桥接器

在这里插入图片描述

场景1: 老项目使用Jcl作为日志门面,使用LOG4J日志框架,现在需要升级Logback日志框架(不需要适配包)。则可以添加桥接器jcl-over-slf4j.jar依赖。同理如果直接使用LOG4J日志框架或者JUL日志框架,可以使用log4j-over-slf4j.jar桥接器。

  • jcl-over-slf4j.jar替换commons-logging.jar依赖
  • log4j-over-slf4j.jar替换log4j.jar
  • jul-to-slf4j.jar桥接器取代直接使用JDK的日志框架

场景2: 老项目直接使用LOG4J日志框架(需要适配包),现在需要升级slf4j作为门面技术,则可以添加jcl-over-slf4j.jar替换commons-logging.jar

假如我现在正在使用JCL作为门面技术使用LOG4J日志框架,现在我需要升级为Logback日志框架

步骤1:添加jcl-over-slf4j.jar

<!--slf4j门面-->
<dependency>  <groupId>org.slf4j</groupId>  <artifactId>jcl-over-slf4j</artifactId>  <version>2.0.13</version>  
</dependency><!--桥接器替换JCL门面-->
<dependency>  <groupId>org.slf4j</groupId>  <artifactId>jcl-over-slf4j</artifactId>  <version>2.0.13</version>  
</dependency>

步骤2:去掉commons-logging.jar依赖、log4j.jar依赖

步骤3:添加logback-classic.jar依赖

<!--logback日志框架-->  
<dependency>  <groupId>ch.qos.logback</groupId>  <artifactId>logback-classic</artifactId>  <version>1.4.11</version>  
</dependency>

不需要任何代码改动,直接运行即可!

日志桥接器原理:

log4j-over-slf4j.jar桥接,要保证log4j源代码不动,以为着入口方法需要保证具有相同的包名。

import org.apache.log4j.Logger;
Logger logger = Logger.getLogger(Logger.class);

实际上,桥接包,使用的正是相同的包名,从而实现从入口调用到log4j-over-slf4j.jar下的内容,最后根据调用链,关键步骤为:this.slf4jLogger = LoggerFactory.getLogger(name);

最终达到使用slf4j门面的相同目的。

2 Logback日志框架

Logback是一个用于Java应用程序的日志框架,由log4j框架的创始人Ceki Gülcü开发。Logback旨在成为log4j的替代品,并提供了更好的性能、可扩展性和灵活性。

官网:Logback

Logback的组成

Logback分为三个模块:

  • logback-core:提供了通用的日志记录功能,是Logback的基础模块,也是其他两个模块的基础。
  • logback-classic:提供了与SLF4J APIlog4j API兼容的日志记录功能,是log4j 1.x的显著改进版本。
  • logback-access:与TomcatJettyServlet容器集成,以提供HTTP访问日志功能。

2.1 快速入门

添加依赖

<!--logback日志框架-->  
<dependency>  <groupId>ch.qos.logback</groupId>  <artifactId>logback-classic</artifactId>  <version>1.4.11</version>  
</dependency>

logback-classic间接依赖了slf4j-api.jar因此这里没有单独引入此jar包了。

使用

public class LogbackTest {  private 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");  // 记录日志信息  String name = "Tom";  int age = 19;  LOGGER.info("用户信息: name={},age={}",name,age);  }  
}

2.2 Logback日志配置

Logback会依次读取以下类型配置文件:

  1. logback.groovy
  2. logback-test.xml
  3. logback.xml
    如果均不存在,则采用默认配置。
2.2.1 Logback组件
  1. Logger:日志记录器,关联到对应的context容器,主要用于存放日志对象,也可以定义日志类型和级别。
  2. Appender:用于指定日志的输出目的,控制台或者文件等。
  3. Layout:用于格式化日志输出,在Logback中被封装在encoder
2.2.2 配置文件详解

logback.xml配置文件

<?xml version="1.0" encoding="UTF-8"?>  <!-- 配置文件修改时重新加载,默认true -->  
<configuration scan="true">  <!--配置集中管理属性-->  <property name="pattern" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%-5level]  %c %M %L - %m%n"/>  <property name="html_pattern" value="%d{yyyy-MM-dd HH:mm:ss.SSS}%thread%-5level%c%M%L%m%n"/>  <property name="log_dir" value="logs"/> <!--关闭logback框架自身日志-->  
<statusListener class="ch.qos.logback.core.status.NopStatusListener"/> <!--  日志输出格式  %-5level                日志级别  %d{yyyy-MM-dd HH:mm:ss} 日期时间  %c                      类的全限定名  %M                      method方法名  %L                      行号  %thread                 线程名称  %m 或者 %msg             信息  %n                      换行  -->  <!--控制台日志输出的 Appender -->    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">  <!--控制输出流对象,默认为 System.out-->        <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 charset="UTF-8" class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">  <pattern>${pattern}</pattern>  </encoder>    </appender>  <!--HTML 格式的日志文件输出 Appender -->    <appender name="htmlFile" class="ch.qos.logback.core.FileAppender">  <!--日志文件保存路径-->  <file>${log_dir}/logback.html</file>  <!-- html 日志消息格式设置-->  <encoder charset="UTF-8" class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">  <layout class="ch.qos.logback.classic.html.HTMLLayout">  <pattern>${html_pattern}</pattern>  </layout>        </encoder>    </appender>  <!--日志拆分与归档压缩的 Appender -->    <appender name="rollingFile" class="ch.qos.logback.core.rolling.RollingFileAppender">  <!--日志文件保存路径-->  <file>${log_dir}/roll_logback.log</file>  <!--日志消息格式设置-->  <encoder charset="UTF-8" class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">  <pattern>${pattern}</pattern>  </encoder>        <!--指定拆分的规则-->  <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">  <!--按照时间和压缩格式声明拆分的文件名,即归档后的文件应该叫啥,比如 20240727121212.gz-->            <fileNamePattern>${log_dir}/rolling.%d{yyyy-MM-dd}.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>  <!--异步日志-->  <appender name="async" class="ch.qos.logback.classic.AsyncAppender">  <!--指定某个具体的 Appender-->        <appender-ref ref="rollingFile"/>  </appender>  <!--RootLogger 顶级父元素配置-->  <root level="DEBUG">  <appender-ref ref="console"/>  </root>  <!--自定义 logger 对象    additivity=false 不继承 RootLogger -->    <logger name="com.example" level="info" additivity="false">  <appender-ref ref="console"/>  </logger>
</configuration>

2.3 Logback-access模块

Logback-access 是 Logback 日志系统的一个模块,专为记录 Web 应用程序的访问日志而设计。

2.3.1 快速入门

基于Tomcat 10.x 版本

1)添加依赖
Tomcat目录下找到lib目录,将common-2.0.2.jarlogback-core-1.5.6.jartomcat-2.0.2.jar添加到里边。

2)替换tomcat配置/conf/server.xml

<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"  prefix="localhost_access_log" suffix=".txt"  pattern="%h %l %u %t &quot;%r&quot; %s %b" />

将上面内容替换为:

<Valve className="ch.qos.logback.access.tomcat.LogbackValve"/>

3)在/conf下添加logback-access.xml配置文件

<?xml version="1.0" encoding="UTF-8"?>  
<configuration>  <!-- always a good activate OnConsoleStatusListener -->  <statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener" />  <property name="LOG_DIR" value="${catalina.base}/logs"/>  <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">  <file>${LOG_DIR}/access.log</file>  <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">  <fileNamePattern>access.%d{yyyy-MM-dd}.log.zip</fileNamePattern>  </rollingPolicy>  <encoder>            <pattern>combined</pattern>  </encoder>    </appender>  <appender-ref ref="FILE" />  
</configuration>

这里的pattern直接使用combined是因为logback对一些常用的模式做了统一,因此可以这样,其等价于%h %l %u [%t] "%r" %s %b "%i{Referer}" "%i{User-Agent}",具体参考下面的官网配置。

启动Tomcat即可,可以看到Tomcat目录下的/logs下存在文件access.log
在这里插入图片描述

官方配置:access configuration


3 Log4j2日志框架

Log4j2是Apache实现的一个开源日志框架,作为Log4j 1.x和Logback的改进版,它采用了新技术,如无锁异步等,使得日志的吞吐量、性能比Log4j 1.x提高了10倍,并解决了一些死锁的bug,同时配置也更加简单灵活。

特点:

  • 异常处理,在logbackAppender中的异常不会被感知,log4j2提供了一些异常处理
  • 性能提升
  • 自动重载配置,修改日志级别而不需要重新启动应用
  • 无垃圾机制

算是目前Java日志框架中最优秀的了。

官网:Apache Log4j2

3.1 快速入门

Log4j2即是门面也是日志框架,所以可以单独使用,而不依赖slf4j不过主流的应用还是slf4j + Log4j2

单独使用Log4j2

添加依赖

<!--Log4j2日志门面-->  
<dependency>  <groupId>org.apache.logging.log4j</groupId>  <artifactId>log4j-api</artifactId>  <version>2.23.1</version>  
</dependency>  
<!--Log4j2日志实现-->  
<dependency>  <groupId>org.apache.logging.log4j</groupId>  <artifactId>log4j-core</artifactId>  <version>2.23.1</version>  
</dependency>

添加配置文件log4j2.xml ^a0a627

<?xml version="1.0" encoding="UTF-8"?>  <Configuration name="ConfigTest" status="ERROR" monitorInterval="5">  <!--集中属性配置管理-->  <properties>  <property name="LOG_DIR" value="logs"/>  <property name="pattern" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] [%-5level] %c{36}:%L - %m%n"/>  </properties>  <Appenders>        <!--控制台输出-->  <Console name="console" target="SYSTEM_OUT">  <PatternLayout pattern="${pattern}"/>  </Console>  <!--日志文件输出-->  <File name="file" fileName="${LOG_DIR}/log4j2.log">  <PatternLayout pattern="${pattern}"/>  </File>  </Appenders>  <Loggers>        <Root level="all">  <AppenderRef ref="console"/>  </Root>    </Loggers>
</Configuration>

使用

public class Log4j2Test {  private 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");  // 记录日志信息  String name = "Tom";  int age = 18;  LOGGER.info("用户信息:name={}, age={}",name,age);  }  
}

输出结果
在这里插入图片描述

slf4j+Log4j2(主流推荐)

添加依赖

<!--slf4j日志门面-->  
<dependency>  <groupId>org.slf4j</groupId>  <artifactId>slf4j-api</artifactId>  <version>2.0.13</version>  
</dependency>  
<!--log4j2的适配器 适配slf4j门面-->  
<dependency>  <groupId>org.apache.logging.log4j</groupId>  <artifactId>log4j-slf4j2-impl</artifactId>  <version>2.23.1</version>  <scope>test</scope>  
</dependency>  
<!--Log4j2日志门面-->  
<dependency>  <groupId>org.apache.logging.log4j</groupId>  <artifactId>log4j-api</artifactId>  <version>2.23.1</version>  
</dependency>  
<!--Log4j2日志实现-->  
<dependency>  <groupId>org.apache.logging.log4j</groupId>  <artifactId>log4j-core</artifactId>  <version>2.23.1</version>  
</dependency>

基本关系为:
调用slf4j-api -> 调用适配器log4j-slf4j2-impl -> 调用Log4j2门面log4j-api ->调用Log4j2的日志实现log4j-core

在这里插入图片描述

添加配置文件(同上)log4j2.xml

使用

public class Log4j2Test {  private static final Logger LOGGER = LoggerFactory.getLogger(Log4j2Test.class);  @Test  public void testQuick2(){  // 日志记录  LOGGER.info("Hello SLF4J!!!");  // 日志级别  LOGGER.error("error");  LOGGER.warn("warn");  LOGGER.info("info");  LOGGER.debug("debug");  LOGGER.trace("trace");  // 使用占位符输出日志信息  String name = "Tom";  int age = 19;  LOGGER.info("用户信息:name={},age={}",name,age);  }  
}

3.2 配置文件详解

<?xml version="1.0" encoding="UTF-8"?>  
<!--  status: 日志框架本身的日志级别  monitorInterval: 自动加载配置文件的间隔时间,不低于5秒  
-->  
<Configuration name="ConfigTest" status="DEBUG" monitorInterval="5">  <!--集中属性配置管理-->  <properties>  <property name="LOG_DIR" value="logs"/>  <property name="pattern" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] [%-5level] %c{36}:%L - %m%n"/>  </properties>  <!-- Appender 日志处理器-->  <Appenders>  <!--控制台输出-->  <Console name="console" target="SYSTEM_OUT">  <PatternLayout pattern="${pattern}"/>  </Console>  <!--日志文件输出-->  <File name="file" fileName="${LOG_DIR}/log4j2.log">  <PatternLayout pattern="${pattern}"/>  </File>  <!--异步的 Appender-->        <Async name="Async">  <Appender-Ref ref="file"/>  </Async>  <!--随机读写流的日志文件输出,性能高-->  <RandomAccessFile name="accessFile" fileName="${LOG_DIR}/log4j2_acc.log">  <PatternLayout pattern="${pattern}"/>  </RandomAccessFile>  <!--  按照规则拆分和归档的日志文件输出  filePattern: 归档日志文件名规则  -->  <RollingFile name="rollingFile" fileName="${LOG_DIR}/log4j2_roll.log"  filePattern="${LOG_DIR}/$${date:yyyy-MM-dd}/rolling-%d{yyyy-MM-dd-HH-mm}-%i.log">  <!--日志级别过滤器-->  <ThresholdFilter level="debug" onMatch="ACCEPT" onMismatch="DENY"/>  <!--日志消息格式-->  <PatternLayout pattern="${pattern}"/>  <!--拆分规则-->  <Policies>  <!--系统启动时候触发拆分规则,产生一个新的日志文件-->  <OnstartupTriggeringPolicy/>  <!--按照文件大小拆分-->  <SizeBasedTriggeringPolicy size="10MB"/>  <!--按照时间的节点拆分,基于上面的filePattern-->  <TimeBasedTriggeringPolicy/>  </Policies>            <!--在同一个目录下,文件存在的最大数量-->  <DefaultRolloverStrategy max="30"/>  </RollingFile>    </Appenders>  <!-- logger 定义-->  <Loggers>  <!--顶级父元素 RootLogger 定义-->  <Root level="debug">  <AppenderRef ref="console"/>  </Root>  <!--  混合异步需要关闭全局异步,即log4j2.component.properties设置关闭,可注释掉也可直接删除文件  自定义异步 Logger 对象  includeLocation="false" 关闭行号信息,开启可能导致性能比同步还差  additivity="false"      不再继承 RootLogger 对象  -->  <AsyncLogger name="com.clcao" level="trace" includeLocation="false" additivity="false">  <AppenderRef ref=""/>  </AsyncLogger>  <!--自定义的logger, additivity=false 不继承父logger的日志级别 -->  <Logger name="com.example" level="trace" additivity="false">  <AppenderRef ref=""/>  </Logger>    </Loggers>
</Configuration>

3.3 异步日志

log4j2性能提升得益于异步日志,使用异步日志之前,先了解一下日志流程和同步与异步流程的区别。

异步日志参考手册

同步日志

同步日志流程图
在这里插入图片描述

异步日志

异步日志流程图
在这里插入图片描述

在异步日志中,Logger对象只要产生了日志事件LogEvent将事件添加到阻塞队列ArrayBlockingQueue就可以继续往下执行了,可见效率会比同步高很多。

官网性能对比图
在这里插入图片描述

异步的实现分两种,Logger异步和Appender异步实现,从性能分析图👆可见,AsyncAppender的性能提升是很低的,所以主要是使用Loggers async

Logger异步实现又分两种:

  • 全局异步
  • 混合异步
异步日志使用

引入依赖

<!--异步日志依赖-->
<dependency>  <groupId>com.lmax</groupId>  <artifactId>disruptor</artifactId>  <version>3.4.2</version>  
</dependency>
1)AsyncAppender

配置文件

<!--异步的 Appender-->
<Async name="Async">  <Appender-ref ref="file"/>  
</Async>

<Appenders>标签内添加,引用file这个Appender,也就是说,对于名称为fileAppender将进行异步日志输出。

使用异步日志

<Root level="debug">  <AppenderRef ref="console"/>  <AppenderRef ref="Async"/>  
</Root>

<Loggers>标签内进行引用AsyncAppender即可配置完毕!

2)AsyncLogger

全局异步

全局异步就是所有的日志信息都通过异步日志输出,不需要修改任何配置文件,之需要添加一个配置文件log4j2.component.properties

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

混合异步

混合异步就是在应用程序中即使用异步Logger记录日志又使用同步记录日志。

关闭全局异步(这里直接注释掉了):

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

添加配置(在<Loggers>标签内):

<!--  混合异步需要关闭全局异步,即log4j2.component.properties设置关闭,可注释掉也可直接删除文件  自定义异步 Logger 对象  includeLocation="false" 关闭行号信息,开启可能导致性能比同步还差  additivity="false"      不再继承 RootLogger 对象  
-->  
<AsyncLogger name="com.clcao" level="trace" includeLocation="false" additivity="false">  <AppenderRef ref="rollingFile"/>  
</AsyncLogger>

混合异步注意:

  1. AsyncAppender和全局异步和混合异步不要同时使用,会导致性能最低,也就是AsyncAppedner所以使用某一种记得关闭其他两种的使用。
  2. 混合异步中includeLocation="false" 可关闭行号信息,开启可能导致性能比同步还差。

3.4 Log4j2性能

无垃圾记录

log4j2 2.6版本(包括)开始通过重用对象和缓存使用,从而实现减少GC提升性能。

Log4j2设计了一套无垃圾机制,通过重用对象和内存缓冲来减少因频繁日志收集导致的垃圾回收(GC)调用。这意味着在日志记录过程中,尽可能重用已经存在的对象,而不是每次都创建新的对象。这可以减少堆内存的分配和释放,从而降低GC的开销。

异步日志
日志过滤

4 SpringBoot 日志设计结构

依赖关系图:
在这里插入图片描述

总结:

  1. Springboot默认使用logback作为日志实现
  2. 使用slf4j作为日志门面
  3. jul也转为slf4j
  4. 也可以使用log4j2作为日志门面,但最终也是调用slf4j调用logback实现

4.1 快速入门

直接使用

@SpringBootTest  
class SpringbootLogApplicationTests {  private static final Logger LOGGER = LoggerFactory.getLogger(SpringbootLogApplicationTests.class);  @Test  void contextLoads() {  // 日志级别  LOGGER.error("ERROR");  LOGGER.warn("WARN");  LOGGER.info("INFO");    // 默认级别  LOGGER.debug("DEBUG");  LOGGER.trace("TRACE");  // 日志记录信息  String name = "Tom";  int age = 18;  LOGGER.info("用户信息:name={}, age={}",name,age);  // 使用 log4j2 门面 通过桥接器(log4j-to-slf4j.jar) ==> 最后使用 slf4j 门面 最终使用 logback 实现  // 所谓门面,就是对外使用的 API 是哪一套的  org.apache.logging.log4j.Logger logger = LogManager.getLogger(SpringbootLogApplicationTests.class);  logger.info("log4j2 info");}  
}

4.2 配置文件

# 自定义 Logger 对象  
logging.level.com.clcao = trace  # 指定控制台输出格式  
logging.pattern.console= %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%-5level]  %c %M %L - %m%n  # 指定文件日志的路径,默认在该路径下生成spring.log文件  
logging.file.path= logs/  
# 指定文件日志的文件名称,不能和logging.file.path同时配置,同时配置优先文件名生效  
logging.file.name= logs/springLog.log
指定配置

如果需要使用自己的配置文件,而不是用springboot的默认配置,可以在类路径下添加以下文件:

日志框架配置文件
Logbacklogback-spring.xmllogback.xml
Log4j2log4j2-spring.xmllog4j2.xml
JULlogging.properties

logback.xml配置参考:2.2.2 配置文件详解
log4j2.xml配置参考:3.2 配置文件详解
logging.properties配置参考:配置文件详解

关于logback-spring.xmllogback.xml的区别

logback-spring.xml会交给spring解析,交给spring解析的好处是可以配置环境profile,这样就可以配合application.properties灵活切换环境了。

修改logback-spring.xml

<!--日志消息格式设置-->  
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">  <SpringProfile name="dev">  <pattern>dev-${pattern}</pattern>  </SpringProfile>  <SpringProfile name="pro">  <pattern>pro-${pattern}</pattern>  </SpringProfile>
</encoder>

设置application.properties

spring.profiles.active=dev

这样通过切换属性就可以达到不同的日志记录效果。

4.3 切换日志实现

默认logback日志框架,切换log4j2日志框架。

排除logaback依赖并且添加log4j2依赖

<dependency>  <groupId>org.springframework.boot</groupId>  <artifactId>spring-boot-starter</artifactId>  <exclusions>        <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>

spring-boot-starter-log4j2依赖图
在这里插入图片描述

基本同slf4j+Log4j2(主流推荐)

slf4j-api门面入口,找到适配器log4j-slf4j-impl然后由桥接器找到log4j2的日志门面log4j-api最后到具体实现log4j-core

这样就可以直接使用了!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/394231.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

STM32——外部中断(EXTI)

目录 前言 一、外部中断基础知识 二、使用步骤 三、固件库实现 四、STM32CubeMX实现 总结 前言 外部中断&#xff08;External Interrupt&#xff0c;简称EXTI&#xff09;是微控制器用于响应外部事件的一种方式&#xff0c;当外部事件发生时&#xff08;如按键按下、传感器信号…

软件设计模式概述

模式的诞生 模式(Pattern)起源于建筑业而非软件业 模式之父——美国加利佛尼亚大学环境结构中心研究所所长Christopher Alexander&#xff08;克里斯托弗亚历山大&#xff09;博士 《A Pattern Language: Towns, Buildings, Construction》——253个建筑和城市规划模式。 他给出…

atsec增加Swift CSP评估资质

atsec信息安全评估员现已被Swift列为Swift客户安全计划&#xff08;CSP&#xff1a;Customer Security Programme&#xff09;认证评估员目录中的评估提供商&#xff0c;可以帮助全球金融机构评估其针对CSP强制性和咨询性控制的合规级别。在金融行业&#xff0c;Swift要求使用其…

MySQL的三大关键日志:Bin Log、Redo Log与Undo Log

MySQL的三大关键日志&#xff1a;Bin Log、Redo Log与Undo Log 1. Bin Log&#xff08;二进制日志&#xff09;2. Redo Log&#xff08;重做日志&#xff09;3. Undo Log&#xff08;回滚日志&#xff09; &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&…

C++现代教程四

float转string不带多余0 float a 1.2; std::tostring(a); // 1.200000 std::ostringstream strStream; strStream << a; // 1.2 if (!strStream.view().empty()) // 判定流有数据// 边框融合 float measureText(std::u8string text, FontTypes::Rectangle &recta…

科研绘图系列:R语言圆形条形图(circular barplot)

禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 介绍 圆形条形图(circular barplot)是一种条形图,其中的条形沿着圆形而不是线性排列展示。这种图表的输入数据集与普通条形图相同:每个组(一个组即一个条形)需要一个数值。(更多解释请参…

IDEA2024.2重磅发布,更新完有4G!

JetBrains 今天宣布了其 IDE 家族版本之 2024.2 更新&#xff0c;其亮点是新 UI 现在已是默认设置&#xff0c;并且对 AI Assistant &#xff08;AI助手&#xff09;进行了几项改进。 安装密道 新 UI 的设计更加简约&#xff0c;可以根据需要以视觉方式扩展复杂功能。值得开发…

Linux 下查看 CPU 使用率

目录 一、什么是 CPU 使用率二、查看 CPU 利用率1、使用 top 查看2、用 pidstat 查看3、用 ps 查看4、用 htop 查看5、用 nmon 查看6、用 atop 查看7、用 glances 查看8、用 vmstat 查看9、用 sar 查看10、dstat11、iostat 三、总结 CPU 使用率是最直观和最常用的系统性能指标&…

QT生成.exe文件无法在未安装QT的电脑上运行的解决办法

在没有安装qt的电脑上运行qt生成的exe文件&#xff0c;提示&#xff1a; The application failed to start because no Qt platform plugin could be initialized 在网上找了很多办法&#xff0c;我尝试了 手动&#xff1a; 1、修改环境变量&#xff0c;2&#xff0c;添加pla…

C#开发编程软件下载安装

1、Visual Studio 2022社区版下载 2、开始安装 3、安装进行中 。。。。

【linux】curl命令用法

curl命令认识 curl命令其实在平常工作中就已经在使用了&#xff0c;但是一直没有系统看过&#xff0c;就在这记录下&#xff0c;以后要用的话&#xff0c;可以在这儿查阅。 curl命令写的更清楚一点其实是cURL&#xff08;client url&#xff0c;客户端URL或者command url命令…

QT(2.0)

1.常用控件的介绍 1.1 TextEdit QTextEdit表示多行输入框&#xff0c;也是一个富文本&markdown编辑器&#xff0c;并且能在内容超出编辑框范围时自动提供滚动条。 核心属性 属性 说明 markdown 输入框内持有的内容&#xff0c;支持markdown格式&#xff0c;能够自动的…

OpenGL实现3D游戏编程【连载3】——3D空间模型光照初步

1、本节实现的内容 上一节课&#xff0c;我们建立了简单的坐标系&#xff0c;同时也显示了一个正方体&#xff0c;但正方体的颜色为纯红色&#xff0c;好像一个平面物体一样&#xff0c;我们这节课就可以加一些光照&#xff0c;并创建更多的模型&#xff0c;使这些物体变得更加…

显示学习5(基于树莓派Pico) -- 彩色LCD的驱动

一 环境搭建 使用的ST7715S驱动的1.8寸彩色屏&#xff0c;主控是我们熟悉的树莓派Pico。软件环境是micropython。连接是屏幕直接从Pico取3.3V的供电&#xff0c;然后总线用的SPI。 ST7735 PinPico PinVCC3.3VGNDGNDSCL (SCK)GP10SDA (MOSI)GP11RES (RST)GP17DC&#xff08;A0…

【HarmonyOS NEXT星河版开发学习】小型测试案例01-今日头条置顶练习

个人主页→VON 收录专栏→鸿蒙开发小型案例总结​​​​​ 基础语法部分会发布于github 和 gitee上面 ​ 前言 本系列可能是博客首发&#xff0c;鸿蒙开发星河版是一个全新的版本&#xff0c;由于参考视频较少鸿蒙开发不被重视导致csdn上面并没有全套的学习路线&#xff0c;…

第20周:Pytorch文本分类入门

目录 前言 一、前期准备 1.1 环境安装导入包 1.2 加载数据 1.3 构建词典 1.4 生成数据批次和迭代器 二、准备模型 2.1 定义模型 2.2 定义示例 2.3 定义训练函数与评估函数 三、训练模型 3.1 拆分数据集并运行模型 3.2 使用测试数据集评估模型 总结 前言 &#x1…

【JUC】03-CompletableFuture使用

1. CompletableFuture CompletableFuture可以进行回调通知、创建异步任务、多个任务前后依赖可以组合处理、对计算速度选最快。  CompletableFuture提供了一种类似于观察者模式的通知方式&#xff0c;可以在任务完成后通知监听方。 CompletableFuture实例化用CompletableFutur…

【弱网】模拟弱网环境

fiddler工具 调整上传/下载速率 打开fiddler脚本工具&#xff0c;在上方状态栏选择 Rules -> Customize Rules…&#xff0c;打开ScriptEditor编辑器 修改上传/下载速率&#xff0c;实现模拟指定弱网环境 计算公示&#xff1a;[1/(上或下行速率/8)] x 1000 网络上行下载2G2…

【Hive】学习笔记

Hive学习笔记 【一】Hive入门【1】什么是Hive【2】Hive的优缺点&#xff08;1&#xff09;优点&#xff08;2&#xff09;缺点 【3】Hive架构原理&#xff08;1&#xff09;用户接口&#xff1a;Client&#xff08;2&#xff09;元数据&#xff1a;Metastore&#xff08;3&…

相机标定——小孔成像、相机模型与坐标系

小孔成像 用一个带有小孔的板遮挡在墙体与物之间&#xff0c;墙体上就会形成物的倒影&#xff0c;我们把这样的现象叫小孔成像。 用一个带有小孔的板遮挡在墙体与物之间&#xff0c;墙体上就会形成物的倒影&#xff0c;我们把这样的现象叫小孔成像。前后移动中间的板&#xff…