4

I would like to add a unique identifier to log statements, so I am able to add documentation (externally, e.g. a wiki) to every log statement, so a user can quickly access the message related documentation using the id. The logging framework I would like to use is SLF4J/logback.

I was not able to find documentation about related approaches except for some bits regarding auditing frameworks.

There is the Marker concept which I thought could be usable for ID injection, or I could just add the ID to the message text itself.

How would I add IDs to the logging statements "the right way"? Are there possibilities I didn't think of?

EDIT

The term unique ID just states there should be an identifier per log statement. A developer e.g. adds such an ID to a table/enum/whatever manually, which could be done wrong. Such ID has to be stable, so documentation can be based on it. So the ID itself is not what I am wondering about.

My question is: what would be the right way of pushing the ID to the logger together with the message text? Would Markers be suited for this kind of requirement, should I embed the ID into the message text or is there some other possibility?

So, basically, would I use

logger.info(IDMarkers.DB_CONNECTION_FAILED, "no connection to the database");

or instead just

logger.info("[{}] no connection to the database", LogIDs.DB_CONNECTION_FAILED);

First approach has the advantage that showing the IDs is up to the logging system/its configuration.

Adrian Genaid
  • 416
  • 5
  • 17
  • How do you want the id to be unique? By call? By thread? By line of code? – zenbeni Nov 15 '13 at 17:04
  • @zenbeni: a log statement (e.g. `logger.debug('some message text')` ) should get the ID. Such statement would produce e.g. a log entry like `[ID1234] some message text` for every call. – Adrian Genaid Nov 15 '13 at 17:17
  • Getting the context of execution at runtime is expensive: http://stackoverflow.com/questions/17473148/dynamically-get-the-current-line-number so I think you should do it statically in the message. – zenbeni Nov 15 '13 at 17:31
  • @zenbeni: I may be wrong, but getting a Marker is not costlier than the string concatenation needed when fetching the ID from elsewhere. This would not involve getting context of execution. – Adrian Genaid Nov 15 '13 at 18:11
  • after some more googling found [this](http://stackoverflow.com/questions/16455814/how-to-do-oracle-style-numbered-logging) related question on SO, which unfortunately does not have much information. – Adrian Genaid Nov 19 '13 at 15:15

2 Answers2

1

Slf4j has http://www.slf4j.org/apidocs/org/slf4j/Marker.html

Unfortunately Markers are advertised for a different purpose. Still you can use them to uniquely mark logging statements.

More cumbersome solution is MDC:

MDC.put("MsgId", "EV-1234");
log.info()
MDC.remove("MsgId");

or with structural logging (requires v2.0.0):

logger.atDebug()
    .addKeyValue("MsgId", "EV-1234")
    .log("Temperature changed.");          
gavenkoa
  • 45,285
  • 19
  • 251
  • 303
0

Unique is only unique within some scope. Eventually, even every int or long value will be used.

So think about what "uniqueness" means to you. Then use a wrapper that will ensure your logging is handled with that id inserted.

Note that with slf4j you are dealing with an interface which will make a number of logging APIs consistent. This means you probably won't have the option to sub-class or even inject your implementation of the interface to ensure your consistent logging. Therefore you will be constrained to techniques like wrapping your logging API (preferably through the "consistent" interface).

package mypackage.log;

public class LoggerWrapper implements org.log4j.Logger {

   private org.log4j.Logger logger;

   public LoggerWrapper(org.log4j.Logger logger) {
     this.logger = logger;
   }

   public String getUniqueId() {
     return ...;
   }

   public void info(String message, Object params...) {
     logger.info(String.format("[%d] %s", getUniqueId(), message), params));
   }

   ... implement all the other methods ...
}

And this means that you will have to make your own LoggerFactory interface too

public Logger getLogger(String name) {
  return new LoggerWrapper(org.sql4j.LoggerFactory(name));
}

While the code above has a few warts (not actually testing it); hopefully, it will give you an idea.

Edwin Buck
  • 69,361
  • 7
  • 100
  • 138
  • I updated my question to point out it's direction. Your proposed solution would give me a unique ID per log entry, which would not support me in linking documentation to the log statement... – Adrian Genaid Nov 15 '13 at 22:19
  • My solution would give you an unique id for anything you wanted. For example, you could construct the wrapper with a constant string, then it would be a unique log id per logger. Or you could use thread detection code in `getUniqueId()` to have a unique id per thread. It's up to you to determine uniqueness. – Edwin Buck Nov 15 '13 at 23:00