我们无法判断一个瞬间的价值,直至它成为回忆。
日志的分类
首先,要知道,在项目中使用的日志分为两个大类。
一类是日志的门面,相当于Java中说的接口API,通过这个门面API去调用底层具体的日志实现。另一类就是日志的实现,即门面调用的日志具体实现。我们在项目中使用一般就是 日志门面 + 日志实现。
1. 日志门面
- JCL
- slf4j
2. 日志实现
- JUL
- log4j
- logback
- log4j2
日志的使用
JUL(Java util logging)
jdk原生的日志框架,使用时不需要引入第三方类库,使用方便,一般用来自己写demo时打印测试日志。
使用示例:
import org.junit.jupiter.api.Test;
import java.util.logging.Level;
import java.util.logging.Logger;
public class AppTest
{
@Test
public void testJUL() {
// 1. 创建日志记录器对象
Logger logger = Logger.getLogger("com.example.AppTest");
// 2. 日志输出
logger.info("test JUL");
logger.log(Level.INFO, "选择日志的输出级别:test JUL");
// 在日志输出中使用占位符
String name = "li";
Integer age = 18;
logger.log(Level.INFO, "用户姓名:{0}, 用户年龄:{1}", new Object[]{name, age});
}
}
输出:
三月 28, 2023 9:41:12 下午 com.example.AppTest testJUL
信息: test JUL
三月 28, 2023 9:41:12 下午 com.example.AppTest testJUL
信息: 选择日志的输出级别:test JUL
三月 28, 2023 9:41:12 下午 com.example.AppTest testJUL
信息: 用户姓名:li, 用户年龄:18
搭配配置文件使用:
# Set root logger level to INFO and its only appender to ConsoleAppender.
log4j.rootLogger=INFO, ConsoleAppender
# Configure the console appender
log4j.appender.ConsoleAppender=org.apache.log4j.ConsoleAppender
log4j.appender.ConsoleAppender.layout=org.apache.log4j.PatternLayout
log4j.appender.ConsoleAppender.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} [%t] %-5p %c %x - %m%n\
# Configure the file appender
log4j.appender.FileAppender=org.apache.log4j.RollingFileAppender
log4j.appender.FileAppender.File=mylog.log
log4j.appender.FileAppender.append=true
log4j.appender.FileAppender.MaxFileSize=10MB
log4j.appender.FileAppender.MaxBackupIndex=5
log4j.appender.FileAppender.layout=org.apache.log4j.PatternLayout
log4j.appender.FileAppender.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} [%t] %-5p %c %x - %m%n
解释:
- 这个配置文件定义了两种日志记录器:ConsoleAppender 和 FileAppender。ConsoleAppender 将日志输出到控制台,FileAppender 将日志输出到文件。
- log4j.rootLogger 定义了日志记录器的根级别为 INFO,这意味着只有 INFO、WARN、ERROR 和 FATAL 级别的日志信息才会被记录。
- og4j.appender.ConsoleAppender 定义了 ConsoleAppender 的布局,它使用了一个 PatternLayout,这个布局输出日志的时间、线程、日志级别、记录器名称和日志消息。
- log4j.appender.FileAppender 定义了 FileAppender 的布局和文件名,它使用了 RollingFileAppender,当日志文件大小超过 10MB 时,它将自动滚动文件。它还设置了最大备份数为 5,这意味着最多有五个备份日志文件。
- 最后,log4j.appender.FileAppender.layout.ConversionPattern 定义了日志记录的格式。
log4j
Apache下一款开源的日志框架。
使用示例
导入pom依赖:
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<!-- log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
编写测试类:
import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.Logger;
import org.junit.Test;
public class AppTest
{
@Test
public void testLog4j() {
// 1. 初始化配置信息
BasicConfigurator.configure();
// 2. 获取日志记录器对象
Logger logger = Logger.getLogger(AppTest.class);
// 3. 日志输出
logger.info("hello log4j");
}
}
搭配配置文件log4j.properties使用:
配置文件示例1:
#设置日志级别
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{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
#输出到文件
log4j.appender.File=org.apache.log4j.RollingFileAppender
log4j.appender.File.File=/var/log/myapp.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{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
#设置包级别
log4j.logger.org.springframework=INFO
log4j.logger.com.example=DEBUG
配置文件示例2:
#设置日志级别
log4j.rootLogger = debug,Console,D,E
#输出信息到控制台
log4j.appender.Console=org.apache.log4j.ConsoleAppender
log4j.appender.Console.layout=org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
#输出DEBUG 级别以上的日志到=E://logs/error.log
log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File = E://logs/log.log
log4j.appender.D.Append = true
log4j.appender.D.Threshold = DEBUG
log4j.appender.D.layout = org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
#输出ERROR 级别以上的日志到=E://logs/error.log
log4j.appender.E = org.apache.log4j.DailyRollingFileAppender
log4j.appender.E.File =E://logs/error.log
log4j.appender.E.Append = true
log4j.appender.E.Threshold = ERROR
log4j.appender.E.layout = org.apache.log4j.PatternLayout
log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
JCL + LOG4J
全称Jakarta Commons Logging,是Apache提供的一个通用日志API。
使用示例:
导入依赖
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<!-- jcl日志门面 -->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<!-- log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
编写测试代码
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.Test;
public class AppTest
{
@Test
public void testJCL() {
Log log = LogFactory.getLog(AppTest.class);
log.info("hello jcl");
}
}
输出
22:28:25.433 [main] INFO com.example.AppTest - hello jcl
slf4j + logback
springboot底层默认使用的日志框架。
- Simple Logging Facade For Java 简单日志门面,主要是为了给java日志访问提供一套标准、规范的API框架,其主要意义在于提供接口,具体的实现可以交由其它日志框架。
- 是由log4j创始人设计的另一个开源日志组件,性能比log4j要好。
使用示例:
导入依赖
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<!-- slf4j -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.30</version>
</dependency>
<!-- logback -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
</dependencies>
编写测试类
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class AppTest
{
private final static Logger LOGGER = LoggerFactory.getLogger(AppTest.class);
@Test
public void testLogback() {
LOGGER.info("hello logback");
}
}
输出
22:19:12.237 [main] INFO com.example.AppTest - hello logback
搭配logback.xml配置文件使用
<configuration>
<!-- 定义日志输出格式 -->
<property name="PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"/>
<!-- 控制台输出 -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${PATTERN}</pattern>
</encoder>
</appender>
<!-- 文件输出 -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/app.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>logs/app-%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${PATTERN}</pattern>
</encoder>
</appender>
<!-- 日志级别 -->
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE"/>
</root>
</configuration>
- 以上配置文件定义了两个输出源,控制台和文件。控制台输出采用ConsoleAppender,文件输出采用RollingFileAppender,并且使用TimeBasedRollingPolicy来按时间轮换日志文件。
- <root>元素定义了日志级别为INFO,并将日志输出到控制台和文件中。可以通过在<root>元素中指定不同的级别来控制日志输出的详细程度。例如,将level属性设置为DEBUG可以在日志中包含更多的详细信息。
log4j2
Log4j2是对Log4j的升级版,参考了logback的一些优秀的设计,并且修复了一些问题,因此带来了一些重大的提升,主要有:
- 异常处理,在logback中,Appender中的异常不会被应用程序感知到,但是在log4j2中,提供了一些异常处理机制。
- 性能提升,log4j相较于logback都具有很明显的性能提升。
- 自动重载机制,参考了logback的设计,当然会提供自动刷新参数配置,最实用的就是我们在生产上可以动态的修改日志的级别而不需要重启应用。
- 无垃圾机制,log4j在大部分情况下,都可以使用其设计的一套无垃圾机制,避免频繁的日志收集导致的jvm gc。
使用示例:
导入依赖
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<!-- 使用slf4j作为日志门面 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.30</version>
</dependency>
<!-- 使用log4j2作为日志实现 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>2.0.7</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.13.3</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.20.0</version>
</dependency>
</dependencies>
编写测试类
import junit.framework.TestCase;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class AppTest
extends TestCase
{
private static final Logger logger = LoggerFactory.getLogger(AppTest.class);
public void testLog4j2() {
logger.info("This is an info message.");
logger.error("This is an error message.");
}
}
输出结果
20:30:16.428 [main] INFO com.example.AppTest - This is an info message.
20:30:16.431 [main] ERROR com.example.AppTest - This is an error message.
搭配配置文件使用
log4j2.xml
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="INFO">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
</Console>
<RollingFile name="RollingFile" fileName="logs/app.log"
filePattern="logs/app-%d{yyyy-MM-dd}.log.gz">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
<Policies>
<TimeBasedTriggeringPolicy interval="1" modulate="true" />
<SizeBasedTriggeringPolicy size="10MB" />
</Policies>
<DefaultRolloverStrategy max="10" />
</RollingFile>
</Appenders>
<Loggers>
<Root level="INFO">
<AppenderRef ref="Console" />
<AppenderRef ref="RollingFile" />
</Root>
</Loggers>
</Configuration>
- 该配置文件定义了两个Appenders(Console和RollingFile)以及一个Root Logger。
- Console Appender将日志输出到控制台,RollingFile Appender将日志输出到一个滚动的日志文件。
- RollingFile Appender使用了时间和大小两种触发策略,每天生成一个新的日志文件,并且日志文件最多保留10个。
log4j2异步日志实现的三种方式
在高并发的情况下,或者业务处理复杂的情况下,使用异步日志实现。
1. 使用AsyncLogger
AsyncLogger是log4j2中提供的异步日志实现方式之一。使用AsyncLogger需要在log4j2.xml配置文件中指定使用AsyncLogger。
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
</Appenders>
<Loggers>
<Root level="info">
<AppenderRef ref="Console"/>
</Root>
<Logger name="async" level="info" additivity="false">
<AppenderRef ref="Console"/>
</Logger>
</Loggers>
<AsyncLogger name="async" level="info" additivity="false">
<AppenderRef ref="Console"/>
</AsyncLogger>
</Configuration>
在上面的配置中,我们定义了一个名为async的Logger,并将其级别设置为info。然后,我们使用AsyncLogger来实现异步日志记录,将日志输出到Console Appender中。
2. 使用AsyncAppender
AsyncAppender是log4j2另一个提供的异步日志实现方式。使用AsyncAppender需要在log4j2.xml配置文件中指定使用AsyncAppender,并同时指定要使用的Appender。
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
<Async name="Async">
<AppenderRef ref="Console"/>
</Async>
</Appenders>
<Loggers>
<Root level="info">
<AppenderRef ref="Console"/>
</Root>
<Logger name="async" level="info" additivity="false">
<AppenderRef ref="Async"/>
</Logger>
</Loggers>
</Configuration>
在上面的配置中,我们定义了一个名为Async的AsyncAppender,并将其Appender设置为Console Appender。然后,我们将async的Logger的Appender设置为Async Appender,实现异步日志记录。
3. 使用Disruptor等其它类似框架
Disruptor是一个高性能的并发框架,可以在log4j2中用来实现异步日志记录。
使用Disruptor需要在log4j2.xml配置文件中指定使用Disruptor,并同时指定要使用的Appender。
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
<Disruptor name="Disruptor" bufferSize="1024">
<AppenderRef ref="Console"/>
</Disruptor>
</Appenders>
<Loggers>
<Root level="info">
<AppenderRef ref="Console"/>
</Root>
<Logger name="async" level="info" additivity="false">
<AppenderRef ref="Disruptor"/>
</Logger>
</Loggers>
</Configuration>
在上面的配置中,我们定义了一个名为Disruptor的Disruptor Appender,并将其Appender设置为Console Appender。然后,我们将async的Logger的Appender设置为Disruptor Appender,实现异步日志记录。
效率 3 > 2 > 1
springboot使用slf4j + log4j2
1. 排除springboot内部默认的logback依赖,导入log4j依赖
2. 在src/main/resources目录下新建log4j2.xml文件,内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<Console name="console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %highlight{%-5level} %logger{36} - %msg%n"/>
</Console>
<RollingFile name="file" fileName="logs/myapp.log"
filePattern="logs/myapp-%d{yyyy-MM-dd}-%i.log.gz">
<PatternLayout>
<Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n</Pattern>
</PatternLayout>
<Policies>
<SizeBasedTriggeringPolicy size="100 MB"/>
</Policies>
<DefaultRolloverStrategy max="20"/>
</RollingFile>
</Appenders>
<Loggers>
<Root level="info">
<AppenderRef ref="console"/>
<AppenderRef ref="file"/>
</Root>
</Loggers>
</Configuration>
以上配置文件中,定义了两个Appender,一个是Console Appender,用于将日志输出到控制台;另一个是RollingFile Appender,用于将日志输出到文件中,并支持文件滚动。其中,RollingFile Appender使用了SizeBasedTriggeringPolicy来控制文件大小,以及DefaultRolloverStrategy来控制文件滚动。最后,在Loggers中配置了Root Logger,将日志输出到Console和RollingFile Appender中。
3. 在Spring Boot的application.properties文件中,配置日志级别:
logging.level.root=info
以上配置将Root Logger的日志级别设置为info,也可以根据需要设置其他Logger的日志级别。
4. 在代码中使用Log4j2输出日志:
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.debug("Debug message");
logger.info("Info message");
logger.warn("Warn message");
logger.error("Error message");
}
}
使用上述配置后,日志将会按照配置输出到控制台和文件中。如果需要更改日志输出的路径,可以在log4j2.xml文件中修改RollingFile Appender中的fileName属性。如果需要更改日志文件的格式,可以修改PatternLayout中的pattern属性。如果需要设置其他的Appender和Logger,可以在log4j2.xml文件中进行配置。