├── .gitignore
├── LICENSE
├── README.md
├── easy-log-core
├── pom.xml
└── src
│ └── main
│ ├── java
│ └── com
│ │ └── easycode8
│ │ └── easylog
│ │ └── core
│ │ ├── LogDefinition.java
│ │ ├── LogHolder.java
│ │ ├── LogInfo.java
│ │ ├── LogStopWatch.java
│ │ ├── adapter
│ │ ├── ControllerLogAttributeMapping.java
│ │ ├── LogAttributeMappingAdapter.java
│ │ └── ServiceLogAttributeMapping.java
│ │ ├── annotation
│ │ ├── EasyLog.java
│ │ ├── EasyLogConfiguration.java
│ │ ├── EasyLogProperties.java
│ │ ├── EnableEasyLog.java
│ │ └── Tag.java
│ │ ├── aop
│ │ ├── BeanFactoryLogAttributeSourceAdvisor.java
│ │ ├── LogStaticMethodMatcherPointcutAdvisor.java
│ │ └── interceptor
│ │ │ ├── AbstractCacheLogAttributeSource.java
│ │ │ ├── AnnotationLogAttributeSource.java
│ │ │ ├── DefaultLogAttribute.java
│ │ │ ├── LogAspectSupport.java
│ │ │ ├── LogAttribute.java
│ │ │ ├── LogAttributeSource.java
│ │ │ ├── LogAttributeSourcePointcut.java
│ │ │ ├── LogMethodInterceptor.java
│ │ │ └── TimingLogAttributeSourcePointcut.java
│ │ ├── cache
│ │ ├── LogAttributeCache.java
│ │ ├── LogAttributeCacheConfiguration.java
│ │ ├── LogAttributeMemoryCache.java
│ │ └── LogAttributeRedisCache.java
│ │ ├── constants
│ │ └── HandleMode.java
│ │ ├── handler
│ │ ├── DefaultLogHandler.java
│ │ └── LogDataHandler.java
│ │ ├── monitor
│ │ └── EasyLogApplicationInfoPrinter.java
│ │ ├── provider
│ │ ├── OperatorProvider.java
│ │ └── SessionOperatorProvider.java
│ │ ├── trace
│ │ ├── LogTracer.java
│ │ └── NoneLogTracer.java
│ │ └── util
│ │ ├── LogUtils.java
│ │ └── SpringSpelUtils.java
│ └── resources
│ └── easy-log
│ └── db
│ └── liquibase
│ └── changelog-1.0.xml
├── easy-log-data-mybatis-plus
├── pom.xml
├── readme.md
└── src
│ └── main
│ ├── java
│ └── com
│ │ └── easycode8
│ │ └── easylog
│ │ └── mybatis
│ │ ├── CompareResult.java
│ │ ├── adapter
│ │ ├── MybatisAdapter.java
│ │ ├── MybatisLogAttributeMappingAdapter.java
│ │ ├── MybatisLogAttributeMappingConfiguration.java
│ │ └── MybatisPlusAdapter.java
│ │ ├── autoconfigure
│ │ ├── EasyLogMybatisPlusProperties.java
│ │ └── MybatisDataLogAutoConfiguration.java
│ │ ├── handler
│ │ ├── DataSnapshotHandler.java
│ │ └── MybatisPlusDataSnapshotHandler.java
│ │ ├── interceptor
│ │ └── DataSnapshotInterceptor.java
│ │ └── util
│ │ ├── CamelCaseUtils.java
│ │ ├── GenericTypeUtils.java
│ │ ├── MybatisUtils.java
│ │ └── SqlUtils.java
│ └── resources
│ └── META-INF
│ └── spring.factories
├── easy-log-spring-boot-starter
├── pom.xml
└── src
│ └── main
│ ├── java
│ └── com
│ │ └── easycode8
│ │ └── easylog
│ │ └── autoconfigure
│ │ ├── EasyLogAutoConfiguration.java
│ │ ├── LogAttributeSourceConfiguration.java
│ │ └── source
│ │ ├── OpenApi3LogAttributeSource.java
│ │ └── SwaggerLogAttributeSource.java
│ └── resources
│ └── META-INF
│ └── spring.factories
├── easy-log-trace
├── pom.xml
└── src
│ └── main
│ ├── java
│ └── com
│ │ └── easycode8
│ │ └── easylog
│ │ └── trace
│ │ ├── DefaultLogTracer.java
│ │ ├── LogTraceConfiguration.java
│ │ ├── ZipkinLogTracer.java
│ │ ├── autoconfigure
│ │ ├── EasyLogTraceAutoConfiguration.java
│ │ └── EasyLogTraceEnvironmentPostProcessor.java
│ │ └── filter
│ │ ├── EasyLogTraceFilter.java
│ │ └── MDCConstants.java
│ └── resources
│ └── META-INF
│ └── spring.factories
├── easy-log-web
├── pom.xml
└── src
│ └── main
│ ├── java
│ └── com
│ │ └── easycode8
│ │ └── easylog
│ │ └── web
│ │ ├── autoconfigure
│ │ ├── EasyLogWebAutoConfiguration.java
│ │ ├── EasyLogWebProperties.java
│ │ └── EasyLogWebSocketAutoConfiguration.java
│ │ ├── controller
│ │ └── EasyLogController.java
│ │ ├── filter
│ │ ├── LogFilter.java
│ │ └── SecurityBasicAuthFilter.java
│ │ └── model
│ │ ├── PageInfo.java
│ │ ├── param
│ │ └── LogAttributeParam.java
│ │ └── vo
│ │ └── LogAttributeVO.java
│ └── resources
│ ├── META-INF
│ ├── resources
│ │ ├── easy-log-ui.html
│ │ └── webjars
│ │ │ └── easy-log
│ │ │ ├── axios
│ │ │ └── axios.min.js
│ │ │ ├── easylog.js
│ │ │ ├── iview
│ │ │ ├── iview.min.js
│ │ │ └── style
│ │ │ │ ├── fonts
│ │ │ │ ├── ionicons.svg
│ │ │ │ ├── ionicons.ttf
│ │ │ │ └── ionicons.woff
│ │ │ │ └── iview.css
│ │ │ ├── iview@3.5.4
│ │ │ └── dist
│ │ │ │ ├── iview.min.js
│ │ │ │ └── styles
│ │ │ │ ├── fonts
│ │ │ │ └── ionicons.woff2
│ │ │ │ └── iview.css
│ │ │ ├── jquery-2.0.2.min.js
│ │ │ ├── lodash.min.js
│ │ │ ├── sockjs-client
│ │ │ └── 1.1.4
│ │ │ │ └── sockjs.min.js
│ │ │ ├── stomp.js
│ │ │ └── 2.3.3
│ │ │ │ └── stomp.min.js
│ │ │ ├── vue
│ │ │ └── vue.js
│ │ │ ├── vue@2.6.14
│ │ │ └── dist
│ │ │ │ └── vue.js
│ │ │ ├── xterm-addon-fit@0.6.0
│ │ │ └── lib
│ │ │ │ └── xterm-addon-fit.js
│ │ │ └── xterm@4.18.0
│ │ │ ├── css
│ │ │ └── xterm.css
│ │ │ └── lib
│ │ │ └── xterm.js
│ └── spring.factories
│ └── templates
│ └── easy-log.html
└── pom.xml
/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled class file
2 | *.class
3 |
4 | # Log file
5 | *.log
6 |
7 | # BlueJ files
8 | *.ctxt
9 |
10 | # Mobile Tools for Java (J2ME)
11 | .mtj.tmp/
12 |
13 | # Package Files #
14 | *.jar
15 | *.war
16 | *.nar
17 | *.ear
18 | *.zip
19 | *.tar.gz
20 | *.rar
21 |
22 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
23 | hs_err_pid*
24 |
25 |
26 | .idea
27 | /target/
28 | .flattened-pom.xml
29 | *.iml
30 |
--------------------------------------------------------------------------------
/easy-log-core/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | easy-log
7 | io.github.easycode8
8 | ${revision}
9 | ../pom.xml
10 |
11 | 4.0.0
12 |
13 | easy-log-core
14 |
15 |
16 |
17 |
18 | org.springframework.boot
19 | spring-boot-configuration-processor
20 | true
21 |
22 |
23 |
24 | org.springframework.boot
25 | spring-boot-starter-web
26 | true
27 |
28 |
29 |
30 | com.alibaba
31 | fastjson
32 |
33 |
34 |
35 |
36 | org.springframework.boot
37 | spring-boot-starter-data-redis
38 | true
39 |
40 |
41 |
42 |
43 | commons-collections
44 | commons-collections
45 | ${commons-collections.version}
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/easy-log-core/src/main/java/com/easycode8/easylog/core/LogDefinition.java:
--------------------------------------------------------------------------------
1 | package com.easycode8.easylog.core;
2 |
3 | import java.io.Serializable;
4 |
5 | public interface LogDefinition extends Serializable {
6 | Integer STATUS_UN_INIT = 0;
7 | Integer STATUS_INIT = 1;
8 | Integer STATUS_BEFORE = 2;
9 | Integer STATUS_FINISH = 3;
10 |
11 | String TYPE_WEB = "web";
12 | String TYPE_SERVICE = "service";
13 | String TYPE_DAO = "dao";
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/easy-log-core/src/main/java/com/easycode8/easylog/core/LogHolder.java:
--------------------------------------------------------------------------------
1 | package com.easycode8.easylog.core;
2 |
3 | import org.springframework.core.NamedThreadLocal;
4 |
5 | import java.util.ArrayDeque;
6 | import java.util.Deque;
7 |
8 | public class LogHolder {
9 | /**
10 | * 为什么不能用String 而要使用队列(这里作为栈实现)来存储
11 | * 因为每次一个方法做多aop增强时候。都会在进入方法前为当前线程绑定当前日志信息。结束时候清除信息
12 | * 如果是使用同一个变量,那么aop增强方法内部还又带有AOP增加的方法,那么进入时候就会覆盖外出方法,同时内部方法aop结束时候,日志信息被清除
13 | * 外部线程拿到的日志信息则会出现空指针
14 | */
15 | private static final ThreadLocal> LOG_DEQUE = new NamedThreadLocal>("LOG_DEQUE") {
16 | @Override
17 | protected Deque initialValue() {
18 | return new ArrayDeque<>();
19 | }
20 | };
21 |
22 |
23 | /**
24 | * 入栈
25 | */
26 | public static void push(LogInfo info) {
27 | LOG_DEQUE.get().push(info);
28 | }
29 |
30 | /**
31 | * 获取栈顶元素
32 | * @return
33 | */
34 | public static LogInfo peek() {
35 | return LOG_DEQUE.get().peek();
36 |
37 | }
38 |
39 |
40 |
41 | /**
42 | * 移除栈顶数据源,如果是最后元素,则清空线程数据
43 | */
44 | public static void poll() {
45 | Deque deque = LOG_DEQUE.get();
46 | deque.poll();
47 | if (deque.isEmpty()) {
48 | LOG_DEQUE.remove();
49 | }
50 | }
51 |
52 | }
53 |
--------------------------------------------------------------------------------
/easy-log-core/src/main/java/com/easycode8/easylog/core/LogInfo.java:
--------------------------------------------------------------------------------
1 | package com.easycode8.easylog.core;
2 |
3 |
4 | import java.util.Date;
5 | import java.util.UUID;
6 |
7 | public class LogInfo implements LogDefinition {
8 |
9 |
10 | /** 日志主键*/
11 | // @TableId(type = IdType.UUID)
12 | private String logId;
13 |
14 | /** 日志类型*/
15 | private String type;
16 |
17 | /** 日志标题*/
18 | private String title;
19 |
20 | /** 日志摘要*/
21 | private String description;
22 |
23 | /** 请求IP*/
24 | private String ip;
25 |
26 | /** URI*/
27 | private String requestUri;
28 |
29 | /** 请求方式*/
30 | private String method;
31 |
32 | /** 提交参数*/
33 | private String params;
34 |
35 | /** 异常*/
36 | private String exception;
37 |
38 | /** 操作时间*/
39 | private Date operateDate;
40 |
41 | /** 请求时长*/
42 | private Long timeout;
43 |
44 | /** 操作人*/
45 | private String operator;
46 |
47 | /** 链路追踪ID(和requestId等效) 遵从OpenTracing规范使用traceId命名*/
48 | private String traceId;
49 |
50 | /** 历史数据*/
51 | private String dataSnapshot;
52 |
53 | /** 日志状态*/
54 | private Integer status = 0;
55 |
56 | /** 日志标签用于扩展业务自定义属性*/
57 | private String tags;
58 |
59 | public String getLogId() {
60 | return logId;
61 | }
62 |
63 | public void setLogId(String logId) {
64 | this.logId = logId;
65 | }
66 |
67 | public String getType() {
68 | return type;
69 | }
70 |
71 | public void setType(String type) {
72 | this.type = type;
73 | }
74 |
75 | public String getTitle() {
76 | return title;
77 | }
78 |
79 | public void setTitle(String title) {
80 | this.title = title;
81 | }
82 |
83 | public String getDescription() {
84 | return description;
85 | }
86 |
87 | public void setDescription(String description) {
88 | this.description = description;
89 | }
90 |
91 | public String getIp() {
92 | return ip;
93 | }
94 |
95 | public void setIp(String ip) {
96 | this.ip = ip;
97 | }
98 |
99 | public String getRequestUri() {
100 | return requestUri;
101 | }
102 |
103 | public void setRequestUri(String requestUri) {
104 | this.requestUri = requestUri;
105 | }
106 |
107 | public String getMethod() {
108 | return method;
109 | }
110 |
111 | public void setMethod(String method) {
112 | this.method = method;
113 | }
114 |
115 | public String getParams() {
116 | return params;
117 | }
118 |
119 | public void setParams(String params) {
120 | this.params = params;
121 | }
122 |
123 | public String getException() {
124 | return exception;
125 | }
126 |
127 | public void setException(String exception) {
128 | this.exception = exception;
129 | }
130 |
131 | public Date getOperateDate() {
132 | return operateDate;
133 | }
134 |
135 | public void setOperateDate(Date operateDate) {
136 | this.operateDate = operateDate;
137 | }
138 |
139 | public Long getTimeout() {
140 | return timeout;
141 | }
142 |
143 | public void setTimeout(Long timeout) {
144 | this.timeout = timeout;
145 | }
146 |
147 | public String getOperator() {
148 | return operator;
149 | }
150 |
151 | public void setOperator(String operator) {
152 | this.operator = operator;
153 | }
154 |
155 | public String getTraceId() {
156 | return traceId;
157 | }
158 |
159 | public void setTraceId(String traceId) {
160 | this.traceId = traceId;
161 | }
162 |
163 | public String getDataSnapshot() {
164 | return dataSnapshot;
165 | }
166 |
167 | public void setDataSnapshot(String dataSnapshot) {
168 | this.dataSnapshot = dataSnapshot;
169 | }
170 |
171 | public Integer getStatus() {
172 | return status;
173 | }
174 |
175 | public void setStatus(Integer status) {
176 | this.status = status;
177 | }
178 |
179 | public String getTags() {
180 | return tags;
181 | }
182 |
183 | public void setTags(String tags) {
184 | this.tags = tags;
185 | }
186 | }
187 |
--------------------------------------------------------------------------------
/easy-log-core/src/main/java/com/easycode8/easylog/core/LogStopWatch.java:
--------------------------------------------------------------------------------
1 | package com.easycode8.easylog.core;
2 |
3 | import org.slf4j.Logger;
4 | import org.slf4j.helpers.MessageFormatter;
5 | import org.springframework.util.StopWatch;
6 |
7 | public class LogStopWatch {
8 | // private static final ThreadLocal STOP_WATCH = new NamedThreadLocal<>("STOP_WATCH");
9 |
10 |
11 | private Logger logger;
12 | private StopWatch stopWatch;
13 |
14 |
15 |
16 | public LogStopWatch(Logger logger, String stopWatchName) {
17 | this.logger = logger;
18 | this.stopWatch = new StopWatch(stopWatchName);
19 | }
20 |
21 |
22 | public LogStopWatch stop() {
23 | if (logger.isDebugEnabled()) {
24 | stopWatch.stop();
25 | }
26 |
27 | return this;
28 | }
29 |
30 | public void start(String message, String... variable) {
31 | if (logger.isDebugEnabled()) {
32 | stopWatch.start(MessageFormatter.arrayFormat(message, variable).getMessage());
33 | }
34 |
35 | }
36 |
37 | public void start(String taskName) {
38 | if (logger.isDebugEnabled()) {
39 | stopWatch.start(taskName);
40 | }
41 |
42 | }
43 |
44 |
45 | public void showDetail() {
46 | // for (StopWatch.TaskInfo taskInfo : stopWatch.getTaskInfo()) {
47 | // stopWatch.getLastTaskInfo().getTimeMillis();
48 | // System.out.println("任务:" + taskInfo.getTaskName() + " timeout:" + taskInfo.getTimeNanos());
49 | // }
50 | if (logger.isDebugEnabled()) {
51 | logger.debug(stopWatch.prettyPrint());
52 | }
53 |
54 | }
55 |
56 |
57 | }
58 |
--------------------------------------------------------------------------------
/easy-log-core/src/main/java/com/easycode8/easylog/core/adapter/ControllerLogAttributeMapping.java:
--------------------------------------------------------------------------------
1 | package com.easycode8.easylog.core.adapter;
2 |
3 | import com.easycode8.easylog.core.annotation.EasyLogProperties;
4 | import com.easycode8.easylog.core.aop.interceptor.DefaultLogAttribute;
5 | import com.easycode8.easylog.core.aop.interceptor.LogAttribute;
6 | import com.easycode8.easylog.core.util.LogUtils;
7 | import org.slf4j.Logger;
8 | import org.slf4j.LoggerFactory;
9 | import org.springframework.web.bind.annotation.*;
10 |
11 | import java.lang.reflect.Method;
12 |
13 | /**
14 | * 从controller bean日志属性提取
15 | */
16 | public class ControllerLogAttributeMapping implements LogAttributeMappingAdapter {
17 |
18 | private static final Logger LOGGER = LoggerFactory.getLogger(ControllerLogAttributeMapping.class);
19 | private final EasyLogProperties easyLogProperties;
20 |
21 | public ControllerLogAttributeMapping(EasyLogProperties easyLogProperties) {
22 | LOGGER.info("[easy-log]启动controller bean日志增强");
23 | this.easyLogProperties = easyLogProperties;
24 | }
25 |
26 | @Override
27 | public LogAttribute getLogAttribute(Method method, Class> targetClass) {
28 | if (easyLogProperties.getScanController().getEnabled() && isControllerPublicMethod(method, targetClass)) {
29 | String title = LogUtils.createDefaultTitle(method, targetClass);
30 |
31 | return DefaultLogAttribute.builder()
32 | .title(title)
33 | .async(easyLogProperties.getAsync())
34 | .build();
35 | }
36 | return null;
37 | }
38 |
39 |
40 | private boolean isControllerPublicMethod(Method method, Class> targetClass) {
41 | // 如果方法不是controller自己的而是继承来的则忽略
42 | if (!method.getDeclaringClass().equals(targetClass)) {
43 | return false;
44 | }
45 | // return (targetClass.getAnnotation(Controller.class) != null && (method.getAnnotation(ResponseBody.class) != null || method.getReturnType() == ResponseEntity.class))
46 | // ||
47 | // (targetClass.getAnnotation(RestController.class) != null && !Modifier.isStatic(method.getModifiers())
48 | // && Modifier.isPublic(method.getModifiers()));
49 |
50 | // 判断是否是Controller的接口方法
51 | return method.isAnnotationPresent(RequestMapping.class) ||
52 | method.isAnnotationPresent(GetMapping.class) ||
53 | method.isAnnotationPresent(PostMapping.class) ||
54 | method.isAnnotationPresent(PutMapping.class) ||
55 | method.isAnnotationPresent(DeleteMapping.class);
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/easy-log-core/src/main/java/com/easycode8/easylog/core/adapter/LogAttributeMappingAdapter.java:
--------------------------------------------------------------------------------
1 | package com.easycode8.easylog.core.adapter;
2 |
3 | import com.easycode8.easylog.core.aop.interceptor.LogAttribute;
4 |
5 | import java.lang.reflect.Method;
6 |
7 | public interface LogAttributeMappingAdapter {
8 |
9 | /** 预留排序接口,用于多个日志属性适配器命中多个接口增强时候定义优先级,*/
10 | default int order() {
11 | return 10;
12 | }
13 |
14 | LogAttribute getLogAttribute(Method method, Class> targetClass);
15 | }
16 |
--------------------------------------------------------------------------------
/easy-log-core/src/main/java/com/easycode8/easylog/core/adapter/ServiceLogAttributeMapping.java:
--------------------------------------------------------------------------------
1 | package com.easycode8.easylog.core.adapter;
2 |
3 | import com.easycode8.easylog.core.annotation.EasyLogProperties;
4 | import com.easycode8.easylog.core.aop.interceptor.DefaultLogAttribute;
5 | import com.easycode8.easylog.core.aop.interceptor.LogAttribute;
6 | import com.easycode8.easylog.core.util.LogUtils;
7 | import org.slf4j.Logger;
8 | import org.slf4j.LoggerFactory;
9 | import org.springframework.stereotype.Service;
10 |
11 | import java.lang.reflect.Method;
12 | import java.lang.reflect.Modifier;
13 |
14 | /**
15 | * 从service bean日志属性提取
16 | */
17 | public class ServiceLogAttributeMapping implements LogAttributeMappingAdapter{
18 |
19 | private static final Logger LOGGER = LoggerFactory.getLogger(ServiceLogAttributeMapping.class);
20 | private final EasyLogProperties easyLogProperties;
21 |
22 | public ServiceLogAttributeMapping(EasyLogProperties easyLogProperties) {
23 | LOGGER.info("[easy-log]启动service bean日志增强");
24 | this.easyLogProperties = easyLogProperties;
25 | }
26 |
27 | @Override
28 | public LogAttribute getLogAttribute(Method method, Class> targetClass) {
29 | // 如果找不到EasyLog 检查是否开启server-debug模式
30 | if (isServicePublicMethod(method, targetClass)) {
31 | String title = LogUtils.createDefaultTitle(method, targetClass);
32 |
33 | return DefaultLogAttribute.builder()
34 | .title(title)
35 | .async(easyLogProperties.getAsync())
36 | .build();
37 | }
38 | return null;
39 | }
40 |
41 |
42 | private boolean isServicePublicMethod(Method method, Class> targetClass) {
43 | // 如果方法来自于Object对象忽略处理
44 | if (method.getDeclaringClass().equals(Object.class)) {
45 | return false;
46 | }
47 | return easyLogProperties.getScanService().getEnabled() && targetClass.getAnnotation(Service.class) != null
48 | && !Modifier.isStatic(method.getModifiers())
49 | && Modifier.isPublic(method.getModifiers());
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/easy-log-core/src/main/java/com/easycode8/easylog/core/annotation/EasyLog.java:
--------------------------------------------------------------------------------
1 | package com.easycode8.easylog.core.annotation;
2 |
3 | import com.easycode8.easylog.core.constants.HandleMode;
4 |
5 | import java.lang.annotation.*;
6 |
7 | @Target({ElementType.METHOD})
8 | @Retention(RetentionPolicy.RUNTIME)
9 | @Documented
10 | public @interface EasyLog {
11 | int MODE_DEFAULT = 0;
12 | int MODE_ASYNC = 1;
13 | int MODE_SYNC = 2;
14 |
15 | /** 日志标题 title别名*/
16 | String value() default "";
17 | /** 日志标题*/
18 | String title() default "";
19 | /** 日志模板 使用spring的spel表达式 用于从请求参数中提取日志描述*/
20 | String template() default "";
21 | /** 日志处理器*/
22 | String handler() default "";
23 | /** 日志操作人 使用spring的spel表达式 用于从请求参数中提取日志操作人*/
24 | String operator() default "";
25 | /** 处理日志的模式 GLOBAL:使用全局默认值 ASYNC:使用异步 SYNC:使用同步*/
26 | HandleMode handleMode() default HandleMode.GLOBAL;
27 | /** 日志自定义标签用于记录扩展的业务值*/
28 | Tag[] tags() default {@Tag(key = "", value = "")};
29 |
30 |
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/easy-log-core/src/main/java/com/easycode8/easylog/core/annotation/EasyLogConfiguration.java:
--------------------------------------------------------------------------------
1 | package com.easycode8.easylog.core.annotation;
2 |
3 |
4 | import com.easycode8.easylog.core.handler.DefaultLogHandler;
5 | import com.easycode8.easylog.core.handler.LogDataHandler;
6 | import com.easycode8.easylog.core.adapter.LogAttributeMappingAdapter;
7 | import com.easycode8.easylog.core.aop.BeanFactoryLogAttributeSourceAdvisor;
8 | import com.easycode8.easylog.core.aop.interceptor.AnnotationLogAttributeSource;
9 | import com.easycode8.easylog.core.aop.interceptor.LogAttributeSource;
10 | import com.easycode8.easylog.core.aop.interceptor.LogMethodInterceptor;
11 | import com.easycode8.easylog.core.cache.LogAttributeCache;
12 | import com.easycode8.easylog.core.cache.LogAttributeCacheConfiguration;
13 | import com.easycode8.easylog.core.monitor.EasyLogApplicationInfoPrinter;
14 | import com.easycode8.easylog.core.provider.OperatorProvider;
15 | import com.easycode8.easylog.core.provider.SessionOperatorProvider;
16 | import com.easycode8.easylog.core.trace.LogTracer;
17 | import com.easycode8.easylog.core.trace.NoneLogTracer;
18 | import org.springframework.aop.Advisor;
19 | import org.springframework.beans.factory.ObjectProvider;
20 | import org.springframework.beans.factory.annotation.Qualifier;
21 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
22 | import org.springframework.boot.context.properties.EnableConfigurationProperties;
23 | import org.springframework.context.annotation.Bean;
24 | import org.springframework.context.annotation.Configuration;
25 | import org.springframework.context.annotation.Import;
26 | import org.springframework.core.task.TaskDecorator;
27 | import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
28 |
29 | import java.util.List;
30 | import java.util.concurrent.ThreadPoolExecutor;
31 |
32 | @Configuration
33 | @EnableConfigurationProperties(EasyLogProperties.class)
34 | public class EasyLogConfiguration {
35 | @Bean
36 | public Advisor easyLogStaticMethodMatcherPointcutAdvisor(LogAttributeSource logAttributeSource, LogMethodInterceptor logMethodInterceptor, EasyLogProperties easyLogProperties) {
37 | //Advisor 是 Spring AOP 对 Advice 和 Pointcut 的抽象,可以理解为“执行通知者”,一个 Pointcut (一般对应方法)和用于“增强”它的 Advice 共同组成这个方法的一个 Advisor
38 | BeanFactoryLogAttributeSourceAdvisor advisor = new BeanFactoryLogAttributeSourceAdvisor();
39 | advisor.setLogAttributeSource(logAttributeSource);
40 | advisor.setAdvice(logMethodInterceptor);
41 | //设置自定义优先级值越小优先级越高
42 | if (easyLogProperties.getAspectOrder() != null) {
43 | advisor.setOrder(easyLogProperties.getAspectOrder());
44 | }
45 | return advisor;
46 | }
47 |
48 | @Bean
49 | public LogMethodInterceptor easyLogMethodInterceptor(LogAttributeSource logAttributeSource,
50 | @Qualifier("easyLogThreadPoolTaskExecutor") ThreadPoolTaskExecutor taskExecutor,
51 | @Qualifier("easyLogDataHandler") LogDataHandler logDataHandler,
52 | ObjectProvider operatorProvider,
53 | ObjectProvider tracerObjectProvider) {
54 | LogMethodInterceptor logMethodInterceptor = new LogMethodInterceptor();
55 | logMethodInterceptor.setLogAttributeSource(logAttributeSource);
56 | logMethodInterceptor.setThreadPoolTaskExecutor(taskExecutor);
57 | logMethodInterceptor.setLogDataHandler(logDataHandler);
58 | logMethodInterceptor.setOperatorProvider(operatorProvider.getIfAvailable());
59 | LogTracer logTracer = tracerObjectProvider.getIfAvailable();
60 | if (logTracer == null) {
61 | logTracer = new NoneLogTracer();
62 | }
63 | logMethodInterceptor.setLogTracer(logTracer);
64 | return logMethodInterceptor;
65 | }
66 |
67 | @Bean
68 | @ConditionalOnMissingBean
69 | public LogAttributeSource logAttributeSource(LogAttributeCache logAttributeCache, EasyLogProperties easyLogProperties, List mappingAdapters) {
70 | return new AnnotationLogAttributeSource(logAttributeCache, easyLogProperties, mappingAdapters);
71 | }
72 |
73 | /**按优先级选择日志属性缓存实现 redis>local*/
74 | @Configuration
75 | @ConditionalOnMissingBean(LogAttributeCache.class)
76 | @Import({LogAttributeCacheConfiguration.RedisCacheConfiguration.class, LogAttributeCacheConfiguration.LocalCacheConfiguration.class})
77 | public static class ChooseLogAttributeCacheConfiguration {
78 |
79 | }
80 |
81 |
82 |
83 | @Bean
84 | @ConditionalOnMissingBean(name = "easyLogDataHandler")
85 | public LogDataHandler easyLogDataHandler() {
86 | return new DefaultLogHandler();
87 | }
88 |
89 | @Bean
90 | @ConditionalOnMissingBean
91 | public OperatorProvider easyLogOperatorProvider(EasyLogProperties easyLogProperties) {
92 | return new SessionOperatorProvider(easyLogProperties);
93 | }
94 |
95 | /**
96 | * 日志处理异步执行线程池
97 | * @param easyLogProperties
98 | * @param objectProvider
99 | * @return
100 | */
101 | @Bean
102 | @ConditionalOnMissingBean(name = "easyLogThreadPoolTaskExecutor")
103 | public ThreadPoolTaskExecutor easyLogThreadPoolTaskExecutor(EasyLogProperties easyLogProperties, ObjectProvider objectProvider) {
104 | EasyLogProperties.Task async = easyLogProperties.getTask();
105 | ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
106 | // 核心线程数定义了最小可以同时运行的线程数量
107 | // 1. cpu密集型CorePoolSize = CPU核心数+1
108 | // 2. IO密集型CorePoolSize = CPU核心数 * 2
109 | taskExecutor.setCorePoolSize(async.getCorePoolSize());
110 | // 当队列中存放的任务达到队列容量的时候,当前可以同时运行的线程数量变为最大线程数
111 | taskExecutor.setMaxPoolSize(async.getMaxPoolSize());
112 | // 任务队列,被提交但尚未被执行的任务
113 | taskExecutor.setQueueCapacity(async.getQueueCapacity());
114 | // 当线程池中的线程数量大于 corePoolSize 的时候,如果这时没有新的任务提交,核心线程外的线程不会立即销毁,而是会等待,直到等待的时间超过了 keepAliveTime才会被回收销毁
115 | taskExecutor.setKeepAliveSeconds(async.getKeepAliveSeconds());
116 | taskExecutor.setThreadNamePrefix(async.getThreadNamePrefix());
117 | //拒绝策略,当队列满了并且工作线程数量大于线程池的最大线程数时,提供拒绝策略
118 | taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
119 | taskExecutor.setTaskDecorator(objectProvider.getIfAvailable());
120 | taskExecutor.initialize();
121 | return taskExecutor;
122 | }
123 |
124 | // 打印应用监控信息
125 | @Bean
126 | public EasyLogApplicationInfoPrinter easyLogApplicationInfoPrinter() {
127 | return new EasyLogApplicationInfoPrinter();
128 | }
129 |
130 |
131 | }
132 |
--------------------------------------------------------------------------------
/easy-log-core/src/main/java/com/easycode8/easylog/core/annotation/EasyLogProperties.java:
--------------------------------------------------------------------------------
1 | package com.easycode8.easylog.core.annotation;
2 |
3 | import org.springframework.boot.context.properties.ConfigurationProperties;
4 |
5 | import java.util.HashSet;
6 | import java.util.Set;
7 |
8 | @ConfigurationProperties(prefix = "spring.easy-log")
9 | public class EasyLogProperties {
10 |
11 | /** 是否启用easy-log 默认true*/
12 | private Boolean enabled = true;
13 |
14 | /**
15 | * 获取操作人信息的表达式 支持从session或请求头获取 默认为空示例:
16 | * spring.easy-log.operator=session.account.username:通过session获取属性为account中对象的username字段作为操作人
17 | * spring.easy-log.operator=header.x-username:代表从请求头中获取x-username作为操作人信息
18 | */
19 | private String operator = "";
20 |
21 | /**是否开启默认异步记录日志 默认false*/
22 | private Boolean async = false;
23 |
24 | /**日志aop生效的包范围,默认为空,尝试增强所有bean,建议填写项目基础包名,减少无用的aop判断,支持多个*/
25 | private Set scanPackages = new HashSet<>();
26 |
27 | // spring顺序默认是Ordered.LOWEST_PRECEDENCE(Integer.MAX_VALUE)优先级最低,如果两个aspect顺序一样,则使用bean注册的顺序
28 | /**切面的顺序 值越小越先执行Integer.MIN_VALUE 优先级最高, 设置为0 比一般spring 默认最低优先级高*/
29 | private Integer aspectOrder = 0;
30 |
31 | private Task task = new Task();
32 |
33 | private ScanOpenApi scanOpenApi;
34 |
35 | private ScanSwagger scanSwagger;
36 |
37 | private ScanService scanService;
38 |
39 | private ScanController scanController;
40 |
41 | private Cache cache = new Cache();
42 |
43 |
44 | public Boolean getEnabled() {
45 | return enabled;
46 | }
47 |
48 | public void setEnabled(Boolean enabled) {
49 | this.enabled = enabled;
50 | }
51 |
52 | public String getOperator() {
53 | return operator;
54 | }
55 |
56 | public void setOperator(String operator) {
57 | this.operator = operator;
58 | }
59 |
60 | public ScanService getScanService() {
61 | return scanService;
62 | }
63 |
64 | public void setScanService(ScanService scanService) {
65 | this.scanService = scanService;
66 | }
67 |
68 | public ScanController getScanController() {
69 | return scanController;
70 | }
71 |
72 | public void setScanController(ScanController scanController) {
73 | this.scanController = scanController;
74 | }
75 |
76 | public ScanOpenApi getScanOpenApi() {
77 | return scanOpenApi;
78 | }
79 |
80 | public void setScanOpenApi(ScanOpenApi scanOpenApi) {
81 | this.scanOpenApi = scanOpenApi;
82 | }
83 |
84 | public ScanSwagger getScanSwagger() {
85 | return scanSwagger;
86 | }
87 |
88 | public void setScanSwagger(ScanSwagger scanSwagger) {
89 | this.scanSwagger = scanSwagger;
90 | }
91 |
92 | public Boolean getAsync() {
93 | return async;
94 | }
95 |
96 | public void setAsync(Boolean async) {
97 | this.async = async;
98 | }
99 |
100 | public Task getTask() {
101 | return task;
102 | }
103 |
104 | public void setTask(Task task) {
105 | this.task = task;
106 | }
107 |
108 | public Integer getAspectOrder() {
109 | return aspectOrder;
110 | }
111 |
112 | public void setAspectOrder(Integer aspectOrder) {
113 | this.aspectOrder = aspectOrder;
114 | }
115 |
116 | public Cache getCache() {
117 | return cache;
118 | }
119 |
120 | public void setCache(Cache cache) {
121 | this.cache = cache;
122 | }
123 |
124 | public Set getScanPackages() {
125 | return scanPackages;
126 | }
127 |
128 | public void setScanPackages(Set scanPackages) {
129 | this.scanPackages = scanPackages;
130 | }
131 |
132 | public static class Task {
133 |
134 |
135 | /**核心线程数定义了最小可以同时运行的线程数量*/
136 | private Integer corePoolSize = 4;
137 | /**当队列中存放的任务达到队列容量的时候,当前可以同时运行的线程数量变为最大线程数*/
138 | private Integer maxPoolSize = 20;
139 | /**任务队列,被提交但尚未被执行的任务*/
140 | private Integer queueCapacity = 50;
141 | /**当线程池中的线程数量大于 corePoolSize 的时候,如果这时没有新的任务提交,核心线程外的线程不会立即销毁,而是会等待,直到等待的时间超过了 keepAliveTime才会被回收销毁*/
142 | private Integer keepAliveSeconds = 1800;
143 | /**线程名称前缀*/
144 | private String threadNamePrefix = "easy-log-";
145 |
146 | public Integer getCorePoolSize() {
147 | return corePoolSize;
148 | }
149 |
150 | public void setCorePoolSize(Integer corePoolSize) {
151 | this.corePoolSize = corePoolSize;
152 | }
153 |
154 | public Integer getMaxPoolSize() {
155 | return maxPoolSize;
156 | }
157 |
158 | public void setMaxPoolSize(Integer maxPoolSize) {
159 | this.maxPoolSize = maxPoolSize;
160 | }
161 |
162 | public Integer getQueueCapacity() {
163 | return queueCapacity;
164 | }
165 |
166 | public void setQueueCapacity(Integer queueCapacity) {
167 | this.queueCapacity = queueCapacity;
168 | }
169 |
170 | public Integer getKeepAliveSeconds() {
171 | return keepAliveSeconds;
172 | }
173 |
174 | public void setKeepAliveSeconds(Integer keepAliveSeconds) {
175 | this.keepAliveSeconds = keepAliveSeconds;
176 | }
177 |
178 | public String getThreadNamePrefix() {
179 | return threadNamePrefix;
180 | }
181 |
182 | public void setThreadNamePrefix(String threadNamePrefix) {
183 | this.threadNamePrefix = threadNamePrefix;
184 | }
185 | }
186 |
187 | public static class ScanOpenApi {
188 | /**是否开启openapi3 的@Operation注解作为日志标识*/
189 | private Boolean enabled = false;
190 |
191 | public Boolean getEnabled() {
192 | return enabled;
193 | }
194 |
195 | public void setEnabled(Boolean enabled) {
196 | this.enabled = enabled;
197 | }
198 | }
199 |
200 | public static class ScanSwagger {
201 | /**是否开启swagger的@ApiOperation注解作为日志标识*/
202 | private Boolean enabled = false;
203 |
204 | public Boolean getEnabled() {
205 | return enabled;
206 | }
207 |
208 | public void setEnabled(Boolean enabled) {
209 | this.enabled = enabled;
210 | }
211 | }
212 |
213 | public static class ScanService {
214 | /**是否开启扫描@Service标记的公开方法*/
215 | private Boolean enabled = false;
216 |
217 | public Boolean getEnabled() {
218 | return enabled;
219 | }
220 |
221 | public void setEnabled(Boolean enabled) {
222 | this.enabled = enabled;
223 | }
224 | }
225 |
226 | public static class ScanController {
227 | /**是否开启扫描Controller的接口方法*/
228 | private Boolean enabled = false;
229 |
230 | public Boolean getEnabled() {
231 | return enabled;
232 | }
233 |
234 | public void setEnabled(Boolean enabled) {
235 | this.enabled = enabled;
236 | }
237 | }
238 |
239 | public static class Cache {
240 | /**easy-log日志属性缓存前缀 默认: easy-log: */
241 | private String keyPrefix = "easy-log:xxx-service";
242 | /** 是否强制删除历史缓存 */
243 | private Boolean dropFirst = false;
244 |
245 | public String getKeyPrefix() {
246 | return keyPrefix;
247 | }
248 |
249 | public void setKeyPrefix(String keyPrefix) {
250 | this.keyPrefix = keyPrefix;
251 | }
252 |
253 |
254 | public Boolean getDropFirst() {
255 | return dropFirst;
256 | }
257 |
258 | public void setDropFirst(Boolean dropFirst) {
259 | this.dropFirst = dropFirst;
260 | }
261 | }
262 |
263 | }
--------------------------------------------------------------------------------
/easy-log-core/src/main/java/com/easycode8/easylog/core/annotation/EnableEasyLog.java:
--------------------------------------------------------------------------------
1 | package com.easycode8.easylog.core.annotation;
2 |
3 |
4 | import org.springframework.context.annotation.Import;
5 |
6 | import java.lang.annotation.*;
7 |
8 | @Target(ElementType.TYPE)
9 | @Retention(RetentionPolicy.RUNTIME)
10 | @Documented
11 | @Import(EasyLogConfiguration.class)
12 | public @interface EnableEasyLog {
13 | }
14 |
--------------------------------------------------------------------------------
/easy-log-core/src/main/java/com/easycode8/easylog/core/annotation/Tag.java:
--------------------------------------------------------------------------------
1 | package com.easycode8.easylog.core.annotation;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | @Retention(RetentionPolicy.RUNTIME)
9 | @Target(ElementType.FIELD)
10 | public @interface Tag {
11 | String key();
12 | String value();
13 | }
--------------------------------------------------------------------------------
/easy-log-core/src/main/java/com/easycode8/easylog/core/aop/BeanFactoryLogAttributeSourceAdvisor.java:
--------------------------------------------------------------------------------
1 | package com.easycode8.easylog.core.aop;
2 |
3 | import com.easycode8.easylog.core.aop.interceptor.LogAttributeSource;
4 | import com.easycode8.easylog.core.aop.interceptor.LogAttributeSourcePointcut;
5 | import com.easycode8.easylog.core.aop.interceptor.TimingLogAttributeSourcePointcut;
6 | import org.springframework.aop.Pointcut;
7 | import org.springframework.aop.support.AbstractBeanFactoryPointcutAdvisor;
8 | import org.springframework.lang.Nullable;
9 |
10 | public class BeanFactoryLogAttributeSourceAdvisor extends AbstractBeanFactoryPointcutAdvisor {
11 | @Nullable
12 | private LogAttributeSource logAttributeSource;
13 |
14 | private final LogAttributeSourcePointcut logAttributeSourcePointcut = new TimingLogAttributeSourcePointcut() {
15 | @Override
16 | protected LogAttributeSource getLogAttributeSource() {
17 | return logAttributeSource;
18 | }
19 | };
20 |
21 | @Override
22 | public Pointcut getPointcut() {
23 | return this.logAttributeSourcePointcut;
24 | }
25 |
26 | public void setLogAttributeSource(LogAttributeSource logAttributeSource) {
27 | this.logAttributeSource = logAttributeSource;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/easy-log-core/src/main/java/com/easycode8/easylog/core/aop/LogStaticMethodMatcherPointcutAdvisor.java:
--------------------------------------------------------------------------------
1 | package com.easycode8.easylog.core.aop;
2 |
3 | import org.springframework.aop.support.StaticMethodMatcherPointcutAdvisor;
4 |
5 | import java.lang.reflect.Method;
6 |
7 | /**
8 | * TODO 暂未使用
9 | */
10 | public class LogStaticMethodMatcherPointcutAdvisor extends StaticMethodMatcherPointcutAdvisor {
11 |
12 | @Override
13 | public boolean matches(Method method, Class> aClass) {
14 | return false;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/easy-log-core/src/main/java/com/easycode8/easylog/core/aop/interceptor/AbstractCacheLogAttributeSource.java:
--------------------------------------------------------------------------------
1 | package com.easycode8.easylog.core.aop.interceptor;
2 |
3 |
4 | import com.easycode8.easylog.core.annotation.EasyLogProperties;
5 | import com.easycode8.easylog.core.cache.LogAttributeCache;
6 | import com.easycode8.easylog.core.util.LogUtils;
7 | import org.slf4j.Logger;
8 | import org.slf4j.LoggerFactory;
9 | import org.springframework.boot.autoconfigure.SpringBootApplication;
10 |
11 | import java.lang.reflect.Method;
12 | import java.lang.reflect.Proxy;
13 | import java.util.Map;
14 | import java.util.Set;
15 |
16 | public abstract class AbstractCacheLogAttributeSource implements LogAttributeSource {
17 | private static final Logger LOGGER = LoggerFactory.getLogger(AbstractCacheLogAttributeSource.class);
18 | private final LogAttributeCache logAttributeCache;
19 | protected EasyLogProperties easyLogProperties;
20 |
21 | protected AbstractCacheLogAttributeSource(LogAttributeCache logAttributeCache, EasyLogProperties easyLogProperties) {
22 | this.logAttributeCache = logAttributeCache;
23 | this.easyLogProperties = easyLogProperties;
24 | }
25 |
26 |
27 | @Override
28 | public LogAttribute getLogAttribute(Method method, Class> targetClass) {
29 | String key;
30 | // 如果是代理对象获取代理接口的真实名称
31 | if (Proxy.isProxyClass(targetClass)) {
32 | String className = ((Class) targetClass.getGenericInterfaces()[0]).getName();
33 | if (!this.matches(className, easyLogProperties.getScanPackages())) {
34 | return null;
35 | }
36 | key = LogUtils.createDefaultTitle(method, ((Class) targetClass.getGenericInterfaces()[0]), false);
37 |
38 | } else {
39 | if (!this.matches(targetClass.getName(), easyLogProperties.getScanPackages())) {
40 | return null;
41 | }
42 | if (targetClass.getAnnotation(SpringBootApplication.class) != null) {
43 | return null;
44 | }
45 |
46 | key = LogUtils.createDefaultTitle(method, targetClass, false);
47 | }
48 |
49 | if (!easyLogProperties.getCache().getKeyPrefix().endsWith(":")) {
50 | key = easyLogProperties.getCache().getKeyPrefix() + ":" + key;
51 | } else {
52 | key = easyLogProperties.getCache().getKeyPrefix() + key;
53 | }
54 | if (logAttributeCache.containsKey(key)) {
55 | return logAttributeCache.get(key);
56 | }
57 | LogAttribute attribute = doGetLogAttribute(method, targetClass);
58 | if (attribute != null) {
59 | logAttributeCache.put(key, attribute);
60 | }
61 | return attribute;
62 | }
63 |
64 | public abstract LogAttribute doGetLogAttribute(Method method, Class> targetClass);
65 |
66 | public Map getCacheMap() {
67 | return this.logAttributeCache.getAll();
68 | }
69 |
70 | /**
71 | * 更新日志属性缓存
72 | *
73 | * @param key
74 | * @param logAttribute
75 | */
76 | public void updateCache(String key, LogAttribute logAttribute) {
77 | this.logAttributeCache.put(key, logAttribute);
78 | }
79 |
80 | private boolean matches(String className, Set basePackages) {
81 | if (basePackages == null || basePackages.isEmpty()) {
82 | return true;
83 | }
84 | boolean matches = basePackages.stream().anyMatch(item -> className.startsWith(item));
85 | if (LOGGER.isTraceEnabled()) {
86 | LOGGER.trace("[ease-log] 类:{} AOP增强:{} 匹配前缀:{}", className, matches, basePackages);
87 | }
88 | return matches;
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/easy-log-core/src/main/java/com/easycode8/easylog/core/aop/interceptor/AnnotationLogAttributeSource.java:
--------------------------------------------------------------------------------
1 | package com.easycode8.easylog.core.aop.interceptor;
2 |
3 | import com.easycode8.easylog.core.adapter.LogAttributeMappingAdapter;
4 | import com.easycode8.easylog.core.annotation.EasyLog;
5 | import com.easycode8.easylog.core.annotation.EasyLogProperties;
6 | import com.easycode8.easylog.core.annotation.Tag;
7 | import com.easycode8.easylog.core.cache.LogAttributeCache;
8 | import com.easycode8.easylog.core.util.LogUtils;
9 | import org.springframework.util.CollectionUtils;
10 | import org.springframework.util.StringUtils;
11 |
12 | import java.lang.reflect.Method;
13 | import java.util.AbstractMap;
14 | import java.util.Arrays;
15 | import java.util.List;
16 | import java.util.Map;
17 | import java.util.stream.Collectors;
18 |
19 | public class AnnotationLogAttributeSource extends AbstractCacheLogAttributeSource {
20 |
21 |
22 | private final List mappingAdapters;
23 |
24 | public AnnotationLogAttributeSource(LogAttributeCache logAttributeCache, EasyLogProperties easyLogProperties, List mappingAdapters) {
25 | super(logAttributeCache, easyLogProperties);
26 | this.mappingAdapters = mappingAdapters;
27 | }
28 |
29 |
30 | @Override
31 | public LogAttribute doGetLogAttribute(Method method, Class> targetClass) {
32 | EasyLog easyLog = method.getAnnotation(EasyLog.class);
33 | // EasyLog注解优先级最高
34 | if (easyLog != null) {
35 | String title = StringUtils.isEmpty(easyLog.title()) ? easyLog.value() : easyLog.title();
36 | // 如果都没有定义标题使用默认标题
37 | if (StringUtils.isEmpty(title)) {
38 | title = LogUtils.createDefaultTitle(method, targetClass);
39 | }
40 | Tag[] tags = easyLog.tags();
41 | Map tagMap = null;
42 | if (tags != null) {
43 | tagMap = Arrays.stream(tags)
44 | // 提出key为空的数据
45 | .filter(item -> StringUtils.hasText(item.key()))
46 | // .map(p -> Map.entry(p.key(), p.value()))
47 | .map(p -> new AbstractMap.SimpleEntry<>(p.key(), p.value()))
48 | // 将 Map.Entry 对象转为 Map
49 | .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
50 |
51 | }
52 |
53 | // 如果注解指定是否同步优先级最高,否则读取项目默认配置值
54 | boolean async = false;
55 | switch (easyLog.handleMode()) {
56 | case GLOBAL:
57 | async = easyLogProperties.getAsync();
58 | break;
59 | case ASYNC:
60 | async = true;
61 | break;
62 | case SYNC:
63 | async = false;
64 | break;
65 | }
66 |
67 | return DefaultLogAttribute.builder()
68 | .title(title)
69 | .handler(easyLog.handler())
70 | .template(easyLog.template())
71 | .operator(easyLog.operator())
72 | .async(async)
73 | .tags(tagMap)
74 | .build();
75 | }
76 | // 从映射适配器中获取自定义的日志属性
77 | if (CollectionUtils.isEmpty(mappingAdapters)) {
78 | return null;
79 | }
80 |
81 | for (LogAttributeMappingAdapter mappingAdapter : mappingAdapters) {
82 | LogAttribute logAttribute = mappingAdapter.getLogAttribute(method, targetClass);
83 | if (logAttribute != null) {
84 | return logAttribute;
85 | }
86 | }
87 | return null;
88 | }
89 |
90 | }
91 |
--------------------------------------------------------------------------------
/easy-log-core/src/main/java/com/easycode8/easylog/core/aop/interceptor/DefaultLogAttribute.java:
--------------------------------------------------------------------------------
1 | package com.easycode8.easylog.core.aop.interceptor;
2 |
3 |
4 | import java.io.Serializable;
5 | import java.util.Map;
6 |
7 | public class DefaultLogAttribute implements LogAttribute, Serializable {
8 | /**
9 | * 日志标题
10 | */
11 | String title;
12 | /**
13 | * 日志处理器
14 | */
15 | String handler;
16 | /**
17 | * 日志spel模板
18 | */
19 | String template;
20 | /**
21 | * 日志操作人
22 | */
23 | String operator;
24 | /**
25 | * 是否异步处理日志
26 | */
27 | Boolean async;
28 | /**
29 | * 日志标签用于使用着扩展属性
30 | */
31 | Map tags;
32 | /**
33 | * 是否活跃:非活跃的忽略增强处理
34 | */
35 | Boolean active = true;
36 |
37 |
38 | @Override
39 | public String title() {
40 | return this.title;
41 | }
42 |
43 | @Override
44 | public String handler() {
45 | return this.handler;
46 | }
47 |
48 | @Override
49 | public String template() {
50 | return this.template;
51 | }
52 |
53 | @Override
54 | public String operator() {
55 | return this.operator;
56 | }
57 |
58 | @Override
59 | public boolean async() {
60 | return this.async;
61 | }
62 |
63 | @Override
64 | public Map tags() {
65 | return this.tags;
66 | }
67 |
68 | @Override
69 | public Boolean active() {
70 | return this.active;
71 | }
72 |
73 |
74 | public static Builder builder() {
75 | return new Builder();
76 | }
77 |
78 | public static final class Builder {
79 | String title;
80 | String handler;
81 | String template;
82 | String operator;
83 | boolean async;
84 | Map tags;
85 |
86 | private Builder() {
87 | }
88 |
89 | public static Builder aDefaultLogAttribute() {
90 | return new Builder();
91 | }
92 |
93 | public Builder title(String title) {
94 | this.title = title;
95 | return this;
96 | }
97 |
98 | public Builder handler(String handler) {
99 | this.handler = handler;
100 | return this;
101 | }
102 |
103 | public Builder template(String template) {
104 | this.template = template;
105 | return this;
106 | }
107 |
108 | public Builder operator(String operator) {
109 | this.operator = operator;
110 | return this;
111 | }
112 |
113 | public Builder async(boolean async) {
114 | this.async = async;
115 | return this;
116 | }
117 |
118 | public Builder tags(Map tags) {
119 | this.tags = tags;
120 | return this;
121 | }
122 |
123 | public DefaultLogAttribute build() {
124 | DefaultLogAttribute defaultLogAttribute = new DefaultLogAttribute();
125 | defaultLogAttribute.handler = this.handler;
126 | defaultLogAttribute.template = this.template;
127 | defaultLogAttribute.title = this.title;
128 | defaultLogAttribute.tags = this.tags;
129 | defaultLogAttribute.async = this.async;
130 | defaultLogAttribute.operator = this.operator;
131 | return defaultLogAttribute;
132 | }
133 | }
134 |
135 | public String getTitle() {
136 | return title;
137 | }
138 |
139 | public void setTitle(String title) {
140 | this.title = title;
141 | }
142 |
143 | public String getHandler() {
144 | return handler;
145 | }
146 |
147 | public void setHandler(String handler) {
148 | this.handler = handler;
149 | }
150 |
151 | public String getTemplate() {
152 | return template;
153 | }
154 |
155 | public void setTemplate(String template) {
156 | this.template = template;
157 | }
158 |
159 | public String getOperator() {
160 | return operator;
161 | }
162 |
163 | public void setOperator(String operator) {
164 | this.operator = operator;
165 | }
166 |
167 | public Boolean getAsync() {
168 | return async;
169 | }
170 |
171 | public void setAsync(Boolean async) {
172 | this.async = async;
173 | }
174 |
175 | public Map getTags() {
176 | return tags;
177 | }
178 |
179 | public void setTags(Map tags) {
180 | this.tags = tags;
181 | }
182 |
183 | public Boolean getActive() {
184 | return active;
185 | }
186 |
187 | public void setActive(Boolean active) {
188 | this.active = active;
189 | }
190 |
191 |
192 | }
193 |
--------------------------------------------------------------------------------
/easy-log-core/src/main/java/com/easycode8/easylog/core/aop/interceptor/LogAspectSupport.java:
--------------------------------------------------------------------------------
1 | package com.easycode8.easylog.core.aop.interceptor;
2 |
3 |
4 | import com.easycode8.easylog.core.handler.LogDataHandler;
5 | import com.easycode8.easylog.core.LogHolder;
6 | import com.easycode8.easylog.core.LogInfo;
7 | import com.easycode8.easylog.core.LogStopWatch;
8 | import com.easycode8.easylog.core.provider.OperatorProvider;
9 | import com.easycode8.easylog.core.trace.LogTracer;
10 | import com.easycode8.easylog.core.util.LogUtils;
11 | import org.slf4j.Logger;
12 | import org.slf4j.LoggerFactory;
13 | import org.springframework.beans.factory.BeanFactory;
14 | import org.springframework.beans.factory.BeanFactoryAware;
15 | import org.springframework.beans.factory.InitializingBean;
16 | import org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils;
17 | import org.springframework.lang.Nullable;
18 | import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
19 | import org.springframework.util.StringUtils;
20 |
21 | import java.lang.reflect.Method;
22 |
23 | /**
24 | * BeanFactoryAware 作用是通过日志注解属性中定义bean名称去spring容器中寻找对应的处理器
25 | * InitializingBean 校验必须需要的资源
26 | */
27 | public abstract class LogAspectSupport implements BeanFactoryAware , InitializingBean {
28 | private static final Logger LOGGER = LoggerFactory.getLogger(LogAspectSupport.class);
29 |
30 |
31 | private LogAttributeSource logAttributeSource;
32 |
33 | private LogDataHandler logDataHandler;
34 |
35 | private ThreadPoolTaskExecutor threadPoolTaskExecutor;
36 |
37 | private OperatorProvider operatorProvider;
38 |
39 | private LogTracer logTracer;
40 |
41 | @Nullable
42 | private BeanFactory beanFactory;
43 |
44 | protected Object invoke(Method method, @Nullable Class> targetClass, Object[] args,
45 | Object targetObject,
46 | final InvocationCallback invocation) throws Throwable {
47 | // 获取日志属性
48 | LogAttribute logAttribute = getLogAttributeSource().getLogAttribute(method, targetClass);
49 | // 如果日志处理非活跃忽略增强处理
50 | if (!logAttribute.active()) {
51 | return invocation.proceedWithLog();
52 | }
53 | final LogDataHandler handler = this.determineLogHandleAdapter(logAttribute);
54 |
55 | // 创建日志信息
56 | long startTime = System.currentTimeMillis();
57 | LogInfo info = handler.init(logAttribute, method, args, targetClass);
58 | boolean isAsync = logAttribute.async();
59 |
60 |
61 | Object returnValue = null;
62 | try {
63 | // 日志trace初始化
64 | logTracer.init(info);
65 | LogHolder.push(info);
66 | // 获取处理器bean
67 | String handlerName = handler.getClass().getSimpleName();
68 | LogStopWatch stopWatch = new LogStopWatch(LOGGER, logAttribute.title());
69 | stopWatch.start("{}.init() //日志初始化属性", handlerName);
70 | LogUtils.initLog(info, logAttribute, method, args, targetClass);
71 | // 设置操作人
72 | this.chooseOperatorIfEmpty(info, operatorProvider);
73 | info.setStatus(LogInfo.STATUS_INIT);
74 |
75 | // This is an around advice: Invoke the next interceptor in the chain.
76 | // This will normally result in a target object being invoked.
77 | stopWatch.stop().start("{}.before() //日志前处理", handlerName);
78 | handler.before(info, method, args, targetClass, targetObject);
79 | info.setStatus(LogInfo.STATUS_BEFORE);
80 | stopWatch.stop().start("{} //{} param:{}" , info.getMethod(), info.getTitle(), info.getParams());;
81 |
82 | logTracer.start(info);
83 | returnValue = invocation.proceedWithLog();
84 | info.setTimeout(System.currentTimeMillis() - startTime);
85 |
86 | stopWatch.stop().start(handlerName + ".after() //日志后处理");
87 |
88 | info.setStatus(LogInfo.STATUS_FINISH);
89 | // 如果是登录接口可能登录后才有用户信息,所以这个补充设置一次
90 | this.chooseOperatorIfEmpty(info, operatorProvider);
91 | if (isAsync) {
92 | final Object retVal = returnValue;
93 | threadPoolTaskExecutor.execute(() -> handler.after(info, method, targetClass, retVal));
94 | } else {
95 | handler.after(info, method, targetClass, returnValue);
96 | }
97 |
98 | stopWatch.stop().showDetail();
99 |
100 | } catch (Throwable ex) {
101 |
102 | info.setTimeout(System.currentTimeMillis() - startTime);
103 | if (LogInfo.STATUS_UN_INIT == info.getStatus()) {
104 | LOGGER.error("[easy-log] handle init {} error:{}", LogUtils.createDefaultTitle(method, targetClass), ex.getMessage(), ex);
105 | }
106 | if (LogInfo.STATUS_INIT == info.getStatus()) {
107 | LOGGER.error("[easy-log] handle before {} error:{}", LogUtils.createDefaultTitle(method, targetClass), ex.getMessage(), ex);
108 | } else if (LogInfo.STATUS_BEFORE == info.getStatus()) { // 业务执行不成功记录失败原因
109 | info.setException(ex.getMessage());
110 | if (isAsync) {
111 | final Object retVal = returnValue;
112 | threadPoolTaskExecutor.execute(() -> handler.after(info, method, targetClass, retVal));
113 | } else {
114 | handler.after(info, method, targetClass, returnValue);
115 | }
116 |
117 | } else if (LogInfo.STATUS_FINISH == info.getStatus()) { // 业务执行成功,但是日志后处理失败,提示错误日志。这时候业务会因为异常回滚操作
118 | LOGGER.error("[easy-log] handle after {} error:{}", LogUtils.createDefaultTitle(method, targetClass), ex.getMessage(), ex);
119 | }
120 |
121 | throw ex;
122 | } finally {
123 | LogHolder.poll();
124 | logTracer.finish(info);
125 | }
126 |
127 | return returnValue;
128 | }
129 |
130 | protected void chooseOperatorIfEmpty(LogInfo info, OperatorProvider operatorProvider) {
131 | // 如果操作人为空,尝试从上下文获取
132 | if (operatorProvider != null && StringUtils.isEmpty(info.getOperator())) {
133 | info.setOperator(operatorProvider.currentOperator());
134 | }
135 |
136 | }
137 |
138 |
139 | /**
140 | * 支持注解中指定 自定义日志处理器
141 | * @param logAttribute
142 | * @return
143 | */
144 | protected LogDataHandler determineLogHandleAdapter(LogAttribute logAttribute) {
145 | if (logAttribute == null || this.beanFactory == null) {
146 | return getLogDataHandler();
147 | }
148 | String qualifier = logAttribute.handler();
149 | if (StringUtils.hasText(qualifier)) {
150 | // TODO 补充从缓存中获取 see org.springframework.transaction.interceptor.TransactionAspectSupport.determineQualifiedTransactionManager
151 | LogDataHandler logDataHandler = BeanFactoryAnnotationUtils.qualifiedBeanOfType(
152 | beanFactory, LogDataHandler.class, qualifier);
153 |
154 |
155 | return logDataHandler;
156 |
157 | } else {
158 | LogDataHandler defaultLogDataHandler = getLogDataHandler();
159 | if (defaultLogDataHandler == null) {
160 | defaultLogDataHandler = this.beanFactory.getBean(LogDataHandler.class);
161 | }
162 | return defaultLogDataHandler;
163 |
164 | }
165 |
166 | }
167 |
168 |
169 | /**
170 | * Simple callback interface for proceeding with the target invocation.
171 | * Concrete interceptors/aspects adapt this to their invocation mechanism.
172 | */
173 | @FunctionalInterface
174 | protected interface InvocationCallback {
175 |
176 | @Nullable
177 | Object proceedWithLog() throws Throwable;
178 | }
179 |
180 | @Override
181 | public void afterPropertiesSet() {
182 | if (getLogDataHandler() == null && this.beanFactory == null) {
183 | throw new IllegalStateException(
184 | "Set the 'LogDataHandler' property or make sure to run within a BeanFactory " +
185 | "containing a LogDataHandler bean!");
186 | }
187 | if (getLogAttributeSource() == null) {
188 | throw new IllegalStateException(
189 | "Either 'LogAttributeSource' or 'LogAttribute' is required: " +
190 | "If there are no log methods, then don't use a log aspect.");
191 | }
192 | }
193 |
194 | @Override
195 | public void setBeanFactory(BeanFactory beanFactory) {
196 | this.beanFactory = beanFactory;
197 | }
198 |
199 | public LogAttributeSource getLogAttributeSource() {
200 | return logAttributeSource;
201 | }
202 |
203 | public void setLogAttributeSource(LogAttributeSource logAttributeSource) {
204 | this.logAttributeSource = logAttributeSource;
205 | }
206 |
207 | public LogDataHandler getLogDataHandler() {
208 | return logDataHandler;
209 | }
210 |
211 | public void setLogDataHandler(LogDataHandler logDataHandler) {
212 | this.logDataHandler = logDataHandler;
213 | }
214 |
215 | public void setThreadPoolTaskExecutor(ThreadPoolTaskExecutor threadPoolTaskExecutor) {
216 | this.threadPoolTaskExecutor = threadPoolTaskExecutor;
217 | }
218 |
219 | public void setOperatorProvider(OperatorProvider operatorProvider) {
220 | this.operatorProvider = operatorProvider;
221 | }
222 |
223 | public void setLogTracer(LogTracer logTracer) {
224 | this.logTracer = logTracer;
225 | }
226 | }
227 |
--------------------------------------------------------------------------------
/easy-log-core/src/main/java/com/easycode8/easylog/core/aop/interceptor/LogAttribute.java:
--------------------------------------------------------------------------------
1 | package com.easycode8.easylog.core.aop.interceptor;
2 |
3 |
4 | import java.util.Map;
5 |
6 | public interface LogAttribute {
7 | /** 日志标题*/
8 | String title();
9 | /** 日志处理器*/
10 | String handler();
11 | /** 日志spel模板*/
12 | String template();
13 | /** 日志操作人*/
14 | String operator();
15 | /** 是否异步处理日志*/
16 | boolean async();
17 | /** 日志标签用于使用着扩展属性*/
18 | Map tags();
19 | /**是否活跃:非活跃的忽略增强处理*/
20 | Boolean active();
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/easy-log-core/src/main/java/com/easycode8/easylog/core/aop/interceptor/LogAttributeSource.java:
--------------------------------------------------------------------------------
1 | package com.easycode8.easylog.core.aop.interceptor;
2 |
3 | import java.lang.reflect.Method;
4 |
5 | public interface LogAttributeSource {
6 | LogAttribute getLogAttribute(Method method, Class> targetClass);
7 | }
8 |
--------------------------------------------------------------------------------
/easy-log-core/src/main/java/com/easycode8/easylog/core/aop/interceptor/LogAttributeSourcePointcut.java:
--------------------------------------------------------------------------------
1 | package com.easycode8.easylog.core.aop.interceptor;
2 |
3 | import org.springframework.aop.support.StaticMethodMatcherPointcut;
4 | import org.springframework.lang.Nullable;
5 |
6 | import java.io.Serializable;
7 | import java.lang.reflect.Method;
8 |
9 | public abstract class LogAttributeSourcePointcut extends StaticMethodMatcherPointcut implements Serializable {
10 |
11 | /**
12 | * 是否作为切点的判断--通过被增强的方法和目标类是否能够提取出日志属性。
13 | * 将是否能够提取出日志属性能力通过接口暴露出去
14 | * @param method
15 | * @param targetClass
16 | * @return
17 | */
18 | @Override
19 | public boolean matches(Method method, Class> targetClass) {
20 | LogAttributeSource source = getLogAttributeSource();
21 | return (source == null || source.getLogAttribute(method, targetClass) != null);
22 | }
23 |
24 | @Nullable
25 | protected abstract LogAttributeSource getLogAttributeSource();
26 | }
27 |
--------------------------------------------------------------------------------
/easy-log-core/src/main/java/com/easycode8/easylog/core/aop/interceptor/LogMethodInterceptor.java:
--------------------------------------------------------------------------------
1 | package com.easycode8.easylog.core.aop.interceptor;
2 |
3 | import org.aopalliance.intercept.MethodInterceptor;
4 | import org.aopalliance.intercept.MethodInvocation;
5 | import org.springframework.aop.support.AopUtils;
6 |
7 | /**
8 | * MethodInterceptor org.aopalliance.aop.Advice
9 | */
10 | public class LogMethodInterceptor extends LogAspectSupport implements MethodInterceptor {
11 | @Override
12 | public Object invoke(MethodInvocation invocation) throws Throwable {
13 | Class> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
14 | return invoke(invocation.getMethod(), targetClass, invocation.getArguments(), invocation.getThis(), invocation::proceed);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/easy-log-core/src/main/java/com/easycode8/easylog/core/aop/interceptor/TimingLogAttributeSourcePointcut.java:
--------------------------------------------------------------------------------
1 | package com.easycode8.easylog.core.aop.interceptor;
2 |
3 | import java.lang.reflect.Method;
4 | import java.util.concurrent.atomic.AtomicLong;
5 |
6 | public abstract class TimingLogAttributeSourcePointcut extends LogAttributeSourcePointcut {
7 | private static AtomicLong totalMatchesTime = new AtomicLong(0);
8 |
9 | @Override
10 | public boolean matches(Method method, Class> targetClass) {
11 | long startTime = System.currentTimeMillis();
12 |
13 | boolean result = super.matches(method, targetClass);
14 |
15 | long endTime = System.currentTimeMillis();
16 | long elapsedTime = endTime - startTime;
17 | totalMatchesTime.addAndGet(elapsedTime);
18 |
19 | return result;
20 | }
21 |
22 | // 在项目启动后调用此方法打印总耗时
23 | public static long getTotalAopMatchesTime() {
24 | //System.out.println("Total matches() execution time: " + totalMatchesTime.get() + " ms");
25 | return totalMatchesTime.get();
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/easy-log-core/src/main/java/com/easycode8/easylog/core/cache/LogAttributeCache.java:
--------------------------------------------------------------------------------
1 | package com.easycode8.easylog.core.cache;
2 |
3 | import com.easycode8.easylog.core.aop.interceptor.LogAttribute;
4 |
5 | import java.util.Map;
6 |
7 | public interface LogAttributeCache {
8 |
9 | boolean containsKey(String key);
10 | void put(String key, LogAttribute attribute);
11 | LogAttribute get(String key);
12 |
13 | Map getAll();
14 | }
15 |
--------------------------------------------------------------------------------
/easy-log-core/src/main/java/com/easycode8/easylog/core/cache/LogAttributeCacheConfiguration.java:
--------------------------------------------------------------------------------
1 | package com.easycode8.easylog.core.cache;
2 |
3 | import com.easycode8.easylog.core.annotation.EasyLogProperties;
4 | import org.slf4j.Logger;
5 | import org.slf4j.LoggerFactory;
6 | import org.springframework.beans.factory.annotation.Qualifier;
7 | import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
8 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
9 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
10 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
11 | import org.springframework.context.annotation.Bean;
12 | import org.springframework.data.redis.connection.RedisConnectionFactory;
13 | import org.springframework.data.redis.core.RedisTemplate;
14 | import org.springframework.data.redis.listener.PatternTopic;
15 | import org.springframework.data.redis.listener.RedisMessageListenerContainer;
16 |
17 | public abstract class LogAttributeCacheConfiguration {
18 | private static final Logger LOGGER = LoggerFactory.getLogger(LogAttributeCacheConfiguration.class);
19 |
20 | @ConditionalOnProperty(value = "spring.easy-log.cache.redis.enabled", havingValue = "true", matchIfMissing = true)
21 | @ConditionalOnClass(RedisTemplate.class)
22 | public static class RedisCacheConfiguration {
23 | @Bean
24 | @ConditionalOnBean(RedisTemplate.class)
25 | @ConditionalOnMissingBean(LogAttributeCache.class)
26 | public LogAttributeCache logAttributeCache (@Qualifier("redisTemplate") RedisTemplate redisTemplate, EasyLogProperties easyLogProperties) {
27 | LOGGER.info("[easy-log]启动redis缓存日志属性(spring.easy-log.cache.redis.enabled 可控制关闭)");
28 | return new LogAttributeRedisCache(redisTemplate, easyLogProperties);
29 | }
30 |
31 |
32 | // 配置redis事件监听器
33 | @Bean
34 | public RedisMessageListenerContainer redisMessageListenerContainer(
35 | RedisConnectionFactory connectionFactory, LogAttributeRedisCache messageListener) {
36 | RedisMessageListenerContainer container = new RedisMessageListenerContainer();
37 | container.setConnectionFactory(connectionFactory);
38 | // 配置监听的频道或模式
39 | container.addMessageListener(messageListener, new PatternTopic("easy-log.*"));
40 | return container;
41 | }
42 | }
43 |
44 | public static class LocalCacheConfiguration {
45 | @Bean
46 | @ConditionalOnMissingBean(LogAttributeCache.class)
47 | public LogAttributeCache logAttributeCache () {
48 | LOGGER.info("[easy-log]日志属性--使用本地内存缓存");
49 | return new LogAttributeMemoryCache();
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/easy-log-core/src/main/java/com/easycode8/easylog/core/cache/LogAttributeMemoryCache.java:
--------------------------------------------------------------------------------
1 | package com.easycode8.easylog.core.cache;
2 |
3 |
4 |
5 | import com.easycode8.easylog.core.aop.interceptor.LogAttribute;
6 |
7 | import java.util.Map;
8 | import java.util.concurrent.ConcurrentHashMap;
9 |
10 | public class LogAttributeMemoryCache implements LogAttributeCache{
11 |
12 | private ConcurrentHashMap cacheMap = new ConcurrentHashMap<>();
13 | @Override
14 | public boolean containsKey(String key) {
15 | return cacheMap.containsKey(key);
16 | }
17 |
18 | @Override
19 | public void put(String key, LogAttribute attribute) {
20 | cacheMap.put(key, attribute);
21 | }
22 |
23 | @Override
24 | public LogAttribute get(String key) {
25 | return cacheMap.get(key);
26 | }
27 |
28 | @Override
29 | public Map getAll() {
30 | return cacheMap;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/easy-log-core/src/main/java/com/easycode8/easylog/core/cache/LogAttributeRedisCache.java:
--------------------------------------------------------------------------------
1 | package com.easycode8.easylog.core.cache;
2 |
3 | import com.alibaba.fastjson.JSON;
4 | import com.easycode8.easylog.core.annotation.EasyLogProperties;
5 | import com.easycode8.easylog.core.aop.interceptor.DefaultLogAttribute;
6 | import com.easycode8.easylog.core.aop.interceptor.LogAttribute;
7 | import org.apache.commons.collections.CollectionUtils;
8 | import org.slf4j.Logger;
9 | import org.slf4j.LoggerFactory;
10 | import org.springframework.boot.ApplicationArguments;
11 | import org.springframework.boot.ApplicationRunner;
12 | import org.springframework.data.redis.connection.Message;
13 | import org.springframework.data.redis.connection.MessageListener;
14 | import org.springframework.data.redis.core.HashOperations;
15 | import org.springframework.data.redis.core.RedisTemplate;
16 |
17 | import java.util.HashSet;
18 | import java.util.List;
19 | import java.util.Map;
20 | import java.util.concurrent.ConcurrentHashMap;
21 | import java.util.stream.Collectors;
22 |
23 | public class LogAttributeRedisCache implements LogAttributeCache, MessageListener, ApplicationRunner {
24 | private final static Logger LOGGER = LoggerFactory.getLogger(LogAttributeRedisCache.class);
25 |
26 | private ConcurrentHashMap cacheMap = new ConcurrentHashMap<>();
27 |
28 | private final RedisTemplate redisTemplate;
29 | private final EasyLogProperties easyLogProperties;
30 |
31 | private Boolean inited = false;
32 | private String hashKey;
33 |
34 | public LogAttributeRedisCache(RedisTemplate redisTemplate, EasyLogProperties easyLogProperties) {
35 | this.redisTemplate = redisTemplate;
36 | this.easyLogProperties = easyLogProperties;
37 |
38 | this.hashKey = easyLogProperties.getCache().getKeyPrefix();
39 | }
40 |
41 | @Override
42 | public boolean containsKey(String key) {
43 | return cacheMap.containsKey(key);
44 | }
45 |
46 | @Override
47 | public void put(String key, LogAttribute attribute) {
48 | if (inited) {
49 | // 1.配置同步更新redis中配置
50 | this.pushToHash(key, attribute);
51 |
52 | // 2.通过redis发送事件,通知其他节点更新配置
53 | redisTemplate.convertAndSend("easy-log.update", (DefaultLogAttribute) attribute);
54 | } else {
55 | cacheMap.put(key, (DefaultLogAttribute) attribute);
56 | }
57 | }
58 |
59 | @Override
60 | public LogAttribute get(String key) {
61 | return cacheMap.get(key);
62 | }
63 |
64 | @Override
65 | public Map getAll() {
66 | Map resultMap = this.getAllFromHash().entrySet()
67 | .stream()
68 | .collect(Collectors.toMap(
69 | Map.Entry::getKey,
70 | entry -> entry.getValue()
71 | ));
72 | return resultMap;
73 | }
74 |
75 | @Override
76 | public void run(ApplicationArguments args) throws Exception {
77 | this.inited = true;
78 | this.initConfig();
79 | }
80 |
81 | @Override
82 | public void onMessage(Message message, byte[] pattern) {
83 | String channel = new String(message.getChannel());
84 | String body = new String(message.getBody());
85 | DefaultLogAttribute defaultLogAttribute = JSON.parseObject(body, DefaultLogAttribute.class);
86 | LOGGER.info("[easy-log] cache receive redis message. channel[{}] content:{}", channel, body);
87 | cacheMap.put(defaultLogAttribute.title(), defaultLogAttribute);
88 | }
89 |
90 | private void initConfig() {
91 |
92 |
93 | Map redisCacheMap = this.getAllFromHash();
94 |
95 | if (redisCacheMap == null || redisCacheMap.isEmpty()) {
96 |
97 | LOGGER.info("[easy-log]通过redis初始化日志属性--开始 cache:{}", this.cacheMap);
98 | this.pushAllToHash(this.cacheMap);
99 | LOGGER.info("[easy-log]通过redis初始化日志属性---成功");
100 | } else if (easyLogProperties.getCache().getDropFirst()) {
101 | LOGGER.warn("[easy-log]使用本地配置强制初始化集群配置--开始");
102 | this.pushAllToHash(this.cacheMap);
103 | LOGGER.warn("[easy-log]使用本地配置强制初始化集群配置--成功");
104 | } else {
105 | LOGGER.info("[easy-log]通过redis刷新数据源配置--开始");
106 | this.reloadLocalConfig();
107 | LOGGER.info("[easy-log]通过redis刷新数据源配置--成功");
108 | }
109 | }
110 |
111 | // private void pushConfig(List attributes) {
112 | // redisTemplate.opsForValue().set(easyLogProperties.getCache().getKeyPrefix() + applicationName, JSON.toJSONString(attributes));
113 | // LOGGER.info("[easy-log] push data to redis success!");
114 | // }
115 |
116 | protected void reloadLocalConfig() {
117 |
118 | Map redisCacheMap = this.getAllFromHash();
119 |
120 | // 远程多出key配置 redis要删除 说明本地代码已经移除了对应的日志记录点/ 也有可能某个研发提交了代码,其他人没有更新
121 | // 这种情况避免误判,只删除redis缓存,日志记录实际上还是使用内存来判断
122 | List waitDeleteSet = (List) CollectionUtils.subtract(redisCacheMap.keySet(), new HashSet(cacheMap.keySet()));
123 | for (String key : waitDeleteSet) {
124 | LOGGER.info("[easy-log] redis remove cache key:{}", key);
125 | this.deleteFromHash(key);
126 | redisCacheMap.remove(key);
127 | }
128 | // 本地和远程都有的部分,使用远程来覆盖
129 | this.cacheMap.putAll(redisCacheMap);
130 |
131 | // 本地多出key配置 补充到 redis中 说明是新补充的日志记录点,或者其他人节点放弃记录了为来得及更新。
132 | List waitAddSet = (List) CollectionUtils.subtract(new HashSet(cacheMap.keySet()), redisCacheMap.keySet());
133 | for (String key : waitAddSet) {
134 | LOGGER.info("[easy-log] redis push cache key:{}", key);
135 | this.pushToHash(key, cacheMap.get(key));
136 | }
137 | }
138 |
139 |
140 | public void pushToHash(String key, Object value) {
141 | HashOperations hashOperations = redisTemplate.opsForHash();
142 | hashOperations.put(hashKey, key, value);
143 | }
144 |
145 | public void pushAllToHash(Map map) {
146 | HashOperations hashOperations = redisTemplate.opsForHash();
147 | hashOperations.putAll(hashKey, map);
148 | }
149 |
150 | public Object getFromHash(String key) {
151 | HashOperations hashOperations = redisTemplate.opsForHash();
152 | return hashOperations.get(hashKey, key);
153 | }
154 |
155 | public Map getAllFromHash() {
156 | HashOperations hashOperations = redisTemplate.opsForHash();
157 | return hashOperations.entries(hashKey);
158 | }
159 |
160 | public void deleteFromHash(String key) {
161 | HashOperations hashOperations = redisTemplate.opsForHash();
162 | hashOperations.delete(hashKey, key);
163 | }
164 | }
165 |
--------------------------------------------------------------------------------
/easy-log-core/src/main/java/com/easycode8/easylog/core/constants/HandleMode.java:
--------------------------------------------------------------------------------
1 | package com.easycode8.easylog.core.constants;
2 |
3 | /**
4 | * 日志处理模式枚举
5 | */
6 | public enum HandleMode {
7 | GLOBAL,
8 | ASYNC,
9 | SYNC
10 | }
11 |
--------------------------------------------------------------------------------
/easy-log-core/src/main/java/com/easycode8/easylog/core/handler/DefaultLogHandler.java:
--------------------------------------------------------------------------------
1 | package com.easycode8.easylog.core.handler;
2 |
3 | import com.easycode8.easylog.core.LogInfo;
4 | import com.easycode8.easylog.core.aop.interceptor.LogAttribute;
5 | import org.slf4j.Logger;
6 | import org.slf4j.LoggerFactory;
7 |
8 | import java.lang.reflect.Method;
9 |
10 | public class DefaultLogHandler implements LogDataHandler {
11 | private static final Logger LOGGER = LoggerFactory.getLogger(DefaultLogHandler.class);
12 |
13 |
14 | @Override
15 | public LogInfo init(LogAttribute logAttribute, Method method, Object[] args, Class> targetClass) {
16 | return new LogInfo();
17 | }
18 |
19 | @Override
20 | public void before(LogInfo info, Method method, Object[] args, Class> targetClass, Object targetObject) {
21 | LOGGER.info("[easy-log][{}]--begin operator:[{}] param:{}", info.getTitle(), info.getOperator(), info.getParams());
22 | }
23 |
24 | @Override
25 | public void after(LogInfo info, Method method, Class> targetClass, Object returnValue) {
26 | if (LogInfo.STATUS_FINISH == info.getStatus()) {
27 | if (LogInfo.TYPE_DAO.equals(info.getType())) {
28 | LOGGER.info("[easy-log][{}]--end timeout:{} dataSnapshot:{}", info.getTitle(), info.getTimeout(), info.getDataSnapshot());
29 | } else {
30 | LOGGER.info("[easy-log][{}]--end timeout:{} ", info.getTitle(), info.getTimeout());
31 | } } else {
32 | LOGGER.warn("[easy-log][{}]--end timeout:{} exception:{}", info.getTitle(), info.getTimeout(), info.getException());
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/easy-log-core/src/main/java/com/easycode8/easylog/core/handler/LogDataHandler.java:
--------------------------------------------------------------------------------
1 | package com.easycode8.easylog.core.handler;
2 |
3 | import com.easycode8.easylog.core.LogInfo;
4 | import com.easycode8.easylog.core.aop.interceptor.LogAttribute;
5 |
6 | import java.lang.reflect.Method;
7 |
8 | /**
9 | * 日志处理器接口
10 | * @param
11 | */
12 | public interface LogDataHandler {
13 |
14 | /**
15 | * 初始化日志对象
16 | * @param logAttribute
17 | * @param method
18 | * @param targetClass
19 | * @return
20 | */
21 | T init(LogAttribute logAttribute, Method method, Object[] args, Class> targetClass);
22 |
23 | /**
24 | * 执行方法前的日志处理
25 | * @param info
26 | * @param method
27 | * @param args
28 | * @param targetClass
29 | * @param targetObject
30 | */
31 | void before(T info, Method method, Object[] args, Class> targetClass, Object targetObject);
32 |
33 | /**
34 | * 执行方法后的日志处理
35 | * @param info
36 | * @param method
37 | * @param targetClass
38 | * @param returnValue
39 | */
40 | void after(T info, Method method, Class> targetClass, Object returnValue);
41 | }
42 |
--------------------------------------------------------------------------------
/easy-log-core/src/main/java/com/easycode8/easylog/core/monitor/EasyLogApplicationInfoPrinter.java:
--------------------------------------------------------------------------------
1 | package com.easycode8.easylog.core.monitor;
2 |
3 |
4 | import com.easycode8.easylog.core.annotation.EasyLogProperties;
5 | import com.easycode8.easylog.core.aop.interceptor.TimingLogAttributeSourcePointcut;
6 | import org.slf4j.Logger;
7 | import org.slf4j.LoggerFactory;
8 | import org.springframework.beans.factory.annotation.Value;
9 | import org.springframework.boot.availability.AvailabilityChangeEvent;
10 | import org.springframework.boot.availability.ReadinessState;
11 | import org.springframework.context.ApplicationListener;
12 | import org.springframework.context.ConfigurableApplicationContext;
13 | import org.springframework.core.env.ConfigurableEnvironment;
14 | import org.springframework.core.env.Environment;
15 |
16 | import java.lang.management.ManagementFactory;
17 | import java.lang.management.MemoryUsage;
18 | import java.util.Optional;
19 |
20 | /**
21 | * 打印应用相关上下文信息
22 | */
23 | public class EasyLogApplicationInfoPrinter implements ApplicationListener> {
24 | private static final Logger LOGGER = LoggerFactory.getLogger(EasyLogApplicationInfoPrinter.class);
25 |
26 | @Value("${easy-log.monitor.printServerInfo:true}")
27 | private boolean printServerInfo;
28 |
29 |
30 | private EasyLogProperties easyLogProperties;
31 |
32 | @Override
33 | public void onApplicationEvent(AvailabilityChangeEvent availabilityChangeEvent) {
34 | // ReadinessState.ACCEPTING_TRAFFIC 表示可以接受流量
35 | if (availabilityChangeEvent.getState() == ReadinessState.ACCEPTING_TRAFFIC && this.printServerInfo) {
36 | try {
37 | ConfigurableApplicationContext context = (ConfigurableApplicationContext) availabilityChangeEvent.getSource();
38 |
39 | ConfigurableEnvironment environment = context.getEnvironment();
40 | String serverPort = getApplicationServerPort(environment);
41 | LOGGER.info("==============================");
42 | LOGGER.info("[easy-log] aop扫描加载耗时:{}毫秒", TimingLogAttributeSourcePointcut.getTotalAopMatchesTime());
43 | // 获取Java虚拟机内存管理的MXBean
44 | MemoryUsage heapUsage = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage();
45 | LOGGER.info("[easy-log] JVM堆内存使用情况: 初始值={}MB, 最大值={}MB, 当前值={}MB",
46 | heapUsage.getInit() / 1024 / 1024,
47 | heapUsage.getMax() / 1024 / 1024,
48 | heapUsage.getUsed() / 1024 / 1024);
49 |
50 | LOGGER.info("[easy-log] 项目启动端口: {},项目启动耗时: {} 秒", serverPort, ManagementFactory.getRuntimeMXBean().getUptime() / 1000f);
51 | LOGGER.info("==============================");
52 | } catch (Exception e) {
53 | // ignore
54 | }
55 | }
56 | }
57 |
58 | public static String getApplicationServerPort(Environment environment) {
59 | return Optional.ofNullable(environment.getProperty("local.server.port"))
60 | .orElse(environment.getProperty("server.port"));
61 | }
62 |
63 | }
--------------------------------------------------------------------------------
/easy-log-core/src/main/java/com/easycode8/easylog/core/provider/OperatorProvider.java:
--------------------------------------------------------------------------------
1 | package com.easycode8.easylog.core.provider;
2 |
3 | /**
4 | * 操作人提供接口
5 | */
6 | public interface OperatorProvider {
7 |
8 | /**获取当前操作人*/
9 | String currentOperator();
10 | }
11 |
--------------------------------------------------------------------------------
/easy-log-core/src/main/java/com/easycode8/easylog/core/provider/SessionOperatorProvider.java:
--------------------------------------------------------------------------------
1 | package com.easycode8.easylog.core.provider;
2 |
3 | import com.easycode8.easylog.core.annotation.EasyLogProperties;
4 | import com.easycode8.easylog.core.util.SpringSpelUtils;
5 | import org.springframework.util.StringUtils;
6 | import org.springframework.web.context.request.RequestContextHolder;
7 | import org.springframework.web.context.request.ServletRequestAttributes;
8 |
9 | import javax.servlet.http.HttpServletRequest;
10 |
11 | /**
12 | * 根据session提取操作人信息
13 | */
14 | public class SessionOperatorProvider implements OperatorProvider{
15 | private static final String PREFIX_SESSION = "SESSION.";
16 | private static final String PREFIX_HEADER = "HEADER.";
17 | private final EasyLogProperties easyLogProperties;
18 |
19 |
20 | public SessionOperatorProvider(EasyLogProperties easyLogProperties) {
21 | this.easyLogProperties = easyLogProperties;
22 | }
23 |
24 | @Override
25 | public String currentOperator() {
26 | String operator = "";
27 | if (StringUtils.isEmpty(easyLogProperties.getOperator())) {
28 | return operator;
29 | }
30 | if (easyLogProperties.getOperator().startsWith(PREFIX_SESSION)) {
31 | String[] info = easyLogProperties.getOperator().replace(PREFIX_SESSION, "").split("\\.", 2);
32 | operator = (String) SpringSpelUtils.getSessionAttribute(info[0], info[1]);
33 | }
34 |
35 | if (easyLogProperties.getOperator().startsWith(PREFIX_HEADER)) {
36 | String expression = easyLogProperties.getOperator().replace(PREFIX_HEADER, "");
37 | // 项目启动后立马执行业务处理,可能是非web请求这里做下兼容
38 | if (RequestContextHolder.getRequestAttributes() == null) {
39 | return operator;
40 | }
41 | HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
42 | operator = request.getHeader(expression);
43 | }
44 | return operator;
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/easy-log-core/src/main/java/com/easycode8/easylog/core/trace/LogTracer.java:
--------------------------------------------------------------------------------
1 | package com.easycode8.easylog.core.trace;
2 |
3 |
4 | import com.easycode8.easylog.core.LogInfo;
5 |
6 | public interface LogTracer {
7 | void init(LogInfo logInfo);
8 | void start(LogInfo logInfo);
9 | void finish(LogInfo logInfo);
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/easy-log-core/src/main/java/com/easycode8/easylog/core/trace/NoneLogTracer.java:
--------------------------------------------------------------------------------
1 | package com.easycode8.easylog.core.trace;
2 |
3 | import com.easycode8.easylog.core.LogInfo;
4 |
5 | public class NoneLogTracer implements LogTracer{
6 | @Override
7 | public void init(LogInfo logInfo) {
8 |
9 | }
10 |
11 | @Override
12 | public void start(LogInfo logInfo) {
13 |
14 | }
15 |
16 | @Override
17 | public void finish(LogInfo logInfo) {
18 |
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/easy-log-core/src/main/java/com/easycode8/easylog/core/util/LogUtils.java:
--------------------------------------------------------------------------------
1 | package com.easycode8.easylog.core.util;
2 |
3 | import com.alibaba.fastjson.JSON;
4 | import com.alibaba.fastjson.JSONObject;
5 | import com.easycode8.easylog.core.LogInfo;
6 | import com.easycode8.easylog.core.aop.interceptor.LogAttribute;
7 | import org.springframework.stereotype.Controller;
8 | import org.springframework.stereotype.Service;
9 | import org.springframework.util.CollectionUtils;
10 | import org.springframework.util.StringUtils;
11 | import org.springframework.web.bind.annotation.RestController;
12 | import org.springframework.web.context.request.RequestContextHolder;
13 | import org.springframework.web.context.request.ServletRequestAttributes;
14 |
15 | import javax.servlet.http.HttpServletRequest;
16 | import java.lang.reflect.Method;
17 | import java.util.*;
18 | import java.util.stream.Collectors;
19 |
20 | public abstract class LogUtils {
21 |
22 |
23 | public static void initLog(LogInfo info, LogAttribute logAttribute, Method method, Object[] args, Class> targetClass) {
24 | info.setLogId(UUID.randomUUID().toString().replace("-", ""));
25 | info.setTitle(logAttribute.title());
26 | // 定义了操作人spel表达式则使用尝试解析
27 | info.setOperator(SpringSpelUtils.parse(method, args, logAttribute.operator()));
28 | info.setOperateDate(new Date());
29 |
30 |
31 | if (RequestContextHolder.getRequestAttributes() != null && targetClass.getAnnotation(Controller.class) != null || targetClass.getAnnotation(RestController.class) != null) {
32 | HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
33 | info.setRequestUri( request.getRequestURI() + " [" + request.getMethod() + "]");
34 | info.setIp(request.getRemoteAddr());
35 | if (StringUtils.isEmpty(info.getType())) {
36 | info.setType(LogInfo.TYPE_WEB);
37 | }
38 | info.setParams(LogUtils.buildRequestParams(request.getParameterMap(), args));
39 | } else {
40 | info.setParams(LogUtils.buildRequestParams(null, args));
41 | }
42 |
43 | if (targetClass.getAnnotation(Service.class) != null && StringUtils.isEmpty(info.getType())) {
44 | info.setType(LogInfo.TYPE_SERVICE);
45 | }
46 | // 处理tags信息
47 | if (!CollectionUtils.isEmpty(logAttribute.tags())) {
48 | JSONObject tags = new JSONObject();
49 | for (Map.Entry item: logAttribute.tags().entrySet()) {
50 | tags.put(item.getKey(), SpringSpelUtils.parse(method, args, item.getValue()));
51 | }
52 | info.setTags(JSON.toJSONString(tags));
53 | }
54 |
55 |
56 | info.setMethod(LogUtils.createDefaultTitle(method, targetClass));
57 | String spelDescription = SpringSpelUtils.parse(method, args, logAttribute.template());
58 | info.setDescription(spelDescription);
59 |
60 | }
61 |
62 | public static String buildRequestParams(Map paramMap, Object[] args) {
63 |
64 | // post/put 请求体json参数
65 | if (CollectionUtils.isEmpty(paramMap)) {
66 | List paramList = new ArrayList<>();
67 | for (Object obj : args) {
68 | try {
69 | paramList.add(JSON.toJSONString(obj));
70 | } catch (Throwable e) {
71 | // 不是所有对象都支持序列化比如 HttpServletRequest
72 | paramList.add("notSerializableParam");
73 | }
74 |
75 | }
76 | return String.join(",",paramList );
77 | // get 请求参数
78 | } else {
79 | StringBuilder params = new StringBuilder();
80 | for (Map.Entry param : ((Map) paramMap).entrySet()) {
81 | params.append(("".equals(params.toString()) ? "" : "&") + param.getKey() + "=");
82 | String paramValue = (param.getValue() != null && param.getValue().length > 0 ? param.getValue()[0] : "");
83 | params.append(paramValue);
84 | }
85 | return params.toString();
86 | }
87 |
88 | }
89 |
90 | public static String createDefaultTitle(Method method, Class> targetClass) {
91 | return createDefaultTitle(method, targetClass, true);
92 | }
93 |
94 | public static String createDefaultTitle(Method method, Class> targetClass, boolean isSimpleName) {
95 | StringBuilder title = new StringBuilder();
96 | if (isSimpleName) {
97 | title.append(targetClass.getSimpleName());
98 | } else {
99 | title.append(targetClass.getName());
100 | }
101 | title.append("." + method.getName());
102 | if (method.getParameters() != null) {
103 | List paramNames = Arrays.stream(method.getParameters()).map(item -> item.getName()).collect(Collectors.toList());
104 | title.append("(" + String.join(",",paramNames) + ")");
105 | } else {
106 | title.append("()");
107 | }
108 | return title.toString();
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/easy-log-core/src/main/java/com/easycode8/easylog/core/util/SpringSpelUtils.java:
--------------------------------------------------------------------------------
1 | package com.easycode8.easylog.core.util;
2 |
3 | import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
4 | import org.springframework.expression.EvaluationContext;
5 | import org.springframework.expression.Expression;
6 | import org.springframework.expression.ExpressionParser;
7 | import org.springframework.expression.spel.standard.SpelExpressionParser;
8 | import org.springframework.expression.spel.support.StandardEvaluationContext;
9 | import org.springframework.util.StringUtils;
10 | import org.springframework.web.context.request.RequestContextHolder;
11 | import org.springframework.web.context.request.ServletRequestAttributes;
12 |
13 | import javax.servlet.http.HttpServletRequest;
14 | import javax.servlet.http.HttpSession;
15 | import java.lang.reflect.Method;
16 |
17 |
18 | public class SpringSpelUtils {
19 |
20 | /**解析spel表达式*/
21 | private static ExpressionParser parser = new SpelExpressionParser();
22 | /**将方法参数纳入Spring管理*/
23 | private static LocalVariableTableParameterNameDiscoverer discoverer = new LocalVariableTableParameterNameDiscoverer();
24 |
25 |
26 | /**
27 | * 解析spel表达式
28 | * @param method 方法
29 | * @param args 获取参数对象数组
30 | * @param template spel表达式
31 | * @return
32 | */
33 | public static String parse(Method method, Object[] args, String template) {
34 | if (StringUtils.isEmpty(template)) {
35 | return "";
36 | }
37 | //获取方法参数名
38 | String[] params = discoverer.getParameterNames(method);
39 | EvaluationContext context = new StandardEvaluationContext();
40 | for (int len = 0; len < params.length; len++) {
41 | context.setVariable(params[len], args[len]);
42 | }
43 | Expression expression = parser.parseExpression(template);
44 | String spelDescription = expression.getValue(context, String.class);
45 | return spelDescription;
46 | }
47 |
48 |
49 | /**
50 | * 使用SpEL表达式从session中提取属性信息
51 | *
52 | * @param expression SpEL表达式
53 | * @return 属性值
54 | */
55 | public static Object getSessionAttribute(String attributeName, String expression) {
56 | if (RequestContextHolder.getRequestAttributes() == null) {
57 | return null;
58 | }
59 | HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
60 | HttpSession session = request.getSession();
61 | // 解析表达式并获取属性值
62 | // return parser.parseExpression(expression).getValue(session.getAttribute("account"));
63 | Object attributeValue = session.getAttribute(attributeName);
64 | if (attributeValue == null) {
65 | return null;
66 | }
67 | return parser.parseExpression(expression).getValue(attributeValue);
68 | }
69 |
70 | }
71 |
--------------------------------------------------------------------------------
/easy-log-core/src/main/resources/easy-log/db/liquibase/changelog-1.0.xml:
--------------------------------------------------------------------------------
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 |
--------------------------------------------------------------------------------
/easy-log-data-mybatis-plus/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | easy-log
7 | io.github.easycode8
8 | ${revision}
9 | ../pom.xml
10 |
11 | 4.0.0
12 |
13 | easy-log-data-mybatis-plus
14 |
15 |
16 |
17 |
18 | org.springframework.boot
19 | spring-boot-configuration-processor
20 | true
21 |
22 |
23 |
24 | io.github.easycode8
25 | easy-log-core
26 | ${revision}
27 |
28 |
29 |
30 |
31 |
32 | com.baomidou
33 | mybatis-plus-boot-starter
34 | ${mybatis-plus.version}
35 | true
36 |
37 |
38 |
39 |
40 | org.apache.commons
41 | commons-lang3
42 | ${commons-lang3.version}
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/easy-log-data-mybatis-plus/readme.md:
--------------------------------------------------------------------------------
1 | # 说明
2 | ## 包目录说明
3 | - com.easycode8.easylog.mybatis
4 | - adapter: 存放适配处理逻辑 (mybatis和mybatis-plus的Mapper不一致需要适配 )
5 | - handler: 处理快照数据生成逻辑
6 | - interceptor: mybatis拦截器入口,是否生成日志数据快照由handler决定
7 |
8 |
9 | ## mybatis拦截器方式实现
10 | ```java
11 | public class RecordInterceptor implements Interceptor {
12 |
13 | @Override
14 | public Object intercept(Invocation invocation) throws Throwable {
15 | Object[] args = invocation.getArgs();
16 | MappedStatement ms = (MappedStatement) args[0];
17 | Object parameterObject = args[1];
18 | BoundSql boundSql = ms.getBoundSql(parameterObject);
19 | String sql = boundSql.getSql();
20 | SqlCommandType sqlCommandType = ms.getSqlCommandType();
21 |
22 | if (sqlCommandType == SqlCommandType.DELETE || sqlCommandType == SqlCommandType.UPDATE) {
23 | // 记录信息的逻辑
24 | String tableName = getTableNameFromSql(sql);
25 | // 获取删除或修改前的记录信息
26 | List