Bootstrap

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$RootLogger
    Logger logger2 = Logger.getLogger("com.clcao");  // 父为 logger1
    Logger logger3 = Logger.getLogger("com.clcao.log");   // 父为 logger2
  
    System.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==>默认日志级别为 INFO
  
    printLog(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

这样就可以直接使用了!

;