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 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.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.of(s -> s
.registryOf(r -> r.add(MDCInterceptor.instance()))
.handler(r ->
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");
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.
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. 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
ExecInterceptor.ExecType
Constructor and Description |
---|
MDCInterceptor() |
Modifier and Type | Method and Description |
---|---|
static MDCInterceptor |
instance() |
void |
intercept(Execution execution,
ExecInterceptor.ExecType type,
Block executionSegment)
Intercepts the execution of an execution segment.
|
public static MDCInterceptor instance()
public void intercept(Execution execution, ExecInterceptor.ExecType type, Block executionSegment) throws Exception
ExecInterceptor
The execution segment argument represents a unit of work of the execution.
Implementations MUST invoke execute()
on the given execution segment block.
intercept
in interface ExecInterceptor
execution
- the execution that this segment belongs totype
- indicates whether this segment is execution on a compute or blocking threadexecutionSegment
- the execution segment that is to be executedException
- any