Gauthier POGAM--LE MONTAGNER
Posted on September 17, 2022
In my previous blog post How to output Log4J2 logs as JSON?, I mentioned that we can extract some properties from our project' pom.xml
to add them to our logs. This can be done using Maven' maven-resource-plugin
and its filtering capabilities.
I will explain what is resource filtering, how it can be configured, and how to apply this to extract properties from a pom.xml
to inject them into a log4j2.xml
configuration.
What is resource filtering?
Before compiling the source or your tests, the resources of your application are copied from your project' resource folder (src/main/resources
) to Maven' target folder. Maven automatically copies these resources using maven-resource-plugin
, one of Maven' standard plugins, called by most goals that require it (such as compile
or test-compile
).
These resources are any file that your application will use but that should not be compiled, such as properties files, assets, TLS certificates,... Most applications have at least one resource file: a logger configuration file like Log4J2' log4j2.xml file.
When building your project, you might want these files to contain some of the many properties defined in your pom.xml, such as your application' name, version, or one of the properties defined in your pom.xml. By default, however, Maven won't apply any change to your resources. You will have to configure it to injects the properties you want in your resources files. Injecting properties in your resources is what we call filtering the resources of your project.
Enabling resource filtering
To filter your resources, you must tell Maven which files it should filter, and in these files you should define the variables Maven should replace.
Enabling filtering in Maven is pretty simple: you just have to enable it in the configuration of your application resources. This can be done by editing the resource
tag in the build section of your application to add the property <filtering>true</filtering>
to your resources configuration:
<project>
...
<build>
...
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
...
</resources>
...
</build>
...
</project>
Once enabled, every resources in src/main/resources
will be filtered. Therefore, we can now use filtering to replace variables in our resource files. Let's create a file test.txt
and add the following content in it:
Hello, I am project ${project.name}, version ${project.version}. You can identify me by my full name ${project.groupId}:${project.artifactId}.
You can now run the command mvn compile
and open target/classes/test.txt
. You will find the following content:
Hello, I am project my-test-app, version 1.0.0-SNAPSHOT. You can identify me by my full name com.company.dev:my-test-app.
Filter specific files
By default, filtering applies to every files in your resources that are not binary files (like images). To avoid replacing content that use the same syntax as a property but which should not be replaced, it is recommended to manually select which files are filtered and exclude everything else.
Fortunately, Maven makes it very simple to configure which resources should be filtered by using the <includes>
tag:
<project>
...
<build>
...
<resources>
<!-- Enable filtering for specific resources -->
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<includes>
<include>test.txt</include>
</includes>
</resource>
<!-- Disable filtering for everything else.
This part is necessary so Maven continues to copy non-filtered resources.
-->
<resource>
<directory>src/main/resources</directory>
<filtering>false</filtering>
<excludes>
<exclude>test.txt</exclude>
</excludes>
</resource>
...
</resources>
...
</build>
...
</project>
You will note that we added the <resource>
tag twice: one with filtering enabled, and a second time without it. If we only added the first tag with the <includes>
configured, Maven would only copy (and filter) the resources included in this configuration (in our case, only log4j2.xml
) and would ignore every other file.
To ensure all resources of our application are copied including non-filtered ones, we must add a second <resource>
tag with filtering disabled and which excludes the filtered files. The list of files to exclude in the second <resource>
tag is the same as the included files in the first resource configuration.
With the configuration above, only our test.txt
file is filtered and any other file in our resource folder (or its sub-folders) will not be filtered, allowing us to use ${project.name}
without worrying that it could be replaced by Maven.
Configuring resources filtering for Log4j2
Applying resource filtering to automatically inject some information now becomes very easy. The first step is to take the code above and enable filtering for log4j2.xml:
<project>
...
<build>
...
<resources>
<!-- Enable filtering for specific resources -->
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<includes>
<include>log4j2.xml</include>
</includes>
</resource>
<!-- Disable filtering for everything else.
This part is necessary so Maven continues to copy non-filtered resources.
-->
<resource>
<directory>src/main/resources</directory>
<filtering>false</filtering>
<excludes>
<exclude>log4j2.xml</exclude>
</excludes>
</resource>
...
</resources>
...
</build>
...
</project>
With our Log4J2 configuration file now filtered, we can easily use the properties defined in our pom.xml
to enrich our logs. Let's configure a RollingFile appender that output JSON logs (more info in my post How to output Log4J2 logs as JSON?):
...
<RollingFile name="json-file" fileName="${sys:mule.home}${sys:file.separator}logs${sys:file.separator}${project.artifactId}.log.json"
filePattern="${sys:mule.home}${sys:file.separator}logs${sys:file.separator}${project.artifactId}-%i.log.json">
<JSONLayout compact="true" eventEol="true" properties="true" stacktraceAsString="true" includeTimeMillis="true">
<!-- Project properties -->
<KeyValuePair key="appName" value="${project.name}" />
<KeyValuePair key="version" value="${project.version}" />
</JSONLayout>
</RollingFile>
...
Our logs will now come with two additional properties: the application name and its version. You can now leverage these additional information, or any other properties extracted from your pom.xml
, to enrich your logs and improve their filtering in a tool like Datadog or Splunk.
Conclusion
Resource filtering is a very useful feature of Maven's maven-resource-plugin
plugin, allowing you to inject properties from your pom.xml
into your resources. With a simple configuration of that plugin, references to properties are injected into your resources when your application is built.
Posted on September 17, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.