Initializing Log4j MDC in Hippo CMS
In Log4j, Mapped Diagnostic Context (MDC) can be used to provide additional context information for every log message. In server applications it will usually be initialized for every request in a filter. In Hippo CMS, a custom valve must be injected into the request pipeline for that purpose.
The valve must extend the AbstractOrderableValve
abstract class and implement the invoke
method:
public class DiagnosisContextInitValve extends AbstractOrderableValve {
@Override
public void invoke(ValveContext valveContext) throws ContainerException {
try {
MDC.put("hostname", getHostName(valveContext));
} finally {
valveContext.invokeNext();
}
}
}
In the above sample, we want to put the hostname value into the context. We can obtain this information using the code from the official documentation:
private String getHostName(ValveContext valveContext) {
final HstRequestContext requestContext = valveContext.getRequestContext();
if (requestContext != null) {
final HstContainerURL baseURL = requestContext.getBaseURL();
if (baseURL != null) {
return baseURL.getHostName();
}
}
return "";
}
The valve can be injected into the pipeline with a Spring bean configuration XML file that needs to be placed in site/src/main/resources/META-INF/hst-assembly/overrides
:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="diagnosisContextInitValve"
class="com.damirscorner.blog.samples.valves.DiagnosisContextInitValve">
<property name="valveName" value="diagnosisContextInitValve" />
<property name="afterValves" value="securityValve"/>
<property name="beforeValves" value="pageCachingValve"/>
</bean>
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject">
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject" ref="org.hippoecm.hst.core.container.Pipelines" />
<property name="targetMethod" value="getPipeline"/>
<property name="arguments">
<value>DefaultSitePipeline</value>
</property>
</bean>
</property>
<property name="targetMethod" value="addProcessingValve"/>
<property name="arguments">
<ref bean="diagnosisContextInitValve" />
</property>
</bean>
</beans>
The MDC information is stored per thread. Since we don't know how threads are reused in Hippo CMS, it's a good practice to clean up the context when a request is completed. We need another custom valve to do that:
public class DiagnosisContextCleanupValve extends AbstractOrderableValve {
@Override
public void invoke(ValveContext valveContext) throws ContainerException {
try {
MDC.clear();
} finally {
valveContext.invokeNext();
}
}
}
This one must be placed into the cleanup valves collection instead of into the processing valves collection:
<bean id="diagnosisContextCleanupValve"
class="com.damirscorner.blog.samples.valves.DiagnosisContextCleanupValve">
<property name="valveName" value="diagnosisContextCleanupValve" />
<property name="afterValves" value="diagnosticReportingValve"/>
<property name="beforeValves" value=""/>
</bean>
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject">
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject" ref="org.hippoecm.hst.core.container.Pipelines" />
<property name="targetMethod" value="getPipeline"/>
<property name="arguments">
<value>DefaultSitePipeline</value>
</property>
</bean>
</property>
<property name="targetMethod" value="addCleanupValve"/>
<property name="arguments">
<ref bean="diagnosisContextCleanupValve" />
</property>
</bean>
To include a value from MDC into the log, the %X{key}
pattern can be used in a pattern layout. To test the valves, you can replace the existing PatternLayout
elements in conf/log4j2-dev.xml
and conf/log4j2-dist.xml
with the following one:
<PatternLayout pattern="%d{dd.MM.yyyy HH:mm:ss} %X{hostname} %-5p %t [%C{1}.%M:%L] %m%n"/>
If you've configured Hippo CMS to use JSON layout instead, all the values from MDC will automatically be included in contextMap
.