前言:
之前想在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