rekowski.info David Rekowski's random stuff

Message context with MDC for singular log statements

2018-03-14

Abstract

Imagine you want to add logging context only for a single logging call. I show a way to do this using MDC with a single tweet (140 chars) worth of code.

Keywords

java, slf4j, logging, MDC, context

Example

If you ever wanted to append an arbitrary set of data to a log message without a litany of x={} portions in the log msg, you may want to give this approach a try:

log(
    () -> log.debug("processing this set of data"),
    new JSONObject().put("id", 123).put("name", "myname"));

And get an output like this:

[main] DEBUG i.r.c.LogTest - processing this set of data - context={"name":"myname","id":123}

It's not particularly short, but chances are you already have a structured object to pass to the logger and you will have structured logging data which depending on the logging pipeline can help you down the line, especially if you're using something like Kibana or Stackify's Retrace.

Note: your logging configuration needs to be modified to support the context identifier (%X{lctx}), e.g.

<pattern> ... context=%X{lctx}%n</pattern>

and if you want to call log() statically, you'll have to import it:

import static info.rekowski.ctxlogger.CtxLogger.log;

You can put anything as context, which yields a useful .toString() result, so a Map may be a common use case, though a JSONObject is briefer in construction. A List.of is also quick to write.

Implementation

This is 144 characters, without spaces it's 129.

class CtxLogger {
  public static void log(Runnable r, Object c) {
    MDC.put("lctx", c.toString());
    r.run();
    MDC.remove("lctx");
  }
}

Explanation

What this class does is basically misuse the MDC in order to add context only for a single log call. You pass the log call as a Runnable lambda and any object as the logging context. It will be converted to String using toString().

  1. the context is added to MDC
  2. the log call is executed
  3. the context is removed from MDC

Caveats

I'm quite sure MDC was never intended to be used like this, but in this case, if it works, it's not wrong, I guess.

The lctx which unsurprisingly stands for logging context, is fixed, but the code is so minimal, it's not worth bothering with a 3rd party lib for it, unless it has extended features such as configuring said context identifier, switching support on and off or convenience methods for data structure creation. As it is, you would just copy&paste it to a class in your project and can name the identifier however you want.