From 5950e9e1e81e1e901743c2e72a956b3c030a5064 Mon Sep 17 00:00:00 2001 From: Him188 Date: Fri, 6 Aug 2021 02:31:17 +0800 Subject: [PATCH] Improve logging: use Log4j internally and support markers Migrate usages of `MiraiLogger.create` to new API apiDump for new logging --- ...binary-compatibility-validator-android.api | 33 ++- .../api/binary-compatibility-validator.api | 28 ++- buildSrc/src/main/kotlin/Versions.kt | 7 +- mirai-core-api/build.gradle.kts | 10 +- .../src/commonMain/kotlin/event/Event.kt | 2 +- .../utils/ExternalResourceLeakObserver.kt | 2 +- .../internal/utils/LoggerAdapterImpls.kt | 90 ++++++-- .../internal/utils/MarkedMiraiLogger.kt | 70 ++++++ .../kotlin/utils/BotConfiguration.kt | 8 +- .../src/commonMain/kotlin/utils/DeviceInfo.kt | 1 + .../commonMain/kotlin/utils/LoggerAdapters.kt | 58 ++++- .../commonMain/kotlin/utils/MiraiLogger.kt | 209 +++++++++++++++--- .../kotlin/logging/Log4j2LoggingTest.kt | 88 ++++++++ .../src/commonTest/resources/log4j.properties | 13 ++ .../src/androidMain/kotlin/Actuals.kt | 15 +- .../src/commonMain/kotlin/Services.kt | 7 +- .../src/jvmMain/kotlin/Actuals.kt | 15 +- mirai-core/build.gradle.kts | 2 +- .../kotlin/test/initPlatform.android.kt | 2 +- .../commonMain/kotlin/message/imagesImpl.kt | 7 +- .../kotlin/network/components/PacketCodec.kt | 2 +- .../kotlin/network/components/ServerList.kt | 4 +- .../network/handler/NetworkHandlerSupport.kt | 3 +- .../handler/state/LoggingStateObserver.kt | 8 +- .../src/commonMain/kotlin/utils/SubLogger.kt | 56 +---- .../kotlin/utils/contentToString.kt | 3 +- .../network/component/EventDispatcherTest.kt | 5 +- .../AbstractMockNetworkHandlerTest.kt | 7 +- .../AbstractRealNetworkHandlerTest.kt | 2 +- .../framework/TestNetworkHandlerContext.kt | 6 +- .../KeepAliveNetworkHandlerSelectorTest.kt | 2 +- .../kotlin/test/initPlatform.common.kt | 13 +- .../src/commonTest/kotlin/test/printing.kt | 11 +- 33 files changed, 620 insertions(+), 169 deletions(-) create mode 100644 mirai-core-api/src/commonMain/kotlin/internal/utils/MarkedMiraiLogger.kt create mode 100644 mirai-core-api/src/commonTest/kotlin/logging/Log4j2LoggingTest.kt create mode 100644 mirai-core-api/src/commonTest/resources/log4j.properties diff --git a/binary-compatibility-validator/android/api/binary-compatibility-validator-android.api b/binary-compatibility-validator/android/api/binary-compatibility-validator-android.api index baacee8d3..f2dfdc70f 100644 --- a/binary-compatibility-validator/android/api/binary-compatibility-validator-android.api +++ b/binary-compatibility-validator/android/api/binary-compatibility-validator-android.api @@ -5651,15 +5651,20 @@ public abstract interface class net/mamoe/mirai/utils/MiraiLogger { public abstract fun error (Ljava/lang/String;)V public abstract fun error (Ljava/lang/String;Ljava/lang/Throwable;)V public fun error (Ljava/lang/Throwable;)V - public abstract fun getFollower ()Lnet/mamoe/mirai/utils/MiraiLogger; + public fun getFollower ()Lnet/mamoe/mirai/utils/MiraiLogger; public abstract fun getIdentity ()Ljava/lang/String; public abstract fun info (Ljava/lang/String;)V public abstract fun info (Ljava/lang/String;Ljava/lang/Throwable;)V public fun info (Ljava/lang/Throwable;)V + public fun isDebugEnabled ()Z public abstract fun isEnabled ()Z - public abstract fun plus (Lnet/mamoe/mirai/utils/MiraiLogger;)Lnet/mamoe/mirai/utils/MiraiLogger; + public fun isErrorEnabled ()Z + public fun isInfoEnabled ()Z + public fun isVerboseEnabled ()Z + public fun isWarningEnabled ()Z + public fun plus (Lnet/mamoe/mirai/utils/MiraiLogger;)Lnet/mamoe/mirai/utils/MiraiLogger; public static fun setDefaultLoggerCreator (Lkotlin/jvm/functions/Function1;)V - public abstract fun setFollower (Lnet/mamoe/mirai/utils/MiraiLogger;)V + public fun setFollower (Lnet/mamoe/mirai/utils/MiraiLogger;)V public abstract fun verbose (Ljava/lang/String;)V public abstract fun verbose (Ljava/lang/String;Ljava/lang/Throwable;)V public fun verbose (Ljava/lang/Throwable;)V @@ -5674,6 +5679,23 @@ public final class net/mamoe/mirai/utils/MiraiLogger$Companion { public final fun setDefaultLoggerCreator (Lkotlin/jvm/functions/Function1;)V } +public abstract interface class net/mamoe/mirai/utils/MiraiLogger$Factory { + public static final field INSTANCE Lnet/mamoe/mirai/utils/MiraiLogger$Factory$INSTANCE; + public fun create (Ljava/lang/Class;)Lnet/mamoe/mirai/utils/MiraiLogger; + public abstract fun create (Ljava/lang/Class;Ljava/lang/String;)Lnet/mamoe/mirai/utils/MiraiLogger; + public fun create (Lkotlin/reflect/KClass;)Lnet/mamoe/mirai/utils/MiraiLogger; + public fun create (Lkotlin/reflect/KClass;Ljava/lang/String;)Lnet/mamoe/mirai/utils/MiraiLogger; + public static synthetic fun create$default (Lnet/mamoe/mirai/utils/MiraiLogger$Factory;Ljava/lang/Class;Ljava/lang/String;ILjava/lang/Object;)Lnet/mamoe/mirai/utils/MiraiLogger; + public static synthetic fun create$default (Lnet/mamoe/mirai/utils/MiraiLogger$Factory;Lkotlin/reflect/KClass;Ljava/lang/String;ILjava/lang/Object;)Lnet/mamoe/mirai/utils/MiraiLogger; +} + +public final class net/mamoe/mirai/utils/MiraiLogger$Factory$INSTANCE : net/mamoe/mirai/utils/MiraiLogger$Factory { + public fun create (Ljava/lang/Class;)Lnet/mamoe/mirai/utils/MiraiLogger; + public fun create (Ljava/lang/Class;Ljava/lang/String;)Lnet/mamoe/mirai/utils/MiraiLogger; + public fun create (Lkotlin/reflect/KClass;)Lnet/mamoe/mirai/utils/MiraiLogger; + public fun create (Lkotlin/reflect/KClass;Ljava/lang/String;)Lnet/mamoe/mirai/utils/MiraiLogger; +} + public abstract class net/mamoe/mirai/utils/MiraiLoggerPlatformBase : net/mamoe/mirai/utils/MiraiLogger { public fun ()V public final fun debug (Ljava/lang/String;)V @@ -5939,7 +5961,12 @@ public final class net/mamoe/mirai/utils/SingleFileLogger : net/mamoe/mirai/util public fun info (Ljava/lang/String;)V public fun info (Ljava/lang/String;Ljava/lang/Throwable;)V public fun info (Ljava/lang/Throwable;)V + public fun isDebugEnabled ()Z public fun isEnabled ()Z + public fun isErrorEnabled ()Z + public fun isInfoEnabled ()Z + public fun isVerboseEnabled ()Z + public fun isWarningEnabled ()Z public fun plus (Lnet/mamoe/mirai/utils/MiraiLogger;)Lnet/mamoe/mirai/utils/MiraiLogger; public fun setFollower (Lnet/mamoe/mirai/utils/MiraiLogger;)V public fun verbose (Ljava/lang/String;)V diff --git a/binary-compatibility-validator/api/binary-compatibility-validator.api b/binary-compatibility-validator/api/binary-compatibility-validator.api index 01f874110..c03f9e2f1 100644 --- a/binary-compatibility-validator/api/binary-compatibility-validator.api +++ b/binary-compatibility-validator/api/binary-compatibility-validator.api @@ -5651,15 +5651,20 @@ public abstract interface class net/mamoe/mirai/utils/MiraiLogger { public abstract fun error (Ljava/lang/String;)V public abstract fun error (Ljava/lang/String;Ljava/lang/Throwable;)V public fun error (Ljava/lang/Throwable;)V - public abstract fun getFollower ()Lnet/mamoe/mirai/utils/MiraiLogger; + public fun getFollower ()Lnet/mamoe/mirai/utils/MiraiLogger; public abstract fun getIdentity ()Ljava/lang/String; public abstract fun info (Ljava/lang/String;)V public abstract fun info (Ljava/lang/String;Ljava/lang/Throwable;)V public fun info (Ljava/lang/Throwable;)V + public fun isDebugEnabled ()Z public abstract fun isEnabled ()Z - public abstract fun plus (Lnet/mamoe/mirai/utils/MiraiLogger;)Lnet/mamoe/mirai/utils/MiraiLogger; + public fun isErrorEnabled ()Z + public fun isInfoEnabled ()Z + public fun isVerboseEnabled ()Z + public fun isWarningEnabled ()Z + public fun plus (Lnet/mamoe/mirai/utils/MiraiLogger;)Lnet/mamoe/mirai/utils/MiraiLogger; public static fun setDefaultLoggerCreator (Lkotlin/jvm/functions/Function1;)V - public abstract fun setFollower (Lnet/mamoe/mirai/utils/MiraiLogger;)V + public fun setFollower (Lnet/mamoe/mirai/utils/MiraiLogger;)V public abstract fun verbose (Ljava/lang/String;)V public abstract fun verbose (Ljava/lang/String;Ljava/lang/Throwable;)V public fun verbose (Ljava/lang/Throwable;)V @@ -5674,6 +5679,23 @@ public final class net/mamoe/mirai/utils/MiraiLogger$Companion { public final fun setDefaultLoggerCreator (Lkotlin/jvm/functions/Function1;)V } +public abstract interface class net/mamoe/mirai/utils/MiraiLogger$Factory { + public static final field INSTANCE Lnet/mamoe/mirai/utils/MiraiLogger$Factory$INSTANCE; + public fun create (Ljava/lang/Class;)Lnet/mamoe/mirai/utils/MiraiLogger; + public abstract fun create (Ljava/lang/Class;Ljava/lang/String;)Lnet/mamoe/mirai/utils/MiraiLogger; + public fun create (Lkotlin/reflect/KClass;)Lnet/mamoe/mirai/utils/MiraiLogger; + public fun create (Lkotlin/reflect/KClass;Ljava/lang/String;)Lnet/mamoe/mirai/utils/MiraiLogger; + public static synthetic fun create$default (Lnet/mamoe/mirai/utils/MiraiLogger$Factory;Ljava/lang/Class;Ljava/lang/String;ILjava/lang/Object;)Lnet/mamoe/mirai/utils/MiraiLogger; + public static synthetic fun create$default (Lnet/mamoe/mirai/utils/MiraiLogger$Factory;Lkotlin/reflect/KClass;Ljava/lang/String;ILjava/lang/Object;)Lnet/mamoe/mirai/utils/MiraiLogger; +} + +public final class net/mamoe/mirai/utils/MiraiLogger$Factory$INSTANCE : net/mamoe/mirai/utils/MiraiLogger$Factory { + public fun create (Ljava/lang/Class;)Lnet/mamoe/mirai/utils/MiraiLogger; + public fun create (Ljava/lang/Class;Ljava/lang/String;)Lnet/mamoe/mirai/utils/MiraiLogger; + public fun create (Lkotlin/reflect/KClass;)Lnet/mamoe/mirai/utils/MiraiLogger; + public fun create (Lkotlin/reflect/KClass;Ljava/lang/String;)Lnet/mamoe/mirai/utils/MiraiLogger; +} + public abstract class net/mamoe/mirai/utils/MiraiLoggerPlatformBase : net/mamoe/mirai/utils/MiraiLogger { public fun ()V public final fun debug (Ljava/lang/String;)V diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index a8ea0e79b..e7c994fb3 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -40,7 +40,7 @@ object Versions { const val shadow = "6.1.0" const val slf4j = "1.7.30" - const val log4j = "2.13.3" + const val log4j = "2.14.1" const val asm = "9.1" const val difflib = "1.3.0" const val netty = "4.1.63.Final" @@ -82,12 +82,15 @@ val `ktor-client-core` = ktor("client-core", Versions.ktor) val `ktor-client-cio` = ktor("client-cio", Versions.ktor) val `ktor-client-okhttp` = ktor("client-okhttp", Versions.ktor) val `ktor-client-android` = ktor("client-android", Versions.ktor) +val `ktor-client-logging` = ktor("client-logging", Versions.ktor) val `ktor-network` = ktor("network", Versions.ktor) val `ktor-client-serialization-jvm` = ktor("client-serialization-jvm", Versions.ktor) -const val slf4j = "org.slf4j:slf4j-api:" + Versions.slf4j +const val `slf4j-api` = "org.slf4j:slf4j-api:" + Versions.slf4j const val `slf4j-simple` = "org.slf4j:slf4j-simple:" + Versions.slf4j const val `log4j-api` = "org.apache.logging.log4j:log4j-api:" + Versions.log4j +const val `log4j-core` = "org.apache.logging.log4j:log4j-core:" + Versions.log4j +const val `log4j-slf4j-impl` = "org.apache.logging.log4j:log4j-slf4j-impl:" + Versions.log4j val ATTRIBUTE_MIRAI_TARGET_PLATFORM: Attribute = Attribute.of("mirai.target.platform", String::class.java) diff --git a/mirai-core-api/build.gradle.kts b/mirai-core-api/build.gradle.kts index c3956a086..d1ec6d2eb 100644 --- a/mirai-core-api/build.gradle.kts +++ b/mirai-core-api/build.gradle.kts @@ -66,8 +66,8 @@ kotlin { api(`ktor-client-core`) api(`ktor-network`) - compileOnly(`log4j-api`) - compileOnly(slf4j) + implementation(`log4j-api`) + compileOnly(`slf4j-api`) // they use Kotlin 1.3 so we need to ignore transitive dependencies @@ -77,6 +77,12 @@ kotlin { } } + commonTest { + dependencies { + runtimeOnly(`log4j-core`) + } + } + if (isAndroidSDKAvailable) { val androidMain by getting { dependsOn(commonMain) diff --git a/mirai-core-api/src/commonMain/kotlin/event/Event.kt b/mirai-core-api/src/commonMain/kotlin/event/Event.kt index a20cbf29c..77215dd23 100644 --- a/mirai-core-api/src/commonMain/kotlin/event/Event.kt +++ b/mirai-core-api/src/commonMain/kotlin/event/Event.kt @@ -190,7 +190,7 @@ internal open class _EventBroadcast { } } - private val topLevelEventLogger by lazy { MiraiLogger.create("EventPipeline") } + private val topLevelEventLogger by lazy { MiraiLogger.Factory.create(Event::class, "EventPipeline") } } /** diff --git a/mirai-core-api/src/commonMain/kotlin/internal/utils/ExternalResourceLeakObserver.kt b/mirai-core-api/src/commonMain/kotlin/internal/utils/ExternalResourceLeakObserver.kt index 7b251bbd6..e63f446b6 100644 --- a/mirai-core-api/src/commonMain/kotlin/internal/utils/ExternalResourceLeakObserver.kt +++ b/mirai-core-api/src/commonMain/kotlin/internal/utils/ExternalResourceLeakObserver.kt @@ -21,7 +21,7 @@ internal object ExternalResourceLeakObserver : Runnable { private val queue = ReferenceQueue() private val references = ConcurrentLinkedDeque() private val logger by lazy { - MiraiLogger.create("ExternalResourceLeakObserver") + MiraiLogger.Factory.create(ExternalResourceLeakObserver::class) } internal class ERReference( diff --git a/mirai-core-api/src/commonMain/kotlin/internal/utils/LoggerAdapterImpls.kt b/mirai-core-api/src/commonMain/kotlin/internal/utils/LoggerAdapterImpls.kt index 4d7d6e928..866309687 100644 --- a/mirai-core-api/src/commonMain/kotlin/internal/utils/LoggerAdapterImpls.kt +++ b/mirai-core-api/src/commonMain/kotlin/internal/utils/LoggerAdapterImpls.kt @@ -10,83 +10,127 @@ package net.mamoe.mirai.internal.utils import net.mamoe.mirai.utils.MiraiLoggerPlatformBase -import org.slf4j.Logger -import java.util.logging.Level +import org.apache.logging.log4j.Marker +import org.apache.logging.log4j.MarkerManager +import java.util.logging.Level as JulLevel +import java.util.logging.Logger as JulLogger -internal class Log4jLogger(private val logger: org.apache.logging.log4j.Logger) : MiraiLoggerPlatformBase() { +internal class Log4jLoggerAdapter( + private val logger: org.apache.logging.log4j.Logger, + override val marker: Marker?, +) : MiraiLoggerPlatformBase(), MarkedMiraiLogger { override fun verbose0(message: String?, e: Throwable?) { - logger.trace(message, e) + val marker = marker + if (marker != null) logger.trace(marker, message, e) + else logger.trace(message, e) } override fun debug0(message: String?, e: Throwable?) { - logger.debug(message, e) + val marker = marker + if (marker != null) logger.debug(marker, message, e) + else logger.debug(message, e) } override fun info0(message: String?, e: Throwable?) { - logger.info(message, e) + val marker = marker + if (marker != null) logger.info(marker, message, e) + else logger.info(message, e) } override fun warning0(message: String?, e: Throwable?) { - logger.warn(message, e) + val marker = marker + if (marker != null) logger.warn(marker, message, e) + else logger.warn(message, e) } override fun error0(message: String?, e: Throwable?) { - logger.error(message, e) + val marker = marker + if (marker != null) logger.error(marker, message, e) + else logger.error(message, e) } - override val identity: String? - get() = logger.name + override val isVerboseEnabled: Boolean get() = logger.isTraceEnabled + override val isDebugEnabled: Boolean get() = logger.isDebugEnabled + override val isInfoEnabled: Boolean get() = logger.isInfoEnabled + override val isWarningEnabled: Boolean get() = logger.isWarnEnabled + override val isErrorEnabled: Boolean get() = logger.isErrorEnabled + + override val identity: String? get() = logger.name + + override fun subLogger(name: String): MarkedMiraiLogger { + return Log4jLoggerAdapter(logger, Marker(name, marker)) + } } -internal class Slf4jLogger(private val logger: Logger) : MiraiLoggerPlatformBase() { +internal val MARKER_MIRAI by lazy { MarkerManager.getMarker("mirai") } + +internal class Slf4jLoggerAdapter(private val logger: org.slf4j.Logger, private val marker: org.slf4j.Marker?) : + MiraiLoggerPlatformBase() { override fun verbose0(message: String?, e: Throwable?) { - logger.trace(message, e) + if (marker == null) logger.trace(message, e) + else logger.trace(marker, message, e) } override fun debug0(message: String?, e: Throwable?) { - logger.debug(message, e) + if (marker == null) logger.debug(message, e) + else logger.debug(marker, message, e) } override fun info0(message: String?, e: Throwable?) { - logger.info(message, e) + if (marker == null) logger.info(message, e) + else logger.info(marker, message, e) } override fun warning0(message: String?, e: Throwable?) { - logger.warn(message, e) + if (marker == null) logger.warn(message, e) + else logger.warn(marker, message, e) } override fun error0(message: String?, e: Throwable?) { - logger.error(message, e) + if (marker == null) logger.error(message, e) + else logger.error(marker, message, e) } + override val isVerboseEnabled: Boolean get() = logger.isTraceEnabled + override val isDebugEnabled: Boolean get() = logger.isDebugEnabled + override val isInfoEnabled: Boolean get() = logger.isInfoEnabled + override val isWarningEnabled: Boolean get() = logger.isWarnEnabled + override val isErrorEnabled: Boolean get() = logger.isErrorEnabled + override val identity: String? get() = logger.name } -internal class JdkLogger(private val logger: java.util.logging.Logger) : MiraiLoggerPlatformBase() { +internal class JdkLoggerAdapter(private val logger: JulLogger) : MiraiLoggerPlatformBase() { override fun verbose0(message: String?, e: Throwable?) { - logger.log(Level.FINER, message, e) + logger.log(JulLevel.FINEST, message, e) } override fun debug0(message: String?, e: Throwable?) { - logger.log(Level.FINEST, message, e) + logger.log(JulLevel.FINER, message, e) } override fun info0(message: String?, e: Throwable?) { - logger.log(Level.INFO, message, e) + logger.log(JulLevel.INFO, message, e) } override fun warning0(message: String?, e: Throwable?) { - logger.log(Level.WARNING, message, e) + logger.log(JulLevel.WARNING, message, e) } override fun error0(message: String?, e: Throwable?) { - logger.log(Level.SEVERE, message, e) + logger.log(JulLevel.SEVERE, message, e) } + override val isVerboseEnabled: Boolean get() = logger.isLoggable(JulLevel.FINE) + override val isDebugEnabled: Boolean get() = logger.isLoggable(JulLevel.FINEST) + override val isInfoEnabled: Boolean get() = logger.isLoggable(JulLevel.INFO) + override val isWarningEnabled: Boolean get() = logger.isLoggable(JulLevel.WARNING) + override val isErrorEnabled: Boolean get() = logger.isLoggable(JulLevel.SEVERE) + override val identity: String? get() = logger.name -} \ No newline at end of file +} diff --git a/mirai-core-api/src/commonMain/kotlin/internal/utils/MarkedMiraiLogger.kt b/mirai-core-api/src/commonMain/kotlin/internal/utils/MarkedMiraiLogger.kt new file mode 100644 index 000000000..60a72633b --- /dev/null +++ b/mirai-core-api/src/commonMain/kotlin/internal/utils/MarkedMiraiLogger.kt @@ -0,0 +1,70 @@ +/* + * Copyright 2019-2021 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.internal.utils + +import net.mamoe.mirai.utils.MiraiLogger +import org.apache.logging.log4j.Marker +import org.apache.logging.log4j.MarkerManager + +/** + * 内部添加 [Marker] 支持, 并兼容旧 [MiraiLogger] API. + */ +internal interface MarkedMiraiLogger : MiraiLogger { + val marker: Marker? + + /** + * Create an implementation-specific [MarkedMiraiLogger]. + * + * Do not call the extension `MiraiLogger.subLogger` inside the function body. + */ + fun subLogger(name: String): MarkedMiraiLogger +} + +internal fun Marker(name: String, parents: Marker?): Marker { + return MarkerManager.getMarker(name).apply { if (parents != null) addParents(parents) } +} + +internal fun Marker(name: String, vararg parents: Marker?): Marker { + return MarkerManager.getMarker(name).apply { + parents.forEach { if (it != null) addParents(it) } + } +} + +internal val MiraiLogger.markerOrNull get() = (this as? MarkedMiraiLogger)?.marker + +/** + * Create a marked logger whose marker is a child of this' marker. + * + * Calling [MarkedMiraiLogger.subLogger] if possible, and creating [MiraiLoggerMarkedWrapper] otherwise. + */ +internal fun MiraiLogger.subLogger(name: String): MarkedMiraiLogger { + return subLoggerImpl(this, name) +} + +// used by mirai-core +internal fun subLoggerImpl(origin: MiraiLogger, name: String): MarkedMiraiLogger { + return if (origin is MarkedMiraiLogger) { + // origin can be Log4JAdapter or MiraiLoggerMarkedWrapper which delegates a non-Log4JAdapter. + origin.subLogger(name) // Log4JAdapter natively supports Markers. + } else { + // origin does not support Markers, so we add a wrapper for it. + MiraiLoggerMarkedWrapper(origin, Marker(name, origin.markerOrNull ?: MARKER_MIRAI)) + } +} + +/** + * 仅当日志系统使用的不是 Log4J 时才会构造 [MiraiLoggerMarkedWrapper]. + */ +private class MiraiLoggerMarkedWrapper( + val origin: MiraiLogger, + override val marker: Marker +) : MiraiLogger by origin, MarkedMiraiLogger { + override fun subLogger(name: String): MarkedMiraiLogger = MiraiLoggerMarkedWrapper(origin, Marker(name, marker)) +} \ No newline at end of file diff --git a/mirai-core-api/src/commonMain/kotlin/utils/BotConfiguration.kt b/mirai-core-api/src/commonMain/kotlin/utils/BotConfiguration.kt index 4ffef8a33..b609d4a7b 100644 --- a/mirai-core-api/src/commonMain/kotlin/utils/BotConfiguration.kt +++ b/mirai-core-api/src/commonMain/kotlin/utils/BotConfiguration.kt @@ -326,7 +326,9 @@ public open class BotConfiguration { // open for Java * * @see MiraiLogger */ - public var botLoggerSupplier: ((Bot) -> MiraiLogger) = { MiraiLogger.create("Bot ${it.id}") } + public var botLoggerSupplier: ((Bot) -> MiraiLogger) = { + MiraiLogger.Factory.create(Bot::class, "Bot ${it.id}") + } /** * 网络层日志构造器 @@ -338,7 +340,9 @@ public open class BotConfiguration { // open for Java * * @see MiraiLogger */ - public var networkLoggerSupplier: ((Bot) -> MiraiLogger) = { MiraiLogger.create("Net ${it.id}") } + public var networkLoggerSupplier: ((Bot) -> MiraiLogger) = { + MiraiLogger.Factory.create(Bot::class, "Net ${it.id}") + } /** diff --git a/mirai-core-api/src/commonMain/kotlin/utils/DeviceInfo.kt b/mirai-core-api/src/commonMain/kotlin/utils/DeviceInfo.kt index 48dd90967..41dc6d4f2 100644 --- a/mirai-core-api/src/commonMain/kotlin/utils/DeviceInfo.kt +++ b/mirai-core-api/src/commonMain/kotlin/utils/DeviceInfo.kt @@ -60,6 +60,7 @@ public class DeviceInfo( ) public companion object { + internal val logger = MiraiLogger.Factory.create(DeviceInfo::class, "DeviceInfo") /** * 加载一个设备信息. 若文件不存在或为空则随机并创建一个设备信息保存. diff --git a/mirai-core-api/src/commonMain/kotlin/utils/LoggerAdapters.kt b/mirai-core-api/src/commonMain/kotlin/utils/LoggerAdapters.kt index 68e586543..85f24ca01 100644 --- a/mirai-core-api/src/commonMain/kotlin/utils/LoggerAdapters.kt +++ b/mirai-core-api/src/commonMain/kotlin/utils/LoggerAdapters.kt @@ -7,25 +7,71 @@ * https://github.com/mamoe/mirai/blob/master/LICENSE */ +@file:Suppress("unused") + package net.mamoe.mirai.utils -import net.mamoe.mirai.internal.utils.JdkLogger -import net.mamoe.mirai.internal.utils.Log4jLogger -import net.mamoe.mirai.internal.utils.Slf4jLogger +import net.mamoe.mirai.internal.utils.JdkLoggerAdapter +import net.mamoe.mirai.internal.utils.Log4jLoggerAdapter +import net.mamoe.mirai.internal.utils.MARKER_MIRAI +import net.mamoe.mirai.internal.utils.Slf4jLoggerAdapter +import org.apache.logging.log4j.LogManager +import org.apache.logging.log4j.Marker +import org.apache.logging.log4j.MarkerManager +/** + * [SLF4J][org.slf4j.Logger], [LOG4J][org.apache.logging.log4j.Logger] 或 [JUL][java.util.logging.Logger] 到 [MiraiLogger] 的转换器. + */ public object LoggerAdapters { + /** + * 使用 [LOG4J2][org.apache.logging.log4j.Logger] 接管全局 Mirai 日志系统. 请在调用 Mirai API 任何其他 API 前调用该方法. + * + * 注意, 若已经通过 service 方式提供 [MiraiLogger.Factory] 来接管日志系统, 则本方法无效. + * + * @since 2.7 + */ + @JvmStatic + @MiraiExperimentalApi + public fun useLog4j2() { + DefaultFactory.override { requester, identity -> + val logger = LogManager.getLogger(requester) + Log4jLoggerAdapter(logger, MarkerManager.getMarker(identity ?: logger.name).addParents(MARKER_MIRAI)) + } + } + + + /** + * 将 [java.util.logging.Logger] 转换作为 [MiraiLogger] 使用. + */ @JvmStatic public fun java.util.logging.Logger.asMiraiLogger(): MiraiLogger { - return JdkLogger(this) + return JdkLoggerAdapter(this) } + /** + * 将 [org.apache.logging.log4j.Logger] 转换作为 [MiraiLogger] 使用. + */ @JvmStatic public fun org.apache.logging.log4j.Logger.asMiraiLogger(): MiraiLogger { - return Log4jLogger(this) + return Log4jLoggerAdapter(this, null) } + /** + * 将 [org.slf4j.Logger] 转换作为 [MiraiLogger] 使用. + */ @JvmStatic public fun org.slf4j.Logger.asMiraiLogger(): MiraiLogger { - return Slf4jLogger(this) + return Slf4jLoggerAdapter(this, null) + } + + /** + * 将 [org.apache.logging.log4j.Logger] 转换作为 [MiraiLogger] 使用. + * + * @since 2.7 + */ + @MiraiExperimentalApi + @JvmStatic + public fun org.apache.logging.log4j.Logger.asMiraiLogger(marker: Marker): MiraiLogger { + return Log4jLoggerAdapter(this, marker) } } \ No newline at end of file diff --git a/mirai-core-api/src/commonMain/kotlin/utils/MiraiLogger.kt b/mirai-core-api/src/commonMain/kotlin/utils/MiraiLogger.kt index 320c9ffc9..307bfbec2 100644 --- a/mirai-core-api/src/commonMain/kotlin/utils/MiraiLogger.kt +++ b/mirai-core-api/src/commonMain/kotlin/utils/MiraiLogger.kt @@ -13,7 +13,8 @@ package net.mamoe.mirai.utils -import net.mamoe.mirai.Bot +import java.util.* +import kotlin.reflect.KClass /** * 给这个 logger 添加一个开关, 用于控制是否记录 log @@ -22,57 +23,106 @@ import net.mamoe.mirai.Bot public fun MiraiLogger.withSwitch(default: Boolean = true): MiraiLoggerWithSwitch = MiraiLoggerWithSwitch(this, default) /** - * 日志记录器. 所有的输出均依赖于它. - * 不同的对象可拥有只属于自己的 logger. 通过 [identity] 来区分. + * 日志记录器. * - * 注意: 如果你需要重新实现日志, 请不要直接实现这个接口, 请继承 [MiraiLoggerPlatformBase] + * ## Mirai 日志系统 * - * 在定义 logger 变量时, 请一直使用 [MiraiLogger] 或者 [MiraiLoggerWithSwitch]. + * Mirai 内建简单的日志系统, 即 [MiraiLogger]. [MiraiLogger] 的实现有 [SimpleLogger], [PlatformLogger], [SilentLogger]. * - * Mirai 内建三种日志实现, 分别是 [SimpleLogger], [PlatformLogger], [SilentLogger] + * [MiraiLogger] 仅能处理简单的日志任务, 通常推荐使用 [SLF4J][org.slf4j.Logger], [LOG4J][org.apache.logging.log4j.Logger] 等日志库. + * + * ## 使用第三方日志库接管 Mirai 日志系统 + * + * 使用 [LoggerAdapters], 将第三方日志 `Logger` 转为 [MiraiLogger]. 然后通过 [MiraiLogger.setDefaultLoggerCreator] 全局覆盖日志. + * + * ## 实现或使用 [MiraiLogger] + * + * 不建议实现或使用 [MiraiLogger]. 请优先考虑使用上述第三方框架. [MiraiLogger] 仅应用于兼容旧版本代码. * * @see SimpleLogger 简易 logger, 它将所有的日志记录操作都转移给 lambda `(String?, Throwable?) -> Unit` * @see PlatformLogger 各个平台下的默认日志记录实现. * @see SilentLogger 忽略任何日志记录操作的 logger 实例. + * @see LoggerAdapters * * @see MiraiLoggerPlatformBase 平台通用基础实现. 若 Mirai 自带的日志系统无法满足需求, 请继承这个类并实现其抽象函数. */ public interface MiraiLogger { + /** + * 可以 service 实现的方式覆盖. + * + * @since 2.7 + */ + public interface Factory { + /** + * 创建 [MiraiLogger] 实例. + * + * @param requester 请求创建 [MiraiLogger] 的对象的 class + * @param identity 对象标记 (备注) + */ + public fun create(requester: KClass<*>, identity: String? = null): MiraiLogger = + this.create(requester.java, identity) + + /** + * 创建 [MiraiLogger] 实例. + * + * @param requester 请求创建 [MiraiLogger] 的对象的 class + * @param identity 对象标记 (备注) + */ + public fun create(requester: Class<*>, identity: String? = null): MiraiLogger + + /** + * 创建 [MiraiLogger] 实例. + * + * @param requester 请求创建 [MiraiLogger] 的对象 + */ + public fun create(requester: KClass<*>): MiraiLogger = create(requester, null) + + /** + * 创建 [MiraiLogger] 实例. + * + * @param requester 请求创建 [MiraiLogger] 的对象 + */ + public fun create(requester: Class<*>): MiraiLogger = create(requester, null) + + public companion object INSTANCE : Factory by loadService({ DefaultFactory() }) + } + public companion object { /** * 顶层日志, 仅供 Mirai 内部使用. */ @MiraiInternalApi @MiraiExperimentalApi - public val TopLevel: MiraiLogger by lazy { create("Mirai") } - - @Volatile - private var defaultLogger: (identity: String?) -> MiraiLogger = { PlatformLogger(it) } + @Deprecated("Deprecated.") + public val TopLevel: MiraiLogger by lazy { Factory.create(MiraiLogger::class, "Mirai") } /** - * 可直接修改这个变量的值来重定向日志输出. + * 已弃用, 请实现 service [net.mamoe.mirai.utils.MiraiLogger.Factory] 并以 [ServiceLoader] 支持的方式提供. */ + @Suppress("DeprecatedCallableAddReplaceWith") + @Deprecated( + "Please set factory by providing an service of type net.mamoe.mirai.utils.MiraiLogger.Factory", + level = DeprecationLevel.WARNING + ) @JvmStatic public fun setDefaultLoggerCreator(creator: (identity: String?) -> MiraiLogger) { - defaultLogger = creator + DefaultFactory.override { _, identity -> creator(identity) } } /** - * 用于创建默认的日志记录器. 在一些需要使用日志的 Mirai 的组件, 如 [Bot], 都会通过这个函数构造日志记录器. - * - * **注意:** 请务必将所有的输出定向到日志记录系统, 否则在某些情况下 (如 web 控制台中) 将无法接收到输出 - * - * **注意:** 请为日志做好分类, 即不同的模块使用不同的 [MiraiLogger]. - * 如, [Bot] 中使用 `identity` 为 "Bot(qqId)" 的 [MiraiLogger] - * 而 [Bot] 的网络处理中使用 `identity` 为 "BotNetworkHandler". + * 旧版本用于创建 [MiraiLogger]. 已弃用. 请使用 [MiraiLogger.Factory.INSTANCE.create]. * * @see setDefaultLoggerCreator */ + @Deprecated( + "Please use MiraiLogger.Factory.create", ReplaceWith( + "MiraiLogger.Factory.create(YourClass::class, identity)", + "net.mamoe.mirai.utils.MiraiLogger" + ), level = DeprecationLevel.WARNING + ) @JvmStatic - public fun create(identity: String?): MiraiLogger { - return defaultLogger.invoke(identity) - } + public fun create(identity: String?): MiraiLogger = Factory.create(MiraiLogger::class, identity) } /** @@ -92,6 +142,61 @@ public interface MiraiLogger { */ public val isEnabled: Boolean + /** + * 当 VERBOSE 级别的日志启用时返回 `true`. + * + * 若 [isEnabled] 为 `false`, 返回 `false`. + * 在使用 [SLF4J][org.slf4j.Logger], [LOG4J][org.apache.logging.log4j.Logger] 或 [JUL][java.util.logging.Logger] 时返回真实配置值. + * 其他情况下返回 [isEnabled] 的值. + * + * @since 2.7 + */ + public val isVerboseEnabled: Boolean get() = isEnabled + + /** + * 当 DEBUG 级别的日志启用时返回 `true` + * + * 若 [isEnabled] 为 `false`, 返回 `false`. + * 在使用 [SLF4J][org.slf4j.Logger], [LOG4J][org.apache.logging.log4j.Logger] 或 [JUL][java.util.logging.Logger] 时返回真实配置值. + * 其他情况下返回 [isEnabled] 的值. + * + * @since 2.7 + */ + public val isDebugEnabled: Boolean get() = isEnabled + + /** + * 当 INFO 级别的日志启用时返回 `true` + * + * 若 [isEnabled] 为 `false`, 返回 `false`. + * 在使用 [SLF4J][org.slf4j.Logger], [LOG4J][org.apache.logging.log4j.Logger] 或 [JUL][java.util.logging.Logger] 时返回真实配置值. + * 其他情况下返回 [isEnabled] 的值. + * + * @since 2.7 + */ + public val isInfoEnabled: Boolean get() = isEnabled + + /** + * 当 WARNING 级别的日志启用时返回 `true` + * + * 若 [isEnabled] 为 `false`, 返回 `false`. + * 在使用 [SLF4J][org.slf4j.Logger], [LOG4J][org.apache.logging.log4j.Logger] 或 [JUL][java.util.logging.Logger] 时返回真实配置值. + * 其他情况下返回 [isEnabled] 的值. + * + * @since 2.7 + */ + public val isWarningEnabled: Boolean get() = isEnabled + + /** + * 当 ERROR 级别的日志启用时返回 `true` + * + * 若 [isEnabled] 为 `false`, 返回 `false`. + * 在使用 [SLF4J][org.slf4j.Logger], [LOG4J][org.apache.logging.log4j.Logger] 或 [JUL][java.util.logging.Logger] 时返回真实配置值. + * 其他情况下返回 [isEnabled] 的值. + * + * @since 2.7 + */ + public val isErrorEnabled: Boolean get() = isEnabled + /** * 随从. 在 this 中调用所有方法后都应继续往 [follower] 传递调用. * [follower] 的存在可以让一次日志被多个日志记录器记录. @@ -102,7 +207,11 @@ public interface MiraiLogger { * * 当然, 多个 logger 也可以加在一起: `val logger = bot.logger + MynLogger() + MyLogger2()` */ + @Suppress("UNUSED_PARAMETER") + @Deprecated("follower 设计不佳, 请避免使用", level = DeprecationLevel.WARNING) // deprecated since 2.7 public var follower: MiraiLogger? + get() = null + set(value) {} /** * 记录一个 `verbose` 级别的日志. @@ -164,48 +273,50 @@ public interface MiraiLogger { * * @return [follower] */ - public operator fun plus(follower: T): T + @Suppress("DeprecatedCallableAddReplaceWith") + @Deprecated("plus 设计不佳, 请避免使用.", level = DeprecationLevel.WARNING) // deprecated since 2.7 + public operator fun plus(follower: T): T = follower } public inline fun MiraiLogger.verbose(message: () -> String) { - if (isEnabled) verbose(message()) + if (isVerboseEnabled) verbose(message()) } public inline fun MiraiLogger.verbose(message: () -> String, e: Throwable?) { - if (isEnabled) verbose(message(), e) + if (isVerboseEnabled) verbose(message(), e) } public inline fun MiraiLogger.debug(message: () -> String?) { - if (isEnabled) debug(message()) + if (isDebugEnabled) debug(message()) } public inline fun MiraiLogger.debug(message: () -> String?, e: Throwable?) { - if (isEnabled) debug(message(), e) + if (isDebugEnabled) debug(message(), e) } public inline fun MiraiLogger.info(message: () -> String?) { - if (isEnabled) info(message()) + if (isInfoEnabled) info(message()) } public inline fun MiraiLogger.info(message: () -> String?, e: Throwable?) { - if (isEnabled) info(message(), e) + if (isInfoEnabled) info(message(), e) } public inline fun MiraiLogger.warning(message: () -> String?) { - if (isEnabled) warning(message()) + if (isWarningEnabled) warning(message()) } public inline fun MiraiLogger.warning(message: () -> String?, e: Throwable?) { - if (isEnabled) warning(message(), e) + if (isWarningEnabled) warning(message(), e) } public inline fun MiraiLogger.error(message: () -> String?) { - if (isEnabled) error(message()) + if (isErrorEnabled) error(message()) } public inline fun MiraiLogger.error(message: () -> String?, e: Throwable?) { - if (isEnabled) error(message(), e) + if (isErrorEnabled) error(message(), e) } /** @@ -353,8 +464,12 @@ public class MiraiLoggerWithSwitch internal constructor(private val delegate: Mi * @see PlatformLogger * @see SimpleLogger */ +@Suppress("DEPRECATION") public abstract class MiraiLoggerPlatformBase : MiraiLogger { public override val isEnabled: Boolean get() = true + + @Suppress("OverridingDeprecatedMember") + @Deprecated("follower 设计不佳, 请避免使用", level = DeprecationLevel.WARNING) // deprecated since 2.7 public final override var follower: MiraiLogger? = null public final override fun verbose(message: String?) { @@ -428,8 +543,36 @@ public abstract class MiraiLoggerPlatformBase : MiraiLogger { protected open fun error0(message: String?): Unit = error0(message, null) protected abstract fun error0(message: String?, e: Throwable?) + @Suppress("OverridingDeprecatedMember") + @Deprecated("plus 设计不佳, 请避免使用.", level = DeprecationLevel.WARNING) // deprecated since 2.7 public override operator fun plus(follower: T): T { this.follower = follower return follower } } + + +internal class DefaultFactory : MiraiLogger.Factory { + init { + INSTANCE = this + } + + companion object { + lateinit var INSTANCE: DefaultFactory + private set + + fun override(lambda: (requester: Class<*>, identity: String?) -> MiraiLogger) { + INSTANCE.override = lambda + } + } + + private var override: ((requester: Class<*>, identity: String?) -> MiraiLogger)? = + null // 支持 LoggerAdapters 以及兼容旧版本 + + override fun create(requester: Class<*>, identity: String?): MiraiLogger { + val override = override + return if (override != null) override(requester, identity) else PlatformLogger( + identity ?: requester.kotlin.simpleName ?: requester.simpleName + ) + } +} diff --git a/mirai-core-api/src/commonTest/kotlin/logging/Log4j2LoggingTest.kt b/mirai-core-api/src/commonTest/kotlin/logging/Log4j2LoggingTest.kt new file mode 100644 index 000000000..8c8822f9a --- /dev/null +++ b/mirai-core-api/src/commonTest/kotlin/logging/Log4j2LoggingTest.kt @@ -0,0 +1,88 @@ +/* + * Copyright 2019-2021 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.logging + +import net.mamoe.mirai.Bot +import net.mamoe.mirai.internal.utils.Log4jLoggerAdapter +import net.mamoe.mirai.internal.utils.MARKER_MIRAI +import net.mamoe.mirai.internal.utils.Marker +import net.mamoe.mirai.internal.utils.subLogger +import net.mamoe.mirai.utils.DefaultFactory +import net.mamoe.mirai.utils.LoggerAdapters.asMiraiLogger +import net.mamoe.mirai.utils.MiraiLogger +import org.apache.logging.log4j.LogManager +import org.junit.jupiter.api.BeforeEach +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertIs +import kotlin.test.assertSame + +internal class Log4j2LoggingTest { + @BeforeEach + fun init() { + DefaultFactory.override { requester, identity -> + LogManager.getLogger(requester).asMiraiLogger(Marker(identity ?: requester.simpleName, MARKER_MIRAI)) + } + } + + private fun MiraiLogger.cast(): Log4jLoggerAdapter = this as Log4jLoggerAdapter + + @Test + fun `created is Log4jLoggerAdapter`() { + val logger = MiraiLogger.Factory.create(Log4j2LoggingTest::class, "test1") + assertIs(logger) + } + + @Test + fun `identity is considered as marker`() { + val logger = MiraiLogger.Factory.create(Log4j2LoggingTest::class, "test1") + assertEquals("test1", logger.cast().marker!!.name) + } + + + @Test + fun `test subLogger Marker`() { + val parent = MiraiLogger.Factory.create(Log4j2LoggingTest::class, "test1") + val parentMarker = parent.cast().marker!! + + val child = parent.subLogger("sub") + val childMarker = child.marker!! + + assertEquals("test1", parentMarker.name) + assertEquals("sub", childMarker.name) + + assertSame(parentMarker, childMarker.parents.single()) + assertSame("test1", childMarker.parents.single().name) + } + + @Test + fun `test subLogger Marker 2`() { + val parent = MiraiLogger.Factory.create(Log4j2LoggingTest::class, "test1") + val parentMarker = parent.cast().marker!! + + val child = parent.subLogger("sub").subLogger("sub2") + val childMarker = child.marker!! + + assertEquals("test1", parentMarker.name) + assertEquals("sub2", childMarker.name) + + assertSame("sub", childMarker.parents.single().name) + assertSame(parentMarker, childMarker.parents.single().parents.single()) + } + + @Test + fun `logging output test`() { + val logger = LogManager.getLogger(Bot::class.java) + logger.info("Test") + MiraiLogger.Factory.create(Bot::class).run { + info("InfoFF") + } + } +} \ No newline at end of file diff --git a/mirai-core-api/src/commonTest/resources/log4j.properties b/mirai-core-api/src/commonTest/resources/log4j.properties new file mode 100644 index 000000000..cebecdfd6 --- /dev/null +++ b/mirai-core-api/src/commonTest/resources/log4j.properties @@ -0,0 +1,13 @@ +# +# Copyright 2019-2021 Mamoe Technologies and contributors. +# +# 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. +# Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. +# +# https://github.com/mamoe/mirai/blob/dev/LICENSE +# +log4j.rootLogger=INFO,stdout +log4j.logger.mirai=INFO +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%p\t%d{ISO8601}\t%r\t%c\t%m%n \ No newline at end of file diff --git a/mirai-core-utils/src/androidMain/kotlin/Actuals.kt b/mirai-core-utils/src/androidMain/kotlin/Actuals.kt index ef2a34d55..d55d56e15 100644 --- a/mirai-core-utils/src/androidMain/kotlin/Actuals.kt +++ b/mirai-core-utils/src/androidMain/kotlin/Actuals.kt @@ -47,12 +47,17 @@ public actual inline fun Throwable.unwrap(): Throwable { public actual fun loadService(clazz: KClass, fallbackImplementation: String?): T { var suppressed: Throwable? = null return ServiceLoader.load(clazz.java).firstOrNull() - ?: runCatching { - Class.forName(fallbackImplementation).cast>().kotlin.run { objectInstance ?: createInstance() } - }.onFailure { - suppressed = it - }.getOrNull() + ?: runCatching { findCreateInstance(fallbackImplementation) }.onFailure { suppressed = it }.getOrNull() ?: throw NoSuchElementException("Could not find an implementation for service class ${clazz.qualifiedName}").apply { if (suppressed != null) addSuppressed(suppressed) } +} + +private fun findCreateInstance(fallbackImplementation: String?): T { + return Class.forName(fallbackImplementation).cast>().kotlin.run { objectInstance ?: createInstance() } +} + +public actual fun loadServiceOrNull(clazz: KClass, fallbackImplementation: String?): T? { + return ServiceLoader.load(clazz.java).firstOrNull() + ?: runCatching { findCreateInstance(fallbackImplementation) }.getOrNull() } \ No newline at end of file diff --git a/mirai-core-utils/src/commonMain/kotlin/Services.kt b/mirai-core-utils/src/commonMain/kotlin/Services.kt index c6bddb54f..a5dfd1c36 100644 --- a/mirai-core-utils/src/commonMain/kotlin/Services.kt +++ b/mirai-core-utils/src/commonMain/kotlin/Services.kt @@ -11,6 +11,11 @@ package net.mamoe.mirai.utils import kotlin.reflect.KClass +public expect fun loadServiceOrNull(clazz: KClass, fallbackImplementation: String? = null): T? public expect fun loadService(clazz: KClass, fallbackImplementation: String? = null): T + public inline fun loadService(fallbackImplementation: String? = null): T = - loadService(T::class, fallbackImplementation) \ No newline at end of file + loadService(T::class, fallbackImplementation) + +public inline fun loadService(noinline fallbackImplementation: () -> T): T = + loadServiceOrNull(T::class) ?: fallbackImplementation() \ No newline at end of file diff --git a/mirai-core-utils/src/jvmMain/kotlin/Actuals.kt b/mirai-core-utils/src/jvmMain/kotlin/Actuals.kt index 3015bb7cb..397e39403 100644 --- a/mirai-core-utils/src/jvmMain/kotlin/Actuals.kt +++ b/mirai-core-utils/src/jvmMain/kotlin/Actuals.kt @@ -35,12 +35,17 @@ public actual inline fun Throwable.unwrap(): Throwable { public actual fun loadService(clazz: KClass, fallbackImplementation: String?): T { var suppressed: Throwable? = null return ServiceLoader.load(clazz.java).firstOrNull() - ?: runCatching { - Class.forName(fallbackImplementation).cast>().kotlin.run { objectInstance ?: createInstance() } - }.onFailure { - suppressed = it - }.getOrNull() + ?: runCatching { findCreateInstance(fallbackImplementation) }.onFailure { suppressed = it }.getOrNull() ?: throw NoSuchElementException("Could not find an implementation for service class ${clazz.qualifiedName}").apply { if (suppressed != null) addSuppressed(suppressed) } +} + +private fun findCreateInstance(fallbackImplementation: String?): T { + return Class.forName(fallbackImplementation).cast>().kotlin.run { objectInstance ?: createInstance() } +} + +public actual fun loadServiceOrNull(clazz: KClass, fallbackImplementation: String?): T? { + return ServiceLoader.load(clazz.java).firstOrNull() + ?: runCatching { findCreateInstance(fallbackImplementation) }.getOrNull() } \ No newline at end of file diff --git a/mirai-core/build.gradle.kts b/mirai-core/build.gradle.kts index 048e9ac1b..e6fbb2cc7 100644 --- a/mirai-core/build.gradle.kts +++ b/mirai-core/build.gradle.kts @@ -72,13 +72,13 @@ kotlin { api1(`kotlinx-io-jvm`) implementation1(`kotlinx-coroutines-io`) implementation(`netty-all`) + implementation(`log4j-api`) } } commonTest { dependencies { implementation(kotlin("script-runtime")) - runtimeOnly(`slf4j-simple`) } } diff --git a/mirai-core/src/androidTest/kotlin/test/initPlatform.android.kt b/mirai-core/src/androidTest/kotlin/test/initPlatform.android.kt index 1b76e62cd..5311888ce 100644 --- a/mirai-core/src/androidTest/kotlin/test/initPlatform.android.kt +++ b/mirai-core/src/androidTest/kotlin/test/initPlatform.android.kt @@ -34,7 +34,7 @@ internal actual class PlatformInitializationTest : AbstractTest() { actual fun test() { assertTrue { @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") - MiraiLogger.create("1") is net.mamoe.mirai.internal.utils.StdoutLogger + MiraiLogger.Factory.create(this::class, "1") is net.mamoe.mirai.internal.utils.StdoutLogger } } } \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/message/imagesImpl.kt b/mirai-core/src/commonMain/kotlin/message/imagesImpl.kt index 8a8fc7d29..f85fce3e9 100644 --- a/mirai-core/src/commonMain/kotlin/message/imagesImpl.kt +++ b/mirai-core/src/commonMain/kotlin/message/imagesImpl.kt @@ -57,6 +57,9 @@ internal class OnlineGroupImageImpl( } +private val imageLogger: MiraiLogger by lazy { MiraiLogger.Factory.create(Image::class) } +internal val Image.Key.logger get() = imageLogger + @Serializable(with = OnlineFriendImageImpl.Serializer::class) internal class OnlineFriendImageImpl( internal val delegate: ImMsgBody.NotOnlineImage, @@ -70,7 +73,7 @@ OnlineFriendImage() { ?: kotlin.run { if (delegate.picMd5.size == 16) generateImageId(delegate.picMd5, imageType) else { - MiraiLogger.TopLevel.warning( + Image.logger.warning( contextualBugReportException( "Failed to compute friend imageId: resId=${delegate.resId}", delegate._miraiContentToString(), @@ -115,7 +118,7 @@ internal fun getImageType(id: Int): String { 2001, 3 -> "png" else -> { if (UNKNOWN_IMAGE_TYPE_PROMPT_ENABLED) { - MiraiLogger.TopLevel.debug( + Image.logger.debug( "Unknown image id: $id. Stacktrace:", Exception() ) diff --git a/mirai-core/src/commonMain/kotlin/network/components/PacketCodec.kt b/mirai-core/src/commonMain/kotlin/network/components/PacketCodec.kt index b55e59237..50e39cc06 100644 --- a/mirai-core/src/commonMain/kotlin/network/components/PacketCodec.kt +++ b/mirai-core/src/commonMain/kotlin/network/components/PacketCodec.kt @@ -45,7 +45,7 @@ internal interface PacketCodec { val PACKET_DEBUG = systemProp("mirai.network.packet.logger", false) internal val PacketLogger: MiraiLoggerWithSwitch by lazy { - MiraiLogger.create("Packet").withSwitch(PACKET_DEBUG) + MiraiLogger.Factory.create(PacketCodec::class, "Packet").withSwitch(PACKET_DEBUG) } } } diff --git a/mirai-core/src/commonMain/kotlin/network/components/ServerList.kt b/mirai-core/src/commonMain/kotlin/network/components/ServerList.kt index 2570f255b..9fb9ddfdc 100644 --- a/mirai-core/src/commonMain/kotlin/network/components/ServerList.kt +++ b/mirai-core/src/commonMain/kotlin/network/components/ServerList.kt @@ -105,10 +105,10 @@ internal class ServerListImpl( initial: Collection = emptyList() ) : ServerList { @TestOnly - constructor(initial: Collection) : this(MiraiLogger.TopLevel, initial) + constructor(initial: Collection) : this(MiraiLogger.Factory.create(ServerListImpl::class), initial) @TestOnly - constructor() : this(MiraiLogger.TopLevel) + constructor() : this(MiraiLogger.Factory.create(ServerListImpl::class)) @Volatile private var preferred: Set = DEFAULT_SERVER_LIST diff --git a/mirai-core/src/commonMain/kotlin/network/handler/NetworkHandlerSupport.kt b/mirai-core/src/commonMain/kotlin/network/handler/NetworkHandlerSupport.kt index 69511c9eb..da1d3b949 100644 --- a/mirai-core/src/commonMain/kotlin/network/handler/NetworkHandlerSupport.kt +++ b/mirai-core/src/commonMain/kotlin/network/handler/NetworkHandlerSupport.kt @@ -23,6 +23,7 @@ import net.mamoe.mirai.internal.network.protocol.packet.IncomingPacket import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacket import net.mamoe.mirai.internal.utils.SingleEntrantLock import net.mamoe.mirai.internal.utils.fromMiraiLogger +import net.mamoe.mirai.internal.utils.subLogger import net.mamoe.mirai.utils.* import net.mamoe.mirai.utils.Either.Companion.fold import java.util.concurrent.ConcurrentLinkedQueue @@ -56,7 +57,7 @@ internal abstract class NetworkHandlerSupport( } protected val packetLogger: MiraiLogger by lazy { - MiraiLogger.create(context.logger.identity + ".debug").withSwitch(PacketCodec.PACKET_DEBUG) + context.logger.subLogger("NetworkDebug").withSwitch(PacketCodec.PACKET_DEBUG) } /////////////////////////////////////////////////////////////////////////// diff --git a/mirai-core/src/commonMain/kotlin/network/handler/state/LoggingStateObserver.kt b/mirai-core/src/commonMain/kotlin/network/handler/state/LoggingStateObserver.kt index 43fb12678..9fe59df25 100644 --- a/mirai-core/src/commonMain/kotlin/network/handler/state/LoggingStateObserver.kt +++ b/mirai-core/src/commonMain/kotlin/network/handler/state/LoggingStateObserver.kt @@ -84,14 +84,14 @@ internal class LoggingStateObserver( return when (ENABLED) { "full" -> { SafeStateObserver( - LoggingStateObserver(MiraiLogger.create("States"), true), - MiraiLogger.create("LoggingStateObserver errors") + LoggingStateObserver(MiraiLogger.Factory.create(LoggingStateObserver::class, "States"), true), + MiraiLogger.Factory.create(LoggingStateObserver::class, "LoggingStateObserver errors") ) } "on", "true" -> { SafeStateObserver( - LoggingStateObserver(MiraiLogger.create("States"), false), - MiraiLogger.create("LoggingStateObserver errors") + LoggingStateObserver(MiraiLogger.Factory.create(LoggingStateObserver::class, "States"), false), + MiraiLogger.Factory.create(LoggingStateObserver::class, "LoggingStateObserver errors") ) } else -> null diff --git a/mirai-core/src/commonMain/kotlin/utils/SubLogger.kt b/mirai-core/src/commonMain/kotlin/utils/SubLogger.kt index 7edc06be6..af80289be 100644 --- a/mirai-core/src/commonMain/kotlin/utils/SubLogger.kt +++ b/mirai-core/src/commonMain/kotlin/utils/SubLogger.kt @@ -11,7 +11,8 @@ package net.mamoe.mirai.internal.utils import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CoroutineExceptionHandler -import net.mamoe.mirai.utils.* +import net.mamoe.mirai.utils.MiraiLogger +import net.mamoe.mirai.utils.coroutineName internal fun CoroutineExceptionHandler.Key.fromMiraiLogger( @@ -25,54 +26,5 @@ internal fun CoroutineExceptionHandler.Key.fromMiraiLogger( } } -internal fun MiraiLogger.subLogger(name: String): MiraiLogger { - return SubLogger(name, this) -} - -private class SubLogger( - private val name: String, - private val main: MiraiLogger, -) : MiraiLoggerPlatformBase() { - override val identity: String? get() = main.identity - override val isEnabled: Boolean get() = main.isEnabled - - override fun verbose0(message: String?, e: Throwable?) { - if (message != null) { - main.verbose({ "[$name] $message" }, e) - } else { - main.verbose(null, e) - } - } - - override fun debug0(message: String?, e: Throwable?) { - if (message != null) { - main.debug({ "[$name] $message" }, e) - } else { - main.debug(null, e) - } - } - - override fun info0(message: String?, e: Throwable?) { - if (message != null) { - main.info({ "[$name] $message" }, e) - } else { - main.info(null, e) - } - } - - override fun warning0(message: String?, e: Throwable?) { - if (message != null) { - main.warning({ "[$name] $message" }, e) - } else { - main.warning(null, e) - } - } - - override fun error0(message: String?, e: Throwable?) { - if (message != null) { - main.error({ "[$name] $message" }, e) - } else { - main.error(null, e) - } - } -} \ No newline at end of file +@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") +internal fun MiraiLogger.subLogger(name: String): MiraiLogger = subLoggerImpl(this, name) \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/utils/contentToString.kt b/mirai-core/src/commonMain/kotlin/utils/contentToString.kt index 95dab2476..7c4832c4a 100644 --- a/mirai-core/src/commonMain/kotlin/utils/contentToString.kt +++ b/mirai-core/src/commonMain/kotlin/utils/contentToString.kt @@ -12,6 +12,7 @@ package net.mamoe.mirai.internal.utils import kotlinx.serialization.Transient +import net.mamoe.mirai.IMirai import net.mamoe.mirai.utils.MiraiLogger import net.mamoe.mirai.utils.debug import net.mamoe.mirai.utils.toUHexString @@ -32,7 +33,7 @@ private fun Sequence.joinToStringPrefixed(prefix: String, transform: (T) return this.joinToString(prefix = "$prefix$indent", separator = "\n$prefix$indent", transform = transform) } -private val SoutvLogger: MiraiLogger by lazy { MiraiLogger.create("soutv") } +private val SoutvLogger: MiraiLogger by lazy { MiraiLogger.Factory.create(IMirai::class, "soutv") } internal fun Any?.soutv(name: String = "unnamed") { @Suppress("DEPRECATION") SoutvLogger.debug { "$name = ${this._miraiContentToString()}" } diff --git a/mirai-core/src/commonTest/kotlin/network/component/EventDispatcherTest.kt b/mirai-core/src/commonTest/kotlin/network/component/EventDispatcherTest.kt index af57e5440..38f28c1df 100644 --- a/mirai-core/src/commonTest/kotlin/network/component/EventDispatcherTest.kt +++ b/mirai-core/src/commonTest/kotlin/network/component/EventDispatcherTest.kt @@ -26,7 +26,10 @@ import org.junit.jupiter.api.Test internal class EventDispatcherTest : AbstractTest() { private class Ev : AbstractEvent() - private val dispatcher = TestEventDispatcherImpl(SupervisorJob(), MiraiLogger.create("EventDispatcherTest")) + private val dispatcher = TestEventDispatcherImpl( + SupervisorJob(), + MiraiLogger.Factory.create(EventDispatcherTest::class) + ) @Test fun `can broadcast`() = runBlockingUnit { diff --git a/mirai-core/src/commonTest/kotlin/network/framework/AbstractMockNetworkHandlerTest.kt b/mirai-core/src/commonTest/kotlin/network/framework/AbstractMockNetworkHandlerTest.kt index 00bd345b6..a8f6904f9 100644 --- a/mirai-core/src/commonTest/kotlin/network/framework/AbstractMockNetworkHandlerTest.kt +++ b/mirai-core/src/commonTest/kotlin/network/framework/AbstractMockNetworkHandlerTest.kt @@ -11,6 +11,7 @@ package net.mamoe.mirai.internal.network.framework +import net.mamoe.mirai.Bot import net.mamoe.mirai.internal.MockBot import net.mamoe.mirai.internal.QQAndroidBot import net.mamoe.mirai.internal.network.component.ConcurrentComponentStorage @@ -39,7 +40,7 @@ internal abstract class AbstractMockNetworkHandlerTest : AbstractNetworkHandlerT nhProvider = { createNetworkHandler() } additionalComponentsProvider = { this@AbstractMockNetworkHandlerTest.components } } - protected val logger = MiraiLogger.create("test") + protected val logger = MiraiLogger.Factory.create(Bot::class, "test") protected val components = ConcurrentComponentStorage().apply { set(SsoProcessor, TestSsoProcessor(bot)) set( @@ -49,8 +50,8 @@ internal abstract class AbstractMockNetworkHandlerTest : AbstractNetworkHandlerT set( StateObserver, SafeStateObserver( - LoggingStateObserver(MiraiLogger.create("States")), - MiraiLogger.create("StateObserver errors") + LoggingStateObserver(MiraiLogger.Factory.create(LoggingStateObserver::class, "States")), + MiraiLogger.Factory.create(SafeStateObserver::class, "StateObserver errors") ) ) } diff --git a/mirai-core/src/commonTest/kotlin/network/framework/AbstractRealNetworkHandlerTest.kt b/mirai-core/src/commonTest/kotlin/network/framework/AbstractRealNetworkHandlerTest.kt index 2a341f992..5495a4992 100644 --- a/mirai-core/src/commonTest/kotlin/network/framework/AbstractRealNetworkHandlerTest.kt +++ b/mirai-core/src/commonTest/kotlin/network/framework/AbstractRealNetworkHandlerTest.kt @@ -53,7 +53,7 @@ internal sealed class AbstractRealNetworkHandlerTest : Abstr } } - open val networkLogger = MiraiLogger.create("network") + open val networkLogger = MiraiLogger.Factory.create(NetworkHandler::class, "network") sealed class NHEvent { object Login : NHEvent() diff --git a/mirai-core/src/commonTest/kotlin/network/framework/TestNetworkHandlerContext.kt b/mirai-core/src/commonTest/kotlin/network/framework/TestNetworkHandlerContext.kt index 9508b68f2..ac29b082c 100644 --- a/mirai-core/src/commonTest/kotlin/network/framework/TestNetworkHandlerContext.kt +++ b/mirai-core/src/commonTest/kotlin/network/framework/TestNetworkHandlerContext.kt @@ -24,14 +24,14 @@ import net.mamoe.mirai.utils.MiraiLogger internal class TestNetworkHandlerContext( override val bot: QQAndroidBot = MockBot(), - override val logger: MiraiLogger = MiraiLogger.create("Test"), + override val logger: MiraiLogger = MiraiLogger.Factory.create(TestNetworkHandlerContext::class, "Test"), components: ComponentStorage = ConcurrentComponentStorage().apply { set(SsoProcessor, SsoProcessorImpl(SsoProcessorContextImpl(bot))) set( StateObserver, SafeStateObserver( - LoggingStateObserver(MiraiLogger.create("States")), - MiraiLogger.create("StateObserver errors") + LoggingStateObserver(MiraiLogger.Factory.create(LoggingStateObserver::class, "States")), + MiraiLogger.Factory.create(LoggingStateObserver::class, "StateObserver errors") ) ) } diff --git a/mirai-core/src/commonTest/kotlin/network/handler/KeepAliveNetworkHandlerSelectorTest.kt b/mirai-core/src/commonTest/kotlin/network/handler/KeepAliveNetworkHandlerSelectorTest.kt index fc2e42498..47e82fbab 100644 --- a/mirai-core/src/commonTest/kotlin/network/handler/KeepAliveNetworkHandlerSelectorTest.kt +++ b/mirai-core/src/commonTest/kotlin/network/handler/KeepAliveNetworkHandlerSelectorTest.kt @@ -21,7 +21,7 @@ import java.util.concurrent.atomic.AtomicInteger import kotlin.test.* import kotlin.time.Duration -internal val selectorLogger = MiraiLogger.create("selector") +internal val selectorLogger = MiraiLogger.Factory.create(TestSelector::class, "selector") internal class TestSelector : AbstractKeepAliveNetworkHandlerSelector { diff --git a/mirai-core/src/commonTest/kotlin/test/initPlatform.common.kt b/mirai-core/src/commonTest/kotlin/test/initPlatform.common.kt index 94235cc41..2dfeb068c 100644 --- a/mirai-core/src/commonTest/kotlin/test/initPlatform.common.kt +++ b/mirai-core/src/commonTest/kotlin/test/initPlatform.common.kt @@ -12,6 +12,7 @@ package net.mamoe.mirai.internal.test import net.mamoe.mirai.IMirai import net.mamoe.mirai.internal.network.framework.SynchronizedStdoutLogger import net.mamoe.mirai.utils.MiraiLogger +import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.Test import org.junit.jupiter.api.Timeout import java.util.concurrent.TimeUnit @@ -26,9 +27,7 @@ abstract class AbstractTest { init { initPlatform() - MiraiLogger.setDefaultLoggerCreator { - SynchronizedStdoutLogger(it) - } + restoreLoggerFactory() System.setProperty("mirai.network.packet.logger", "true") System.setProperty("mirai.network.state.observer.logging", "true") @@ -38,6 +37,14 @@ abstract class AbstractTest { } + @AfterEach + protected fun restoreLoggerFactory() { + @Suppress("DEPRECATION") + MiraiLogger.setDefaultLoggerCreator { + SynchronizedStdoutLogger(it) + } + } + companion object { init { Exception() // create a exception to load relevant classes to estimate invocation time of test cases more accurately. diff --git a/mirai-core/src/commonTest/kotlin/test/printing.kt b/mirai-core/src/commonTest/kotlin/test/printing.kt index 6842d25e0..c01c7a143 100644 --- a/mirai-core/src/commonTest/kotlin/test/printing.kt +++ b/mirai-core/src/commonTest/kotlin/test/printing.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2020 Mamoe Technologies and contributors. + * Copyright 2019-2021 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ @file:Suppress("NOTHING_TO_INLINE") @@ -14,12 +14,13 @@ package test import kotlinx.io.core.ByteReadPacket import kotlinx.io.core.Input import kotlinx.io.core.readAvailable +import net.mamoe.mirai.IMirai import net.mamoe.mirai.utils.* import kotlin.contracts.InvocationKind import kotlin.contracts.contract -val DebugLogger: MiraiLogger = MiraiLogger.create("Packet Debug") +val DebugLogger: MiraiLogger = MiraiLogger.Factory.create(IMirai::class, "Packet Debug") internal inline fun ByteArray.debugPrintThis(name: String): ByteArray { DebugLogger.debug(name + "=" + this.toUHexString())