AOP统一接口日志打印
1.前言
在后端开发中,记录接口调用日志作为事后排查问题的关键手段,特别是在生产的环境当中,因为无法通过本地调试获取生成环境,导致无法精确的定位问题的所在。所以,使用AOP来记录接口的入参出参以及调用时间,是运维的必要手段。
2.引入依赖
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
<scope>runtime</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjrt -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.6</version>
<scope>runtime</scope>
</dependency>
3.日志记录
自处我们使用@Around注解。代码如下
@Aspect
@Component
@Slf4j
public class GlobalLog {
// 定制日志显示颜色
public static final String CYAN_BOLD = "\033[1;36m";
public static final String YELLOW = "\033[0;33m";
public static final String RED_BOLD = "\033[1;31m";
public static final String RESET = "\033[0m";
@Around("execution(* com.achao.music.controller..*.*(..))")
public Object globalLog(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
// 获取调用地址
String uri = getRestApi(method);
// 获取接口描述(基于swagger3注解)
String description = getOperationDescription(method);
// 获取接口入参
String param = toPrettyJsonString(joinPoint.getArgs());
log.info("{}统一接口记录--接口{}-{}\n入参:{}{}", YELLOW, uri, description, param, RESET);
try {
Object result = joinPoint.proceed();
log.info("{}统一接口返回记录--接口{}-{}\n入参:{}\n返回:\n{}{}", CYAN_BOLD, uri, description, param, toPrettyJsonString(result), RESET);
return result;
} catch (Throwable throwable) {
log.error("{}统一异常记录--接口{}-{}\n入参:{}\n发生异常{}\n详细:{}{}", RED_BOLD, uri, description, param, throwable.getMessage(), throwable, RESET);
throw throwable;
}
}
private String getRestApi(Method method) {
Class<?> declaringClass = method.getDeclaringClass();
RequestMapping requestMapping = declaringClass.getAnnotation(RequestMapping.class);
// 获取当前方法类中的路劲前缀
String apiPrefix = requestMapping != null ? Arrays.toString(requestMapping.value()) : "";
Annotation[] annotations = method.getAnnotations();
String api = "";
// 对不同请求的注解获取其路径值
for (Annotation annotation : annotations) {
String annotationName = annotation.toString().substring(annotation.toString().lastIndexOf('.'));
if (annotationName.contains("RequestMapping")) {
RequestMapping mapping = method.getAnnotation(RequestMapping.class);
api = apiPrefix + "/" + Arrays.toString(mapping.value());
}
if (annotationName.contains("PostMapping")) {
PostMapping postMapping = method.getAnnotation(PostMapping.class);
api = apiPrefix + "/" + Arrays.toString(postMapping.value());
}
if (annotationName.contains("PutMapping")) {
PutMapping putMapping = method.getAnnotation(PutMapping.class);
api = apiPrefix + "/" + Arrays.toString(putMapping.value());
}
if (annotationName.contains("DeleteMapping")) {
DeleteMapping deleteMapping = method.getAnnotation(DeleteMapping.class);
api = apiPrefix + "/" + Arrays.toString(deleteMapping.value());
}
if (annotationName.contains("GetMapping")) {
GetMapping getMapping = method.getAnnotation(GetMapping.class);
api = apiPrefix + "/" + Arrays.toString(getMapping.value());
}
}
api = api.replaceAll("[\[|\]]", "");
return api;
}
private String getOperationDescription(Method method) {
for (Annotation annotation : method.getAnnotations()) {
String annotationName = annotation.toString().substring(annotation.toString().lastIndexOf('.'));
if (annotationName.contains("Operation")) {
Operation operation = method.getAnnotation(Operation.class);
return operation.description();
}
}
return "";
}
private String toPrettyJsonString(Object object) {
return JSON.toJSONString(object, SerializerFeature.PrettyFormat,SerializerFeature.WriteMapNullValue,
SerializerFeature.WriteDateUseDateFormat);
}
}