NDC  Nested Diagnostic Context )和 MDC  Mapped Diagnostic Context )是 log4j 种非常有用的两个类,它们用于存储应用程序的上下文信息( context infomation ),从而便于在 log 中使用这些上下文信息。

 

NDC的实现是用hashtable来存储每个线程的stack信息,这个stack是每个线程可以设置当前线程的request的相关信息,然后当前线 程在处理过程中只要在log4j配置打印出%x的信息,那么当前线程的整个stack信息就会在log4j打印日志的时候也会都打印出来,这样可以很好的 跟踪当前request的用户行为功能。

MDC的实现是使用threadlocal来保存每个线程的Hashtable的类似map的信息,其他功能类似。

性能

在记录一些日志信息时,会一定程度地影响系统的运行效率,这时日志工具是否高效就是一个关键。Log4J的首要设计目标就是高效,一些关键组件都重写过很多次以不断提高性能。根据Log4J项目小组的报告,在AMD Duron 800MHz + JDK1.3.1的环境下,Log4J判断一条日志语句是否需要输出仅需要5纳秒。实际的日志语句执行的也非常快速,从使用SimpleLayout的21微秒(几乎与System.out.println一样快),到使用TTCCLayout的37微秒不等。

NDC的实现代码:

public class NDC {        static Hashtable ht = new Hashtable();          private static Stack getCurrentStack() {        if (ht != null) {            return (Stack) ht.get(Thread.currentThread());        }        return null;    }      public    static    String pop() {      Stack stack = getCurrentStack();      if(stack != null && !stack.isEmpty())         return ((DiagnosticContext) stack.pop()).message;      else        return "";    }    public    static    String peek() {      Stack stack = getCurrentStack();      if(stack != null && !stack.isEmpty())        return ((DiagnosticContext) stack.peek()).message;      else        return "";    }    public    static    void push(String message) {      Stack stack = getCurrentStack();              if(stack == null) {        DiagnosticContext dc = new DiagnosticContext(message, null);              stack = new Stack();        Thread key = Thread.currentThread();        ht.put(key, stack);        stack.push(dc);      } else if (stack.isEmpty()) {        DiagnosticContext dc = new DiagnosticContext(message, null);                    stack.push(dc);      } else {        DiagnosticContext parent = (DiagnosticContext) stack.peek();        stack.push(new DiagnosticContext(message, parent));      }        }

MDC的实现:

public class MDC {        final static MDC mdc = new MDC();        static final int HT_SIZE = 7;      boolean java1;        Object tlm;      private Method removeMethod;      private    MDC() {      java1 = Loader.isJava1();      if(!java1) {        tlm = new ThreadLocalMap();      }        try {        removeMethod = ThreadLocal.class.getMethod("remove", null);      } catch (NoSuchMethodException e) {        // don't do anything - java prior 1.5      }    }          */    static    public    void put(String key, Object o) {       if (mdc != null) {           mdc.put0(key, o);       }    }      static     public    Object get(String key) {      if (mdc != null) {          return mdc.get0(key);      }      return null;    }      static     public    void remove(String key) {      if (mdc != null) {          mdc.remove0(key);      }    }        public static Hashtable getContext() {      if (mdc != null) {          return mdc.getContext0();      } else {          return null;      }    }        public static void clear() {      if (mdc != null) {          mdc.clear0();      }    }        private    void put0(String key, Object o) {      if(java1 || tlm == null) {        return;      } else {        Hashtable ht = (Hashtable) ((ThreadLocalMap)tlm).get();        if(ht == null) {          ht = new Hashtable(HT_SIZE);          ((ThreadLocalMap)tlm).set(ht);        }            ht.put(key, o);      }    }        private    Object get0(String key) {      if(java1 || tlm == null) {        return null;      } else {               Hashtable ht = (Hashtable) ((ThreadLocalMap)tlm).get();        if(ht != null && key != null) {          return ht.get(key);        } else {          return null;        }      }    }      private    void remove0(String key) {      if(!java1 && tlm != null) {        Hashtable ht = (Hashtable) ((ThreadLocalMap)tlm).get();        if(ht != null) {          ht.remove(key);          // clean up if this was the last key          if (ht.isEmpty()) {            clear0();          }        }       }    }        private    Hashtable getContext0() {       if(java1 || tlm == null) {        return null;      } else {               return (Hashtable) ((ThreadLocalMap)tlm).get();      }    }      private    void clear0() {      if(!java1 && tlm != null) {        Hashtable ht = (Hashtable) ((ThreadLocalMap)tlm).get();        if(ht != null) {          ht.clear();        }        if(removeMethod != null) {            // java 1.3/1.4 does not have remove - will suffer from a memory leak            try {              removeMethod.invoke(tlm, null);            } catch (IllegalAccessException e) {              // should not happen            } catch (InvocationTargetException e) {              // should not happen            }        }      }    }    }

日志信息代表的含义:

%p: 输出日志信息优先级,即DEBUG,INFO,WARN,ERROR,FATAL, %d: 输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式,比如:%d{yyy MMM dd HH:mm:ss,SSS},输出类似:2002年10月18日 22:10:28,921 %r: 输出自应用启动到输出该log信息耗费的毫秒数 %c: 输出日志信息所属的类目,通常就是所在类的全名 %t: 输出产生该日志事件的线程名 %l: 输出日志事件的发生位置,相当于%C.%M(%F:%L)的组合,包括类目名、发生的线程,以及在代码中的行数。举例:Testlog4.main (TestLog4.java:10) %x: 输出和当前线程相关联的NDC(嵌套诊断环境),尤其用到像Java servlets这样的多客户多线程的应用中。 %%: 输出一个”%”字符 %F: 输出日志消息产生时所在的文件名称 %L: 输出代码中的行号 %m: 输出代码中指定的消息,产生的日志具体信息 %n: 输出一个回车换行符,Windows平台为”\r\n”,Unix平台为”\n”输出日志信息换行