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的实现代码:



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
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的实现:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
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  
           }  
       }  
     }  
   }  
   
}


日志信息代表的含义:


1
2
3
4
5
6
7
8
9
10
11
12
%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”输出日志信息换行