第一章 日志原理
1.1 log发展历史
从JDK1.4开始提供java.until.logging,后来大佬发现JUL太难用了,就自己手撸了个log4j,后来log4j发现安全漏洞,加上代码结构问题难以维护,于是从1.2就停止更新log4j,并又重新手撸了个log4j2,后来这个大佬手撸了一个性能更高、功能更全的logback,从此,这个大佬构建了log的世界,也创造了最常见的日志框架:JUL、log4j、log4j2、logback。
1.2 SLF4J接口
SLF4J:Simple Logging Facade for Java
目前已经提及的四个日志框架,如果我们想用来记录日志的话,需要完成它们必要的配置,并且在代码中获取Logger,打印日志。
// 使用log4j,需要log4j.jar
import org.apache.log4j.Logger;
Logger logger = Logger.getLogger(Test.class);
logger.info("Hello World!");// 使用log4j2,需要log4j-api.jar、log4j-core.jar
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
Logger logger = LogManager.getLogger(Test.class);
logger.info("Hello World!");// logback,需要logback-classic.jar、logback-core.jar
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
Logger logger = new LoggerContext().getLogger(Test.class);
logger.info("Hello World!");// java.until.logging,简称jul
import java.util.logging.Logger;
Logger logger = Logger.getLogger("java.Test");
从上面不难看出,使用不同的日志框架,就要引入不同的jar包,使用不同的代码获取Logger。假设一个项目在漫长的升级过程中,想从jul升级到logback,还得需要修改代码。如果100个class中使用了jul,就得修改100个地方,这是多么一个繁琐的工作。于是Apache Commons Logging出现了。Common-logging提供了一个日志入口,称作"门面日志",即它不负责写日志,而是提供用一个统一的接口,通过jar来决定使用的日志框架,这样就不要再更换框架的时候再修改代码了。后来开发了log4j的大佬在嫌弃Common-logging难用之后又开发了同样功能的slf4j,今天就拿slf4j讲述门面日志。
使用slf4j的代码如下:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Logger logger = LoggerFactory.getLogger(Test.class);
logger.info("Hello World!")
那么slf4j如何决定使用哪个框架日志呢?如slf4j官方图所示:
这就是slf4j和其他框架的组合,使用slf4j需要首先导入slf4j-api.jar,和log4j配合,你就要导入log4j.jar,以及他们之间的桥接包slf4j-log412.jar。这个官方图美中不足的地方是,没有log4j2的配合方式,和log4j2配合需要导入桥接包log4j-slf4j-impl.jar和log4j2的**log4j-api.jar、log4j-core.jar。logback只需要导入logback-classic和logback-core.jar**即可,不需要桥接包。
“Class path contains multiple SLF4J bindings.” 在使用slf4j的时候会遇到以上的报告信息。我也曾遇到过web服务因为slf4j问题启动失败。究其根本是因为logback-classic、log4j-slf4j-impl、slf4j-log412、slf4j-jdk这些jar不能同时存在。他们都实现了StaticLoggerBinder类而导致冲突,slf4j无法确定到底用哪个日志框架。
第二章 SLF4J门面与其他日志框架
参考:https://www.slf4j.org/manual.html
2.1 JUL和SLF4J
JUL:是Java平台自带的日志系统,即java.util.logging包提供的日志功能。
2.1.1 引入依赖
<!-- java.util.logging适配slf4j -->
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-jdk14 -->
<dependency><groupId>org.slf4j</groupId><artifactId>slf4j-jdk14</artifactId><version>2.0.17</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
<dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>2.0.17</version>
</dependency>
2.1.1 配置文件
logging.properties
# 配置根日志记录器的级别和处理器
.level = INFO
handlers = java.util.logging.FileHandler, java.util.logging.ConsoleHandler# 配置文件处理器
java.util.logging.FileHandler.level = INFO
java.util.logging.FileHandler.pattern = %h/java%u.log
java.util.logging.FileHandler.limit = 50000
java.util.logging.FileHandler.count = 1
java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter
java.util.logging.FileHandler.append = true# 配置控制台处理器
java.util.logging.ConsoleHandler.level = INFO
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter# 配置特定包的日志级别
com.example.level = error# 配置特定类的日志级别(如果需要的话)
# com.example.myapp.MyClass.level = FINE# 全局格式化器配置(可选,通常已在处理器中指定)
# java.util.logging.ConsoleFormatter = java.util.logging.SimpleFormatter
# java.util.logging.XMLFormatter.limit = 5000
# 注意:全局格式化器配置通常不是必需的,因为可以在处理器级别指定# 注意:%h 表示用户主目录,%u 表示唯一标识符(用于避免文件名冲突)
# limit 属性指定文件大小限制(以字节为单位),count 属性指定保留的旧文件数量
2.1.3 案例演示
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;public class MyApp {private static final Logger logger = LoggerFactory.getLogger(MyApp.class);public static void main(String[] args) {logger.info("这是info级别的日志信息");logger.error("这是error级别的日志信息");}
}
2.2 log4j和SLF4J
接口包、桥接包、log4j
2.2.1 引入依赖
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
<dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>2.0.17</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-log4j12 -->
<dependency><groupId>org.slf4j</groupId><artifactId>slf4j-log4j12</artifactId><version>2.0.17</version>
</dependency>
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version>
</dependency>
2.2.2 配置文件
log4j.properties
# 设置根日志级别为INFO,输出到控制台和文件
log4j.rootLogger=INFO, CONSOLE, FILE# 控制台输出配置
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d [%t] %-5p %c - %m%n# 文件输出配置
log4j.appender.FILE=org.apache.log4j.RollingFileAppender
log4j.appender.FILE.File=./logfile.log
log4j.appender.FILE.MaxFileSize=10MB
log4j.appender.FILE.MaxBackupIndex=10
log4j.appender.FILE.layout=org.apache.log4j.PatternLayout
log4j.appender.FILE.layout.ConversionPattern=%d [%t] %-5p %c - %m%n
log4j.appender.FILE.encoding=UTF-8
或者log4j.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/"><!-- 定义根记录器的级别和输出目标 --><root><priority value="INFO" /><appender-ref ref="FILE" /><appender-ref ref="CONSOLE" /></root><!-- 配置文件输出 --><appender name="FILE" class="org.apache.log4j.RollingFileAppender"><param name="File" value="/path/to/logfile.log" /><param name="MaxFileSize" value="10MB" /><param name="MaxBackupIndex" value="10" /><layout class="org.apache.log4j.PatternLayout"><param name="ConversionPattern" value="%d [%t] %-5p %c - %m%n" /></layout></appender><!-- 配置控制台输出 --><appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender"><layout class="org.apache.log4j.PatternLayout"><param name="ConversionPattern" value="%d [%t] %-5p %c - %m%n" /></layout></appender><!-- 可选:为特定包或类设置日志级别 --><!--<logger name="com.example.myapp" additivity="false"><level value="debug" /><appender-ref ref="FILE" /></logger>--></log4j:configuration>
2.2.3 案例演示
import org.apache.log4j.Logger;public class Main {private static final Logger logger = Logger.getLogger(Main.class);public static void main(String[] args) {logger.debug("这是一个debug级别的日志消息");logger.info("这是一个info级别的日志消息");logger.warn("这是一个warn级别的日志消息");logger.error("这是一个error级别的日志消息");}
}
2.3 reload4j和SLF4J
接口包、桥接包、reload4j包
reload4j是Log4j 1.2.17的一个分支,旨在解决Log4j中最为迫切的安全问题。它设计成一种即插即用的解决方案,可以无缝切换,直接替换库文件,无需代码更改,就能简化升级过程。
你需要在你的pom.xml文件中添加reload4j和SLF4J的依赖。由于reload4j是log4j 1.2.7的替代品,并且与SLF4J兼容
2.5.1 引入依赖
<dependencies>
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
<dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>2.0.17</version>
</dependency>
<!-- reload4j作为log4j 1.2.7的替代品 -->
<!-- https://mvnrepository.com/artifact/ch.qos.reload4j/reload4j -->
<dependency><groupId>ch.qos.reload4j</groupId><artifactId>reload4j</artifactId><version>1.2.26</version>
</dependency>
<!-- SLF4J到reload4j的绑定 -->
<dependency><groupId>org.slf4j</groupId><artifactId>slf4j-reload4j</artifactId><version>2.0.17</version><scope>compile</scope>
</dependency>
</dependencies>
2.5.2 配置文件
reload4j的配置文件与log4j 1.2.7的配置文件兼容,因此你可以使用相同的log4j.properties文件来配置reload4j
# 设置根Logger的级别和Appender
log4j.rootLogger=DEBUG, stdout, file# 控制台Appender配置
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} [%t] %-5p %c - %m%n# 文件Appender配置
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.File=app.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} [%t] %-5p %c - %m%n
2.5.3 案例演示
package com.example;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;public class Main {// 获取一个Logger实例,通常使用当前类的Class对象作为参数private static final Logger logger = LoggerFactory.getLogger(Main.class);public static void main(String[] args) {// 记录不同级别的日志信息logger.trace("This is a TRACE level message");logger.debug("This is a DEBUG level message");logger.info("This is an INFO level message");logger.warn("This is a WARN level message");logger.error("This is an ERROR level message");// 记录包含变量信息的日志String userName = "JohnDoe";int age = 30;logger.info("User {} is {} years old.", userName, age);// 记录异常信息try {int result = 10 / 0; // 这将引发一个ArithmeticException} catch (ArithmeticException e) {logger.error("An arithmetic error occurred: {}", e.getMessage(), e);}}
}
2.4 log4j2和SLF4J
Log4j2是Apache Log4j的升级版,提供了强大的日志记录功能
- org.apache.logging.log4j:log4j-core:2.24.2
- org.apache.logging.log4j:log4j-api:2.24.2
根据依赖。我们需要接口包、桥接包、log4j2本身包。但是我们发现log4j-slf4j2-impl依赖其他包。因此可以简化。只需引入log4j-slf4j2-impl这一个包即可。
<!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-slf4j2-impl -->
<dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-slf4j2-impl</artifactId><version>2.24.3</version><scope>compile</scope>
</dependency>
2.3.1 引入依赖
log4j-core包含了log4j-api一个顶两
<dependencies>
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
<dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>2.0.17</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-slf4j2-impl -->
<dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-slf4j2-impl</artifactId><version>2.24.3</version><scope>compile</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core -->
<dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-core</artifactId><version>2.24.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-api -->
<dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-api</artifactId><version>2.24.3</version>
</dependency>
</dependencies>
2.3.2 配置文件
log4j2.properties
# 设置根日志级别为 INFO,并指定日志输出到控制台和文件
rootLogger.level = info
rootLogger.appenderRefs = stdout, file
rootLogger.appenderRef.stdout.ref = Console
rootLogger.appenderRef.file.ref = File# 控制台日志配置
appender.stdout.type = Console
appender.stdout.name = Console
appender.stdout.layout.type = PatternLayout
appender.stdout.layout.pattern = %d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n# 文件日志配置
appender.file.type = File
appender.file.name = File
appender.file.fileName = logs/app.log
appender.file.layout.type = PatternLayout
appender.file.layout.pattern = %d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n# 日志文件滚动策略配置(可选)
appender.rolling.type = RollingFile
appender.rolling.name = RollingFile
appender.rolling.fileName = logs/app-rolling.log
appender.rolling.filePattern = logs/app-rolling-%d{yyyy-MM-dd}.log.gz
appender.rolling.layout.type = PatternLayout
appender.rolling.layout.pattern = %d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
appender.rolling.policies.type = Policies
appender.rolling.policies.time.type = TimeBasedTriggeringPolicy
appender.rolling.policies.time.interval = 1
appender.rolling.policies.time.modulate = true
appender.rolling.strategy.type = DefaultRolloverStrategy
appender.rolling.strategy.max = 30# 将滚动日志添加到根日志记录器(可选)
rootLogger.appenderRef.rolling.ref = RollingFile
log4j2.xml
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN"><Appenders><!-- Console Appender --><Console name="Console" target="SYSTEM_OUT"><PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} %-5level %logger{36} - %msg%n"/></Console><!-- File Appender --><File name="File" fileName="logs/app.log"><PatternLayout><Pattern>%d{yyyy-MM-dd HH:mm:ss} %-5level %logger{36} - %msg%n</Pattern></PatternLayout></File></Appenders><Loggers><!-- Root Logger --><Root level="debug"><AppenderRef ref="Console"/><AppenderRef ref="File"/></Root><!-- Example Logger for a specific package --><Logger name="com.example" level="info" additivity="false"><AppenderRef ref="Console"/><AppenderRef ref="File"/></Logger></Loggers>
</Configuration>
2.3.3 案例演示
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;public class Slf4jExample {private static final Logger logger = LoggerFactory.getLogger(Slf4jExample.class);public static void main(String[] args) {logger.error("This is an error message using SLF4J");logger.info("This is an info message using SLF4J");logger.debug("This is a debug message using SLF4J");}
}
2.5 logback和SLF4J
2.4.1 引入依赖
我们只需要引入logback-classic另外两个就自动引入了。
<dependencies><!-- SLF4J API --><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>2.0.17</version> </dependency>
<!-- https://mvnrepository.com/artifact/ch.qos.logback/logback-classic -->
<dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.5.18</version><scope>compile</scope>
</dependency>
</dependencies>
2.4.2 配置文件
Logback通常会在类路径下寻找名为logback.xml的配置文件来配置日志记录行为
<?xml version="1.0" encoding="UTF-8"?>
<configuration><!-- 控制台Appender --><appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"><encoder><pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern></encoder></appender><!-- 文件Appender --><appender name="FILE" class="ch.qos.logback.core.FileAppender"><file>app.log</file><append>true</append><encoder><pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern></encoder></appender><!-- Root Logger配置 --><root level="debug"><appender-ref ref="STDOUT" /><appender-ref ref="FILE" /></root>
</configuration>
2.4.3 案例演示
package com.example;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;public class Main {// 获取一个Logger实例,通常使用当前类的Class对象作为参数private static final Logger logger = LoggerFactory.getLogger(Main.class);public static void main(String[] args) {// 记录不同级别的日志信息logger.trace("This is a TRACE level message");logger.debug("This is a DEBUG level message");logger.info("This is an INFO level message");logger.warn("This is a WARN level message");logger.error("This is an ERROR level message");// 记录包含变量信息的日志String userName = "JohnDoe";int age = 30;logger.info("User {} is {} years old.", userName, age);// 记录异常信息try {int result = 10 / 0; // 这将引发一个ArithmeticException} catch (ArithmeticException e) {logger.error("An arithmetic error occurred: {}", e.getMessage(), e);}}
}
2.6 slf4j-simpl自带
slf4j-api-2.0.16.jar和 slf4j-simple-2.0.16.jar
SLF4J(Simple Logging Facade for Java)是一个为Java平台上的多种日志框架提供通用接口的日志门面。通过SLF4J,开发者可以在不修改代码的情况下轻松地切换底层日志实现。以下是一个使用slf4j-api-2.0.17.jar
和slf4j-simple-2.0.17.jar
的案例,展示了如何配置和使用这两个jar包进行日志记录。
假设我们正在开发一个Java应用程序,并且希望使用SLF4J作为日志门面,同时使用SLF4J自带的简单日志实现slf4j-simple
作为日志后端。
2.6.1 添加依赖
<dependencies><!-- SLF4J API --><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>2.0.17</version></dependency><!-- SLF4J Simple Binding --><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-simple</artifactId><version>2.0.17</version></dependency>
</dependencies>
2.6.2 配置文件
slf4j-simple
提供了简单的日志配置选项,默认情况下会将日志输出到标准输出(控制台)。如果需要自定义日志配置(如日志级别、输出格式等),可以通过创建一个名为simplelogger.properties
的配置文件来实现。以下是一个示例配置文件:
# 设置默认的日志级别为INFO
org.slf4j.simpleLogger.defaultLogLevel=INFO# 为特定的类设置日志级别
org.slf4j.simpleLogger.log.Main=DEBUG# 设置日期和时间格式
org.slf4j.simpleLogger.dateTimeFormat=yyyy-MM-dd HH:mm:ss# 设置日志输出格式
org.slf4j.simpleLogger.showDateTime=true
org.slf4j.simpleLogger.showLogName=true
org.slf4j.simpleLogger.showThreadName=true
org.slf4j.simpleLogger.levelInBrackets=true
#org.slf4j.simpleLogger.logFile=myapp.log
2.6.3 案例演示
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;public class MyApp {// 创建一个Logger实例private static final Logger logger = LoggerFactory.getLogger(MyApp.class);public static void main(String[] args) {// 记录不同级别的日志信息logger.debug("This is a debug message");logger.info("This is an info message");logger.warn("This is a warn message");logger.error("This is an error message");// 记录带有参数的日志信息String name = "World";logger.info("Hello, {}", name);}
}
第三章 Commons Logging门面与其他日志框架
commons-logging总是能找到一个日志实现类,并且尽可能找到一个"最合适"的日志实现类.
3.1 原理
LogFactory作为log的工厂存在,使用动态查找机制进行log实例的获取
①首先在classpath下寻找commons-logging.properties文件。如果找到,则使用其中定义的Log实现类;如果找不到,则在查找是否已定义系统环境变量org.apache.commons.logging.Log,找到则使用其定义的Log实现类;
②查看classpath中是否有Log4j的包,如果发现,则自动使用Log4j作为日志实现类;
③使用JDK自身的日志实现类(JDK1.4以后才有日志实现类);
④使用commons-logging自己提供的一个简单的日志实现类SimpleLog;
上述步骤当LogFactory成功找到一个日志实现之后就会停止
org.apache.commons.logging.impl.Jdk14Logger,
org.apache.commons.logging.impl.Log4JLogger,
org.apache.commons.logging.impl.LogKitLogger,
org.apache.commons.logging.impl.SimpleLog,
org.apache.commons.logging.impl.NoOpLog
LogFactory使用动态查找机制进行日志实例化,执行顺序为:common-logging.properties---->系统环境变量------->log4j—>jul—>simplelog---->nooplog
3.2 与Log4j结合
我们将配置Commons Logging以在运行时选择Log4j作为日志实现,并编写一个简单的Java类来记录日志。最悲观的情况下也总能保证提供一个日志实现(SimpleLog)
3.2.1 引入依赖
<!-- https://mvnrepository.com/artifact/commons-logging/commons-logging -->
<dependency><groupId>commons-logging</groupId><artifactId>commons-logging</artifactId><version>1.3.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version>
</dependency>
3.2.2 配置文件
log4j.properties
# 配置根Logger
log4j.rootLogger=INFO, stdout, file# 配置控制台输出
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n# 配置文件输出
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.File=application.log
log4j.appender.file.MaxFileSize=10MB
log4j.appender.file.MaxBackupIndex=5
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
3.2.3 案例演示
package org.example;import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;/*** @author whboy* @create 2025-05-02* @Description*/
public class Main {private static final Log logger = LogFactory.getLog(Main.class);public static void main(String[] args) {logger.info("This is an info message");logger.debug("This is a debug message");logger.error("This is an error message");}
}
3.3 与java.util.logging结合
Commons Logging与java.util.logging(JUL)的结合可以通过配置来实现,尽管在实际应用中,由于JUL的功能相对有限,开发者更倾向于将Commons Logging与更强大的日志框架(如Log4j或Logback)结合使用。不过,以下是一个简单的案例,展示了如何将Commons Logging配置为使用JUL作为底层日志实现。
3.2.1 引入依赖
<!-- https://mvnrepository.com/artifact/commons-logging/commons-logging -->
<dependency><groupId>commons-logging</groupId><artifactId>commons-logging</artifactId><version>1.3.5</version>
</dependency>
通常不需要单独引入JUL库,因为JUL是Java标准库的一部分,随JDK一起提供
3.2.2 配置文件
我们不需要显式配置commons-logging.properties
文件或设置系统属性,因为JUL是JDK自带的,Commons Logging会自动检测到它并使用它(如果其他日志实现未被明确指定)
3.2.3 案例演示
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;public class Main {private static final Log logger = LogFactory.getLog(Main.class);public static void main(String[] args) {logger.info("This is an info message from Commons Logging using JUL");logger.debug("This is a debug message");logger.error("This is an error message");}
}
3.4 和Log4j2结合
以下是一个将Commons Logging与Log4j2结合的案例,展示了如何配置和使用这两个框架来记录日志。
在这个案例中,我们将使用Commons Logging作为日志门面,Log4j2作为实际的日志实现框架。我们将编写一个简单的Java类来演示如何记录不同级别的日志信息,并通过配置Log4j2的XML文件来控制日志的输出格式和目的地。
3.4.1 引入依赖
<!-- https://mvnrepository.com/artifact/commons-logging/commons-logging -->
<dependency><groupId>commons-logging</groupId><artifactId>commons-logging</artifactId><version>1.3.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core -->
<dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-core</artifactId><version>2.24.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-jcl -->
<dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-jcl</artifactId><version>2.24.3</version>
</dependency>
3.4.2 配置文件
log4j2.properties
# 设置根日志级别为 INFO,并指定日志输出到控制台和文件
rootLogger.level = info
rootLogger.appenderRefs = stdout, file
rootLogger.appenderRef.stdout.ref = Console
rootLogger.appenderRef.file.ref = File# 控制台日志配置
appender.stdout.type = Console
appender.stdout.name = Console
appender.stdout.layout.type = PatternLayout
appender.stdout.layout.pattern = %d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n# 文件日志配置
appender.file.type = File
appender.file.name = File
appender.file.fileName = logs/app.log
appender.file.layout.type = PatternLayout
appender.file.layout.pattern = %d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n# 日志文件滚动策略配置(可选)
appender.rolling.type = RollingFile
appender.rolling.name = RollingFile
appender.rolling.fileName = logs/app-rolling.log
appender.rolling.filePattern = logs/app-rolling-%d{yyyy-MM-dd}.log.gz
appender.rolling.layout.type = PatternLayout
appender.rolling.layout.pattern = %d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
appender.rolling.policies.type = Policies
appender.rolling.policies.time.type = TimeBasedTriggeringPolicy
appender.rolling.policies.time.interval = 1
appender.rolling.policies.time.modulate = true
appender.rolling.strategy.type = DefaultRolloverStrategy
appender.rolling.strategy.max = 30# 将滚动日志添加到根日志记录器(可选)
rootLogger.appenderRef.rolling.ref = RollingFile
log4j2.xml
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN"><Appenders><!-- Console Appender --><Console name="Console" target="SYSTEM_OUT"><PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} %-5level %logger{36} - %msg%n"/></Console><!-- File Appender --><File name="File" fileName="logs/app.log"><PatternLayout><Pattern>%d{yyyy-MM-dd HH:mm:ss} %-5level %logger{36} - %msg%n</Pattern></PatternLayout></File></Appenders><Loggers><!-- Root Logger --><Root level="debug"><AppenderRef ref="Console"/><AppenderRef ref="File"/></Root><!-- Example Logger for a specific package --><Logger name="com.example" level="info" additivity="false"><AppenderRef ref="Console"/><AppenderRef ref="File"/></Logger></Loggers>
</Configuration>
3.4.3 案例演示
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;public class Main {private static final Log logger = LogFactory.getLog(Main.class);public static void main(String[] args) {logger.trace("This is a TRACE message");logger.debug("This is a DEBUG message");logger.info("This is an INFO message");logger.warn("This is a WARN message");logger.error("This is an ERROR message");logger.fatal("This is a FATAL message"); // Note: Commons Logging does not have a fatal level, but Log4j2 will handle it}
}
3.5 与logback结合
Commons Logging和Logback是Java日志记录领域的两种工具,虽然它们各自有独立的用途和实现方式,但在某些情况下可以结合使用。以下是一个简单的案例,展示了如何在项目中结合使用Commons Logging和Logback。
假设我们有一个Java Web项目,该项目使用Commons Logging作为日志记录的接口,而实际的日志实现则使用Logback。我们的目标是配置项目,以便它能够记录日志到控制台和文件中。
3.5.1 引入依赖
<!-- https://mvnrepository.com/artifact/commons-logging/commons-logging -->
<dependency><groupId>commons-logging</groupId><artifactId>commons-logging</artifactId><version>1.3.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/ch.qos.logback/logback-classic -->
<dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.5.18</version><scope>compile</scope>
</dependency>
3.5.2 配置文件
Logback通常会在类路径下寻找名为logback.xml的配置文件来配置日志记录行为
<?xml version="1.0" encoding="UTF-8"?>
<configuration><!-- 控制台Appender --><appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"><encoder><pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern></encoder></appender><!-- 文件Appender --><appender name="FILE" class="ch.qos.logback.core.FileAppender"><file>app.log</file><append>true</append><encoder><pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern></encoder></appender><!-- Root Logger配置 --><root level="debug"><appender-ref ref="STDOUT" /><appender-ref ref="FILE" /></root>
</configuration>
3.5.3 案例演示
package org.example;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;public class Main {// 获取一个Logger实例,通常使用当前类的Class对象作为参数private static final Logger logger = LoggerFactory.getLogger(Main.class);public static void main(String[] args) {// 记录不同级别的日志信息logger.trace("This is a TRACE level message");logger.debug("This is a DEBUG level message");logger.info("This is an INFO level message");logger.warn("This is a WARN level message");logger.error("This is an ERROR level message");// 记录包含变量信息的日志String userName = "JohnDoe";int age = 30;logger.info("User {} is {} years old.", userName, age);// 记录异常信息try {int result = 10 / 0; // 这将引发一个ArithmeticException} catch (ArithmeticException e) {logger.error("An arithmetic error occurred: {}", e.getMessage(), e);}}
}
3.6 与reload4j结合
要将Commons Logging与reload4j结合使用,通常是因为项目中已经使用了Commons Logging作为日志门面(facade),但希望将底层的日志实现替换为reload4j以利用其安全性增强和漏洞修复。
3.6.1 引入依赖
<!-- https://mvnrepository.com/artifact/commons-logging/commons-logging -->
<dependency><groupId>commons-logging</groupId><artifactId>commons-logging</artifactId><version>1.3.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/ch.qos.reload4j/reload4j -->
<dependency><groupId>ch.qos.reload4j</groupId><artifactId>reload4j</artifactId><version>1.2.26</version>
</dependency>
3.6.2 配置文件
reload4j的配置方式与Log4j 1.2.x相似。通常,你需要创建一个log4j.properties
或log4j.xml
配置文件,并在其中指定日志记录器(logger)、附加器(appender)和布局(layout)等设置
以下是一个简单的log4j.properties
配置文件示例:
# 设置根记录器的级别和Appender
log4j.rootLogger=DEBUG, stdout, file# 配置控制台输出的Appender
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n# 配置文件输出的Appender
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.File=logs/app.log
log4j.appender.file.MaxFileSize=10MB
log4j.appender.file.MaxBackupIndex=5
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
3.6.3 案例演示
package org.example;import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;/*** @author whboy* @create 2025-05-02* @Description*/public class Main {private static final Log log = LogFactory.getLog(Main.class);public static void main(String[] args) {log.info("This is an info message");log.debug("This is a debug message");log.error("This is an error message");}
}
附录
门面和接口只能选择一个。否则会冲突报错。