Class MDCInterceptor
- java.lang.Object
-
- ratpack.core.logging.MDCInterceptor
-
- All Implemented Interfaces:
ExecInterceptor
public final class MDCInterceptor extends Object implements ExecInterceptor
An execution interceptor that adds support for SLF4J's Mapped Diagnostic Context (MDC) feature.The MDC is a set of key-value pairs (i.e. map) that can be implicitly added to all logging statements within the context. The term “context” here comes from SLF4J's lexicon and does not refer to Ratpack's
Context
. It refers to a logical sequence of execution (e.g. handling of a request). SLF4J's default strategy for MDC is based on a thread-per-request model, which doesn't work for Ratpack applications. This interceptor maps SLF4J's notion of a “context” to Ratpack's notion of an“execution”
. This means that after installing this interceptor, theMDC API
can be used naturally.Please be sure to read the SLF4J manual section on MDC, particularly about how the actual logging implementation being used must support MDC. If your logging implementation doesn't support MDC (e.g.
slf4j-simple
) then all of the methods on theMDC
API become no-ops.The interceptor should be added to the server registry, so that it automatically is applied to all executions. The following example shows the registration of the interceptor and MDC API usage.
import java.util.List; import java.util.ArrayList; import ratpack.test.embed.EmbeddedApp; import ratpack.exec.Blocking; import org.slf4j.MDC; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import static org.junit.jupiter.api.Assertions.assertEquals; import ratpack.core.logging.MDCInterceptor; public class Example { private static final Logger LOGGER = LoggerFactory.getLogger(Example.class); public static void main(String... args) throws Exception { EmbeddedApp.of(s -> s .registryOf(r -> r.add(MDCInterceptor.instance())) .handler(r -> ctx -> { // Put a value into the MDC MDC.put("clientIp", ctx.getRequest().getRemoteAddress().getHost()); // The logging implementation/configuration may inject values from the MDC into log statements LOGGER.info("about to block"); Blocking.get(() -> { // The MDC is carried across asynchronous boundaries by the interceptor LOGGER.info("blocking"); return "something"; }).then(str -> { // And back again LOGGER.info("back from blocking"); ctx.render("ok"); }); } ) ).test(httpClient -> assertEquals("ok", httpClient.getText()) ); } }
Given the code above, using the Log4j bindings with configuration such as:
<Console name="Console" target="SYSTEM_OUT"> <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg - [%X{clientIp}] %n"/> </Console>
The client IP address will be appended to all log messages made while processing requests.
Inheritance
The MDC is not inherited by forked executions (e.g.
Execution.fork()
). If you wish context to be inherited, you must do so explicitly by capturing the variables you wish to be inherited (i.e. viaMDC.get(String)
) as local variables and then add them to the MDC (i.e. viaMDC.put(String, String)
) in the forked execution.- See Also:
ExecInterceptor
-
-
Nested Class Summary
-
Nested classes/interfaces inherited from interface ratpack.exec.ExecInterceptor
ExecInterceptor.ExecType
-
-
Method Summary
All Methods Static Methods Instance Methods Concrete Methods Modifier and Type Method Description static MDCInterceptor
instance()
Creates an interceptor with no initialisation action.void
intercept(Execution execution, ExecInterceptor.ExecType execType, Block executionSegment)
Intercepts the execution of an execution segment.static MDCInterceptor
withInit(Action<? super Execution> init)
Creates an interceptor with the given initialisation action.
-
-
-
Method Detail
-
instance
public static MDCInterceptor instance()
Creates an interceptor with no initialisation action.- Returns:
- an interceptor with no initialisation action.
- See Also:
withInit(Action)
-
withInit
public static MDCInterceptor withInit(Action<? super Execution> init)
Creates an interceptor with the given initialisation action.The given action will be executed before the first segment of each execution, allowing the MDC to be primed with initial values.
The following demonstrates priming the MDC with the
RequestId
.import org.slf4j.MDC; import ratpack.core.handling.RequestId; import ratpack.core.logging.MDCInterceptor; import ratpack.test.embed.EmbeddedApp; import static org.junit.jupiter.api.Assertions.assertEquals; public class Example { public static void main(String... args) throws Exception { EmbeddedApp.of(s -> s .registryOf(r -> r .add(MDCInterceptor.withInit(e -> e.maybeGet(RequestId.class).ifPresent(requestId -> MDC.put("requestId", requestId.toString()) ) )) ) .handlers(c -> c .get(ctx -> ctx.render(MDC.get("requestId"))) ) ).test(http -> // The default request ID generator generates UUIDs (i.e. 36 chars long) assertEquals(http.getText().length(), 36) ); } }
If no initialisation is required, use
instance()
.- Parameters:
init
- the initialisation action- Returns:
- an
MDCInterceptor
- Since:
- 1.1
-
intercept
public void intercept(Execution execution, ExecInterceptor.ExecType execType, Block executionSegment) throws Exception
Description copied from interface:ExecInterceptor
Intercepts the execution of an execution segment.The execution segment argument represents a unit of work of the execution.
Implementations MUST invoke
execute()
on the given execution segment block.- Specified by:
intercept
in interfaceExecInterceptor
- Parameters:
execution
- the execution that this segment belongs toexecType
- indicates whether this segment is execution on a compute or blocking threadexecutionSegment
- the execution segment that is to be executed- Throws:
Exception
- any
-
-