Use Spring boot application properties in log4j2.xml
Asked Answered
T

3

18

I am working on a web application based on spring boot and want to use log4j2 as the logger implementation.
Everything works fine with the logging configuration defined in a log4j2-spring.xml file.

What is not working: I want to use property placeholders in the log4j2-spring.xml file that should be resolved from properties defined in the application.yml file used for configuring spring boot.

Is this possible? If yes, how?

Triley answered 23/2, 2018 at 4:25 Comment(0)
H
19

Direct substitution of properties in log4j2-spring.xml via property placeholder is not possible as the log4j2-spring.xml is outside the ambit of Spring, and used purely for configuration purpose.

However, you can leverage the Log4j2 out-of-box feature of property substitution as outlined here.

Step 1 - Specify the property name and its variable in log4j2-spring.xml as below

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

Step 2 - Use the above defined property in the log configuration e.g. suffix to log file name

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

Step 3 - Create a bundle (viz. properties file) to hold the properties value e.g. test.properties

# properties for log4j2
someKey=someValue
someKey1=someValue1

In your case this file will contain the values in yaml which you seek to use in log4j2 configuration. In case those properties are used in application as well, they will be duplicated in yaml and the bundle (i.e. properties file) which should be acceptable compromise given spring can not inject them in log4j2 configuration.

Let know in comments in case of any more information is required.

Hypersensitize answered 23/4, 2018 at 13:46 Comment(7)
what will be the approach for this same scenario but with property file is passed through command line i.e. --spring.config.additional-location, how we would get the filename in this scenarioPresentationism
@Presentationism In this case the external property file should contain the properties required by your application as well as the log4j. Make sure the property file name matches the bundle name e.g. ${bundle:test:someKey} here test is the bundle name which should also be the property file name. Although I haven't verified it (using same property file for as bundle for log4j & external config for spring) but see no reason why it shouldn't work.Hypersensitize
What if I want to have only a single properties file fro my application i.e. application.properties? If I just name the bundle application, this only works if the application.properties is included in the .jar archive. It will not use the properties from the higher precedence locations (.e.g config/application.properties)Unitive
Do you please know, how to do this for log4j version 1? My xml has log4j doctype (xml starts with <log4j:configuration..) and the <springProperty.. does not work there.Birdbath
@Birdbath Consider posting a new question (optionally linking it with this one) with all the details so that we can assist you better :)Hypersensitize
As far as I've investigated, you cannot do this in log4j v1. I've decided to use log4j2.Birdbath
I'm unable to access from application.yaml file. Any idea how to do it with yamlAchorn
A
6

I've faced similiar problem with injecting Spring Boot YAML properties into log4j xml configuration, and I found a solution for Spring Boot 1.5.X (and probably 2.0, I didn't test it) which is a little bit hacky and operates on system properties lookup but it certainly works.

Let say you have profile "dev" in your application and some property to inject, then your application-dev.yml looks like this:

property:
    toInject: someValue

In your xml configuration log4j2-spring-dev.xml you put something like this:

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

Now you have to somehow transfer this spring property to system property. You have to do that after application environment will be prepared and before logging system will initialize. In Spring Boot there is a listener LoggingApplicationListener, which initialize whole logging system and it's triggered by event ApplicationEnvironmentPreparedEvent, so let's create listener with order with higher precedence than 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);
    }
}

Now register this listener in your application:

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

And that's it. Your Spring Boot properties should be "injected" in your log4j2 configuration file. This solution works with classpath properties and --spring.config.location properties. Note, it would not work with with some external configuration system like Spring Cloud Config.

Hope it helps

Amphioxus answered 27/9, 2018 at 13:16 Comment(3)
this solution sounds OK but too complicated.Anabelle
This is the only solution that worked for me fully. I needed to have a single configuration file for my application. Users need to be able to use higher precedence configuration files to override the packaged default configuration, which did not work with the accepted answer.Unitive
Could you help me understand why it wouldn't work if you app uses Cloud Config?Misunderstanding
B
0

If you use mvn, you could use the mvn resource plugin. This will let you achieve your goal in build time.

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

Bookmobile answered 20/7, 2021 at 19:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.