利用Spring AOP为项目增加日志追踪

需求

给项目加上日志追踪


代码

废话不多说直接放码

日志实体类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package xx.xx.xx.xx.log.entity;

import java.util.Date;

public class NcmLog{
private static final long serialVersionUID = 1L;
private String username; // 用户名
private String module; // 执行模块
private String methods; // 执行方法
private String description; //操作描述
private String content; // 操作内容
private String actionurl; // 请求url
private String ip; // IP地址
private Date date; // 执行时间
private String commite; // 执行描述(1:执行成功、2:执行失败)
private String show; // 是否显示(1:显示2:不显示)

}

日志注解

1
2
3
4
5
6
7
8
9
10
11
12
13
package xx.xx.xx.xx.log.annotation;

import java.lang.annotation.*;

@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface OptionalLog {
String module() default ""; //模块名
String methods() default "";//方法名
String description() default "";//执行描述
boolean show() default true;//是否显示
}

自定义Spring 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
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
package xx.xx.xx.xx.log.web;

import com.alibaba.fastjson.JSONObject;
import xx.xx.xx.xx.context.ThreadLocalUtil;
import xx.xx.xx.xx.annotation.OptionalLog;
import xx.xx.xx.xx.entity.NcmLog;
import xx.xx.xx.xx.service.NcmLogService;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@Aspect
public class LogAopAction {
// 注入service,用来将日志信息保存在数据库
@Resource(name = "ncmLogService")
private NcmLogService logService;

// 配置接入点,即为所要记录的action操作目录

@Pointcut("execution(* gov.cnao.ac.ncm.*.web..*.*(..))")
private void controllerAspect() {
}

@Around("controllerAspect()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
//日志实体对象
NcmLog logBo = new NcmLog();

HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
.getRequest();
// 获取用户名
String username = "";
if (ThreadLocalUtil.getContextUser() == null){
return null;
} else {
username = ThreadLocalUtil.getContextUser().getUserName();
}
logBo.setUsername(username);
//获取系统当前时间
logBo.setDate(new Date());

// 获取访问真实IP
String ipAddress = request.getHeader("x-forwarded-for");
if(ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("Proxy-Client-IP");
}
if(ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("WL-Proxy-Client-IP");
}
if(ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getRemoteAddr();
if(ipAddress.equals("127.0.0.1") || ipAddress.equals("0:0:0:0:0:0:0:1")){
//根据网卡取本机配置的IP
InetAddress inet=null;
try {
inet = InetAddress.getLocalHost();
} catch (UnknownHostException e) {
e.printStackTrace();
}
ipAddress= inet.getHostAddress();
}
}
//对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
if(ipAddress!=null && ipAddress.length()>15){ //"***.***.***.***".length() = 15
if(ipAddress.indexOf(",")>0){
ipAddress = ipAddress.substring(0,ipAddress.indexOf(","));
}
}

logBo.setIp(ipAddress);

// 拦截的实体类,就是当前正在执行的controller
Object target = pjp.getTarget();
// 拦截的方法名称。当前正在执行的方法
String methodName = pjp.getSignature().getName();
// 拦截的方法参数
Object[] args = pjp.getArgs();

//获取请求路径
String actionUrl = request.getRequestURI();

// 拦截的放参数类型
Signature sig = pjp.getSignature();
MethodSignature msig = null;
if (!(sig instanceof MethodSignature)) {
throw new IllegalArgumentException("该注解只能用于方法");
}
msig = (MethodSignature) sig;
Class[] parameterTypes = msig.getMethod().getParameterTypes();

Object object = null;
// 获得被拦截的方法
Method method = null;


try {
method = target.getClass().getMethod(methodName, parameterTypes);
} catch (Exception e1) {
e1.printStackTrace();
}
if (null != method) {
// 获取方法(此为自定义注解)
OptionalLog op = method.getAnnotation(OptionalLog.class);

// 获取注解的modules 设为操作模块
logBo.setModule(op.module());
// 获取注解的methods 设为执行方法
logBo.setMethods(op.methods());
logBo.setShow(op.show()?"1":"2");
logBo.setDescription(op.description());
// 将上面获取到的请求路径 设为请求路径
logBo.setActionurl(actionUrl);
try {
object = pjp.proceed();
//接受客户端的数据
Map<String,String[]> map = request.getParameterMap();
// 解决获取参数乱码
Map<String,String[]> newmap = new HashMap<String,String[]>();
for(Map.Entry<String, String[]> entry : map.entrySet()){
String name = entry.getKey();
String values[] = entry.getValue();
if(values==null){
newmap.put(name, new String[]{});
continue;
}
String newvalues[] = new String[values.length];
for(int i=0; i<values.length;i++){
String value = values[i];
value = new String(value.getBytes("iso8859-1"),request.getCharacterEncoding());
newvalues[i] = value; //解决乱码后封装到Map中
}

newmap.put(name, newvalues);

}
logBo.setContent(JSONObject.toJSONString(newmap));
//1为执行成功
logBo.setCommite("1");
// 添加到数据库
logService.save(logBo);
} catch (Throwable e) {

//接受客户端的数据
Map<String,String[]> map = request.getParameterMap();
// 解决获取参数乱码
Map<String,String[]> newmap = new HashMap<String,String[]>();
for(Map.Entry<String, String[]> entry : map.entrySet()){
String name = entry.getKey();
String values[] = entry.getValue();

if(values==null){
newmap.put(name, new String[]{});
continue;
}
String newvalues[] = new String[values.length];
for(int i=0; i<values.length;i++){
String value = values[i];
value = new String(value.getBytes("iso8859-1"),request.getCharacterEncoding());
newvalues[i] = value; //解决乱码后封装到Map中
}

newmap.put(name, newvalues);

}
//MapperUtil.toJsonStr为自定义的转换工具类
logBo.setContent(JSONObject.toJSONString(newmap));
//2为执行失败
logBo.setCommite("2");
//添加到数据库
logService.save(logBo);
}
}
return object;
}
}

spring-mvc.xml中注入bean

1
2
3
<!-- 操作日志配置 指定扫描aop执行操作的类 -->
<aop:aspectj-autoproxy proxy-target-class="true" />
<bean id="logAopAction" class="xx.xx.xx.xx.log.web.LogAopAction"/>

在请求上加上@OptionalLog注解

1
2
3
4
5
@OptionalLog(description="查看行政区划列表")
@RequestMapping(value = "list")
public String list() {
return "areas/areasList";
}