Quantcast
Channel: Damir's Corner
Viewing all articles
Browse latest Browse all 485

Initializing Log4j MDC in Hippo CMS

$
0
0

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.


Viewing all articles
Browse latest Browse all 485

Trending Articles