1. log4j2对比 原始Logback 优势
对于 Spring Boot 3.x,Logback 是默认日志框架,但在高并发、异步日志场景下,Log4j2 通常表现更优。当业务百万级用户、微服务、日志量大时:
✅ 1. Logback(默认 Spring Boot 集成)
优势:
-
Spring Boot 原生支持:无需额外配置即可使用。
-
生态成熟:有
logstash-logback-encoder
,方便输出 JSON 日志。 -
简单易用:配置相对直观,官方支持全面。
-
性能稳定:同步写日志表现很好。
劣势:
-
异步性能略弱于 Log4j2:需要借助
AsyncAppender
,但整体吞吐比 Log4j2 略低。 -
无内建 LMAX Disruptor:在极端高并发下性能不如 Log4j2。
✅ 2. Log4j2
优势:
-
异步日志性能极强:基于 LMAX Disruptor,比 Logback AsyncAppender 快 10 倍以上。
-
支持异步安全:IO 密集型日志收集(比如发送到 Kafka)更高效。
-
灵活的 JSON Layout:自带
JsonTemplateLayout
,不依赖第三方包。 -
热加载配置文件:支持修改日志级别而不重启服务。
劣势:
-
非默认框架:需要额外依赖,Spring Boot starter 需要
spring-boot-starter-log4j2
替换。 -
生态稍弱于 Logback:但主流场景完全足够。
2. 使用版本信息
- spring boot 3.3.2
- log4j2: 跟随 spring boot版本,不必特意指定 (spring boot 3.3.2 默认对应log4j2 版本 是 2.24.0)
- jackson: 2.17.2 (参考官网 https://github.com/apache/logging-log4j2/blob/rel/2.24.0/src/changelog/2.24.0/update_com_fasterxml_jackson_jackson_bom.xml)
- win11下
父pom配置
<!-- https://github.com/spring-projects/spring-boot/releases?page=4 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.2</version> <!--对应 mybatis-plus.version 3.5.8-->
<relativePath/>
</parent>
<properties>
<log4j2.version>2.24.0</log4j2.version> <!--spring boot 3.3.2 默认对应log4j2-->
<!--https://github.com/apache/logging-log4j2/blob/rel/2.24.0/src/changelog/2.24.0/update_com_fasterxml_jackson_jackson_bom.xml-->
<jackson.version>2.17.2</jackson.version>
<dependencyManagement>
<dependencies>
<!-- 核心 Spring Boot Starter,但排除默认日志框架 Logback -->
<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>
<!-- 强制所有 commons-logging 排除 -->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version> <!-- 任意真实存在的版本 -->
<!--标记 scope 为 provided,这样 Maven 会认为依赖存在,但不会打包-->
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${jackson.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
子使用模块 service-user 配置
pom配置:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
<!-- 可选:Log4j2 JSON Layout -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-layout-template-json</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
</dependency>
resources 中的 log4j2-spring.xml 配置
log4j2-spring.xml 必须放到 resources 目录下, 不能再加一层目录。
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="TRACE" monitorInterval="30" messageFactory="org.apache.logging.log4j.message.ParameterizedMessageFactory">
<Properties>
<Property name="LOG_PATH">C:/Data/logs</Property>
<Property name="LOG_FILE_NAME">log_${spring:spring.application.name:-unknown}</Property>
<Property name="service.name">${spring:spring.application.name:-unknown}</Property>
</Properties>
<Appenders>
<!-- 控制台输出(原始格式) -->
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="jjj %d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
<!-- JSON 文件输出 -->
<RollingFile name="JsonFile" fileName="${LOG_PATH}/${LOG_FILE_NAME}.json"
filePattern="${LOG_PATH}/${LOG_FILE_NAME}-%d{yyyy-MM-dd}-%i.json">
<!-- <PatternLayout pattern="%d %p %C{1.} [%t] %m%n"/>-->
<JsonTemplateLayout eventTemplateUri="classpath:johnlogging/Log4j2JsonLayoutV4.json"/>
<Policies>
<TimeBasedTriggeringPolicy interval="1" modulate="true"/>
<SizeBasedTriggeringPolicy size="10MB"/>
</Policies>
<DefaultRolloverStrategy max="10"/>
</RollingFile>
</Appenders>
<Loggers>
<!--additivity 属性为“false”,表示在这个日志记录器中,日志不会被发送到它的父级日志记录器中-->
<Logger name="com.johnsport" level="info" additivity="false">
<AppenderRef ref="Console" />
<AppenderRef ref="JsonFile" />
</Logger>
<Root level="INFO">
<AppenderRef ref="Console"/>
<AppenderRef ref="JsonFile"/>
</Root>
</Loggers>
</Configuration>
Log4j2JsonLayout 自定义
resources 中的 johnlogging目录下 Log4j2JsonLayoutV4.json 配置如下,这个里面的内容不能随便写,需要参考官方文档中的定义,才能正确解析:JSON Template Layout :: Apache Log4j
{
"timestamp": {
"$resolver": "timestamp",
"pattern": {
"format": "yyyy-MM-dd HH:mm:ss.SSS",
"timeZone": "Asia/Shanghai"
}
},
"host": "${hostName:-unknown}",
"service": "${sys:service.name:-unknown}",
"level": {
"$resolver": "level",
"field": "name"
},
"logger": {
"$resolver": "logger",
"field": "name"
},
"source": {
"$resolver": "source",
"field": "methodName"
},
"thread": {
"$resolver": "thread",
"field": "name"
},
"message": {
"$resolver": "message",
"stringified": true
},
"exception": {
"$resolver": "exception",
"field": "stackTrace",
"stackTrace": {
"stringified": {
"truncation": {
"suffix": "... [truncated]",
"pointMatcherStrings": ["servlet.http.HttpServlet"]
}
}
}
}
}
子使用模块service-user的 application.yml 相关定义:
spring:
application:
name: service-user
# ...
logging:
config:
- classpath:log4j2-spring.xml
level:
root: info
3. 输出样例
在 指定的目录生成了log文件, 名称为 log_service-user.json
示例内容(手动格式化了下)如下:
{
"timestamp": "2025-08-17 19:29:43.094",
"host": "johnwang",
"service": "service-user",
"level": "INFO",
"logger": "com.yourcompanyname.projname.ServiceUserApplication",
"thread": "main",
"message": "Starting ServiceUserApplication v1.0-SNAPSHOT using Java 17.0.15 with PID 41056 (E:\\Data\\code_dir\\v0\\projname_back\\service\\service-user\\target\\service-user-1.0-SNAPSHOT.jar started by jopc in E:\\Data\\code_dir\\v0\\projname_back)"
}
{
"timestamp": "2025-08-17 19:29:43.103",
"host": "johnwang",
"service": "service-user",
"level": "DEBUG",
"logger": "com.yourcompanyname.projname.ServiceUserApplication",
"thread": "main",
"message": "Running with Spring Boot v3.3.2, Spring v6.1.11"
}
{
"timestamp": "2025-08-17 19:29:43.105",
"host": "johnwang",
"service": "service-user",
"level": "INFO",
"logger": "com.yourcompanyname.projname.ServiceUserApplication",
"thread": "main",
"message": "The following 1 profile is active: \"dev\""
}
{
"timestamp": "2025-08-17 19:29:43.184",
"host": "johnwang",
"service": "service-user",
"level": "WARN",
"logger": "com.alibaba.cloud.nacos.configdata.NacosConfigDataLoader",
"thread": "main",
"message": "[Nacos Config] config[dataId=service-user.yaml, group=DEFAULT_GROUP] is empty"
}
{
"timestamp": "2025-08-17 19:29:46.083",
"host": "johnwang",
"service": "service-user",
"level": "INFO",
"logger": "org.springframework.data.repository.config.RepositoryConfigurationDelegate",
"thread": "main",
"message": "Multiple Spring Data modules found, entering strict repository configuration mode"
}
{
"timestamp": "2025-08-17 19:29:46.087",
"host": "johnwang",
"service": "service-user",
"level": "INFO",
"logger": "org.springframework.data.repository.config.RepositoryConfigurationDelegate",
"thread": "main",
"message": "Bootstrapping Spring Data Redis repositories in DEFAULT mode."
}
{
"timestamp": "2025-08-17 19:29:46.128",
"host": "johnwang",
"service": "service-user",
"level": "INFO",
"logger": "org.springframework.data.repository.config.RepositoryConfigurationDelegate",
"thread": "main",
"message": "Finished Spring Data repository scanning in 19 ms. Found 0 Redis repository interfaces."
}
{
"timestamp": "2025-08-17 19:29:46.538",
"host": "johnwang",
"service": "service-user",
"level": "INFO",
"logger": "org.springframework.cloud.context.scope.GenericScope",
"thread": "main",
"message": "BeanFactory id=5e73db5d-3847-3fef-bba4-62fff8906613"
}
{
"timestamp": "2025-08-17 19:29:48.414",
"host": "johnwang",
"service": "service-user",
"level": "INFO",
"logger": "org.springframework.boot.web.embedded.tomcat.TomcatWebServer",
"thread": "main",
"message": "Tomcat initialized with port 7203 (http)"
}
{
"timestamp": "2025-08-17 19:29:48.451",
"host": "johnwang",
"service": "service-user",
"level": "INFO",
"logger": "org.apache.coyote.http11.Http11NioProtocol",
"thread": "main",
"message": "Initializing ProtocolHandler [\"http-nio-7203\"]"
}
{
"timestamp": "2025-08-17 19:29:48.457",
"host": "johnwang",
"service": "service-user",
"level": "INFO",
"logger": "org.apache.catalina.core.StandardService",
"thread": "main",
"message": "Starting service [Tomcat]"
}
{
"timestamp": "2025-08-17 19:29:48.458",
"host": "johnwang",
"service": "service-user",
"level": "INFO",
"logger": "org.apache.catalina.core.StandardEngine",
"thread": "main",
"message": "Starting Servlet engine: [Apache Tomcat/10.1.26]"
}
{
"timestamp": "2025-08-17 19:29:48.560",
"host": "johnwang",
"service": "service-user",
"level": "INFO",
"logger": "org.apache.catalina.core.ContainerBase.[Tomcat].[localhost].[/]",
"thread": "main",
"message": "Initializing Spring embedded WebApplicationContext"
}
{
"timestamp": "2025-08-17 19:29:48.563",
"host": "johnwang",
"service": "service-user",
"level": "INFO",
"logger": "org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext",
"thread": "main",
"message": "Root WebApplicationContext: initialization completed in 5369 ms"
}
{
"timestamp": "2025-08-17 19:29:49.451",
"host": "johnwang",
"service": "service-user",
"level": "DEBUG",
"logger": "com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean",
"thread": "main",
"message": "Registered plugin: 'MybatisPlusInterceptor{interceptors=[PaginationInnerInterceptor(logger=org.apache.ibatis.logging.slf4j.Slf4jImpl@2adce412, overflow=false, maxLimit=null, dbType=MYSQL, dialect=null, optimizeJoin=true), com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor@629cf53c]}'"
}
{
"timestamp": "2025-08-17 19:29:49.456",
"host": "johnwang",
"service": "service-user",
"level": "DEBUG",
"logger": "com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean",
"thread": "main",
"message": "Property 'mapperLocations' was not specified."
}
{
"timestamp": "2025-08-17 19:29:51.897",
"host": "johnwang",
"service": "service-user",
"level": "INFO",
"logger": "com.alibaba.cloud.sentinel.SentinelWebMvcConfigurer",
"thread": "main",
"message": "[Sentinel Starter] register SentinelWebInterceptor with urlPatterns: [/**]."
}
{
"timestamp": "2025-08-17 19:29:56.239",
"host": "johnwang",
"service": "service-user",
"level": "INFO",
"logger": "org.springframework.boot.actuate.endpoint.web.EndpointLinksResolver",
"thread": "main",
"message": "Exposing 1 endpoint beneath base path '/actuator'"
}
{
"timestamp": "2025-08-17 19:29:56.414",
"host": "johnwang",
"service": "service-user",
"level": "INFO",
"logger": "org.apache.coyote.http11.Http11NioProtocol",
"thread": "main",
"message": "Starting ProtocolHandler [\"http-nio-7203\"]"
}
{
"timestamp": "2025-08-17 19:29:56.439",
"host": "johnwang",
"service": "service-user",
"level": "INFO",
"logger": "org.springframework.boot.web.embedded.tomcat.TomcatWebServer",
"thread": "main",
"message": "Tomcat started on port 7203 (http) with context path '/'"
}
{
"timestamp": "2025-08-17 19:29:56.771",
"host": "johnwang",
"service": "service-user",
"level": "INFO",
"logger": "com.alibaba.cloud.nacos.registry.NacosServiceRegistry",
"thread": "main",
"message": "nacos registry, DEFAULT_GROUP service-user 192.168.171.1:7203 register finished"
}
{
"timestamp": "2025-08-17 19:29:57.606",
"host": "johnwang",
"service": "service-user",
"level": "INFO",
"logger": "com.yourcompanyname.projname.ServiceUserApplication",
"thread": "main",
"message": "Started ServiceUserApplication in 20.963 seconds (process running for 23.326)"
}
{
"timestamp": "2025-08-17 19:29:57.724",
"host": "johnwang",
"service": "service-user",
"level": "INFO",
"logger": "com.alibaba.cloud.nacos.refresh.NacosContextRefresher",
"thread": "main",
"message": "[Nacos Config] Listening config: dataId=service-user.yaml, group=DEFAULT_GROUP"
}
{
"timestamp": "2025-08-17 19:30:10.982",
"host": "johnwang",
"service": "service-user",
"level": "INFO",
"logger": "org.apache.catalina.core.ContainerBase.[Tomcat].[localhost].[/]",
"thread": "http-nio-7203-exec-1",
"message": "Initializing Spring DispatcherServlet 'dispatcherServlet'"
}
{
"timestamp": "2025-08-17 19:30:10.982",
"host": "johnwang",
"service": "service-user",
"level": "INFO",
"logger": "org.springframework.web.servlet.DispatcherServlet",
"thread": "http-nio-7203-exec-1",
"message": "Initializing Servlet 'dispatcherServlet'"
}
{
"timestamp": "2025-08-17 19:30:10.986",
"host": "johnwang",
"service": "service-user",
"level": "INFO",
"logger": "org.springframework.web.servlet.DispatcherServlet",
"thread": "http-nio-7203-exec-1",
"message": "Completed initialization in 3 ms"
}
{
"timestamp": "2025-08-17 19:30:11.581",
"host": "johnwang",
"service": "service-user",
"level": "INFO",
"logger": "com.zaxxer.hikari.HikariDataSource",
"thread": "http-nio-7203-exec-1",
"message": "HikariCP - Starting..."
}
{
"timestamp": "2025-08-17 19:30:12.007",
"host": "johnwang",
"service": "service-user",
"level": "INFO",
"logger": "com.zaxxer.hikari.pool.HikariPool",
"thread": "http-nio-7203-exec-1",
"message": "HikariCP - Added connection com.mysql.cj.jdbc.ConnectionImpl@d123cb8"
}
{
"timestamp": "2025-08-17 19:30:12.014",
"host": "johnwang",
"service": "service-user",
"level": "INFO",
"logger": "com.zaxxer.hikari.HikariDataSource",
"thread": "http-nio-7203-exec-1",
"message": "HikariCP - Start completed."
}
{
"timestamp": "2025-08-17 19:30:12.314",
"host": "johnwang",
"service": "service-user",
"level": "INFO",
"logger": "com.yourcompanyname.projname.guess.controller.GuessUserController",
"thread": "http-nio-7203-exec-1",
"message": "This is an info log test"
}
{
"timestamp": "2025-08-17 19:30:12.314",
"host": "johnwang",
"service": "service-user",
"level": "WARN",
"logger": "com.yourcompanyname.projname.guess.controller.GuessUserController",
"thread": "http-nio-7203-exec-1",
"message": "This is a warn log test"
}
{
"timestamp": "2025-08-17 19:30:41.521",
"host": "johnwang",
"service": "service-user",
"level": "INFO",
"logger": "com.yourcompanyname.projname.guess.controller.LogTestController",
"thread": "http-nio-7203-exec-2",
"message": "This is an info message"
}
{
"timestamp": "2025-08-17 19:30:41.523",
"host": "johnwang",
"service": "service-user",
"level": "ERROR",
"logger": "com.yourcompanyname.projname.guess.controller.LogTestController",
"thread": "http-nio-7203-exec-2",
"message": "Division error occurred",
"exception": "java.lang.ArithmeticException: / by zero\r\n\tat com.yourcompanyname.projname.guess.controller.LogTestController.testLog(LogTestController.java:40)\r\n\tat java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\r\n\tat java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)\r\n\tat java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\r\n\tat java.base/java.lang.reflect.Method.invoke(Method.java:568)\r\n\tat org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:255)\r\n\tat org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:188)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:118)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:926)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:831)\r\n\tat org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)\r\n\tat org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1089)\r\n\tat org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979)\r\n\tat org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014)\r\n\tat org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:903)\r\n\tat jakarta.servlet.http.HttpServlet\r\n... [truncated]"
}