From dc33975cf8f9b2a8d7df87e02b74308f2b6be1d4 Mon Sep 17 00:00:00 2001 From: JamesChenX Date: Mon, 13 Dec 2021 10:27:22 +0800 Subject: [PATCH] Replace log4j2 with our own far more efficient logging implementation #875 --- README.md | 1 - README_zh.md | 3 +- pom.xml | 6 - turms-docs/src/README.md | 3 +- .../src/for-developers/observability.md | 29 +- turms-docs/src/intro/README.md | 3 +- turms-docs/src/intro/redevelopment.md | 2 + .../access/common/UserSessionDispatcher.java | 23 +- .../controller/UserRequestDispatcher.java | 21 +- .../gateway/access/tcp/TcpDispatcher.java | 10 +- .../access/tcp/handler/TcpHandlerConfig.java | 4 +- .../gateway/access/udp/UdpDispatcher.java | 12 +- .../access/websocket/WebSocketDispatcher.java | 10 +- .../websocket/factory/WebSocketFactory.java | 2 - .../turms/gateway/dao/config/MongoConfig.java | 2 - .../gateway/fake/ClientFakingManager.java | 18 +- .../gateway/logging/ClientApiLogging.java | 16 +- .../gateway/logging/NotificationLogging.java | 6 +- .../gateway/manager/HeartbeatManager.java | 10 +- .../manager/ServiceAddressManager.java | 8 +- .../gateway/manager/UserSessionsManager.java | 8 +- .../gateway/plugin/TurmsPluginManager.java | 2 - .../gateway/pojo/bo/session/UserSession.java | 8 +- .../bo/session/connection/TcpConnection.java | 12 +- .../connection/WebSocketConnection.java | 12 +- .../impl/message/InboundRequestService.java | 2 - .../impl/message/OutboundMessageService.java | 14 +- .../impl/observability/MetricsService.java | 2 - .../impl/observability/StatisticsService.java | 2 - .../service/mediator/ServiceMediator.java | 2 - .../src/main/resources/application-dev.yaml | 3 +- turms-gateway/src/main/resources/banner.txt | 8 - .../src/main/resources/log4j2-spring.xml | 68 ----- .../resources/log4j2.component.properties | 2 - .../java/integration/access/TcpServerIT.java | 4 +- .../impl/OutboundMessageServiceTests.java | 2 +- .../com/mydomain/MyTurmsRequestHandler.java | 8 +- .../antispam/property/AntiSpamProperties.java | 2 - .../impl/MinioStorageServiceProvider.java | 18 +- turms-server-common/pom.xml | 10 +- .../server/common/BaseTurmsApplication.java | 27 +- .../access/http/dto/response/ResponseDTO.java | 1 + .../common/address/AddressCollector.java | 2 - .../address/BaseServiceAddressManager.java | 10 +- .../server/common/client/TurmsTcpClient.java | 8 +- .../server/common/cluster/node/Node.java | 14 +- .../cluster/service/codec/CodecService.java | 2 - .../service/config/SharedConfigService.java | 2 - .../service/config/SharedPropertyService.java | 22 +- .../domain/property/CommonProperties.java | 2 +- .../service/connection/ConnectionServer.java | 12 +- .../service/connection/ConnectionService.java | 50 ++-- .../service/discovery/DiscoveryService.java | 10 +- .../discovery/LocalNodeStatusManager.java | 24 +- .../cluster/service/idgen/IdService.java | 10 +- .../service/idgen/SnowflakeIdGenerator.java | 2 - .../cluster/service/rpc/RpcEndpoint.java | 8 +- .../service/rpc/RpcRequestExecutor.java | 16 +- .../cluster/service/rpc/RpcService.java | 42 +-- .../ApplicationEnvironmentEventListener.java | 81 +++--- .../context/TurmsApplicationContext.java | 14 +- .../common/healthcheck/CpuHealthChecker.java | 15 +- .../healthcheck/MemoryHealthChecker.java | 24 +- .../common/log4j/LogContextConstant.java | 48 ---- .../log4j/plugin/TurmsContextLookup.java | 91 ------ .../common/logging/AdminApiLogging.java | 7 +- .../server/common/logging/CommonLogger.java | 52 ++++ .../common/logging/RequestLoggingContext.java | 66 ----- .../logging/core/appender/Appender.java | 60 ++++ .../appender/ConsoleAppender.java} | 21 +- .../core/appender/RollingFileAppender.java | 269 ++++++++++++++++++ .../core/context/LogThreadContext.java | 59 ++++ .../core/idle/BackoffIdleStrategy.java | 86 ++++++ .../logging/core/layout/TemplateLayout.java | 152 ++++++++++ .../core/layout/TurmsTemplateLayout.java | 194 +++++++++++++ .../logging/core/logger/AsyncLogger.java | 181 ++++++++++++ .../logging/core/logger/InternalLogger.java | 40 +++ .../common/logging/core/logger/Logger.java | 63 ++++ .../logging/core/logger/LoggerFactory.java | 161 +++++++++++ .../logging/core/logger/LoggerOptions.java | 36 +++ .../logging/core/logger/NoOpLogger.java | 112 ++++++++ .../logging/core/logger/WrappedLogger.java | 122 ++++++++ .../common/logging/core/model/LogLevel.java | 35 +++ .../common/logging/core/model/LogRecord.java | 32 +++ .../core/processor/LogProcessorRunner.java | 88 ++++++ .../common/logging/slf4j/Slf4jBridge.java | 183 ++++++++++++ .../logging/slf4j/Slf4jBridgeFactory.java | 39 +++ .../common/mongo/codec/EntityCodec.java | 10 +- .../mongo/codec/EntityCodecProvider.java | 2 - .../mongo/operation/TurmsMongoOperations.java | 18 +- .../plugin/AbstractTurmsPluginManager.java | 8 +- .../server/common/plugin/PluginManager.java | 2 - .../{util => property}/PropertiesUtil.java | 4 +- .../common/property/TurmsProperties.java | 2 +- .../property/TurmsPropertiesManager.java | 13 +- .../env/common/cluster/NodeProperties.java | 2 +- .../logging/ConsoleLoggingProperties.java | 42 +++ .../common/logging/FileLoggingProperties.java | 49 ++++ .../{ => logging}/LoggingProperties.java | 12 +- .../env/redis/TurmsRedisProperties.java | 1 - .../common/redis/CommonRedisConfig.java | 10 +- .../server/common/redis/TurmsRedisClient.java | 2 - .../common/redis/TurmsRedisClientManager.java | 8 +- .../rpc/codec/request/RpcRequestCodec.java | 5 +- .../rpc/request/SendNotificationRequest.java | 2 +- .../rpc/service/IOutboundMessageService.java | 2 +- .../service/IServiceRequestDispatcher.java | 2 +- .../blocklist/BlocklistServiceManager.java | 11 +- .../tracing/TracingCloseableContext.java | 13 +- .../server/common/tracing/TracingContext.java | 83 +++--- .../im/turms/server/common/util/DateUtil.java | 93 +++++- .../server/common/util/ExceptionUtil.java | 2 + .../turms/server/common/util/Formatter.java | 176 ++++++++++++ .../common/util/JsonSizeCalculator.java | 8 +- .../org/slf4j/impl/StaticLoggerBinder.java | 30 ++ .../java/org/slf4j/impl/StaticMDCBinder.java | 41 +++ .../common/rpc/codec/BaseCodecTest.java | 8 +- .../rpc/codec/RpcFrameEncoderTests.java | 4 +- .../common/testing/ServiceLogConsumer.java | 4 +- .../common/testing/TestingEnvContainer.java | 4 +- .../src/main/resources/log4j2.xml | 30 -- .../test/java/stress/WebSocketAccessST.java | 2 +- .../service/constant/SecurityConstant.java | 2 +- .../service/logging/ClientApiLogging.java | 10 +- .../service/logging/NotificationLogging.java | 6 +- .../manager/ServiceAddressManager.java | 8 +- .../service/plugin/TurmsPluginManager.java | 2 - .../im/turms/service/util/ProtoModelUtil.java | 2 - .../http/codec/BlockedClientSerializer.java | 2 +- .../config/TurmsErrorWebExceptionHandler.java | 19 +- .../controller/cluster/SettingController.java | 4 +- .../access/http/filter/ControllerFilter.java | 17 +- .../dispatcher/ServiceRequestDispatcher.java | 30 +- .../dao/MongoCollectionInitializer.java | 30 +- .../workflow/dao/MongoFakingManager.java | 42 +-- .../workflow/dao/config/MongoConfig.java | 8 +- .../workflow/dao/domain/group/GroupType.java | 5 +- .../service/impl/admin/AdminRoleService.java | 10 +- .../service/impl/admin/AdminService.java | 10 +- .../conversation/ConversationService.java | 2 - .../service/impl/group/GroupService.java | 8 +- .../service/impl/group/GroupTypeService.java | 10 +- .../service/impl/message/MessageService.java | 8 +- .../impl/message/OutboundMessageService.java | 12 +- .../impl/statistics/MetricsService.java | 2 - .../impl/statistics/StatisticsService.java | 10 +- .../impl/user/UserPermissionGroupService.java | 10 +- .../service/impl/user/UserService.java | 8 +- .../src/main/resources/application-dev.yaml | 3 +- .../src/main/resources/application-test.yaml | 3 +- turms-service/src/main/resources/banner.txt | 7 - .../src/main/resources/log4j2-spring.xml | 79 ----- .../resources/log4j2.component.properties | 2 - 153 files changed, 3035 insertions(+), 1014 deletions(-) delete mode 100644 turms-gateway/src/main/resources/banner.txt delete mode 100644 turms-gateway/src/main/resources/log4j2-spring.xml delete mode 100644 turms-gateway/src/main/resources/log4j2.component.properties delete mode 100644 turms-server-common/src/main/java/im/turms/server/common/log4j/LogContextConstant.java delete mode 100644 turms-server-common/src/main/java/im/turms/server/common/log4j/plugin/TurmsContextLookup.java create mode 100644 turms-server-common/src/main/java/im/turms/server/common/logging/CommonLogger.java delete mode 100644 turms-server-common/src/main/java/im/turms/server/common/logging/RequestLoggingContext.java create mode 100644 turms-server-common/src/main/java/im/turms/server/common/logging/core/appender/Appender.java rename turms-server-common/src/main/java/im/turms/server/common/logging/{CustomLogger.java => core/appender/ConsoleAppender.java} (51%) create mode 100644 turms-server-common/src/main/java/im/turms/server/common/logging/core/appender/RollingFileAppender.java create mode 100644 turms-server-common/src/main/java/im/turms/server/common/logging/core/context/LogThreadContext.java create mode 100644 turms-server-common/src/main/java/im/turms/server/common/logging/core/idle/BackoffIdleStrategy.java create mode 100644 turms-server-common/src/main/java/im/turms/server/common/logging/core/layout/TemplateLayout.java create mode 100644 turms-server-common/src/main/java/im/turms/server/common/logging/core/layout/TurmsTemplateLayout.java create mode 100644 turms-server-common/src/main/java/im/turms/server/common/logging/core/logger/AsyncLogger.java create mode 100644 turms-server-common/src/main/java/im/turms/server/common/logging/core/logger/InternalLogger.java create mode 100644 turms-server-common/src/main/java/im/turms/server/common/logging/core/logger/Logger.java create mode 100644 turms-server-common/src/main/java/im/turms/server/common/logging/core/logger/LoggerFactory.java create mode 100644 turms-server-common/src/main/java/im/turms/server/common/logging/core/logger/LoggerOptions.java create mode 100644 turms-server-common/src/main/java/im/turms/server/common/logging/core/logger/NoOpLogger.java create mode 100644 turms-server-common/src/main/java/im/turms/server/common/logging/core/logger/WrappedLogger.java create mode 100644 turms-server-common/src/main/java/im/turms/server/common/logging/core/model/LogLevel.java create mode 100644 turms-server-common/src/main/java/im/turms/server/common/logging/core/model/LogRecord.java create mode 100644 turms-server-common/src/main/java/im/turms/server/common/logging/core/processor/LogProcessorRunner.java create mode 100644 turms-server-common/src/main/java/im/turms/server/common/logging/slf4j/Slf4jBridge.java create mode 100644 turms-server-common/src/main/java/im/turms/server/common/logging/slf4j/Slf4jBridgeFactory.java rename turms-server-common/src/main/java/im/turms/server/common/{util => property}/PropertiesUtil.java (99%) create mode 100644 turms-server-common/src/main/java/im/turms/server/common/property/env/common/logging/ConsoleLoggingProperties.java create mode 100644 turms-server-common/src/main/java/im/turms/server/common/property/env/common/logging/FileLoggingProperties.java rename turms-server-common/src/main/java/im/turms/server/common/property/env/common/{ => logging}/LoggingProperties.java (70%) create mode 100644 turms-server-common/src/main/java/im/turms/server/common/util/Formatter.java create mode 100644 turms-server-common/src/main/java/org/slf4j/impl/StaticLoggerBinder.java create mode 100644 turms-server-common/src/main/java/org/slf4j/impl/StaticMDCBinder.java delete mode 100644 turms-server-test-common/src/main/resources/log4j2.xml delete mode 100644 turms-service/src/main/resources/banner.txt delete mode 100644 turms-service/src/main/resources/log4j2-spring.xml delete mode 100644 turms-service/src/main/resources/log4j2.component.properties diff --git a/README.md b/README.md index 758d2ad348..f1e44cd765 100644 --- a/README.md +++ b/README.md @@ -99,7 +99,6 @@ Note: The main disadvantage of the current Turms project is that it does not pro * During business logic processing, there is no synchronization or lock, only CAS * Memory * The Turms server allocates heap or direct memory smartly according to its usage to reduce the memory footprint - * If you add `-XX:+AlwaysPreTouch` to the JVM configuration, the Turms server can ensure that it commits all the required heap memory when the server starts, so that no page faults will occur when the Turms server is running to improve efficiency * The Turms server refactors parts of MongoDB/Redis client dependencies to ensure that there is no redundant memory allocation in the Turms server, which greatly improves the effective use of memory * Cache: The Turms server makes full use of the local memory cache ## Subprojects diff --git a/README_zh.md b/README_zh.md index 90c6bcd13c..bc7296d4be 100644 --- a/README_zh.md +++ b/README_zh.md @@ -104,7 +104,6 @@ Turms基于读扩散消息模型进行架构设计,对业务数据变化感知 - 业务逻辑处理过程中,无同步加锁操作,只有CAS操作 - 内存 - 在划分内存空间时,合理且充分地循环利用堆内存与直接内存 - - 如果您在JVM配置中添加了`-XX:+AlwaysPreTouch`,即可保证Turms在服务端启动时向系统commit所有需要的堆内存,保证Turms服务端在运作时不会发生缺页异常,以提升运行效率 - Turms通过重写MongoDB/Redis客户端依赖的部分实现,保证了Turms服务端中无冗余的内存分配,极大地提高了内存的有效使用率 - 缓存:Turms服务端各功能模块充分利用本地内存缓存 @@ -119,7 +118,7 @@ Turms基于读扩散消息模型进行架构设计,对业务数据变化感知 | turms-client-kotlin | 同上 | | turms-client-swift | 同上 | | turms-plugin | 当指定事件(如用户上下线、消息接收与转发等)被触发时,turms-gateway和turms-service会调用对应的自定义插件以方便开发者实现各种各样定制化功能 | -| turms-plugin-antispam | 基于双数组Trie的AC自动机算法实现(检测的时间复杂度为O(n),n为目标字符串code points的长度) | +| turms-plugin-antispam | 基于双数组Trie的AC自动机算法实现的反垃圾机制(检测的时间复杂度为O(n),n为目标字符串code points的长度) | | turms-plugin-minio | 基于turms-plugin实现的存储服务插件。用于与MinIO服务端进行交互 | | turms-data(TODO) | 尚未发布。基于Flink生态的独立数据分析系统,负责业务数据统计与分析,为turms的管理员统计API与turms-admin运营报表提供底层数据支持 | diff --git a/pom.xml b/pom.xml index ed19b9f721..ac9fce9ec3 100644 --- a/pom.xml +++ b/pom.xml @@ -122,12 +122,6 @@ lettuce-core - - - org.springframework.boot - spring-boot-starter-log4j2 - - com.github.ben-manes.caffeine diff --git a/turms-docs/src/README.md b/turms-docs/src/README.md index 6e80c4a29b..3a1926f1dc 100644 --- a/turms-docs/src/README.md +++ b/turms-docs/src/README.md @@ -104,7 +104,6 @@ Turms基于读扩散消息模型进行架构设计,对业务数据变化感知 - 业务逻辑处理过程中,无同步加锁操作,只有CAS操作 - 内存 - 在划分内存空间时,合理且充分地循环利用堆内存与直接内存 - - 如果您在JVM配置中添加了`-XX:+AlwaysPreTouch`,即可保证Turms在服务端启动时向系统commit所有需要的堆内存,保证Turms服务端在运作时不会发生缺页异常,以提升运行效率 - Turms通过重写MongoDB/Redis客户端依赖的部分实现,保证了Turms服务端中无冗余的内存分配,极大地提高了内存的有效使用率 - 缓存:Turms服务端各功能模块充分利用本地内存缓存 @@ -119,7 +118,7 @@ Turms基于读扩散消息模型进行架构设计,对业务数据变化感知 | turms-client-kotlin | 同上 | | turms-client-swift | 同上 | | turms-plugin | 当指定事件(如用户上下线、消息接收与转发等)被触发时,turms-gateway和turms-service会调用对应的自定义插件以方便开发者实现各种各样定制化功能 | -| turms-plugin-antispam | 基于双数组Trie的AC自动机算法实现(检测的时间复杂度为O(n),n为目标字符串code points的长度) | +| turms-plugin-antispam | 基于双数组Trie的AC自动机算法实现的反垃圾机制(检测的时间复杂度为O(n),n为目标字符串code points的长度) | | turms-plugin-minio | 基于turms-plugin实现的存储服务插件。用于与MinIO服务端进行交互 | | turms-data(TODO) | 尚未发布。基于Flink生态的独立数据分析系统,负责业务数据统计与分析,为turms的管理员统计API与turms-admin运营报表提供底层数据支持 | diff --git a/turms-docs/src/for-developers/observability.md b/turms-docs/src/for-developers/observability.md index 97fe55c7af..5787ceb802 100644 --- a/turms-docs/src/for-developers/observability.md +++ b/turms-docs/src/for-developers/observability.md @@ -135,7 +135,20 @@ Turms与其他常规服务端一样,将可观测性的具体实现分为三类 每条日志都对应着Turms服务端运行时发生的事件,用于追踪系统的运行状态与生成高纬度的统计数据。Turms中的日志分类两大类,即`应用日志`与`业务日志`。应用运行日志本身数量不多,占用空间不大,遵循精与准原则。但为业务分析而设计的客户端API访问日志则不同,它是大部分统计数据的基础数据,是企业的重要资产,因此Turms默认对其进行100%采样,存储消耗巨大。 -注意:Turms的所有日志、度量与链路追踪的数据格式设计,都是兼顾“简单快捷,方便快速查询”与“精准采样,方便日志服务分析”设计的,但Turms本身不提供任何日志分析功能。 +注意 + +* Turms的所有日志、度量与链路追踪的数据格式设计,都是兼顾“简单快捷,方便快速查询”与“精准采样,方便日志服务分析”设计的,但Turms本身不提供任何日志分析功能 +* Turms的日志时间戳与日志切割都是根据UTC时间,而非系统默认时间 + +### 定制实现原因 + +1. 第三方Logging实现过于冗余,性能低下且内存占用高 +2. 避免第三方Logging的开发人员由于缺乏安全常识,写出类似[Remote code injection in Log4j](https://github.com/advisories/GHSA-jfh8-c2jp-5v3q)的Critical bug +3. Turms的日志实现通过“几乎什么功能都没实现”,并且实现了的功能也照着几乎最高性能标准实现(之后我们会避免使用Java低效的`String`与`StringBuilder`,达到更高的性能),因此该实现的吞吐量能比log4j2 async logger高数倍,同时内存开销小数倍 +4. Turms打印日志的过程也非常精简,大概只实现了标准日志库的百分之几的功能,具体包括: + * 调用`log`接口 + * `log`接口内部通过`PooledByteBufAllocator.DEFAULT`分配一块堆外内存,并遍历一遍message,将非占位符直接写入该内存,跳过占位符并写入具体参数,最后将这块内存放到日志处理的MPSC队列中(基于jctools的`MpscUnboundedArrayQueue`) + * 日志处理线程检测到有新的日志(即`ByteBuffer`对象)时,会将该堆外内存写入NIO包的`FileChannel`(可以是控制台、也可以是文件)中,该对象在Linux系统下,会最终调用`pwrite`直接将堆外内存写入文件描述符中 ### 不使用JSON格式的原因 @@ -197,7 +210,7 @@ turms-service的服务端JVM GC配置为:`-Xlog:gc*,gc+age=trace,safepoint:fil 示例: ```spreadsheet -2021-09-02 07:19:27.219 INFO S wzocsebz 3501287524626242885 Thread-28 : turms|0:0:0:0:0:0:0:1|db612e82-199|2021-09-02T07:30:30.414Z|updateUser|1|{ids=[1], updateUserDTO=UpdateUserDTO[password=******, name=null, intro=null, profileAccess=null, permissionGroupId=null, registrationDate=null, isActive=null]}|TRUE| +2021-09-02 07:19:27.219 INFO S wzocsebz 3501287524626242885 Thread-28 : turms|0:0:0:0:0:0:0:1|db612e82-199|2021-09-02 07:30:30.414|updateUser|1|{ids=[1], updateUserDTO=UpdateUserDTO[password=******, name=null, intro=null, profileAccess=null, permissionGroupId=null, registrationDate=null, isActive=null]}|TRUE| ``` #### 客户端API访问日志 @@ -217,9 +230,9 @@ turms-service的服务端JVM GC配置为:`-Xlog:gc*,gc+age=trace,safepoint:fil 示例: ```spreadsheet -2021-08-17 13:21:10.082 INFO G ocnpinxk 4073578036035627538 gateway-tcp-worker-18-2 : 1669286372|100|DESKTOP|1|0:0:0:0:0:0:0:1|6275734689527119988|CREATE_GROUP_MEMBER_REQUEST|32|2021-08-17T13:21:10.079Z|1201||21|3 -2021-08-17 13:21:10.086 INFO G ocnpinxk 8485909300068121199 gateway-tcp-worker-18-1 : 315622910|101|DESKTOP|1|0:0:0:0:0:0:0:1|8981788720014999664|QUERY_GROUP_JOIN_REQUESTS_REQUEST|17|2021-08-17T13:21:10.082Z|1201||21|4 -2021-08-17 13:21:10.087 INFO G ocnpinxk 195568170846055794 gateway-tcp-worker-18-2 : 1669286372|100|DESKTOP|1|0:0:0:0:0:0:0:1|7875023820838742819|CREATE_GROUP_JOIN_QUESTION_REQUEST|181|2021-08-17T13:21:10.083Z|1201||21|4 +2021-08-17 13:21:10.082 INFO G ocnpinxk 4073578036035627538 gateway-tcp-worker-18-2 : 1669286372|100|DESKTOP|1|0:0:0:0:0:0:0:1|6275734689527119988|CREATE_GROUP_MEMBER_REQUEST|32|2021-08-17 13:21:10.079|1201||21|3 +2021-08-17 13:21:10.086 INFO G ocnpinxk 8485909300068121199 gateway-tcp-worker-18-1 : 315622910|101|DESKTOP|1|0:0:0:0:0:0:0:1|8981788720014999664|QUERY_GROUP_JOIN_REQUESTS_REQUEST|17|2021-08-17 13:21:10.082|1201||21|4 +2021-08-17 13:21:10.087 INFO G ocnpinxk 195568170846055794 gateway-tcp-worker-18-2 : 1669286372|100|DESKTOP|1|0:0:0:0:0:0:0:1|7875023820838742819|CREATE_GROUP_JOIN_QUESTION_REQUEST|181|2021-08-17 13:21:10.083|1201||21|4 ``` ##### turms-service服务端 @@ -235,9 +248,9 @@ turms-service的服务端JVM GC配置为:`-Xlog:gc*,gc+age=trace,safepoint:fil 示例: ```spreadsheet -2021-08-17 13:25:11.809 INFO S lkumxlpd 1650561895646191481 Thread-13 : 101|DESKTOP|::1|6798130843268792999|QUERY_MESSAGES_REQUEST|28|2021-08-17T13:25:11.807Z|1001||2 -2021-08-17 13:25:11.809 INFO S lkumxlpd 2979813149711907727 Thread-9 : 100|DESKTOP|::1|5095384146247218867|QUERY_GROUP_JOIN_QUESTIONS_REQUEST|17|2021-08-17T13:25:11.807Z|1002||2 -2021-08-17 13:25:11.809 INFO S lkumxlpd 7231219143674352809 ver-worker-14-1 : 101|DESKTOP|::1|358075665001342897|QUERY_SIGNED_GET_URL_REQUEST|40|2021-08-17T13:25:11.809Z|6000||0 +2021-08-17 13:25:11.809 INFO S lkumxlpd 1650561895646191481 Thread-13 : 101|DESKTOP|::1|6798130843268792999|QUERY_MESSAGES_REQUEST|28|2021-08-17 13:25:11.807|1001||2 +2021-08-17 13:25:11.809 INFO S lkumxlpd 2979813149711907727 Thread-9 : 100|DESKTOP|::1|5095384146247218867|QUERY_GROUP_JOIN_QUESTIONS_REQUEST|17|2021-08-17 13:25:11.807|1002||2 +2021-08-17 13:25:11.809 INFO S lkumxlpd 7231219143674352809 ver-worker-14-1 : 101|DESKTOP|::1|358075665001342897|QUERY_SIGNED_GET_URL_REQUEST|40|2021-08-17 13:25:11.809|6000||0 ``` 补充: diff --git a/turms-docs/src/intro/README.md b/turms-docs/src/intro/README.md index 6e80c4a29b..3a1926f1dc 100644 --- a/turms-docs/src/intro/README.md +++ b/turms-docs/src/intro/README.md @@ -104,7 +104,6 @@ Turms基于读扩散消息模型进行架构设计,对业务数据变化感知 - 业务逻辑处理过程中,无同步加锁操作,只有CAS操作 - 内存 - 在划分内存空间时,合理且充分地循环利用堆内存与直接内存 - - 如果您在JVM配置中添加了`-XX:+AlwaysPreTouch`,即可保证Turms在服务端启动时向系统commit所有需要的堆内存,保证Turms服务端在运作时不会发生缺页异常,以提升运行效率 - Turms通过重写MongoDB/Redis客户端依赖的部分实现,保证了Turms服务端中无冗余的内存分配,极大地提高了内存的有效使用率 - 缓存:Turms服务端各功能模块充分利用本地内存缓存 @@ -119,7 +118,7 @@ Turms基于读扩散消息模型进行架构设计,对业务数据变化感知 | turms-client-kotlin | 同上 | | turms-client-swift | 同上 | | turms-plugin | 当指定事件(如用户上下线、消息接收与转发等)被触发时,turms-gateway和turms-service会调用对应的自定义插件以方便开发者实现各种各样定制化功能 | -| turms-plugin-antispam | 基于双数组Trie的AC自动机算法实现(检测的时间复杂度为O(n),n为目标字符串code points的长度) | +| turms-plugin-antispam | 基于双数组Trie的AC自动机算法实现的反垃圾机制(检测的时间复杂度为O(n),n为目标字符串code points的长度) | | turms-plugin-minio | 基于turms-plugin实现的存储服务插件。用于与MinIO服务端进行交互 | | turms-data(TODO) | 尚未发布。基于Flink生态的独立数据分析系统,负责业务数据统计与分析,为turms的管理员统计API与turms-admin运营报表提供底层数据支持 | diff --git a/turms-docs/src/intro/redevelopment.md b/turms-docs/src/intro/redevelopment.md index f0446de227..5ee767244f 100644 --- a/turms-docs/src/intro/redevelopment.md +++ b/turms-docs/src/intro/redevelopment.md @@ -95,6 +95,8 @@ git submodule foreach git pull origin master * 部分依赖库在一些地方会自行Suppress异常,上层应用代码无法感知。由于出问题的时候,底层库代码与上层应用代码在大部分情况下,是跑在不同的栈上的。除非底层依赖库支持全局的异常回调,否则上层应用甚至无法感知异常的发生。对于一些Trivial级别的错误,上层应用感知不到也没关系。但如果是一些上层应用非常关注的异常(如RPC的TCP连接的异常断开),这将是引发整个系统异常与失序的导火索了。 +* 部分知名依赖库的开发人员甚至缺乏最基本的安全常识。比如`Log4j`的开发人员竟然添加代码来自动检测预备打印的字符串中是否存在`${jndi}`模式,如果存在则调用对应的JNDI服务,并默认开启该功能。作为专门编写日志依赖库的开发人员竟然如此缺乏安全常识,且还通过了PR review。 + 另一方面,自研能规避掉上述所有问题,在提高代码可控性的同时,也极大地降低了研发难度与问题排查难度,并提升代码性能与资源利用率。 综上,Turms项目在引用一个类库时,通常不引入抽象封装库(如Spring),而仅引入实现库。对依赖库中需要性能优化或逻辑优化的点,会直接在Turms项目内部进行重构。结合考虑到自研的难易程度与代码可控性,我们在大部分情况下会尽可能选择自研。 diff --git a/turms-gateway/src/main/java/im/turms/gateway/access/common/UserSessionDispatcher.java b/turms-gateway/src/main/java/im/turms/gateway/access/common/UserSessionDispatcher.java index 459986f454..590ccc81fb 100644 --- a/turms-gateway/src/main/java/im/turms/gateway/access/common/UserSessionDispatcher.java +++ b/turms-gateway/src/main/java/im/turms/gateway/access/common/UserSessionDispatcher.java @@ -29,13 +29,13 @@ import im.turms.gateway.service.mediator.ServiceMediator; import im.turms.server.common.constant.TurmsStatusCode; import im.turms.server.common.dto.CloseReason; -import im.turms.server.common.logging.RequestLoggingContext; +import im.turms.server.common.logging.core.logger.LoggerFactory; import im.turms.server.common.tracing.TracingCloseableContext; import im.turms.server.common.tracing.TracingContext; +import im.turms.server.common.logging.core.logger.Logger; import im.turms.server.common.util.ExceptionUtil; import io.netty.buffer.ByteBuf; import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame; -import lombok.extern.log4j.Log4j2; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.netty.Connection; @@ -49,9 +49,10 @@ /** * @author James Chen */ -@Log4j2 public abstract class UserSessionDispatcher { + private static final Logger LOGGER = LoggerFactory.getLogger(UserSessionDispatcher.class); + private final ApiLoggingContext apiLoggingContext; protected final ServiceMediator serviceMediator; protected final UserRequestDispatcher userRequestDispatcher; @@ -104,10 +105,10 @@ private Mono respondWithRequests(Connection connection, requestData.retain(); // Note that handleRequest() should never return MonoError - RequestLoggingContext loggingContext = new RequestLoggingContext(); + TracingContext ctx = new TracingContext(); userRequestDispatcher.handleRequest(sessionWrapper, requestData) .onErrorResume(throwable -> { - loggingContext.updateMdc(); + ctx.updateThreadContext(); handleNotificationError(throwable, sessionWrapper.getUserSession()); return Mono.empty(); }) @@ -118,9 +119,9 @@ private Mono respondWithRequests(Connection connection, return Mono.from(outbound); }) .onErrorResume(throwable -> handleConnectionError(throwable, sessionWrapper.getConnection(), - sessionWrapper.getUserSession(), loggingContext.getTracingContext())) - .contextWrite(context -> context.put(RequestLoggingContext.CTX_KEY_NAME, loggingContext)) - .doFinally(signal -> loggingContext.clearMdc()) + sessionWrapper.getUserSession(), ctx)) + .contextWrite(context -> context.put(TracingContext.CTX_KEY_NAME, ctx)) + .doFinally(signal -> ctx.clearThreadContext()) .subscribe(); }) .then() @@ -175,7 +176,7 @@ private Mono handleConnectionError(Throwable throwable, TracingContext tracingContext) { if (!ExceptionUtil.isDisconnectedClientError(throwable)) { try (TracingCloseableContext ignored = tracingContext.asCloseable()) { - log.error("Caught an exception from a connection bound with the session: {}", + LOGGER.error("Caught an exception from a connection bound with the session: {}", userSession, throwable); } @@ -191,7 +192,7 @@ private Mono handleConnectionError(Throwable throwable, .onErrorResume(t -> { // Log because this should be the last error handler here try (TracingCloseableContext ignored = tracingContext.asCloseable()) { - log.error("Caught an exception when setting the local session [{}:{}] offline due to connection error", + LOGGER.error("Caught an exception when setting the local session [{}:{}] offline due to connection error", userId, deviceType, t); } return Mono.empty(); @@ -205,7 +206,7 @@ private void handleNotificationError(Throwable throwable, @Nullable UserSession } CloseReason closeReason = CloseReason.get(throwable); if (closeReason.isServerError()) { - log.error("Failed to send outbound notification to the session: " + userSession, throwable); + LOGGER.error("Failed to send outbound notification to the session: " + userSession, throwable); } Long userId = userSession.getUserId(); DeviceType deviceType = userSession.getDeviceType(); diff --git a/turms-gateway/src/main/java/im/turms/gateway/access/common/controller/UserRequestDispatcher.java b/turms-gateway/src/main/java/im/turms/gateway/access/common/controller/UserRequestDispatcher.java index 5542efd36b..ed5d4b1eae 100644 --- a/turms-gateway/src/main/java/im/turms/gateway/access/common/controller/UserRequestDispatcher.java +++ b/turms-gateway/src/main/java/im/turms/gateway/access/common/controller/UserRequestDispatcher.java @@ -35,16 +35,16 @@ import im.turms.server.common.exception.TurmsBusinessException; import im.turms.server.common.factory.NotificationFactory; import im.turms.server.common.healthcheck.ServerStatusManager; -import im.turms.server.common.logging.RequestLoggingContext; -import im.turms.server.common.service.blocklist.BlocklistService; +import im.turms.server.common.logging.core.logger.LoggerFactory; import im.turms.server.common.tracing.TracingCloseableContext; import im.turms.server.common.tracing.TracingContext; +import im.turms.server.common.logging.core.logger.Logger; +import im.turms.server.common.service.blocklist.BlocklistService; import im.turms.server.common.util.ProtoUtil; import io.netty.buffer.ByteBuf; import io.netty.buffer.EmptyByteBuf; import io.netty.buffer.Unpooled; import io.netty.buffer.UnpooledByteBufAllocator; -import lombok.extern.log4j.Log4j2; import org.springframework.stereotype.Component; import reactor.core.publisher.Mono; @@ -58,9 +58,10 @@ * @author James Chen */ @Component -@Log4j2 public class UserRequestDispatcher { + private static final Logger LOGGER = LoggerFactory.getLogger(UserRequestDispatcher.class); + private static final ByteBuf HEARTBEAT_RESPONSE_SUCCESS = new EmptyByteBuf(UnpooledByteBufAllocator.DEFAULT); private static final ByteBuf HEARTBEAT_RESPONSE_UPDATE_NON_EXISTING_SESSION_HEARTBEAT; private static final ByteBuf HEARTBEAT_RESPONSE_SERVER_UNAVAILABLE; @@ -153,8 +154,8 @@ public Mono handleRequest(UserSessionWrapper sessionWrapper, ByteBuf se .onErrorResume(throwable -> { ThrowableInfo info = ThrowableInfo.get(throwable); if (info.code().isServerError()) { - tracingContext.updateMdc(); - log.error("Failed to handle the service request: {}", request, throwable); + tracingContext.updateThreadContext(); + LOGGER.error("Failed to handle the service request: {}", request, throwable); } return Mono.just(NotificationFactory.create(info, request.requestId())); }) @@ -192,8 +193,8 @@ public Mono handleRequest(UserSessionWrapper sessionWrapper, ByteBuf se return ProtoUtil.getDirectByteBuffer(notification); }) .contextWrite(context -> { - RequestLoggingContext loggingContext = context.get(RequestLoggingContext.CTX_KEY_NAME); - loggingContext.setTracingContext(tracingContext); + TracingContext ctx = context.get(TracingContext.CTX_KEY_NAME); + ctx.setTraceId(tracingContext.getTraceId()); return context; }); } @@ -221,7 +222,7 @@ public Mono handleServiceRequest(UserSessionWrapper sessionWr } // Handle the request to get a response TurmsRequest.KindCase requestType = request.type(); - tracingContext.updateMdc(); + tracingContext.updateThreadContext(); return switch (requestType) { case CREATE_SESSION_REQUEST -> sessionController .handleCreateSessionRequest(sessionWrapper, request.createSessionRequest()) @@ -238,7 +239,7 @@ public Mono handleServiceRequest(UserSessionWrapper sessionWr return Mono.just(notification); } finally { serviceRequestBuffer.release(); - tracingContext.clearMdc(); + tracingContext.clearThreadContext(); } } diff --git a/turms-gateway/src/main/java/im/turms/gateway/access/tcp/TcpDispatcher.java b/turms-gateway/src/main/java/im/turms/gateway/access/tcp/TcpDispatcher.java index 2611d4f618..72ca6b32bd 100644 --- a/turms-gateway/src/main/java/im/turms/gateway/access/tcp/TcpDispatcher.java +++ b/turms-gateway/src/main/java/im/turms/gateway/access/tcp/TcpDispatcher.java @@ -23,11 +23,12 @@ import im.turms.gateway.logging.ApiLoggingContext; import im.turms.gateway.service.mediator.ServiceMediator; import im.turms.server.common.healthcheck.ServerStatusManager; +import im.turms.server.common.logging.core.logger.LoggerFactory; +import im.turms.server.common.logging.core.logger.Logger; import im.turms.server.common.property.TurmsPropertiesManager; import im.turms.server.common.property.env.gateway.TcpProperties; import im.turms.server.common.service.blocklist.BlocklistService; import lombok.Getter; -import lombok.extern.log4j.Log4j2; import org.springframework.stereotype.Component; import reactor.netty.DisposableServer; @@ -36,10 +37,11 @@ /** * @author James Chen */ -@Log4j2 @Component public class TcpDispatcher extends UserSessionDispatcher { + private static final Logger LOGGER = LoggerFactory.getLogger(TcpDispatcher.class); + private final DisposableServer server; @Getter private final boolean enabled; @@ -64,7 +66,7 @@ public TcpDispatcher(ApiLoggingContext apiLoggingContext, bindConnectionWithSessionWrapper()); host = server.host(); port = server.port(); - log.info("TCP server started on {}:{}", host, port); + LOGGER.info("TCP server started on {}:{}", host, port); } else { server = null; host = null; @@ -75,7 +77,7 @@ public TcpDispatcher(ApiLoggingContext apiLoggingContext, @PreDestroy public void preDestroy() { if (server != null) { - log.info("Closing TCP server"); + LOGGER.info("Closing TCP server"); server.dispose(); } } diff --git a/turms-gateway/src/main/java/im/turms/gateway/access/tcp/handler/TcpHandlerConfig.java b/turms-gateway/src/main/java/im/turms/gateway/access/tcp/handler/TcpHandlerConfig.java index 172186be7f..2f27abfb42 100644 --- a/turms-gateway/src/main/java/im/turms/gateway/access/tcp/handler/TcpHandlerConfig.java +++ b/turms-gateway/src/main/java/im/turms/gateway/access/tcp/handler/TcpHandlerConfig.java @@ -45,9 +45,9 @@ public void configureConnection(Connection connection) { // Outbound connection.addHandlerLast("varintLengthFieldPrepender", CodecFactory.getVarintLengthFieldPrepender()); - // For advanced operations, they encode messages to buffers themselves, + // For advanced operations, they encode objects to buffers themselves, // "protobufFrameEncoder" will just ignore them. But some simple - // operations pass TurmsNotification instances down, so we need to encode them. + // operations will pass TurmsNotification instances down, so we still need to encode them. connection.addHandlerLast("protobufFrameEncoder", CodecFactory.getProtobufFrameEncoder()); } diff --git a/turms-gateway/src/main/java/im/turms/gateway/access/udp/UdpDispatcher.java b/turms-gateway/src/main/java/im/turms/gateway/access/udp/UdpDispatcher.java index 5c76431143..a6326fa092 100644 --- a/turms-gateway/src/main/java/im/turms/gateway/access/udp/UdpDispatcher.java +++ b/turms-gateway/src/main/java/im/turms/gateway/access/udp/UdpDispatcher.java @@ -31,6 +31,8 @@ import im.turms.server.common.constant.TurmsStatusCode; import im.turms.server.common.dto.CloseReason; import im.turms.server.common.exception.TurmsBusinessException; +import im.turms.server.common.logging.core.logger.LoggerFactory; +import im.turms.server.common.logging.core.logger.Logger; import im.turms.server.common.metrics.TurmsMicrometerChannelMetricsRecorder; import im.turms.server.common.property.TurmsPropertiesManager; import im.turms.server.common.property.env.gateway.UdpProperties; @@ -38,7 +40,6 @@ import io.netty.channel.ChannelOption; import io.netty.channel.socket.DatagramPacket; import lombok.Getter; -import lombok.extern.log4j.Log4j2; import org.springframework.stereotype.Component; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -52,10 +53,11 @@ /** * @author James Chen */ -@Log4j2 @Component public class UdpDispatcher { + private static final Logger LOGGER = LoggerFactory.getLogger(UdpDispatcher.class); + public static UdpDispatcher instance; private static final int REQUEST_LENGTH = Long.BYTES + Byte.BYTES * 2 + Integer.BYTES; @Getter @@ -97,7 +99,7 @@ public UdpDispatcher(ServiceMediator serviceMediator, TurmsPropertiesManager pro }) .bind() .block(); - log.info("UDP server started on {}:{}", host, port); + LOGGER.info("UDP server started on {}:{}", host, port); } else { notificationSink = null; connection = null; @@ -150,11 +152,11 @@ private TurmsStatusCode handleExceptionForIncomingPacket(Throwable throwable) { if (throwable instanceof TurmsBusinessException exception) { TurmsStatusCode code = exception.getCode(); if (code.isServerError()) { - log.error("Failed to handle incoming package", throwable); + LOGGER.error("Failed to handle incoming package", throwable); } return code; } else { - log.error("Failed to handle incoming package", throwable); + LOGGER.error("Failed to handle incoming package", throwable); return TurmsStatusCode.SERVER_INTERNAL_ERROR; } } diff --git a/turms-gateway/src/main/java/im/turms/gateway/access/websocket/WebSocketDispatcher.java b/turms-gateway/src/main/java/im/turms/gateway/access/websocket/WebSocketDispatcher.java index 13690f0445..0cb027cc73 100644 --- a/turms-gateway/src/main/java/im/turms/gateway/access/websocket/WebSocketDispatcher.java +++ b/turms-gateway/src/main/java/im/turms/gateway/access/websocket/WebSocketDispatcher.java @@ -24,10 +24,11 @@ import im.turms.gateway.service.mediator.ServiceMediator; import im.turms.server.common.cluster.node.Node; import im.turms.server.common.healthcheck.ServerStatusManager; +import im.turms.server.common.logging.core.logger.LoggerFactory; +import im.turms.server.common.logging.core.logger.Logger; import im.turms.server.common.property.TurmsPropertiesManager; import im.turms.server.common.property.env.gateway.WebSocketProperties; import im.turms.server.common.service.blocklist.BlocklistService; -import lombok.extern.log4j.Log4j2; import org.springframework.stereotype.Component; import reactor.netty.DisposableServer; @@ -37,9 +38,10 @@ * @author James Chen */ @Component -@Log4j2 public class WebSocketDispatcher extends UserSessionDispatcher { + private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketDispatcher.class); + private final DisposableServer server; public WebSocketDispatcher( @@ -59,7 +61,7 @@ public WebSocketDispatcher( blocklistService, serverStatusManager, bindConnectionWithSessionWrapper()); - log.info("WebSocket server started on {}:{}", server.host(), server.port()); + LOGGER.info("WebSocket server started on {}:{}", server.host(), server.port()); } else { server = null; } @@ -68,7 +70,7 @@ public WebSocketDispatcher( @PreDestroy public void preDestroy() { if (server != null) { - log.info("Closing WebSocket server"); + LOGGER.info("Closing WebSocket server"); server.dispose(); } } diff --git a/turms-gateway/src/main/java/im/turms/gateway/access/websocket/factory/WebSocketFactory.java b/turms-gateway/src/main/java/im/turms/gateway/access/websocket/factory/WebSocketFactory.java index 75563ee912..e35f27cb30 100644 --- a/turms-gateway/src/main/java/im/turms/gateway/access/websocket/factory/WebSocketFactory.java +++ b/turms-gateway/src/main/java/im/turms/gateway/access/websocket/factory/WebSocketFactory.java @@ -31,7 +31,6 @@ import io.netty.handler.codec.http.HttpMethod; import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame; -import lombok.extern.log4j.Log4j2; import org.reactivestreams.Publisher; import org.springframework.boot.web.server.Ssl; import reactor.core.publisher.Flux; @@ -73,7 +72,6 @@ * spring-reative-websocket can do for us with really clear code * @see WebSocketFactory#getHttpRequestHandler */ -@Log4j2 public final class WebSocketFactory { /** * Note: The average size of turms requests is 16~64 bytes, diff --git a/turms-gateway/src/main/java/im/turms/gateway/dao/config/MongoConfig.java b/turms-gateway/src/main/java/im/turms/gateway/dao/config/MongoConfig.java index 002ebf15df..fb28fc4a47 100644 --- a/turms-gateway/src/main/java/im/turms/gateway/dao/config/MongoConfig.java +++ b/turms-gateway/src/main/java/im/turms/gateway/dao/config/MongoConfig.java @@ -22,7 +22,6 @@ import im.turms.server.common.mongo.TurmsMongoClient; import im.turms.server.common.property.TurmsPropertiesManager; import im.turms.server.common.property.env.service.env.database.TurmsMongoProperties; -import lombok.extern.log4j.Log4j2; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -33,7 +32,6 @@ * @author James Chen * @see org.springframework.boot.autoconfigure.data.mongo.MongoReactiveDataAutoConfiguration */ -@Log4j2 @Configuration public class MongoConfig { diff --git a/turms-gateway/src/main/java/im/turms/gateway/fake/ClientFakingManager.java b/turms-gateway/src/main/java/im/turms/gateway/fake/ClientFakingManager.java index 7c801965f5..a4e17f01b0 100644 --- a/turms-gateway/src/main/java/im/turms/gateway/fake/ClientFakingManager.java +++ b/turms-gateway/src/main/java/im/turms/gateway/fake/ClientFakingManager.java @@ -29,12 +29,13 @@ import im.turms.server.common.context.TurmsApplicationContext; import im.turms.server.common.fake.RandomProtobufGenerator; import im.turms.server.common.fake.RandomRequestFactory; +import im.turms.server.common.logging.core.logger.LoggerFactory; +import im.turms.server.common.logging.core.logger.Logger; import im.turms.server.common.property.TurmsPropertiesManager; import im.turms.server.common.property.env.gateway.FakeProperties; import im.turms.server.common.util.ExceptionUtil; import im.turms.server.common.util.ProtoUtil; import io.netty.util.concurrent.DefaultThreadFactory; -import lombok.extern.log4j.Log4j2; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; @@ -54,10 +55,11 @@ * @author James Chen */ @Component -@Log4j2 @Order(Ordered.LOWEST_PRECEDENCE) public class ClientFakingManager { + private static final Logger LOGGER = LoggerFactory.getLogger(ClientFakingManager.class); + private final List clients; private final FakeProperties fakeProperties; private final TcpDispatcher tcpDispatcher; @@ -77,7 +79,7 @@ public ClientFakingManager(TcpDispatcher tcpDispatcher, if (!tcpDispatcher.isEnabled()) { throw new IllegalStateException("Cannot run clients because the TCP server is disabled"); } - log.info("Preparing clients"); + LOGGER.info("Preparing clients"); // Though the local TCP server has just been set up, // we wait to ensure it's ready for connections. // Otherwise, clients will fail to connect due to "Connection reset" @@ -88,7 +90,7 @@ public ClientFakingManager(TcpDispatcher tcpDispatcher, @PostConstruct private void init() { - log.info("Start sending random requests from clients"); + LOGGER.info("Start sending random requests from clients"); startSendingRandomRequests(clients, fakeProperties.getFirstUserId(), fakeProperties.getUserCount(), @@ -154,7 +156,7 @@ private void startSendingRandomRequests(List clients, } client.sendRequest(builder) .onErrorResume(t -> { - log.error("Caught an internal error when sending request: {}", + LOGGER.error("Caught an internal error when sending request: {}", ProtoUtil.toLogString(builder.build()), t); if (ExceptionUtil.isDisconnectedClientError(t)) { @@ -164,7 +166,7 @@ private void startSendingRandomRequests(List clients, }) .subscribe(); } catch (Exception e) { - log.error("Caught an internal error when sending request", e); + LOGGER.error("Caught an internal error when sending request", e); } sentRequestCount++; } @@ -177,14 +179,14 @@ private void startSendingRandomRequests(List clients, break; } } - log.warn("All fake clients has been closed"); + LOGGER.warn("All fake clients has been closed"); }); thread.start(); } private void removeCurrentClient(Iterator clientIterator, TurmsClient client) { clientIterator.remove(); - log.warn("The session {} has been closed and removed", client.getSessionId()); + LOGGER.warn("The session {} has been closed and removed", client.getSessionId()); } } diff --git a/turms-gateway/src/main/java/im/turms/gateway/logging/ClientApiLogging.java b/turms-gateway/src/main/java/im/turms/gateway/logging/ClientApiLogging.java index d91284b1cd..bbe58dbd22 100644 --- a/turms-gateway/src/main/java/im/turms/gateway/logging/ClientApiLogging.java +++ b/turms-gateway/src/main/java/im/turms/gateway/logging/ClientApiLogging.java @@ -20,13 +20,13 @@ import im.turms.common.constant.DeviceType; import im.turms.common.model.dto.notification.TurmsNotification; import im.turms.common.model.dto.request.TurmsRequest; -import im.turms.server.common.logging.CustomLogger; import im.turms.server.common.util.DateUtil; import im.turms.server.common.util.StringUtil; import javax.annotation.Nullable; -import static im.turms.server.common.logging.CustomLogger.LOG_FIELD_DELIMITER; +import static im.turms.server.common.logging.CommonLogger.CLIENT_API_LOGGER; +import static im.turms.server.common.logging.CommonLogger.LOG_FIELD_DELIMITER; /** * @author James Chen @@ -65,13 +65,13 @@ public static void log(@Nullable Integer sessionId, String.valueOf(requestId), requestType.name(), String.valueOf(requestSize), - DateUtil.toISO(requestTime), + DateUtil.toStr(requestTime), // response information String.valueOf(response.getCode()), response.hasData() ? response.getData().getKindCase().name() : "", String.valueOf(response.getSerializedSize()), String.valueOf(processingTime)); - CustomLogger.CLIENT_API_LOGGER.info(message); + CLIENT_API_LOGGER.info(message); } public static void log(@Nullable Integer sessionId, @@ -96,13 +96,13 @@ public static void log(@Nullable Integer sessionId, String.valueOf(requestId), requestType.name(), String.valueOf(requestSize), - DateUtil.toISO(requestTime), + DateUtil.toStr(requestTime), // response information String.valueOf(responseCode), "", // Response data type "0", // Response serialized size String.valueOf(processingTime)); - CustomLogger.CLIENT_API_LOGGER.info(message); + CLIENT_API_LOGGER.info(message); } public static void log(@Nullable Integer sessionId, @@ -129,13 +129,13 @@ public static void log(@Nullable Integer sessionId, String.valueOf(requestId), requestType, String.valueOf(requestSize), - DateUtil.toISO(requestTime), + DateUtil.toStr(requestTime), // response information String.valueOf(responseCode), StringUtil.toString(responseDataType), String.valueOf(responseSize), String.valueOf(processingTime)); - CustomLogger.CLIENT_API_LOGGER.info(message); + CLIENT_API_LOGGER.info(message); } } \ No newline at end of file diff --git a/turms-gateway/src/main/java/im/turms/gateway/logging/NotificationLogging.java b/turms-gateway/src/main/java/im/turms/gateway/logging/NotificationLogging.java index 281c64e170..886bbedc57 100644 --- a/turms-gateway/src/main/java/im/turms/gateway/logging/NotificationLogging.java +++ b/turms-gateway/src/main/java/im/turms/gateway/logging/NotificationLogging.java @@ -18,10 +18,10 @@ package im.turms.gateway.logging; import im.turms.gateway.pojo.dto.SimpleTurmsNotification; -import im.turms.server.common.logging.CustomLogger; import im.turms.server.common.util.StringUtil; -import static im.turms.server.common.logging.CustomLogger.LOG_FIELD_DELIMITER; +import static im.turms.server.common.logging.CommonLogger.LOG_FIELD_DELIMITER; +import static im.turms.server.common.logging.CommonLogger.NOTIFICATION_LOGGER; /** * @author James Chen @@ -44,7 +44,7 @@ public static void log(boolean sent, SimpleTurmsNotification notification, int s String.valueOf(size), // Relayed request info notification.relayedRequestType().name()); - CustomLogger.NOTIFICATION_LOGGER.info(message); + NOTIFICATION_LOGGER.info(message); } } \ No newline at end of file diff --git a/turms-gateway/src/main/java/im/turms/gateway/manager/HeartbeatManager.java b/turms-gateway/src/main/java/im/turms/gateway/manager/HeartbeatManager.java index af9f822100..127cf03598 100644 --- a/turms-gateway/src/main/java/im/turms/gateway/manager/HeartbeatManager.java +++ b/turms-gateway/src/main/java/im/turms/gateway/manager/HeartbeatManager.java @@ -23,10 +23,11 @@ import im.turms.gateway.pojo.bo.session.UserSession; import im.turms.gateway.service.impl.session.SessionService; import im.turms.server.common.dto.CloseReason; +import im.turms.server.common.logging.core.logger.LoggerFactory; +import im.turms.server.common.logging.core.logger.Logger; import im.turms.server.common.service.session.UserStatusService; import io.netty.util.concurrent.DefaultThreadFactory; import lombok.Setter; -import lombok.extern.log4j.Log4j2; import reactor.core.publisher.Mono; import javax.annotation.Nullable; @@ -49,9 +50,10 @@ * online user map to get a snapshot of the online users that need to update * online status (go offline or refresh heartbeat) */ -@Log4j2 public class HeartbeatManager { + private static final Logger LOGGER = LoggerFactory.getLogger(HeartbeatManager.class); + private static final int UPDATE_HEARTBEAT_INTERVAL_MILLIS = 500; private static final float UPDATE_HEARTBEAT_INTERVAL_FACTOR = UPDATE_HEARTBEAT_INTERVAL_MILLIS / 1000f; @@ -87,7 +89,7 @@ public HeartbeatManager(SessionService sessionService, try { updateOnlineUsersTtl(); } catch (Exception e) { - log.error(e); + LOGGER.error("Failed to update the TTL of users on Redis", e); } try { Thread.sleep(UPDATE_HEARTBEAT_INTERVAL_MILLIS); @@ -169,7 +171,7 @@ private Long handleSession(UserSession session, long now) { session.getDeviceType(), closeReason) .onErrorResume(t -> { - log.error("Caught an error when disconnecting the local session: {} with the close reason: {}", + LOGGER.error("Caught an error when disconnecting the local session: {} with the close reason: {}", session, closeReason); return Mono.empty(); }) diff --git a/turms-gateway/src/main/java/im/turms/gateway/manager/ServiceAddressManager.java b/turms-gateway/src/main/java/im/turms/gateway/manager/ServiceAddressManager.java index 540924d12d..eb3c7f0fe5 100644 --- a/turms-gateway/src/main/java/im/turms/gateway/manager/ServiceAddressManager.java +++ b/turms-gateway/src/main/java/im/turms/gateway/manager/ServiceAddressManager.java @@ -21,6 +21,8 @@ import im.turms.server.common.address.AddressCollector; import im.turms.server.common.address.BaseServiceAddressManager; import im.turms.server.common.address.PublicIpManager; +import im.turms.server.common.logging.core.logger.LoggerFactory; +import im.turms.server.common.logging.core.logger.Logger; import im.turms.server.common.property.TurmsProperties; import im.turms.server.common.property.TurmsPropertiesManager; import im.turms.server.common.property.constant.AdvertiseStrategy; @@ -30,7 +32,6 @@ import im.turms.server.common.property.env.gateway.TcpProperties; import im.turms.server.common.property.env.gateway.UdpProperties; import im.turms.server.common.property.env.gateway.WebSocketProperties; -import lombok.extern.log4j.Log4j2; import org.apache.commons.lang3.StringUtils; import org.springframework.boot.autoconfigure.web.ServerProperties; import org.springframework.boot.web.server.Ssl; @@ -43,9 +44,10 @@ * @author James Chen */ @Component -@Log4j2 public class ServiceAddressManager extends BaseServiceAddressManager { + private static final Logger LOGGER = LoggerFactory.getLogger(ServiceAddressManager.class); + private DiscoveryProperties gatewayApiDiscoveryProperties; private String metricsApiAddress; @@ -69,7 +71,7 @@ public ServiceAddressManager( try { updateCollectorAndAddresses(metricsApiProperties, properties); } catch (Exception e) { - log.error("Failed to update address collector", e); + LOGGER.error("Failed to update address collector", e); } } if (areAddressPropertiesChange || isMemberHostChanged) { diff --git a/turms-gateway/src/main/java/im/turms/gateway/manager/UserSessionsManager.java b/turms-gateway/src/main/java/im/turms/gateway/manager/UserSessionsManager.java index 066b8893e8..f99559db98 100644 --- a/turms-gateway/src/main/java/im/turms/gateway/manager/UserSessionsManager.java +++ b/turms-gateway/src/main/java/im/turms/gateway/manager/UserSessionsManager.java @@ -23,12 +23,13 @@ import im.turms.gateway.pojo.bo.session.UserSession; import im.turms.server.common.dto.CloseReason; import im.turms.server.common.lang.ConcurrentEnumMap; +import im.turms.server.common.logging.core.logger.LoggerFactory; +import im.turms.server.common.logging.core.logger.Logger; import im.turms.server.common.throttle.TokenBucketContext; import im.turms.server.common.util.ProtoUtil; import io.netty.buffer.ByteBuf; import io.netty.util.HashedWheelTimer; import lombok.Data; -import lombok.extern.log4j.Log4j2; import org.springframework.data.geo.Point; import org.springframework.util.Assert; @@ -42,9 +43,10 @@ * @author James Chen */ @Data -@Log4j2 public final class UserSessionsManager { + private static final Logger LOGGER = LoggerFactory.getLogger(UserSessionsManager.class); + /** * The count of pending timeouts should be roughly the same as the count of online sessions */ @@ -113,7 +115,7 @@ public boolean pushSessionNotification(DeviceType deviceType, String serverId) { userSession.sendNotification(byteBuffer); return true; } catch (Exception e) { - log.error("Failed to send the session notification", e); + LOGGER.error("Failed to send the session notification", e); return false; } } diff --git a/turms-gateway/src/main/java/im/turms/gateway/plugin/TurmsPluginManager.java b/turms-gateway/src/main/java/im/turms/gateway/plugin/TurmsPluginManager.java index 3a5c7733b0..4e21b60322 100644 --- a/turms-gateway/src/main/java/im/turms/gateway/plugin/TurmsPluginManager.java +++ b/turms-gateway/src/main/java/im/turms/gateway/plugin/TurmsPluginManager.java @@ -24,7 +24,6 @@ import im.turms.server.common.plugin.AbstractTurmsPluginManager; import im.turms.server.common.property.TurmsPropertiesManager; import lombok.Getter; -import lombok.extern.log4j.Log4j2; import org.springframework.context.ApplicationContext; import org.springframework.stereotype.Component; @@ -35,7 +34,6 @@ */ @Component @Getter -@Log4j2 public class TurmsPluginManager extends AbstractTurmsPluginManager { private List notificationHandlerList; diff --git a/turms-gateway/src/main/java/im/turms/gateway/pojo/bo/session/UserSession.java b/turms-gateway/src/main/java/im/turms/gateway/pojo/bo/session/UserSession.java index 55d182d52b..66fae32d2a 100644 --- a/turms-gateway/src/main/java/im/turms/gateway/pojo/bo/session/UserSession.java +++ b/turms-gateway/src/main/java/im/turms/gateway/pojo/bo/session/UserSession.java @@ -22,6 +22,8 @@ import im.turms.common.util.RandomUtil; import im.turms.gateway.pojo.bo.session.connection.NetConnection; import im.turms.server.common.dto.CloseReason; +import im.turms.server.common.logging.core.logger.Logger; +import im.turms.server.common.logging.core.logger.LoggerFactory; import im.turms.server.common.throttle.TokenBucket; import im.turms.server.common.throttle.TokenBucketContext; import im.turms.server.common.tracing.TracingContext; @@ -29,7 +31,6 @@ import lombok.AccessLevel; import lombok.Data; import lombok.Getter; -import lombok.extern.log4j.Log4j2; import org.springframework.data.geo.Point; import javax.annotation.Nullable; @@ -42,9 +43,10 @@ * @author James Chen */ @Data -@Log4j2 public final class UserSession { + private static final Logger LOGGER = LoggerFactory.getLogger(UserSession.class); + private final int id = RandomUtil.nextPositiveInt(); private final int version; @@ -117,7 +119,7 @@ public void close(@NotNull CloseReason closeReason) { if (connection != null) { connection.close(closeReason); } else { - log.warn("The connection is missing for the user session: {}", this); + LOGGER.warn("The connection is missing for the user session: {}", this); } } } diff --git a/turms-gateway/src/main/java/im/turms/gateway/pojo/bo/session/connection/TcpConnection.java b/turms-gateway/src/main/java/im/turms/gateway/pojo/bo/session/connection/TcpConnection.java index 5393781002..22d3582654 100644 --- a/turms-gateway/src/main/java/im/turms/gateway/pojo/bo/session/connection/TcpConnection.java +++ b/turms-gateway/src/main/java/im/turms/gateway/pojo/bo/session/connection/TcpConnection.java @@ -20,8 +20,9 @@ import im.turms.common.model.dto.notification.TurmsNotification; import im.turms.server.common.dto.CloseReason; import im.turms.server.common.factory.NotificationFactory; +import im.turms.server.common.logging.core.logger.LoggerFactory; +import im.turms.server.common.logging.core.logger.Logger; import im.turms.server.common.util.ExceptionUtil; -import lombok.extern.log4j.Log4j2; import reactor.core.publisher.Mono; import reactor.netty.channel.ChannelOperations; @@ -30,9 +31,10 @@ /** * @author James Chen */ -@Log4j2 public class TcpConnection extends NetConnection { + private static final Logger LOGGER = LoggerFactory.getLogger(TcpConnection.class); + private final ChannelOperations connection; protected TcpConnection(ChannelOperations connection, boolean isConnected) { @@ -58,12 +60,12 @@ public void close(CloseReason closeReason) { .then() .doOnError(throwable -> { if (!ExceptionUtil.isDisconnectedClientError(throwable)) { - log.error("Failed to send the close notification", throwable); + LOGGER.error("Failed to send the close notification", throwable); } }) .retryWhen(RETRY_SEND_CLOSE_NOTIFICATION) .onErrorResume(throwable -> { - log.error("Failed to send the close notification with retries exhausted: " + + LOGGER.error("Failed to send the close notification with retries exhausted: " + RETRY_SEND_CLOSE_NOTIFICATION.maxAttempts, throwable); return Mono.empty(); }) @@ -78,7 +80,7 @@ public void close() { connection.dispose(); } catch (Exception e) { if (!ExceptionUtil.isDisconnectedClientError(e)) { - log.error("Failed to close the connection", e); + LOGGER.error("Failed to close the connection", e); } } } diff --git a/turms-gateway/src/main/java/im/turms/gateway/pojo/bo/session/connection/WebSocketConnection.java b/turms-gateway/src/main/java/im/turms/gateway/pojo/bo/session/connection/WebSocketConnection.java index 5f9e0dbc19..04bb753e59 100644 --- a/turms-gateway/src/main/java/im/turms/gateway/pojo/bo/session/connection/WebSocketConnection.java +++ b/turms-gateway/src/main/java/im/turms/gateway/pojo/bo/session/connection/WebSocketConnection.java @@ -20,12 +20,13 @@ import im.turms.common.model.dto.notification.TurmsNotification; import im.turms.server.common.dto.CloseReason; import im.turms.server.common.factory.NotificationFactory; +import im.turms.server.common.logging.core.logger.LoggerFactory; +import im.turms.server.common.logging.core.logger.Logger; import im.turms.server.common.util.ExceptionUtil; import im.turms.server.common.util.ProtoUtil; import io.netty.buffer.ByteBuf; import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame; import io.netty.handler.codec.http.websocketx.WebSocketCloseStatus; -import lombok.extern.log4j.Log4j2; import reactor.core.publisher.Mono; import reactor.netty.Connection; import reactor.netty.http.websocket.WebsocketOutbound; @@ -35,9 +36,10 @@ /** * @author James Chen */ -@Log4j2 public class WebSocketConnection extends NetConnection { + private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketConnection.class); + private final Connection connection; private final WebsocketOutbound out; @@ -65,12 +67,12 @@ public void close(CloseReason closeReason) { .then() .doOnError(throwable -> { if (!ExceptionUtil.isDisconnectedClientError(throwable)) { - log.error("Failed to send the close notification", throwable); + LOGGER.error("Failed to send the close notification", throwable); } }) .retryWhen(RETRY_SEND_CLOSE_NOTIFICATION) .onErrorResume(throwable -> { - log.error("Failed to send the close notification with retries exhausted: " + + LOGGER.error("Failed to send the close notification with retries exhausted: " + RETRY_SEND_CLOSE_NOTIFICATION.maxAttempts, throwable); return Mono.empty(); }) @@ -84,7 +86,7 @@ public void close() { out.sendClose(WebSocketCloseStatus.NORMAL_CLOSURE.code(), null) .onErrorResume(throwable -> { if (!ExceptionUtil.isDisconnectedClientError(throwable)) { - log.error("Failed to close the connection", throwable); + LOGGER.error("Failed to close the connection", throwable); } return Mono.empty(); }) diff --git a/turms-gateway/src/main/java/im/turms/gateway/service/impl/message/InboundRequestService.java b/turms-gateway/src/main/java/im/turms/gateway/service/impl/message/InboundRequestService.java index a150528036..74dfb03bd0 100644 --- a/turms-gateway/src/main/java/im/turms/gateway/service/impl/message/InboundRequestService.java +++ b/turms-gateway/src/main/java/im/turms/gateway/service/impl/message/InboundRequestService.java @@ -28,7 +28,6 @@ import im.turms.server.common.exception.TurmsBusinessException; import im.turms.server.common.rpc.request.HandleServiceRequest; import im.turms.server.common.service.blocklist.BlocklistService; -import lombok.extern.log4j.Log4j2; import org.springframework.stereotype.Service; import reactor.core.publisher.Mono; @@ -39,7 +38,6 @@ * @author James Chen */ @Service -@Log4j2 public class InboundRequestService { private static final ServiceResponse REQUEST_RESPONSE_NO_CONTENT = new ServiceResponse(null, TurmsStatusCode.NO_CONTENT, null); diff --git a/turms-gateway/src/main/java/im/turms/gateway/service/impl/message/OutboundMessageService.java b/turms-gateway/src/main/java/im/turms/gateway/service/impl/message/OutboundMessageService.java index 25f3734abc..961c0f5312 100644 --- a/turms-gateway/src/main/java/im/turms/gateway/service/impl/message/OutboundMessageService.java +++ b/turms-gateway/src/main/java/im/turms/gateway/service/impl/message/OutboundMessageService.java @@ -28,14 +28,15 @@ import im.turms.gateway.pojo.parser.TurmsNotificationParser; import im.turms.gateway.service.impl.session.SessionService; import im.turms.server.common.cluster.node.Node; +import im.turms.server.common.logging.core.logger.LoggerFactory; +import im.turms.server.common.tracing.TracingContext; +import im.turms.server.common.logging.core.logger.Logger; import im.turms.server.common.property.TurmsPropertiesManager; import im.turms.server.common.rpc.service.IOutboundMessageService; -import im.turms.server.common.tracing.TracingContext; import im.turms.server.common.util.AssertUtil; import im.turms.server.common.util.CollectionUtil; import io.netty.buffer.ByteBuf; import io.netty.buffer.RefCntAwareByteBuf; -import lombok.extern.log4j.Log4j2; import org.springframework.stereotype.Component; import reactor.core.publisher.Mono; @@ -49,9 +50,10 @@ * @author James Chen */ @Component -@Log4j2 public class OutboundMessageService implements IOutboundMessageService { + private static final Logger LOGGER = LoggerFactory.getLogger(OutboundMessageService.class); + private final Node node; private final ApiLoggingContext apiLoggingContext; private final SessionService sessionService; @@ -120,7 +122,7 @@ public boolean sendNotificationToLocalClients(TracingContext tracingContext, userSession.sendNotification(wrappedNotificationData, tracingContext); } catch (Exception e) { if (userSession.isSessionOpen()) { - log.warn("Failed to send a notification to the session: {}", userSession); + LOGGER.warn("Failed to send a notification to the session: {}", userSession); } } // Keep the logic easy, and we don't care about whether the notification is really flushed @@ -160,7 +162,7 @@ private void triggerPlugins(@NotNull ByteBuf notificationData, // Note that "parseFrom" won't block because the buffer is fully read notification = TurmsNotification.parseFrom(notificationData.nioBuffer()); } catch (Exception e) { - log.error("Failed to parse TurmsNotification", e); + LOGGER.error("Failed to parse TurmsNotification", e); } if (notification == null) { return; @@ -183,7 +185,7 @@ private void triggerPlugins(@NotNull ByteBuf notificationData, } resultMono .onErrorResume(t -> { - log.error("Plugins failed to handle", t); + LOGGER.error("Plugins failed to handle", t); return Mono.empty(); }) .subscribe(); diff --git a/turms-gateway/src/main/java/im/turms/gateway/service/impl/observability/MetricsService.java b/turms-gateway/src/main/java/im/turms/gateway/service/impl/observability/MetricsService.java index 50b4d1aa80..6844b5f2c2 100644 --- a/turms-gateway/src/main/java/im/turms/gateway/service/impl/observability/MetricsService.java +++ b/turms-gateway/src/main/java/im/turms/gateway/service/impl/observability/MetricsService.java @@ -20,7 +20,6 @@ import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.Metrics; import lombok.Getter; -import lombok.extern.log4j.Log4j2; import org.springframework.stereotype.Service; /** @@ -28,7 +27,6 @@ */ @Service -@Log4j2 public class MetricsService { @Getter diff --git a/turms-gateway/src/main/java/im/turms/gateway/service/impl/observability/StatisticsService.java b/turms-gateway/src/main/java/im/turms/gateway/service/impl/observability/StatisticsService.java index 05aca18a62..16f96c621a 100644 --- a/turms-gateway/src/main/java/im/turms/gateway/service/impl/observability/StatisticsService.java +++ b/turms-gateway/src/main/java/im/turms/gateway/service/impl/observability/StatisticsService.java @@ -19,14 +19,12 @@ import im.turms.gateway.service.impl.session.SessionService; import im.turms.server.common.rpc.service.IStatisticsService; -import lombok.extern.log4j.Log4j2; import org.springframework.stereotype.Service; /** * @author James Chen */ @Service -@Log4j2 public class StatisticsService implements IStatisticsService { private final SessionService sessionService; diff --git a/turms-gateway/src/main/java/im/turms/gateway/service/mediator/ServiceMediator.java b/turms-gateway/src/main/java/im/turms/gateway/service/mediator/ServiceMediator.java index be5fe4ff4e..5fa0e00ef9 100644 --- a/turms-gateway/src/main/java/im/turms/gateway/service/mediator/ServiceMediator.java +++ b/turms-gateway/src/main/java/im/turms/gateway/service/mediator/ServiceMediator.java @@ -36,7 +36,6 @@ import im.turms.server.common.dto.CloseReason; import im.turms.server.common.dto.ServiceRequest; import im.turms.server.common.exception.TurmsBusinessException; -import lombok.extern.log4j.Log4j2; import org.springframework.data.geo.Point; import org.springframework.stereotype.Component; import reactor.core.publisher.Mono; @@ -53,7 +52,6 @@ * @author James Chen */ @Component -@Log4j2 public class ServiceMediator { private static final long ADMIN_ID = 0; diff --git a/turms-gateway/src/main/resources/application-dev.yaml b/turms-gateway/src/main/resources/application-dev.yaml index 29f0c6ca39..b29a37c7c6 100644 --- a/turms-gateway/src/main/resources/application-dev.yaml +++ b/turms-gateway/src/main/resources/application-dev.yaml @@ -33,4 +33,5 @@ turms: # For testing purposes enabled: true logging: - enable-console-appender: true \ No newline at end of file + console: + enabled: true \ No newline at end of file diff --git a/turms-gateway/src/main/resources/banner.txt b/turms-gateway/src/main/resources/banner.txt deleted file mode 100644 index 763333df42..0000000000 --- a/turms-gateway/src/main/resources/banner.txt +++ /dev/null @@ -1,8 +0,0 @@ -${AnsiColor.BRIGHT_CYAN} -${application.title}${application.formatted-version} - ______ ______ __ - /_ __/__ __ _____ ____ ___ _____ / ____/____ _ / /_ ___ _ __ ____ _ __ __ - / / / / / // ___// __ `__ \ / ___/ / / __ / __ `// __// _ \| | /| / // __ `// / / / - / / / /_/ // / / / / / / /(__ ) / /_/ // /_/ // /_ / __/| |/ |/ // /_/ // /_/ / -/_/ \__,_//_/ /_/ /_/ /_//____/ \____/ \__,_/ \__/ \___/ |__/|__/ \__,_/ \__, / - /____/ \ No newline at end of file diff --git a/turms-gateway/src/main/resources/log4j2-spring.xml b/turms-gateway/src/main/resources/log4j2-spring.xml deleted file mode 100644 index 3623328fa4..0000000000 --- a/turms-gateway/src/main/resources/log4j2-spring.xml +++ /dev/null @@ -1,68 +0,0 @@ - - - - %xwEx - %5p - yyyy-MM-dd HH:mm:ss.SSS - %clr{%d{${sys:LOG_DATEFORMAT_PATTERN}}{GMT+0}}{faint} %clr{${sys:LOG_LEVEL_PATTERN}} %clr{${myctx:NODE_TYPE} ${myctx:NODE_ID}}{cyan} %clr{[%-19.19X{traceId}]}{magenta} %clr{%-15.15t}{faint} %clr{%-40.40c{1.}}{cyan} %clr{:}{faint} %m%n${sys:LOG_EXCEPTION_CONVERSION_WORD} - %d{${sys:LOG_DATEFORMAT_PATTERN}}{GMT+0} ${sys:LOG_LEVEL_PATTERN} ${myctx:NODE_TYPE} ${myctx:NODE_ID} %-19.19X{traceId} %t %-40.40c{1.} : %m%n${sys:LOG_EXCEPTION_CONVERSION_WORD} - %d{${sys:LOG_DATEFORMAT_PATTERN}}{GMT+0} ${sys:LOG_LEVEL_PATTERN} ${myctx:NODE_TYPE} ${myctx:NODE_ID} %-19.19X{traceId} %t : %m%n${sys:LOG_EXCEPTION_CONVERSION_WORD} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/turms-gateway/src/main/resources/log4j2.component.properties b/turms-gateway/src/main/resources/log4j2.component.properties deleted file mode 100644 index d76f35316d..0000000000 --- a/turms-gateway/src/main/resources/log4j2.component.properties +++ /dev/null @@ -1,2 +0,0 @@ -# org.apache.logging.log4j.core.util.Constants -Log4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector \ No newline at end of file diff --git a/turms-gateway/src/test/java/integration/access/TcpServerIT.java b/turms-gateway/src/test/java/integration/access/TcpServerIT.java index d0aaa042df..0a844c3eaf 100644 --- a/turms-gateway/src/test/java/integration/access/TcpServerIT.java +++ b/turms-gateway/src/test/java/integration/access/TcpServerIT.java @@ -5,7 +5,6 @@ import im.turms.server.common.healthcheck.ServerStatusManager; import im.turms.server.common.property.env.gateway.TcpProperties; import im.turms.server.common.service.blocklist.BlocklistService; -import lombok.extern.log4j.Log4j2; import org.junit.jupiter.api.Test; import org.mockito.stubbing.OngoingStubbing; import reactor.core.publisher.Mono; @@ -20,7 +19,6 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -@Log4j2 class TcpServerIT { private static final ConnectionHandler NEVER_CLOSE = (connection, isWebSocketConnection, in, out, onClose) -> Mono.never(); @@ -47,7 +45,7 @@ void shouldCloseOrAcceptConnection_accordingTo_ServerStatusManager_isActive() th int i = 0; for (Boolean isActive : isActiveReturnValues) { - log.debug("The client with index {} is connecting...", i); + System.out.printf("The client with index %d is connecting...", i); Connection connection = TcpClient.create() .host(server.host()) .port(server.port()) diff --git a/turms-gateway/src/test/java/unit/im/turms/gateway/service/impl/OutboundMessageServiceTests.java b/turms-gateway/src/test/java/unit/im/turms/gateway/service/impl/OutboundMessageServiceTests.java index 85fd907eb4..ed6e74e9b3 100644 --- a/turms-gateway/src/test/java/unit/im/turms/gateway/service/impl/OutboundMessageServiceTests.java +++ b/turms-gateway/src/test/java/unit/im/turms/gateway/service/impl/OutboundMessageServiceTests.java @@ -27,11 +27,11 @@ import im.turms.gateway.service.impl.message.OutboundMessageService; import im.turms.gateway.service.impl.session.SessionService; import im.turms.server.common.cluster.node.Node; +import im.turms.server.common.tracing.TracingContext; import im.turms.server.common.property.TurmsProperties; import im.turms.server.common.property.TurmsPropertiesManager; import im.turms.server.common.property.env.common.PluginProperties; import im.turms.server.common.throttle.TokenBucketContext; -import im.turms.server.common.tracing.TracingContext; import io.netty.buffer.ByteBuf; import io.netty.buffer.UnpooledByteBufAllocator; import org.junit.jupiter.api.Test; diff --git a/turms-plugin-demo/src/main/java/com/mydomain/MyTurmsRequestHandler.java b/turms-plugin-demo/src/main/java/com/mydomain/MyTurmsRequestHandler.java index e7152e8e36..ded44ad40b 100644 --- a/turms-plugin-demo/src/main/java/com/mydomain/MyTurmsRequestHandler.java +++ b/turms-plugin-demo/src/main/java/com/mydomain/MyTurmsRequestHandler.java @@ -1,11 +1,11 @@ package com.mydomain; import im.turms.common.model.dto.request.TurmsRequest; +import im.turms.server.common.logging.core.logger.LoggerFactory; +import im.turms.server.common.logging.core.logger.Logger; import im.turms.server.common.plugin.TurmsExtension; import im.turms.service.plugin.extension.ClientRequestTransformer; import im.turms.service.workflow.access.servicerequest.dto.ClientRequest; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; import reactor.core.publisher.Mono; import javax.validation.constraints.NotNull; @@ -15,7 +15,7 @@ */ public class MyTurmsRequestHandler extends TurmsExtension implements ClientRequestTransformer { - private static final Logger logger = LogManager.getLogger(MyTurmsRequestHandler.class); + private static final Logger LOGGER = LoggerFactory.getLogger(MyTurmsRequestHandler.class); @Override public Mono transform(@NotNull ClientRequest clientRequest) { @@ -24,7 +24,7 @@ public Mono transform(@NotNull ClientRequest clientRequest) { builder.getCreateMessageRequestBuilder() .setText("Hi Turms, I have changed the text of the request"); } - logger.info("Hi Turms, I have handled the request"); + LOGGER.info("Hi Turms, I have handled the request"); return Mono.just(clientRequest); } diff --git a/turms-plugins/turms-plugin-antispam/src/main/java/im/turms/plugin/antispam/property/AntiSpamProperties.java b/turms-plugins/turms-plugin-antispam/src/main/java/im/turms/plugin/antispam/property/AntiSpamProperties.java index a0bf00a026..324b6e74b6 100644 --- a/turms-plugins/turms-plugin-antispam/src/main/java/im/turms/plugin/antispam/property/AntiSpamProperties.java +++ b/turms-plugins/turms-plugin-antispam/src/main/java/im/turms/plugin/antispam/property/AntiSpamProperties.java @@ -27,8 +27,6 @@ import java.util.Set; import static im.turms.plugin.antispam.property.TextParsingStrategy.NORMALIZATION_TRANSLITERATION; -import static im.turms.plugin.antispam.property.TextType.CREATE_MESSAGE_REQUEST_TEXT; -import static im.turms.plugin.antispam.property.UnwantedWordHandleStrategy.MASK_TEXT; import static im.turms.plugin.antispam.property.UnwantedWordHandleStrategy.REJECT_REQUEST; /** diff --git a/turms-plugins/turms-plugin-minio/src/main/java/im/turms/plugin/impl/MinioStorageServiceProvider.java b/turms-plugins/turms-plugin-minio/src/main/java/im/turms/plugin/impl/MinioStorageServiceProvider.java index 2cd6f5df90..b31d546002 100644 --- a/turms-plugins/turms-plugin-minio/src/main/java/im/turms/plugin/impl/MinioStorageServiceProvider.java +++ b/turms-plugins/turms-plugin-minio/src/main/java/im/turms/plugin/impl/MinioStorageServiceProvider.java @@ -20,14 +20,14 @@ import im.turms.common.constant.ContentType; import im.turms.server.common.constant.TurmsStatusCode; import im.turms.server.common.exception.TurmsBusinessException; +import im.turms.server.common.logging.core.logger.LoggerFactory; +import im.turms.server.common.logging.core.logger.Logger; import im.turms.server.common.plugin.TurmsExtension; import im.turms.server.common.property.TurmsProperties; import im.turms.server.common.property.env.service.business.StorageProperties; import im.turms.service.plugin.extension.StorageServiceProvider; import im.turms.service.workflow.service.impl.group.GroupMemberService; import im.turms.service.workflow.service.impl.message.MessageService; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; import org.springframework.context.ApplicationContext; import org.springframework.util.MimeTypeUtils; import reactor.core.publisher.Mono; @@ -72,7 +72,7 @@ */ public class MinioStorageServiceProvider extends TurmsExtension implements StorageServiceProvider { - private static final Logger logger = LogManager.getLogger(MinioStorageServiceProvider.class); + private static final Logger LOGGER = LoggerFactory.getLogger(MinioStorageServiceProvider.class); private static final int TIMEOUT_SECONDS = 10; @@ -245,7 +245,7 @@ public void run() { currentRetryTimes++; int maxAttempts = minioProperties.getRetry().getMaxAttempts(); if (maxAttempts > 0 && currentRetryTimes > maxAttempts) { - logger.warn("The MinIO client failed to initialize"); + LOGGER.warn("The MinIO client failed to initialize"); executor.shutdown(); throw new RuntimeException("The MinIO client failed to initialize"); } @@ -257,7 +257,7 @@ public void run() { } }, minioProperties.getRetry().getInitialInterval(), minioProperties.getRetry().getInterval(), TimeUnit.SECONDS); } else { - logger.warn("The MinIO client failed to initialize"); + LOGGER.warn("The MinIO client failed to initialize"); } } } @@ -286,7 +286,7 @@ private void initClient(String endpointStr, String regionStr, String accessKey, .credentialsProvider(credentialsProvider) .region(region) .build(); - logger.info("The MinIO client is connecting to: {}", endpoint); + LOGGER.info("The MinIO client is connecting to: {}", endpoint); } private void initBuckets() throws InterruptedException, ExecutionException, TimeoutException { @@ -295,13 +295,13 @@ private void initBuckets() throws InterruptedException, ExecutionException, Time boolean exists = bucketExists(type); String bucket = getBucketName(type); if (!exists) { - logger.info("Bucket {} is being created", bucket); + LOGGER.info("Bucket {} is being created", bucket); createBucket(type); putBucketPolicy(type); putBucketLifecycleConfig(type); - logger.info("Bucket {} is created", bucket); + LOGGER.info("Bucket {} is created", bucket); } else { - logger.info("Bucket {} has already existed", bucket); + LOGGER.info("Bucket {} has already existed", bucket); } } } diff --git a/turms-server-common/pom.xml b/turms-server-common/pom.xml index 28067083ae..f6b748b891 100644 --- a/turms-server-common/pom.xml +++ b/turms-server-common/pom.xml @@ -32,9 +32,10 @@ ${revision} - 10.4.0 + 11.0.0 2.2.1 - 1.5.12 + 3.3.0 + 1.5.13 @@ -53,6 +54,11 @@ flight-recorder-starter ${flight-recorder-starter.version} + + org.jctools + jctools-core + ${jctools-core.version} + org.springdoc springdoc-openapi-webflux-ui diff --git a/turms-server-common/src/main/java/im/turms/server/common/BaseTurmsApplication.java b/turms-server-common/src/main/java/im/turms/server/common/BaseTurmsApplication.java index f29a705c73..366f91a05a 100644 --- a/turms-server-common/src/main/java/im/turms/server/common/BaseTurmsApplication.java +++ b/turms-server-common/src/main/java/im/turms/server/common/BaseTurmsApplication.java @@ -17,13 +17,12 @@ package im.turms.server.common; -import org.apache.logging.log4j.Logger; -import org.apache.logging.log4j.core.async.AsyncLogger; +import im.turms.server.common.logging.core.logger.LoggerFactory; +import im.turms.server.common.logging.core.logger.Logger; +import im.turms.server.common.logging.core.processor.LogProcessorRunner; import org.springframework.boot.SpringApplication; -import java.util.concurrent.TimeUnit; - -import static org.apache.logging.log4j.LogManager.getLogger; +import java.time.Duration; /** * @author James Chen @@ -35,7 +34,7 @@ public abstract class BaseTurmsApplication { // Disable the max direct memory limit and buffer counters of Netty // so that we can get the used direct memory via BufferPoolMXBean without depending on ByteBufAllocator of Netty System.setProperty("io.netty.maxDirectMemory", "0"); - System.setProperty("log4j2.contextSelector", "org.apache.logging.log4j.core.async.AsyncLoggerContextSelector"); + System.setProperty("spring.main.banner-mode", "off"); } protected static void bootstrap(Class applicationClass, String[] args) { @@ -45,12 +44,16 @@ protected static void bootstrap(Class applicationClass, String[] args) { // Note that org.springframework.boot.SpringApplication.handleRunFailure may not trigger // im.turms.service.context.ApplicationContextConfig.handleContextClosedEvent // if the context hadn't been initialized. - - Logger log = getLogger(BaseTurmsApplication.class); - log.error(e); - // Flush - AsyncLogger logger = (AsyncLogger) log; - logger.getContext().stop(1, TimeUnit.MINUTES); + if (LoggerFactory.isInitialized()) { + try { + Logger logger = LoggerFactory.getLogger(BaseTurmsApplication.class); + logger.error("Failed to bootstrap", e); + } catch (Exception ignored) { + e.printStackTrace(); + } + } else { + e.printStackTrace(); + } // Make sure turms can exit if SpringApplication failed to bootstrap // (e.g. PortInUseException) because there are may still some non-daemon diff --git a/turms-server-common/src/main/java/im/turms/server/common/access/http/dto/response/ResponseDTO.java b/turms-server-common/src/main/java/im/turms/server/common/access/http/dto/response/ResponseDTO.java index 1008c1f657..36e7f7e3d0 100644 --- a/turms-server-common/src/main/java/im/turms/server/common/access/http/dto/response/ResponseDTO.java +++ b/turms-server-common/src/main/java/im/turms/server/common/access/http/dto/response/ResponseDTO.java @@ -29,6 +29,7 @@ /** * @author James Chen + * @see im.turms.service.workflow.access.http.dto.response.ErrorAttributes */ @AllArgsConstructor(onConstructor = @__(@JsonCreator)) @Data diff --git a/turms-server-common/src/main/java/im/turms/server/common/address/AddressCollector.java b/turms-server-common/src/main/java/im/turms/server/common/address/AddressCollector.java index 49944c250a..fe0e78955f 100644 --- a/turms-server-common/src/main/java/im/turms/server/common/address/AddressCollector.java +++ b/turms-server-common/src/main/java/im/turms/server/common/address/AddressCollector.java @@ -19,7 +19,6 @@ import im.turms.server.common.property.constant.AdvertiseStrategy; import lombok.Getter; -import lombok.extern.log4j.Log4j2; import javax.annotation.Nullable; import javax.validation.constraints.NotNull; @@ -32,7 +31,6 @@ /** * @author James Chen */ -@Log4j2 public class AddressCollector { private static final Duration IP_DETECTION_TIMEOUT = Duration.ofSeconds(15); diff --git a/turms-server-common/src/main/java/im/turms/server/common/address/BaseServiceAddressManager.java b/turms-server-common/src/main/java/im/turms/server/common/address/BaseServiceAddressManager.java index 1915cc4e08..d504fa61ee 100644 --- a/turms-server-common/src/main/java/im/turms/server/common/address/BaseServiceAddressManager.java +++ b/turms-server-common/src/main/java/im/turms/server/common/address/BaseServiceAddressManager.java @@ -17,10 +17,11 @@ package im.turms.server.common.address; +import im.turms.server.common.logging.core.logger.LoggerFactory; +import im.turms.server.common.logging.core.logger.Logger; import im.turms.server.common.property.TurmsProperties; import im.turms.server.common.property.constant.AdvertiseStrategy; import im.turms.server.common.property.env.common.AddressProperties; -import lombok.extern.log4j.Log4j2; import org.springframework.boot.autoconfigure.web.ServerProperties; import org.springframework.boot.web.server.Ssl; @@ -34,9 +35,10 @@ /** * @author James Chen */ -@Log4j2 public abstract class BaseServiceAddressManager { + private static final Logger LOGGER = LoggerFactory.getLogger(BaseServiceAddressManager.class); + protected final PublicIpManager publicIpManager; private final List> onAddressesChangedListeners = new LinkedList<>(); @@ -92,7 +94,7 @@ protected boolean updateMemberHostIfChanged(TurmsProperties newProperties) { memberAddressProperties = newAddressProperties; return true; } catch (UnknownHostException e) { - log.error(e); + LOGGER.error("Failed to update the member host", e); } } return false; @@ -128,7 +130,7 @@ protected void triggerOnAddressesChangedListeners(AddressCollection addresses) { try { listener.accept(addresses); } catch (Exception e) { - log.error("Failed to run onAddressesChangedListener", e); + LOGGER.error("Failed to run onAddressesChangedListener", e); } } } diff --git a/turms-server-common/src/main/java/im/turms/server/common/client/TurmsTcpClient.java b/turms-server-common/src/main/java/im/turms/server/common/client/TurmsTcpClient.java index b443bb8981..dac6eb8c6e 100644 --- a/turms-server-common/src/main/java/im/turms/server/common/client/TurmsTcpClient.java +++ b/turms-server-common/src/main/java/im/turms/server/common/client/TurmsTcpClient.java @@ -25,7 +25,8 @@ import im.turms.common.util.RandomUtil; import im.turms.server.common.access.tcp.codec.CodecFactory; import im.turms.server.common.constant.TurmsStatusCode; -import lombok.extern.log4j.Log4j2; +import im.turms.server.common.logging.core.logger.LoggerFactory; +import im.turms.server.common.logging.core.logger.Logger; import reactor.core.publisher.Mono; import reactor.netty.channel.ChannelOperations; import reactor.netty.resources.LoopResources; @@ -36,9 +37,10 @@ /** * @author James Chen */ -@Log4j2 public class TurmsTcpClient extends TurmsClient { + private static final Logger LOGGER = LoggerFactory.getLogger(TurmsTcpClient.class); + private ChannelOperations connection; private long userId; private DeviceType deviceType; @@ -79,7 +81,7 @@ public Mono connect(String host, int port, LoopResources loopResources) { connection .receiveObject() .onErrorResume(t -> { - log.error("The turms client is closed unexpectedly", t); + LOGGER.error("The turms client is closed unexpectedly", t); return Mono.empty(); }) .cast(TurmsNotification.class) diff --git a/turms-server-common/src/main/java/im/turms/server/common/cluster/node/Node.java b/turms-server-common/src/main/java/im/turms/server/common/cluster/node/Node.java index 57d1c5a213..24f231e899 100644 --- a/turms-server-common/src/main/java/im/turms/server/common/cluster/node/Node.java +++ b/turms-server-common/src/main/java/im/turms/server/common/cluster/node/Node.java @@ -29,6 +29,8 @@ import im.turms.server.common.cluster.service.rpc.RpcService; import im.turms.server.common.context.TurmsApplicationContext; import im.turms.server.common.healthcheck.HealthCheckManager; +import im.turms.server.common.logging.core.logger.LoggerFactory; +import im.turms.server.common.logging.core.logger.Logger; import im.turms.server.common.property.TurmsProperties; import im.turms.server.common.property.TurmsPropertiesManager; import im.turms.server.common.property.env.common.cluster.ClusterProperties; @@ -38,7 +40,6 @@ import im.turms.server.common.property.env.common.cluster.SharedConfigProperties; import im.turms.server.common.property.env.common.cluster.connection.ConnectionProperties; import lombok.Getter; -import lombok.extern.log4j.Log4j2; import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.context.ApplicationContext; @@ -53,9 +54,10 @@ * @author James Chen */ @Getter -@Log4j2 public class Node { + private static final Logger LOGGER = LoggerFactory.getLogger(Node.class); + /** * For best performance for im.turms.server.common.log4j.plugin.TurmsContextLookup * to access, we use static. @@ -105,9 +107,9 @@ public Node( try { String version = turmsContext.getVersion(); nodeVersion = NodeVersion.parse(version); - log.info("The local node version is {}", version); + LOGGER.info("The local node version is {}", version); } catch (Exception e) { - log.error("Failed to get the Turms version of the current node", e); + LOGGER.error("Failed to get the Turms version of the current node", e); throw e; } @@ -164,7 +166,7 @@ public static synchronized String initNodeId(String id) { } if (StringUtils.isBlank(id)) { id = RandomStringUtils.randomAlphabetic(8).toLowerCase(); - log.warn("A random node ID {} has been used. You should better set a node ID manually in the production environment", + LOGGER.warn("A random node ID {} has been used. You should better set a node ID manually in the production environment", id); } else { if (id.length() > NodeProperties.NODE_ID_MAX_LENGTH) { @@ -242,7 +244,7 @@ private void shutdownService(ClusterService service) { try { service.stop(); } catch (Exception e) { - log.error("Failed to stop service {}", service.getClass().getName(), e); + LOGGER.error("Failed to stop service {}", service.getClass().getName(), e); } } diff --git a/turms-server-common/src/main/java/im/turms/server/common/cluster/service/codec/CodecService.java b/turms-server-common/src/main/java/im/turms/server/common/cluster/service/codec/CodecService.java index 66bee42990..a0f7da9f0b 100644 --- a/turms-server-common/src/main/java/im/turms/server/common/cluster/service/codec/CodecService.java +++ b/turms-server-common/src/main/java/im/turms/server/common/cluster/service/codec/CodecService.java @@ -25,7 +25,6 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.PooledByteBufAllocator; import io.netty.util.IllegalReferenceCountException; -import lombok.extern.log4j.Log4j2; /** * Note that to get a better performance and serialize data with the least bytes, @@ -37,7 +36,6 @@ * * @author James Chen */ -@Log4j2 public class CodecService implements ClusterService { public CodecService() { diff --git a/turms-server-common/src/main/java/im/turms/server/common/cluster/service/config/SharedConfigService.java b/turms-server-common/src/main/java/im/turms/server/common/cluster/service/config/SharedConfigService.java index d97d9c950a..49bf9258ec 100644 --- a/turms-server-common/src/main/java/im/turms/server/common/cluster/service/config/SharedConfigService.java +++ b/turms-server-common/src/main/java/im/turms/server/common/cluster/service/config/SharedConfigService.java @@ -30,7 +30,6 @@ import im.turms.server.common.mongo.operation.option.Filter; import im.turms.server.common.mongo.operation.option.Update; import im.turms.server.common.property.env.service.env.database.TurmsMongoProperties; -import lombok.extern.log4j.Log4j2; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -40,7 +39,6 @@ /** * @author James Chen */ -@Log4j2 public class SharedConfigService implements ClusterService { // Note that 60s is the minimum TTL supported by the TTL index of MongoDB diff --git a/turms-server-common/src/main/java/im/turms/server/common/cluster/service/config/SharedPropertyService.java b/turms-server-common/src/main/java/im/turms/server/common/cluster/service/config/SharedPropertyService.java index bfe7f5c70e..41eee18fde 100644 --- a/turms-server-common/src/main/java/im/turms/server/common/cluster/service/config/SharedPropertyService.java +++ b/turms-server-common/src/main/java/im/turms/server/common/cluster/service/config/SharedPropertyService.java @@ -27,6 +27,8 @@ import im.turms.server.common.cluster.service.discovery.DiscoveryService; import im.turms.server.common.cluster.service.idgen.IdService; import im.turms.server.common.cluster.service.rpc.RpcService; +import im.turms.server.common.logging.core.logger.LoggerFactory; +import im.turms.server.common.logging.core.logger.Logger; import im.turms.server.common.mongo.exception.DuplicateKeyException; import im.turms.server.common.mongo.operation.option.Filter; import im.turms.server.common.mongo.operation.option.Update; @@ -34,7 +36,6 @@ import im.turms.server.common.property.TurmsPropertiesManager; import im.turms.server.common.property.env.gateway.GatewayProperties; import im.turms.server.common.property.env.service.ServiceProperties; -import lombok.extern.log4j.Log4j2; import reactor.core.publisher.Mono; import java.time.Duration; @@ -48,9 +49,10 @@ /** * @author James Chen */ -@Log4j2 public class SharedPropertyService implements ClusterService { + private static final Logger LOGGER = LoggerFactory.getLogger(SharedPropertyService.class); + private final String clusterId; private final NodeType nodeType; @@ -97,7 +99,7 @@ public void start() { notifyListeners(sharedClusterProperties.getTurmsProperties()); } case INVALIDATE -> { - log.warn("The shared properties has been removed from database unexpectedly"); + LOGGER.warn("The shared properties has been removed from database unexpectedly"); initializeSharedProperties().subscribe(); } default -> { @@ -106,7 +108,7 @@ public void start() { } }) .onErrorContinue( - (throwable, o) -> log.error("Error while processing the change stream event of SharedProperties: {}", o, throwable)) + (throwable, o) -> LOGGER.error("Error while processing the change stream event of SharedProperties: {}", o, throwable)) .subscribe(); initializeSharedProperties().block(Duration.ofMinutes(1)); } @@ -116,7 +118,7 @@ public void start() { * there is not an efficient way to update nested objects of a document in MongoDB. */ public Mono updateSharedProperties(TurmsProperties turmsProperties) { - log.info("Share new turms properties to all members"); + LOGGER.info("Share new turms properties to all members"); SharedClusterProperties clusterProperties = getClusterProperties(sharedClusterProperties, turmsProperties); Date now = new Date(); Filter filter = Filter.newBuilder(2) @@ -131,10 +133,10 @@ public Mono updateSharedProperties(TurmsProperties turmsProperties) { update.set(SharedClusterProperties.Fields.serviceProperties, clusterProperties.getServiceProperties()); } return sharedConfigService.upsert(filter, update, clusterProperties) - .doOnError(e -> log.error("Failed to share new turms properties", e)) + .doOnError(e -> LOGGER.error("Failed to share new turms properties", e)) .then(Mono.defer(() -> { sharedClusterProperties = clusterProperties; - log.info("Turms properties have been shared"); + LOGGER.info("Turms properties have been shared"); return Mono.empty(); })); } @@ -148,13 +150,13 @@ private void notifyListeners(TurmsProperties properties) { try { listener.accept(properties); } catch (Exception e) { - log.error("The properties listener {} failed to handle the new properties", listener.getClass().getName(), e); + LOGGER.error("The properties listener {} failed to handle the new properties", listener.getClass().getName(), e); } } } private Mono initializeSharedProperties() { - log.info("Trying to get shared properties"); + LOGGER.info("Trying to get shared properties"); TurmsProperties localProperties = turmsPropertiesManager.getLocalProperties(); SharedClusterProperties clusterProperties = new SharedClusterProperties(clusterId, localProperties, new Date()); if (nodeType == NodeType.GATEWAY) { @@ -167,7 +169,7 @@ private Mono initializeSharedProperties() { .onErrorResume(DuplicateKeyException.class, e -> findAndUpdatePropertiesByNodeType(clusterProperties)) .doOnSuccess(properties -> { sharedClusterProperties = properties; - log.info("Shared properties were retrieved successfully"); + LOGGER.info("Shared properties were retrieved successfully"); }); } diff --git a/turms-server-common/src/main/java/im/turms/server/common/cluster/service/config/domain/property/CommonProperties.java b/turms-server-common/src/main/java/im/turms/server/common/cluster/service/config/domain/property/CommonProperties.java index 3d51e41c1d..931a374a2f 100644 --- a/turms-server-common/src/main/java/im/turms/server/common/cluster/service/config/domain/property/CommonProperties.java +++ b/turms-server-common/src/main/java/im/turms/server/common/cluster/service/config/domain/property/CommonProperties.java @@ -18,7 +18,7 @@ package im.turms.server.common.cluster.service.config.domain.property; import im.turms.server.common.property.env.common.IpProperties; -import im.turms.server.common.property.env.common.LoggingProperties; +import im.turms.server.common.property.env.common.logging.LoggingProperties; import im.turms.server.common.property.env.common.PluginProperties; import im.turms.server.common.property.env.common.UserStatusProperties; import im.turms.server.common.property.env.common.cluster.ClusterProperties; diff --git a/turms-server-common/src/main/java/im/turms/server/common/cluster/service/connection/ConnectionServer.java b/turms-server-common/src/main/java/im/turms/server/common/cluster/service/connection/ConnectionServer.java index 27b55cefd7..9388e64b49 100644 --- a/turms-server-common/src/main/java/im/turms/server/common/cluster/service/connection/ConnectionServer.java +++ b/turms-server-common/src/main/java/im/turms/server/common/cluster/service/connection/ConnectionServer.java @@ -18,9 +18,10 @@ package im.turms.server.common.cluster.service.connection; import im.turms.server.common.access.common.resource.LoopResourcesFactory; +import im.turms.server.common.logging.core.logger.LoggerFactory; +import im.turms.server.common.logging.core.logger.Logger; import im.turms.server.common.util.SslUtil; import lombok.Getter; -import lombok.extern.log4j.Log4j2; import org.springframework.boot.web.server.Ssl; import reactor.netty.ChannelBindException; import reactor.netty.DisposableServer; @@ -36,9 +37,10 @@ /** * @author James Chen */ -@Log4j2 public class ConnectionServer { + private static final Logger LOGGER = LoggerFactory.getLogger(ConnectionServer.class); + private final String host; private final int proposedPort; private final boolean portAutoIncrement; @@ -83,14 +85,14 @@ public synchronized void blockUntilConnect() { tcpServer.secure(spec -> SslUtil.configureSslContextSpec(spec, ssl, true)); } server = tcpServer.bindNow(Duration.ofSeconds(60)); - log.info("The local server {}:{} has been set up", host, currentPort); + LOGGER.info("The local server {}:{} has been set up", host, currentPort); break; } catch (Exception e) { // e.g. port in use if (e instanceof ChannelBindException && portAutoIncrement && currentPort <= proposedPort + portCount) { - log.warn("Failed to bind on the port {}. Trying to bind on the next port {}", currentPort++, currentPort); + LOGGER.warn("Failed to bind on the port {}. Trying to bind on the next port {}", currentPort++, currentPort); } else { - log.error("Failed to set up the local discovery server", e); + LOGGER.error("Failed to set up the local discovery server", e); throw e; } } diff --git a/turms-server-common/src/main/java/im/turms/server/common/cluster/service/connection/ConnectionService.java b/turms-server-common/src/main/java/im/turms/server/common/cluster/service/connection/ConnectionService.java index 24a5399b56..54ce8ff43f 100644 --- a/turms-server-common/src/main/java/im/turms/server/common/cluster/service/connection/ConnectionService.java +++ b/turms-server-common/src/main/java/im/turms/server/common/cluster/service/connection/ConnectionService.java @@ -28,6 +28,9 @@ import im.turms.server.common.cluster.service.discovery.MemberConnectionListener; import im.turms.server.common.cluster.service.idgen.IdService; import im.turms.server.common.cluster.service.rpc.RpcService; +import im.turms.server.common.logging.core.logger.LoggerFactory; +import im.turms.server.common.logging.core.model.LogLevel; +import im.turms.server.common.logging.core.logger.Logger; import im.turms.server.common.property.env.common.cluster.connection.ConnectionClientProperties; import im.turms.server.common.property.env.common.cluster.connection.ConnectionProperties; import im.turms.server.common.property.env.common.cluster.connection.ConnectionServerProperties; @@ -36,8 +39,6 @@ import io.netty.channel.nio.NioEventLoopGroup; import io.netty.util.concurrent.DefaultThreadFactory; import lombok.Getter; -import lombok.extern.log4j.Log4j2; -import org.apache.logging.log4j.Level; import org.springframework.boot.web.server.Ssl; import reactor.core.publisher.Mono; import reactor.netty.Connection; @@ -84,9 +85,10 @@ * 2. It's more nature for users to call "rpcService.requestResponse()" instead of "connectionService.requestResponse()", * which makes it too general in functionality */ -@Log4j2 public class ConnectionService implements ClusterService { + private static final Logger LOGGER = LoggerFactory.getLogger(ConnectionService.class); + private final Ssl clientSsl; private final long keepaliveIntervalMillis; private final long keepaliveTimeoutMillis; @@ -135,7 +137,7 @@ public ConnectionService(ConnectionProperties connectionProperties) { keepaliveThread = new DefaultThreadFactory("turms-cluster-connection-keepalive", true) .newThread(() -> { sendKeepaliveToConnectionsForever(); - log.warn("Keepalive thread has been stopped"); + LOGGER.warn("Keepalive thread has been stopped"); }); keepaliveThread.start(); server = setupServer(); @@ -148,7 +150,7 @@ public void stop() { try { server.dispose(); } catch (Exception e) { - log.error("Failed to stop the local server", e); + LOGGER.error("Failed to stop the local server", e); } } ClosingHandshakeRequest request = new ClosingHandshakeRequest(CLOSE_STATUS_CODE_SERVER_SHUTTING_DOWN); @@ -164,7 +166,7 @@ public void stop() { } else { rpcService.requestResponse(nodeId, request) .onErrorResume(t -> { - log.error("Failed to send a closing handshake request", t); + LOGGER.error("Failed to send a closing handshake request", t); return Mono.empty(); }) .doOnTerminate(conn::dispose) @@ -247,7 +249,7 @@ public void connectMemberUntilSucceedOrRemoved(Member member) { private void connectMemberUntilSucceedOrRemoved0(Member member) { String nodeId = member.getNodeId(); - log.info("[Client] Connecting to member: {}[{}:{}]. Retry times: {}", + LOGGER.info("[Client] Connecting to member: {}[{}:{}]. Retry times: {}", nodeId, member.getMemberHost(), member.getMemberPort(), @@ -258,12 +260,12 @@ private void connectMemberUntilSucceedOrRemoved0(Member member) { new TurmsConnection(nodeId, (ChannelOperations) conn, true, newMemberConnectionListeners()); onMemberConnectionAdded(member, connection); String localNodeId = discoveryService.getLocalMember().getNodeId(); - log.info("[Client] Sending a open handshake request to member: {}[{}:{}]", + LOGGER.info("[Client] Sending a open handshake request to member: {}[{}:{}]", nodeId, member.getMemberHost(), member.getMemberPort()); rpcService.requestResponse(nodeId, new OpeningHandshakeRequest(localNodeId), null, connection) .doOnSuccess(unused -> onMemberConnectionHandshakeCompleted(member, connection, true)) .onErrorResume(throwable -> { - log.error("[Client] Failed to complete handshake with member: {}[{}:{}]. Closing connection to reconnect", + LOGGER.error("[Client] Failed to complete handshake with member: {}[{}:{}]. Closing connection to reconnect", nodeId, member.getMemberHost(), member.getMemberPort(), throwable); // To keep logic simple, just disconnect to // connect and start a handshake again. @@ -278,7 +280,7 @@ private void connectMemberUntilSucceedOrRemoved0(Member member) { return Mono.empty(); } int retryTimes = connectionRetryTimesMap.getOrDefault(nodeId, 0); - log.error("[Client] Failed to connect to member: {}[{}:{}]. Retry times: {}", + LOGGER.error("[Client] Failed to connect to member: {}[{}:{}]. Retry times: {}", nodeId, member.getMemberHost(), member.getMemberPort(), retryTimes, throwable); retryTimes++; connectionRetryTimesMap.put(nodeId, retryTimes); @@ -313,7 +315,7 @@ private void sendKeepaliveToConnectionsForever() { try { sendKeepalive(iterator); } catch (Exception e) { - log.error("Caught an exception when sending keepalive", e); + LOGGER.error("Caught an exception when sending keepalive", e); } } try { @@ -339,7 +341,7 @@ private void sendKeepalive(Iterator> iterator long now = System.currentTimeMillis(); long elapsedTime = now - connection.getLastKeepaliveTimestamp(); if (elapsedTime > keepaliveTimeoutMillis) { - log.warn("Reconnecting to the member {} due to keepalive timeout", nodeId); + LOGGER.warn("Reconnecting to the member {} due to keepalive timeout", nodeId); // onConnectionClosed() will reconnect the member disconnectConnection(connection); iterator.remove(); @@ -352,12 +354,12 @@ private void sendKeepalive(Iterator> iterator rpcService.requestResponse(nodeId, new KeepaliveRequest()) .doOnSuccess(unused -> connection.setLastKeepaliveTimestamp(System.currentTimeMillis())) .onErrorResume(t -> { - log.warn("Failed to send a keepalive request to the member " + nodeId, t); + LOGGER.warn("Failed to send a keepalive request to the member " + nodeId, t); return Mono.empty(); }) .subscribe(); } catch (Exception e) { - log.error("Failed to send a keepalive request to the member " + nodeId, e); + LOGGER.error("Failed to send a keepalive request to the member " + nodeId, e); } } @@ -410,14 +412,14 @@ private List newMemberConnectionListeners() { private void onMemberConnectionAdded(@Nullable Member member, TurmsConnection connection) { String endpointType = connection.isLocalNodeClient() ? "Client" : "Server"; String memberIdAndAddress = getMemberIdAndAddress(member); - log.info("[{}] Connected to member{}", + LOGGER.info("[{}] Connected to member{}", endpointType, member == null ? "" : ": " + memberIdAndAddress); for (MemberConnectionListener listener : connection.getListeners()) { try { listener.onConnectionOpen(connection); } catch (Exception e) { - log.error("Caught an error when invoking onConnectionOpen listeners", e); + LOGGER.error("Caught an error when invoking onConnectionOpen listeners", e); } } ChannelOperations conn = connection.getConnection(); @@ -427,7 +429,7 @@ private void onMemberConnectionAdded(@Nullable Member member, TurmsConnection co try { listener.onDataReceived(value); } catch (Exception e) { - log.error("Caught an error when invoking onDataReceived listeners", e); + LOGGER.error("Caught an error when invoking onDataReceived listeners", e); } } }) @@ -435,7 +437,7 @@ private void onMemberConnectionAdded(@Nullable Member member, TurmsConnection co if (ExceptionUtil.isDisconnectedClientError(t) && connection.isClosing()) { return Mono.empty(); } - log.error("[{}] Failed to listen to the connection to the member{}", + LOGGER.error("[{}] Failed to listen to the connection to the member{}", endpointType, member == null ? "" : ": " + memberIdAndAddress, t); @@ -457,8 +459,8 @@ private void onConnectionClosed(TurmsConnection connection, @Nullable Throwable String nodeId = connection.getNodeId(); Member member = discoveryService.getMember(nodeId); String memberIdAndAddress = member == null ? "" : ": " + getMemberIdAndAddress(member); - Level logLevel = connection.isClosing() ? Level.INFO : Level.WARN; - log.log(logLevel, "[{}] The connection to the member{} has been closed{}", + LogLevel logLevel = connection.isClosing() ? LogLevel.INFO : LogLevel.WARN; + LOGGER.log(logLevel, "[{}] The connection to the member{} has been closed{}", nodeType, memberIdAndAddress, connection.isClosing() ? "" : " unexpectedly", @@ -467,7 +469,7 @@ private void onConnectionClosed(TurmsConnection connection, @Nullable Throwable try { listener.onConnectionClosed(); } catch (Exception e) { - log.error("Caught an error when invoking onConnectionClosed listeners", e); + LOGGER.error("Caught an error when invoking onConnectionClosed listeners", e); } } boolean isKnownMember = discoveryService.isKnownMember(nodeId); @@ -486,14 +488,14 @@ private void onConnectionClosed(TurmsConnection connection, @Nullable Throwable : !isKnownMember ? "the member is unknown" : "the local node is closing"; - log.info("[{}] Stop to connect the member{} because {}", + LOGGER.info("[{}] Stop to connect the member{} because {}", nodeType, memberIdAndAddress, reason); } } private void onMemberConnectionHandshakeCompleted(Member member, TurmsConnection connection, boolean isLocalNodeClient) { String nodeId = member.getNodeId(); - log.info("[{}] Completed the handshake with member: {}[{}:{}]", + LOGGER.info("[{}] Completed the handshake with member: {}[{}:{}]", isLocalNodeClient ? "Client" : "Server", nodeId, member.getMemberHost(), @@ -506,7 +508,7 @@ private void onMemberConnectionHandshakeCompleted(Member member, TurmsConnection try { listener.onOpeningHandshakeCompleted(member); } catch (Exception e) { - log.error("Caught an error when invoking onOpeningHandshakeCompleted listeners", e); + LOGGER.error("Caught an error when invoking onOpeningHandshakeCompleted listeners", e); } } } diff --git a/turms-server-common/src/main/java/im/turms/server/common/cluster/service/discovery/DiscoveryService.java b/turms-server-common/src/main/java/im/turms/server/common/cluster/service/discovery/DiscoveryService.java index 5eb96cfb92..7321137ffc 100644 --- a/turms-server-common/src/main/java/im/turms/server/common/cluster/service/discovery/DiscoveryService.java +++ b/turms-server-common/src/main/java/im/turms/server/common/cluster/service/discovery/DiscoveryService.java @@ -33,13 +33,14 @@ import im.turms.server.common.cluster.service.rpc.RpcService; import im.turms.server.common.constant.TurmsStatusCode; import im.turms.server.common.exception.TurmsBusinessException; +import im.turms.server.common.logging.core.logger.LoggerFactory; +import im.turms.server.common.logging.core.logger.Logger; import im.turms.server.common.mongo.operation.option.Filter; import im.turms.server.common.mongo.operation.option.Update; import im.turms.server.common.property.env.common.cluster.DiscoveryProperties; import im.turms.server.common.util.CollectorUtil; import io.netty.util.concurrent.DefaultThreadFactory; import lombok.Getter; -import lombok.extern.log4j.Log4j2; import org.apache.commons.collections4.ListUtils; import org.bson.BsonValue; import reactor.core.publisher.Flux; @@ -72,9 +73,10 @@ * * @author James Chen */ -@Log4j2 public class DiscoveryService implements ClusterService { + private static final Logger LOGGER = LoggerFactory.getLogger(DiscoveryService.class); + private static final Duration CRUD_TIMEOUT_DURATION = Duration.ofSeconds(10); private static final Comparator MEMBER_PRIORITY_COMPARATOR = DiscoveryService::compareMemberPriority; @@ -274,7 +276,7 @@ private void listenLeadershipChangeEvent() { } } }) - .onErrorContinue((throwable, o) -> log.error("Error while processing the change stream event of Leader: {}", o, throwable)) + .onErrorContinue((throwable, o) -> LOGGER.error("Error while processing the change stream event of Leader: {}", o, throwable)) .subscribe(); } @@ -318,7 +320,7 @@ private void listenMembersChangeEvent() { updateActiveMembers(allKnownMembers.values()); connectionService.updateHasConnectedToAllMembers(allKnownMembers.keySet()); }) - .onErrorContinue((throwable, o) -> log.error("Error while processing the change stream event of Member: {}", o, throwable)) + .onErrorContinue((throwable, o) -> LOGGER.error("Error while processing the change stream event of Member: {}", o, throwable)) .subscribe(); } diff --git a/turms-server-common/src/main/java/im/turms/server/common/cluster/service/discovery/LocalNodeStatusManager.java b/turms-server-common/src/main/java/im/turms/server/common/cluster/service/discovery/LocalNodeStatusManager.java index 69b2630f50..972530e5b1 100644 --- a/turms-server-common/src/main/java/im/turms/server/common/cluster/service/discovery/LocalNodeStatusManager.java +++ b/turms-server-common/src/main/java/im/turms/server/common/cluster/service/discovery/LocalNodeStatusManager.java @@ -20,13 +20,14 @@ import im.turms.server.common.cluster.service.config.SharedConfigService; import im.turms.server.common.cluster.service.config.domain.discovery.Leader; import im.turms.server.common.cluster.service.config.domain.discovery.Member; +import im.turms.server.common.logging.core.logger.LoggerFactory; +import im.turms.server.common.logging.core.logger.Logger; import im.turms.server.common.mongo.exception.DuplicateKeyException; import im.turms.server.common.mongo.operation.option.Filter; import im.turms.server.common.mongo.operation.option.Update; import io.netty.util.concurrent.DefaultThreadFactory; import lombok.Getter; import lombok.Setter; -import lombok.extern.log4j.Log4j2; import org.eclipse.collections.impl.set.mutable.UnifiedSet; import reactor.core.publisher.Mono; @@ -46,9 +47,10 @@ /** * @author James Chen */ -@Log4j2 public class LocalNodeStatusManager { + private static final Logger LOGGER = LoggerFactory.getLogger(LocalNodeStatusManager.class); + private final DiscoveryService discoveryService; private final SharedConfigService sharedConfigService; @Getter @@ -92,31 +94,31 @@ public Mono upsertLocalNodeInfo(Update update) { } public Mono registerLocalMember(boolean suppressDuplicateMemberError) { - log.info("Registering the local member"); + LOGGER.info("Registering the local member"); return discoveryService.registerMember(localMember) .doOnSuccess(ignored -> { isLocalNodeRegistered = true; - log.info("Registered the local member successfully"); + LOGGER.info("Registered the local member successfully"); }) .onErrorResume(t -> { if (suppressDuplicateMemberError && t instanceof DuplicateKeyException) { - log.info("Cancelled the local member registration because it has been registered"); + LOGGER.info("Cancelled the local member registration because it has been registered"); return Mono.empty(); } else { - log.error("Failed to register the local member", t); + LOGGER.error("Failed to register the local member", t); return Mono.error(t); } }); } public Mono unregisterLocalMember() { - log.info("Unregistering the local member"); + LOGGER.info("Unregistering the local member"); return discoveryService.unregisterMembers(Set.of(localMember.getNodeId())) .then(unregisterLocalMemberLeadership()) - .doOnError(e -> log.error("Failed to unregister the local member", e)) + .doOnError(e -> LOGGER.error("Failed to unregister the local member", e)) .doOnSuccess(ignored -> { isLocalNodeRegistered = false; - log.info("Unregistered the local member successfully"); + LOGGER.info("Unregistered the local member successfully"); }); } @@ -172,12 +174,12 @@ public void startHeartbeat() { .timeout(heartbeatInterval) .doOnSuccess(ignored -> localMember.getStatus().setLastHeartbeatDate(now)) .onErrorResume(e -> { - log.error("Failed to send heartbeat request", e); + LOGGER.error("Failed to send heartbeat request", e); return Mono.empty(); }) .subscribe(); } catch (Exception e) { - log.error("Failed to send heartbeat request", e); + LOGGER.error("Failed to send heartbeat request", e); } }, heartbeatIntervalMillis, heartbeatIntervalMillis, TimeUnit.MILLISECONDS); } diff --git a/turms-server-common/src/main/java/im/turms/server/common/cluster/service/idgen/IdService.java b/turms-server-common/src/main/java/im/turms/server/common/cluster/service/idgen/IdService.java index 5ccb4afd95..c95ed2cc67 100644 --- a/turms-server-common/src/main/java/im/turms/server/common/cluster/service/idgen/IdService.java +++ b/turms-server-common/src/main/java/im/turms/server/common/cluster/service/idgen/IdService.java @@ -20,16 +20,18 @@ import im.turms.server.common.cluster.service.ClusterService; import im.turms.server.common.cluster.service.config.domain.discovery.Member; import im.turms.server.common.cluster.service.discovery.DiscoveryService; -import lombok.extern.log4j.Log4j2; +import im.turms.server.common.logging.core.logger.LoggerFactory; +import im.turms.server.common.logging.core.logger.Logger; import java.util.TreeSet; /** * @author James Chen */ -@Log4j2 public class IdService implements ClusterService { + private static final Logger LOGGER = LoggerFactory.getLogger(IdService.class); + private static final int FLAKE_ID_GENERATORS_LENGTH = ServiceType.values().length; private final DiscoveryService discoveryService; @@ -80,7 +82,7 @@ private int findNewDataCenterId() { .size(); if (dataCenterId >= SnowflakeIdGenerator.MAX_DATA_CENTER_ID) { int fallbackDataCenterId = dataCenterId % SnowflakeIdGenerator.MAX_DATA_CENTER_ID; - log.warn("The data center ID {} is larger than {}, so the ID falls back to {}." + + LOGGER.warn("The data center ID {} is larger than {}, so the ID falls back to {}." + " It runs the risk of generating same IDs in the cluster", dataCenterId, SnowflakeIdGenerator.MAX_DATA_CENTER_ID - 1, fallbackDataCenterId); dataCenterId = fallbackDataCenterId; @@ -97,7 +99,7 @@ private int findNewWorkerId() { } if (localWorkerId >= SnowflakeIdGenerator.MAX_WORKER_ID) { int fallbackWorkerId = localWorkerId % SnowflakeIdGenerator.MAX_WORKER_ID; - log.warn("The node ID {} is larger than {}, so the ID falls back to {}." + + LOGGER.warn("The node ID {} is larger than {}, so the ID falls back to {}." + " It runs the risk of generating same IDs in the cluster", localWorkerId, SnowflakeIdGenerator.MAX_WORKER_ID - 1, fallbackWorkerId); return fallbackWorkerId; diff --git a/turms-server-common/src/main/java/im/turms/server/common/cluster/service/idgen/SnowflakeIdGenerator.java b/turms-server-common/src/main/java/im/turms/server/common/cluster/service/idgen/SnowflakeIdGenerator.java index ab2faf9c5a..5b93553333 100644 --- a/turms-server-common/src/main/java/im/turms/server/common/cluster/service/idgen/SnowflakeIdGenerator.java +++ b/turms-server-common/src/main/java/im/turms/server/common/cluster/service/idgen/SnowflakeIdGenerator.java @@ -19,7 +19,6 @@ import im.turms.common.util.RandomUtil; -import lombok.extern.log4j.Log4j2; import java.util.concurrent.atomic.AtomicLong; @@ -47,7 +46,6 @@ * * @author James Chen */ -@Log4j2 public class SnowflakeIdGenerator { /** diff --git a/turms-server-common/src/main/java/im/turms/server/common/cluster/service/rpc/RpcEndpoint.java b/turms-server-common/src/main/java/im/turms/server/common/cluster/service/rpc/RpcEndpoint.java index 0aefba067b..fd2e8d4bb2 100644 --- a/turms-server-common/src/main/java/im/turms/server/common/cluster/service/rpc/RpcEndpoint.java +++ b/turms-server-common/src/main/java/im/turms/server/common/cluster/service/rpc/RpcEndpoint.java @@ -21,11 +21,12 @@ import im.turms.server.common.cluster.service.rpc.codec.RpcFrameEncoder; import im.turms.server.common.cluster.service.rpc.dto.RpcRequest; import im.turms.server.common.cluster.service.rpc.dto.RpcResponse; +import im.turms.server.common.logging.core.logger.LoggerFactory; +import im.turms.server.common.logging.core.logger.Logger; import im.turms.server.common.util.MapUtil; import io.netty.buffer.ByteBuf; import io.netty.util.IllegalReferenceCountException; import lombok.Getter; -import lombok.extern.log4j.Log4j2; import reactor.core.publisher.Mono; import reactor.core.publisher.Sinks; import reactor.netty.channel.ChannelOperations; @@ -38,9 +39,10 @@ /** * @author James Chen */ -@Log4j2 public final class RpcEndpoint { + private static final Logger LOGGER = LoggerFactory.getLogger(RpcEndpoint.class); + private static final int EXPECTED_MAX_QPS = 1000; private static final int EXPECTED_AVERAGE_RTT = 10; private static final int INITIAL_CAPACITY_PERCENTAGE = 10; @@ -120,7 +122,7 @@ public void handleResponse(RpcResponse response) { private void resolveRequest(int requestId, T response, Throwable error) { Sinks.One sink = (Sinks.One) pendingRequestMap.remove(requestId); if (sink == null) { - log.warn("No sink of the request with ID {} is found for the response: " + response, requestId); + LOGGER.warn("No sink of the request with ID {} is found for the response: " + response, requestId); return; } if (error == null) { diff --git a/turms-server-common/src/main/java/im/turms/server/common/cluster/service/rpc/RpcRequestExecutor.java b/turms-server-common/src/main/java/im/turms/server/common/cluster/service/rpc/RpcRequestExecutor.java index b4654ca250..5fd4eebe9c 100644 --- a/turms-server-common/src/main/java/im/turms/server/common/cluster/service/rpc/RpcRequestExecutor.java +++ b/turms-server-common/src/main/java/im/turms/server/common/cluster/service/rpc/RpcRequestExecutor.java @@ -23,9 +23,7 @@ import im.turms.server.common.constant.TurmsStatusCode; import im.turms.server.common.exception.TurmsBusinessException; import im.turms.server.common.lang.Null; -import im.turms.server.common.logging.RequestLoggingContext; import im.turms.server.common.tracing.TracingContext; -import lombok.extern.log4j.Log4j2; import org.springframework.context.ApplicationContext; import reactor.core.publisher.Mono; @@ -34,7 +32,6 @@ /** * @author James Chen */ -@Log4j2 public class RpcRequestExecutor { private final ApplicationContext context; @@ -47,15 +44,14 @@ public RpcRequestExecutor(ApplicationContext context) { * @implNote 1. We record request time/response here because the RPC request may run on the local machine * 2.The method itself will call {@link RpcRequest#release()} */ - public Mono runRpcRequest(RequestLoggingContext loggingContext, + public Mono runRpcRequest(TracingContext tracingContext, RpcRequest rpcRequest, @Nullable TurmsConnection connection, String fromNodeId) { rpcRequest.touch(rpcRequest); - TracingContext tracingContext = loggingContext.getTracingContext(); try { - tracingContext.updateMdc(); - rpcRequest.init(context, connection, fromNodeId); + tracingContext.updateThreadContext(); + rpcRequest.init(this.context, connection, fromNodeId); Mono result; // It's the responsibility of the implementations of call() or callAsync() // to release by 1 if the request has a bound buffer @@ -75,7 +71,7 @@ public Mono runRpcRequest(RequestLoggingContext loggingContext, .onErrorMap(e -> e instanceof RpcException ? e : RpcException.get(RpcErrorCode.FAILED_TO_RUN_RPC, TurmsStatusCode.SERVER_INTERNAL_ERROR, e.toString(), e)) - .doFinally(signalType -> tracingContext.clearMdc()); + .doFinally(signalType -> tracingContext.clearThreadContext()); } catch (RpcException e) { rpcRequest.release(); return Mono.error(e); @@ -86,9 +82,7 @@ public Mono runRpcRequest(RequestLoggingContext loggingContext, rpcRequest.release(); return Mono.error(RpcException.get(RpcErrorCode.FAILED_TO_RUN_RPC, TurmsStatusCode.SERVER_INTERNAL_ERROR, e.toString(), e)); } finally { - if (tracingContext != null) { - tracingContext.clearMdc(); - } + tracingContext.clearThreadContext(); } } diff --git a/turms-server-common/src/main/java/im/turms/server/common/cluster/service/rpc/RpcService.java b/turms-server-common/src/main/java/im/turms/server/common/cluster/service/rpc/RpcService.java index 637c874b3d..c4107f9883 100644 --- a/turms-server-common/src/main/java/im/turms/server/common/cluster/service/rpc/RpcService.java +++ b/turms-server-common/src/main/java/im/turms/server/common/cluster/service/rpc/RpcService.java @@ -34,7 +34,8 @@ import im.turms.server.common.cluster.service.rpc.dto.RpcResponse; import im.turms.server.common.cluster.service.rpc.exception.RpcException; import im.turms.server.common.constant.TurmsStatusCode; -import im.turms.server.common.logging.RequestLoggingContext; +import im.turms.server.common.logging.core.logger.Logger; +import im.turms.server.common.logging.core.logger.LoggerFactory; import im.turms.server.common.property.env.common.cluster.RpcProperties; import im.turms.server.common.tracing.TracingCloseableContext; import im.turms.server.common.tracing.TracingContext; @@ -44,7 +45,6 @@ import io.micrometer.core.instrument.Tag; import io.netty.buffer.ByteBuf; import lombok.Getter; -import lombok.extern.log4j.Log4j2; import org.springframework.context.ApplicationContext; import org.springframework.data.util.Pair; import reactor.core.publisher.Flux; @@ -63,9 +63,10 @@ /** * @author James Chen */ -@Log4j2 public class RpcService implements ClusterService { + private static final Logger LOGGER = LoggerFactory.getLogger(RpcService.class); + private static final String METRICS_NAME_RPC_REQUEST = "rpc.request"; private static final String METRICS_TAG_REQUEST_NAME = "name"; private static final String METRICS_TAG_REQUEST_TARGET_NODE_ID = "node"; @@ -135,7 +136,7 @@ public void onDataReceived(Object data) { } else if (data instanceof RpcResponse response) { onResponseReceived(response); } else { - log.error("Receive an unknown data: " + data); + LOGGER.error("Received an unknown data: " + data); } } @@ -143,43 +144,44 @@ public void onRequestReceived(RpcRequest request) { // Retain to avoid being released by FluxReceive request.retain(); ChannelOperations conn = connection.getConnection(); - RequestLoggingContext loggingContext = new RequestLoggingContext(request.getTracingContext()); - requestExecutor.runRpcRequest(loggingContext, request, connection, connection.getNodeId()) + TracingContext ctx = request.getTracingContext(); + requestExecutor.runRpcRequest(ctx, request, connection, connection.getNodeId()) .cast(Object.class) .onErrorResume(RpcException.class, Mono::just) .doOnNext(response -> { if (conn.isDisposed()) { - try (TracingCloseableContext ignored = loggingContext.asCloseable()) { - log.error("Cannot send response to disposed connection: " + response); + try (TracingCloseableContext ignored = ctx.asCloseable()) { + LOGGER.error("Cannot send response to disposed connection: " + response); } return; } ByteBuf buf; try { buf = RpcFrameEncoder.INSTANCE.encode(request.getRequestId(), response); + buf.touch(response); } catch (Exception e) { - try (TracingCloseableContext ignored = loggingContext.asCloseable()) { - log.error("Failed to encode response: {}", response, e); + try (TracingCloseableContext ignored = ctx.asCloseable()) { + LOGGER.error("Failed to encode response: {}", response, e); } return; } if (buf.refCnt() == 0) { - try (TracingCloseableContext ignored = loggingContext.asCloseable()) { - log.error("The buffer of response is released unexpectedly: " + response); + try (TracingCloseableContext ignored = ctx.asCloseable()) { + LOGGER.error("The buffer of response is released unexpectedly: " + response); } return; } conn.sendObject(buf) .then() .onErrorResume(t -> { - try (TracingCloseableContext ignored = loggingContext.asCloseable()) { - log.error("Failed to send response", t); + try (TracingCloseableContext ignored = ctx.asCloseable()) { + LOGGER.error("Failed to send response", t); } return Mono.empty(); }) .subscribe(); }) - .contextWrite(context -> context.put(RequestLoggingContext.CTX_KEY_NAME, loggingContext)) + .contextWrite(context -> context.put(TracingContext.CTX_KEY_NAME, ctx)) .subscribe(); } @@ -305,7 +307,7 @@ public Mono requestResponse(String memberNodeId, RpcRequest request, D public Mono requestResponse(String memberNodeId, RpcRequest request, Duration timeout, @Nullable TurmsConnection connection) { try { if (discoveryService.getLocalNodeStatusManager().isLocalNodeId(memberNodeId)) { - return requestExecutor.runRpcRequest(new RequestLoggingContext(request.getTracingContext()), + return requestExecutor.runRpcRequest(new TracingContext(request.getTracingContext()), request, null, memberNodeId); @@ -517,17 +519,17 @@ private Mono> requestResponsesAsMap(List members, } private void addTraceIdToRequestFromContext(ContextView contextView, RpcRequest request) { - if (request.getTracingContext().hasTraceId()) { + long traceId = request.getTracingContext().getTraceId(); + if (traceId != TracingContext.UNDEFINED_TRACE_ID) { return; } - Long traceId = RequestLoggingContext.readTraceIdFromContext(contextView); - if (traceId == null) { + traceId = TracingContext.readTraceIdFromContext(contextView); + if (traceId == TracingContext.UNDEFINED_TRACE_ID) { traceId = RandomUtil.nextPositiveLong(); } request.setTracingContext(new TracingContext(traceId)); } - private Throwable mapThrowable(Throwable throwable, RpcRequest callable) { // e.g. ClosedChannelException return new IllegalStateException("Failed to request a response for the request: " + callable, throwable); diff --git a/turms-server-common/src/main/java/im/turms/server/common/context/ApplicationEnvironmentEventListener.java b/turms-server-common/src/main/java/im/turms/server/common/context/ApplicationEnvironmentEventListener.java index 13912e248e..90cd428496 100644 --- a/turms-server-common/src/main/java/im/turms/server/common/context/ApplicationEnvironmentEventListener.java +++ b/turms-server-common/src/main/java/im/turms/server/common/context/ApplicationEnvironmentEventListener.java @@ -19,19 +19,18 @@ import im.turms.server.common.cluster.node.Node; import im.turms.server.common.cluster.node.NodeType; -import im.turms.server.common.log4j.plugin.TurmsContextLookup; -import im.turms.server.common.property.env.common.LoggingProperties; -import org.apache.logging.log4j.core.async.AsyncLoggerContext; -import org.apache.logging.log4j.core.config.Configuration; -import org.apache.logging.log4j.core.config.LoggerConfig; -import org.apache.logging.log4j.core.config.Order; +import im.turms.server.common.logging.core.logger.LoggerFactory; +import im.turms.server.common.logging.core.model.LogLevel; +import im.turms.server.common.property.env.common.logging.ConsoleLoggingProperties; +import im.turms.server.common.property.env.common.logging.FileLoggingProperties; +import im.turms.server.common.property.env.common.logging.LoggingProperties; +import io.netty.buffer.PooledByteBufAllocator; import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent; import org.springframework.boot.context.logging.LoggingApplicationListener; import org.springframework.context.ApplicationListener; +import org.springframework.core.annotation.Order; import org.springframework.core.env.ConfigurableEnvironment; -import static org.apache.logging.log4j.LogManager.getContext; - /** * @author James Chen */ @@ -43,49 +42,53 @@ public class ApplicationEnvironmentEventListener implements ApplicationListener< */ @Override public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) { - configContextForLogging(event); + configureContextForLogging(event); } - private void configContextForLogging(ApplicationEnvironmentPreparedEvent event) { + private void configureContextForLogging(ApplicationEnvironmentPreparedEvent event) { ConfigurableEnvironment env = event.getEnvironment(); - // Though it's more reasonable to init the node type/ID in im.turms.server.common.cluster.node.Node, // we need to ensure the local node info is logged even if the local node hasn't been inited. // So we initialize the node info here String applicationClassName = event.getSpringApplication().getMainApplicationClass().getSimpleName(); - TurmsContextLookup.setNodeType(applicationClassName.equals("TurmsGatewayApplication") + NodeType nodeType = applicationClassName.equals("TurmsGatewayApplication") ? NodeType.GATEWAY - : NodeType.SERVICE); + : NodeType.SERVICE; Node.initNodeId(env.getProperty("turms.cluster.node.id", String.class)); - boolean enableConsoleAppender = env.getProperty("turms.logging.enable-console-appender", - Boolean.class, - LoggingProperties.ENABLE_CONSOLE_APPENDER_DEFAULT_VALUE); - boolean enableFileAppender = env.getProperty("turms.logging.enable-file-appender", - Boolean.class, - LoggingProperties.ENABLE_FILE_APPENDER_DEFAULT_VALUE); + PooledByteBufAllocator.DEFAULT.metric().usedDirectMemory(); - AsyncLoggerContext context = (AsyncLoggerContext) getContext(false); - Configuration config = context.getConfiguration(); - LoggerConfig loggerConfig = config.getRootLogger(); - - if (!enableConsoleAppender || !enableFileAppender) { - if (!enableConsoleAppender) { - removeAppender(loggerConfig, "Console"); - } - if (!enableFileAppender) { - removeAppender(loggerConfig, "Routing"); - } - context.updateLoggers(); - } - } + ConsoleLoggingProperties consoleLoggingProperties = ConsoleLoggingProperties.builder() + .enabled(env.getProperty("turms.logging.console.enabled", + Boolean.class, + ConsoleLoggingProperties.DEFAULT_VALUE_ENABLED)) + .level(env.getProperty("turms.logging.console.level", + LogLevel.class, + ConsoleLoggingProperties.DEFAULT_VALUE_LEVEL)) + .build(); + FileLoggingProperties fileLoggingProperties = FileLoggingProperties.builder() + .enabled(env.getProperty("turms.logging.file.enabled", + Boolean.class, + FileLoggingProperties.DEFAULT_VALUE_ENABLED)) + .level(env.getProperty("turms.logging.file.level", + LogLevel.class, + FileLoggingProperties.DEFAULT_VALUE_LEVEL)) + .filePath(env.getProperty("turms.logging.file.filePath", + String.class, + FileLoggingProperties.DEFAULT_VALUE_FILE_PATH)) + .maxFiles(env.getProperty("turms.logging.file.maxFiles", + Integer.class, + FileLoggingProperties.DEFAULT_VALUE_MAX_FILES)) + .maxFileSizeMb(env.getProperty("turms.logging.file.maxFileSizeMb", + Integer.class, + FileLoggingProperties.DEFAULT_VALUE_FILE_SIZE_MB)) + .build(); + LoggingProperties loggingProperties = new LoggingProperties().toBuilder() + .console(consoleLoggingProperties) + .file(fileLoggingProperties) + .build(); - private void removeAppender(LoggerConfig loggerConfig, String appenderName) { - if (loggerConfig.getAppenders().containsKey(appenderName)) { - loggerConfig.removeAppender(appenderName); - } else { - throw new IllegalStateException("Cannot find appender %s".formatted(appenderName)); - } + LoggerFactory.init(nodeType, Node.getNodeId(), loggingProperties); } } \ No newline at end of file diff --git a/turms-server-common/src/main/java/im/turms/server/common/context/TurmsApplicationContext.java b/turms-server-common/src/main/java/im/turms/server/common/context/TurmsApplicationContext.java index 294baa9ac4..b4d38c027f 100644 --- a/turms-server-common/src/main/java/im/turms/server/common/context/TurmsApplicationContext.java +++ b/turms-server-common/src/main/java/im/turms/server/common/context/TurmsApplicationContext.java @@ -18,9 +18,10 @@ package im.turms.server.common.context; import im.turms.server.common.cluster.node.NodeType; +import im.turms.server.common.logging.core.logger.LoggerFactory; +import im.turms.server.common.logging.core.logger.Logger; import io.lettuce.core.RedisException; import lombok.Getter; -import lombok.extern.log4j.Log4j2; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.info.BuildProperties; import org.springframework.context.event.ContextClosedEvent; @@ -39,9 +40,10 @@ */ @Component @Getter -@Log4j2 public class TurmsApplicationContext { + private static final Logger LOGGER = LoggerFactory.getLogger(TurmsApplicationContext.class); + private static final String BUILD_INFO_PROPS_PATH = "classpath:META-INF/build-info.properties"; private static final String DEFAULT_VERSION = "0.0.0"; @@ -81,7 +83,7 @@ public TurmsApplicationContext(Environment environment, isProduction = !isDevOrLocalTest && !isInProfiles(testEnvs, activeProfiles); version = getVersion(isProduction, buildProperties); - log.info("The local node with version {} is running in a {} environment", + LOGGER.info("The local node with version {} is running in a {} environment", version, isProduction ? "production" : "non-production"); @@ -130,7 +132,7 @@ private String getVersion(boolean isProduction, BuildProperties buildProperties) if (buildProperties == null) { // We allow build-info.properties not exist in non-production // environments for a better development experience - log.warn("Cannot find " + BUILD_INFO_PROPS_PATH + + LOGGER.warn("Cannot find " + BUILD_INFO_PROPS_PATH + ", fall back to the default version " + DEFAULT_VERSION + " in non-production environments." + " Fix it by running \"mvn compile\""); @@ -149,7 +151,7 @@ private void setupErrorHandlerContext() { // Log the exception only in non-production env, so we can try to optimize the code of // client to close the connection with a 4-way handshake on the client side if (!this.isProduction) { - log.warn(t); + LOGGER.warn("Failed to read from a forcibly closed connection", t); } } else { if (isClosing) { @@ -157,7 +159,7 @@ private void setupErrorHandlerContext() { return; } } - log.error("Unhandled exception", t); + LOGGER.error("Unhandled exception", t); } }); } diff --git a/turms-server-common/src/main/java/im/turms/server/common/healthcheck/CpuHealthChecker.java b/turms-server-common/src/main/java/im/turms/server/common/healthcheck/CpuHealthChecker.java index 066ccbdee3..45505638c4 100644 --- a/turms-server-common/src/main/java/im/turms/server/common/healthcheck/CpuHealthChecker.java +++ b/turms-server-common/src/main/java/im/turms/server/common/healthcheck/CpuHealthChecker.java @@ -18,18 +18,19 @@ package im.turms.server.common.healthcheck; import com.sun.management.OperatingSystemMXBean; +import im.turms.server.common.logging.core.logger.LoggerFactory; +import im.turms.server.common.logging.core.logger.Logger; import im.turms.server.common.property.env.common.healthcheck.CpuHealthCheckProperties; -import lombok.extern.log4j.Log4j2; -import org.apache.logging.log4j.Level; import java.lang.management.ManagementFactory; /** * @author James Chen */ -@Log4j2 public final class CpuHealthChecker extends HealthChecker { + private static final Logger LOGGER = LoggerFactory.getLogger(CpuHealthChecker.class); + private final OperatingSystemMXBean operatingSystemBean; private final boolean isCpuHealthCheckAvailable; @@ -47,7 +48,7 @@ public CpuHealthChecker(CpuHealthCheckProperties properties) { boolean isCpuHealthCheckAvailable = true; if (cpuLoad < 0) { - log.warn("CPU health checker cannot work because the \"recent cpu usage\" for the whole operating environment is unavailable"); + LOGGER.warn("CPU health checker cannot work because the \"recent cpu usage\" for the whole operating environment is unavailable"); isCpuHealthCheckAvailable = false; isCpuHealthy = true; } @@ -76,12 +77,12 @@ public void updateHealthStatus() { } // Log - log.log(Level.DEBUG, "CPU load: {}", cpuLoad); + LOGGER.debug( "CPU load: {}", cpuLoad); if (wasCpuHealthy != isCpuHealthy) { if (isCpuHealthy) { - log.info("The CPU has become healthy. The current CPU load: {}", cpuLoad); + LOGGER.info("The CPU has become healthy. The current CPU load: {}", cpuLoad); } else { - log.info("The CPU has become unhealthy. The current CPU load: {}", cpuLoad); + LOGGER.info("The CPU has become unhealthy. The current CPU load: {}", cpuLoad); } } } diff --git a/turms-server-common/src/main/java/im/turms/server/common/healthcheck/MemoryHealthChecker.java b/turms-server-common/src/main/java/im/turms/server/common/healthcheck/MemoryHealthChecker.java index 4ae1b115d7..41c0fa4e34 100644 --- a/turms-server-common/src/main/java/im/turms/server/common/healthcheck/MemoryHealthChecker.java +++ b/turms-server-common/src/main/java/im/turms/server/common/healthcheck/MemoryHealthChecker.java @@ -20,10 +20,11 @@ import com.sun.management.HotSpotDiagnosticMXBean; import com.sun.management.OperatingSystemMXBean; import com.sun.management.VMOption; +import im.turms.server.common.logging.core.logger.Logger; +import im.turms.server.common.logging.core.logger.LoggerFactory; +import im.turms.server.common.logging.core.model.LogLevel; import im.turms.server.common.property.env.common.healthcheck.MemoryHealthCheckProperties; import io.netty.util.internal.PlatformDependent; -import lombok.extern.log4j.Log4j2; -import org.apache.logging.log4j.Level; import java.lang.management.BufferPoolMXBean; import java.lang.management.ManagementFactory; @@ -45,9 +46,10 @@ * @see jdk.internal.access.JavaNioAccess#getDirectBufferPool * @see io.micrometer.core.instrument.binder.jvm.JvmMemoryMetrics */ -@Log4j2 public final class MemoryHealthChecker extends HealthChecker { + private static final Logger LOGGER = LoggerFactory.getLogger(MemoryHealthChecker.class); + private final OperatingSystemMXBean operatingSystemBean; private final long maxAvailableMemory; @@ -126,7 +128,7 @@ public MemoryHealthChecker(MemoryHealthCheckProperties properties) { } int estimatedMaxNonHeapMemory = 256 * 1024 * 1024; if (maxAvailableMemory > maxAvailableDirectMemory + maxHeapMemory + estimatedMaxNonHeapMemory) { - log.warn("The max available memory %s is larger than the total of the available direct memory %s, the max heap memory %s, and the estimated max non-heap memory %s, " + LOGGER.warn("The max available memory %s is larger than the total of the available direct memory %s, the max heap memory %s, and the estimated max non-heap memory %s, " .formatted(asMbString(maxAvailableMemory), asMbString(maxAvailableDirectMemory), asMbString(maxHeapMemory), asMbString(estimatedMaxNonHeapMemory)) + "which indicates that some memory will never be used by the server"); } @@ -161,13 +163,15 @@ public void updateHealthStatus() { usedAvailableMemory = usedDirectMemory + usedHeapMemory + usedNonHeapMemory; freeSystemMemory = operatingSystemBean.getFreeMemorySize(); usedSystemMemory = totalPhysicalMemorySize - freeSystemMemory; + + tryLog(); } private void tryLog() { boolean isHealthy = isHealthy(); - Level logLevel = isHealthy ? Level.DEBUG : Level.WARN; - if (log.isEnabled(logLevel)) { - log.log(logLevel, "Used system memory: {}/{}; " + LogLevel logLevel = isHealthy ? LogLevel.DEBUG : LogLevel.WARN; + if (LOGGER.isEnabled(logLevel)) { + LOGGER.log(logLevel, "Used system memory: {}/{}; " + "Used available memory: {}/{}; " + "Used direct memory: {}/{}/{}; " + "Used heap memory: {}/{}; " @@ -192,20 +196,20 @@ private void tryLog() { if (directMemoryWarningThresholdPercentage > 0 && directMemoryWarningThresholdPercentage < usedMemoryPercentage && minMemoryWarningIntervalMillis < (now - lastDirectMemoryWarningTimestamp)) { lastDirectMemoryWarningTimestamp = now; - log.warn("The used direct memory has exceeded the warning threshold: {}/{}/{}/{}", + LOGGER.warn("The used direct memory has exceeded the warning threshold: {}/{}/{}/{}", asMbString(usedDirectMemory), asMbString(maxDirectMemory), usedMemoryPercentage, directMemoryWarningThresholdPercentage); } usedMemoryPercentage = 100F * usedHeapMemory / maxHeapMemory; if (heapMemoryWarningThresholdPercentage > 0 && heapMemoryWarningThresholdPercentage < usedMemoryPercentage && minMemoryWarningIntervalMillis < (now - lastHeapMemoryWarningTimestamp)) { lastHeapMemoryWarningTimestamp = now; - log.warn("The used heap memory has exceeded the warning threshold: {}/{}/{}/{}", + LOGGER.warn("The used heap memory has exceeded the warning threshold: {}/{}/{}/{}", asMbString(usedHeapMemory), asMbString(maxHeapMemory), usedMemoryPercentage, heapMemoryWarningThresholdPercentage); } if (!isHealthy && heapMemoryGcThresholdPercentage > 0 && heapMemoryGcThresholdPercentage < usedMemoryPercentage && minHeapMemoryGcIntervalMillis < (now - lastHeapMemoryGcTimestamp)) { lastHeapMemoryGcTimestamp = now; - log.info("Trying to start GC because the available memory has exceeded and the used heap memory has exceeded the GC threshold: {}/{}/{}/{}", + LOGGER.info("Trying to start GC because the available memory has exceeded and the used heap memory has exceeded the GC threshold: {}/{}/{}/{}", asMbString(usedHeapMemory), asMbString(maxHeapMemory), usedMemoryPercentage, heapMemoryGcThresholdPercentage); System.gc(); } diff --git a/turms-server-common/src/main/java/im/turms/server/common/log4j/LogContextConstant.java b/turms-server-common/src/main/java/im/turms/server/common/log4j/LogContextConstant.java deleted file mode 100644 index a74a51fb31..0000000000 --- a/turms-server-common/src/main/java/im/turms/server/common/log4j/LogContextConstant.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2019 The Turms Project - * https://github.com/turms-im/turms - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package im.turms.server.common.log4j; - -/** - * @author James Chen - */ -public final class LogContextConstant { - private LogContextConstant() { - } - - public static final String LOG_TYPE = "TYPE"; - public static final String NODE_TYPE = "NODE_TYPE"; - public static final String NODE_ID = "NODE_ID"; - - public static final class LogType { - private LogType() { - } - - public static final String ADMIN_API = "ADMIN_API"; - public static final String CLIENT_API = "CLIENT_API"; - public static final String NOTIFICATION = "NOTIFICATION"; - } - - public static final class NodeType { - private NodeType() { - } - - public static final String SERVICE = "S"; - public static final String GATEWAY = "G"; - } - -} \ No newline at end of file diff --git a/turms-server-common/src/main/java/im/turms/server/common/log4j/plugin/TurmsContextLookup.java b/turms-server-common/src/main/java/im/turms/server/common/log4j/plugin/TurmsContextLookup.java deleted file mode 100644 index 361b7d55fa..0000000000 --- a/turms-server-common/src/main/java/im/turms/server/common/log4j/plugin/TurmsContextLookup.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (C) 2019 The Turms Project - * https://github.com/turms-im/turms - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package im.turms.server.common.log4j.plugin; - - -import im.turms.server.common.cluster.node.Node; -import im.turms.server.common.cluster.node.NodeType; -import im.turms.server.common.log4j.LogContextConstant; -import im.turms.server.common.logging.CustomLogger; -import im.turms.server.common.util.ReflectionUtil; -import lombok.Setter; -import lombok.SneakyThrows; -import org.apache.logging.log4j.core.LogEvent; -import org.apache.logging.log4j.core.async.AsyncLogger; -import org.apache.logging.log4j.core.async.RingBufferLogEvent; -import org.apache.logging.log4j.core.config.plugins.Plugin; -import org.apache.logging.log4j.core.lookup.StrLookup; - -import java.lang.invoke.VarHandle; - -/** - * @author James Chen - */ -@Plugin(name = "myctx", category = StrLookup.CATEGORY) -public class TurmsContextLookup implements StrLookup { - - private static final VarHandle LOGGER = ReflectionUtil.getVarHandle(RingBufferLogEvent.class, "asyncLogger"); - - @Setter - private static NodeType nodeType; - - @Override - public String lookup(String key) { - return null; - } - - /** - * @param event should always be RingBufferLogEvent - */ - @SneakyThrows - @Override - public String lookup(LogEvent event, String key) { - if (!(event instanceof RingBufferLogEvent logEvent)) { - return null; - } - return switch (key) { - case LogContextConstant.LOG_TYPE -> { - AsyncLogger logger = (AsyncLogger) LOGGER.get(logEvent); - if (logger == CustomLogger.ADMIN_API_LOGGER) { - yield LogContextConstant.LogType.ADMIN_API; - } else if (logger == CustomLogger.CLIENT_API_LOGGER) { - yield LogContextConstant.LogType.CLIENT_API; - } else if (logger == CustomLogger.NOTIFICATION_LOGGER) { - yield LogContextConstant.LogType.NOTIFICATION; - } else { - yield null; - } - } - case LogContextConstant.NODE_TYPE -> { - if (nodeType == null) { - yield ""; - } - yield switch (nodeType) { - case SERVICE -> LogContextConstant.NodeType.SERVICE; - case GATEWAY -> LogContextConstant.NodeType.GATEWAY; - }; - } - case LogContextConstant.NODE_ID -> { - String nodeId = Node.getNodeId(); - yield nodeId == null ? "" : nodeId; - } - default -> throw new IllegalStateException("Unexpected value: " + key); - }; - } - -} \ No newline at end of file diff --git a/turms-server-common/src/main/java/im/turms/server/common/logging/AdminApiLogging.java b/turms-server-common/src/main/java/im/turms/server/common/logging/AdminApiLogging.java index bbdebb9e87..e8197d8ec7 100644 --- a/turms-server-common/src/main/java/im/turms/server/common/logging/AdminApiLogging.java +++ b/turms-server-common/src/main/java/im/turms/server/common/logging/AdminApiLogging.java @@ -21,7 +21,8 @@ import java.util.Map; -import static im.turms.server.common.logging.CustomLogger.LOG_FIELD_DELIMITER; +import static im.turms.server.common.logging.CommonLogger.ADMIN_API_LOGGER; +import static im.turms.server.common.logging.CommonLogger.LOG_FIELD_DELIMITER; /** * @author James Chen @@ -46,14 +47,14 @@ public static void log(String account, ip, // Request requestId, - DateUtil.toISO(requestTime), + DateUtil.toStr(requestTime), action, params.toString(), // Response isSuccessful ? "TRUE" : "FALSE", String.valueOf(processingTime), isSuccessful ? "" : throwable.toString()); - CustomLogger.ADMIN_API_LOGGER.info(msg); + ADMIN_API_LOGGER.info(msg); } } \ No newline at end of file diff --git a/turms-server-common/src/main/java/im/turms/server/common/logging/CommonLogger.java b/turms-server-common/src/main/java/im/turms/server/common/logging/CommonLogger.java new file mode 100644 index 0000000000..13a812bae3 --- /dev/null +++ b/turms-server-common/src/main/java/im/turms/server/common/logging/CommonLogger.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2019 The Turms Project + * https://github.com/turms-im/turms + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.turms.server.common.logging; + +import im.turms.server.common.logging.core.logger.Logger; +import im.turms.server.common.logging.core.logger.LoggerFactory; +import im.turms.server.common.logging.core.logger.LoggerOptions; +import im.turms.server.common.logging.core.model.LogLevel; + +/** + * @author James Chen + */ +public final class CommonLogger { + + public static final String LOG_FIELD_DELIMITER = "|"; + + // TODO: make configurable + + public static final Logger ADMIN_API_LOGGER = LoggerFactory.getLogger(LoggerOptions.builder() + .filePath("@HOME/log/@SERVICE_TYPE_NAME-admin-api.log") + .level(LogLevel.INFO) + .build()); + + public static final Logger CLIENT_API_LOGGER = LoggerFactory.getLogger(LoggerOptions.builder() + .filePath("@HOME/log/@SERVICE_TYPE_NAME-client-api.log") + .level(LogLevel.INFO) + .build()); + + public static final Logger NOTIFICATION_LOGGER = LoggerFactory.getLogger(LoggerOptions.builder() + .filePath("@HOME/log/@SERVICE_TYPE_NAME-notification.log") + .level(LogLevel.INFO) + .build()); + + private CommonLogger() { + } + +} \ No newline at end of file diff --git a/turms-server-common/src/main/java/im/turms/server/common/logging/RequestLoggingContext.java b/turms-server-common/src/main/java/im/turms/server/common/logging/RequestLoggingContext.java deleted file mode 100644 index 257b098c73..0000000000 --- a/turms-server-common/src/main/java/im/turms/server/common/logging/RequestLoggingContext.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2019 The Turms Project - * https://github.com/turms-im/turms - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package im.turms.server.common.logging; - -import im.turms.server.common.tracing.TracingCloseableContext; -import im.turms.server.common.tracing.TracingContext; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; -import reactor.util.context.ContextView; - -/** - * @author James Chen - */ -@AllArgsConstructor -@NoArgsConstructor -public class RequestLoggingContext { - - @Getter - @Setter - private TracingContext tracingContext = TracingContext.NOOP; - - public static final RequestLoggingContext DEFAULT = new RequestLoggingContext(TracingContext.NOOP); - public static final String CTX_KEY_NAME = "REQ"; - - public static Long readTraceIdFromContext(ContextView context) { - RequestLoggingContext loggingContext = context.getOrDefault(CTX_KEY_NAME, null); - if (loggingContext == null) { - return null; - } - TracingContext tracingContext = loggingContext.getTracingContext(); - if (tracingContext == TracingContext.NOOP) { - return null; - } - return tracingContext.getTraceId(); - } - - public TracingCloseableContext asCloseable() { - return new TracingCloseableContext(tracingContext); - } - - public void clearMdc() { - tracingContext.clearMdc(); - } - - public void updateMdc() { - tracingContext.updateMdc(); - } - -} \ No newline at end of file diff --git a/turms-server-common/src/main/java/im/turms/server/common/logging/core/appender/Appender.java b/turms-server-common/src/main/java/im/turms/server/common/logging/core/appender/Appender.java new file mode 100644 index 0000000000..992f6d3734 --- /dev/null +++ b/turms-server-common/src/main/java/im/turms/server/common/logging/core/appender/Appender.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2019 The Turms Project + * https://github.com/turms-im/turms + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.turms.server.common.logging.core.appender; + +import im.turms.server.common.logging.core.model.LogLevel; +import im.turms.server.common.logging.core.model.LogRecord; +import lombok.Data; +import lombok.SneakyThrows; + +import java.nio.channels.FileChannel; + +/** + * @author James Chen + */ +@Data +public abstract class Appender implements AutoCloseable { + + private final String name; + private final LogLevel level; + + protected int offset; + protected FileChannel channel; + + @SneakyThrows + protected Appender(String name, LogLevel level) { + this.name = name; + this.level = level; + } + + @SneakyThrows + @Override + public void close() { + channel.force(true); + channel.close(); + } + + @SneakyThrows + public int append(LogRecord record) { + if (record.level().isLoggable(level)) { + return channel.write(record.data().nioBuffer()); + } + return 0; + } + +} diff --git a/turms-server-common/src/main/java/im/turms/server/common/logging/CustomLogger.java b/turms-server-common/src/main/java/im/turms/server/common/logging/core/appender/ConsoleAppender.java similarity index 51% rename from turms-server-common/src/main/java/im/turms/server/common/logging/CustomLogger.java rename to turms-server-common/src/main/java/im/turms/server/common/logging/core/appender/ConsoleAppender.java index 3d6c39519a..999dfda166 100644 --- a/turms-server-common/src/main/java/im/turms/server/common/logging/CustomLogger.java +++ b/turms-server-common/src/main/java/im/turms/server/common/logging/core/appender/ConsoleAppender.java @@ -15,24 +15,21 @@ * limitations under the License. */ -package im.turms.server.common.logging; +package im.turms.server.common.logging.core.appender; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; +import im.turms.server.common.logging.core.model.LogLevel; + +import java.io.FileDescriptor; +import java.io.FileOutputStream; /** * @author James Chen */ -public final class CustomLogger { - - public static final String LOG_FIELD_DELIMITER = "|"; - public static final Logger ADMIN_API_LOGGER = LogManager.getLogger(CustomLogger.class); - public static final Logger CLIENT_API_LOGGER = LogManager.getLogger(CustomLogger.class); - public static final Logger NOTIFICATION_LOGGER = LogManager.getLogger(CustomLogger.class); - -// public static final org.apache.logging.log4j.Logger slowlogLogger = LogManager.getLogger(Logger.class); +public class ConsoleAppender extends Appender { - private CustomLogger() { + public ConsoleAppender(LogLevel level) { + super("console", level); + channel = new FileOutputStream(FileDescriptor.out).getChannel(); } } \ No newline at end of file diff --git a/turms-server-common/src/main/java/im/turms/server/common/logging/core/appender/RollingFileAppender.java b/turms-server-common/src/main/java/im/turms/server/common/logging/core/appender/RollingFileAppender.java new file mode 100644 index 0000000000..993c6fac05 --- /dev/null +++ b/turms-server-common/src/main/java/im/turms/server/common/logging/core/appender/RollingFileAppender.java @@ -0,0 +1,269 @@ +/* + * Copyright (C) 2019 The Turms Project + * https://github.com/turms-im/turms + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.turms.server.common.logging.core.appender; + +import im.turms.server.common.logging.core.logger.InternalLogger; +import im.turms.server.common.logging.core.model.LogLevel; +import im.turms.server.common.logging.core.model.LogRecord; +import lombok.SneakyThrows; + +import java.nio.channels.FileChannel; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.StandardOpenOption; +import java.nio.file.attribute.BasicFileAttributes; +import java.time.LocalDate; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoUnit; +import java.time.temporal.TemporalAccessor; +import java.util.ArrayDeque; +import java.util.Collections; +import java.util.Comparator; +import java.util.TreeSet; +import java.util.concurrent.TimeUnit; + +/** + * @author James Chen + */ +public class RollingFileAppender extends Appender { + + private static final char FIELD_DELIMITER = '_'; + private static final ZoneId UTC = ZoneId.of("UTC"); + + private final String filePrefix; + private final String fileSuffix; + private final String fileMiddle = "yyyyMMdd"; + + private final Path fileDirectory; + + private final DateTimeFormatter fileMiddleTemplate = DateTimeFormatter.ofPattern(fileMiddle) + .withZone(UTC); + + private final int maxFiles; + private final long maxFileSize; + + private final ArrayDeque files; + private LogFile file; + + private long nextFileSize; + private long nextIndex; + private long nextDay = Long.MIN_VALUE; + + @SneakyThrows + public RollingFileAppender(LogLevel level, + String file, + int maxFiles, + long maxFileMb) { + super("file", level); + Path filePath = Paths.get(file).toAbsolutePath(); + + String fileName = filePath.getFileName().toString(); + int index = fileName.lastIndexOf('.'); + + this.filePrefix = index == -1 ? fileName : fileName.substring(0, index); + this.fileSuffix = index == -1 ? "" : fileName.substring(index); + + this.fileDirectory = filePath.getParent(); + + this.maxFiles = Math.max(maxFiles, 0); + this.maxFileSize = (maxFileMb > 0) ? maxFileMb * 1024 * 1024 : Long.MAX_VALUE; + + Files.createDirectories(fileDirectory); + files = LogDirectoryVisitor.visit(fileDirectory, filePrefix, fileSuffix, fileMiddle, fileMiddleTemplate, maxFiles); + + LogFile logFile = files.peekLast(); + + if (logFile == null) { + openNewFile(); + } else { + openExistingFile(logFile); + } + } + + private FileChannel openChannel(Path file) throws Exception { + Path directory = file.getParent(); + if (directory != null) { + Files.createDirectories(directory); + } + return FileChannel.open( + file, + StandardOpenOption.CREATE, + StandardOpenOption.WRITE, + StandardOpenOption.APPEND + ); + } + + private boolean needRoll(LogRecord record) { + return record.timestamp() >= nextDay || nextFileSize >= maxFileSize; + } + + @Override + public int append(LogRecord record) { + if (needRoll(record)) { + roll(); + } + int bytes = super.append(record); + nextFileSize += bytes; + return bytes; + } + + /** + * Note that it's fine to close file channel and open a new one not atomically + * because there is always one thread will call roll() + */ + @SneakyThrows + private void roll() { + closeFile(); + openNewFile(); + clean(); + } + + private void openNewFile() throws Exception { + ZonedDateTime now = ZonedDateTime.now(UTC); + ZonedDateTime next = now.plus(1, ChronoUnit.DAYS).truncatedTo(ChronoUnit.DAYS); + + Path filePath = getFilePath(now); + FileChannel fileChannel = openChannel(filePath); + + channel = fileChannel; + file = new LogFile(filePath, now, nextIndex); + files.add(file); + + nextFileSize = fileChannel.size(); + nextDay = TimeUnit.SECONDS.toNanos(next.toEpochSecond()); + nextIndex++; + } + + private void openExistingFile(LogFile existingFile) throws Exception { + ZonedDateTime now = existingFile.dateTime; + ZonedDateTime next = now.plus(1, ChronoUnit.DAYS).truncatedTo(ChronoUnit.DAYS); + + Path filePath = existingFile.path; + FileChannel fileChannel = openChannel(filePath); + + channel = fileChannel; + file = existingFile; + + nextFileSize = fileChannel.size(); + nextDay = TimeUnit.SECONDS.toNanos(next.toEpochSecond()); + nextIndex = existingFile.index + 1; + } + + @SneakyThrows + private void closeFile() { + channel.force(true); + channel.close(); + } + + private void clean() { + while (files.size() > maxFiles) { + LogFile file = files.remove(); + + if (maxFiles > 0) { + deleteFile(file); + } + } + } + + private Path getFilePath(ZonedDateTime currentDay) { + String name = filePrefix + FIELD_DELIMITER + fileMiddleTemplate.format(currentDay) + FIELD_DELIMITER + nextIndex + fileSuffix; + return fileDirectory.resolve(name); + } + + private static void deleteFile(LogFile file) { + Path path = file.path; + try { + Files.deleteIfExists(path); + } catch (Throwable e) { + } + } + + private record LogFile(Path path, ZonedDateTime dateTime, long index) { + } + + private static class LogDirectoryVisitor extends SimpleFileVisitor { + + private final TreeSet files = new TreeSet<>(Comparator.comparingLong(o -> o.index)); + + private final String prefix; + private final String suffix; + private final String middle; + + private final DateTimeFormatter template; + + private final int maxFilesToKeep; + private final boolean deleteExceedFiles; + + public LogDirectoryVisitor(String prefix, String suffix, String middle, DateTimeFormatter template, int maxFiles) { + this.prefix = prefix; + this.suffix = suffix; + this.middle = middle; + this.template = template; + this.maxFilesToKeep = Math.max(1, maxFiles); + this.deleteExceedFiles = maxFiles > 0; + } + + @Override + public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) { + try { + String name = path.getFileName().toString(); + boolean isLogFile = (name.length() > prefix.length() + suffix.length() + middle.length() + 1) && name.startsWith(prefix) && name.endsWith(suffix); + if (!isLogFile) { + return FileVisitResult.CONTINUE; + } + int indexEnd = name.length() - suffix.length(); + int indexStart = name.lastIndexOf(FIELD_DELIMITER, indexEnd - 1); + if (indexStart == prefix.length() + middle.length() + 1) { + long index = Long.parseUnsignedLong(name.substring(indexStart + 1, indexEnd)); + TemporalAccessor time = template.parse(name.substring(prefix.length() + 1, indexStart)); + ZonedDateTime timestamp = LocalDate.from(time).atStartOfDay(UTC); + handleLogFile(path, timestamp, index); + } + } catch (Throwable e) { + InternalLogger.INSTANCE.warn("Cannot figure out if a file matches the template: " + path, e); + } + return FileVisitResult.CONTINUE; + } + + private void handleLogFile(Path path, ZonedDateTime timestamp, long index) { + LogFile file = new LogFile(path, timestamp, index); + files.add(file); + + if (files.size() > maxFilesToKeep) { + LogFile firstLogFile = files.first(); + files.remove(firstLogFile); + if (deleteExceedFiles) { + deleteFile(firstLogFile); + } + } + } + + public static ArrayDeque visit(Path directory, String prefix, String suffix, String middle, DateTimeFormatter template, int maxFiles) throws Exception { + LogDirectoryVisitor visitor = new LogDirectoryVisitor(prefix, suffix, middle, template, maxFiles); + Files.walkFileTree(directory, Collections.emptySet(), 1, visitor); + return new ArrayDeque<>(visitor.files); + } + + } + +} diff --git a/turms-server-common/src/main/java/im/turms/server/common/logging/core/context/LogThreadContext.java b/turms-server-common/src/main/java/im/turms/server/common/logging/core/context/LogThreadContext.java new file mode 100644 index 0000000000..300ee3fbdc --- /dev/null +++ b/turms-server-common/src/main/java/im/turms/server/common/logging/core/context/LogThreadContext.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2019 The Turms Project + * https://github.com/turms-im/turms + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.turms.server.common.logging.core.context; + +import im.turms.server.common.tracing.TracingContext; +import io.netty.util.concurrent.FastThreadLocal; + +import javax.annotation.Nullable; + +/** + * @author James Chen + */ +public class LogThreadContext { + + // We only need to use "TracingContext" currently, + // so just keep it simple, don't wrap it + private static final FastThreadLocal CONTEXT = new FastThreadLocal<>(); + + public static void put(TracingContext context) { + CONTEXT.set(context); + } + + @Nullable + public static TracingContext get() { + return CONTEXT.getIfExists(); + } + + public static void remove() { + CONTEXT.remove(); + } + + public static TracingContext removeAndGet() { + TracingContext context = CONTEXT.getIfExists(); + CONTEXT.remove(); + return context; + } + + public static void removeIfEquals(TracingContext context) { + TracingContext currentContext = CONTEXT.getIfExists(); + if (currentContext == context) { + CONTEXT.remove(); + } + } +} \ No newline at end of file diff --git a/turms-server-common/src/main/java/im/turms/server/common/logging/core/idle/BackoffIdleStrategy.java b/turms-server-common/src/main/java/im/turms/server/common/logging/core/idle/BackoffIdleStrategy.java new file mode 100644 index 0000000000..455bc999bf --- /dev/null +++ b/turms-server-common/src/main/java/im/turms/server/common/logging/core/idle/BackoffIdleStrategy.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2019 The Turms Project + * https://github.com/turms-im/turms + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.turms.server.common.logging.core.idle; + +import java.util.concurrent.locks.LockSupport; + +/** + * @author James Chen + */ +public final class BackoffIdleStrategy { + + static final int WORKING = 0; + static final int SPINNING = 1; + static final int YIELDING = 2; + static final int PARKING = 3; + + final long maxSpins; + final long maxYields; + final long minParkPeriodNs; + final long maxParkPeriodNs; + + int state = WORKING; + long value; + + public BackoffIdleStrategy(long maxSpins, long maxYields, long minParkPeriodNs, long maxParkPeriodNs) { + if (minParkPeriodNs < 1 || maxParkPeriodNs < minParkPeriodNs) { + throw new IllegalArgumentException("Min park period " + minParkPeriodNs + + " < 1 or max park period " + maxParkPeriodNs + " < min"); + } + this.maxSpins = maxSpins; + this.maxYields = maxYields; + this.minParkPeriodNs = minParkPeriodNs; + this.maxParkPeriodNs = maxParkPeriodNs; + } + + public void idle() { + switch (state) { + case WORKING: + value = 0; + state = SPINNING; + // fallthrough + + case SPINNING: + if (++value <= maxSpins) { + Thread.onSpinWait(); + break; + } + value = 0; + state = YIELDING; + // fallthrough + + case YIELDING: + if (++value <= maxYields) { + Thread.yield(); + break; + } + value = minParkPeriodNs; + state = PARKING; + // fallthrough + + case PARKING: + LockSupport.parkNanos(value); + value = Math.min(value << 1, maxParkPeriodNs); + } + } + + public void reset() { + state = WORKING; + } + +} \ No newline at end of file diff --git a/turms-server-common/src/main/java/im/turms/server/common/logging/core/layout/TemplateLayout.java b/turms-server-common/src/main/java/im/turms/server/common/logging/core/layout/TemplateLayout.java new file mode 100644 index 0000000000..b985252118 --- /dev/null +++ b/turms-server-common/src/main/java/im/turms/server/common/logging/core/layout/TemplateLayout.java @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2019 The Turms Project + * https://github.com/turms-im/turms + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.turms.server.common.logging.core.layout; + +import io.netty.buffer.ByteBuf; + +/** + * @author James Chen + */ +public class TemplateLayout { + + static final int DEPTH_LIMIT = 16; + static final int WHITESPACE = ' '; + + public void pad(ByteBuf buffer, int minLength) { + for (int i = 0; i < minLength; i++) { + buffer.writeByte(WHITESPACE); + } + } + + public void padStart(ByteBuf buffer, byte[] bytes, int minLength) { + int diff = minLength - bytes.length; + for (int i = 0; i < diff; i++) { + buffer.writeByte(WHITESPACE); + } + buffer.writeBytes(bytes); + } + + public StringBuilder appendException(Throwable e, StringBuilder entry) { + entry.append('\n'); + return appendException(e, entry, 1, 1); + } + + private StringBuilder appendException(Throwable e, + StringBuilder builder, + int indent, + int depth) { + if (depth > DEPTH_LIMIT) { + builder.append(">>(Cyclic Exception?)>>"); + return builder; + } + + builder.append(e.getClass().getName()); + + String message = e.getMessage(); + if (message != null) { + builder.append(": ") + .append(message); + } + + StackTraceElement[] stacks = e.getStackTrace(); + Throwable[] suppresses = e.getSuppressed(); + Throwable cause = e.getCause(); + + boolean isStacksNotEmpty = stacks != null && stacks.length > 0; + boolean isSuppressesNotEmpty = suppresses != null && suppresses.length > 0; + boolean isCauseNotNull = cause != null; + + if (isStacksNotEmpty || isSuppressesNotEmpty || isCauseNotNull) { + builder.append('\n'); + } + + if (isStacksNotEmpty) { + appendStack(stacks, builder, indent); + } + + if (isSuppressesNotEmpty) { + appendSuppresses(suppresses, builder, indent, depth); + } + + if (isCauseNotNull) { + appendCause(cause, builder, indent, depth); + } + return builder; + } + + private void appendStack(StackTraceElement[] stack, + StringBuilder builder, + int indent) { + for (StackTraceElement element : stack) { + appendTabs(indent, builder); + + builder.append("at "); + builder.append(element.getClassName()); + builder.append('.'); + builder.append(element.getMethodName()); + builder.append('('); + + if (element.isNativeMethod()) { + builder.append("native"); + } else { + String fileName = element.getFileName(); + int lineNumber = element.getLineNumber(); + if (fileName == null) { + builder.append("unknown"); + } else { + builder.append(fileName); + if (lineNumber >= 0) { + builder.append(':'); + builder.append(lineNumber); + } + } + } + builder.append(')'); + builder.append('\n'); + } + } + + public void appendSuppresses(Throwable[] suppresses, + StringBuilder entry, + int indent, + int depth) { + for (Throwable suppress : suppresses) { + entry.append('\n'); + appendTabs(indent, entry); + entry.append("suppressed: "); + appendException(suppress, entry, indent + 1, depth + 1); + } + } + + public void appendCause(Throwable cause, + StringBuilder entry, + int indent, + int depth) { + entry.append('\n'); + appendTabs(indent - 1, entry); + entry.append("caused by: "); + appendException(cause, entry, indent, depth + 1); + } + + public void appendTabs(int tabs, StringBuilder builder) { + for (int i = 0; i < tabs; i++) { + builder.append('\t'); + } + } + +} \ No newline at end of file diff --git a/turms-server-common/src/main/java/im/turms/server/common/logging/core/layout/TurmsTemplateLayout.java b/turms-server-common/src/main/java/im/turms/server/common/logging/core/layout/TurmsTemplateLayout.java new file mode 100644 index 0000000000..82ff7415c3 --- /dev/null +++ b/turms-server-common/src/main/java/im/turms/server/common/logging/core/layout/TurmsTemplateLayout.java @@ -0,0 +1,194 @@ +/* + * Copyright (C) 2019 The Turms Project + * https://github.com/turms-im/turms + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.turms.server.common.logging.core.layout; + +import com.google.common.base.Strings; +import im.turms.server.common.cluster.node.NodeType; +import im.turms.server.common.logging.core.context.LogThreadContext; +import im.turms.server.common.logging.core.model.LogLevel; +import im.turms.server.common.tracing.TracingContext; +import im.turms.server.common.util.DateUtil; +import im.turms.server.common.util.Formatter; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.PooledByteBufAllocator; +import org.springframework.util.StringUtils; + +import javax.annotation.Nullable; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; + +/** + * @author James Chen + * @implNote Note that we do NOT escape or remove non-printable characters for logging + * because it has a significant impact on performance. The caller of logger should ensure + * won't pass malicious texts especially the user input texts + */ +public class TurmsTemplateLayout extends TemplateLayout { + + private static final int ESTIMATED_PATTERN_TEXT_LENGTH = 128; + private static final byte[][] LEVELS; + private static final byte[] NULL = new byte[]{'n', 'u', 'l', 'l'}; + private static final byte[] COLON_SEPARATOR = new byte[]{' ', ':', ' '}; + private static final int TRACE_ID_LENGTH = 19; + private static final int CLASS_NAME_LENGTH = 40; + + static { + LogLevel[] levels = LogLevel.values(); + LEVELS = new byte[levels.length][]; + int maxLength = 0; + for (LogLevel level : levels) { + maxLength = Math.max(level.name().length(), maxLength); + } + for (int i = 0; i < levels.length; i++) { + LEVELS[i] = Strings.padStart(levels[i].name(), maxLength, ' ') + .toUpperCase() + .getBytes(StandardCharsets.US_ASCII); + } + } + + private final int nodeType; + private final byte[] nodeId; + + public TurmsTemplateLayout(NodeType nodeType, String nodeId) { + this.nodeType = nodeType == NodeType.GATEWAY ? 'G' : 'S'; + this.nodeId = nodeId.getBytes(StandardCharsets.UTF_8); + } + + /** + * @implNote Note that we do NOT escape or remove non-printable characters + */ + public ByteBuf format(boolean shouldParse, @Nullable byte[] className, LogLevel level, CharSequence msg, Object[] args, Throwable throwable) { + int expectedLength = msg.length() + ESTIMATED_PATTERN_TEXT_LENGTH; + if (args != null && shouldParse) { + expectedLength += args.length * 8; + } + if (throwable != null) { + expectedLength += 256; + } + ByteBuf buffer = PooledByteBufAllocator.DEFAULT.directBuffer(expectedLength); + byte[] timestamp = DateUtil.toBytes(System.currentTimeMillis()); + +// buffer.writeBytes(bytes); + + String threadName = Thread.currentThread().getName(); + + // Write template text + + buffer.writeBytes(timestamp) + .writeByte(WHITESPACE) + .writeBytes(LEVELS[level.ordinal()]) + .writeByte(WHITESPACE) + .writeByte(nodeType) + .writeByte(WHITESPACE) + .writeBytes(nodeId) + .writeByte(WHITESPACE); + TracingContext context = LogThreadContext.get(); + // trace ID + if (context == null) { + pad(buffer, TRACE_ID_LENGTH); + } else { + long traceId = context.getTraceId(); + if (traceId != TracingContext.UNDEFINED_TRACE_ID) { + padStart(buffer, Formatter.toCharacterBytes(traceId), TRACE_ID_LENGTH); + } + } + // thread name + buffer.writeByte(WHITESPACE) + .writeBytes(threadName.getBytes(StandardCharsets.UTF_8)); + // class name + if (className != null) { + buffer.writeByte(WHITESPACE) + .writeBytes(className); + } + buffer.writeBytes(COLON_SEPARATOR); + // message + appendMessage(shouldParse, msg, args, buffer); + // exception + if (throwable != null) { + StringBuilder exception = appendException(throwable, new StringBuilder(256)); + buffer.writeCharSequence(exception, StandardCharsets.UTF_8); + } + // eol + buffer.writeByte('\n'); + return buffer; + } + + private void appendMessage(boolean shouldParse, CharSequence msg, Object[] args, ByteBuf buffer) { + if (!shouldParse) { + buffer.writeCharSequence(msg, StandardCharsets.UTF_8); + return; + } + int argCount = args == null ? 0 : args.length; + if (argCount == 0) { + buffer.writeCharSequence(msg, StandardCharsets.UTF_8); + return; + } + int argIndex = 0; + byte[] bytes = msg.toString().getBytes(); + int length = bytes.length; + for (int i = 0; i < length; i++) { + byte b = bytes[i]; + if (b == '{' && i < length - 1 && bytes[i + 1] == '}') { + if (argIndex < argCount) { + Object arg = args[argIndex++]; + buffer.writeCharSequence(arg.toString(), StandardCharsets.UTF_8); + } else { + buffer.writeBytes(NULL); + } + i++; + } else { + buffer.writeByte(b); + } + } + } + + public static byte[] formatClassName(String name) { + byte[] rawBytes = name.getBytes(StandardCharsets.US_ASCII); + if (rawBytes.length == CLASS_NAME_LENGTH) { + return name.getBytes(StandardCharsets.US_ASCII); + } + if (rawBytes.length > CLASS_NAME_LENGTH) { + String[] parts = StringUtils.tokenizeToStringArray(name, "."); + String className = parts[parts.length - 1]; + int classNameLength = className.length(); + if (classNameLength >= CLASS_NAME_LENGTH) { + return className.substring(0, CLASS_NAME_LENGTH).getBytes(StandardCharsets.US_ASCII); + } + byte[] result = new byte[CLASS_NAME_LENGTH]; + int writeIndex = CLASS_NAME_LENGTH; + for (int i = parts.length - 1; i >= 0; i--) { + byte[] part = parts[i].getBytes(StandardCharsets.US_ASCII); + if (i == parts.length - 1) { + writeIndex -= part.length; + System.arraycopy(part, 0, result, writeIndex, part.length); + } else if (writeIndex >= 2) { + writeIndex -= 2; + result[writeIndex] = part[0]; + result[writeIndex + 1] = '.'; + } else { + break; + } + } + Arrays.fill(result, 0, writeIndex, (byte) ' '); + return result; + } + return Strings.padStart(name, CLASS_NAME_LENGTH, ' ') + .getBytes(StandardCharsets.US_ASCII); + } + +} diff --git a/turms-server-common/src/main/java/im/turms/server/common/logging/core/logger/AsyncLogger.java b/turms-server-common/src/main/java/im/turms/server/common/logging/core/logger/AsyncLogger.java new file mode 100644 index 0000000000..d4e1a4a67d --- /dev/null +++ b/turms-server-common/src/main/java/im/turms/server/common/logging/core/logger/AsyncLogger.java @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2019 The Turms Project + * https://github.com/turms-im/turms + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.turms.server.common.logging.core.logger; + +import im.turms.server.common.logging.core.appender.Appender; +import im.turms.server.common.logging.core.layout.TurmsTemplateLayout; +import im.turms.server.common.logging.core.model.LogLevel; +import im.turms.server.common.logging.core.model.LogRecord; +import io.netty.buffer.ByteBuf; +import lombok.Data; +import org.jctools.queues.MpscUnboundedArrayQueue; + +import javax.annotation.Nullable; +import java.util.List; + +/** + * @author James Chen + */ +@Data +public final class AsyncLogger implements Logger { + + private final String name; + private final int level; + private final List appenders; + private final TurmsTemplateLayout layout; + private final MpscUnboundedArrayQueue queue; + + private final byte[] nameForLog; + private final boolean shouldParse; + + public AsyncLogger(@Nullable String name, + boolean shouldParse, + List appenders, + TurmsTemplateLayout layout, + MpscUnboundedArrayQueue queue) { + this.name = name; + this.shouldParse = shouldParse; + this.appenders = appenders; + this.layout = layout; + this.queue = queue; + nameForLog = name == null ? null : TurmsTemplateLayout.formatClassName(name); + + int level; + if (appenders.isEmpty()) { + level = Integer.MAX_VALUE; + } else { + level = -1; + for (Appender appender : appenders) { + level = Math.max(level, appender.getLevel().ordinal()); + } + } + this.level = level; + } + + @Override + public boolean isTraceEnabled() { + return level <= 0; + } + + @Override + public boolean isDebugEnabled() { + return level <= 1; + } + + @Override + public boolean isInfoEnabled() { + return level <= 2; + } + + @Override + public boolean isWarnEnabled() { + return level <= 3; + } + + @Override + public boolean isErrorEnabled() { + return level <= 4; + } + + @Override + public boolean isFatalEnabled() { + return level <= 5; + } + + @Override + public boolean isEnabled(LogLevel logLevel) { + return level <= logLevel.ordinal(); + } + + @Override + public void log(LogLevel level, String message) { + if (!isEnabled(level)) { + return; + } + doLog(level, message, null, null); + } + + @Override + public void log(LogLevel level, String message, Object... objects) { + if (!isEnabled(level)) { + return; + } + Throwable throwable = null; + if (objects != null && objects.length > 0 && objects[objects.length - 1] instanceof Throwable t) { + throwable = t; + } + doLog(level, message, objects, throwable); + } + + @Override + public void log(LogLevel level, String message, Throwable throwable) { + if (!isEnabled(level)) { + return; + } + doLog(level, message, null, throwable); + } + + @Override + public void debug(String message, Object... args) { + log(LogLevel.DEBUG, message, args); + } + + @Override + public void info(String message, Object... args) { + log(LogLevel.INFO, message, args); + } + + @Override + public void warn(String message) { + doLog(LogLevel.WARN, message, null, null); + } + + @Override + public void warn(String message, Object... args) { + log(LogLevel.WARN, message, args); + } + + @Override + public void error(String message, @Nullable Throwable throwable) { + doLog(LogLevel.ERROR, message, null, throwable); + } + + @Override + public void error(String message, Object... args) { + log(LogLevel.ERROR, message, args); + } + + @Override + public void fatal(String message, Throwable throwable) { + doLog(LogLevel.FATAL, message, null, throwable); + } + + @Override + public void fatal(String message) { + doLog(LogLevel.FATAL, message, null, null); + } + + private void doLog(LogLevel level, CharSequence message, @Nullable Object[] args, @Nullable Throwable throwable) { + ByteBuf buffer = layout.format(shouldParse, nameForLog, level, message, args, throwable); + boolean offer = queue.offer(new LogRecord(this, level, System.currentTimeMillis(), buffer)); + // Should never happen because the queue is unlimited + if (!offer) { + buffer.release(); + } + } +} diff --git a/turms-server-common/src/main/java/im/turms/server/common/logging/core/logger/InternalLogger.java b/turms-server-common/src/main/java/im/turms/server/common/logging/core/logger/InternalLogger.java new file mode 100644 index 0000000000..6c1ef91566 --- /dev/null +++ b/turms-server-common/src/main/java/im/turms/server/common/logging/core/logger/InternalLogger.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2019 The Turms Project + * https://github.com/turms-im/turms + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.turms.server.common.logging.core.logger; + +/** + * @author James Chen + */ +public class InternalLogger extends WrappedLogger { + + public static final InternalLogger INSTANCE = new InternalLogger(); + + private boolean initialized; + + private InternalLogger() { + } + + public synchronized void init() { + if (initialized) { + return; + } + setLogger(LoggerFactory.getLogger(InternalLogger.class)); + initialized = true; + } + +} diff --git a/turms-server-common/src/main/java/im/turms/server/common/logging/core/logger/Logger.java b/turms-server-common/src/main/java/im/turms/server/common/logging/core/logger/Logger.java new file mode 100644 index 0000000000..ade546b122 --- /dev/null +++ b/turms-server-common/src/main/java/im/turms/server/common/logging/core/logger/Logger.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2019 The Turms Project + * https://github.com/turms-im/turms + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.turms.server.common.logging.core.logger; + +import im.turms.server.common.logging.core.model.LogLevel; + +import javax.annotation.Nullable; + +/** + * @author James Chen + */ +public interface Logger { + boolean isTraceEnabled(); + + boolean isDebugEnabled(); + + boolean isInfoEnabled(); + + boolean isWarnEnabled(); + + boolean isErrorEnabled(); + + boolean isFatalEnabled(); + + boolean isEnabled(LogLevel logLevel); + + void log(LogLevel level, String message); + + void log(LogLevel level, String message, Object... objects); + + void log(LogLevel level, String message, Throwable throwable); + + void debug(String message, Object... args); + + void info(String message, Object... args); + + void warn(String message); + + void warn(String message, Object... args); + + void error(String message, @Nullable Throwable throwable); + + void error(String message, Object... args); + + void fatal(String message, Throwable throwable); + + void fatal(String message); +} diff --git a/turms-server-common/src/main/java/im/turms/server/common/logging/core/logger/LoggerFactory.java b/turms-server-common/src/main/java/im/turms/server/common/logging/core/logger/LoggerFactory.java new file mode 100644 index 0000000000..32ef6b001a --- /dev/null +++ b/turms-server-common/src/main/java/im/turms/server/common/logging/core/logger/LoggerFactory.java @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2019 The Turms Project + * https://github.com/turms-im/turms + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.turms.server.common.logging.core.logger; + +import im.turms.server.common.cluster.node.NodeType; +import im.turms.server.common.logging.core.appender.Appender; +import im.turms.server.common.logging.core.appender.ConsoleAppender; +import im.turms.server.common.logging.core.appender.RollingFileAppender; +import im.turms.server.common.logging.core.layout.TurmsTemplateLayout; +import im.turms.server.common.logging.core.model.LogLevel; +import im.turms.server.common.logging.core.model.LogRecord; +import im.turms.server.common.logging.core.processor.LogProcessorRunner; +import im.turms.server.common.property.env.common.logging.ConsoleLoggingProperties; +import im.turms.server.common.property.env.common.logging.FileLoggingProperties; +import im.turms.server.common.property.env.common.logging.LoggingProperties; +import lombok.Getter; +import lombok.SneakyThrows; +import org.jctools.queues.MpscUnboundedArrayQueue; +import org.springframework.data.util.Pair; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; +import java.util.concurrent.CopyOnWriteArrayList; + +public class LoggerFactory { + + private static TurmsTemplateLayout layout; + + @Getter + private static boolean initialized = false; + + private static final List ALL_APPENDERS = new CopyOnWriteArrayList<>(); + private static final List DEFAULT_APPENDERS = new ArrayList<>(2); + private static final MpscUnboundedArrayQueue QUEUE = new MpscUnboundedArrayQueue<>(1024); + private static final Queue> UNINITIALIZED_LOGGERS = new LinkedList<>(); + + private static String homeDir; + private static String serverTypeName; + private static FileLoggingProperties fileLoggingProperties; + private static ConsoleAppender defaultConsoleAppender; + + @SneakyThrows + public static synchronized void init(NodeType nodeType, String nodeId, LoggingProperties properties) { + if (initialized) { + return; + } + homeDir = nodeType == NodeType.SERVICE + ? System.getenv("TURMS_SERVICE_HOME") + : System.getenv("TURMS_GATEWAY_HOME"); + if (homeDir == null) { + homeDir = "."; + } + serverTypeName = nodeType == NodeType.SERVICE + ? "turms-service" + : "turms-gateway"; + ConsoleLoggingProperties consoleLoggingProperties = properties.getConsole(); + FileLoggingProperties fileLoggingProperties = properties.getFile(); + if (consoleLoggingProperties.isEnabled()) { + ConsoleAppender consoleAppender = new ConsoleAppender(consoleLoggingProperties.getLevel()); + defaultConsoleAppender = consoleAppender; + DEFAULT_APPENDERS.add(consoleAppender); + } + LoggerFactory.fileLoggingProperties = fileLoggingProperties; + if (fileLoggingProperties.isEnabled()) { + RollingFileAppender fileAppender = new RollingFileAppender(fileLoggingProperties.getLevel(), + getFilePath(fileLoggingProperties.getFilePath()), + fileLoggingProperties.getMaxFiles(), + fileLoggingProperties.getMaxFileSizeMb()); + DEFAULT_APPENDERS.add(fileAppender); + } + layout = new TurmsTemplateLayout(nodeType, nodeId); + initialized = true; + + InternalLogger.INSTANCE.init(); + Pair pair; + while ((pair = UNINITIALIZED_LOGGERS.poll()) != null) { + pair.getSecond().setLogger(getLogger(pair.getFirst())); + } + + new LogProcessorRunner(QUEUE).start(); + } + + public static Logger getLogger(String name) { + return getLogger(LoggerOptions.builder() + .loggerName(name) + .shouldParse(true) + .build()); + } + + public static Logger getLogger(Class clazz) { + return getLogger(LoggerOptions.builder() + .loggerClass(clazz) + .shouldParse(true) + .build()); + } + + public static synchronized Logger getLogger(LoggerOptions options) { + if (initialized) { + String loggerName = options.getLoggerName(); + Class loggerClass = options.getLoggerClass(); + if (loggerName == null && loggerClass != null) { + loggerName = loggerClass.getName(); + } + String filePath = options.getFilePath(); + List appenders = new ArrayList<>(2); + if (filePath != null) { + filePath = getFilePath(filePath); + LogLevel level = options.getLevel(); + if (level == null) { + level = fileLoggingProperties.getLevel(); + } + RollingFileAppender appender = new RollingFileAppender(level, + filePath, + fileLoggingProperties.getMaxFiles(), + fileLoggingProperties.getMaxFileSizeMb()); + appenders.add(appender); + ALL_APPENDERS.add(appender); + if (defaultConsoleAppender != null) { + appenders.add(defaultConsoleAppender); + } + } else { + appenders = DEFAULT_APPENDERS; + } + return new AsyncLogger(loggerName, options.isShouldParse(), appenders, layout, QUEUE); + } + // Spring will access this method via SLF4J before we can initialize LoggerFactory + // (because we need to wait Spring to parse the properties file for logging) + // so we need the following code to return a WrappedLogger for them + WrappedLogger logger = new WrappedLogger(); + UNINITIALIZED_LOGGERS.add(Pair.of(options, logger)); + return logger; + } + + public static List getAllAppenders() { + return ALL_APPENDERS; + } + + private static String getFilePath(String path) { + return path + .replace("@HOME", homeDir) + .replace("@SERVICE_TYPE_NAME", serverTypeName); + } + +} diff --git a/turms-server-common/src/main/java/im/turms/server/common/logging/core/logger/LoggerOptions.java b/turms-server-common/src/main/java/im/turms/server/common/logging/core/logger/LoggerOptions.java new file mode 100644 index 0000000000..56322f2001 --- /dev/null +++ b/turms-server-common/src/main/java/im/turms/server/common/logging/core/logger/LoggerOptions.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2019 The Turms Project + * https://github.com/turms-im/turms + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.turms.server.common.logging.core.logger; + +import im.turms.server.common.logging.core.model.LogLevel; +import lombok.Builder; +import lombok.Data; + +/** + * @author James Chen + */ +@Builder(toBuilder = true) +@Data +public final class LoggerOptions { + private final Class loggerClass; + private final String loggerName; + private final LogLevel level; + private final String filePath; + private final boolean shouldParse; +// private final boolean shouldEscape; +} diff --git a/turms-server-common/src/main/java/im/turms/server/common/logging/core/logger/NoOpLogger.java b/turms-server-common/src/main/java/im/turms/server/common/logging/core/logger/NoOpLogger.java new file mode 100644 index 0000000000..a33408e568 --- /dev/null +++ b/turms-server-common/src/main/java/im/turms/server/common/logging/core/logger/NoOpLogger.java @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2019 The Turms Project + * https://github.com/turms-im/turms + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.turms.server.common.logging.core.logger; + +import im.turms.server.common.logging.core.model.LogLevel; + +import javax.annotation.Nullable; + +/** + * @author James Chen + */ +public class NoOpLogger implements Logger { + + public static final NoOpLogger INSTANCE = new NoOpLogger(); + + private NoOpLogger() { + } + + @Override + public boolean isTraceEnabled() { + return false; + } + + @Override + public boolean isDebugEnabled() { + return false; + } + + @Override + public boolean isInfoEnabled() { + return false; + } + + @Override + public boolean isWarnEnabled() { + return false; + } + + @Override + public boolean isErrorEnabled() { + return false; + } + + @Override + public boolean isFatalEnabled() { + return false; + } + + @Override + public boolean isEnabled(LogLevel logLevel) { + return false; + } + + @Override + public void log(LogLevel level, String message) { + } + + @Override + public void log(LogLevel level, String message, Object... objects) { + } + + @Override + public void log(LogLevel level, String message, Throwable throwable) { + } + + @Override + public void debug(String message, Object... args) { + } + + @Override + public void info(String message, Object... args) { + } + + @Override + public void warn(String message) { + } + + @Override + public void warn(String message, Object... args) { + } + + @Override + public void error(String message, @Nullable Throwable throwable) { + } + + @Override + public void error(String message, Object... args) { + } + + @Override + public void fatal(String message, Throwable throwable) { + } + + @Override + public void fatal(String message) { + } +} diff --git a/turms-server-common/src/main/java/im/turms/server/common/logging/core/logger/WrappedLogger.java b/turms-server-common/src/main/java/im/turms/server/common/logging/core/logger/WrappedLogger.java new file mode 100644 index 0000000000..e9f02707e5 --- /dev/null +++ b/turms-server-common/src/main/java/im/turms/server/common/logging/core/logger/WrappedLogger.java @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2019 The Turms Project + * https://github.com/turms-im/turms + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.turms.server.common.logging.core.logger; + +import im.turms.server.common.logging.core.model.LogLevel; +import lombok.Setter; + +import javax.annotation.Nullable; + +/** + * @author James Chen + */ +public class WrappedLogger implements Logger { + + @Setter + private Logger logger = NoOpLogger.INSTANCE; + + @Override + public boolean isTraceEnabled() { + return logger.isTraceEnabled(); + } + + @Override + public boolean isDebugEnabled() { + return logger.isDebugEnabled(); + } + + @Override + public boolean isInfoEnabled() { + return logger.isInfoEnabled(); + } + + @Override + public boolean isWarnEnabled() { + return logger.isWarnEnabled(); + } + + @Override + public boolean isErrorEnabled() { + return logger.isErrorEnabled(); + } + + @Override + public boolean isFatalEnabled() { + return logger.isFatalEnabled(); + } + + @Override + public boolean isEnabled(LogLevel logLevel) { + return logger.isEnabled(logLevel); + } + + @Override + public void log(LogLevel level, String message) { + logger.log(level, message); + } + + @Override + public void log(LogLevel level, String message, Object... objects) { + logger.log(level, message, objects); + } + + @Override + public void log(LogLevel level, String message, Throwable throwable) { + logger.log(level, message, throwable); + } + + @Override + public void debug(String message, Object... args) { + logger.debug(message, args); + } + + @Override + public void info(String message, Object... args) { + logger.info(message, args); + } + + @Override + public void warn(String message) { + logger.warn(message); + } + + @Override + public void warn(String message, Object... args) { + logger.warn(message, args); + } + + @Override + public void error(String message, @Nullable Throwable throwable) { + logger.error(message, throwable); + } + + @Override + public void error(String message, Object... args) { + logger.error(message, args); + } + + @Override + public void fatal(String message, Throwable throwable) { + logger.fatal(message, throwable); + } + + @Override + public void fatal(String message) { + logger.fatal(message); + } +} diff --git a/turms-server-common/src/main/java/im/turms/server/common/logging/core/model/LogLevel.java b/turms-server-common/src/main/java/im/turms/server/common/logging/core/model/LogLevel.java new file mode 100644 index 0000000000..6c2cf62acd --- /dev/null +++ b/turms-server-common/src/main/java/im/turms/server/common/logging/core/model/LogLevel.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2019 The Turms Project + * https://github.com/turms-im/turms + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.turms.server.common.logging.core.model; + +/** + * @author James Chen + */ +public enum LogLevel { + TRACE, + DEBUG, + INFO, + WARN, + ERROR, + FATAL; + + public boolean isLoggable(LogLevel enabledLevel) { + return enabledLevel.ordinal() <= ordinal(); + } + +} diff --git a/turms-server-common/src/main/java/im/turms/server/common/logging/core/model/LogRecord.java b/turms-server-common/src/main/java/im/turms/server/common/logging/core/model/LogRecord.java new file mode 100644 index 0000000000..9645a3cf96 --- /dev/null +++ b/turms-server-common/src/main/java/im/turms/server/common/logging/core/model/LogRecord.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2019 The Turms Project + * https://github.com/turms-im/turms + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.turms.server.common.logging.core.model; + +import im.turms.server.common.logging.core.logger.AsyncLogger; +import io.netty.buffer.ByteBuf; + +/** + * @author James Chen + */ +public record LogRecord( + AsyncLogger logger, + LogLevel level, + long timestamp, + ByteBuf data +) { +} \ No newline at end of file diff --git a/turms-server-common/src/main/java/im/turms/server/common/logging/core/processor/LogProcessorRunner.java b/turms-server-common/src/main/java/im/turms/server/common/logging/core/processor/LogProcessorRunner.java new file mode 100644 index 0000000000..4b08f5a020 --- /dev/null +++ b/turms-server-common/src/main/java/im/turms/server/common/logging/core/processor/LogProcessorRunner.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2019 The Turms Project + * https://github.com/turms-im/turms + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.turms.server.common.logging.core.processor; + +import im.turms.server.common.logging.core.appender.Appender; +import im.turms.server.common.logging.core.idle.BackoffIdleStrategy; +import im.turms.server.common.logging.core.logger.LoggerFactory; +import im.turms.server.common.logging.core.model.LogRecord; +import io.netty.util.concurrent.DefaultThreadFactory; +import org.jctools.queues.MpscUnboundedArrayQueue; + +import java.util.List; + +/** + * @author James Chen + */ +public final class LogProcessorRunner { + + private final Thread thread; + private volatile boolean active; + + public LogProcessorRunner(MpscUnboundedArrayQueue recordQueue) { + Runnable processor = () -> { + BackoffIdleStrategy idleStrategy = new BackoffIdleStrategy(128, 128, 1024000, 1024000); + LogRecord record; + Thread thread = Thread.currentThread(); + while (active && !thread.isInterrupted()) { + while ((record = recordQueue.relaxedPoll()) != null) { + idleStrategy.reset(); + List appenders = record.logger().getAppenders(); + for (Appender appender : appenders) { + try { + appender.append(record); + } catch (Exception e) { + e.printStackTrace(); + } + } + try { + record.data().release(); + } catch (Exception e) { + e.printStackTrace(); + } + } + idleStrategy.idle(); + } + List appenders = LoggerFactory.getAllAppenders(); + for (Appender appender : appenders) { + try { + appender.close(); + } catch (Throwable e) { + e.printStackTrace(); + } + } + }; + thread = new DefaultThreadFactory("turms-log-process") + .newThread(processor); + active = true; + Runtime.getRuntime() + .addShutdownHook(new DefaultThreadFactory("turms-log-shutdown") + .newThread(this::close)); + } + + public void start() { + if (thread.getState() == Thread.State.NEW) { + thread.start(); + } + } + + public void close() { + active = false; + } + +} diff --git a/turms-server-common/src/main/java/im/turms/server/common/logging/slf4j/Slf4jBridge.java b/turms-server-common/src/main/java/im/turms/server/common/logging/slf4j/Slf4jBridge.java new file mode 100644 index 0000000000..43981a2174 --- /dev/null +++ b/turms-server-common/src/main/java/im/turms/server/common/logging/slf4j/Slf4jBridge.java @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2019 The Turms Project + * https://github.com/turms-im/turms + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.turms.server.common.logging.slf4j; + +import im.turms.server.common.logging.core.model.LogLevel; +import im.turms.server.common.logging.core.logger.Logger; +import org.slf4j.helpers.MarkerIgnoringBase; + +public class Slf4jBridge extends MarkerIgnoringBase { + + private final Logger logger; + + public Slf4jBridge(String name, Logger logger) { + this.name = name; + this.logger = logger; + } + + @Override + public boolean isTraceEnabled() { + return logger.isTraceEnabled(); + } + + @Override + public void trace(String message) { + logger.log(LogLevel.TRACE, message); + } + + @Override + public void trace(String message, Object object) { + logger.log(LogLevel.TRACE, message, object); + } + + @Override + public void trace(String message, Object object1, Object object2) { + logger.log(LogLevel.TRACE, message, object1, object2); + } + + @Override + public void trace(String message, Object[] objects) { + logger.log(LogLevel.TRACE, message, objects); + } + + @Override + public void trace(String message, Throwable exception) { + logger.log(LogLevel.TRACE, message, exception); + } + + @Override + public boolean isDebugEnabled() { + return logger.isDebugEnabled(); + } + + @Override + public void debug(String message) { + logger.log(LogLevel.DEBUG, message); + } + + @Override + public void debug(String message, Object object) { + logger.log(LogLevel.DEBUG, message, object); + } + + @Override + public void debug(String message, Object object1, Object object2) { + logger.log(LogLevel.DEBUG, message, object1, object2); + } + + @Override + public void debug(String message, Object[] objects) { + logger.log(LogLevel.DEBUG, message, objects); + } + + @Override + public void debug(String message, Throwable exception) { + logger.log(LogLevel.DEBUG, message, exception); + } + + @Override + public boolean isInfoEnabled() { + return logger.isInfoEnabled(); + } + + @Override + public void info(String message) { + logger.log(LogLevel.INFO, message); + } + + @Override + public void info(String message, Object object) { + logger.log(LogLevel.INFO, message, object); + } + + @Override + public void info(String message, Object object1, Object object2) { + logger.log(LogLevel.INFO, message, object1, object2); + } + + @Override + public void info(String message, Object[] objects) { + logger.log(LogLevel.INFO, message, objects); + } + + @Override + public void info(String message, Throwable exception) { + logger.log(LogLevel.INFO, message, exception); + } + + @Override + public boolean isWarnEnabled() { + return logger.isWarnEnabled(); + } + + @Override + public void warn(String message) { + logger.log(LogLevel.WARN, message); + } + + @Override + public void warn(String message, Object object) { + logger.log(LogLevel.WARN, message, object); + } + + @Override + public void warn(String message, Object object1, Object object2) { + logger.log(LogLevel.WARN, message, object1, object2); + } + + @Override + public void warn(String message, Object[] objects) { + logger.log(LogLevel.WARN, message, objects); + } + + @Override + public void warn(String message, Throwable exception) { + logger.log(LogLevel.WARN, message, exception); + } + + @Override + public boolean isErrorEnabled() { + return logger.isErrorEnabled(); + } + + @Override + public void error(String message) { + logger.log(LogLevel.ERROR, message); + } + + @Override + public void error(String message, Object object) { + logger.log(LogLevel.ERROR, message, object); + } + + @Override + public void error(String message, Object object1, Object object2) { + logger.log(LogLevel.ERROR, message, object1, object2); + } + + @Override + public void error(String message, Object[] objects) { + logger.log(LogLevel.ERROR, message, objects); + } + + @Override + public void error(String message, Throwable exception) { + logger.log(LogLevel.ERROR, message, exception); + } + +} \ No newline at end of file diff --git a/turms-server-common/src/main/java/im/turms/server/common/logging/slf4j/Slf4jBridgeFactory.java b/turms-server-common/src/main/java/im/turms/server/common/logging/slf4j/Slf4jBridgeFactory.java new file mode 100644 index 0000000000..905a8f8ba7 --- /dev/null +++ b/turms-server-common/src/main/java/im/turms/server/common/logging/slf4j/Slf4jBridgeFactory.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2019 The Turms Project + * https://github.com/turms-im/turms + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.turms.server.common.logging.slf4j; + +import im.turms.server.common.logging.core.logger.LoggerFactory; +import im.turms.server.common.logging.core.logger.Logger; +import org.slf4j.ILoggerFactory; + +import java.util.concurrent.ConcurrentHashMap; + + +public final class Slf4jBridgeFactory implements ILoggerFactory { + + private final ConcurrentHashMap loggerMap = new ConcurrentHashMap<>(); + + @Override + public org.slf4j.Logger getLogger(String name) { + return loggerMap.computeIfAbsent(name, n -> { + Logger logger = LoggerFactory.getLogger(name); + return new Slf4jBridge(name, logger); + }); + } + +} diff --git a/turms-server-common/src/main/java/im/turms/server/common/mongo/codec/EntityCodec.java b/turms-server-common/src/main/java/im/turms/server/common/mongo/codec/EntityCodec.java index c4ea4e6d5b..f0a7cf12cb 100644 --- a/turms-server-common/src/main/java/im/turms/server/common/mongo/codec/EntityCodec.java +++ b/turms-server-common/src/main/java/im/turms/server/common/mongo/codec/EntityCodec.java @@ -17,10 +17,11 @@ package im.turms.server.common.mongo.codec; +import im.turms.server.common.logging.core.logger.LoggerFactory; +import im.turms.server.common.logging.core.logger.Logger; import im.turms.server.common.mongo.MongoContext; import im.turms.server.common.mongo.entity.EntityField; import im.turms.server.common.mongo.entity.MongoEntity; -import lombok.extern.log4j.Log4j2; import org.apache.commons.lang3.ClassUtils; import org.bson.BsonReader; import org.bson.BsonType; @@ -39,9 +40,10 @@ * @author James Chen * @see org.bson.codecs.pojo.PojoCodecImpl */ -@Log4j2 public class EntityCodec implements Codec { + private static final Logger LOGGER = LoggerFactory.getLogger(EntityCodec.class); + private static final String ID_FIELD = "_id"; private final CodecRegistry registry; @@ -134,7 +136,7 @@ private void initInstance(T instance, BsonReader reader, DecoderContext decoderC throw new IllegalStateException(message, e); } } else { - log.warn("Found property {} not present in the entity {}", fieldName, entity.collectionName()); + LOGGER.warn("Found property {} not present in the entity {}", fieldName, entity.collectionName()); reader.skipValue(); } } @@ -165,7 +167,7 @@ private Object[] parseCtorValues(BsonReader reader, DecoderContext decoderContex } values[field.getCtorParamIndex()] = value; } else { - log.warn("Found property not present in the entity: " + fieldName); + LOGGER.warn("Found property not present in the entity: " + fieldName); reader.skipValue(); } } diff --git a/turms-server-common/src/main/java/im/turms/server/common/mongo/codec/EntityCodecProvider.java b/turms-server-common/src/main/java/im/turms/server/common/mongo/codec/EntityCodecProvider.java index 7a0d9d360a..7d16d0c1fe 100644 --- a/turms-server-common/src/main/java/im/turms/server/common/mongo/codec/EntityCodecProvider.java +++ b/turms-server-common/src/main/java/im/turms/server/common/mongo/codec/EntityCodecProvider.java @@ -18,7 +18,6 @@ package im.turms.server.common.mongo.codec; import lombok.Data; -import lombok.extern.log4j.Log4j2; import org.bson.codecs.Codec; import org.bson.codecs.configuration.CodecProvider; import org.bson.codecs.configuration.CodecRegistry; @@ -31,7 +30,6 @@ * @see org.bson.codecs.pojo.PojoCodecProvider */ @Data -@Log4j2 public class EntityCodecProvider implements CodecProvider { private final Map, EntityCodec> codecs = new IdentityHashMap<>(64); diff --git a/turms-server-common/src/main/java/im/turms/server/common/mongo/operation/TurmsMongoOperations.java b/turms-server-common/src/main/java/im/turms/server/common/mongo/operation/TurmsMongoOperations.java index 2e28c09990..e3c6f85a4e 100644 --- a/turms-server-common/src/main/java/im/turms/server/common/mongo/operation/TurmsMongoOperations.java +++ b/turms-server-common/src/main/java/im/turms/server/common/mongo/operation/TurmsMongoOperations.java @@ -43,6 +43,8 @@ import com.mongodb.reactivestreams.client.internal.MongoCollectionUtil; import com.mongodb.reactivestreams.client.internal.MongoOperationPublisher; import com.mongodb.reactivestreams.client.internal.TurmsFindPublisherImpl; +import im.turms.server.common.logging.core.logger.Logger; +import im.turms.server.common.logging.core.logger.LoggerFactory; import im.turms.server.common.mongo.BsonPool; import im.turms.server.common.mongo.MongoContext; import im.turms.server.common.mongo.entity.MongoEntity; @@ -51,7 +53,6 @@ import im.turms.server.common.mongo.operation.option.QueryOptions; import im.turms.server.common.mongo.operation.option.Update; import im.turms.server.common.util.CollectorUtil; -import lombok.extern.log4j.Log4j2; import org.apache.commons.lang3.StringUtils; import org.bson.BsonDocument; import org.bson.BsonDocumentWriter; @@ -81,9 +82,10 @@ * 2. The publishers of mongo-java-driver are cold * and the publishers of TurmsMongoOperations are also cold */ -@Log4j2 public class TurmsMongoOperations implements MongoOperationsSupport { + private static final Logger LOGGER = LoggerFactory.getLogger(TurmsMongoOperations.class); + private static final EncoderContext DEFAULT_ENCODER_CONTEXT = EncoderContext.builder().build(); private static final BsonDocument ID_ONLY = new BsonDocument("_id", BsonPool.BSON_INT32_1); @@ -383,14 +385,14 @@ public Mono ensureIndexes(Class clazz, List indexModels String collectionName = context.getEntity(clazz).collectionName(); return Flux.from(source) .then() - .doOnError(throwable -> log.error("Failed to index the collection {}", collectionName, throwable)) + .doOnError(throwable -> LOGGER.error("Failed to index the collection {}", collectionName, throwable)) .doOnSuccess(ignored -> { List indexDocs = indexModels .stream() .map(indexModel -> indexModel.getKeys().toBsonDocument()) .collect(CollectorUtil.toList(indexModels.size())); String indexes = StringUtils.join(indexDocs, ", "); - log.info("Indexing the collection {} successfully: [{}]", collectionName, indexes); + LOGGER.info("Indexed the collection {} successfully: [{}]", collectionName, indexes); }); } @@ -407,8 +409,8 @@ public Flux listIndexes(Class clazz) { public Mono enableSharding(MongoDatabase databaseToShard, MongoDatabase adminDatabase) { String dbName = databaseToShard.getName(); Mono enableSharding = Mono.from(adminDatabase.runCommand(new Document("enableSharding", dbName))) - .doOnError(throwable -> log.error("Failed to enable sharding the database {}", dbName, throwable)) - .doOnSuccess(ignored -> log.info("Enable sharding the database {} successfully", dbName)); + .doOnError(throwable -> LOGGER.error("Failed to enable sharding the database {}", dbName, throwable)) + .doOnSuccess(ignored -> LOGGER.info("Enabled sharding the database {} successfully", dbName)); return enableSharding.then(); } @@ -423,9 +425,9 @@ public Mono shard(MongoDatabase databaseToShard, MongoDatabase adminDataba Document command = new Document("shardCollection", namespace).append("key", shardKey); Mono shardCollection = Mono.from(adminDatabase.runCommand(command)) .doOnError(throwable -> - log.error("Failed to shard the collection {} with the shard key {}", namespace, shardKey.toJson(), throwable)) + LOGGER.error("Failed to shard the collection {} with the shard key {}", namespace, shardKey.toJson(), throwable)) .doOnSuccess(ignored -> - log.info("Shard the collection {} with the shard key {} successfully", namespace, shardKey.toJson())); + LOGGER.info("Sharded the collection {} with the shard key {} successfully", namespace, shardKey.toJson())); return shardCollection.then(); } diff --git a/turms-server-common/src/main/java/im/turms/server/common/plugin/AbstractTurmsPluginManager.java b/turms-server-common/src/main/java/im/turms/server/common/plugin/AbstractTurmsPluginManager.java index 1fa08440a0..eda216fd1c 100644 --- a/turms-server-common/src/main/java/im/turms/server/common/plugin/AbstractTurmsPluginManager.java +++ b/turms-server-common/src/main/java/im/turms/server/common/plugin/AbstractTurmsPluginManager.java @@ -18,9 +18,10 @@ package im.turms.server.common.plugin; import im.turms.server.common.context.TurmsApplicationContext; +import im.turms.server.common.logging.core.logger.LoggerFactory; +import im.turms.server.common.logging.core.logger.Logger; import im.turms.server.common.property.TurmsPropertiesManager; import lombok.Getter; -import lombok.extern.log4j.Log4j2; import org.springframework.context.ApplicationContext; import javax.annotation.Nullable; @@ -31,9 +32,10 @@ /** * @author James Chen */ -@Log4j2 public abstract class AbstractTurmsPluginManager { + private static final Logger LOGGER = LoggerFactory.getLogger(AbstractTurmsPluginManager.class); + @Getter private final boolean enabled; @Nullable @@ -67,7 +69,7 @@ public void destroy() { try { pluginManager.stopExtension(extension); } catch (Exception e) { - log.error("Caught an exception when stopping the extension " + extension.getClass().getName(), e); + LOGGER.error("Caught an exception when stopping the extension " + extension.getClass().getName(), e); } } } diff --git a/turms-server-common/src/main/java/im/turms/server/common/plugin/PluginManager.java b/turms-server-common/src/main/java/im/turms/server/common/plugin/PluginManager.java index f87ef11232..fd94d4224d 100644 --- a/turms-server-common/src/main/java/im/turms/server/common/plugin/PluginManager.java +++ b/turms-server-common/src/main/java/im/turms/server/common/plugin/PluginManager.java @@ -18,7 +18,6 @@ package im.turms.server.common.plugin; import lombok.SneakyThrows; -import lombok.extern.log4j.Log4j2; import org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor; import org.springframework.context.ApplicationContext; @@ -31,7 +30,6 @@ /** * @author James Chen */ -@Log4j2 public class PluginManager { private final PluginRepository pluginRepository = new PluginRepository(); diff --git a/turms-server-common/src/main/java/im/turms/server/common/util/PropertiesUtil.java b/turms-server-common/src/main/java/im/turms/server/common/property/PropertiesUtil.java similarity index 99% rename from turms-server-common/src/main/java/im/turms/server/common/util/PropertiesUtil.java rename to turms-server-common/src/main/java/im/turms/server/common/property/PropertiesUtil.java index 3d319ca0bc..4b42d17752 100644 --- a/turms-server-common/src/main/java/im/turms/server/common/util/PropertiesUtil.java +++ b/turms-server-common/src/main/java/im/turms/server/common/property/PropertiesUtil.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package im.turms.server.common.util; +package im.turms.server.common.property; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonInclude; @@ -33,10 +33,10 @@ import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; -import im.turms.server.common.property.TurmsProperties; import im.turms.server.common.property.metadata.annotation.Description; import im.turms.server.common.property.metadata.annotation.GlobalProperty; import im.turms.server.common.property.metadata.view.MutablePropertiesView; +import im.turms.server.common.util.MapUtil; import org.apache.commons.lang3.reflect.FieldUtils; import org.yaml.snakeyaml.DumperOptions; import org.yaml.snakeyaml.Yaml; diff --git a/turms-server-common/src/main/java/im/turms/server/common/property/TurmsProperties.java b/turms-server-common/src/main/java/im/turms/server/common/property/TurmsProperties.java index 97ffd100eb..68559d8582 100644 --- a/turms-server-common/src/main/java/im/turms/server/common/property/TurmsProperties.java +++ b/turms-server-common/src/main/java/im/turms/server/common/property/TurmsProperties.java @@ -19,7 +19,7 @@ import com.fasterxml.jackson.annotation.JsonView; import im.turms.server.common.property.env.common.IpProperties; -import im.turms.server.common.property.env.common.LoggingProperties; +import im.turms.server.common.property.env.common.logging.LoggingProperties; import im.turms.server.common.property.env.common.PluginProperties; import im.turms.server.common.property.env.common.UserStatusProperties; import im.turms.server.common.property.env.common.cluster.ClusterProperties; diff --git a/turms-server-common/src/main/java/im/turms/server/common/property/TurmsPropertiesManager.java b/turms-server-common/src/main/java/im/turms/server/common/property/TurmsPropertiesManager.java index ee5a754b5d..ad71b6c2a7 100644 --- a/turms-server-common/src/main/java/im/turms/server/common/property/TurmsPropertiesManager.java +++ b/turms-server-common/src/main/java/im/turms/server/common/property/TurmsPropertiesManager.java @@ -19,9 +19,9 @@ import im.turms.server.common.cluster.node.Node; import im.turms.server.common.context.TurmsApplicationContext; -import im.turms.server.common.util.PropertiesUtil; +import im.turms.server.common.logging.core.logger.LoggerFactory; +import im.turms.server.common.logging.core.logger.Logger; import lombok.Setter; -import lombok.extern.log4j.Log4j2; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; import reactor.core.publisher.Mono; @@ -38,10 +38,11 @@ * * @author James Chen */ -@Log4j2 @Component public class TurmsPropertiesManager { + private static final Logger LOGGER = LoggerFactory.getLogger(TurmsPropertiesManager.class); + public final List> propertiesChangeListeners = new LinkedList<>(); private static final TurmsProperties DEFAULT_PROPERTIES = new TurmsProperties(); @@ -63,7 +64,7 @@ public TurmsPropertiesManager(@Lazy Node node, TurmsProperties localTurmsPropert // The property should be passed from "bin/run.sh" String configDir = System.getProperty("spring.config.location"); if (configDir == null || configDir.isBlank()) { - log.warn("The property \"spring.config.location\" is empty"); + LOGGER.warn("The property \"spring.config.location\" is empty"); configDir = "./config"; } String latestConfigFileName = activeProfile != null @@ -108,7 +109,7 @@ public void updateLocalProperties( localTurmsProperties = newLocalProperties; PropertiesUtil.persist(latestConfigFilePath, newPropertiesStr); } catch (IOException e) { - log.error("Failed to persist new turms properties", e); + LOGGER.error("Failed to persist new turms properties", e); } notifyListeners(newLocalProperties); } @@ -137,7 +138,7 @@ public void notifyListeners(TurmsProperties properties) { try { listener.accept(properties); } catch (Exception e) { - log.error("The properties listener {} failed to handle the new properties", listener.getClass().getName(), e); + LOGGER.error("The properties listener {} failed to handle the new properties", listener.getClass().getName(), e); } } } diff --git a/turms-server-common/src/main/java/im/turms/server/common/property/env/common/cluster/NodeProperties.java b/turms-server-common/src/main/java/im/turms/server/common/property/env/common/cluster/NodeProperties.java index a158e69fc2..8dca17bdb5 100644 --- a/turms-server-common/src/main/java/im/turms/server/common/property/env/common/cluster/NodeProperties.java +++ b/turms-server-common/src/main/java/im/turms/server/common/property/env/common/cluster/NodeProperties.java @@ -44,7 +44,7 @@ public class NodeProperties { private boolean activeByDefault = true; - @Description("Only works when it's a service node") + @Description("Only works when it's a turms-service node") private boolean leaderEligible = true; } \ No newline at end of file diff --git a/turms-server-common/src/main/java/im/turms/server/common/property/env/common/logging/ConsoleLoggingProperties.java b/turms-server-common/src/main/java/im/turms/server/common/property/env/common/logging/ConsoleLoggingProperties.java new file mode 100644 index 0000000000..66c3a13c49 --- /dev/null +++ b/turms-server-common/src/main/java/im/turms/server/common/property/env/common/logging/ConsoleLoggingProperties.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2019 The Turms Project + * https://github.com/turms-im/turms + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.turms.server.common.property.env.common.logging; + +import im.turms.server.common.logging.core.model.LogLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author James Chen + */ +@AllArgsConstructor +@Builder(toBuilder = true) +@Data +@NoArgsConstructor +public class ConsoleLoggingProperties { + + public static final boolean DEFAULT_VALUE_ENABLED = false; + public static final LogLevel DEFAULT_VALUE_LEVEL = LogLevel.INFO; + + private boolean enabled = DEFAULT_VALUE_ENABLED; + + private LogLevel level = DEFAULT_VALUE_LEVEL; + +} \ No newline at end of file diff --git a/turms-server-common/src/main/java/im/turms/server/common/property/env/common/logging/FileLoggingProperties.java b/turms-server-common/src/main/java/im/turms/server/common/property/env/common/logging/FileLoggingProperties.java new file mode 100644 index 0000000000..f7a14b5c0b --- /dev/null +++ b/turms-server-common/src/main/java/im/turms/server/common/property/env/common/logging/FileLoggingProperties.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2019 The Turms Project + * https://github.com/turms-im/turms + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.turms.server.common.property.env.common.logging; + +import im.turms.server.common.logging.core.model.LogLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author James Chen + */ +@AllArgsConstructor +@Builder(toBuilder = true) +@Data +@NoArgsConstructor +public class FileLoggingProperties { + + public static final boolean DEFAULT_VALUE_ENABLED = true; + public static final LogLevel DEFAULT_VALUE_LEVEL = LogLevel.INFO; + public static final String DEFAULT_VALUE_FILE_PATH = "@HOME/@SERVICE_TYPE_NAME.log"; + public static final int DEFAULT_VALUE_MAX_FILES = 32; + public static final int DEFAULT_VALUE_FILE_SIZE_MB = 32; + + private boolean enabled = DEFAULT_VALUE_ENABLED; + + private LogLevel level = DEFAULT_VALUE_LEVEL; + + private String filePath = DEFAULT_VALUE_FILE_PATH; + private int maxFiles = 32; + private int maxFileSizeMb = 32; + +} \ No newline at end of file diff --git a/turms-server-common/src/main/java/im/turms/server/common/property/env/common/LoggingProperties.java b/turms-server-common/src/main/java/im/turms/server/common/property/env/common/logging/LoggingProperties.java similarity index 70% rename from turms-server-common/src/main/java/im/turms/server/common/property/env/common/LoggingProperties.java rename to turms-server-common/src/main/java/im/turms/server/common/property/env/common/logging/LoggingProperties.java index 09d1ecc102..af6c04b080 100644 --- a/turms-server-common/src/main/java/im/turms/server/common/property/env/common/LoggingProperties.java +++ b/turms-server-common/src/main/java/im/turms/server/common/property/env/common/logging/LoggingProperties.java @@ -15,12 +15,13 @@ * limitations under the License. */ -package im.turms.server.common.property.env.common; +package im.turms.server.common.property.env.common.logging; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; +import org.springframework.boot.context.properties.NestedConfigurationProperty; /** * @author James Chen @@ -31,11 +32,10 @@ @NoArgsConstructor public class LoggingProperties { - public static final boolean ENABLE_CONSOLE_APPENDER_DEFAULT_VALUE = false; - public static final boolean ENABLE_FILE_APPENDER_DEFAULT_VALUE = true; + @NestedConfigurationProperty + private ConsoleLoggingProperties console = new ConsoleLoggingProperties(); - private boolean enableConsoleAppender = ENABLE_CONSOLE_APPENDER_DEFAULT_VALUE; - - private boolean enableFileAppender = ENABLE_FILE_APPENDER_DEFAULT_VALUE; + @NestedConfigurationProperty + private FileLoggingProperties file = new FileLoggingProperties(); } \ No newline at end of file diff --git a/turms-server-common/src/main/java/im/turms/server/common/property/env/service/env/redis/TurmsRedisProperties.java b/turms-server-common/src/main/java/im/turms/server/common/property/env/service/env/redis/TurmsRedisProperties.java index fd09cf2aef..fac5e0c528 100644 --- a/turms-server-common/src/main/java/im/turms/server/common/property/env/service/env/redis/TurmsRedisProperties.java +++ b/turms-server-common/src/main/java/im/turms/server/common/property/env/service/env/redis/TurmsRedisProperties.java @@ -20,7 +20,6 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import im.turms.server.common.property.env.common.CommonRedisProperties; import im.turms.server.common.redis.RedisProperties; -import im.turms.server.common.redis.SimpleRedisProperties; import lombok.AllArgsConstructor; import lombok.Data; import lombok.EqualsAndHashCode; diff --git a/turms-server-common/src/main/java/im/turms/server/common/redis/CommonRedisConfig.java b/turms-server-common/src/main/java/im/turms/server/common/redis/CommonRedisConfig.java index 7e9acf712b..408c2a480d 100644 --- a/turms-server-common/src/main/java/im/turms/server/common/redis/CommonRedisConfig.java +++ b/turms-server-common/src/main/java/im/turms/server/common/redis/CommonRedisConfig.java @@ -17,9 +17,10 @@ package im.turms.server.common.redis; +import im.turms.server.common.logging.core.logger.LoggerFactory; +import im.turms.server.common.logging.core.logger.Logger; import im.turms.server.common.property.env.common.CommonRedisProperties; import im.turms.server.common.redis.codec.context.RedisCodecContext; -import lombok.extern.log4j.Log4j2; import org.apache.commons.collections4.ListUtils; import org.springframework.context.annotation.Bean; @@ -38,9 +39,10 @@ * @see org.springframework.boot.autoconfigure.data.redis.RedisConnectionConfiguration * @see org.springframework.boot.autoconfigure.data.redis.LettuceConnectionConfiguration */ -@Log4j2 public abstract class CommonRedisConfig { + private static final Logger LOGGER = LoggerFactory.getLogger(CommonRedisConfig.class); + private final TurmsRedisClientManager sessionRedisClientManager; private final TurmsRedisClientManager locationRedisClientManager; @@ -91,14 +93,14 @@ public void destroy() { try { manager.destroy(); } catch (Exception e) { - log.error("Failed to destroy a redis client", e); + LOGGER.error("Failed to destroy a redis client", e); } } for (TurmsRedisClient client : ListUtils.union(registeredClients, List.of(ipBlocklistRedisClient, userIdBlocklistRedisClient))) { try { client.destroy(); } catch (Exception e) { - log.error("Failed to destroy a redis client", e); + LOGGER.error("Failed to destroy a redis client", e); } } } diff --git a/turms-server-common/src/main/java/im/turms/server/common/redis/TurmsRedisClient.java b/turms-server-common/src/main/java/im/turms/server/common/redis/TurmsRedisClient.java index 6e45247a43..059a49cca5 100644 --- a/turms-server-common/src/main/java/im/turms/server/common/redis/TurmsRedisClient.java +++ b/turms-server-common/src/main/java/im/turms/server/common/redis/TurmsRedisClient.java @@ -44,7 +44,6 @@ import io.netty.util.concurrent.DefaultEventExecutorGroup; import io.netty.util.concurrent.DefaultThreadFactory; import lombok.Data; -import lombok.extern.log4j.Log4j2; import org.springframework.data.geo.Point; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -62,7 +61,6 @@ * because it hasn't flushed the buffers, * @see AbstractRedisReactiveCommands */ -@Log4j2 @Data public class TurmsRedisClient { diff --git a/turms-server-common/src/main/java/im/turms/server/common/redis/TurmsRedisClientManager.java b/turms-server-common/src/main/java/im/turms/server/common/redis/TurmsRedisClientManager.java index a5bec15ee0..c29bbbc0e7 100644 --- a/turms-server-common/src/main/java/im/turms/server/common/redis/TurmsRedisClientManager.java +++ b/turms-server-common/src/main/java/im/turms/server/common/redis/TurmsRedisClientManager.java @@ -19,6 +19,8 @@ import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ListMultimap; +import im.turms.server.common.logging.core.logger.LoggerFactory; +import im.turms.server.common.logging.core.logger.Logger; import im.turms.server.common.redis.codec.context.RedisCodecContext; import im.turms.server.common.redis.script.RedisScript; import im.turms.server.common.redis.sharding.ShardingAlgorithm; @@ -27,7 +29,6 @@ import io.lettuce.core.GeoCoordinates; import io.lettuce.core.GeoWithin; import io.netty.buffer.ByteBuf; -import lombok.extern.log4j.Log4j2; import org.springframework.data.geo.Point; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -44,9 +45,10 @@ /** * @author James Chen */ -@Log4j2 public class TurmsRedisClientManager { + private static final Logger LOGGER = LoggerFactory.getLogger(TurmsRedisClientManager.class); + private final List clients; private final ShardingAlgorithm shardingAlgorithm; @@ -65,7 +67,7 @@ public void destroy() { try { client.destroy(); } catch (Exception e) { - log.error("Failed to shutdown a connection", e); + LOGGER.error("Failed to shutdown a connection", e); } } } diff --git a/turms-server-common/src/main/java/im/turms/server/common/rpc/codec/request/RpcRequestCodec.java b/turms-server-common/src/main/java/im/turms/server/common/rpc/codec/request/RpcRequestCodec.java index 10e4e58d99..20533c9151 100644 --- a/turms-server-common/src/main/java/im/turms/server/common/rpc/codec/request/RpcRequestCodec.java +++ b/turms-server-common/src/main/java/im/turms/server/common/rpc/codec/request/RpcRequestCodec.java @@ -41,10 +41,11 @@ public int initialCapacity(T data) { @Override public void write(ByteBuf output, T data) { TracingContext tracingContext = data.getTracingContext(); - if (!tracingContext.hasTraceId()) { + long traceId = tracingContext.getTraceId(); + if (traceId == TracingContext.UNDEFINED_TRACE_ID) { throw new IllegalArgumentException("The trace ID is missing in the request: " + data.name()); } - output.writeLong(tracingContext.getTraceId()); + output.writeLong(traceId); writeRequestData(output, data); } diff --git a/turms-server-common/src/main/java/im/turms/server/common/rpc/request/SendNotificationRequest.java b/turms-server-common/src/main/java/im/turms/server/common/rpc/request/SendNotificationRequest.java index 4e65f54ee5..c6e6004fd7 100644 --- a/turms-server-common/src/main/java/im/turms/server/common/rpc/request/SendNotificationRequest.java +++ b/turms-server-common/src/main/java/im/turms/server/common/rpc/request/SendNotificationRequest.java @@ -82,7 +82,7 @@ public void setApplicationContext(ApplicationContext applicationContext) { */ @Override public Boolean call() { - return outboundMessageService.sendNotificationToLocalClients(getTracingContext(), notificationBuffer, recipientIds); + return outboundMessageService.sendNotificationToLocalClients(this.getTracingContext(), notificationBuffer, recipientIds); } } diff --git a/turms-server-common/src/main/java/im/turms/server/common/rpc/service/IOutboundMessageService.java b/turms-server-common/src/main/java/im/turms/server/common/rpc/service/IOutboundMessageService.java index 10107ad557..4f00d042c5 100644 --- a/turms-server-common/src/main/java/im/turms/server/common/rpc/service/IOutboundMessageService.java +++ b/turms-server-common/src/main/java/im/turms/server/common/rpc/service/IOutboundMessageService.java @@ -30,7 +30,7 @@ public interface IOutboundMessageService { boolean sendNotificationToLocalClients( - @NotNull TracingContext tracingContext, + @NotNull TracingContext context, @NotNull ByteBuf notificationData, @NotEmpty Set recipientIds); diff --git a/turms-server-common/src/main/java/im/turms/server/common/rpc/service/IServiceRequestDispatcher.java b/turms-server-common/src/main/java/im/turms/server/common/rpc/service/IServiceRequestDispatcher.java index 01b22a8b02..da883a7661 100644 --- a/turms-server-common/src/main/java/im/turms/server/common/rpc/service/IServiceRequestDispatcher.java +++ b/turms-server-common/src/main/java/im/turms/server/common/rpc/service/IServiceRequestDispatcher.java @@ -27,6 +27,6 @@ */ public interface IServiceRequestDispatcher { - Mono dispatch(TracingContext tracingContext, ServiceRequest serviceRequest); + Mono dispatch(TracingContext context, ServiceRequest serviceRequest); } diff --git a/turms-server-common/src/main/java/im/turms/server/common/service/blocklist/BlocklistServiceManager.java b/turms-server-common/src/main/java/im/turms/server/common/service/blocklist/BlocklistServiceManager.java index 189d3c166b..f01d409639 100644 --- a/turms-server-common/src/main/java/im/turms/server/common/service/blocklist/BlocklistServiceManager.java +++ b/turms-server-common/src/main/java/im/turms/server/common/service/blocklist/BlocklistServiceManager.java @@ -22,6 +22,8 @@ import im.turms.server.common.constant.TurmsStatusCode; import im.turms.server.common.exception.TurmsBusinessException; import im.turms.server.common.lang.ByteArrayWrapper; +import im.turms.server.common.logging.core.logger.LoggerFactory; +import im.turms.server.common.logging.core.logger.Logger; import im.turms.server.common.redis.TurmsRedisClient; import im.turms.server.common.redis.script.RedisScript; import im.turms.server.common.util.ByteBufUtil; @@ -29,12 +31,12 @@ import io.netty.buffer.ByteBufAllocator; import io.netty.buffer.PooledByteBufAllocator; import io.netty.buffer.Unpooled; -import lombok.extern.log4j.Log4j2; import org.springframework.util.CollectionUtils; import reactor.core.publisher.Mono; import java.time.Duration; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.LinkedList; @@ -75,9 +77,10 @@ * 3. "blocklist:ip:log": List. Used to perform delta sync * 4. "blocklist:ip:log_id": Counter. Used to perform delta sync */ -@Log4j2 public class BlocklistServiceManager { + private static final Logger LOGGER = LoggerFactory.getLogger(BlocklistServiceManager.class); + /** * 2021-08-27T00:00:00.00Z */ @@ -173,7 +176,7 @@ public BlocklistServiceManager( try { syncLocalBlocklist().subscribe(); } catch (Exception e) { - log.error("Caught an error when synchronizing blocklist"); + LOGGER.error("Caught an error when synchronizing blocklist"); } }, syncIntervalMillis, syncIntervalMillis, TimeUnit.MILLISECONDS); } @@ -536,7 +539,7 @@ private void triggerOnTargetBlocked(T id) { try { onTargetBlocked.accept(id); } catch (Exception e) { - log.error("onTargetBlocked failed to handle the blocked target: " + id); + LOGGER.error("onTargetBlocked failed to handle the blocked target: " + id); } } diff --git a/turms-server-common/src/main/java/im/turms/server/common/tracing/TracingCloseableContext.java b/turms-server-common/src/main/java/im/turms/server/common/tracing/TracingCloseableContext.java index fea87c52b5..a5f109de71 100644 --- a/turms-server-common/src/main/java/im/turms/server/common/tracing/TracingCloseableContext.java +++ b/turms-server-common/src/main/java/im/turms/server/common/tracing/TracingCloseableContext.java @@ -17,20 +17,21 @@ package im.turms.server.common.tracing; -import java.io.Closeable; - /** * @author James Chen */ -public record TracingCloseableContext(TracingContext tracingContext) implements Closeable { +public class TracingCloseableContext implements AutoCloseable { + + private final TracingContext context; - public TracingCloseableContext { - tracingContext.updateMdc(); + public TracingCloseableContext(TracingContext context) { + this.context = context; + context.updateThreadContext(); } @Override public void close() { - tracingContext.clearMdc(); + context.clearThreadContext(); } } diff --git a/turms-server-common/src/main/java/im/turms/server/common/tracing/TracingContext.java b/turms-server-common/src/main/java/im/turms/server/common/tracing/TracingContext.java index ad3fb36f78..c96c6ca28b 100644 --- a/turms-server-common/src/main/java/im/turms/server/common/tracing/TracingContext.java +++ b/turms-server-common/src/main/java/im/turms/server/common/tracing/TracingContext.java @@ -18,71 +18,68 @@ package im.turms.server.common.tracing; import im.turms.common.util.RandomUtil; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.ToString; -import lombok.experimental.FieldNameConstants; -import org.apache.logging.log4j.ThreadContext; +import im.turms.server.common.logging.core.context.LogThreadContext; +import lombok.Getter; +import lombok.Setter; import reactor.core.scheduler.Schedulers; +import reactor.util.context.ContextView; /** * @author James Chen */ -@Data -@EqualsAndHashCode(onlyExplicitlyIncluded = true) -@FieldNameConstants -@ToString(onlyExplicitlyIncluded = true) public class TracingContext { - public static final String SCHEDULE_HOOK_NAME = "TRACING"; + public static final long UNDEFINED_TRACE_ID = -1; - public static final TracingContext NOOP = new TracingContext(0) { + public static final TracingContext DEFAULT = new TracingContext(); + public static final String CTX_KEY_NAME = "REQ"; + public static final String SCHEDULE_HOOK_NAME = "TRACING"; + public static final TracingContext NOOP = new TracingContext(UNDEFINED_TRACE_ID) { @Override - public String getTraceIdStr() { - return super.getTraceIdStr(); + public void updateThreadContext() { } @Override - public boolean hasTraceId() { - return false; + public void clearThreadContext() { } @Override - public void updateMdc() { + public long getTraceId() { + return UNDEFINED_TRACE_ID; } @Override - public void clearMdc() { + public void setTraceId(long traceId) { } }; static { Schedulers.onScheduleHook(SCHEDULE_HOOK_NAME, task -> { - String traceId = ThreadContext.get(Fields.traceId); - // Make sure the traceId is removed on the current thread - // because the traceId will be misused if the current thread is scheduled to handle other requests - if (traceId != null) { - ThreadContext.remove(Fields.traceId); - } + TracingContext context = LogThreadContext.removeAndGet(); + // Make sure the request context is removed on the current thread + // because the request context will be misused if the current thread is scheduled to handle other requests return () -> { - if (traceId == null) { + if (context == null) { task.run(); } else { - ThreadContext.put(Fields.traceId, traceId); + LogThreadContext.put(context); try { task.run(); } finally { - ThreadContext.remove(Fields.traceId); + LogThreadContext.remove(); } } }; }); } - @EqualsAndHashCode.Include - @ToString.Include - private final long traceId; - private String traceIdStr; + @Getter + @Setter + private long traceId; + + public TracingContext(TracingContext tracingContext) { + this.traceId = tracingContext.traceId; + } public TracingContext(long traceId) { this.traceId = traceId; @@ -93,30 +90,24 @@ public TracingContext() { this.traceId = RandomUtil.nextPositiveLong(); } - public String getTraceIdStr() { - if (traceIdStr == null) { - traceIdStr = String.valueOf(traceId); + public static long readTraceIdFromContext(ContextView context) { + TracingContext ctx = context.getOrDefault(CTX_KEY_NAME, null); + if (ctx == null) { + return UNDEFINED_TRACE_ID; } - return traceIdStr; + return ctx.traceId; } - public boolean hasTraceId() { - return true; + public void updateThreadContext() { + LogThreadContext.put(this); } - public void updateMdc() { - ThreadContext.put(Fields.traceId, getTraceIdStr()); - } - - public void clearMdc() { - String currentTraceId = ThreadContext.get(Fields.traceId); - if (currentTraceId != null && currentTraceId.equals(getTraceIdStr())) { - ThreadContext.remove(Fields.traceId); - } + public void clearThreadContext() { + LogThreadContext.removeIfEquals(this); } public TracingCloseableContext asCloseable() { return new TracingCloseableContext(this); } -} +} \ No newline at end of file diff --git a/turms-server-common/src/main/java/im/turms/server/common/util/DateUtil.java b/turms-server-common/src/main/java/im/turms/server/common/util/DateUtil.java index 3a99957c1a..f12de8c225 100644 --- a/turms-server-common/src/main/java/im/turms/server/common/util/DateUtil.java +++ b/turms-server-common/src/main/java/im/turms/server/common/util/DateUtil.java @@ -30,33 +30,85 @@ public final class DateUtil { private static final ThreadLocal CALENDAR_THREAD_LOCAL = ThreadLocal .withInitial(() -> new GregorianCalendar(TimeZone.getTimeZone("UTC"))); - // "1970-01-01T00:00:00.000Z" - public static final int DATE_TIME_LENGTH = 24; + // "1970-01-01 00:00:00.000" + public static final int DATE_TIME_LENGTH = 23; private DateUtil() { } - public static String toISO(long timeInMillis) { + public static String toStr(long timeInMillis) { Calendar calendar = CALENDAR_THREAD_LOCAL.get(); calendar.setTimeInMillis(timeInMillis); StringBuilder sb = new StringBuilder(DATE_TIME_LENGTH); sb.append(calendar.get(Calendar.YEAR)) - .append("-") + .append('-') .append(twoDigit(calendar.get(Calendar.MONTH) + 1)) - .append("-") + .append('-') .append(twoDigit(calendar.get(Calendar.DAY_OF_MONTH))) - .append("T") + .append(' ') .append(twoDigit(calendar.get(Calendar.HOUR_OF_DAY))) - .append(":") + .append(':') .append(twoDigit(calendar.get(Calendar.MINUTE))) - .append(":") + .append(':') .append(twoDigit(calendar.get(Calendar.SECOND))) - .append(".") - .append(threeDigit(calendar.get(Calendar.MILLISECOND))) - .append("Z"); + .append('.') + .append(threeDigit(calendar.get(Calendar.MILLISECOND))); return sb.toString(); } + public static byte[] toBytes(long timeInMillis) { + Calendar calendar = CALENDAR_THREAD_LOCAL.get(); + calendar.setTimeInMillis(timeInMillis); + byte[] bytes = new byte[DATE_TIME_LENGTH]; + + byte[] src = Formatter.toCharacterBytes(calendar.get(Calendar.YEAR)); + System.arraycopy(src, 0, bytes, 0, src.length); + int i = src.length; + + bytes[i] = '-'; + i++; + + src = twoDigitBytes(calendar.get(Calendar.MONTH) + 1); + System.arraycopy(src, 0, bytes, i, src.length); + i += src.length; + + bytes[i] = '-'; + i++; + + src = twoDigitBytes(calendar.get(Calendar.DAY_OF_MONTH)); + System.arraycopy(src, 0, bytes, i, src.length); + i += src.length; + + bytes[i] = ' '; + i++; + + src = twoDigitBytes(calendar.get(Calendar.HOUR_OF_DAY)); + System.arraycopy(src, 0, bytes, i, src.length); + i += src.length; + + bytes[i] = ':'; + i++; + + src = twoDigitBytes(calendar.get(Calendar.MINUTE)); + System.arraycopy(src, 0, bytes, i, src.length); + i += src.length; + + bytes[i] = ':'; + i++; + + src = twoDigitBytes(calendar.get(Calendar.SECOND)); + System.arraycopy(src, 0, bytes, i, src.length); + i += src.length; + + bytes[i] = '.'; + i++; + + src = threeDigitBytes(calendar.get(Calendar.MILLISECOND)); + System.arraycopy(src, 0, bytes, i, src.length); + + return bytes; + } + public static Date max(@Nullable Date date1, @Nullable Date date2) { if (date1 == null) { if (date2 != null) { @@ -99,4 +151,23 @@ private static String threeDigit(int i) { return String.valueOf(i); } + private static byte[] twoDigitBytes(int i) { + if (i >= 0 && i < 10) { + // TODO: cache + return new byte[]{'0', (byte) (i + 48)}; + } + return Formatter.toCharacterBytes(i); + } + + private static byte[] threeDigitBytes(int i) { + if (i >= 0 && i < 10) { + // TODO: cache + return new byte[]{'0', '0', (byte) (i + 48)}; + } else if (i < 100) { + // TODO: cache + return ArrayUtil.concat(new byte[]{'0'}, Formatter.toCharacterBytes(i)); + } + return Formatter.toCharacterBytes(i); + } + } diff --git a/turms-server-common/src/main/java/im/turms/server/common/util/ExceptionUtil.java b/turms-server-common/src/main/java/im/turms/server/common/util/ExceptionUtil.java index a3cdd05f08..1b3be76c91 100644 --- a/turms-server-common/src/main/java/im/turms/server/common/util/ExceptionUtil.java +++ b/turms-server-common/src/main/java/im/turms/server/common/util/ExceptionUtil.java @@ -22,6 +22,7 @@ import im.turms.server.common.exception.TurmsBusinessException; import reactor.netty.channel.AbortedException; +import javax.annotation.Nullable; import java.io.EOFException; import java.io.IOException; import java.nio.channels.ClosedChannelException; @@ -43,6 +44,7 @@ public final class ExceptionUtil { private ExceptionUtil() { } + @Nullable public static T suppress(Supplier supplier) { try { return supplier.get(); diff --git a/turms-server-common/src/main/java/im/turms/server/common/util/Formatter.java b/turms-server-common/src/main/java/im/turms/server/common/util/Formatter.java new file mode 100644 index 0000000000..4e4f63e1e0 --- /dev/null +++ b/turms-server-common/src/main/java/im/turms/server/common/util/Formatter.java @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2019 The Turms Project + * https://github.com/turms-im/turms + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.turms.server.common.util; + +/** + * @author James Chen + */ +public class Formatter { + + private Formatter() { + } + + static final byte[] DigitTens = { + '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', + '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', + '2', '2', '2', '2', '2', '2', '2', '2', '2', '2', + '3', '3', '3', '3', '3', '3', '3', '3', '3', '3', + '4', '4', '4', '4', '4', '4', '4', '4', '4', '4', + '5', '5', '5', '5', '5', '5', '5', '5', '5', '5', + '6', '6', '6', '6', '6', '6', '6', '6', '6', '6', + '7', '7', '7', '7', '7', '7', '7', '7', '7', '7', + '8', '8', '8', '8', '8', '8', '8', '8', '8', '8', + '9', '9', '9', '9', '9', '9', '9', '9', '9', '9', + }; + + static final byte[] DigitOnes = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + }; + + public static byte[] toCharacterBytes(int i) { + int size = intStringSize(i); + byte[] bytes = new byte[size]; + getIntChars(i, size, bytes); + return bytes; + } + + public static byte[] toCharacterBytes(long i) { + int size = longStringSize(i); + byte[] bytes = new byte[size]; + getLongChars(i, size, bytes); + return bytes; + } + + static int intStringSize(int x) { + int d = 1; + if (x >= 0) { + d = 0; + x = -x; + } + int p = -10; + for (int i = 1; i < 10; i++) { + if (x > p) + return i + d; + p = 10 * p; + } + return 10 + d; + } + + static int longStringSize(long x) { + int d = 1; + if (x >= 0) { + d = 0; + x = -x; + } + long p = -10; + for (int i = 1; i < 19; i++) { + if (x > p) + return i + d; + p = 10 * p; + } + return 19 + d; + } + + static void getIntChars(int i, int index, byte[] buf) { + int q, r; + int charPos = index; + + boolean negative = i < 0; + if (!negative) { + i = -i; + } + + // Generate two digits per iteration + while (i <= -100) { + q = i / 100; + r = (q * 100) - i; + i = q; + buf[--charPos] = DigitOnes[r]; + buf[--charPos] = DigitTens[r]; + } + + // We know there are at most two digits left at this point. + q = i / 10; + r = (q * 10) - i; + buf[--charPos] = (byte) ('0' + r); + + // Whatever left is the remaining digit. + if (q < 0) { + buf[--charPos] = (byte) ('0' - q); + } + + if (negative) { + buf[--charPos] = (byte) '-'; + } + } + + public static void getLongChars(long i, int index, byte[] buf) { + long q; + int r; + int charPos = index; + + boolean negative = (i < 0); + if (!negative) { + i = -i; + } + + // Get 2 digits/iteration using longs until quotient fits into an int + while (i <= Integer.MIN_VALUE) { + q = i / 100; + r = (int)((q * 100) - i); + i = q; + buf[--charPos] = DigitOnes[r]; + buf[--charPos] = DigitTens[r]; + } + + // Get 2 digits/iteration using ints + int q2; + int i2 = (int)i; + while (i2 <= -100) { + q2 = i2 / 100; + r = (q2 * 100) - i2; + i2 = q2; + buf[--charPos] = DigitOnes[r]; + buf[--charPos] = DigitTens[r]; + } + + // We know there are at most two digits left at this point. + q2 = i2 / 10; + r = (q2 * 10) - i2; + buf[--charPos] = (byte)('0' + r); + + // Whatever left is the remaining digit. + if (q2 < 0) { + buf[--charPos] = (byte)('0' - q2); + } + + if (negative) { + buf[--charPos] = (byte)'-'; + } + } + +} diff --git a/turms-server-common/src/main/java/im/turms/server/common/util/JsonSizeCalculator.java b/turms-server-common/src/main/java/im/turms/server/common/util/JsonSizeCalculator.java index 4c956b99e3..fc2ddcdf97 100644 --- a/turms-server-common/src/main/java/im/turms/server/common/util/JsonSizeCalculator.java +++ b/turms-server-common/src/main/java/im/turms/server/common/util/JsonSizeCalculator.java @@ -17,7 +17,8 @@ package im.turms.server.common.util; -import lombok.extern.log4j.Log4j2; +import im.turms.server.common.logging.core.logger.LoggerFactory; +import im.turms.server.common.logging.core.logger.Logger; import javax.annotation.Nullable; import java.lang.reflect.Field; @@ -29,9 +30,10 @@ /** * @author James Chen */ -@Log4j2 public final class JsonSizeCalculator { + private static final Logger LOGGER = LoggerFactory.getLogger(JsonSizeCalculator.class); + private static final int ESTIMATED_JSON_FIELD_META_SIZE = 8; private JsonSizeCalculator() { @@ -55,7 +57,7 @@ public static int estimateJson(@Nullable Object val) { } } else { // We don't support other array types now because we don't use them - log.warn("Unknown array type: " + val.getClass()); + LOGGER.warn("Unknown array type: " + val.getClass()); } } else if (val instanceof Map map) { for (Map.Entry entry : map.entrySet()) { diff --git a/turms-server-common/src/main/java/org/slf4j/impl/StaticLoggerBinder.java b/turms-server-common/src/main/java/org/slf4j/impl/StaticLoggerBinder.java new file mode 100644 index 0000000000..fd251f5510 --- /dev/null +++ b/turms-server-common/src/main/java/org/slf4j/impl/StaticLoggerBinder.java @@ -0,0 +1,30 @@ +package org.slf4j.impl; + +import im.turms.server.common.logging.slf4j.Slf4jBridgeFactory; +import org.slf4j.ILoggerFactory; +import org.slf4j.spi.LoggerFactoryBinder; + +public final class StaticLoggerBinder implements LoggerFactoryBinder { + + private static final StaticLoggerBinder SINGLETON = new StaticLoggerBinder(); + + public static StaticLoggerBinder getSingleton() { + return SINGLETON; + } + + private final ILoggerFactory loggerFactory = new Slf4jBridgeFactory(); + + private StaticLoggerBinder() { + } + + @Override + public ILoggerFactory getLoggerFactory() { + return loggerFactory; + } + + @Override + public String getLoggerFactoryClassStr() { + return Slf4jBridgeFactory.class.getName(); + } + +} diff --git a/turms-server-common/src/main/java/org/slf4j/impl/StaticMDCBinder.java b/turms-server-common/src/main/java/org/slf4j/impl/StaticMDCBinder.java new file mode 100644 index 0000000000..971f38ba2c --- /dev/null +++ b/turms-server-common/src/main/java/org/slf4j/impl/StaticMDCBinder.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2019 The Turms Project + * https://github.com/turms-im/turms + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.slf4j.impl; + +import org.slf4j.helpers.NOPMDCAdapter; +import org.slf4j.spi.MDCAdapter; + +/** + * @author James Chen + */ +public class StaticMDCBinder { + + public static final StaticMDCBinder SINGLETON = new StaticMDCBinder(); + + private StaticMDCBinder() { + } + + public MDCAdapter getMDCA() { + return new NOPMDCAdapter(); + } + + public String getMDCAdapterClassStr() { + return NOPMDCAdapter.class.getName(); + } + +} diff --git a/turms-server-common/src/test/java/unit/im/turms/server/common/rpc/codec/BaseCodecTest.java b/turms-server-common/src/test/java/unit/im/turms/server/common/rpc/codec/BaseCodecTest.java index 6f53d134c5..d6e5c056bb 100644 --- a/turms-server-common/src/test/java/unit/im/turms/server/common/rpc/codec/BaseCodecTest.java +++ b/turms-server-common/src/test/java/unit/im/turms/server/common/rpc/codec/BaseCodecTest.java @@ -20,8 +20,8 @@ import im.turms.server.common.cluster.service.codec.codec.Codec; import im.turms.server.common.cluster.service.codec.codec.CodecPool; import im.turms.server.common.cluster.service.rpc.dto.RpcRequest; -import im.turms.server.common.rpc.codec.request.RpcRequestCodec; import im.turms.server.common.tracing.TracingContext; +import im.turms.server.common.rpc.codec.request.RpcRequestCodec; import io.netty.buffer.ByteBuf; import io.netty.buffer.CompositeByteBuf; import io.netty.buffer.UnpooledByteBufAllocator; @@ -35,12 +35,12 @@ public abstract class BaseCodecTest { } public > T writeRequestAndReadBuffer(RpcRequestCodec codec, T request) { - TracingContext tracingContext = new TracingContext(); - request.setTracingContext(tracingContext); + TracingContext context = new TracingContext(); + request.setTracingContext(context); T parsedRequest = writeDataAndReadBuffer(codec, request); - assertThat(parsedRequest.getTracingContext().getTraceId()).isEqualTo(tracingContext.getTraceId()); + assertThat(parsedRequest.getTracingContext().getTraceId()).isEqualTo(context.getTraceId()); return parsedRequest; } diff --git a/turms-server-common/src/test/java/unit/im/turms/server/common/rpc/codec/RpcFrameEncoderTests.java b/turms-server-common/src/test/java/unit/im/turms/server/common/rpc/codec/RpcFrameEncoderTests.java index 3fbd0f76b4..0bee15c1d0 100644 --- a/turms-server-common/src/test/java/unit/im/turms/server/common/rpc/codec/RpcFrameEncoderTests.java +++ b/turms-server-common/src/test/java/unit/im/turms/server/common/rpc/codec/RpcFrameEncoderTests.java @@ -24,8 +24,8 @@ import im.turms.server.common.cluster.service.rpc.codec.RpcFrameDecoder; import im.turms.server.common.cluster.service.rpc.codec.RpcFrameEncoder; import im.turms.server.common.dto.ServiceRequest; -import im.turms.server.common.rpc.request.HandleServiceRequest; import im.turms.server.common.tracing.TracingContext; +import im.turms.server.common.rpc.request.HandleServiceRequest; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import org.junit.jupiter.api.Test; @@ -43,7 +43,7 @@ void test() throws InvalidProtocolBufferException { .setText("hello") .build()) .build(); - ServiceRequest serviceRequest = new ServiceRequest(new byte[] {1, 2, 3, 4}, + ServiceRequest serviceRequest = new ServiceRequest(new byte[]{1, 2, 3, 4}, 123L, DeviceType.ANDROID, 1L, diff --git a/turms-server-test-common/src/main/java/im/turms/server/common/testing/ServiceLogConsumer.java b/turms-server-test-common/src/main/java/im/turms/server/common/testing/ServiceLogConsumer.java index 8fc901a3cf..76c28bf360 100644 --- a/turms-server-test-common/src/main/java/im/turms/server/common/testing/ServiceLogConsumer.java +++ b/turms-server-test-common/src/main/java/im/turms/server/common/testing/ServiceLogConsumer.java @@ -1,13 +1,13 @@ package im.turms.server.common.testing; -import lombok.extern.log4j.Log4j2; +import lombok.extern.slf4j.Slf4j; import org.testcontainers.containers.output.BaseConsumer; import org.testcontainers.containers.output.OutputFrame; /** * @author James Chen */ -@Log4j2 +@Slf4j public class ServiceLogConsumer extends BaseConsumer { private final String serviceName; diff --git a/turms-server-test-common/src/main/java/im/turms/server/common/testing/TestingEnvContainer.java b/turms-server-test-common/src/main/java/im/turms/server/common/testing/TestingEnvContainer.java index 163fb27813..0aa9fef1b3 100644 --- a/turms-server-test-common/src/main/java/im/turms/server/common/testing/TestingEnvContainer.java +++ b/turms-server-test-common/src/main/java/im/turms/server/common/testing/TestingEnvContainer.java @@ -1,6 +1,6 @@ package im.turms.server.common.testing; -import lombok.extern.log4j.Log4j2; +import lombok.extern.slf4j.Slf4j; import org.testcontainers.containers.ContainerState; import org.testcontainers.containers.DockerComposeContainer; import org.testcontainers.shaded.org.apache.commons.io.FileUtils; @@ -18,7 +18,7 @@ /** * @author James Chen */ -@Log4j2 +@Slf4j public class TestingEnvContainer extends DockerComposeContainer implements Closeable { private static final String MONGO_SERVICE_NAME = "mongodb-router_1"; diff --git a/turms-server-test-common/src/main/resources/log4j2.xml b/turms-server-test-common/src/main/resources/log4j2.xml deleted file mode 100644 index 293152c370..0000000000 --- a/turms-server-test-common/src/main/resources/log4j2.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - %xwEx - yyyy-MM-dd HH:mm:ss.SSS - ./log/turms-server-test.log - %5p - ./log - %clr{%d{${sys:LOG_DATEFORMAT_PATTERN}}}{faint} %clr{${sys:LOG_LEVEL_PATTERN}} %clr{%pid}{magenta} %clr{---}{faint} %clr{[%15.15t]}{faint} %clr{%-40.40c{1.}}{cyan} %clr{:}{faint} %m%n${sys:LOG_EXCEPTION_CONVERSION_WORD} - %d{${LOG_DATEFORMAT_PATTERN}} ${LOG_LEVEL_PATTERN} %pid --- [%t] %-40.40c{1.} : %m%n${sys:LOG_EXCEPTION_CONVERSION_WORD} - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/turms-server-test/src/test/java/stress/WebSocketAccessST.java b/turms-server-test/src/test/java/stress/WebSocketAccessST.java index 3e4de9b5ae..6a69aa1a4f 100644 --- a/turms-server-test/src/test/java/stress/WebSocketAccessST.java +++ b/turms-server-test/src/test/java/stress/WebSocketAccessST.java @@ -63,7 +63,7 @@ void shouldDenyServiceRatherThanCrash_whenManyUsersTryToLogin_on1gJvm() throws I .turmsGatewayJvmOptions(List.of( "-Xms=1g", "-Xmx=1g", - "turms.monitor.updateMemoryIntervalSeconds=1" + "turms.healthCheck.checkIntervalSeconds=1" )) .build()); container.start(); diff --git a/turms-service/src/main/java/im/turms/service/constant/SecurityConstant.java b/turms-service/src/main/java/im/turms/service/constant/SecurityConstant.java index 16c90786dd..eba7ca3656 100644 --- a/turms-service/src/main/java/im/turms/service/constant/SecurityConstant.java +++ b/turms-service/src/main/java/im/turms/service/constant/SecurityConstant.java @@ -22,7 +22,7 @@ */ public final class SecurityConstant { - public static final String SENSITIVE_VALUE = "******"; + public static final String SENSITIVE_VALUE = "***"; private SecurityConstant() { } diff --git a/turms-service/src/main/java/im/turms/service/logging/ClientApiLogging.java b/turms-service/src/main/java/im/turms/service/logging/ClientApiLogging.java index 26730578e4..65a466649b 100644 --- a/turms-service/src/main/java/im/turms/service/logging/ClientApiLogging.java +++ b/turms-service/src/main/java/im/turms/service/logging/ClientApiLogging.java @@ -20,11 +20,11 @@ import im.turms.common.model.dto.notification.TurmsNotification; import im.turms.server.common.dto.ServiceRequest; import im.turms.server.common.dto.ServiceResponse; -import im.turms.server.common.logging.CustomLogger; import im.turms.server.common.util.DateUtil; import im.turms.service.workflow.access.servicerequest.dto.ClientRequest; -import static im.turms.server.common.logging.CustomLogger.LOG_FIELD_DELIMITER; +import static im.turms.server.common.logging.CommonLogger.CLIENT_API_LOGGER; +import static im.turms.server.common.logging.CommonLogger.LOG_FIELD_DELIMITER; /** * @author James Chen @@ -58,15 +58,15 @@ public static void log(ClientRequest request, request.requestId().toString(), request.turmsRequest().getKindCase().name(), String.valueOf(requestSize), - DateUtil.toISO(requestTime), + DateUtil.toStr(requestTime), // response information String.valueOf(response.code().getBusinessCode()), responseType, String.valueOf(processingTime)); if (response.code().isServerError()) { - CustomLogger.CLIENT_API_LOGGER.error(message); + CLIENT_API_LOGGER.error(message); } else { - CustomLogger.CLIENT_API_LOGGER.info(message); + CLIENT_API_LOGGER.info(message); } } diff --git a/turms-service/src/main/java/im/turms/service/logging/NotificationLogging.java b/turms-service/src/main/java/im/turms/service/logging/NotificationLogging.java index 0ce9e816e4..d6ecafad7f 100644 --- a/turms-service/src/main/java/im/turms/service/logging/NotificationLogging.java +++ b/turms-service/src/main/java/im/turms/service/logging/NotificationLogging.java @@ -19,9 +19,9 @@ import im.turms.common.model.dto.notification.TurmsNotification; import im.turms.common.model.dto.request.TurmsRequest; -import im.turms.server.common.logging.CustomLogger; -import static im.turms.server.common.logging.CustomLogger.LOG_FIELD_DELIMITER; +import static im.turms.server.common.logging.CommonLogger.LOG_FIELD_DELIMITER; +import static im.turms.server.common.logging.CommonLogger.NOTIFICATION_LOGGER; /** * @author James Chen @@ -49,7 +49,7 @@ public static void log(boolean sent, TurmsNotification notification, int recipie // Relayed request info String.valueOf(relayedRequest.getRequestId()), relayedRequest.getKindCase().name()); - CustomLogger.NOTIFICATION_LOGGER.info(message); + NOTIFICATION_LOGGER.info(message); } } \ No newline at end of file diff --git a/turms-service/src/main/java/im/turms/service/manager/ServiceAddressManager.java b/turms-service/src/main/java/im/turms/service/manager/ServiceAddressManager.java index f4141a0a76..f48edd663a 100644 --- a/turms-service/src/main/java/im/turms/service/manager/ServiceAddressManager.java +++ b/turms-service/src/main/java/im/turms/service/manager/ServiceAddressManager.java @@ -21,9 +21,10 @@ import im.turms.server.common.address.AddressCollector; import im.turms.server.common.address.BaseServiceAddressManager; import im.turms.server.common.address.PublicIpManager; +import im.turms.server.common.logging.core.logger.LoggerFactory; +import im.turms.server.common.logging.core.logger.Logger; import im.turms.server.common.property.TurmsPropertiesManager; import im.turms.server.common.property.env.common.AddressProperties; -import lombok.extern.log4j.Log4j2; import org.springframework.boot.autoconfigure.web.ServerProperties; import org.springframework.stereotype.Component; @@ -33,9 +34,10 @@ * @author James Chen */ @Component -@Log4j2 public class ServiceAddressManager extends BaseServiceAddressManager { + private static final Logger LOGGER = LoggerFactory.getLogger(ServiceAddressManager.class); + private AddressProperties adminApiAddressProperties; private String metricsApiAddress; private String adminApiAddress; @@ -55,7 +57,7 @@ public ServiceAddressManager( try { updateCollectorAndAddresses(adminApiServerProperties, newAdminApiDiscoveryProperties); } catch (UnknownHostException e) { - log.error("Failed to update address collector", e); + LOGGER.error("Failed to update address collector", e); } } if (areAddressPropertiesChange || isMemberHostChanged) { diff --git a/turms-service/src/main/java/im/turms/service/plugin/TurmsPluginManager.java b/turms-service/src/main/java/im/turms/service/plugin/TurmsPluginManager.java index 96fba6192f..ddbd81d057 100644 --- a/turms-service/src/main/java/im/turms/service/plugin/TurmsPluginManager.java +++ b/turms-service/src/main/java/im/turms/service/plugin/TurmsPluginManager.java @@ -26,7 +26,6 @@ import im.turms.service.plugin.extension.ExpiredMessageAutoDeletionNotificationHandler; import im.turms.service.plugin.extension.StorageServiceProvider; import lombok.Getter; -import lombok.extern.log4j.Log4j2; import org.springframework.context.ApplicationContext; import org.springframework.stereotype.Component; @@ -37,7 +36,6 @@ */ @Component @Getter -@Log4j2 public class TurmsPluginManager extends AbstractTurmsPluginManager { private List adminActionHandlerList; diff --git a/turms-service/src/main/java/im/turms/service/util/ProtoModelUtil.java b/turms-service/src/main/java/im/turms/service/util/ProtoModelUtil.java index 3201da363e..c2a37cb780 100644 --- a/turms-service/src/main/java/im/turms/service/util/ProtoModelUtil.java +++ b/turms-service/src/main/java/im/turms/service/util/ProtoModelUtil.java @@ -44,7 +44,6 @@ import im.turms.server.common.dao.domain.User; import im.turms.service.workflow.dao.domain.message.Message; import io.lettuce.core.GeoCoordinates; -import lombok.extern.log4j.Log4j2; import javax.annotation.Nullable; import javax.validation.constraints.NotNull; @@ -55,7 +54,6 @@ /** * @author James Chen */ -@Log4j2 public final class ProtoModelUtil { private ProtoModelUtil() { diff --git a/turms-service/src/main/java/im/turms/service/workflow/access/http/codec/BlockedClientSerializer.java b/turms-service/src/main/java/im/turms/service/workflow/access/http/codec/BlockedClientSerializer.java index 4d7fc98d27..fda3ec9f64 100644 --- a/turms-service/src/main/java/im/turms/service/workflow/access/http/codec/BlockedClientSerializer.java +++ b/turms-service/src/main/java/im/turms/service/workflow/access/http/codec/BlockedClientSerializer.java @@ -44,7 +44,7 @@ public void serialize(BlockedClient value, JsonGenerator gen, SerializerProvider } else { gen.writeStringField("id", InetAddressUtil.ipBytesToString(((ByteArrayWrapper) id).bytes())); } - gen.writeStringField("blockEndTime", DateUtil.toISO(value.blockEndTime())); + gen.writeStringField("blockEndTime", DateUtil.toStr(value.blockEndTime())); gen.writeEndObject(); } } diff --git a/turms-service/src/main/java/im/turms/service/workflow/access/http/config/TurmsErrorWebExceptionHandler.java b/turms-service/src/main/java/im/turms/service/workflow/access/http/config/TurmsErrorWebExceptionHandler.java index 42ed767e29..26bf693147 100644 --- a/turms-service/src/main/java/im/turms/service/workflow/access/http/config/TurmsErrorWebExceptionHandler.java +++ b/turms-service/src/main/java/im/turms/service/workflow/access/http/config/TurmsErrorWebExceptionHandler.java @@ -18,19 +18,18 @@ package im.turms.service.workflow.access.http.config; import im.turms.server.common.cluster.node.NodeType; -import im.turms.server.common.logging.RequestLoggingContext; +import im.turms.server.common.logging.core.logger.LoggerFactory; import im.turms.server.common.tracing.TracingCloseableContext; import im.turms.server.common.tracing.TracingContext; +import im.turms.server.common.logging.core.logger.Logger; import im.turms.server.common.util.ExceptionUtil; import im.turms.service.workflow.access.http.dto.response.ErrorAttributes; import im.turms.service.workflow.access.http.dto.response.ErrorAttributesFactory; -import lombok.extern.log4j.Log4j2; import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler; import org.springframework.context.annotation.Configuration; import org.springframework.core.NestedExceptionUtils; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; -import org.springframework.core.log.LogMessage; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.codec.HttpMessageReader; @@ -60,10 +59,11 @@ * @see org.springframework.web.server.handler.ResponseStatusExceptionHandler */ @Configuration -@Log4j2 @Order(Ordered.HIGHEST_PRECEDENCE) public class TurmsErrorWebExceptionHandler implements ErrorWebExceptionHandler { + private static final Logger LOGGER = LoggerFactory.getLogger(TurmsErrorWebExceptionHandler.class); + private final ServerResponse.Context context; private final List> messageReaders; private final NodeType nodeType; @@ -101,13 +101,10 @@ public Mono handle(ServerWebExchange exchange, Throwable throwable) { return responseMono.flatMap(response -> { if (status == HttpStatus.INTERNAL_SERVER_ERROR.value()) { ServerRequest request = ServerRequest.create(exchange, this.messageReaders); - RequestLoggingContext loggingContext = exchange.getAttribute(RequestLoggingContext.CTX_KEY_NAME); - TracingContext tracingContext = loggingContext == null - ? TracingContext.NOOP - : loggingContext.getTracingContext(); - try (TracingCloseableContext ctx = tracingContext.asCloseable()) { - log.error(LogMessage.of(() -> "%s 500 Server Error for %s" - .formatted(request.exchange().getLogPrefix(), formatRequest(request))), + TracingContext context = exchange.getAttributeOrDefault(TracingContext.CTX_KEY_NAME, TracingContext.NOOP); + try (TracingCloseableContext ignored = context.asCloseable()) { + LOGGER.error("%s 500 Server Error for %s" + .formatted(request.exchange().getLogPrefix(), formatRequest(request)), throwable); } } diff --git a/turms-service/src/main/java/im/turms/service/workflow/access/http/controller/cluster/SettingController.java b/turms-service/src/main/java/im/turms/service/workflow/access/http/controller/cluster/SettingController.java index 2b9b83b8fc..2a0af451fc 100644 --- a/turms-service/src/main/java/im/turms/service/workflow/access/http/controller/cluster/SettingController.java +++ b/turms-service/src/main/java/im/turms/service/workflow/access/http/controller/cluster/SettingController.java @@ -22,14 +22,13 @@ import im.turms.server.common.cluster.node.Node; import im.turms.server.common.constant.TurmsStatusCode; import im.turms.server.common.exception.TurmsBusinessException; +import im.turms.server.common.property.PropertiesUtil; import im.turms.server.common.property.TurmsProperties; import im.turms.server.common.property.TurmsPropertiesManager; -import im.turms.server.common.util.PropertiesUtil; import im.turms.service.workflow.access.http.permission.RequiredPermission; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; -import lombok.extern.log4j.Log4j2; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PutMapping; @@ -49,7 +48,6 @@ /** * @author James Chen */ -@Log4j2 @RestController @RequestMapping("/cluster/settings") public class SettingController { diff --git a/turms-service/src/main/java/im/turms/service/workflow/access/http/filter/ControllerFilter.java b/turms-service/src/main/java/im/turms/service/workflow/access/http/filter/ControllerFilter.java index 425577518a..92be6b098e 100644 --- a/turms-service/src/main/java/im/turms/service/workflow/access/http/filter/ControllerFilter.java +++ b/turms-service/src/main/java/im/turms/service/workflow/access/http/filter/ControllerFilter.java @@ -22,8 +22,6 @@ import im.turms.server.common.constant.TurmsStatusCode; import im.turms.server.common.exception.TurmsBusinessException; import im.turms.server.common.logging.AdminApiLogging; -import im.turms.server.common.logging.RequestLoggingContext; -import im.turms.server.common.property.env.service.env.adminapi.AdminApiProperties; import im.turms.server.common.property.env.service.env.adminapi.LogProperties; import im.turms.server.common.tracing.TracingCloseableContext; import im.turms.server.common.tracing.TracingContext; @@ -238,7 +236,6 @@ private Mono tryPersistAndPass( boolean isLogEnabled = logProperties.isEnabled(); boolean triggerHandlers = pluginEnabled && !turmsPluginManager.getAdminActionHandlerList().isEmpty(); TracingContext tracingContext = new TracingContext(); - RequestLoggingContext loggingContext = new RequestLoggingContext(tracingContext); if (isLogEnabled || triggerHandlers) { long requestTime = System.currentTimeMillis(); ServerHttpRequest request = exchange.getRequest(); @@ -249,8 +246,8 @@ private Mono tryPersistAndPass( } return checkFrequencyAndPass(chain, exchange) .contextWrite(context -> { - exchange.getAttributes().put(RequestLoggingContext.CTX_KEY_NAME, loggingContext); - return context.put(RequestLoggingContext.CTX_KEY_NAME, loggingContext); + exchange.getAttributes().put(TracingContext.CTX_KEY_NAME, tracingContext); + return context.put(TracingContext.CTX_KEY_NAME, tracingContext); }); } @@ -275,7 +272,7 @@ private boolean isValidDeleteRequest(Object[] args) { private Mono logAndTriggerHandlers( TurmsHandlerMethod handlerMethod, - TracingContext tracingContext, + TracingContext context, String account, String ip, String requestId, @@ -292,7 +289,7 @@ private Mono logAndTriggerHandlers( return; } logAndTriggerHandlers0(handlerMethod, - tracingContext, + context, account, ip, requestId, @@ -306,7 +303,7 @@ private Mono logAndTriggerHandlers( throw new IllegalStateException("Unexpected response type: Flux. Use Mono instead"); } else { logAndTriggerHandlers0(handlerMethod, - tracingContext, + context, account, ip, requestId, @@ -321,7 +318,7 @@ private Mono logAndTriggerHandlers( private void logAndTriggerHandlers0( TurmsHandlerMethod handlerMethod, - TracingContext tracingContext, + TracingContext context, String account, String ip, String requestId, @@ -355,7 +352,7 @@ private void logAndTriggerHandlers0( } handleAdminAction.subscribe(); } - try (TracingCloseableContext ignored = tracingContext.asCloseable()) { + try (TracingCloseableContext ignored = context.asCloseable()) { AdminApiLogging.log( account, ip, diff --git a/turms-service/src/main/java/im/turms/service/workflow/access/servicerequest/dispatcher/ServiceRequestDispatcher.java b/turms-service/src/main/java/im/turms/service/workflow/access/servicerequest/dispatcher/ServiceRequestDispatcher.java index 3caa71d518..b2e3990e17 100644 --- a/turms-service/src/main/java/im/turms/service/workflow/access/servicerequest/dispatcher/ServiceRequestDispatcher.java +++ b/turms-service/src/main/java/im/turms/service/workflow/access/servicerequest/dispatcher/ServiceRequestDispatcher.java @@ -26,11 +26,13 @@ import im.turms.server.common.dto.ServiceResponse; import im.turms.server.common.exception.ThrowableInfo; import im.turms.server.common.healthcheck.ServerStatusManager; +import im.turms.server.common.logging.core.logger.LoggerFactory; +import im.turms.server.common.tracing.TracingCloseableContext; +import im.turms.server.common.tracing.TracingContext; +import im.turms.server.common.logging.core.logger.Logger; import im.turms.server.common.property.TurmsPropertiesManager; import im.turms.server.common.rpc.service.IServiceRequestDispatcher; import im.turms.server.common.service.blocklist.BlocklistService; -import im.turms.server.common.tracing.TracingCloseableContext; -import im.turms.server.common.tracing.TracingContext; import im.turms.server.common.util.ProtoUtil; import im.turms.service.logging.ApiLoggingContext; import im.turms.service.logging.ClientApiLogging; @@ -42,7 +44,6 @@ import im.turms.service.workflow.access.servicerequest.dto.ServiceResponseFactory; import im.turms.service.workflow.service.impl.message.OutboundMessageService; import io.netty.buffer.ByteBuf; -import lombok.extern.log4j.Log4j2; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.context.ApplicationContext; @@ -66,10 +67,11 @@ /** * @author James Chen */ -@Log4j2 @Service public class ServiceRequestDispatcher implements IServiceRequestDispatcher { + private static final Logger LOGGER = LoggerFactory.getLogger(ServiceRequestDispatcher.class); + private final ApiLoggingContext apiLoggingContext; private final BlocklistService blocklistService; private final ServerStatusManager serverStatusManager; @@ -138,20 +140,20 @@ private boolean isRequestForGateway(TurmsRequest.KindCase type) { * 3. The method ensures turmsRequestBuffer in serviceRequest will be released by 1 */ @Override - public Mono dispatch(TracingContext tracingContext, ServiceRequest serviceRequest) { + public Mono dispatch(TracingContext context, ServiceRequest serviceRequest) { ByteBuf requestBuffer = serviceRequest.getTurmsRequestBuffer(); try { requestBuffer.touch(serviceRequest); - return dispatch0(tracingContext, serviceRequest); + return dispatch0(context, serviceRequest); } catch (Exception e) { - log.error("Failed to handle the request: {}", serviceRequest, e); + LOGGER.error("Failed to handle the request: {}", serviceRequest, e); return Mono.just(ServiceResponseFactory.get(TurmsStatusCode.SERVER_INTERNAL_ERROR, e.toString())); } finally { requestBuffer.release(); } } - private Mono dispatch0(TracingContext tracingContext, ServiceRequest serviceRequest) { + private Mono dispatch0(TracingContext context, ServiceRequest serviceRequest) { long requestTime = System.currentTimeMillis(); // 1. Validate ServiceResponse Long userId = serviceRequest.getUserId(); @@ -239,8 +241,8 @@ private Mono dispatch0(TracingContext tracingContext, ServiceRe } notifyRelatedUsersOfAction(requestResult, userId, deviceType) .onErrorResume(t -> { - try (TracingCloseableContext ignored = tracingContext.asCloseable()) { - log.error("Failed to notify related users of the action", t); + try (TracingCloseableContext ignored = context.asCloseable()) { + LOGGER.error("Failed to notify related users of the action", t); } return Mono.empty(); }) @@ -250,13 +252,13 @@ private Mono dispatch0(TracingContext tracingContext, ServiceRe .onErrorResume(t -> { ThrowableInfo info = ThrowableInfo.get(t); if (info.code().isServerError()) { - // We update MDC and not clear because we know the downstream will clear - tracingContext.updateMdc(); + // We update the thread context and not clear because we know the downstream will clear + context.updateThreadContext(); // Note we log the whole request instead of the request ID for troubleshooting // because CommonClientApiLogging only logs a brief description, // which isn't enough for debugging, but it's enough for statistics // and user behavior analysis, so we don't plan to change it - log.error("Caught an internal server error when handling the request: " + lastClientRequest, t); + LOGGER.error("Caught an internal server error when handling the request: " + lastClientRequest, t); } return Mono.just(RequestHandlerResultFactory.get(info.code(), info.reason())); }) @@ -267,7 +269,7 @@ private Mono dispatch0(TracingContext tracingContext, ServiceRe handlerResult.reason()); // 6. Log if (response.code().isServerError() || apiLoggingContext.shouldLogRequest(requestType)) { - tracingContext.updateMdc(); + context.updateThreadContext(); ClientApiLogging .log(lastClientRequest, serviceRequest, diff --git a/turms-service/src/main/java/im/turms/service/workflow/dao/MongoCollectionInitializer.java b/turms-service/src/main/java/im/turms/service/workflow/dao/MongoCollectionInitializer.java index 16ce593d59..696afa5b47 100644 --- a/turms-service/src/main/java/im/turms/service/workflow/dao/MongoCollectionInitializer.java +++ b/turms-service/src/main/java/im/turms/service/workflow/dao/MongoCollectionInitializer.java @@ -21,6 +21,8 @@ import com.google.common.collect.Multimap; import im.turms.server.common.context.TurmsApplicationContext; import im.turms.server.common.dao.domain.User; +import im.turms.server.common.logging.core.logger.LoggerFactory; +import im.turms.server.common.logging.core.logger.Logger; import im.turms.server.common.mongo.BsonPool; import im.turms.server.common.mongo.IMongoCollectionInitializer; import im.turms.server.common.mongo.TurmsMongoClient; @@ -49,7 +51,6 @@ import im.turms.service.workflow.dao.domain.user.UserRelationshipGroup; import im.turms.service.workflow.dao.domain.user.UserRelationshipGroupMember; import im.turms.service.workflow.dao.domain.user.UserVersion; -import lombok.extern.log4j.Log4j2; import org.apache.commons.lang3.time.DateUtils; import org.bson.Document; import org.springframework.stereotype.Component; @@ -68,10 +69,11 @@ /** * @author James Chen */ -@Log4j2 @Component(IMongoCollectionInitializer.BEAN_NAME) public class MongoCollectionInitializer implements IMongoCollectionInitializer { + private static final Logger LOGGER = LoggerFactory.getLogger(MongoCollectionInitializer.class); + private final TurmsMongoClient adminMongoClient; private final TurmsMongoClient userMongoClient; private final TurmsMongoClient groupMongoClient; @@ -122,21 +124,21 @@ public MongoCollectionInitializer( private void initCollections() { Duration timeout = Duration.ofMinutes(1); if (!context.isProduction() && fakingManager.isClearAllCollectionsBeforeFaking()) { - log.info("Start dropping databases..."); + LOGGER.info("Start dropping databases..."); dropAllDatabases().block(timeout); - log.info("All collections are cleared"); + LOGGER.info("All collections are cleared"); } - log.info("Start creating collections..."); + LOGGER.info("Start creating collections..."); createCollectionsIfNotExist() - .doOnError(t -> log.error("Failed to create collections", t)) - .doOnSuccess(ignored -> log.info("All collections are created")) + .doOnError(t -> LOGGER.error("Failed to create collections", t)) + .doOnSuccess(ignored -> LOGGER.info("All collections are created")) .flatMap(exists -> { if (exists && !fakingManager.isFakeIfCollectionExists()) { return Mono.empty(); } return Mono.defer(() -> ensureZones() .then(Mono.defer(this::ensureIndexesAndShard) - .doOnError(t -> log.error("Failed to ensure indexes and shard", t))) + .doOnError(t -> LOGGER.error("Failed to ensure indexes and shard", t))) .then(Mono.defer(() -> !context.isProduction() && fakingManager.isFakingEnabled() ? fakingManager.fakeData() : Mono.empty()))); @@ -252,7 +254,7 @@ private Mono ensureZones(MultiTemperatureProperties temperatureProperties, return Mono.empty(); } String collectionName = entity.collectionName(); - log.info("Adding the shards of the {} collection to zones...", collectionName); + LOGGER.info("Adding the shards of the {} collection to zones...", collectionName); Map temperatures = new LinkedHashMap<>(8); temperatures.put(collectionName + "_hot", temperatureProperties.getHot()); temperatures.put(collectionName + "_warm", temperatureProperties.getWarm()); @@ -283,8 +285,8 @@ private Mono ensureZones(MultiTemperatureProperties temperatureProperties, : DateUtils.addDays(startDate, -currentDay); ensureZones = ensureZones .then(Mono.defer(() -> mongoClient.addShardToZone(shard, zoneName) - .doOnError(t -> log.error("Failed to add a shard {} to the zone {}", shard, zoneName, t))) - .doOnSuccess(unused -> log.info("Added a shard {} to the zone {}", shard, zoneName))) + .doOnError(t -> LOGGER.error("Failed to add a shard {} to the zone {}", shard, zoneName, t))) + .doOnSuccess(unused -> LOGGER.info("Added a shard {} to the zone {}", shard, zoneName))) .then(Mono.defer(() -> { // TODO: support the shard key consisting of multiple fields Document minimum = new Document(creationDateFieldName, min); @@ -293,12 +295,12 @@ private Mono ensureZones(MultiTemperatureProperties temperatureProperties, zoneName, minimum, maximum) - .doOnError(t -> log.error("Failed to update the zone {} with the key ranges: {} -->> {}", + .doOnError(t -> LOGGER.error("Failed to update the zone {} with the key ranges: {} -->> {}", zoneName, minimum.toJson(), maximum.toJson(), t)) - .doOnSuccess(unused -> log.info("Updated the zone {} with the key ranges: {} -->> {}", + .doOnSuccess(unused -> LOGGER.info("Updated the zone {} with the key ranges: {} -->> {}", zoneName, minimum.toJson(), maximum.toJson())); @@ -307,7 +309,7 @@ private Mono ensureZones(MultiTemperatureProperties temperatureProperties, currentDay += days; } return ensureZones - .doOnSuccess(unused -> log.info("Added the shards of the {} collection to zones", collectionName)); + .doOnSuccess(unused -> LOGGER.info("Added the shards of the {} collection to zones", collectionName)); } } \ No newline at end of file diff --git a/turms-service/src/main/java/im/turms/service/workflow/dao/MongoFakingManager.java b/turms-service/src/main/java/im/turms/service/workflow/dao/MongoFakingManager.java index 58da418cdd..882af3e7b3 100644 --- a/turms-service/src/main/java/im/turms/service/workflow/dao/MongoFakingManager.java +++ b/turms-service/src/main/java/im/turms/service/workflow/dao/MongoFakingManager.java @@ -26,6 +26,8 @@ import im.turms.common.constant.ProfileAccessStrategy; import im.turms.common.constant.RequestStatus; import im.turms.server.common.dao.domain.User; +import im.turms.server.common.logging.core.logger.LoggerFactory; +import im.turms.server.common.logging.core.logger.Logger; import im.turms.server.common.mongo.TurmsMongoClient; import im.turms.server.common.property.env.service.env.FakeProperties; import im.turms.server.common.security.PasswordManager; @@ -52,7 +54,6 @@ import im.turms.service.workflow.dao.domain.user.UserRelationshipGroupMember; import im.turms.service.workflow.dao.domain.user.UserVersion; import lombok.Getter; -import lombok.extern.log4j.Log4j2; import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.time.DateUtils; import reactor.core.publisher.Mono; @@ -67,9 +68,10 @@ /** * @author James Chen */ -@Log4j2 public final class MongoFakingManager { + private static final Logger LOGGER = LoggerFactory.getLogger(MongoFakingManager.class); + private final PasswordManager passwordManager; private final TurmsMongoClient adminMongoClient; @@ -139,7 +141,7 @@ public MongoFakingManager(FakeProperties properties, } public Mono fakeData() { - log.info("Start faking..."); + LOGGER.info("Start faking..."); final int adminCount = 10; @@ -377,34 +379,34 @@ public Mono fakeData() { // but it won't block when running in non-debug mode // and there is no blocking method after reviewing the workflow of TurmsMongoClient#insertAll Mono adminMono = adminMongoClient.insertAll(adminRelatedObjs) - .doOnSubscribe(s -> log.info("Start faking admin-related data")) - .doOnError(error -> log.error("Failed to fake admin-related data", error)) - .doOnSuccess(unused -> log.info("Admin-related data has been faked")) + .doOnSubscribe(s -> LOGGER.info("Start faking admin-related data")) + .doOnError(error -> LOGGER.error("Failed to fake admin-related data", error)) + .doOnSuccess(unused -> LOGGER.info("Admin-related data has been faked")) .subscribeOn(Schedulers.boundedElastic()); Mono userMono = userMongoClient.insertAll(userRelatedObjs) - .doOnSubscribe(s -> log.info("Start faking user-related data")) - .doOnError(error -> log.error("Failed to fake user-related data", error)) - .doOnSuccess(unused -> log.info("User-related data has been faked")) + .doOnSubscribe(s -> LOGGER.info("Start faking user-related data")) + .doOnError(error -> LOGGER.error("Failed to fake user-related data", error)) + .doOnSuccess(unused -> LOGGER.info("User-related data has been faked")) .subscribeOn(Schedulers.boundedElastic()); Mono groupMono = groupMongoClient.insertAll(groupRelatedObjs) - .doOnSubscribe(s -> log.info("Start faking group-related data")) - .doOnError(error -> log.error("Failed to fake group-related data", error)) - .doOnSuccess(unused -> log.info("Group-related data has been faked")) + .doOnSubscribe(s -> LOGGER.info("Start faking group-related data")) + .doOnError(error -> LOGGER.error("Failed to fake group-related data", error)) + .doOnSuccess(unused -> LOGGER.info("Group-related data has been faked")) .subscribeOn(Schedulers.boundedElastic()); Mono conversationMono = conversationMongoClient.insertAll(conversationRelatedObjs) - .doOnSubscribe(s -> log.info("Start faking conversation-related data")) - .doOnError(error -> log.error("Failed to fake conversation-related data", error)) - .doOnSuccess(unused -> log.info("Conversation-related data has been faked")) + .doOnSubscribe(s -> LOGGER.info("Start faking conversation-related data")) + .doOnError(error -> LOGGER.error("Failed to fake conversation-related data", error)) + .doOnSuccess(unused -> LOGGER.info("Conversation-related data has been faked")) .subscribeOn(Schedulers.boundedElastic()); Mono messageMono = messageMongoClient.insertAll(messageRelatedObjs) - .doOnSubscribe(s -> log.info("Start faking message-related data")) - .doOnError(error -> log.error("Failed to fake message-related data", error)) - .doOnSuccess(unused -> log.info("Message-related data has been faked")) + .doOnSubscribe(s -> LOGGER.info("Start faking message-related data")) + .doOnError(error -> LOGGER.error("Failed to fake message-related data", error)) + .doOnSuccess(unused -> LOGGER.info("Message-related data has been faked")) .subscribeOn(Schedulers.boundedElastic()); return Mono.when(adminMono, userMono, groupMono, conversationMono, messageMono) .then() - .doOnSuccess(ignored -> log.info("All data has been faked")) - .doOnError(t -> log.error("Failed to fake data", t)); + .doOnSuccess(ignored -> LOGGER.info("All data has been faked")) + .doOnError(t -> LOGGER.error("Failed to fake data", t)); } private long nextId() { diff --git a/turms-service/src/main/java/im/turms/service/workflow/dao/config/MongoConfig.java b/turms-service/src/main/java/im/turms/service/workflow/dao/config/MongoConfig.java index f547a9a0cb..70943e6093 100644 --- a/turms-service/src/main/java/im/turms/service/workflow/dao/config/MongoConfig.java +++ b/turms-service/src/main/java/im/turms/service/workflow/dao/config/MongoConfig.java @@ -19,6 +19,8 @@ import com.google.common.collect.Maps; import im.turms.server.common.dao.domain.User; +import im.turms.server.common.logging.core.logger.LoggerFactory; +import im.turms.server.common.logging.core.logger.Logger; import im.turms.server.common.mongo.TurmsMongoClient; import im.turms.server.common.mongo.operation.MongoCollectionOptions; import im.turms.server.common.property.TurmsPropertiesManager; @@ -47,7 +49,6 @@ import im.turms.service.workflow.dao.domain.user.UserRelationshipGroup; import im.turms.service.workflow.dao.domain.user.UserRelationshipGroupMember; import im.turms.service.workflow.dao.domain.user.UserVersion; -import lombok.extern.log4j.Log4j2; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -58,10 +59,11 @@ * @author James Chen * @see org.springframework.boot.autoconfigure.data.mongo.MongoReactiveDataAutoConfiguration */ -@Log4j2 @Configuration public class MongoConfig { + private static final Logger LOGGER = LoggerFactory.getLogger(MongoConfig.class); + private static final int SERVICE_TYPES_NUMBER = 5; private static final Map CLIENT_MAP = Maps.newHashMapWithExpectedSize(SERVICE_TYPES_NUMBER); @@ -71,7 +73,7 @@ public void destroy() { try { client.destroy(); } catch (Exception ignored) { - log.error("Failed to destroy a mongo client"); + LOGGER.error("Failed to destroy a mongo client"); } } } diff --git a/turms-service/src/main/java/im/turms/service/workflow/dao/domain/group/GroupType.java b/turms-service/src/main/java/im/turms/service/workflow/dao/domain/group/GroupType.java index bb0ee53594..4109fc90e6 100644 --- a/turms-service/src/main/java/im/turms/service/workflow/dao/domain/group/GroupType.java +++ b/turms-service/src/main/java/im/turms/service/workflow/dao/domain/group/GroupType.java @@ -30,10 +30,7 @@ *

* Note that there is a built-in immutable group type: *

- * {
- *     id: 0,
- *     name: "DEFAULT"
- * }
+ * { id: 0, name: "DEFAULT" }
  * 
* * @author James Chen diff --git a/turms-service/src/main/java/im/turms/service/workflow/service/impl/admin/AdminRoleService.java b/turms-service/src/main/java/im/turms/service/workflow/service/impl/admin/AdminRoleService.java index 6e8358929c..c6400a24c6 100644 --- a/turms-service/src/main/java/im/turms/service/workflow/service/impl/admin/AdminRoleService.java +++ b/turms-service/src/main/java/im/turms/service/workflow/service/impl/admin/AdminRoleService.java @@ -25,6 +25,8 @@ import im.turms.server.common.constant.TurmsStatusCode; import im.turms.server.common.constraint.NoWhitespace; import im.turms.server.common.exception.TurmsBusinessException; +import im.turms.server.common.logging.core.logger.LoggerFactory; +import im.turms.server.common.logging.core.logger.Logger; import im.turms.server.common.mongo.IMongoCollectionInitializer; import im.turms.server.common.mongo.TurmsMongoClient; import im.turms.server.common.mongo.operation.option.Filter; @@ -35,7 +37,6 @@ import im.turms.service.constant.OperationResultConstant; import im.turms.service.workflow.access.http.permission.AdminPermission; import im.turms.service.workflow.dao.domain.admin.AdminRole; -import lombok.extern.log4j.Log4j2; import org.apache.commons.lang3.tuple.Triple; import org.hibernate.validator.constraints.Length; import org.springframework.beans.factory.annotation.Qualifier; @@ -56,11 +57,12 @@ /** * @author James Chen */ -@Log4j2 @Service @DependsOn(IMongoCollectionInitializer.BEAN_NAME) public class AdminRoleService { + private static final Logger LOGGER = LoggerFactory.getLogger(AdminRoleService.class); + private static final int MIN_ROLE_NAME_LIMIT = 1; private static final int MAX_ROLE_NAME_LIMIT = 32; private static final AdminRole ROOT_ROLE = @@ -96,11 +98,11 @@ public void listenAndLoadRoles() { roles.remove(roleId); } case INVALIDATE -> resetRoles(); - default -> log.fatal("Detect an illegal operation on AdminRole collection: " + event); + default -> LOGGER.fatal("Detected an illegal operation on AdminRole collection: " + event); } }) .onErrorContinue( - (throwable, o) -> log.error("Error while processing the change stream event of AdminRole: {}", o, throwable)) + (throwable, o) -> LOGGER.error("Error while processing the change stream event of AdminRole: {}", o, throwable)) .subscribe(); // Load diff --git a/turms-service/src/main/java/im/turms/service/workflow/service/impl/admin/AdminService.java b/turms-service/src/main/java/im/turms/service/workflow/service/impl/admin/AdminService.java index 6c8ccab2f0..f1c4934a2a 100644 --- a/turms-service/src/main/java/im/turms/service/workflow/service/impl/admin/AdminService.java +++ b/turms-service/src/main/java/im/turms/service/workflow/service/impl/admin/AdminService.java @@ -26,6 +26,8 @@ import im.turms.server.common.constant.TurmsStatusCode; import im.turms.server.common.constraint.NoWhitespace; import im.turms.server.common.exception.TurmsBusinessException; +import im.turms.server.common.logging.core.logger.LoggerFactory; +import im.turms.server.common.logging.core.logger.Logger; import im.turms.server.common.mongo.IMongoCollectionInitializer; import im.turms.server.common.mongo.TurmsMongoClient; import im.turms.server.common.mongo.operation.option.Filter; @@ -39,7 +41,6 @@ import im.turms.service.constant.OperationResultConstant; import im.turms.service.workflow.access.http.permission.AdminPermission; import im.turms.service.workflow.dao.domain.admin.Admin; -import lombok.extern.log4j.Log4j2; import org.apache.commons.lang3.RandomStringUtils; import org.hibernate.validator.constraints.Length; import org.springframework.beans.factory.annotation.Qualifier; @@ -63,11 +64,12 @@ /** * @author James Chen */ -@Log4j2 @Service @DependsOn(IMongoCollectionInitializer.BEAN_NAME) public class AdminService { + private static final Logger LOGGER = LoggerFactory.getLogger(AdminService.class); + private static final String ERROR_UPDATE_ADMIN_WITH_HIGHER_RANK = "Only a admin with a lower rank compared to the account can be created, updated, or deleted"; /** @@ -113,10 +115,10 @@ public void listenAndLoadAdmins(String initialRootPassword) { adminMap.remove(account); } case INVALIDATE -> adminMap.clear(); - default -> log.fatal("Detect an illegal operation on Admin collection: " + event); + default -> LOGGER.fatal("Detected an illegal operation on Admin collection: " + event); } }) - .onErrorContinue((throwable, o) -> log.error("Error while processing the change stream event of Admin: {}", o, throwable)) + .onErrorContinue((throwable, o) -> LOGGER.error("Error while processing the change stream event of Admin: {}", o, throwable)) .subscribe(); // Load diff --git a/turms-service/src/main/java/im/turms/service/workflow/service/impl/conversation/ConversationService.java b/turms-service/src/main/java/im/turms/service/workflow/service/impl/conversation/ConversationService.java index efa77e8c42..a53894a1ad 100644 --- a/turms-service/src/main/java/im/turms/service/workflow/service/impl/conversation/ConversationService.java +++ b/turms-service/src/main/java/im/turms/service/workflow/service/impl/conversation/ConversationService.java @@ -35,7 +35,6 @@ import im.turms.service.constant.DaoConstant; import im.turms.service.workflow.dao.domain.conversation.GroupConversation; import im.turms.service.workflow.dao.domain.conversation.PrivateConversation; -import lombok.extern.log4j.Log4j2; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.DependsOn; import org.springframework.stereotype.Service; @@ -55,7 +54,6 @@ /** * @author James Chen */ -@Log4j2 @Service @DependsOn(IMongoCollectionInitializer.BEAN_NAME) public class ConversationService { diff --git a/turms-service/src/main/java/im/turms/service/workflow/service/impl/group/GroupService.java b/turms-service/src/main/java/im/turms/service/workflow/service/impl/group/GroupService.java index 421f7ae933..778f61938f 100644 --- a/turms-service/src/main/java/im/turms/service/workflow/service/impl/group/GroupService.java +++ b/turms-service/src/main/java/im/turms/service/workflow/service/impl/group/GroupService.java @@ -31,6 +31,8 @@ import im.turms.server.common.constant.TurmsStatusCode; import im.turms.server.common.dao.util.OperationResultUtil; import im.turms.server.common.exception.TurmsBusinessException; +import im.turms.server.common.logging.core.logger.LoggerFactory; +import im.turms.server.common.logging.core.logger.Logger; import im.turms.server.common.mongo.IMongoCollectionInitializer; import im.turms.server.common.mongo.TurmsMongoClient; import im.turms.server.common.mongo.operation.option.Filter; @@ -52,7 +54,6 @@ import im.turms.service.workflow.service.impl.user.UserPermissionGroupService; import im.turms.service.workflow.service.impl.user.UserVersionService; import io.micrometer.core.instrument.Counter; -import lombok.extern.log4j.Log4j2; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.DependsOn; @@ -86,9 +87,10 @@ */ @Service @DependsOn(IMongoCollectionInitializer.BEAN_NAME) -@Log4j2 public class GroupService { + private static final Logger LOGGER = LoggerFactory.getLogger(GroupService.class); + private final Node node; private final TurmsMongoClient mongoClient; private final GroupTypeService groupTypeService; @@ -279,7 +281,7 @@ public Mono deleteGroupsAndGroupMembers( .then(conversationService.deleteGroupConversations(groupIds, session)) .then(groupVersionService.delete(groupIds, session)) .then(deleteSequenceIds - .doOnError(t -> log.error("Failed to remove the message sequence IDs for the group IDs: {}", groupIds, t))) + .doOnError(t -> LOGGER.error("Failed to remove the message sequence IDs for the group IDs: {}", groupIds, t))) .thenReturn(result); }); }) diff --git a/turms-service/src/main/java/im/turms/service/workflow/service/impl/group/GroupTypeService.java b/turms-service/src/main/java/im/turms/service/workflow/service/impl/group/GroupTypeService.java index babb6ed6ef..ba4b4b0629 100644 --- a/turms-service/src/main/java/im/turms/service/workflow/service/impl/group/GroupTypeService.java +++ b/turms-service/src/main/java/im/turms/service/workflow/service/impl/group/GroupTypeService.java @@ -31,6 +31,8 @@ import im.turms.server.common.constant.TurmsStatusCode; import im.turms.server.common.constraint.NoWhitespace; import im.turms.server.common.exception.TurmsBusinessException; +import im.turms.server.common.logging.core.logger.LoggerFactory; +import im.turms.server.common.logging.core.logger.Logger; import im.turms.server.common.mongo.IMongoCollectionInitializer; import im.turms.server.common.mongo.TurmsMongoClient; import im.turms.server.common.mongo.operation.option.Filter; @@ -40,7 +42,6 @@ import im.turms.service.constant.DaoConstant; import im.turms.service.constant.OperationResultConstant; import im.turms.service.workflow.dao.domain.group.GroupType; -import lombok.extern.log4j.Log4j2; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.DependsOn; import org.springframework.stereotype.Service; @@ -58,11 +59,12 @@ /** * @author James Chen */ -@Log4j2 @Service @DependsOn(IMongoCollectionInitializer.BEAN_NAME) public class GroupTypeService { + private static final Logger LOGGER = LoggerFactory.getLogger(GroupTypeService.class); + private final Node node; private final Map groupTypeMap = new ConcurrentHashMap<>(16); private final TurmsMongoClient mongoTemplate; @@ -101,11 +103,11 @@ public void initGroupTypes() { groupTypeMap.remove(groupTypeId); } case INVALIDATE -> groupTypeMap.keySet().removeIf(id -> !id.equals(DaoConstant.DEFAULT_GROUP_TYPE_ID)); - default -> log.fatal("Detect an illegal operation on GroupType collection: " + event); + default -> LOGGER.fatal("Detected an illegal operation on GroupType collection: " + event); } }) .onErrorContinue( - (throwable, o) -> log.error("Error while processing the change stream event of GroupType: {}", o, throwable)) + (throwable, o) -> LOGGER.error("Error while processing the change stream event of GroupType: {}", o, throwable)) .subscribe(); mongoTemplate.findAll(GroupType.class) .doOnNext(groupType -> groupTypeMap.put(groupType.getId(), groupType)) diff --git a/turms-service/src/main/java/im/turms/service/workflow/service/impl/message/MessageService.java b/turms-service/src/main/java/im/turms/service/workflow/service/impl/message/MessageService.java index efb721a6a8..7ce6fb5647 100644 --- a/turms-service/src/main/java/im/turms/service/workflow/service/impl/message/MessageService.java +++ b/turms-service/src/main/java/im/turms/service/workflow/service/impl/message/MessageService.java @@ -32,6 +32,8 @@ import im.turms.server.common.constant.TurmsStatusCode; import im.turms.server.common.dao.util.OperationResultUtil; import im.turms.server.common.exception.TurmsBusinessException; +import im.turms.server.common.logging.core.logger.LoggerFactory; +import im.turms.server.common.logging.core.logger.Logger; import im.turms.server.common.mongo.IMongoCollectionInitializer; import im.turms.server.common.mongo.TurmsMongoClient; import im.turms.server.common.mongo.operation.option.Filter; @@ -60,7 +62,6 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.PooledByteBufAllocator; import lombok.Getter; -import lombok.extern.log4j.Log4j2; import org.apache.commons.lang3.tuple.Pair; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; @@ -98,10 +99,11 @@ * @author James Chen */ @Service -@Log4j2 @DependsOn(IMongoCollectionInitializer.BEAN_NAME) public class MessageService { + private static final Logger LOGGER = LoggerFactory.getLogger(MessageService.class); + private static final byte[] GROUP_CONVERSATION_SEQUENCE_ID_PREFIX = new byte[]{'g', 'i'}; private static final byte[] PRIVATE_CONVERSATION_SEQUENCE_ID_PREFIX = new byte[]{'p', 'i'}; @@ -857,7 +859,7 @@ public Mono authAndSaveAndSendMessage( // No need to let the client wait to send notifications to recipients sendMessage(message, pair.getRight()) .onErrorResume(t -> { - log.error("Failed to send message", t); + LOGGER.error("Failed to send message", t); return Mono.empty(); }) .subscribe(); diff --git a/turms-service/src/main/java/im/turms/service/workflow/service/impl/message/OutboundMessageService.java b/turms-service/src/main/java/im/turms/service/workflow/service/impl/message/OutboundMessageService.java index d53247a74f..80cb2ee97b 100644 --- a/turms-service/src/main/java/im/turms/service/workflow/service/impl/message/OutboundMessageService.java +++ b/turms-service/src/main/java/im/turms/service/workflow/service/impl/message/OutboundMessageService.java @@ -23,11 +23,11 @@ import im.turms.common.constant.DeviceType; import im.turms.common.model.dto.notification.TurmsNotification; import im.turms.server.common.cluster.node.Node; -import im.turms.server.common.logging.RequestLoggingContext; +import im.turms.server.common.tracing.TracingCloseableContext; +import im.turms.server.common.tracing.TracingContext; import im.turms.server.common.mongo.IMongoCollectionInitializer; import im.turms.server.common.rpc.request.SendNotificationRequest; import im.turms.server.common.service.session.UserStatusService; -import im.turms.server.common.tracing.TracingCloseableContext; import im.turms.server.common.util.CollectionUtil; import im.turms.server.common.util.CollectorUtil; import im.turms.server.common.util.ProtoUtil; @@ -35,7 +35,6 @@ import im.turms.service.logging.ApiLoggingContext; import im.turms.service.logging.NotificationLogging; import io.netty.buffer.ByteBuf; -import lombok.extern.log4j.Log4j2; import org.springframework.context.annotation.DependsOn; import org.springframework.stereotype.Service; import reactor.core.publisher.Flux; @@ -57,7 +56,6 @@ * In other words, it will leak memory if cancellation occurs. */ @Service -@Log4j2 @DependsOn(IMongoCollectionInitializer.BEAN_NAME) public class OutboundMessageService { @@ -270,9 +268,9 @@ private Mono tryLogNotification(Mono mono, TurmsNotification n return; } boolean sent = Boolean.TRUE.equals(signal.get()); - RequestLoggingContext loggingContext = signal.getContextView() - .getOrDefault(RequestLoggingContext.CTX_KEY_NAME, RequestLoggingContext.DEFAULT); - try (TracingCloseableContext ignored = loggingContext.asCloseable()) { + TracingContext context = signal.getContextView() + .getOrDefault(TracingContext.CTX_KEY_NAME, TracingContext.DEFAULT); + try (TracingCloseableContext ignored = context.asCloseable()) { NotificationLogging.log(sent, notification, recipientCount); } }); diff --git a/turms-service/src/main/java/im/turms/service/workflow/service/impl/statistics/MetricsService.java b/turms-service/src/main/java/im/turms/service/workflow/service/impl/statistics/MetricsService.java index 0f1bb9b551..84d65dd532 100644 --- a/turms-service/src/main/java/im/turms/service/workflow/service/impl/statistics/MetricsService.java +++ b/turms-service/src/main/java/im/turms/service/workflow/service/impl/statistics/MetricsService.java @@ -19,14 +19,12 @@ import io.micrometer.core.instrument.MeterRegistry; import lombok.Getter; -import lombok.extern.log4j.Log4j2; import org.springframework.stereotype.Service; /** * @author James Chen */ @Service -@Log4j2 public class MetricsService { @Getter diff --git a/turms-service/src/main/java/im/turms/service/workflow/service/impl/statistics/StatisticsService.java b/turms-service/src/main/java/im/turms/service/workflow/service/impl/statistics/StatisticsService.java index f2e1a742d4..546161b0f1 100644 --- a/turms-service/src/main/java/im/turms/service/workflow/service/impl/statistics/StatisticsService.java +++ b/turms-service/src/main/java/im/turms/service/workflow/service/impl/statistics/StatisticsService.java @@ -20,10 +20,11 @@ import im.turms.server.common.cluster.node.Node; import im.turms.server.common.cluster.service.rpc.RpcErrorCode; import im.turms.server.common.cluster.service.rpc.exception.RpcException; +import im.turms.server.common.logging.core.logger.LoggerFactory; +import im.turms.server.common.logging.core.logger.Logger; import im.turms.server.common.property.TurmsPropertiesManager; import im.turms.server.common.rpc.request.CountOnlineUsersRequest; import im.turms.server.common.task.TrivialTaskManager; -import lombok.extern.log4j.Log4j2; import org.springframework.stereotype.Service; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -37,9 +38,10 @@ */ @Service -@Log4j2 public class StatisticsService { + private static final Logger LOGGER = LoggerFactory.getLogger(StatisticsService.class); + private final Node node; private static final String ONLINE_USERS_NUMBER_LOGGING_FORMAT = "The current online users number is: {}"; @@ -58,10 +60,10 @@ public StatisticsService( node.getSharedProperties().getService().getStatistics().isLogOnlineUsersNumber()) { countOnlineUsers() .onErrorResume(t -> { - log.error("Failed to count online users", t); + LOGGER.error("Failed to count online users", t); return Mono.empty(); }) - .doOnNext(count -> log.info(ONLINE_USERS_NUMBER_LOGGING_FORMAT, count)) + .doOnNext(count -> LOGGER.info(ONLINE_USERS_NUMBER_LOGGING_FORMAT, count)) .subscribe(); } }); diff --git a/turms-service/src/main/java/im/turms/service/workflow/service/impl/user/UserPermissionGroupService.java b/turms-service/src/main/java/im/turms/service/workflow/service/impl/user/UserPermissionGroupService.java index ffda8ff6c6..62f85c67e9 100644 --- a/turms-service/src/main/java/im/turms/service/workflow/service/impl/user/UserPermissionGroupService.java +++ b/turms-service/src/main/java/im/turms/service/workflow/service/impl/user/UserPermissionGroupService.java @@ -27,6 +27,8 @@ import im.turms.server.common.cluster.service.idgen.ServiceType; import im.turms.server.common.constant.TurmsStatusCode; import im.turms.server.common.exception.TurmsBusinessException; +import im.turms.server.common.logging.core.logger.LoggerFactory; +import im.turms.server.common.logging.core.logger.Logger; import im.turms.server.common.mongo.IMongoCollectionInitializer; import im.turms.server.common.mongo.TurmsMongoClient; import im.turms.server.common.mongo.operation.option.Filter; @@ -36,7 +38,6 @@ import im.turms.service.constant.DaoConstant; import im.turms.service.constant.OperationResultConstant; import im.turms.service.workflow.dao.domain.user.UserPermissionGroup; -import lombok.extern.log4j.Log4j2; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.DependsOn; import org.springframework.stereotype.Service; @@ -54,11 +55,12 @@ /** * @author James Chen */ -@Log4j2 @Service @DependsOn(IMongoCollectionInitializer.BEAN_NAME) public class UserPermissionGroupService { + private static final Logger LOGGER = LoggerFactory.getLogger(UserPermissionGroupService.class); + private final Map userPermissionGroupMap = new ConcurrentHashMap<>(16); private final TurmsMongoClient mongoTemplate; private final Node node; @@ -93,10 +95,10 @@ public UserPermissionGroupService( userPermissionGroupMap.remove(groupTypeId); } case INVALIDATE -> userPermissionGroupMap.clear(); - default -> log.fatal("Detect an illegal operation on UserPermissionGroup collection: " + event); + default -> LOGGER.fatal("Detected an illegal operation on UserPermissionGroup collection: " + event); } }) - .onErrorContinue((throwable, o) -> log + .onErrorContinue((throwable, o) -> LOGGER .error("Error while processing the change stream event of UserPermissionGroup: {}", o, throwable)) .subscribe(); } diff --git a/turms-service/src/main/java/im/turms/service/workflow/service/impl/user/UserService.java b/turms-service/src/main/java/im/turms/service/workflow/service/impl/user/UserService.java index 6a74346c1f..398c71f791 100644 --- a/turms-service/src/main/java/im/turms/service/workflow/service/impl/user/UserService.java +++ b/turms-service/src/main/java/im/turms/service/workflow/service/impl/user/UserService.java @@ -29,6 +29,8 @@ import im.turms.server.common.dao.domain.User; import im.turms.server.common.dao.util.OperationResultUtil; import im.turms.server.common.exception.TurmsBusinessException; +import im.turms.server.common.logging.core.logger.LoggerFactory; +import im.turms.server.common.logging.core.logger.Logger; import im.turms.server.common.mongo.IMongoCollectionInitializer; import im.turms.server.common.mongo.TurmsMongoClient; import im.turms.server.common.mongo.operation.option.Filter; @@ -50,7 +52,6 @@ import im.turms.service.workflow.service.impl.user.relationship.UserRelationshipService; import im.turms.service.workflow.service.util.DomainConstraintUtil; import io.micrometer.core.instrument.Counter; -import lombok.extern.log4j.Log4j2; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.DependsOn; import org.springframework.context.annotation.Lazy; @@ -72,9 +73,10 @@ */ @Component @DependsOn(IMongoCollectionInitializer.BEAN_NAME) -@Log4j2 public class UserService { + private static final Logger LOGGER = LoggerFactory.getLogger(UserService.class); + private final GroupMemberService groupMemberService; private final UserRelationshipService userRelationshipService; private final UserRelationshipGroupService userRelationshipGroupService; @@ -339,7 +341,7 @@ public Mono deleteUsers( .then(conversationService.deletePrivateConversations(userIds, session)) .then(userVersionService.delete(userIds, session).onErrorResume(t -> Mono.empty())) .then(messageService.deleteSequenceIds(false, userIds) - .doOnError(t -> log.error("Failed to remove the message sequence IDs for the user IDs: {}", userIds, t))) + .doOnError(t -> LOGGER.error("Failed to remove the message sequence IDs for the user IDs: {}", userIds, t))) .thenReturn(result); })) .retryWhen(DaoConstant.TRANSACTION_RETRY); diff --git a/turms-service/src/main/resources/application-dev.yaml b/turms-service/src/main/resources/application-dev.yaml index fb59835bcd..127144ceb7 100644 --- a/turms-service/src/main/resources/application-dev.yaml +++ b/turms-service/src/main/resources/application-dev.yaml @@ -23,7 +23,8 @@ turms: mongo: uri: mongodb://localhost:27017/turms-config-dev logging: - enable-console-appender: true + console: + enabled: true service: client-api: logging: diff --git a/turms-service/src/main/resources/application-test.yaml b/turms-service/src/main/resources/application-test.yaml index d09b18226f..894ce624e2 100644 --- a/turms-service/src/main/resources/application-test.yaml +++ b/turms-service/src/main/resources/application-test.yaml @@ -17,7 +17,8 @@ spring: turms: logging: - enable-console-appender: true + console: + enabled: true service: client-api: logging: diff --git a/turms-service/src/main/resources/banner.txt b/turms-service/src/main/resources/banner.txt deleted file mode 100644 index 9bcdc22dd5..0000000000 --- a/turms-service/src/main/resources/banner.txt +++ /dev/null @@ -1,7 +0,0 @@ -${AnsiColor.BRIGHT_CYAN} -${application.title}${application.formatted-version} - ______ - /_ __/__ __ _____ ____ ___ _____ - / / / / / // ___// __ `__ \ / ___/ - / / / /_/ // / / / / / / /(__ ) -/_/ \__,_//_/ /_/ /_/ /_//____/ \ No newline at end of file diff --git a/turms-service/src/main/resources/log4j2-spring.xml b/turms-service/src/main/resources/log4j2-spring.xml deleted file mode 100644 index b73226302d..0000000000 --- a/turms-service/src/main/resources/log4j2-spring.xml +++ /dev/null @@ -1,79 +0,0 @@ - - - - %xwEx - %5p - yyyy-MM-dd HH:mm:ss.SSS - %clr{%d{${sys:LOG_DATEFORMAT_PATTERN}}{GMT+0}}{faint} %clr{${sys:LOG_LEVEL_PATTERN}} %clr{${myctx:NODE_TYPE} ${myctx:NODE_ID}}{cyan} %clr{[%-19.19X{traceId}]}{magenta} %clr{%-15.15t}{faint} %clr{%-40.40c{1.}}{cyan} %clr{:}{faint} %m%n${sys:LOG_EXCEPTION_CONVERSION_WORD} - %d{${sys:LOG_DATEFORMAT_PATTERN}}{GMT+0} ${sys:LOG_LEVEL_PATTERN} ${myctx:NODE_TYPE} ${myctx:NODE_ID} %-19.19X{traceId} %t %-40.40c{1.} : %m%n${sys:LOG_EXCEPTION_CONVERSION_WORD} - %d{${sys:LOG_DATEFORMAT_PATTERN}}{GMT+0} ${sys:LOG_LEVEL_PATTERN} ${myctx:NODE_TYPE} ${myctx:NODE_ID} %-19.19X{traceId} %t : %m%n${sys:LOG_EXCEPTION_CONVERSION_WORD} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/turms-service/src/main/resources/log4j2.component.properties b/turms-service/src/main/resources/log4j2.component.properties deleted file mode 100644 index d76f35316d..0000000000 --- a/turms-service/src/main/resources/log4j2.component.properties +++ /dev/null @@ -1,2 +0,0 @@ -# org.apache.logging.log4j.core.util.Constants -Log4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector \ No newline at end of file