系统内日志唯一流水
一次请求,在系统内部可能调用很多不同方法,在排查问题时,如果没有一个串联日志的key,会影响排查问题效率,以下是使用MDC生成日志唯一流水示例
定义annocation
@Retention(RetentionPolicy.RUNTIME)
//方法或者类上生效
@Target({ElementType.METHOD,ElementType.TYPE})
public @interface ServiceLog {
}
切面代码
@Slf4j
@Aspect
@Component
public class AopServiceLogger {
//方法或类生效
@Around("@within(ServiceLog) || @annotation(ServiceLog)")
public Object log(ProceedingJoinPoint point) throws Throwable {
return generalLogOpr(point, "{} request {}", "{} response: {} in {} ms");
}
private Object generalLogOpr(ProceedingJoinPoint point, String prefix, String suffix) throws Throwable {
try{
//设置唯一流水
MDC.put(traceId, UUID.randomUUID().toString().replaceAll("-",""));
String methodName = point.getTarget().getClass().getSimpleName() + "." + point.getSignature().getName();
requestLog(methodName,prefix,point);
StopWatch stopWatch = new StopWatch();
stopWatch.start();
Object retVal = null;
try {
retVal = point.proceed();
} catch (Throwable e) {
log.error(methodName,e);
throw e;
}
generalOprLogEnd(methodName,stopWatch, retVal, suffix);
return retVal;
}finally{
MDC.clear();
}
}
private void generalOprLogEnd(String methodName,StopWatch stopWatch, Object retVal, String suffix) {
stopWatch.stop();
log.info(suffix, methodName,retVal, stopWatch.getTotalTimeMillis());
}
private void requestLog(String methodName,String prefix,JoinPoint point) {
MethodSignature methodSignature = (MethodSignature) point.getSignature();
String[] parameterNames = methodSignature.getParameterNames();
StringBuilder args = new StringBuilder();
args.append("(");
for (int i = 0; i < point.getArgs().length; i++) {
Object[] arg = point.getArgs();
if (i > 0) {
args.append(",");
}
args.append(parameterNames[i]).append(":").append(arg[i]);
}
args.append(")");
log.info(prefix, methodName,args.toString());
}
}
log4j2.xml中设置traceId占位符
<Properties>
<Property name="LOG_PATTERN">%d %-5p [%t,%X{traceId}] [%c] %mm%n</Property>
<Property name="APP_LOG_ROOT">./logs</Property>
</Properties>
需要注意的是,这种方式只能保证同一个线程的日志都有同一个流水,多线程会是不同流水,可以通过以下方式设置traceId
String traceId = MDC.get("traceId");
threadPoolTaskExecutor.execute(()->{
MDC.put("traceId",traceId);
try {
//do some thing
} finally {
MDC.remove("traceId");
}
});
系统间唯一流水
通过在通讯协议中携带唯一流水
比如http header中或者dubbo 自定义头来保证系统间同一次请求使用同一个唯一流水。这种方式需要自己开发
开源框架
使用开源日志追踪框架,zipkin ,sleuth 等