一种轻量级的技术是给每个为客户端服务的 logger 打一个标记。Neil Harrison 在 Patterns for Logging Diagnostic Messages in Pattern Languages of Program Design 3, edited by R. Martin, D. Riehle, and F. Buschmann (Addison-Wesley, 1997) 这本书中描述了这种方法。logback 在 SLF4J API 利用了这种技术的变体:诊断上下文映射 (MDC)。
package chapters.mdc;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import ch.qos.logback.classic.PatternLayout;
import ch.qos.logback.core.ConsoleAppender;
public class SimpleMDC {
static public void main(String[] args) throws Exception {
// 你可以选择在任何时候将值放入 MDC 中
MDC.put("first", "Dorothy");
[ SNIP ]
Logger logger = LoggerFactory.getLogger(SimpleMDC.class);
MDC.put("last", "Parker");
logger.info("Check enclosed.");
logger.debug("The most beautiful two words in English.");
MDC.put("first", "Richard");
MDC.put("last", "Nixon");
logger.info("I am not a crook.");
logger.info("Attributed to the former US president. 17 Nov 1973.");
}
[ SNIP ]
}
main 方法在启动的时候在 MDC 中将 Dorothy 关联到 first 上。你可以在 MDC 放置尽可能多的键值对,反正你开心就好。多次插入同一个 key,新值会覆盖旧值。然后代码继续配置 logback。
Dorothy Parker - Check enclosed.
Dorothy Parker - The most beautiful two words in English.
Richard Nixon - I am not a crook.
Richard Nixon - Attributed to the former US president. 17 Nov 1973.
package chapters.mdc;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import java.util.Vector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.joran.JoranConfigurator;
import ch.qos.logback.core.joran.spi.JoranException;
/**
* A simple NumberCruncher implementation that logs its progress when
* factoring numbers. The purpose of the whole exercise is to show the
* use of mapped diagnostic contexts in order to distinguish the log
* output from different client requests.
* */
public class NumberCruncherServer extends UnicastRemoteObject
implements NumberCruncher {
private static final long serialVersionUID = 1L;
static Logger logger = LoggerFactory.getLogger(NumberCruncherServer.class);
public NumberCruncherServer() throws RemoteException {
}
public int[] factor(int number) throws RemoteException {
// The client's host is an important source of information.
try {
MDC.put("client", NumberCruncherServer.getClientHost());
} catch (java.rmi.server.ServerNotActiveException e) {
logger.warn("Caught unexpected ServerNotActiveException.", e);
}
// The information contained within the request is another source
// of distinctive information. It might reveal the users name,
// date of request, request ID etc. In servlet type environments,
// useful information is contained in the HttpRequest or in the
// HttpSession.
MDC.put("number", String.valueOf(number));
logger.info("Beginning to factor.");
if (number <= 0) {
throw new IllegalArgumentException(number +
" is not a positive integer.");
} else if (number == 1) {
return new int[] { 1 };
}
Vector<Integer> factors = new Vector<Integer>();
int n = number;
for (int i = 2; (i <= n) && ((i * i) <= number); i++) {
// It is bad practice to place log requests within tight loops.
// It is done here to show interleaved log output from
// different requests.
logger.debug("Trying " + i + " as a factor.");
if ((n % i) == 0) {
logger.info("Found factor " + i);
factors.addElement(new Integer(i));
do {
n /= i;
} while ((n % i) == 0);
}
// Placing artificial delays in tight loops will also lead to
// sub-optimal results. :-)
delay(100);
}
if (n != 1) {
logger.info("Found factor " + n);
factors.addElement(new Integer(n));
}
int len = factors.size();
int[] result = new int[len];
for (int i = 0; i < len; i++) {
result[i] = ((Integer) factors.elementAt(i)).intValue();
}
// clean up
MDC.remove("client");
MDC.remove("number");
return result;
}
static void usage(String msg) {
System.err.println(msg);
System.err.println("Usage: java chapters.mdc.NumberCruncherServer configFile\n" +
" where configFile is a logback configuration file.");
System.exit(1);
}
public static void delay(int millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
}
}
public static void main(String[] args) {
if (args.length != 1) {
usage("Wrong number of arguments.");
}
String configFile = args[0];
if (configFile.endsWith(".xml")) {
try {
LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
JoranConfigurator configurator = new JoranConfigurator();
configurator.setContext(lc);
lc.reset();
configurator.doConfigure(args[0]);
} catch (JoranException je) {
je.printStackTrace();
}
}
NumberCruncherServer ncs;
try {
ncs = new NumberCruncherServer();
logger.info("Creating registry.");
Registry registry = LocateRegistry.createRegistry(Registry.REGISTRY_PORT);
registry.rebind("Factor", ncs);
logger.info("NumberCruncherServer bound and ready.");
} catch (Exception e) {
logger.error("Could not bind NumberCruncherServer.", e);
return;
}
}
}