在 log4j2.xml 中使用 Spring Boot 应用程序属性

Posted

技术标签:

【中文标题】在 log4j2.xml 中使用 Spring Boot 应用程序属性【英文标题】:Use Spring boot application properties in log4j2.xml 【发布时间】:2018-08-03 02:40:38 【问题描述】:

我正在开发一个基于 Spring Boot 的 Web 应用程序,并希望使用 log4j2 作为记录器实现。log4j2-spring.xml 文件中定义的日志记录配置一切正常。

什么不起作用:我想在 log4j2-spring.xml 文件中使用属性占位符,该占位符应该从用于配置 Spring Boot 的 application.yml 文件中定义的属性中解析。

这可能吗?如果是,怎么做?

【问题讨论】:

【参考方案1】:

如果你使用 mvn,你可以使用 mvn 资源插件。这将使您在构建时间内实现目标。

链接:https://maven.apache.org/plugins/maven-resources-plugin/examples/filter.html

【讨论】:

【参考方案2】:

我在将 Spring Boot YAML 属性注入 log4j xml 配置时遇到了类似的问题,我找到了 Spring Boot 1.5.X(可能是 2.0,我没有测试它)的解决方案,这有点 hacky 和对系统属性查找进行操作,但它确实有效。

假设您的应用程序中有配置文件“dev”和一些要注入的属性,那么您的 application-dev.yml 看起来像这样:

property:
    toInject: someValue

在你的 xml 配置 log4j2-spring-dev.xml 你把这样的东西:

<Properties>
    <property name="someProp">$sys:property.toInject</property>
</Properties>

现在您必须以某种方式将此弹簧属性转移到系统属性。您必须在准备好应用程序环境之后和日志系统初始化之前执行此操作。在 Spring Boot 中有一个监听器 LoggingApplicationListener,它初始化整个日志系统,它是由事件 ApplicationEnvironmentPreparedEvent 触发的,所以让我们创建一个比 LoggingApplicationListener 优先级更高的监听器:

public class LoggingListener implements ApplicationListener, Ordered 

@Override
public int getOrder() 
    return LoggingApplicationListener.DEFAULT_ORDER - 1;


@Override
public void onApplicationEvent(ApplicationEvent event) 
    if (event instanceof ApplicationEnvironmentPreparedEvent) 
        ConfigurableEnvironment environment = ((ApplicationEnvironmentPreparedEvent) event).getEnvironment();
        List<String> activeProfiles = Arrays.asList(environment.getActiveProfiles());
        if (!activeProfiles.contains("dev")) 
            return;
        

        String someProp = environment.getProperty("property.toInject")
        validateProperty(someProp);

        System.setProperty("property.toInject", someProp);
    

现在在你的应用中注册这个监听器:

public static void main(String[] args) 
    SpringApplication application = new SpringApplication(MyApplication.class);
    application.addListeners(new LoggingListener());
    application.run(args);

就是这样。您的 Spring Boot 属性应该在您的 log4j2 配置文件中“注入”。此解决方案适用于类路径属性和 --spring.config.location 属性。请注意,它不适用于 Spring Cloud Config 等外部配置系统。

希望对你有帮助

【讨论】:

这个解决方案听起来不错,但太复杂了。 这是唯一完全适合我的解决方案。我需要为我的应用程序提供一个配置文件。用户需要能够使用更高优先级的配置文件来覆盖打包的默认配置,这不适用于接受的答案。 你能帮我理解为什么如果你的应用使用 Cloud Config 就不能工作吗?【参考方案3】:

通过属性占位符直接替换log4j2-spring.xml 中的属性不可能,因为log4j2-spring.xml 超出了Spring 的范围,纯粹用于配置目的。

但是,您可以利用Log4j2 开箱即用的属性替换功能,如here 所述。

第 1 步 - 在log4j2-spring.xml 中指定属性名称及其变量,如下所示

<Configuration status="warn">
    <Properties>
        <Property name="someProp">$bundle:test:someKey</Property>
    </Properties> 
    <!--other configs -->
</Configuration>

第 2 步 - 在日志配置中使用上述定义的属性例如日志文件名的后缀

<Appenders>
    <File name="file" fileName="/path/to/logs/app-$someProp.log">
        <PatternLayout pattern="%dyyyy-MM-dd HH:mm:ss.SSS %-5p %-40c1. - %m%n"/>
    </File>
</Appenders>

第 3 步 - 创建一个bundle即属性文件)来保存属性值例如test.properties

# properties for log4j2
someKey=someValue
someKey1=someValue1

在您的情况下,此文件将包含 yaml 中的值,您希望在 log4j2 配置中使用这些值。如果这些属性也在应用程序中使用,它们将在 yaml 和捆绑包(即属性文件)中复制,这应该是可以接受的妥协,因为 spring 无法在 log4j2 配置中注入它们。

如果需要更多信息,请在 cmets 中告知。

【讨论】:

对于同样的场景,但是属性文件通过命令行传递的方法是什么,即--spring.config.additional-location,我们将如何在这种场景中获取文件名 @user2846382 在这种情况下,外部属性文件应包含应用程序所需的属性以及 log4j。确保属性文件名与包名匹配,例如$bundle:test:someKey 这里 test 是包名称,也应该是属性文件名。虽然我还没有验证它(使用相同的属性文件作为 log4j 的 bundle 和 spring 的外部配置)但看不出它不应该工作的原因。 如果我希望我的应用程序只有一个属性文件,即 application.properties,该怎么办?如果我只是将捆绑包命名为 application,那么这仅在 .jar 存档中包含 application.properties 时才有效。它不会使用来自更高优先级位置的属性(例如config/application.properties 您知道吗,如何为 log4j 版本 1 执行此操作?我的 xml 有 log4j doctype(xml 以 &lt;log4j:configuration.. 开头),&lt;springProperty.. 在那里不起作用。 据我调查,您不能在 log4j v1 中执行此操作。我决定使用 log4j2。

以上是关于在 log4j2.xml 中使用 Spring Boot 应用程序属性的主要内容,如果未能解决你的问题,请参考以下文章

log4j2.xml日志文件设置文件路径

spring-boot application.properties 文件可以与 log4j2.xml 配置一起工作吗?

如何在log4j2.xml中动态设置日志文件路径

Spring boot 配置 log4j2.xml

Spring Boot 2.x 实践记:log4j2.xml

Spring Boot 2.x 实践记:log4j2.xml