java_spring boot 中使用 log4j2 及 自定义layout设置示例

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. 使用版本信息

父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]"
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

惊鸿一博

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值