How to dynamically load the log file path in [Spring] log4j2.xml

Keywords: Spring xml log4j2

Note: it is only for Spring projects, not for Spring boot and Spring cloud

Project scenario:

The project framework is Spring + spring MVC + mybatis. When the project starts, different configuration files are loaded, and then the effect of different environments is achieved. It can be understood that there are four configuration files a.properties, b.properties, c.properties and d.properties in the project, and then there is a configuration file called boot.properties. Deployment is like this. When you want to deploy the environment, you deploy the a environment. Then the boot.properties in the war package must be agreed to load the a.properties. The same is true for other environments. That is to say as like as two peas, there are four settings, but their boot.properties is different, others are identical code. The current problem is that the log file of log4j2.xml file is written dead, for example, / data/log /. If both a and B environments are arranged on the same machine, their logs will be overwritten. The solution is [log4f2.xml in each war package is modified separately] to optimize this problem.

Problem Description:

There are a.war, b.war, c.war and d.war. boot.properties in the four war packages are loaded with their respective configuration and pricing. boot.properties in a.war only loads a.properties. Meanwhile, the log file path in log4f.xml in a.war is modified to / data/a/log. b.war,c.war,d.war.
Optimize the above problems

a.war in boot.properties Configuration of
a.properties
#b.properties
#c.properties
#d.properties
//
log4j2.xml Log configuration
<>/data/a/log</>
b.war in boot.properties Configuration of
#a.properties
b.properties
#c.properties
#d.properties
//
log4j2.xml Log configuration
/data/b/log

Cause analysis:

The main point of optimization is to write the log configuration of log4f2.xml in their respective configuration files. For example, I configure the following in a.properties in a environment

a.properties Configuration in
log-uri=/data/a/log
b.properties Configuration in
log-uri=/data/b/log

In this way, I only need to get the log address and deploy a and B to the same machine in time, and the logs will not conflict.
What we need to do now is that we have written the configuration, so how to get your own configuration from log4j2.xml is the focus of this article
Usually, the log is loaded before the Spring container is loaded, because I need to write the log in the file after the log is loaded, so I can't put the log loading at the end, so the startup logs can't be recorded, so it doesn't make any sense.
Therefore, it can't be handled by Spring for the time being (ps: if you can, discuss it in the comment area)

Solution:

Solution I

Using jvm parameters, this is relatively simple.
a.war, b.war, c.war and d.war add custom jvm parameters when their services are started, such as
a.war -Dlog-uri=/data/a/log
b.war -Dlog-uri=/data/b/log
c.war -Dlog-uri=/data/c/log
d.war -Dlog-uri=/data/d/log
Then, the log4j2.xml log file address can only be configured in this way

log4j2.xml Configuration of log files in
<configuration status="info">
	<{Properties>
		<properties name="logBaseFolder">${sys:log-uri}</properties>
		...

Solution II

The same is true for jvm parameters, but you don't need to specify jvm parameters at startup
tomcat listener ServletContextListener

This listener helps us solve this problem. You can have a look at this knowledge point.
What can he do for us
1. To read the configuration file to be loaded in the current environment is to check the configuration file to be loaded in the current environment in boot.properties, load the configuration in the configuration file into Property, and write a tool class to facilitate reading the configuration in the future.
2. Read the specified attribute name log URI and load the log URI into the system environment variable.

See the following steps for details:

Step 1: configure the configuration file

a.war log-uri=/data/a/log
b.war log-uri=/data/b/log
c.war log-uri=/data/c/log
d.war log-uri=/data/d/log

Step 2: configure log files
All war packages are configured like this

log4j2.xml Configuration of log files in
<configuration status="info">
	<{Properties>
		<properties name="logBaseFolder">${sys:log-uri}</properties>
		...

Step 3: customize ServletContextListener
Implement this interface, rewrite
Step 4: configure the listener in the configuration file of web.zml. It must be configured before the configuration of the log file, otherwise it is meaningless.
(1) Load profile
(2) The corresponding attribute is added to the system environment variable

package com.anik.csdnblog;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

public class LogContextListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        //Load the properties of the configuration file

        //Load the log file attribute into the system environment variable. configUtil needs to write it by itself. The main logic is to read the stream file and set it into the Property
        System.setProperty("log-uri",configUtil.getProperties("log-uri","/data/log/"));
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
    }
}
//The following configuration must be configured before the log file
<listener>
	<listener-class>com.csdnblog.anik.listener.LogContextListener</listener-class>
</listener>

I have thought about this way before. I directly write a static code block. The main logic is to load the configuration file into the tool class and load the log configuration into the system environment variable. Logically, this logic is no problem, because the class loading must be before web.xml, so when loading the address of the web.xml loading log, it can be read, Actually not. This must refer to the knowledge points of class loading and class loading timing.
In fact, the static code block is not executed when the class is loaded. This part of the static code block will be executed only when the class is called for the first time, and only once. When service starts, there is no place to call this class.
(ps: if you have any questions, welcome to the comment area for discussion)

Then, the static code block is responsible for loading the configuration file into the tool class, and the tomcat listener is responsible for loading the specified attributes into the system environment variables (the configuration must be read through the tool class, so the static code block will be executed).

Posted by phencesgirl on Thu, 07 Oct 2021 18:13:51 -0700