一、通过切面的方式记录请求日志信息
@Aspect
@Component
@Slf4j
@Order(1)
public class WebLogAspect {
private static final String START_TIME = "request-start";
/**
* 服务名
*/
@Value("${spring.application.name}")
private String serverName;
@Autowired
private SaveLogInfoService logInfoService;
/**
* 切入点
*/
@Pointcut("execution(public * com.xxx..controller..*(..))")//两个..代表所有子目录,最后括号里的两个..代表所有参数
public void log() {
}
/**
* 前置操作
*
* @param point 切入点
*/
@Before("log()")
public void beforeLog(JoinPoint point) {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = Objects.requireNonNull(attributes).getRequest();
log.info("({})【请求 URL】:{},【IP】:{}",serverName, request.getRequestURL(), IpUtils.getIpAddr(request));
log.info("({})【请求类名】:{},【请求方法名】:{}",serverName, point.getSignature().getDeclaringTypeName(), point.getSignature().getName());
Object[] args = point.getArgs();
log.info("({})【请求参数】:{},", serverName,JSONUtil.toJsonStr(args));
Long start = System.currentTimeMillis();
request.setAttribute(START_TIME, start);
//saveLog
Class clazz = point.getTarget().getClass();
String method = point.getSignature().getName();
OperationLogBean operationLogBean=new OperationLogBean();
operationLogBean.setMethod(method);
operationLogBean.setIp(IpUtils.getIpAddr(request));
operationLogBean.setClassName(point.getSignature().getDeclaringTypeName());
operationLogBean.setReqParam(JSONUtil.toJsonStr(args));
operationLogBean.setTraceId(MDC.get(AuthUtils.TRACE_ID));
operationLogBean.setCreateDate(new Date());
MethodSignature signature = (MethodSignature)point.getSignature();
Method d = signature.getMethod();
Annotation[] annotations = d.getDeclaredAnnotations();
if (annotations != null) {
for (Annotation annotation : annotations) {
if (annotation instanceof ApiOperation) {
ApiOperation methodDesc = (ApiOperation) annotation;
String desc = methodDesc.value();
operationLogBean.setMethodName(desc);
}
}
}
Annotation[] declaredAnnotations = clazz.getDeclaredAnnotations();
if(declaredAnnotations !=null){
for (Annotation annotation : declaredAnnotations) {
if (annotation instanceof Api) {
Api classDesc = (Api) annotation;
String desc = classDesc.value();
operationLogBean.setClassNameInfo(desc);
}
}
}
AuthTokenData auth = AuthThreadLocal.getAuth();
if(Objects.nonNull(auth)&&Objects.nonNull(auth.getUser())){
operationLogBean.setUserId(auth.getUser().getUserId());
operationLogBean.setUserName(auth.getUser().getUsername());
}
saveLog(operationLogBean);
}
/**
* 环绕操作
*
* @param point 切入点
* @return 原方法返回值
* @throws Throwable 异常信息
*/
@Around("log()")
public Object aroundLog(ProceedingJoinPoint point) throws Throwable {
Object result = point.proceed();
//log.info("【返回值】:{}", JSONUtil.toJsonStr(result));
return result;
}
/**
* 后置操作
*/
@AfterReturning("log()")
public void afterReturning() {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = Objects.requireNonNull(attributes).getRequest();
Long start = (Long) request.getAttribute(START_TIME);
Long end = System.currentTimeMillis();
log.info("({})【耗时】:{}毫秒,【请求 URL】:{},【IP】:{}",serverName, end - start, request.getRequestURL(), IpUtils.getIpAddr(request));
String header = request.getHeader("User-Agent");
UserAgent userAgent = UserAgentUtil.parse(header);
log.info("({})【浏览器类型】:{},【操作系统】:{},【原始User-Agent】:{}", serverName,userAgent.getBrowser().toString(), userAgent.getOs().toString(), header);
}
private void saveLog(OperationLogBean operationLogBean){
if(Objects.nonNull(logInfoService)){
logInfoService.save(operationLogBean);
}
}
}
二、使用MDC统一处理微服务场景各服务间调用日志
原理就是给每一条日志打上标签,比如一次调用从网关开始,在该次请求上添加链路追踪id
被路由到的服务会全局拦截请求,获取id,放入MDC中
三、自定义logback日志打印处理类
所有的日志存储都带上来链路追踪id
feign
在feign调用的时候,可以将id传递
/**
- @Description: feign相互调用过程中,将请求头传到被调用服务中
*/
@Component
public class FeignRequestInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate requestTemplate) {
//传递日志traceId
String traceId = MDC.get(AuthUtils.TRACE_ID);
// 加入请求来源
requestTemplate.header(AuthUtils.FROM, AuthUtils.GATEWAY);
// 加入Feign调用标志
requestTemplate.header(Global.FEIGN, Global.FEIGN);
// 加入认证信息
AuthTokenData auth = AuthThreadLocal.getAuth();
if (ObjectUtils.isNotEmpty(auth)) {
requestTemplate.header(AuthUtils.AUTH, URLUtil.encode(JsonUtils.obj2String(auth)));
}
if(StringUtils.isNotEmpty(traceId)){
requestTemplate.header(AuthUtils.TRACE_ID, traceId);
}
}
}