public class MDCInterceptor extends Object implements ExecInterceptor
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, the MDC 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 the MDC
API become no-ops.
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 org.slf4j.MDC;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.junit.Assert.assertEquals;
import ratpack.logging.MDCInterceptor;
public class Example {
private static final Logger LOGGER = LoggerFactory.getLogger(Example.class);
public static void main(String... args) throws Exception {
EmbeddedApp.fromHandlers(chain -> chain
.handler(ctx -> ctx.addInterceptor(MDCInterceptor.instance(), ctx::next))
.handler(ctx -> {
// Put a value into the MDC
MDC.put("clientIp", ctx.getRequest().getRemoteAddress().getHostText());
// The logging implementation/configuration may inject values from the MDC into log statements
LOGGER.info("about to block");
ctx.blocking(() -> {
// 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.
The MDC is not inherited by forked executions (e.g. Context.exec()
).
If you wish context to be inherited, you must do so explicitly by capturing the variables you wish to be inherited
(i.e. via MDC.get(String)
) as local variables and then add them to the MDC (i.e. via MDC.put(String, String)
) in the forked execution.
ExecInterceptor.ExecType
Constructor and Description |
---|
MDCInterceptor() |
Modifier and Type | Method and Description |
---|---|
static MDCInterceptor |
instance() |
void |
intercept(Execution execution,
ExecInterceptor.ExecType type,
NoArgAction continuation)
Intercepts the “rest” of the execution on the current thread.
|
public static MDCInterceptor instance()
public void intercept(Execution execution, ExecInterceptor.ExecType type, NoArgAction continuation) throws Exception
ExecInterceptor
The given action argument represents the rest of the execution to occur on this thread. This does not necessarily mean the rest of the execution until the work (e.g. responding to a request) is complete. Execution may involve multiple parallel (but not concurrent) threads of execution because of blocking IO or asynchronous APIs.
intercept
in interface ExecInterceptor
execution
- the execution who's segment is being interceptedtype
- indicates whether this is a compute (e.g. request handling) segment or blocking segmentcontinuation
- the “rest” of the executionException
- any