Using JSONLayout for Log4j with Hippo CMS
If you want to post-process the application logs, using the JSON format will usually make it easier. In Log4j, there's the JSON Layout available for that purpose. Making it work in Hippo CMS took me longer than expected.
Hippo CMS is configured to use Log4j by default. You can find the configuration in the conf/log4j2-dev.xml
and conf/log4j2-dist.xml
files for the development and distribution builds respectively. To use the JSON format instead of plain text you should replace both PatternLayout
elements in each file with JSONLayout
elements:
<JSONLayout complete="false" compact="true" eventEol="true"
charset="UTF-8" properties="true"/>
Since JSON Layout has additional runtime dependencies on Jackson databind, this change will break Hippo CMS. The application will fail to start. You'll find the error logged in target/tomcat8x/logs/localhost.{date}.log
:
org.apache.catalina.core.StandardContext.filterStart Exception starting filter [CMS]
java.lang.NoClassDefFoundError: com/fasterxml/jackson/databind/ser/impl/SimpleFilterProvider
at org.apache.logging.log4j.core.layout.JacksonFactory.newWriter(JacksonFactory.java:212)
To resolve the issue, the dependencies need to be deployed to Tomcat's shared/lib
folder.
During development, Cargo Maven plugin is used for deployment. Therefore, the dependencies must be specified in its configuration:
<plugin>
<groupId>org.codehaus.cargo</groupId>
<artifactId>cargo-maven2-plugin</artifactId>
<configuration>
<!-- ... -->
<container>
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<classpath>shared</classpath>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<classpath>shared</classpath>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<classpath>shared</classpath>
</dependency>
</dependencies>
<!-- ... -->
</container>
</configuration>
</plugin>
Transitive dependencies are listed explicitly because the plugin does not include them automatically (jackson-annotations
and jackson-core
).
Of course, for this to work, the dependency must also be added at the project level:
<dependencyManagement>
<dependencies>
<!-- ... -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
And also at the profile level:
<profile>
<id>cargo.run</id>
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
</dependencies>
<!-- ... -->
</profile>
With all that in place, the application will start again. However, you might encounter Jackson version conflicts at run time if you specify a version different from the one that's already distributed with Hippo CMS. Check the version in target/tomcat8x/webapps/site.war/WEB-INF/lib
and set jackson.version
accordingly:
<jackson.version>2.8.8</jackson.version>
There are some additional changes required to also make everything work in distribution builds. First, the dependency needs to be added to both distribution build profiles (dist
and dist-with-development-data
):
<profile>
<id>dist</id>
<dependencies>
<!-- ... -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<!-- ... -->
</profile>
<profile>
<id>dist-with-development-data</id>
<dependencies>
<!-- ... -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<!-- ... -->
</profile>
The Maven Assembly Plugin is used to create the distribution archive. For the shared/lib
folder, the component is already configured in src/main/assembly/shared-lib-component.xml
. The three dependencies (direct and transitive) must be added to the existing dependencySet
:
<dependencySet>
<useProjectArtifact>false</useProjectArtifact>
<outputDirectory>shared/lib</outputDirectory>
<scope>provided</scope>
<includes>
<!-- ... -->
<include>com.fasterxml.jackson.core:jackson-annotations</include>
<include>com.fasterxml.jackson.core:jackson-core</include>
<include>com.fasterxml.jackson.core:jackson-databind</include>
</includes>
</dependencySet>
You can validate the configuration by checking the generated .tar.gz
distribution archive in the target
folder. The shared/lib
folder inside it must contain the required dependencies.