log4j之NDC、MDC
NDC(Nested Diagnostic Context)是 Neil Harrison 在名为《Patterns for Logging Diagnostic Messages》的书中提出的嵌套诊断环境的机制。这种机制的提出,主要为了减少多线程的系统为每个客户单独记录日志的系统开销。在过去,区分两个客户的日志输出的常用方法是单独为每个客户机实例化新类别,但该方法会增加类别数量,并增加日志记录的管理开销。Neil Harrison 提出的方法就是把用户的上下文信息放到嵌套式诊断环境 (NDC) 中。
NDC 为每一个线程管理一个堆栈。开发人员可以在代码中合适的位置嵌入简单的 push 和 pop 方法,用来维护堆栈。通常 push 进堆栈的是可以唯一标示客户的上下文信息,如 SessionID 或者客户名称,IP 地址等。因为每个客户请求都会有单独的 NDC 堆栈,因此日志系统在输出的时候会根据每个线程找到对应的堆栈,并在输出日志的时候附加上堆栈内的信息。开发人员就可以很容易的在日志中区分出各个不同客户所产生的日志条目。
Log4J 从 1.2 起开始支持 NDC,org.apache.log4j.NDC 声明如下:
代码 5. NDC 声明代码
- public class NDC {
- // 返回诊断堆栈的内容
- public static String get();
- // 从堆栈的顶端删除一个元素
- public static String pop();
- //在堆栈顶端加入一个元素
- public static void push(String message);
- //察看这个堆栈最顶层的元素,但不删除它
- public static String peek()
- // 删除这个线程的堆栈内容
- public static void remove();
- }
要注意的是,org.apache.log4j.NDC 类中所有的方法都是静态的。假设 NDC 日志输出功能被打开,每一次的日志请求,Log4J 组件都会把当前线程的整个 NDC 堆栈内容输出在日志条目中。这样的过程不需要开发人员写过多的代码,程序员只需要在代码中合适的地方通过 push 和 pop 方法将正确的信息放到 NDC 的堆栈中,然后通过修改 Log4J 的配置文件,指定用户标志信息输出的位置和格式,而原来 Java 代码中输出日志的代码不需要任何修改,就能够输出带有用户标志信息的日志。
NDC和MDC是log4j用于存储应用程序的上下文信息(context infomation),从而便于在log中使用这些上下文信息。
NDC采用了一个类似栈的机制来push存储上下文信息,每一个线程都独立地储存上下文信息。比如说一个servlet就可以针对每一个request创建对应的NDC,储存客户端地址等等信息。相关的信息使用NDC.push(message);
在log的时候将信息输出。在相应的PatternLayout中使用”%x”来输出存储的上下文信息
例如:String remoteAddr = request.getRemoteAddr();
NDC.push(remoteAddr);
在log4j.properties文件中作如下的配置即可
log4j.appender.console.layout.ConversionPattern=%-d{yyyy/MM/dd HH:mm:ss,SSS} [%X] -[%c]-[%p] %m%n
Log4J 配置文件中相应的配置信息,其中 PatternLayout 的 ConversionPattern 用于程序员指定日志输出的格式。要使用 NDC 的方式输出用户标志信息,只需要在 PatternLayout 的格式定义 ConversionPattern 中使用 %x,就能在相应的位置上输出 NDC 存储的上下文信息。
MDC介绍
MDC 和 NDC 相似,也可以减少多线程的系统为每个客户单独记录日志的系统开销。它同样是为每个线程建立一个独立的存储空间,开发人员可以根据需要把信息存入其中。不同的是 MDC 使用 Map 的机制来存储信息,信息以 key/value 对的形式存储在 Map 中。
Log4J 从 1.3 alpha 版本开始提供对 MDC 的支持,org.apache.log4j.MDC 声明如下:
代码 6. MDC 声明代码
- public class MDC {
- // 清空map所有的条目。
- public static void clear();
- // 根据key值返回相应的对象
- public static object get(String key);
- //返回所有的key值.
- public static Enumeration getKeys();
- //把key值和关联的对象,插入map中
- public static void put(String key, Object val),
- //删除key对应的对象
- public static remove(String key)
- }
MDC 和 NDC 的使用方法也类似,区别只是在
Log4J配置文件中,在通过 PatternLayout 的 ConversionPattern 来配置日志的格式的时候,需要使用 %x{key} 来输出相应的用户标志信息对象。
下面,我们通过具体的例子来说明如何在使用 Log4J 的 Web 应用中增加用户标志信息,达到进行用户跟踪的目的。在开发中,对于使用 NDC 还是 MDC 的机制,要看具体的应用在处理上下文信息的时候,是采用堆栈式的还是 Map 式的方便。下面我们以 MDC 为例进行说明。
MDC内部使用了类似map的机制来存储信息,相对应的方法,MDC.put(key, value);在配置PatternLayout的时候使用:%x{key}来输出对应的value
例如:String remoteAddr = request.getRemoteAddr();
MDC.put("ip", remoteAddr);
在log4j.properties文件中作如下的配置即可
log4j.appender.console.layout.ConversionPattern=%-d{yyyy/MM/dd HH:mm:ss,SSS} [%X{ip}] -[%c]-[%p] %m%n
如果在项目中有过滤器,你可以把获取ip 的方法直接定义在过滤器中,然后在配置文件中配置获取ip的显示就可以了
相关配置例子如下:
web.xml中:
<filter>
<filter-name>logFilter</filter-name>
<filter-class>com.speed.base.filter.Log4jUserFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>logFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
public class Log4jUserFilter implements Filter{ @Override public void destroy() { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { try { HttpServletRequest httpReq = (HttpServletRequest)request; MDC.put("sessionId", httpReq.getSession().getId()); chain.doFilter(request, response); } finally{ if(MDC.getContext() != null) MDC.getContext().clear(); } } @Override public void init(FilterConfig arg0) throws ServletException { } }
log4j之NDC、MDC的更多相关文章
- log4j的NDC/MDC区别与应用
MDC与NDC除了存储方式(MDC采用MapNDC采用堆栈结构)有区别,其他都一样的 关键点 A -//引入log4j MDC类org.apache.log4j.MDC -//设置值 -MDC.put ...
- log4j中的MDC和NDC
NDC和MDC NDC(Nested Diagnostic Context)和MDC(Mapped Diagnostic Context)是log4j种非常有用的两个类,它们用于存储应用程序的上下文信 ...
- Log4j NDC MDC
NDC(Nested Diagnostic Context)和MDC(Mapped Diagnostic Context)是log4j种非常有用的两个类,它们用于存储应用程序的上下文信息(contex ...
- [翻译]Java日志终极指南
本文由 ImportNew - Wing 翻译自 loggly.欢迎加入翻译小组.转载请见文末要求. Java日志基础 Java使用了一种自定义的.可扩展的方法来输出日志.虽然Java通过java.u ...
- Java日志终极指南
Java日志基础 Java使用了一种自定义的.可扩展的方法来输出日志.虽然Java通过java.util.logging包提供了一套基本的日志处理API,但你可以很轻松的使用一种或者多种其它日志解决方 ...
- log4j2配置推荐
<?xml version="1.0" encoding="UTF-8"?> <!-- monitorInterval为监听配置变化的间隔,3 ...
- Java日志实战及解析
Java日志实战及解析 日志是程序员必须掌握的基础技能之一,如果您写的软件没有日志,可以说你没有成为一个真正意义上的程序员. 为什么要记日志? • 监控代码 • 变量变化情况, ...
- Kafka【第一篇】Kafka集群搭建
Kafka初识 1.Kafka使用背景 在我们大量使用分布式数据库.分布式计算集群的时候,是否会遇到这样的一些问题: 我们想分析下用户行为(pageviews),以便我们设计出更好的广告位 我想对用户 ...
- zookeeper 日志输出到指定文件夹
最近在研究Zookeeper Storm Kafka, 顺便在本地搭了一套集群, 遇到了Zookeeper日志问题输出路径的问题, 发现zookeeper设置log4j.properties不能解决日 ...
随机推荐
- C/C++大数库简介
在网络安全技术领域中各种加密解密算法的软件实现上始终有一个共同的问题就是如何在普通的PC机上实现大数的运算.我们日常生活中所应用的PC机内部字长多是32位或64位,但是在各种加密解密的算法中为了达到一 ...
- media query ie8- 兼容实现总结
虽然说响应式设计的理想状态是,需对pc/移动各种终端进行响应:但是现实是高分辨率的pc端与手机终端屏幕相差太大,像电商这样有大量图片和文字信息的同时排版要求精准的页面,设计一个同时适应高分辨率pc又适 ...
- jmeter生成报告指示板
JMeter支持仪表板图表和报告生成 数据从一个测试计划. 这一章描述了如何配置和使用生成器. 概述 JMeter的仪表板生成器是一个模块化的扩展. 它的缺省行为是读取和处理样本 CSV文件生成HTM ...
- var isObj = length === undefined || i
这个其实是因为你前面那个===是肯定为false导致的,所以执行到了i那一步了var length=undefined;var a=length===undefined || i;这样你不定义i也是不 ...
- Qt之Concurrent Map和Map-Reduce
简述 QtConcurrent::map().QtConcurrent::mapped()和QtConcurrent::mappedReduced()函数在一个序列中(例如:QList或QVector ...
- lrzsz在CentOS7的安装
在超级用户下打一句命令: yum install lrzsz 或者,在普通用户打一句命令,需要输入超级用户密码: sudo yum install lrzsz 然后使用Xshell 5建立连接即可
- Java集合——List接口
1.定义 List是Collection的子接口,元素有序并且可以重复,表示线性表. 2.方法 add(int index,Object e):在指定索引(和数组下标类似,为0,1,2....)放入元 ...
- BZOJ1747 [Usaco2005 open]Expedition 探险
首先我们可以发现如果错过了一个加油站,而继续往前走的时候没有油了,可以再假装之前经过加油站的时候加过油 于是我们维护一个大根堆,表示错过的加油站是哪些,每当没有油的时候从堆顶取出最大值加上去即可 /* ...
- vim多行注释和取消多行注释
多行注释: 1. 进入命令行模式,按ctrl + v进入 visual block模式(可视快模式),然后按j, 或者k选中多行,把需要注释的行标记起来 2. 按大写字母i,再插入注释符,例如// 3 ...
- 二分图 最大权匹配 km算法
这个算法的本质还是不断的找增广路: KM算法的正确性基于以下定理:若由二分图中所有满足A[i]+B[j]=w[i,j]的边(i,j)构成的子图(称做相等子图)有完备匹配,那么这个完备匹配就是二分图的最 ...