Spring: Logback access logs with custom JSON format and (a workaround to read) MDC

Elvis Ciotti
2 min readJan 6, 2023

--

In case you need to send the Spring access log to ELK or Datadog, you’ll probably use the LogstashAccessEncoder . Below the configuration to load it into Spring.

@Slf4j
@Configuration
public class Web {
@Bean
public WebServerFactoryCustomizer<TomcatServletWebServerFactory> accessLogsCustomizer() {
return factory -> {
var logbackValve = new LogbackValve();
logbackValve.setFilename("logback-access.xml");
logbackValve.setAsyncSupported(true);
factory.addContextValves(logbackValve);
};
}
}
<!-- logback-access.xml --> 
<configuration>
<appender name="ACCESS_STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="net.logstash.logback.encoder.LogstashAccessEncoder">
... config ...
</encoder>
</appender>
<appender-ref ref="ACCESS_STDOUT"/>
</configuration>

If more customization is what you need, then you can use the AccessEventCompositeJsonEncoder, where you specify the JSON template under <pattern> using the conversion specifiers for the logback layout.

<configuration>
<appender name="ACCESS_STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="net.logstash.logback.encoder.AccessEventCompositeJsonEncoder">
<providers>
<timestamp />
<pattern>
<pattern>
{
"yourField": {
"yourNestedField": "%reqAttribute{abc}"
},
"http": {
"path" : "%requestURI"
}
"@version" : "2023-01-03",
"@type" : "access"
}
</pattern>
</pattern>
</providers>
</encoder>
</appender>
<appender-ref ref="ACCESS_STDOUT"/>
</configuration>

Note that MDC is not unfortunately supported in the current version, but one way to solve is creating a filter that sets the data as a request attribute in addition to MDC. See the example below the adds the “abc” request attribute that is read by the config above.


import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Component
public class MDCXRequestIdLoggingFilter implements Filter {
public void doFilter(ServletRequest request,
ServletResponse response,
FilterChain filterChain) throws IOException, ServletException {
if (request instanceof HttpServletRequest httpServletRequest
&& response instanceof HttpServletResponse httpServletResponse) {

var abcValue = "SET ME";

MDC.put("abc", abcValue);
httpServletRequest.setAttribute("abc", abcValue);
}

filterChain.doFilter(request, response);
}
}

If the code sets “first value” and then “second value”, that will be the output in the access logs

{
"yourField": {
"yourNestedField": "first value"
},
"http": {
"path" : "/"
},
"@version" : "2023-01-03",
"@type" : "access"
}
{
"yourField": {
"yourNestedField": "second value"
},
"http": {
"path" : "/"
},
"@version" : "2023-01-03",
"@type" : "access"
}

Clap if useful, follow me for more

--

--

Elvis Ciotti
Elvis Ciotti

Written by Elvis Ciotti

Software Contractor — Java, Spring, k8s, AWS, Javascript @ London - hire me at https://elvisciotti.github.io/

No responses yet