Skip to content

Commit a38bae4

Browse files
authored
Merge pull request #104 from qqxx6661/dev_1.6.x
custom thread pool support
2 parents c71d944 + deb9dd7 commit a38bae4

File tree

23 files changed

+378
-35
lines changed

23 files changed

+378
-35
lines changed

.github/workflows/ci.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ jobs:
2323
path: ~/.m2
2424
key: ${{ env.cache-name }}-${{ hashFiles('./log-record-starter/pom.xml') }}
2525
restore-keys: ${{ env.cache-name }}-
26+
- name: Install current version to local repository
27+
run: mvn install -DskipTests -Dgpg.skip
28+
working-directory: ./log-record-core
2629
- name: Test with Maven
2730
working-directory: ./log-record-starter
2831
run: mvn -V --no-transfer-progress test

README.md

Lines changed: 43 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,8 @@ public Response<T> function(Request request) {
147147
- 支持自动重试和兜底处理:支持配置重试次数和处理失败兜底逻辑`SPI`
148148
- 支持控制切面执行时机(方法执行前后)
149149
- 支持自定义执行成功判断
150+
- 支持非注解方式手动记录日志
151+
- 自定义消息线程池
150152
- 更多特性等你来发掘...
151153

152154
**日志实体(LogDTO)内包含:**
@@ -347,8 +349,9 @@ public Response<T> function(Request request) {
347349
- [实体类`Diff`](#实体类Diff)
348350
- [日志处理重试次数及兜底函数配置](#日志处理重试次数及兜底函数配置)
349351
- [重复注解](#重复注解)
350-
- [消息分发线程池配置](#消息分发线程池配置)
352+
- [自定义消息线程池](#自定义消息线程池)
351353
- [函数返回值记录开关](#函数返回值记录开关)
354+
- [非注解方式手动记录日志](#非注解方式)
352355
- [操作日志数据表结构推荐](#操作日志数据表结构推荐)
353356
- [让注解支持`IDEA`自动补全](#让注解支持IDEA自动补全)
354357

@@ -512,7 +515,7 @@ public Response<T> function(Request request) {
512515
}
513516
```
514517

515-
LogRecordContext内部使用TransmittableThreadLocal,在线程池中也可以读取到主线程的ThreadLocal
518+
LogRecordContext内部使用TransmittableThreadLocal实现与主线程的ThreadLocal传递
516519

517520
### 自定义函数
518521

@@ -795,20 +798,49 @@ public class LogRecordErrorHandlerServiceImpl implements LogRecordErrorHandlerSe
795798

796799
我们还加上了重复注解的支持,可以在一个方法上同时加多个`@OperationLog`**会保证按照`@OperationLog`从上到下的顺序输出日志**
797800

798-
### 消息分发线程池配置
801+
### 自定义消息线程池
799802

800-
在组装好`logDTO`后,默认使用线程池对消息进行分发,发送至本地监听函数或者消息队列发送者。
801-
802-
**注意:`logDTO`的组装在切面中,该切面仍然在函数执行的线程中运行。**
803-
804-
可以使用如下配置:
803+
starter提供了如下配置:
805804

806805
```properties
807806
log-record.thread-pool.pool-size=4(线程池核心线程大小 默认为4)
808-
log-record.thread-pool.enabled=true(线程池开关 默认为开启 若关闭则使用主线程进行消息处理发送)
807+
log-record.thread-pool.enabled=true(线程池开关 默认为开启 若关闭则使用业务线程进行消息处理发送)
808+
```
809+
810+
在组装好`logDTO`后,默认会使用线程池对消息进行处理,发送至本地监听函数或者消息队列发送者,也可以通过配置关闭线程池,让主线程执行全部消息处理逻辑。
811+
812+
**注意:`logDTO`的组装逻辑在切面中,该切面仍然在函数执行的线程中运行。**
813+
814+
默认线程池配置如下(拒绝策略为丢弃):
815+
816+
```java
817+
return new ThreadPoolExecutor(poolSize, poolSize, 0L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1024), THREAD_FACTORY, new ThreadPoolExecutor.AbortPolicy());
818+
```
819+
820+
此外,还提供了用户传入自定义线程池的方式,用户可自行实现cn.monitor4all.logRecord.thread.ThreadPoolProvider,传入线程池。
821+
822+
示例:
823+
824+
```java
825+
public class CustomThreadPoolProvider implements ThreadPoolProvider {
826+
827+
private static ThreadPoolExecutor EXECUTOR;
828+
829+
private static final ThreadFactory THREAD_FACTORY = new CustomizableThreadFactory("custom-log-record-");
830+
831+
832+
private CustomThreadPoolProvider() {
833+
log.info("CustomThreadPoolProvider init");
834+
EXECUTOR = new ThreadPoolExecutor(3, 3, 0L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(100), THREAD_FACTORY, new ThreadPoolExecutor.AbortPolicy());
835+
}
836+
837+
@Override
838+
public ThreadPoolExecutor buildLogRecordThreadPool() {
839+
return EXECUTOR;
840+
}
841+
}
809842
```
810843

811-
关闭使用线程池后,所有发送由主线程执行,带来的副作用是大量日志并发发送,会降低主线程处理效率。
812844

813845
### 函数返回值记录开关
814846

@@ -893,7 +925,7 @@ public void testBizIdWithSpEL(String bizId) {
893925

894926
应用之间通过关键操作的日志消息,互相通知。
895927

896-
## 附录:Demo
928+
## Demo
897929

898930
当你觉得用法不熟悉,可以查看单元测试用例,里面有最为详细且最全的使用示例。
899931

log-record-core/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
<groupId>cn.monitor4all</groupId>
1212
<artifactId>log-record-core</artifactId>
13-
<version>1.6.2</version>
13+
<version>1.6.3</version>
1414

1515
<properties>
1616
<maven.compiler.source>8</maven.compiler.source>

log-record-core/src/main/java/cn/monitor4all/logRecord/aop/SystemLogAspect.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@
1010
import cn.monitor4all.logRecord.service.IOperatorIdGetService;
1111
import cn.monitor4all.logRecord.service.LogRecordErrorHandlerService;
1212
import cn.monitor4all.logRecord.thread.LogRecordThreadPool;
13-
import com.alibaba.fastjson.JSON;
14-
import com.alibaba.fastjson.serializer.SerializerFeature;
13+
import cn.monitor4all.logRecord.util.JsonUtil;
1514
import com.alibaba.ttl.TtlRunnable;
1615
import lombok.extern.slf4j.Slf4j;
1716
import org.apache.commons.lang3.StringUtils;
@@ -128,7 +127,7 @@ public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
128127
logDTO.setSuccess(true);
129128
}
130129
if (annotation.recordReturnValue() && result != null) {
131-
logDTO.setReturnStr(JSON.toJSONString(result));
130+
logDTO.setReturnStr(JsonUtil.safeToJsonString(result));
132131
}
133132
});
134133
} catch (Throwable throwableAfterFuncSuccess) {
@@ -305,7 +304,7 @@ private String parseParamToStringOrJson(String spel, StandardEvaluationContext c
305304
Expression msgExpression = parser.parseExpression(spel);
306305
Object obj = msgExpression.getValue(context, Object.class);
307306
if (obj != null) {
308-
return obj instanceof String ? (String) obj : JSON.toJSONString(obj, SerializerFeature.WriteMapNullValue);
307+
return obj instanceof String ? (String) obj : JsonUtil.safeToJsonString(obj);
309308
}
310309
return null;
311310
}

log-record-core/src/main/java/cn/monitor4all/logRecord/function/CustomFunctionObjectDiff.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,11 @@ private static boolean isJsonArray(Object obj) {
260260
private static Field[] getAllFields(Class<?> type) {
261261
List<Field> fields = new ArrayList<>();
262262
for (Class<?> c = type; c != null && !c.isSynthetic(); c = c.getSuperclass()) {
263-
Collections.addAll(fields, c.getDeclaredFields());
263+
for (Field field : c.getDeclaredFields()) {
264+
if (!field.isSynthetic()) {
265+
fields.add(field);
266+
}
267+
}
264268
}
265269
return fields.toArray(new Field[0]);
266270
}

log-record-core/src/main/java/cn/monitor4all/logRecord/service/impl/RabbitMqDataPipelineServiceImpl.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import cn.monitor4all.logRecord.configuration.LogRecordProperties;
55
import cn.monitor4all.logRecord.constants.LogConstants;
66
import cn.monitor4all.logRecord.service.DataPipelineService;
7-
import com.alibaba.fastjson.JSON;
7+
import cn.monitor4all.logRecord.util.JsonUtil;
88
import lombok.extern.slf4j.Slf4j;
99
import org.springframework.amqp.rabbit.core.RabbitTemplate;
1010
import org.springframework.beans.factory.annotation.Autowired;
@@ -27,7 +27,7 @@ public class RabbitMqDataPipelineServiceImpl implements DataPipelineService {
2727
@Override
2828
public boolean createLog(LogDTO logDTO) {
2929
log.info("LogRecord RabbitMq ready to send routingKey [{}] LogDTO [{}]", properties.getRabbitMqProperties().getRoutingKey(), logDTO);
30-
rubeExchangeTemplate.convertAndSend(properties.getRabbitMqProperties().getRoutingKey(), JSON.toJSONString(logDTO));
30+
rubeExchangeTemplate.convertAndSend(properties.getRabbitMqProperties().getRoutingKey(), JsonUtil.safeToJsonString(logDTO));
3131
return true;
3232
}
3333
}

log-record-core/src/main/java/cn/monitor4all/logRecord/service/impl/RocketMqDataPipelineServiceImpl.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import cn.monitor4all.logRecord.configuration.LogRecordProperties;
55
import cn.monitor4all.logRecord.constants.LogConstants;
66
import cn.monitor4all.logRecord.service.DataPipelineService;
7-
import com.alibaba.fastjson.JSON;
7+
import cn.monitor4all.logRecord.util.JsonUtil;
88
import lombok.extern.slf4j.Slf4j;
99
import org.apache.rocketmq.client.producer.DefaultMQProducer;
1010
import org.apache.rocketmq.client.producer.SendResult;
@@ -30,7 +30,7 @@ public class RocketMqDataPipelineServiceImpl implements DataPipelineService {
3030
@Override
3131
public boolean createLog(LogDTO logDTO) {
3232
try {
33-
Message msg = new Message(properties.getRocketMqProperties().getTopic(), properties.getRocketMqProperties().getTag(), (JSON.toJSONString(logDTO)).getBytes(RemotingHelper.DEFAULT_CHARSET));
33+
Message msg = new Message(properties.getRocketMqProperties().getTopic(), properties.getRocketMqProperties().getTag(), (JsonUtil.safeToJsonString(logDTO)).getBytes(RemotingHelper.DEFAULT_CHARSET));
3434
SendResult sendResult = defaultMqProducer.send(msg);
3535
log.info("LogRecord RocketMq send LogDTO [{}] sendResult: [{}]", logDTO, sendResult);
3636
return true;
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package cn.monitor4all.logRecord.thread;
2+
3+
import cn.monitor4all.logRecord.configuration.LogRecordProperties;
4+
import lombok.extern.slf4j.Slf4j;
5+
import org.springframework.scheduling.concurrent.CustomizableThreadFactory;
6+
7+
import java.util.concurrent.LinkedBlockingQueue;
8+
import java.util.concurrent.ThreadFactory;
9+
import java.util.concurrent.ThreadPoolExecutor;
10+
import java.util.concurrent.TimeUnit;
11+
12+
/**
13+
* 默认线程池提供者
14+
*/
15+
@Slf4j
16+
public class DefaultThreadPoolProvider implements ThreadPoolProvider {
17+
18+
private final LogRecordProperties logRecordProperties;
19+
private static final ThreadFactory THREAD_FACTORY = new CustomizableThreadFactory("log-record-");
20+
21+
22+
public DefaultThreadPoolProvider(LogRecordProperties logRecordProperties) {
23+
this.logRecordProperties = logRecordProperties;
24+
}
25+
26+
@Override
27+
public ThreadPoolExecutor buildLogRecordThreadPool() {
28+
log.info("LogRecordThreadPool init poolSize [{}]", logRecordProperties.getThreadPool().getPoolSize());
29+
int poolSize = logRecordProperties.getThreadPool().getPoolSize();
30+
return new ThreadPoolExecutor(poolSize, poolSize, 0L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1024), THREAD_FACTORY, new ThreadPoolExecutor.AbortPolicy());
31+
}
32+
33+
34+
}

log-record-core/src/main/java/cn/monitor4all/logRecord/thread/LogRecordThreadPool.java

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,28 +4,31 @@
44
import lombok.extern.slf4j.Slf4j;
55
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
66
import org.springframework.boot.context.properties.EnableConfigurationProperties;
7-
import org.springframework.scheduling.concurrent.CustomizableThreadFactory;
7+
import org.springframework.context.ApplicationContext;
88
import org.springframework.stereotype.Component;
99

10-
import java.util.concurrent.*;
10+
import java.util.concurrent.ThreadPoolExecutor;
1111

1212
@Slf4j
1313
@Component
1414
@ConditionalOnProperty(name = "log-record.thread-pool.enabled", havingValue = "true", matchIfMissing = true)
1515
@EnableConfigurationProperties({LogRecordProperties.class})
1616
public class LogRecordThreadPool {
1717

18-
private static final ThreadFactory THREAD_FACTORY = new CustomizableThreadFactory("log-record-");
18+
private final ThreadPoolExecutor logRecordPoolExecutor;
1919

20-
private final ExecutorService LOG_RECORD_POOL_EXECUTOR;
2120

22-
public LogRecordThreadPool(LogRecordProperties logRecordProperties) {
23-
log.info("LogRecordThreadPool init poolSize [{}]", logRecordProperties.getThreadPool().getPoolSize());
24-
int poolSize = logRecordProperties.getThreadPool().getPoolSize();
25-
this.LOG_RECORD_POOL_EXECUTOR = new ThreadPoolExecutor(poolSize, poolSize, 0L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1024), THREAD_FACTORY, new ThreadPoolExecutor.CallerRunsPolicy());
21+
/**
22+
* 操作日志主逻辑线程池
23+
* 提供顺序:用户传入线程池 优先于 通过配置文件创建的默认线程池
24+
*/
25+
public LogRecordThreadPool(LogRecordProperties logRecordProperties, ApplicationContext applicationContext) {
26+
ThreadPoolProvider threadPoolProvider = applicationContext.getBeanProvider(ThreadPoolProvider.class)
27+
.getIfUnique(() -> new DefaultThreadPoolProvider(logRecordProperties));
28+
this.logRecordPoolExecutor = threadPoolProvider.buildLogRecordThreadPool();
2629
}
2730

28-
public ExecutorService getLogRecordPoolExecutor() {
29-
return LOG_RECORD_POOL_EXECUTOR;
31+
public ThreadPoolExecutor getLogRecordPoolExecutor() {
32+
return logRecordPoolExecutor;
3033
}
3134
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package cn.monitor4all.logRecord.thread;
2+
3+
4+
import java.util.concurrent.ThreadPoolExecutor;
5+
6+
/**
7+
* 线程池提供者
8+
*/
9+
public interface ThreadPoolProvider {
10+
11+
/**
12+
* 提供操作日志处理线程池
13+
*/
14+
ThreadPoolExecutor buildLogRecordThreadPool();
15+
16+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package cn.monitor4all.logRecord.util;
2+
3+
4+
import com.alibaba.fastjson.JSON;
5+
import lombok.extern.slf4j.Slf4j;
6+
7+
@Slf4j
8+
public class JsonUtil {
9+
10+
public static String safeToJsonString(Object object) {
11+
try {
12+
return JSON.toJSONString(object);
13+
} catch (Exception e) {
14+
log.error("safeToJsonString error, object {}", object, e);
15+
return object.toString();
16+
}
17+
}
18+
}

log-record-springboot3-starter/pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
<groupId>cn.monitor4all</groupId>
1212
<artifactId>log-record-springboot3-starter</artifactId>
13-
<version>1.6.2</version>
13+
<version>1.6.3</version>
1414

1515
<properties>
1616
<maven.compiler.source>17</maven.compiler.source>
@@ -35,7 +35,7 @@
3535
<dependency>
3636
<groupId>cn.monitor4all</groupId>
3737
<artifactId>log-record-core</artifactId>
38-
<version>1.6.2</version>
38+
<version>1.6.3</version>
3939
</dependency>
4040

4141
<!-- 单元测试依赖 -->
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package cn.monitor4all.logRecord.springboot3.test;
2+
3+
4+
import cn.monitor4all.logRecord.bean.LogDTO;
5+
import cn.monitor4all.logRecord.springboot3.test.service.OperatorIdGetService;
6+
import cn.monitor4all.logRecord.springboot3.test.service.TestService;
7+
import cn.monitor4all.logRecord.springboot3.LogRecordAutoConfiguration;
8+
import cn.monitor4all.logRecord.springboot3.test.utils.TestHelper;
9+
import lombok.extern.slf4j.Slf4j;
10+
import org.junit.jupiter.api.Assertions;
11+
import org.junit.jupiter.api.Test;
12+
import org.springframework.beans.factory.annotation.Autowired;
13+
import org.springframework.boot.test.context.SpringBootTest;
14+
import org.springframework.context.annotation.EnableAspectJAutoProxy;
15+
import org.springframework.context.annotation.PropertySource;
16+
import org.springframework.test.context.ContextConfiguration;
17+
18+
/**
19+
* 单元测试:自定义线程池
20+
*/
21+
@Slf4j
22+
@SpringBootTest
23+
@ContextConfiguration(classes = {
24+
LogRecordAutoConfiguration.class,
25+
OperatorIdGetService.class,
26+
TestService.class,})
27+
@PropertySource("classpath:testCustomThreadPool.properties")
28+
@EnableAspectJAutoProxy(proxyTargetClass = true)
29+
public class OperationLogCustomThreadPoolTest {
30+
31+
@Autowired
32+
private TestService testService;
33+
34+
/**
35+
* 测试:用户传入自定义线程池
36+
*/
37+
@Test
38+
public void testCustomThreadPool() {
39+
TestHelper.addLock("testCustomThreadPool");
40+
testService.testCustomThreadPool();
41+
TestHelper.await("testCustomThreadPool");
42+
LogDTO logDTO = TestHelper.getLogDTO("testCustomThreadPool");
43+
44+
Assertions.assertEquals(logDTO.getBizType(), "testCustomThreadPool");
45+
}
46+
47+
}

0 commit comments

Comments
 (0)