log4j干啥的不在细说。
一、先介绍log4j自带的两个类MDC和NDC作用以及用途。
NDC和MDC是log4j用于存储应用程序的上下文信息(context infomation),从而便于在log中使用这些上下文信息。
1、NDC采用了一个类似栈的机制来push存储上下文信息,每一个线程都独立地储存上下文信息。比如说一个servlet就可以针对每一个request创建对应的NDC,储存客户端地址等等信息。
相关的信息使用NDC.push(message);
在log的时候将信息输出。在相应的PatternLayout中使用”%x”来输出存储的上下文信息(
x要小写)
eg:
NDC.push("ipdizhi");
log4j.appender.console.layout.ConversionPattern=%-d{yyyy/MM/dd HH:mm:ss,SSS} [%x] -[%c]-[%p] %m%n
2、MDC内部使用了类似map的机制来存储信息。
相对应的方法,MDC.put(key, value);
在配置PatternLayout的时候使用:%x{key}来输出对应的value
MDC.put("ip", "ipdizhi");
log4j.appender.console.layout.ConversionPattern=%-d{yyyy/MM/dd HH:mm:ss,SSS} [%X{ip}] -[%c]-[%p] %m%n
二、log4j的使用方法(存储数据库)
1、新建个数据库表log4j存储数据
-- ----------------------------
DROP TABLE IF EXISTS `log4j`;
CREATE TABLE `log4j` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`LogName` varchar(200) NOT NULL,
`UserName` varchar(200) NOT NULL,
`ip` varchar(255) NOT NULL,
`Class` varchar(200) NOT NULL,
`Mothod` varchar(200) NOT NULL,
`LogLevel` varchar(20) NOT NULL,
`CreateTime` datetime NOT NULL,
`MSG` varchar(500) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=516 DEFAULT CHARSET=utf8;
2、配置log4j的配置文件
log4j.rootLogger=db
log4j.appender.db=org.apache.log4j.jdbc.JDBCAppender
log4j.appender.db.layout=org.apache.log4j.PatternLayout
log4j.appender.db.driver=com.mysql.jdbc.Driver
#定义什么级别的错误将写入到数据库中
log4j.appender.database.Threshold=info,error,warn
#设置缓存大小,就是当有1条日志信息是才忘数据库插一次
log4j.appender.db.BufferSize=3
#数据库链接
log4j.appender.db.URL=jdbc:mysql://localhost:3306/cy_test?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false
log4j.appender.db.user=root
log4j.appender.db.password=sasa
#数据库插入语句
log4j.appender.db.sql=insert into LOG4j (LogName,UserName,ip,Class,Mothod,createTime,LogLevel,MSG) values ('%X{userId}','%X{userName}','[%x]','%C','%M','%d{yyyy-MM-dd HH:mm:ss}','%p','%m')
3、使用
可以写个拦截器,进行拦截写入。
这里举个简单的栗子:
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.log4j.MDC;
import org.apache.log4j.NDC;
import org.junit.Test;
public class log4jtest {
Log logger = LogFactory.getLog(test.class);
@Test
public void dbtest() {
//MDC test
MDC.put("userId", "h333");
MDC.put("userName", "zhang");
//NDC test
NDC.push("17.3.3.2");
// 在具体方法中就可以写入日志
logger.info("33");
logger.warn("55");
logger.error("66");
}
}
三、一个请求过程中日志追踪
1、可以根据用户id在一段时间内的记录,进行预估排查。
2、在日志中增加一次请求标识
MDC.put("req.id",rund());。
在一次请求时增加一个id,在这个请求过程中所有日志进行一个标识。
四、分布式多系统日志追踪
这个可以看做是多个请求进行标识。
实现自定义Filter,在接受到http请求时,计算一个eventID并存储在MDC中。如果涉及分布式多系统,那么向其他子系统发送请求时,需要携带此eventID。
response.setHeader("eventsign", eventsign);
很有用,返回的response中,将有httpheader:eventsign:000010x1489108079237
请求时再携带获取 request.getHeader("eventsign").
五、拦截器举例举例:
拦截器:
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String eventsign;
if (request.getHeader("eventsign") == null || request.getHeader("eventsign").length() == 0) {
eventsign = this.sign + String.valueOf(new Date().getTime()); //计算eventID
logger.error("set eventid:" + eventsign);
} else {
eventsign = request.getHeader("eventsign");//从请求端获取eventsign
logger.error("get eventid from request:" + eventsign);
}
MDC.put("eventid", eventsign);
response.setHeader("eventsign", eventsign);
filterChain.doFilter(request, response);
}
六、总结:
根据个人系统的特点,合理设计字段以助于问题发现排查。