通过NDC为Log4j日志加上线程标识id的方法

当我们在多线程的环境下需要跟踪某一线程的产生的日志,如果没有一个用于标注日志所对应的线程的标识,那么查日志会是一个很痛苦的过程。

在日志中为不同线程加上唯一的标识是一个不错的解决办法,Log4j已经其实已经提供了多种实现方式,这里要说的是通过DNC的方式为日志加上线程标识,其他的方法请参考这里:https://blog.csdn.net/liulin_good/article/details/5995884

假设:当我们收到一个请求之后,业务层或持久层报了错,我们如何跟踪判断这个请求从前到后的所有日志?

简单的说,spring容器会为每一个请求分配一个线程,既然是要从收到请求开始跟踪,那么我们就在controller层做切面,收到请求后创建id并push到DNC中,并在请求处理结束时后,清除掉我们push到DNC中的id,下面上代码:

controller:

@Controller
public class UserController {
	@Autowired
	UserService userService;

	@RequestMapping("/getUsername")
	@ResponseBody
	public User getUserById(Integer id) throws Exception {
		User user = userService.getUserById(id);
		return user;
	}
}

service:(故意写一个会报错的代码,用于一会测试)

@Service
public class UserServiceImpl implements UserService {
	@Autowired
	UserMapper userMapper;

	@Override
	public User getUserById(Integer id) throws Exception {
		try {
			int i = 1 / 0;
			System.out.println(i);
		} catch (Exception e) {
			LogUtils.info("UserServiceImpl执行出错,入参为:id=" + id);
			throw new Exception("出错啦!!!", e);
		}
		User user = userMapper.getUserById(id);
		return user;
	}

}

日志实现类:

package com.test.log;

import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;

public class LogAspect {
	Logger logger = Logger.getLogger(LogAspect.class);
	String logStr = null;

	/**
	 * 前置通知:在某连接点之前执行的通知,但这个通知不能阻止连接点前的执行
	 * 
	 * @param jp
	 *            连接点:程序执行过程中的某一行为
	 */
	public void doBefore(JoinPoint jp) {
		logStr = jp.getTarget().getClass().getName() + "类的" + jp.getSignature().getName() + "方法开始执行******Start******\n";
		logger.info(logStr);
	}

	/**
	 * 环绕通知:包围一个连接点的通知,可以在方法的调用前后完成自定义的行为,也可以选择不执行。
	 * 类似web中Servlet规范中Filter的doFilter方法。
	 * 
	 * @param pjp
	 *            当前进程中的连接点
	 * @return
	 */
	public Object doAround(ProceedingJoinPoint pjp) {
		Object result = null;
		try {
			result = pjp.proceed();
		} catch (Throwable e) {
			e.printStackTrace();
			StringBuilder logInfo = new StringBuilder(
					"方法:" + pjp.getTarget().getClass() + "." + pjp.getSignature().getName() + "()\n");
			logInfo.append("提示信息为:" + e.getMessage() + "\n");
			logInfo.append("具体错误信息如下:\n");
			StackTraceElement[] stackTrace = e.getStackTrace();
			for (StackTraceElement stackTraceElement : stackTrace) {
				logInfo.append(stackTraceElement + "\n");
			}
			logger.info(logInfo);
		}
		return result;
	}

	/**
	 * 后置通知
	 * 
	 * @param jp
	 */
	public void doAfter(JoinPoint jp) {
		logStr = jp.getTarget().getClass().getName() + "类的" + jp.getSignature().getName() + "方法执行结束******End******\n";
		logger.info(logStr);
	}
}

生成日志id实现类:

package com.test.log;

import java.util.UUID;

import org.apache.log4j.NDC;

public class LogId {

	/**
	 * 通过uuid为线程标注唯一标识
	 */
	public void pushLogId() {
		NDC.push(UUID.randomUUID().toString());
	}

	/**
	 * 记得清除一下
	 */
	public void removeLogId() {
		NDC.remove();
	}
}

在spring的配置文件applicationContext.xml中配置业务层和持久层的日志切面:

	<!-- 日志 -->
	<bean id="logAspect" class="com.test.log.LogAspect"></bean>
	<aop:config>
		<!-- controller日志 -->
		<aop:pointcut id="serviceLogPointcut"
			expression="execution(* com.test.*.*.*(..))" />
		<aop:aspect id="aspect" ref="logAspect">
			<aop:before pointcut-ref="serviceLogPointcut"
				method="doBefore" />
			<aop:after pointcut-ref="serviceLogPointcut"
				method="doAfter" />
			<aop:around pointcut-ref="serviceLogPointcut"
				method="doAround" />
		</aop:aspect>
	</aop:config>

在springMVC的配置文件springmvc.xml中配置controller的日志切面,并生成线程标识id:

	<bean id="logId" class="com.test.log.LogId"></bean>
	<aop:config>
		<aop:pointcut id="controllerLogPointcut"
			expression="execution(* com.test.controller.*.*(..))" />
		<!-- 生成日志id -->
		<aop:aspect id="logIdAspect" ref="logId" order="1">
			<aop:before pointcut-ref="controllerLogPointcut"
				method="pushLogId" />
			<aop:after pointcut-ref="controllerLogPointcut"
				method="removeLogId" />
		</aop:aspect>
		<!-- controller日志 -->
		<aop:aspect id="aspect" ref="logAspect" order="2">
			<aop:before pointcut-ref="contr
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值