如何在Log4j2 YAML配置中引用属性?占位符未替换问题求助
Log4j2 YAML配置中Spring属性占位符未替换的解决方法
问题现象
使用以下Log4j2.yaml配置:
Configuration: Appenders: Console: name: Console target: SYSTEM_OUT JSONLayout: compact: true eventEol: true properties: true stacktraceAsString: true includeTimeMillis: true KeyValuePair: - key: appName value: ${spring.application.name} - key: version value: ${spring.application.version} Loggers: Root: level: INFO AppenderRef: ref: Console Logger: - name: org.apache level: WARN AppenderRef: ref: Console - name: org.springframework.beans.factory level: WARN AppenderRef: ref: Console
生成的日志中,appName和version的占位符未被替换:
{"instant":{"epochSecond":1705250772,"nanoOfSecond":209226000},"thread":"main","level":"INFO","loggerName":"com.hyperbank.architecture.base.AbstractHyperBankApplication","message":"Hello, it's me, 'config-provider' !!!","endOfBatch":false,"loggerFqcn":"org.apache.logging.slf4j.Log4jLogger","contextMap":{},"threadId":1,"threadPriority":5,"appName":"${spring.application.name}","version":"${spring.application.version}"}
但相同的属性引用在banner.txt中能正常显示实际值:
_ _ ___ _ ___ __ _ ___ _ _ | || |_ _ _ __ ___ _ _| _ ) __ _ _ _ | |__ / __|___ _ _ / _(_)__ _ | _ \_ _ _____ _(_)__| |___ _ _ | __ | || | '_ \/ -_) '_| _ \/ _` | ' \| / / | (__/ _ \ ' \| _| / _` | | _/ '_/ _ \ V / / _` / -_) '_| |_||_|\_, | .__/\___|_| |___/\__,_|_||_|_\_\ \___\___/_||_|_| |_\__, | |_| |_| \___/\_/|_\__,_\___|_| |__/|_| |___/ Name: config-provider Version: 1.0.0 Powered by Spring Boot 3.1.2
问题原因
Log4j2的配置加载时机早于Spring上下文初始化,默认无法直接识别并解析Spring环境中的${spring.application.name}这类属性;而banner.txt的属性替换由Spring Boot直接处理,因此能正常生效。
解决方案
方法1:使用Spring集成的Log4j2属性语法
在Log4j2配置中通过${spring:属性名}的语法直接引用Spring环境属性,这是Spring Boot集成Log4j2的标准方式,无需额外配置即可实现属性替换。修改后的配置如下:
Configuration: Appenders: Console: name: Console target: SYSTEM_OUT JSONLayout: compact: true eventEol: true properties: true stacktraceAsString: true includeTimeMillis: true KeyValuePair: - key: appName value: ${spring:spring.application.name} - key: version value: ${spring:spring.application.version} Loggers: Root: level: INFO AppenderRef: ref: Console Logger: - name: org.apache level: WARN AppenderRef: ref: Console - name: org.springframework.beans.factory level: WARN AppenderRef: ref: Console
该方式依赖Spring Boot的Log4j2集成模块,需确保项目已引入spring-boot-starter-log4j2依赖(若使用Web场景,可引入spring-boot-starter-web并排除默认的logback依赖)。
方法2:通过系统属性传递属性值
将Spring属性提前设置为JVM系统属性,让Log4j2直接解析占位符:
- 启动时通过JVM参数传递:
java -jar your-app.jar -Dspring.application.name=config-provider -Dspring.application.version=1.0.0
- 在application.properties中配置:
spring.application.name=config-provider spring.application.version=1.0.0 logging.log4j2.config=classpath:log4j2.yaml
此方法需确保属性在Log4j2配置加载前已被设置为系统属性,灵活性不如方法1。
方法3:自定义Log4j2属性源(进阶)
实现org.springframework.boot.logging.log4j2.Log4j2PropertySource接口,将Spring环境属性注入到Log4j2属性上下文,适合有特殊属性处理需求的场景。示例代码如下:
import org.springframework.boot.logging.log4j2.Log4j2PropertySource; import org.springframework.core.env.Environment; public class SpringEnvironmentLog4j2PropertySource extends Log4j2PropertySource { private final Environment environment; public SpringEnvironmentLog4j2PropertySource(Environment environment) { super("spring-environment"); this.environment = environment; } @Override public boolean containsProperty(String key) { return environment.containsProperty(key); } @Override public String getProperty(String key) { return environment.getProperty(key); } }
在Spring启动类中注册该属性源:
import org.apache.logging.log4j.core.config.Configurator; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.ConfigurableApplicationContext; @SpringBootApplication public class ConfigProviderApplication { public static void main(String[] args) { SpringApplication application = new SpringApplication(ConfigProviderApplication.class); application.addInitializers((ApplicationContextInitializer<ConfigurableApplicationContext>) context -> { Configurator.initialize(new SpringEnvironmentLog4j2PropertySource(context.getEnvironment())); }); application.run(args); } }
此方法复杂度较高,仅在其他方法无法满足需求时使用。
内容的提问来源于stack exchange,提问作者Paul Marcelin Bejan
相关产品推荐
相关产品推荐

