/*
* Based heavily on the Log4j NDC published under the terms of the Apache Software License
* version 1.1
*/
package org.oddjob.logging;
import java.util.Stack;
import org.apache.log4j.MDC;
/**
* An OddjobNDC is a Nested Diagnostic Context for Oddjob. It provides a way of
* allowing component by component logging.
* <p>
* It is very crude wrapper for the Log4j MDC class. One day it would be nice to work
* out how to apply this to other logging utilities.
* <p>
* Note that this is a thread based nested diagnostic context that does not
* interfere with Log4j's own. It populates log4j's mapped diagnostic context instead.
*
* @author rob
*/
public class OddjobNDC implements LoggingConstants {
/** The stack of contexts. */
private static InheritableThreadLocal<Stack<LoggerAndJob>> local =
new InheritableThreadLocal<Stack<LoggerAndJob>>() {
protected Stack<LoggerAndJob> initialValue() {
return new Stack<LoggerAndJob>();
}
@SuppressWarnings("unchecked")
protected Stack<LoggerAndJob> childValue(Stack<LoggerAndJob> parentValue) {
if (parentValue != null) {
return (Stack<LoggerAndJob>)parentValue.clone();
}
else {
return null;
}
}
};
// No instances allowed.
private OddjobNDC() {
}
/**
* Clients should call this method before leaving a diagnostic context.
*
* <p>
* The returned value is the value that was pushed last. If no context is
* available, then the empty string "" is returned.
*
* @return LoggerAndJob The innermost diagnostic context.
*
*/
public static LoggerAndJob pop() {
Stack<LoggerAndJob> stack = local.get();
LoggerAndJob result = stack.pop();
if (stack.isEmpty()) {
MDC.remove(MDC_LOGGER);
MDC.remove(MDC_JOB_NAME);
} else {
LoggerAndJob peek = stack.peek();
MDC.put(MDC_LOGGER, peek.getLogger());
MDC.put(MDC_JOB_NAME, peek.getJob().toString());
}
return result;
}
/**
* Looks at the last diagnostic context at the top of this NDC without
* removing it.
*
* <p>
* The returned value is the value that was pushed last. If no context is
* available, then the empty string "" is returned.
*
* @return LoggerAndJob The innermost diagnostic context.
*
*/
public static LoggerAndJob peek() {
Stack<LoggerAndJob> stack = local.get();
if (stack.isEmpty()) {
return null;
}
return stack.peek();
}
/**
* Push new diagnostic context information for the current thread.
*
* @param loggerName
* The new diagnostic context information.
*
*/
public static void push(String loggerName, Object job) {
if (loggerName == null) {
throw new NullPointerException("Can't push null logger name.");
}
if (job == null) {
throw new NullPointerException("Can't push null job.");
}
Stack<LoggerAndJob> stack = local.get();
stack.push(new LoggerAndJob(loggerName, job));
MDC.put(MDC_LOGGER, loggerName);
MDC.put(MDC_JOB_NAME, job.toString());
}
/**
* Holds Logger and Job information for the Stack.
*/
public static class LoggerAndJob implements Cloneable {
private final String logger;
private final Object job;
public LoggerAndJob(String logger, Object job) {
this.logger = logger;
this.job = job;
}
public Object getJob() {
return job;
}
public String getLogger() {
return logger;
}
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
}