AOP实现请求日志
使用AOP实现请求和返回日志打印
# 一、自定义注解
/***
* @Description: 接口日志注解
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ApiLog {
/**
* 日志描述
*
* @return
*/
String value() default "";
/**
* kafka 主题
*
* @return
*/
String topic() default "";
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 二、AOP拦截处理
import com.alibaba.fastjson.JSONObject;
import com.cloudwarehouse.core.log.annotation.ApiLog;
import com.cloudwarehouse.core.log.kafka.KafkaSender;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;
/***
* @Description: 系统接口日志切片
*/
@Aspect
@Component
@Slf4j
public class ApiLogAspect {
@Autowired
private KafkaSender<JSONObject> kafkaSender;
// 申明一个切点 里面是 annotation
@Pointcut("@annotation(com.XXX.ApiLog)")
private void serviceAspect() {
}
@Around(value = "serviceAspect()")
public Object methodAround(ProceedingJoinPoint joinPoint) throws Throwable {
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = requestAttributes.getRequest();
JSONObject jsonObject = new JSONObject();
// 接口描述
String value = getSystemApiLogMethodAnnotation((ProceedingJoinPoint) joinPoint, "value");
// kafka消息主题
String topic = getSystemApiLogMethodAnnotation((ProceedingJoinPoint) joinPoint, "topic");
// 请求时间 // 设置日期格式
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
jsonObject.put("request_time", df.format(new Date()));
// 请求URL
jsonObject.put("request_url", request.getRequestURL().toString());
// 请求的方法
jsonObject.put("request_method", request.getMethod());
// 请求类方法
jsonObject.put("signature", joinPoint.getSignature().getName());
// 请求参数
jsonObject.put("request_args", getRequestParams(joinPoint));
// 请求时间
long beginTime = System.currentTimeMillis();
// 执行目标方法
Object result = joinPoint.proceed();
// 执行时长(毫秒)
long total_time = System.currentTimeMillis() - beginTime;
JSONObject requestJsonObject = new JSONObject();
requestJsonObject.put("description", value);
requestJsonObject.put("request", jsonObject);
requestJsonObject.put("response", result);
requestJsonObject.put("total_time", total_time);
// 打印日志到本地
printLocalLog((ProceedingJoinPoint) joinPoint, JSONObject.toJSONString(result), total_time);
// if (!topic.isEmpty()) {
// // 发送到MQ Kafka消息中间件
// kafkaSender.send(topic, requestJsonObject);
// }
return result;
}
@AfterThrowing(pointcut = "serviceAspect()", throwing = "e")
public void logAfterThrowing(JoinPoint joinPoint, Throwable e) throws Exception {
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = requestAttributes.getRequest();
JSONObject jsonObject = new JSONObject();
// 接口描述
String value = getSystemApiLogMethodAnnotation((ProceedingJoinPoint) joinPoint, "value");
// kafka消息主题
String topic = getSystemApiLogMethodAnnotation((ProceedingJoinPoint) joinPoint, "topic");
// 请求时间
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
jsonObject.put("request_time", df.format(new Date()));
// 请求URL
jsonObject.put("request_url", request.getRequestURL().toString());
// 请求的方法
jsonObject.put("request_method", request.getMethod());
// 请求类方法
jsonObject.put("signature", joinPoint.getSignature().getName());
// 请求参数
jsonObject.put("request_args", getRequestParams(joinPoint));
// 请求时间
long beginTime = System.currentTimeMillis();
Object result = e.getMessage();
// 执行时长(毫秒)
long total_time = System.currentTimeMillis() - beginTime;
JSONObject requestJsonObject = new JSONObject();
requestJsonObject.put("description", value);
requestJsonObject.put("request", jsonObject);
requestJsonObject.put("response", result);
requestJsonObject.put("total_time", total_time);
// 打印日志到本地
printLocalLog((ProceedingJoinPoint) joinPoint, JSONObject.toJSONString(result), total_time);
// if (!topic.isEmpty()) {
// // 发送到MQ Kafka消息中间件
// kafkaSender.send(topic, requestJsonObject);
// }
}
public void printLocalLog(ProceedingJoinPoint joinPoint, String response_result, long total_time) throws Exception {
String value = getSystemApiLogMethodAnnotation(joinPoint, "value");
String method = joinPoint.getSignature().getName();
StringBuilder request_params = new StringBuilder();
if(joinPoint.getArgs().length > 0){
for (int i = 0; i <joinPoint.getArgs().length; i++) {
request_params.append(JSONObject.toJSONString(joinPoint.getArgs()[i]));
}
}
log.info("{},调用[{}] 方法开始", value, method);
log.info("{},请求参数:{}", value, request_params);
log.info("{},响应结果:{}", value, response_result);
log.info("{},调用[{}] 方法结束,耗时{}毫秒", value, method, total_time);
}
/**
* 获取请求参数
*
* @param joinPoint
* @return
*/
public Object getRequestParams(JoinPoint joinPoint) {
StringBuilder request_params = new StringBuilder();
if(joinPoint.getArgs().length > 0){
for (int i = 0; i <joinPoint.getArgs().length; i++) {
request_params.append(JSONObject.toJSONString(joinPoint.getArgs()[i]));
}
}
return request_params;
}
/**
* 获取注解内容
*
* @param point
* @param attribute
* @return
* @throws Exception
*/
public String getSystemApiLogMethodAnnotation(ProceedingJoinPoint point, String attribute) throws Exception {
//获取连接点目标类名
String targetName = point.getTarget().getClass().getName();
//获取连接点签名的方法名
String methodName = point.getSignature().getName();
//获取连接点参数
Object[] args = point.getArgs();
//根据连接点类的名字获取指定类
Class targetClass = Class.forName(targetName);
//获取类里面的方法
Method[] methods = targetClass.getMethods();
String result = "";
for (Method method : methods) {
if (method.getName().equals(methodName)) {
Class[] clazzs = method.getParameterTypes();
if (clazzs.length == args.length) {
if ("value".equals(attribute)) {
result = method.getAnnotation(ApiLog.class).value();
} else if ("topic".equals(attribute)) {
result = method.getAnnotation(ApiLog.class).topic();
}
break;
}
}
}
return result;
}
}
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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
# 三、kafka
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.kafka.support.SendResult;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.util.concurrent.ListenableFuture;
import org.springframework.util.concurrent.ListenableFutureCallback;
/***
* @Description: 日志采集 Kafka生产者
*/
@Component
@Slf4j
public class KafkaSender<T> {
@Autowired
private KafkaTemplate<String, Object> kafkaTemplate;
/**
* kafka 发送消息
* @param log_topic 主题
* @param obj 消息对象
*/
@Async
public void send(String log_topic, T obj) {
String jsonObj = JSON.toJSONString(obj);
// log.info("Produce: 消息发送到Kafka:{}", jsonObj);
// 发送消息 实现可配置化 主题是可配置化
ListenableFuture<SendResult<String, Object>> future = kafkaTemplate.send(log_topic, jsonObj);
future.addCallback(new ListenableFutureCallback<SendResult<String, Object>>() {
@Override
public void onFailure(Throwable throwable) {
log.info("Produce: 消息发送到Kafka失败:" + throwable.getMessage());
}
@Override
public void onSuccess(SendResult<String, Object> stringObjectSendResult) {
// TODO 业务处理
log.info("Produce: 消息发送到Kafka成功:{}", stringObjectSendResult.toString());
}
});
}
}
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
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
上次更新: 2022/08/30, 13:59:56