前言:
之前想在filter層直接過濾httpServerletRequest請求進行日志處理,但是之后再getWriter()的 時候報already been call異常。查了下,才發現原來流形式的只能讀取一次。。就好像食物,吃了就沒了。。 所以在filter和inteceptor里面是沒法通過獲取request的流來進行日志記錄的。
于是還是準備用通用的方法:controller層aop進行切面記錄日志。
使用Aop記錄操作日志
第一步:添加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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
|
/** * 統一日志處理Handler * @author Mingchenchen * */ public class LogAopHandler { @Autowired private AuditLogDao auditLogDao; /** * controller層面記錄操作日志 * 注意此處是aop:around的 因為需要得到請求前的參數以及請求后接口返回的結果 * @throws Throwable */ public Object doSaveLog(ProceedingJoinPoint joinPoint) throws Throwable { MethodSignature method = (MethodSignature) joinPoint.getSignature(); String methodName = method.getName(); Object[] objects = joinPoint.getArgs(); String requestBody = null ; if (objects!= null && objects.length> 0 ) { for (Object object : objects) { if (object == null ) { requestBody = null ; //POST接口參數為空 比如刪除XXX } else if (object instanceof String) { requestBody = (String) object; //有些接口直接把參數轉換成對象了 } else { requestBody = JSONObject.toJSONString(object); } } } //只記錄POST方法的日志 boolean isNeedSaveLog = false ; //此處不能用getAnnotationByType 是JAVA8的特性,因為注解能夠重名,所以得到的是數組 RequestMapping annotation = method.getMethod().getAnnotation(RequestMapping. class ); for (RequestMethod requestMethod : annotation.method()) { if (requestMethod==RequestMethod.POST) { isNeedSaveLog = true ; } } JSONObject requestBodyJson = null ; try { requestBodyJson = JSONObject.parseObject(requestBody); } catch (Exception e) { //do nothing 即POST請求沒傳body } HttpServletRequest request = RequestContextUtil.getRequestByCurrentContext(); String userName = RequestContextUtil.getUserNameByCurrentContext(); if (StringUtil.isEmpty(userName)) { try { userName = DmsCache.get(requestBodyJson.getString( "userName" )).getName(); } catch (Exception e) { userName = RequestContextUtil.getAsynUserInfoByAutoDeploy().getName(); } } //得到request的參數后讓方法執行它 //注意around的情況下需要返回result 否則將不會返回值給請求者 Object result = joinPoint.proceed(objects); try { JSONObject resultJson = JSONObject.parseObject(result.toString()); if (isNeedSaveLog) { //如果是POST請求 則記錄日志 LogTypeEnum logTypeEnum = LogTypeEnum.getDesByMethodName(methodName); if (logTypeEnum != null ) { AuditLogEntity auditLogEntity = new AuditLogEntity(); auditLogEntity.setUuid(StringUtil.createRandomUuid()); auditLogEntity.setOperator(userName); auditLogEntity.setRequestIp(request.getRemoteAddr()); auditLogEntity.setRequestUrl(request.getRequestURI().replace( "/cloud-master" , "" )); auditLogEntity.setEventType(logTypeEnum.getKey()); auditLogEntity.setEventDesc(logTypeEnum.getDescription()); auditLogEntity.setRequest(requestBody); int isSuccess = "200" .equals(resultJson.getString( "code" )) ? 1 : 0 ; auditLogEntity.setSuccessFlag(isSuccess); auditLogEntity.setResponse(result.toString()); auditLogEntity.setCreateTime( new Date()); auditLogDao.insert(auditLogEntity); } } } catch (Exception e) { e.printStackTrace(); } return result; } } |
第二步:在spring的xml中聲明
1
2
3
4
5
6
7
8
|
<!-- 記錄操作日志 --> < bean id = "operationLogAop" class = "com.ming.learn.core.aop.LogAopHandler" /> < aop:config > < aop:aspect id = "logAOP" ref = "operationLogAop" > < aop:pointcut id = "target" expression = "execution(* com.ming.learn..*Controller.*(..))" /> < aop:around method = "doSaveLog" pointcut-ref = "target" /> </ aop:aspect > </ aop:config > |
如此一來,核心步驟就完成了,剩下的就是自己組裝需要記錄的東西了。
第三步:寫Dao、Entity、Mapper
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
|
import java.util.Date; import javax.persistence.Column; import javax.persistence.Id; import javax.persistence.Table; /** * 日志審計 * @author Mingchenchen * */ @Table (name= "audit_log" ) public class AuditLogEntity { @Id private String uuid; @Column (name= "event_type" ) private String eventType; //事件類型 @Column (name= "event_desc" ) private String eventDesc; //事件中文描述 @Column (name= "operator" ) private String operator; //操作者 @Column (name= "request_ip" ) private String requestIp; //客戶端地址 @Column (name= "request_url" ) private String requestUrl; //請求地址 @Column (name= "request" ) private String request; //請求body @Column (name= "response" ) private String response; //請求返回值 @Column (name= "create_time" ) private Date createTime; public String getUuid() { return uuid; } public void setUuid(String uuid) { this .uuid = uuid; } public String getEventType() { return eventType; } public void setEventType(String eventType) { this .eventType = eventType; } public String getEventDesc() { return eventDesc; } public void setEventDesc(String eventDesc) { this .eventDesc = eventDesc; } public String getOperator() { return operator; } public void setOperator(String operator) { this .operator = operator; } public String getRequestIp() { return requestIp; } public void setRequestIp(String requestIp) { this .requestIp = requestIp; } public String getRequestUrl() { return requestUrl; } public void setRequestUrl(String requestUrl) { this .requestUrl = requestUrl; } public String getRequest() { return request; } public void setRequest(String request) { this .request = request; } public String getResponse() { return response; } public void setResponse(String response) { this .response = response; } public Date getCreateTime() { return createTime; } public void setCreateTime(Date createTime) { this .createTime = createTime; } } |
第四步:根據Controller的方法名稱定制響應的事件類型
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
|
import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * 操作日志類型 * @author Mingchenchen * */ public enum LogTypeEnum { //用戶 COMMON_LOGIN( "login" , "login" , "登錄" ); //其他 private String methodName; //方法名稱與controller一致 private String key; //保存到數據庫的事件類型 private String description; //保存到數據庫的描述 private LogTypeEnum(String methodName,String key,String description){ this .methodName = methodName; this .key = key; this .description = description; } public String getMethodName() { return methodName; } public void setMethodName(String methodName) { this .methodName = methodName; } public String getKey() { return key; } public void setKey(String key) { this .key = key; } public String getDescription() { return description; } public void setDescription(String description) { this .description = description; } /** * 根據方法名返回 * @param methodName * @return */ public static LogTypeEnum getDesByMethodName(String methodName){ return innerMap.map.get(methodName); } /** * 內部類 用戶保存所有的enum 無須通過Enum.values()每次遍歷 * @author Mingchenchen * */ private static class innerMap{ private static Map<String, LogTypeEnum> map = new ConcurrentHashMap<>( 128 ); static { //初始化整個枚舉類到Map for (LogTypeEnum logTypeEnum : LogTypeEnum.values()) { map.put(logTypeEnum.getMethodName(), logTypeEnum); } } } } |
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。
原文鏈接:http://blog.csdn.net/jinzhencs/article/details/51882751