log4j 系列06 -- log4j MDC 的使用--配合 Filter (保存到数据库)

10 篇文章 0 订阅

本文是在 SSM 框架下进行的。springBoot 框架下还没实践过,应该大同小异。

本文是在前文: SSM 框架配置使用 slf4j-log4j  的基础上进行的

简单来说,就是 存储在 NDC 或 MDC 内的信息可以在日志里直接使用,而不用每次生成日志就设置一次。

参考文章:

log4j自带的两个类MDC和NDC作用以及用途

log4j MDC用户操作日志追踪配置

log4j中MDC用法

=====================================

1、概念入门

要想实现获取IP(或其他自定义信息)并显示在log中必须先了解log4j自带的两个类MDC和NDC

NDC(Nested Diagnostic Context)和MDC(Mapped Diagnostic Context)
log4j用于存储应用程序的上下文信息(context infomation),从而便于在log中使用这些上下文信息。

NDC

采用了一个类似栈的机制来push和pop上下文信息,每一个线程都独立地储存上下文信息。比如说一个servlet就可以针对每一个request创建对应的NDC,储存客户端地址等等信息。

当使用的时候,我们要尽可能确保在进入一个context的时候,把相关的信息使用NDC.push(message);在离开这个context的时候使用NDC.pop()将信息删除。另外由于设计上的一些问题,还需要保证在当前thread结束的时候使用NDC.remove()清除内存,否则会产生内存泄漏的问题。

存储了上下文信息之后,我们就可以在log的时候将信息输出。在相应的PatternLayout中使用”%x”来输出存储的上下文信息,下面是一个PatternLayout的例子:

%r [%t] %-5p %c{2} %x - %m%n

在log的时候将信息输出。在相应的PatternLayout中使用”%x”来输出存储的上下文信息

在最新的log4j 1.3版本中增加了一个org.apache.log4j.filters.NDCMatchFilter,用来根据NDC中存储的信息接受或拒绝一条log信息。

MDC

和NDC非常相似,所不同的是MDC内部使用了类似map的机制来存储信息,上下文信息也是每个线程独立地储存,所不同的是信息都是以它们的key值存储在”map”中。相对应的方法,MDC.put(key, value); MDC.remove(key); MDC.get(key); 在配置PatternLayout的时候使用:%x{key}来输出对应的value。
同样地,MDC也有一个org.apache.log4j.filters.MDCMatchFilter。这里需要注意的一点,MDC是线程独立的,但是一个子线程会自动获得一个父线程MDC的copy。

至于选择NDC还是MDC要看需要存储的上下文信息是堆栈式的还是key/value形式的。

2、使用 Demo:

见:log4j MDC用户操作日志追踪配置

3、SSM 框架下的配置使用

基本配置不再赘述,详见  SSM 框架配置使用 slf4j-log4j

(1)、新建 Filter

public class LogFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse rep = (HttpServletResponse) response;
        
        MDC.put("id", UuidUtil.get32UUID());
              
        System.out.println("Filter:"+MDC.getContext());
      
        chain.doFilter(request, response);
    }

    @Override
    public void destroy() {

    }

}

(2)、在web.xml加入 Filter:

<!-- log4j filter  -->
<filter>
	<filter-name>log4jUserLog</filter-name>
	<filter-class>com.fh.filter.LogFilter</filter-class>
</filter>
<filter-mapping>
	<filter-name>log4jUserLog</filter-name>
	<url-pattern>/*</url-pattern>
</filter-mapping>

(3)、log4j.properties 中配置自定义 Logger

保存到数据库--首先新建表 photo (id,msg,createtime)

########################
# JDBC Appender
#######################
log4j.logger.DEVICE=INFO,DEVICE
log4j.appender.DEVICE=org.apache.log4j.jdbc.JDBCAppender
log4j.appender.DEVICE.BufferSize=1
log4j.appender.DEVICE.driver=com.mysql.jdbc.Driver
log4j.appender.DEVICE.URL=jdbc:mysql://localhost:3306/photo
log4j.appender.DEVICE.Threshold=INFO
log4j.appender.DEVICE.user=root
log4j.appender.DEVICE.password=root
log4j.appender.DEVICE.encoding=UTF8
log4j.appender.DEVICE.sql=insert into photo (id,msg,createtime) values ('%X{id}',"%m",'%d{yyyy-MM-dd HH:mm:ss}')
log4j.appender.DEVICE.layout=org.apache.log4j.PatternLayout

(4)、程序中使用 Logger

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

#####
public Logger loggerDev = LoggerFactory.getLogger("DEVICE");
#####
loggerDev.info("启动拍照");

=====================

(5)、运行项目,访问包含 loggerDev  的 URL(接口) ,数据库中可见新增了数据

=======================================

注意:Filter 存在的局限

Filter 的拦截粒度仅到 URL(接口) 层面,也就是说,访问一次接口,只能生成一条日志,

即使你在该接口的方法中多次调用  loggerDev.info("");

======

据目前所掌握的资料,NDC/MDC 实际使用中常常配合 Filter 使用(它的便捷性正是体现在此)
如果不想被  Filter 上面这一条限制,
那就 
每次生成新日志的时候,自己拼接所需要的信息吧,这时用不用  NDC /MDC 都随意(此时 NDC /MDC 已经失去了便携性意义)。

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值