Java使用自定义注解和AOP记录接口请求日志

数据库表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
-- 业务日志表
CREATE TABLE "t_log" (
"id" SERIAL NOT NULL,
"operate_class" VARCHAR(64) NULL DEFAULT NULL,
"operate_method" VARCHAR(64) NULL DEFAULT NULL,
"request_params" JSON NULL DEFAULT NULL,
"response_result" JSON NULL DEFAULT NULL,
"description" VARCHAR(500) NOT NULL,
"result" VARCHAR(50) NOT NULL,
"operator_id" VARCHAR(64) NOT NULL,
"operator_name" VARCHAR(64) NOT NULL,
"client_ip" VARCHAR(100) NOT NULL,
"operate_date" TIMESTAMP NOT NULL DEFAULT pg_systimestamp() ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY ("id")
)
COMMENT='业务日志表'
;
COMMENT ON COLUMN "t_log"."id" IS '业务日志ID';
COMMENT ON COLUMN "t_log"."operate_class" IS '操作的接口类名';
COMMENT ON COLUMN "t_log"."operate_method" IS '操作的方法名';
COMMENT ON COLUMN "t_log"."request_params" IS '请求参数';
COMMENT ON COLUMN "t_log"."response_result" IS '返回参数';
COMMENT ON COLUMN "t_log"."description" IS '操作说明';
COMMENT ON COLUMN "t_log"."result" IS '操作结果';
COMMENT ON COLUMN "t_log"."operator_id" IS '操作人id';
COMMENT ON COLUMN "t_log"."operator_name" IS '操作人名称';
COMMENT ON COLUMN "t_log"."client_ip" IS '客户端IP';
COMMENT ON COLUMN "t_log"."operate_date" IS '操作时间';

自定义注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LogAnnotation {
/**
* 操作说明
*/
String description() default "";
/**
* 是否记录请求参数
*/
boolean recordParams() default true;
/**
* 是否记录返回参数
*/
boolean recordResult() default true;

}

AOP切面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
@Aspect
@Component
public class LogAspect {
@Resource
private LogMapper logMapper;

@Around("@annotation(logAnnotation)")
public Object logAround(ProceedingJoinPoint joinPoint, LogAnnotation logAnnotation) throws Throwable {
// 1. 获取方法信息
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
// 2. 构建日志实体
Log log = new Log();
log.setOperateClass(method.getDeclaringClass().getSimpleName());
log.setOperateMethod(method.getName());
log.setDescription(logAnnotation.description());
// 3. 获取请求信息
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
log.setClientIp(getClientIp(request));
// 4. 获取操作人信息
UserInfo sessionUser = HeaderInformation.getUserInfo();
log.setOperatorId(sessionUser.getEuserId());
log.setOperatorName(sessionUser.getUserName());
// 5. 记录请求参数
if (logAnnotation.recordParams()) {
Object[] args = joinPoint.getArgs();
log.setRequestParams(JSON.toJSONString(args));
}
Object result = null;
try {
// 执行目标方法
result = joinPoint.proceed();
log.setResult("SUCCESS");
} catch (Exception e) {
log.setResult("FAIL");
log.setResponseResult(JSON.toJSONString(new ErrorResult(e.getMessage())));
throw e;
}
// 6. 记录返回结果
if (logAnnotation.recordResult() && result != null) {
log.setResponseResult(JSON.toJSONString(result));
}
// 7. 保存到数据库
logMapper.insertSelective(log);
return result;
}

private String getClientIp(HttpServletRequest request) {
String ip = request.getHeader("X-Forwarded-For");
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return ip;
}

// 错误结果封装类
private static class ErrorResult {

private String error;

public ErrorResult(String error) {
this.error = error;
}

public String getError() {
return error;
}
}
}

使用示例

方法中增加@LogAnnotation注解即可:

1
2
3
4
5
6
7
@Operation(summary ="删除附件")
@PostMapping("/deleteAttach")
@LogAnnotation(description = "触发流程附件-删除附件接口")
public BaseResult deleteAttach(@RequestBody @Valid DeleteAttachDto dto) {
flowAttachService.deleteAttach(dto);
return new BaseResult();
}