Add context key to a logger

Hello,
I’m looking to add context key to my logger for some modules…

This is done using MDC.

I use the AbstractTagDriver.
I would like to add some information in order to filter logs by device.

There is a MDC key device-name reported by the gateway logger.
is it enough if I add :

MDC.put("device-name", "MyDevice");

in the constructor of the AbstractTagDriver.

Or I need to add this at the beginning of each method ?
the doc mention that the MDC manages contextual information on a per thread basis .

You have to add and remove this key every time you log something. It’s important to remove it before leaving the context of whatever method(s) you’re in or else the next loggers that happen to run on the current thread will have the key set still.

You can use LoggerEx to build a logger that always sets/unsets the key on every logging statement:

logger = LoggerEx.newBuilder()
    .mdcContext("device-name", driverContext.getDeviceName())
    .build(String.format("drivers.%s", getClass().getSimpleName()));

There’s a bit of a performance penalty for the convenience, but we tend to use it anyway…

:+1: I will try to replace

   import org.slf4j.Logger;

   logger = LoggerFactory.getLogger(getClass());
   import org.slf4j.Logger;
   import com.inductiveautomation.ignition.common.util.LoggerEx;

   logger = LoggerEx.newBuilder()
           .mdcContext("device-name", driverContext.getDeviceName())
           .build(getClass()).getLoggerSLF4J(); 

You need to define your logger field as a LoggerEx rather than assigning the value of getLoggerSLF4J() or the MDC keys won’t be set. That method just returns the underlying SLF4J Logger instance.

All my log use

logger.debug("blahblah={}",myVariable);

I have to replace it as follow ? debug => debugf

logger.debugf("blahblah={}",myVariable);

errorf() don’t work with markers like {} :cry:

@Kevin.Herron, do you see any risk to replace :

import org.slf4j.Logger;

logger = LoggerFactory.getLogger(getClass());
logger.debug("blahblah={}",myVariable);

with :

import com.inductiveautomation.ignition.common.util.LoggerEx;
import org.slf4j.helpers.MessageFormatter

logger = LoggerEx.newBuilder()
           .mdcContext("device-name", driverContext.getDeviceName())
           .build(getClass()).getLoggerSLF4J(); 

logger.debug(MessageFormatter.format("blahblah={}",myVariable));

Using the SLF4J Logger will not get you automatic MDC support. That only happens with LoggerEx. (your example still uses getLoggerSLF4J())

You’ll have to manually set/unset the context key if you want to continue using the SLF4J Logger.

oops,
I was thinking at :

import com.inductiveautomation.ignition.common.util.LoggerEx;
import org.slf4j.helpers.MessageFormatter

logger = LoggerEx.newBuilder()
           .mdcContext("device-name", driverContext.getDeviceName())
           .build(getClass()); 

logger.debug(MessageFormatter.format("blahblah={}",myVariable));

I think that will work… haven’t tried it though.

1 Like

@Kevin.Herron

If we have common class used in Gateway and Client scope, when we use com.inductiveautomation.ignition.common.util.LoggerEx the MDC data are ignored in the client scope ?
the vision console in 7.9 or 8.0 doesn’t suport MDC filtering.

It’s %s and %d like String.format.

MDC data is only used in the gateway.