一:添加AOP依赖
1
2
3
4
5 1<dependency>
2 <groupId>org.springframework.boot</groupId>
3 <artifactId>spring-boot-starter-aop</artifactId>
4</dependency>
5
二:创建自定义注解和切面
创建core→aop文件夹
在aop文件夹下中创建注解,其中remark为记录的备注
1
2
3
4
5
6
7
8
9
10
11
12 1package com.example.demo.core.aop;
2
3import java.lang.annotation.*;
4
5@Target(ElementType.METHOD)
6@Retention(RetentionPolicy.RUNTIME)
7@Documented
8public @interface AnnotationLog {
9 String remark() default "";
10
11}
12
创建切面
注:关于SystemLog类,之前集成generator自动生成model,xml,dao功能这篇文章提到过的,忘记的可以再去看一下
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 1package com.example.demo.core.aop;
2
3import com.alibaba.fastjson.JSON;
4import com.example.demo.core.systemlog.SystemLogQueue;
5import com.example.demo.core.ret.ServiceException;
6import com.example.demo.core.utils.ApplicationUtils;
7import com.example.demo.model.SystemLog;
8import com.example.demo.model.UserInfo;
9import com.example.demo.service.SystemLogService;
10import org.apache.ibatis.javassist.*;
11import org.apache.ibatis.javassist.bytecode.CodeAttribute;
12import org.apache.ibatis.javassist.bytecode.LocalVariableAttribute;
13import org.apache.ibatis.javassist.bytecode.MethodInfo;
14import org.apache.shiro.SecurityUtils;
15import org.aspectj.lang.JoinPoint;
16import org.aspectj.lang.annotation.*;
17import org.slf4j.Logger;
18import org.slf4j.LoggerFactory;
19import org.springframework.stereotype.Component;
20import org.springframework.web.context.request.RequestContextHolder;
21import org.springframework.web.context.request.ServletRequestAttributes;
22
23import javax.annotation.Resource;
24import javax.servlet.http.HttpServletRequest;
25import javax.servlet.http.HttpServletResponse;
26import java.lang.reflect.Method;
27import java.util.Date;
28import java.util.HashMap;
29import java.util.Map;
30
31/**
32 * @Description: aop记录操作日志
33 * @author phubing
34 * @date 2019年5月28日 18:43:28
35 */
36@Aspect
37@Component
38public class AspectLog {
39
40 private static final Logger logger = LoggerFactory.getLogger(AspectLog.class);
41
42 @Resource
43 private SystemLogService systemLogService;
44
45 /**
46 * 定义切点
47 */
48 @Pointcut("@annotation(com.example.demo.core.aop.AnnotationLog)")
49 public void methodCachePointcut() {
50 }
51
52 @Before("methodCachePointcut()")
53 public void doBefore(JoinPoint p) throws Exception{
54 SystemLog systemLog = getSystemLogInit(p);
55 systemLog.setLogType(SystemLog.LOGINFO);
56 systemLogService.insert(systemLog);
57 }
58
59 /**
60 * 调用后的异常处理
61 * @param p
62 * @param e
63 */
64 @AfterThrowing(pointcut = "methodCachePointcut()", throwing = "e")
65 public void doAfterThrowing(JoinPoint p, Throwable e) throws Throwable {
66 //业务异常不用记录
67 if(!(e instanceof ServiceException)) {
68 try {
69 SystemLog systemLog =getSystemLogInit(p);
70 systemLog.setLogType(SystemLog.LOGERROR);
71 systemLog.setExceptionCode(e.getClass().getName());
72 systemLog.setExceptionDetail(e.getMessage());
73 systemLogService.insert(systemLog);
74 } catch (Exception ex) {
75 logger.error("==异常通知异常==");
76 logger.error("异常信息:{}", ex.getMessage());
77 }
78 }
79 }
80
81 private SystemLog getSystemLogInit(JoinPoint p){
82 SystemLog systemLog = new SystemLog();
83 try{
84 //类名
85 String targetClass = p.getTarget().getClass().toString();
86 //请求的方法名
87 String tartgetMethod = p.getSignature().getName();
88 //获取类名 UserController
89 String classType = p.getTarget().getClass().getName();
90 Class<?> clazz = Class.forName(classType);
91 String clazzName = clazz.getName();
92 //请求参数名+参数值的map
93 Map<String, Object> nameAndArgs = getFieldsName(this.getClass(), clazzName, tartgetMethod, p.getArgs());
94 systemLog.setId(ApplicationUtils.getUUID());
95 systemLog.setDescription(getMthodRemark(p));
96 systemLog.setMethod(targetClass+"."+tartgetMethod);
97 //大家可自行百度获取ip的方法
98 systemLog.setRequestIp("192.168.1.104");
99 systemLog.setParams(JSON.toJSONString(nameAndArgs));
100 systemLog.setUserId(getUserId());
101 systemLog.setCreateTime(new Date());
102 }catch (Exception ex){
103 logger.error("==异常通知异常==");
104 logger.error("异常信息:{}", ex.getMessage());
105 }
106 return systemLog;
107 }
108
109 /**
110 * 通过反射机制 获取被切参数名以及参数值
111 *
112 * @param cls
113 * @param clazzName
114 * @param methodName
115 * @param args
116 * @return
117 * @throws NotFoundException
118 */
119 private Map<String, Object> getFieldsName(Class cls, String clazzName, String methodName, Object[] args) throws NotFoundException {
120 Map<String, Object> map = new HashMap<>();
121 ClassPool pool = ClassPool.getDefault();
122 ClassClassPath classPath = new ClassClassPath(cls);
123 pool.insertClassPath(classPath);
124 CtClass cc = pool.get(clazzName);
125 CtMethod cm = cc.getDeclaredMethod(methodName);
126 MethodInfo methodInfo = cm.getMethodInfo();
127 CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
128 LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);
129 int pos = Modifier.isStatic(cm.getModifiers()) ? 0 : 1;
130 for (int i = 0; i < cm.getParameterTypes().length; i++) {
131 //HttpServletRequest 和HttpServletResponse 不做处理
132 if(!(args[i] instanceof HttpServletRequest || args[i] instanceof HttpServletResponse)){
133 //paramNames即参数名
134 map.put(attr.variableName(i + pos), JSON.toJSONString(args[i]));
135 }
136 }
137 return map;
138 }
139
140 /**
141 * 获取方法的中文备注____用于记录用户的操作日志描述
142 * @param joinPoint
143 * @return
144 * @throws Exception
145 */
146 private static String getMthodRemark(JoinPoint joinPoint) throws Exception {
147 String targetName = joinPoint.getTarget().getClass().getName();
148 String methodName = joinPoint.getSignature().getName();
149 Object[] arguments = joinPoint.getArgs();
150 Class targetClass = Class.forName(targetName);
151 Method[] method = targetClass.getMethods();
152 String methode = "";
153 for (Method m : method) {
154 if (m.getName().equals(methodName)) {
155 Class[] tmpCs = m.getParameterTypes();
156 if (tmpCs.length == arguments.length) {
157 AnnotationLog methodCache = m.getAnnotation(AnnotationLog.class);
158 if (methodCache != null) {
159 methode = methodCache.remark();
160 }
161 break;
162 }
163 }
164 }
165 return methode;
166 }
167
168 private static String getUserId() {
169 String userId = "";
170 UserInfo userInfo = (UserInfo) SecurityUtils.getSubject().getPrincipal();
171 if(userInfo != null){
172 userId = userInfo.getId();
173 }
174 return userId;
175 }
176
177
178
179}
180
三:用法
在UserInfoController的selectById上添加我们刚创建的注解
1
2
3
4
5
6
7 1@PostMapping("/selectById")
2@AnnotationLog(remark = "查询")
3public RetResult<UserInfo> selectById(@RequestParam String id) {
4 UserInfo userInfo = userInfoService.selectById(id);
5 return RetResponse.makeOKRsp(userInfo);
6}
7
四:测试
输入localhost:8080/userInfo/selectById
拿到结果
查看数据库System_log
可以看到我们的日志
五:优化
我们知道,日志系统主要是方便我们分析程序,定位异常的。但是对于用户来说,用户一点都不在乎,所以我们不能因为需要记录日志,而延长用户的等待时间,所以,这里我们创建队列来异步执行记录日志操作
1:创建批量添加方法
SystemLogMapper.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 1<insert id="insertByBatch" parameterType="java.util.List" >
2 insert into system_log (
3 id, description, method, log_type, request_ip, exception_code,
4 exception_detail, params, user_id, create_time
5 )
6 values
7 <foreach collection="list" item="item" index= "index" separator =",">
8 (
9 #{item.id,jdbcType=VARCHAR},
10 #{item.description,jdbcType=VARCHAR},
11 #{item.method,jdbcType=VARCHAR},
12 #{item.logType,jdbcType=VARCHAR},
13 #{item.requestIp,jdbcType=VARCHAR},
14 #{item.exceptionCode,jdbcType=VARCHAR},
15 #{item.exceptionDetail,jdbcType=VARCHAR},
16 #{item.params,jdbcType=VARCHAR},
17 #{item.userId,jdbcType=VARCHAR},
18 #{item.createTime,jdbcType=TIMESTAMP}
19 )
20 </foreach>
21
SystemLogMapper.java
1
2 1Integer insertByBatch(List<SystemLog> list);
2
Service层可参考码云地址,这里就不做示例
2:创建日志的存放队列
创建core→systemlog文件夹
在该文件夹下创建SystemLogQueue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 1package com.example.demo.core.systemlog;
2
3import com.example.demo.model.SystemLog;
4import org.springframework.stereotype.Component;
5
6import java.util.concurrent.BlockingQueue;
7import java.util.concurrent.LinkedBlockingQueue;
8import java.util.concurrent.TimeUnit;
9
10@Component
11public class SystemLogQueue {
12
13 private BlockingQueue<SystemLog> blockingQueue = new LinkedBlockingQueue<>();
14
15 public void add(SystemLog systemLog) {
16 blockingQueue.add(systemLog);
17 }
18
19 public SystemLog poll() throws InterruptedException {
20 return blockingQueue.poll(1, TimeUnit.SECONDS);
21 }
22}
23
24
3:创建日志队列的消费者
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 1package com.example.demo.core.systemlog;
2
3import com.example.demo.model.SystemLog;
4import com.example.demo.service.SystemLogService;
5import org.slf4j.Logger;
6import org.slf4j.LoggerFactory;
7import org.springframework.stereotype.Component;
8
9import javax.annotation.PostConstruct;
10import javax.annotation.PreDestroy;
11import javax.annotation.Resource;
12import java.util.ArrayList;
13import java.util.List;
14
15@Component
16public class SystemLogConsumer implements Runnable{
17
18 private static Logger logger = LoggerFactory.getLogger(SystemLogConsumer.class);
19
20 public static final int DEFAULT_BATCH_SIZE = 64;
21
22 private SystemLogQueue auditLogQueue;
23
24 private SystemLogService systemLogService;
25
26 private int batchSize = DEFAULT_BATCH_SIZE;
27
28 private boolean active = true;
29
30 private Thread thread;
31
32 @PostConstruct
33 public void init() {
34 thread = new Thread(this);
35 thread.start();
36 }
37
38 @PreDestroy
39 public void close() {
40 active = false;
41 }
42
43 @Override
44 public void run() {
45 while (active) {
46 execute();
47 }
48 }
49
50 public void execute() {
51 List<SystemLog> systemLogs = new ArrayList<>();
52 try {
53 int size = 0;
54 while (size < batchSize) {
55 SystemLog systemLog = auditLogQueue.poll();
56 if (systemLog == null) {
57 break;
58 }
59 systemLogs.add(systemLog);
60 size++;
61 }
62 } catch (Exception ex) {
63 logger.info(ex.getMessage(), ex);
64 }
65 if (!systemLogs.isEmpty()) {
66 try {
67 //休眠10秒来模拟业务复杂,正在计算,测试之后大家别忘记删除这句话
68 Thread.sleep(10000);
69 systemLogService.insertByBatch(systemLogs);
70 }catch (Exception e){
71 logger.error("异常信息:{}", e.getMessage());
72 }
73
74 }
75 }
76
77 @Resource
78 public void setAuditLogQueue(SystemLogQueue auditLogQueue) {
79 this.auditLogQueue = auditLogQueue;
80 }
81 @Resource
82 public void setAuditLogService(SystemLogService systemLogService) {
83 this.systemLogService = systemLogService;
84 }
85
86 public void setBatchSize(int batchSize) {
87 this.batchSize = batchSize;
88 }
89
90
91
92}
93
94
4:修改切面AspectLog
将:
1
2
3 1@Resourc
2 private SystemLogService systemLogService;
3
修改为:
1
2
3 1@Resource
2private SystemLogQueue systemLogQueue;
3
将:
1
2 1systemLogQueue.insert(systemLog);
2
修改为:
1
2 1systemLogQueue.add(systemLog);
2
5:测试
输入localhost:8080/userInfo/selectById
注意:这里是立刻返回结果,并没有等待10秒,10秒之后打开数据库,得到我们刚操作的日志