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
.