java ndc_log4j MDC NDC详解

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”输出日志信息换行

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值