xuefei
2020-12-10 eeeb7233935ea9b10e99043bdbf740ef86c9bf20
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
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
package cn.cetc54.platform.core.common.aop;
 
import cn.cetc54.platform.core.common.annotation.SystemLog;
import cn.cetc54.platform.core.common.utils.IpInfoUtil;
import cn.cetc54.platform.core.common.utils.ObjectUtil;
import cn.cetc54.platform.core.common.utils.ThreadPoolUtil;
import cn.cetc54.platform.core.entity.Log;
import cn.cetc54.platform.core.entity.elasticsearch.EsLog;
import cn.cetc54.platform.core.service.LogService;
import cn.cetc54.platform.core.service.UserService;
import cn.cetc54.platform.core.service.elasticsearch.EsLogService;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.NamedThreadLocal;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
 
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
 
/**
 * Spring AOP实现日志管理
 * @author
 */
@Aspect
@Component
@Slf4j
public class SystemLogAspect {
 
    private static final ThreadLocal<Date> beginTimeThreadLocal = new NamedThreadLocal<Date>("ThreadLocal beginTime");
 
    @Value("${platform.logRecord.es:false}")
    private Boolean esRecord;
 
    @Autowired
    private EsLogService esLogService;
 
    @Autowired
    private LogService logService;
 
    @Autowired
    private UserService userService;
 
    @Autowired(required = false)
    private HttpServletRequest request;
 
    @Autowired
    private IpInfoUtil ipInfoUtil;
 
    /**
     * Controller层切点,注解方式
     */
    //@Pointcut("execution(* *..controller..*Controller*.*(..))")
    @Pointcut("@annotation(cn.cetc54.platform.core.common.annotation.SystemLog)")
    public void controllerAspect() {
 
    }
 
    /**
     * 前置通知 (在方法执行之前返回)用于拦截Controller层记录用户的操作的开始时间
     * @param joinPoint 切点
     * @throws InterruptedException
     */
    @Before("controllerAspect()")
    public void doBefore(JoinPoint joinPoint) throws InterruptedException{
 
        //线程绑定变量(该数据只有当前请求的线程可见)
        Date beginTime = new Date();
        beginTimeThreadLocal.set(beginTime);
    }
 
 
    /**
     * 后置通知(在方法执行之后并返回数据) 用于拦截Controller层无异常的操作
     * @param joinPoint 切点
     */
    @AfterReturning("controllerAspect()")
    public void after(JoinPoint joinPoint){
        try {
            String username = "";
            String description = getControllerMethodInfo(joinPoint).get("description").toString();
            int type = (int)getControllerMethodInfo(joinPoint).get("type");
            Map<String, String[]> logParams = request.getParameterMap();
            Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
            if("anonymousUser".equals(principal.toString())){
                return;
            }
            UserDetails user = (UserDetails) principal;
            username = user.getUsername();
 
            if(esRecord){
                EsLog esLog = new EsLog();
 
                //日志标题
                esLog.setName(description);
                //日志类型
                esLog.setLogType(type);
                //日志请求url
                esLog.setRequestUrl(request.getRequestURI());
                //请求方式
                esLog.setRequestType(request.getMethod());
                //请求参数
                esLog.setMapToParams(logParams);
                //请求用户
                esLog.setUsername(username);
                //请求IP
                esLog.setIp(ipInfoUtil.getIpAddr(request));
                //IP地址
                esLog.setIpInfo(ipInfoUtil.getIpCity(request));
                //请求开始时间
                Date logStartTime = beginTimeThreadLocal.get();
 
                long beginTime = beginTimeThreadLocal.get().getTime();
                long endTime = System.currentTimeMillis();
                //请求耗时
                Long logElapsedTime = endTime - beginTime;
                esLog.setCostTime(logElapsedTime.intValue());
                ipInfoUtil.getInfo(request, ObjectUtil.mapToStringAll(request.getParameterMap()));
 
                //调用线程保存至ES
                ThreadPoolUtil.getPool().execute(new SaveEsSystemLogThread(esLog, esLogService));
            }else{
                Log log = new Log();
 
                //日志标题
                log.setName(description);
                //日志类型
                log.setLogType(type);
                //日志请求url
                log.setRequestUrl(request.getRequestURI());
                //请求方式
                log.setRequestType(request.getMethod());
                //请求参数
                log.setMapToParams(logParams);
                //请求用户
                log.setUsername(username);
                //请求IP
                log.setIp(ipInfoUtil.getIpAddr(request));
                //IP地址
                log.setIpInfo(ipInfoUtil.getIpCity(request));
                //请求开始时间
                Date logStartTime = beginTimeThreadLocal.get();
 
                long beginTime = beginTimeThreadLocal.get().getTime();
                long endTime = System.currentTimeMillis();
                //请求耗时
                Long logElapsedTime = endTime - beginTime;
                log.setCostTime(logElapsedTime.intValue());
                ipInfoUtil.getInfo(request, ObjectUtil.mapToStringAll(request.getParameterMap()));
 
                //调用线程保存至ES
                ThreadPoolUtil.getPool().execute(new SaveSystemLogThread(log, logService));
            }
        } catch (Exception e) {
            log.error("AOP后置通知异常", e);
        }
    }
 
    /**
     * 保存日志至ES
     */
    private static class SaveEsSystemLogThread implements Runnable {
 
        private EsLog esLog;
        private EsLogService esLogService;
 
        public SaveEsSystemLogThread(EsLog esLog, EsLogService esLogService) {
            this.esLog = esLog;
            this.esLogService = esLogService;
        }
 
        @Override
        public void run() {
 
            esLogService.saveLog(esLog);
        }
    }
 
    /**
     * 保存日志至数据库
     */
    private static class SaveSystemLogThread implements Runnable {
 
        private Log log;
        private LogService logService;
 
        public SaveSystemLogThread(Log esLog, LogService logService) {
            this.log = esLog;
            this.logService = logService;
        }
 
        @Override
        public void run() {
 
            logService.save(log);
        }
    }
 
    /**
     * 获取注解中对方法的描述信息 用于Controller层注解
     * @param joinPoint 切点
     * @return 方法描述
     * @throws Exception
     */
    public static Map<String, Object> getControllerMethodInfo(JoinPoint joinPoint) throws Exception{
 
        Map<String, Object> map = new HashMap<String, Object>(16);
        //获取目标类名
        String targetName = joinPoint.getTarget().getClass().getName();
        //获取方法名
        String methodName = joinPoint.getSignature().getName();
        //获取相关参数
        Object[] arguments = joinPoint.getArgs();
        //生成类对象
        Class targetClass = Class.forName(targetName);
        //获取该类中的方法
        Method[] methods = targetClass.getMethods();
 
        String description = "";
        Integer type = null;
 
        for(Method method : methods) {
            if(!method.getName().equals(methodName)) {
                continue;
            }
            Class[] clazzs = method.getParameterTypes();
            if(clazzs.length != arguments.length) {
                //比较方法中参数个数与从切点中获取的参数个数是否相同,原因是方法可以重载哦
                continue;
            }
            description = method.getAnnotation(SystemLog.class).description();
            type = method.getAnnotation(SystemLog.class).type().ordinal();
            map.put("description", description);
            map.put("type", type);
        }
        return map;
    }
 
}