From f0651c81c685d5f74408ff371e499ec2daaf0bda Mon Sep 17 00:00:00 2001 From: Him188 Date: Sat, 27 Aug 2022 18:46:16 +0800 Subject: [PATCH] [core+console] Improve logging hierarchy: (#2196) - always use factory API from mirai-core - Terminal is still overriding standard outputs - plugins are forbidden to override logging framework (this was allowed but not working before) --- .../test/MiraiLog4JAdapterTest.kt | 16 +-- .../backend/mirai-console/src/MiraiConsole.kt | 12 ++- .../src/MiraiConsoleImplementation.kt | 22 ++++- .../MiraiConsoleImplementationBridge.kt | 53 +++++++--- .../internal/command/CommandManagerImpl.kt | 3 +- .../plugin/BuiltInJvmPluginLoaderImpl.kt | 3 +- .../internal/plugin/JvmPluginClassLoader.kt | 8 +- .../src/internal/plugin/JvmPluginInternal.kt | 2 +- .../src/internal/plugin/PluginManagerImpl.kt | 7 +- .../AbstractConsoleInstanceTest.kt | 5 + .../MockConsoleImplementation.kt | 13 +++ .../src/MiraiConsoleImplementationTerminal.kt | 54 +++++++--- .../src/MiraiConsoleTerminalLoader.kt | 27 +++-- .../android/api/android.api | 4 +- .../compatibility-validation/jvm/api/jvm.api | 4 +- .../kotlin/utils/LoggerAdapters.kt | 13 ++- .../jvmBaseMain/kotlin/utils/MiraiLogger.kt | 98 ++++++++++++++----- .../kotlin/logging/AbstractLoggingTest.kt | 20 ++++ .../kotlin/logging/Log4j2LoggingTest.kt | 19 ++-- .../logging/LoggingCompatibilityTest.kt | 14 ++- .../kotlin/test/initPlatform.android.kt | 8 +- .../jvmBaseTest/kotlin/test/AbstractTest.kt | 14 ++- .../kotlin/netinternalkit/LogCapture.kt | 22 +++-- 23 files changed, 314 insertions(+), 127 deletions(-) create mode 100644 mirai-core-api/src/jvmBaseTest/kotlin/logging/AbstractLoggingTest.kt diff --git a/logging/mirai-logging-log4j2/test/MiraiLog4JAdapterTest.kt b/logging/mirai-logging-log4j2/test/MiraiLog4JAdapterTest.kt index 9b0f4a660..a33fddbf7 100644 --- a/logging/mirai-logging-log4j2/test/MiraiLog4JAdapterTest.kt +++ b/logging/mirai-logging-log4j2/test/MiraiLog4JAdapterTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 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. @@ -7,6 +7,8 @@ * https://github.com/mamoe/mirai/blob/dev/LICENSE */ +@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") + package net.mamoe.mirai.utils.logging import io.ktor.client.* @@ -20,18 +22,6 @@ import kotlin.test.assertFalse import kotlin.test.assertIs internal class MiraiLog4JAdapterTest { - - @Suppress("DEPRECATION_ERROR") - @Test - fun `services prevail than legacy overrides`() { - MiraiLogger.setDefaultLoggerCreator { - net.mamoe.mirai.utils.SimpleLogger("my logger") { _: String?, _: Throwable? -> } - } - - @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") - assertIs(MiraiLogger.Factory.create(this::class)) - } - @Test fun `using log4j`() { assertIs(loadService(MiraiLogger.Factory::class)) diff --git a/mirai-console/backend/mirai-console/src/MiraiConsole.kt b/mirai-console/backend/mirai-console/src/MiraiConsole.kt index 091198e3a..04efa73c9 100644 --- a/mirai-console/backend/mirai-console/src/MiraiConsole.kt +++ b/mirai-console/backend/mirai-console/src/MiraiConsole.kt @@ -128,8 +128,17 @@ public interface MiraiConsole : CoroutineScope { get() = throw UnsupportedOperationException("PluginCenter is not supported yet") /** - * 创建一个 logger + * 创建一个 logger. 已弃用. 请使用 [MiraiLogger.Factory.create]. */ + @Deprecated( + "Please use the standard way in mirai-core to create loggers, i.e. MiraiLogger.Factory.INSTANCE.create()", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith( + "MiraiLogger.Factory.create(yourClass::class, identity)", + "net.mamoe.mirai.utils.MiraiLogger" + ), + ) + @DeprecatedSinceMirai(warningSince = "2.13") @ConsoleExperimentalApi public fun createLogger(identity: String?): MiraiLogger @@ -204,6 +213,7 @@ public interface MiraiConsole : CoroutineScope { mainLogger.verbose { "Renaming $deviceInfoInWorkingDir to $deviceInWorkingDir" } deviceInfoInWorkingDir.renameTo(deviceInWorkingDir) } + deviceInRoot.exists() -> { // copy root/device.json to bots/id/device.json mainLogger.verbose { "Coping $deviceInRoot to $deviceInWorkingDir" } diff --git a/mirai-console/backend/mirai-console/src/MiraiConsoleImplementation.kt b/mirai-console/backend/mirai-console/src/MiraiConsoleImplementation.kt index 4ac891081..23bdbc080 100644 --- a/mirai-console/backend/mirai-console/src/MiraiConsoleImplementation.kt +++ b/mirai-console/backend/mirai-console/src/MiraiConsoleImplementation.kt @@ -37,10 +37,7 @@ import net.mamoe.mirai.console.plugin.loader.PluginLoader import net.mamoe.mirai.console.util.ConsoleExperimentalApi import net.mamoe.mirai.console.util.ConsoleInput import net.mamoe.mirai.message.data.Message -import net.mamoe.mirai.utils.BotConfiguration -import net.mamoe.mirai.utils.LoginSolver -import net.mamoe.mirai.utils.MiraiLogger -import net.mamoe.mirai.utils.NotStableForInheritance +import net.mamoe.mirai.utils.* import java.nio.file.Path import java.util.* import java.util.concurrent.locks.ReentrantLock @@ -216,9 +213,14 @@ public interface MiraiConsoleImplementation : CoroutineScope { /** * 创建一个 [MiraiLogger]. * - * **注意**: [MiraiConsole] 会将 [net.mamoe.mirai.utils.MiraiLogger.setDefaultLoggerCreator] 设置为 `MiraiConsole::createLogger`. + * **注意**: [MiraiConsole] 会将 [net.mamoe.mirai.utils.MiraiLogger.Factory] 设置为 `MiraiConsole::createLogger`. * 因此不要在 [createLogger] 中调用 [net.mamoe.mirai.utils.MiraiLogger.create] */ + @Deprecated( + "Deprecated for removal. Implement the other overload, or use MiraiConsole.createLogger instead.", + level = DeprecationLevel.ERROR + ) + @DeprecatedSinceMirai(errorSince = "2.13") public fun createLogger(identity: String?): MiraiLogger /** @see [MiraiConsole.newProcessProgress] */ @@ -226,6 +228,16 @@ public interface MiraiConsoleImplementation : CoroutineScope { return DefaultLoggingProcessProgress() } + /** + * 创建一个 [MiraiLogger.Factory]. 在返回的实例中必须调用 [platformImplementation] 来适配平台日志实现. + * + * @param platformImplementation 平台的日志实现, 这可能是使用 SLF4J 等日志框架转接的实例. + * + * @since 2.13 + */ + public fun createLoggerFactory(platformImplementation: MiraiLogger.Factory): MiraiLogger.Factory + + /** * 该前端是否支持使用 Ansi 输出彩色信息 * diff --git a/mirai-console/backend/mirai-console/src/internal/MiraiConsoleImplementationBridge.kt b/mirai-console/backend/mirai-console/src/internal/MiraiConsoleImplementationBridge.kt index be8b480d0..11fdc9e28 100644 --- a/mirai-console/backend/mirai-console/src/internal/MiraiConsoleImplementationBridge.kt +++ b/mirai-console/backend/mirai-console/src/internal/MiraiConsoleImplementationBridge.kt @@ -59,9 +59,9 @@ import net.mamoe.mirai.utils.* import java.time.Instant import java.time.ZoneId import java.time.format.DateTimeFormatter -import java.util.concurrent.atomic.AtomicBoolean import kotlin.contracts.InvocationKind import kotlin.contracts.contract +import kotlin.reflect.KClass import kotlin.reflect.KProperty import kotlin.reflect.KProperty0 @@ -100,22 +100,49 @@ internal class MiraiConsoleImplementationBridge( externalImplementation.loggerController } - override val mainLogger: MiraiLogger by lazy { createLogger("main") } + override val mainLogger: MiraiLogger by lazy { MiraiLogger.Factory.create(MiraiConsole::class, "main") } - init { - // TODO: Replace to standard api - @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") - DefaultFactoryOverrides.override { requester, identity -> - return@override createLogger( - identity ?: requester.kotlin.simpleName ?: requester.simpleName - ) + /** + * Delegates the [platformImplementation] with [loggerController]. + */ + private inner class ControlledLoggerFactory( + private val platformImplementation: MiraiLogger.Factory, + ) : MiraiLogger.Factory { + override fun create(requester: KClass<*>, identity: String?): MiraiLogger { + return MiraiConsoleLogger(loggerController, platformImplementation.create(requester, identity)) + } + + override fun create(requester: Class<*>, identity: String?): MiraiLogger { + return MiraiConsoleLogger(loggerController, platformImplementation.create(requester, identity)) } } + init { + // When writing a log: + // 1. ControlledLoggerFactory checks if that log level is enabled + // 2. ... if enabled, goto 3 + // ... if not, return + // 3. [externalImplementation] decides how to log the message + // 4. [externalImplementation] outputs by using [platform] + @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") + MiraiLoggerFactoryImplementationBridge.wrapCurrent { platform -> + ControlledLoggerFactory(externalImplementation.createLoggerFactory(platform)) + } + @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") + MiraiLoggerFactoryImplementationBridge.freeze() // forbid any further overrides + } + + @Deprecated( + "Please use the standard way in mirai-core to create loggers, i.e. MiraiLogger.Factory.INSTANCE.create()", + replaceWith = ReplaceWith( + "MiraiLogger.Factory.create(yourClass::class, identity)", + "net.mamoe.mirai.utils.MiraiLogger" + ), + level = DeprecationLevel.WARNING + ) override fun createLogger(identity: String?): MiraiLogger { - val controller = loggerController - return MiraiConsoleLogger(controller, externalImplementation.createLogger(identity)) + return MiraiLogger.Factory.create(MiraiConsole::class, identity) } @Suppress("RemoveRedundantBackticks") @@ -286,6 +313,7 @@ internal class MiraiConsoleImplementationBridge( PLAIN -> { MiraiConsole.addBot(id, account.password.value, BotConfiguration::configBot) } + MD5 -> { val md5 = kotlin.runCatching { account.password.value.hexToBytes() @@ -339,4 +367,5 @@ internal class MiraiConsoleImplementationBridge( override fun postPhase(phase: String) { externalImplementation.postPhase(phase) } -} \ No newline at end of file +} + diff --git a/mirai-console/backend/mirai-console/src/internal/command/CommandManagerImpl.kt b/mirai-console/backend/mirai-console/src/internal/command/CommandManagerImpl.kt index d40bc063d..38d0c64f3 100644 --- a/mirai-console/backend/mirai-console/src/internal/command/CommandManagerImpl.kt +++ b/mirai-console/backend/mirai-console/src/internal/command/CommandManagerImpl.kt @@ -11,7 +11,6 @@ package net.mamoe.mirai.console.internal.command import kotlinx.atomicfu.locks.withLock import kotlinx.coroutines.CoroutineScope -import net.mamoe.mirai.console.MiraiConsole import net.mamoe.mirai.console.MiraiConsoleImplementation.ConsoleDataScope.Companion.get import net.mamoe.mirai.console.command.* import net.mamoe.mirai.console.command.Command.Companion.allNames @@ -37,7 +36,7 @@ internal class CommandManagerImpl( parentCoroutineContext: CoroutineContext ) : CommandManager, CoroutineScope by parentCoroutineContext.childScope("CommandManagerImpl") { private val logger: MiraiLogger by lazy { - MiraiConsole.createLogger("command") + MiraiLogger.Factory.create(CommandManager::class, "command") } @Suppress("ObjectPropertyName") diff --git a/mirai-console/backend/mirai-console/src/internal/plugin/BuiltInJvmPluginLoaderImpl.kt b/mirai-console/backend/mirai-console/src/internal/plugin/BuiltInJvmPluginLoaderImpl.kt index cd48da5a4..9f90f8623 100644 --- a/mirai-console/backend/mirai-console/src/internal/plugin/BuiltInJvmPluginLoaderImpl.kt +++ b/mirai-console/backend/mirai-console/src/internal/plugin/BuiltInJvmPluginLoaderImpl.kt @@ -41,7 +41,8 @@ internal class BuiltInJvmPluginLoaderImpl( JvmPluginLoader { companion object { - internal val logger: MiraiLogger = MiraiConsole.createLogger(JvmPluginLoader::class.simpleName!!) + internal val logger: MiraiLogger = + MiraiLogger.Factory.create(JvmPluginLoader::class) } fun pluginsFilesSequence( diff --git a/mirai-console/backend/mirai-console/src/internal/plugin/JvmPluginClassLoader.kt b/mirai-console/backend/mirai-console/src/internal/plugin/JvmPluginClassLoader.kt index b1d2ddc3d..194413b35 100644 --- a/mirai-console/backend/mirai-console/src/internal/plugin/JvmPluginClassLoader.kt +++ b/mirai-console/backend/mirai-console/src/internal/plugin/JvmPluginClassLoader.kt @@ -10,7 +10,6 @@ package net.mamoe.mirai.console.internal.plugin -import net.mamoe.mirai.console.MiraiConsole import net.mamoe.mirai.console.plugin.jvm.ExportManager import net.mamoe.mirai.console.plugin.jvm.JvmPluginClasspath import net.mamoe.mirai.utils.* @@ -197,7 +196,12 @@ internal class JvmPluginClassLoaderN : URLClassLoader { private val file_: File get() = file - var linkedLogger by lateinitMutableProperty { MiraiConsole.createLogger("JvmPlugin[" + file_.name + "]") } + var linkedLogger by lateinitMutableProperty { + MiraiLogger.Factory.create( + JvmPluginClassLoaderN::class, + "JvmPlugin[" + file_.name + "]" + ) + } val undefinedDependencies = mutableSetOf() @Suppress("UNUSED_PARAMETER") diff --git a/mirai-console/backend/mirai-console/src/internal/plugin/JvmPluginInternal.kt b/mirai-console/backend/mirai-console/src/internal/plugin/JvmPluginInternal.kt index c6284f085..ad27e484d 100644 --- a/mirai-console/backend/mirai-console/src/internal/plugin/JvmPluginInternal.kt +++ b/mirai-console/backend/mirai-console/src/internal/plugin/JvmPluginInternal.kt @@ -65,7 +65,7 @@ internal abstract class JvmPluginInternal( // region JvmPlugin final override val logger: MiraiLogger by lazy { BuiltInJvmPluginLoaderImpl.logger.runCatchingLog { - MiraiConsole.createLogger(this.description.name) + MiraiLogger.Factory.create(this@JvmPluginInternal::class, this.description.name) }.getOrThrow() } diff --git a/mirai-console/backend/mirai-console/src/internal/plugin/PluginManagerImpl.kt b/mirai-console/backend/mirai-console/src/internal/plugin/PluginManagerImpl.kt index 7f681f959..89e835731 100644 --- a/mirai-console/backend/mirai-console/src/internal/plugin/PluginManagerImpl.kt +++ b/mirai-console/backend/mirai-console/src/internal/plugin/PluginManagerImpl.kt @@ -26,10 +26,7 @@ import net.mamoe.mirai.console.plugin.loader.PluginLoadException import net.mamoe.mirai.console.plugin.loader.PluginLoader import net.mamoe.mirai.console.plugin.name import net.mamoe.mirai.console.util.SemVersion -import net.mamoe.mirai.utils.TestOnly -import net.mamoe.mirai.utils.cast -import net.mamoe.mirai.utils.childScope -import net.mamoe.mirai.utils.info +import net.mamoe.mirai.utils.* import java.io.File import java.nio.file.Path import java.util.concurrent.CopyOnWriteArrayList @@ -60,7 +57,7 @@ internal class PluginManagerImpl( builtInLoaders.toMutableList() } - private val logger = MiraiConsole.createLogger("plugin") + private val logger = MiraiLogger.Factory.create(PluginManager::class, "plugin") @JvmField internal val resolvedPlugins: MutableList = diff --git a/mirai-console/backend/mirai-console/test/testFramework/AbstractConsoleInstanceTest.kt b/mirai-console/backend/mirai-console/test/testFramework/AbstractConsoleInstanceTest.kt index a6af33268..37dcfef41 100644 --- a/mirai-console/backend/mirai-console/test/testFramework/AbstractConsoleInstanceTest.kt +++ b/mirai-console/backend/mirai-console/test/testFramework/AbstractConsoleInstanceTest.kt @@ -22,6 +22,11 @@ import org.junit.jupiter.api.AfterEach import kotlin.test.BeforeTest abstract class AbstractConsoleInstanceTest { + init { + @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") + net.mamoe.mirai.utils.MiraiLoggerFactoryImplementationBridge.reinit() + } + val mockPlugin by lazy { mockKotlinPlugin() } private lateinit var implementation: MiraiConsoleImplementation val consoleImplementation: MiraiConsoleImplementation by ::implementation diff --git a/mirai-console/backend/mirai-console/test/testFramework/MockConsoleImplementation.kt b/mirai-console/backend/mirai-console/test/testFramework/MockConsoleImplementation.kt index d4534cb35..a36341ae5 100644 --- a/mirai-console/backend/mirai-console/test/testFramework/MockConsoleImplementation.kt +++ b/mirai-console/backend/mirai-console/test/testFramework/MockConsoleImplementation.kt @@ -76,10 +76,23 @@ open class MockConsoleImplementation : MiraiConsoleImplementation { override fun createLoginSolver(requesterBot: Long, configuration: BotConfiguration): LoginSolver = LoginSolver.Default!! + @Suppress("DeprecatedCallableAddReplaceWith") + @Deprecated( + "Deprecated for removal. Implement the other overload, or use MiraiConsole.createLogger instead.", + level = DeprecationLevel.ERROR + ) override fun createLogger(identity: String?): MiraiLogger { return PlatformLogger(identity) } + override fun createLoggerFactory(platformImplementation: MiraiLogger.Factory): MiraiLogger.Factory { + return object : MiraiLogger.Factory { + override fun create(requester: Class<*>, identity: String?): MiraiLogger { + return PlatformLogger(identity) + } + } + } + override val consoleDataScope: MiraiConsoleImplementation.ConsoleDataScope by lazy { MiraiConsoleImplementation.ConsoleDataScope.createDefault( coroutineContext, diff --git a/mirai-console/frontend/mirai-console-terminal/src/MiraiConsoleImplementationTerminal.kt b/mirai-console/frontend/mirai-console-terminal/src/MiraiConsoleImplementationTerminal.kt index 5e22ce841..bf341b3de 100644 --- a/mirai-console/frontend/mirai-console-terminal/src/MiraiConsoleImplementationTerminal.kt +++ b/mirai-console/frontend/mirai-console-terminal/src/MiraiConsoleImplementationTerminal.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 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( @@ -55,6 +55,7 @@ import java.nio.file.Path import java.nio.file.Paths import kotlin.concurrent.withLock import kotlin.coroutines.Continuation +import kotlin.reflect.KClass import kotlin.reflect.KProperty /** @@ -102,14 +103,45 @@ open class MiraiConsoleImplementationTerminal return StandardCharImageLoginSolver(input = { requestInput("LOGIN> ") }) } + @Suppress("DeprecatedCallableAddReplaceWith") + @Deprecated( + "Deprecated for removal. Implement the other overload, or use MiraiConsole.createLogger instead.", + level = DeprecationLevel.ERROR + ) override fun createLogger(identity: String?): MiraiLogger { - return PlatformLogger(identity = identity, output = { line -> - val text = line + ANSI_RESET - prePrintNewLog() - lineReader.printAbove(text) - postPrintNewLog() - logService.pushLine(text) - }) + return MiraiLogger.Factory.create(MiraiConsoleImplementationTerminal::class, identity) +// return PlatformLogger(identity = identity, output = { line -> +// val text = line + ANSI_RESET +// lineReader.printAbove(text) +// logService.pushLine(text) +// }) + } + + override fun createLoggerFactory(platformImplementation: MiraiLogger.Factory): MiraiLogger.Factory { + // platformImplementation is not used by Terminal + + return object : MiraiLogger.Factory { + override fun create(requester: Class<*>, identity: String?): MiraiLogger { + return PlatformLogger(identity = identity ?: requester.simpleName, output = { line -> + val text = line + ANSI_RESET + prePrintNewLog() + lineReader.printAbove(text) + postPrintNewLog() + logService.pushLine(text) + }) + } + + override fun create(requester: KClass<*>, identity: String?): MiraiLogger { + return PlatformLogger(identity = identity ?: requester.simpleName, output = { line -> + val text = line + ANSI_RESET + prePrintNewLog() + lineReader.printAbove(text) + postPrintNewLog() + logService.pushLine(text) + }) + } + + } } init { diff --git a/mirai-console/frontend/mirai-console-terminal/src/MiraiConsoleTerminalLoader.kt b/mirai-console/frontend/mirai-console-terminal/src/MiraiConsoleTerminalLoader.kt index 5606cc20b..ab231ae3d 100644 --- a/mirai-console/frontend/mirai-console-terminal/src/MiraiConsoleTerminalLoader.kt +++ b/mirai-console/frontend/mirai-console-terminal/src/MiraiConsoleTerminalLoader.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 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( @@ -28,10 +28,7 @@ import net.mamoe.mirai.console.terminal.noconsole.SystemOutputPrintStream import net.mamoe.mirai.console.util.ConsoleExperimentalApi import net.mamoe.mirai.console.util.ConsoleInternalApi import net.mamoe.mirai.message.data.Message -import net.mamoe.mirai.utils.childScope -import net.mamoe.mirai.utils.debug -import net.mamoe.mirai.utils.info -import net.mamoe.mirai.utils.verbose +import net.mamoe.mirai.utils.* import org.jline.utils.Signals import java.io.FileDescriptor import java.io.FileOutputStream @@ -48,6 +45,9 @@ import kotlin.system.exitProcess * mirai-console-terminal CLI 入口点 */ object MiraiConsoleTerminalLoader { + + // Note: Do not run this in IDEA, as you will get invalid classpath and `java.lang.NoClassDefFoundError`. + // Run `RunTerminal.kt` under `test` source set instead. @JvmStatic fun main(args: Array) { parse(args, exitProcess = true) @@ -123,19 +123,24 @@ object MiraiConsoleTerminalLoader { if (exitProcess) exitProcess(0) return } + "--no-console" -> { ConsoleTerminalSettings.noConsole = true } + "--dont-setup-terminal-ansi" -> { ConsoleTerminalSettings.setupAnsi = false } + "--no-logging" -> { ConsoleTerminalSettings.noLogging = true } + "--no-ansi" -> { ConsoleTerminalSettings.noAnsi = true ConsoleTerminalSettings.setupAnsi = false } + "--reading-replacement" -> { ConsoleTerminalSettings.noConsoleSafeReading = true if (iterator.hasNext()) { @@ -148,9 +153,11 @@ object MiraiConsoleTerminalLoader { return } } + "--safe-reading" -> { ConsoleTerminalSettings.noConsoleSafeReading = true } + else -> { println("Unknown option `$option`") printHelpMessage() @@ -287,7 +294,7 @@ internal fun overrideSTD(terminal: MiraiConsoleImplementation) { System.setOut( PrintStream( BufferedOutputStream( - logger = terminal.createLogger("stdout")::info + logger = MiraiLogger.Factory.create(terminal::class, "stdout")::info ), false, "UTF-8" @@ -296,7 +303,7 @@ internal fun overrideSTD(terminal: MiraiConsoleImplementation) { System.setErr( PrintStream( BufferedOutputStream( - logger = terminal.createLogger("stderr")::warning + logger = MiraiLogger.Factory.create(terminal::class, "stderr")::warning ), false, "UTF-8" diff --git a/mirai-core-api/compatibility-validation/android/api/android.api b/mirai-core-api/compatibility-validation/android/api/android.api index 1e0a6cfdd..dbd909f10 100644 --- a/mirai-core-api/compatibility-validation/android/api/android.api +++ b/mirai-core-api/compatibility-validation/android/api/android.api @@ -6162,7 +6162,7 @@ public abstract interface class net/mamoe/mirai/utils/MiraiLogger { public fun isVerboseEnabled ()Z public fun isWarningEnabled ()Z public synthetic fun plus (Lnet/mamoe/mirai/utils/MiraiLogger;)Lnet/mamoe/mirai/utils/MiraiLogger; - public static fun setDefaultLoggerCreator (Lkotlin/jvm/functions/Function1;)V + public static synthetic fun setDefaultLoggerCreator (Lkotlin/jvm/functions/Function1;)V public synthetic 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 @@ -6175,7 +6175,7 @@ public abstract interface class net/mamoe/mirai/utils/MiraiLogger { public final class net/mamoe/mirai/utils/MiraiLogger$Companion { public final synthetic fun create (Ljava/lang/String;)Lnet/mamoe/mirai/utils/MiraiLogger; public final synthetic fun getTopLevel ()Lnet/mamoe/mirai/utils/MiraiLogger; - public final fun setDefaultLoggerCreator (Lkotlin/jvm/functions/Function1;)V + public final synthetic fun setDefaultLoggerCreator (Lkotlin/jvm/functions/Function1;)V } public abstract interface class net/mamoe/mirai/utils/MiraiLogger$Factory { diff --git a/mirai-core-api/compatibility-validation/jvm/api/jvm.api b/mirai-core-api/compatibility-validation/jvm/api/jvm.api index bbae04d11..a1eb44d2e 100644 --- a/mirai-core-api/compatibility-validation/jvm/api/jvm.api +++ b/mirai-core-api/compatibility-validation/jvm/api/jvm.api @@ -6162,7 +6162,7 @@ public abstract interface class net/mamoe/mirai/utils/MiraiLogger { public fun isVerboseEnabled ()Z public fun isWarningEnabled ()Z public synthetic fun plus (Lnet/mamoe/mirai/utils/MiraiLogger;)Lnet/mamoe/mirai/utils/MiraiLogger; - public static fun setDefaultLoggerCreator (Lkotlin/jvm/functions/Function1;)V + public static synthetic fun setDefaultLoggerCreator (Lkotlin/jvm/functions/Function1;)V public synthetic 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 @@ -6175,7 +6175,7 @@ public abstract interface class net/mamoe/mirai/utils/MiraiLogger { public final class net/mamoe/mirai/utils/MiraiLogger$Companion { public final synthetic fun create (Ljava/lang/String;)Lnet/mamoe/mirai/utils/MiraiLogger; public final synthetic fun getTopLevel ()Lnet/mamoe/mirai/utils/MiraiLogger; - public final fun setDefaultLoggerCreator (Lkotlin/jvm/functions/Function1;)V + public final synthetic fun setDefaultLoggerCreator (Lkotlin/jvm/functions/Function1;)V } public abstract interface class net/mamoe/mirai/utils/MiraiLogger$Factory { diff --git a/mirai-core-api/src/jvmBaseMain/kotlin/utils/LoggerAdapters.kt b/mirai-core-api/src/jvmBaseMain/kotlin/utils/LoggerAdapters.kt index 8c3c6e7b9..4acea122d 100644 --- a/mirai-core-api/src/jvmBaseMain/kotlin/utils/LoggerAdapters.kt +++ b/mirai-core-api/src/jvmBaseMain/kotlin/utils/LoggerAdapters.kt @@ -32,9 +32,16 @@ public object LoggerAdapters { */ @JvmStatic public fun useLog4j2() { - DefaultFactoryOverrides.override { requester, identity -> - val logger = LogManager.getLogger(requester) - Log4jLoggerAdapter(logger, MarkerManager.getMarker(identity ?: logger.name).addParents(MARKER_MIRAI)) + MiraiLoggerFactoryImplementationBridge.wrapCurrent { + object : MiraiLogger.Factory { + override fun create(requester: Class<*>, identity: String?): MiraiLogger { + val logger = LogManager.getLogger(requester) + return Log4jLoggerAdapter( + logger, + MarkerManager.getMarker(identity ?: logger.name).addParents(MARKER_MIRAI) + ) + } + } } } diff --git a/mirai-core-api/src/jvmBaseMain/kotlin/utils/MiraiLogger.kt b/mirai-core-api/src/jvmBaseMain/kotlin/utils/MiraiLogger.kt index 53ac41f7e..edc9692cb 100644 --- a/mirai-core-api/src/jvmBaseMain/kotlin/utils/MiraiLogger.kt +++ b/mirai-core-api/src/jvmBaseMain/kotlin/utils/MiraiLogger.kt @@ -12,8 +12,13 @@ package net.mamoe.mirai.utils +import kotlinx.atomicfu.atomic +import kotlinx.atomicfu.loop +import me.him188.kotlin.dynamic.delegation.dynamicDelegation import net.mamoe.mirai.utils.* import java.util.* +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract import kotlin.reflect.KClass /** @@ -27,7 +32,7 @@ import kotlin.reflect.KClass * * ## 使用第三方日志库接管 Mirai 日志系统 * - * 使用 [LoggerAdapters], 将第三方日志 `Logger` 转为 [MiraiLogger]. 然后通过 [MiraiLogger.setDefaultLoggerCreator] 全局覆盖日志. + * 使用 [LoggerAdapters], 将第三方日志 `Logger` 转为 [MiraiLogger]. 然后通过 [MiraiLogger.Factory] 提供实现. * * ## 实现或使用 [MiraiLogger] * @@ -79,7 +84,8 @@ public actual interface MiraiLogger { */ public fun create(requester: Class<*>): MiraiLogger = create(requester, null) - public actual companion object INSTANCE : Factory by loadService(Factory::class, { DefaultFactory() }) + public actual companion object INSTANCE : + Factory by dynamicDelegation({ MiraiLoggerFactoryImplementationBridge }) } public actual companion object { @@ -95,21 +101,21 @@ public actual interface MiraiLogger { /** * 已弃用, 请实现 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.ERROR + level = DeprecationLevel.HIDDEN ) // deprecated since 2.7 @JvmStatic - @DeprecatedSinceMirai(warningSince = "2.7", errorSince = "2.10") // left ERROR intentionally, for internal uses. - public fun setDefaultLoggerCreator(creator: (identity: String?) -> MiraiLogger) { - DefaultFactoryOverrides.override { _, identity -> creator(identity) } + @DeprecatedSinceMirai(warningSince = "2.7", errorSince = "2.10", hiddenSince = "2.13") + public fun setDefaultLoggerCreator(@Suppress("UNUSED_PARAMETER") creator: (identity: String?) -> MiraiLogger) { + // nop + + +// DefaultFactoryOverrides.override { _, identity -> creator(identity) } } /** * 旧版本用于创建 [MiraiLogger]. 已弃用. 请使用 [MiraiLogger.Factory.INSTANCE.create]. - * - * @see setDefaultLoggerCreator */ @Deprecated( "Please use MiraiLogger.Factory.create", ReplaceWith( @@ -249,33 +255,75 @@ public actual interface MiraiLogger { public actual fun call(priority: SimpleLogger.LogPriority, message: String?, e: Throwable?): Unit = priority.correspondingFunction(this, message, e) - @Suppress("DeprecatedCallableAddReplaceWith") @Deprecated("plus 设计不佳, 请避免使用.", level = DeprecationLevel.HIDDEN) // deprecated since 2.7 @DeprecatedSinceMirai(warningSince = "2.7", errorSince = "2.10", hiddenSince = "2.11") public operator fun plus(follower: T): T = follower } +// used by Mirai Console +/** + * @since 2.13 + */ +internal object MiraiLoggerFactoryImplementationBridge : MiraiLogger.Factory { + @Volatile + var instance: MiraiLogger.Factory = createPlatformInstance() + private set -internal object DefaultFactoryOverrides { - var override: ((requester: Class<*>, identity: String?) -> MiraiLogger)? = - null // 支持 LoggerAdapters 以及兼容旧版本 + fun createPlatformInstance() = loadService(MiraiLogger.Factory::class) { DefaultFactory() } - @JvmStatic - fun override(lambda: (requester: Class<*>, identity: String?) -> MiraiLogger) { - override = lambda + private val frozen = atomic(false) + + fun freeze(): Boolean { + return frozen.compareAndSet(expect = false, update = true) } - @JvmStatic - fun clearOverride() { - override = null + @TestOnly + fun reinit() { + frozen.loop { value -> + instance = createPlatformInstance() + if (frozen.compareAndSet(value, false)) return + } + } + + fun setInstance(instance: MiraiLogger.Factory) { + if (frozen.value) { + error( + "LoggerFactory instance had been frozen, so it's impossible to override it." + + "If you are using Mirai Console and you want to override platform logging implementation, " + + "please do so before initialization of MiraiConsole, that is, before `MiraiConsoleImplementation.start()`. " + + "Plugins are not allowed to override logging implementation, and this is done in the very fundamental implementation of Mirai Console so there is no way to escape that." + + "Normally it is only sensible for Mirai Console frontend implementor to do that." + + "If you are just using mirai-core, this error should not happen. There should be no limitation in overriding logging implementation with mirai-core. " + + "Check if you actually did use mirai-console somewhere, or please file an issue on https://github.com/mamoe/mirai/issues/new/choose" + ) + } + this.instance = instance + } + + inline fun wrapCurrent(mapper: (current: MiraiLogger.Factory) -> MiraiLogger.Factory) { + contract { callsInPlace(mapper, InvocationKind.EXACTLY_ONCE) } + setInstance(this.instance.let(mapper)) + } + + override fun create(requester: KClass<*>, identity: String?): MiraiLogger { + return instance.create(requester, identity) } -} -internal class DefaultFactory : MiraiLogger.Factory { override fun create(requester: Class<*>, identity: String?): MiraiLogger { - val override = DefaultFactoryOverrides.override - return if (override != null) override(requester, identity) else PlatformLogger( - identity ?: requester.kotlin.simpleName ?: requester.simpleName - ) + return instance.create(requester, identity) + } + + override fun create(requester: KClass<*>): MiraiLogger { + return instance.create(requester) + } + + override fun create(requester: Class<*>): MiraiLogger { + return instance.create(requester) + } +} + +private class DefaultFactory : MiraiLogger.Factory { + override fun create(requester: Class<*>, identity: String?): MiraiLogger { + return PlatformLogger(identity ?: requester.kotlin.simpleName ?: requester.simpleName) } } diff --git a/mirai-core-api/src/jvmBaseTest/kotlin/logging/AbstractLoggingTest.kt b/mirai-core-api/src/jvmBaseTest/kotlin/logging/AbstractLoggingTest.kt new file mode 100644 index 000000000..b9eb72a0c --- /dev/null +++ b/mirai-core-api/src/jvmBaseTest/kotlin/logging/AbstractLoggingTest.kt @@ -0,0 +1,20 @@ +/* + * Copyright 2019-2022 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.utils.MiraiLoggerFactoryImplementationBridge +import org.junit.jupiter.api.AfterEach + +internal abstract class AbstractLoggingTest { + @AfterEach + fun cleanup() { + MiraiLoggerFactoryImplementationBridge.run { setInstance(createPlatformInstance()) } + } +} \ No newline at end of file diff --git a/mirai-core-api/src/jvmBaseTest/kotlin/logging/Log4j2LoggingTest.kt b/mirai-core-api/src/jvmBaseTest/kotlin/logging/Log4j2LoggingTest.kt index 31de6f9cc..4d9c6a548 100644 --- a/mirai-core-api/src/jvmBaseTest/kotlin/logging/Log4j2LoggingTest.kt +++ b/mirai-core-api/src/jvmBaseTest/kotlin/logging/Log4j2LoggingTest.kt @@ -11,26 +11,25 @@ package net.mamoe.mirai.logging import net.mamoe.mirai.Bot import net.mamoe.mirai.internal.utils.* -import net.mamoe.mirai.utils.DefaultFactoryOverrides import net.mamoe.mirai.utils.LoggerAdapters.asMiraiLogger import net.mamoe.mirai.utils.MiraiLogger +import net.mamoe.mirai.utils.MiraiLoggerFactoryImplementationBridge import org.apache.logging.log4j.LogManager -import org.junit.jupiter.api.AfterEach import kotlin.test.* -internal class Log4j2LoggingTest { +internal class Log4j2LoggingTest : AbstractLoggingTest() { @BeforeTest fun init() { - DefaultFactoryOverrides.override { requester, identity -> - LogManager.getLogger(requester).asMiraiLogger(Marker(identity ?: requester.simpleName, MARKER_MIRAI)) + MiraiLoggerFactoryImplementationBridge.wrapCurrent { + object : MiraiLogger.Factory { + override fun create(requester: Class<*>, identity: String?): MiraiLogger { + return LogManager.getLogger(requester) + .asMiraiLogger(Marker(identity ?: requester.simpleName, MARKER_MIRAI)) + } + } } } - @AfterEach - fun cleanup() { - DefaultFactoryOverrides.clearOverride() - } - private fun MiraiLogger.cast(): Log4jLoggerAdapter = this as Log4jLoggerAdapter @Test diff --git a/mirai-core-api/src/jvmBaseTest/kotlin/logging/LoggingCompatibilityTest.kt b/mirai-core-api/src/jvmBaseTest/kotlin/logging/LoggingCompatibilityTest.kt index d8b252f69..aeeed68df 100644 --- a/mirai-core-api/src/jvmBaseTest/kotlin/logging/LoggingCompatibilityTest.kt +++ b/mirai-core-api/src/jvmBaseTest/kotlin/logging/LoggingCompatibilityTest.kt @@ -10,20 +10,26 @@ package net.mamoe.mirai.logging import net.mamoe.mirai.utils.MiraiLogger +import net.mamoe.mirai.utils.MiraiLoggerFactoryImplementationBridge import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertIs -internal class LoggingCompatibilityTest { +internal class LoggingCompatibilityTest : AbstractLoggingTest() { @Suppress("DEPRECATION_ERROR") @Test fun `legacy overrides are still working if no services are found`() { val messages = StringBuilder() - MiraiLogger.setDefaultLoggerCreator { - net.mamoe.mirai.utils.SimpleLogger("my logger") { message: String?, _: Throwable? -> - messages.append(message) + MiraiLoggerFactoryImplementationBridge.wrapCurrent { + object : MiraiLogger.Factory { + override fun create(requester: Class<*>, identity: String?): MiraiLogger { + return net.mamoe.mirai.utils.SimpleLogger("my logger") { message: String?, _: Throwable? -> + messages.append(message) + } + + } } } diff --git a/mirai-core/src/androidTest/kotlin/test/initPlatform.android.kt b/mirai-core/src/androidTest/kotlin/test/initPlatform.android.kt index 331d597ea..ff718a53d 100644 --- a/mirai-core/src/androidTest/kotlin/test/initPlatform.android.kt +++ b/mirai-core/src/androidTest/kotlin/test/initPlatform.android.kt @@ -13,7 +13,7 @@ import net.mamoe.mirai.utils.MiraiLogger import org.bouncycastle.jce.provider.BouncyCastleProvider import java.security.Security import kotlin.test.Test -import kotlin.test.assertTrue +import kotlin.test.assertIs internal actual fun initPlatform() { init @@ -32,9 +32,7 @@ internal actual class PlatformInitializationTest : AbstractTest() { @Test actual fun test() { - assertTrue { - @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") - MiraiLogger.Factory.create(this::class, "1") is net.mamoe.mirai.internal.utils.StdoutLogger - } + @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") + assertIs(MiraiLogger.Factory.create(this::class, "1")) } } \ No newline at end of file diff --git a/mirai-core/src/jvmBaseTest/kotlin/test/AbstractTest.kt b/mirai-core/src/jvmBaseTest/kotlin/test/AbstractTest.kt index a44ec819d..b74eba689 100644 --- a/mirai-core/src/jvmBaseTest/kotlin/test/AbstractTest.kt +++ b/mirai-core/src/jvmBaseTest/kotlin/test/AbstractTest.kt @@ -51,9 +51,17 @@ internal actual abstract class AbstractTest actual constructor() : CommonAbstrac DebugProbes.install() - @Suppress("DEPRECATION_ERROR") - MiraiLogger.setDefaultLoggerCreator { - SynchronizedStdoutLogger(it) + @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") + net.mamoe.mirai.utils.MiraiLoggerFactoryImplementationBridge.wrapCurrent { + object : MiraiLogger.Factory { + override fun create(requester: Class<*>, identity: String?): MiraiLogger { + return SynchronizedStdoutLogger(identity ?: requester.simpleName) + } + + override fun create(requester: KClass<*>, identity: String?): MiraiLogger { + return SynchronizedStdoutLogger(identity ?: requester.simpleName) + } + } } setSystemProp("mirai.network.packet.logger", "true") diff --git a/mirai-core/src/jvmTest/kotlin/netinternalkit/LogCapture.kt b/mirai-core/src/jvmTest/kotlin/netinternalkit/LogCapture.kt index dcea33a54..d551191c4 100644 --- a/mirai-core/src/jvmTest/kotlin/netinternalkit/LogCapture.kt +++ b/mirai-core/src/jvmTest/kotlin/netinternalkit/LogCapture.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 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. @@ -12,10 +12,7 @@ package net.mamoe.mirai.internal.netinternalkit import kotlinx.atomicfu.locks.withLock -import net.mamoe.mirai.utils.DefaultFactoryOverrides -import net.mamoe.mirai.utils.PlatformLogger -import net.mamoe.mirai.utils.SizedCache -import net.mamoe.mirai.utils.currentTimeMillis +import net.mamoe.mirai.utils.* import java.io.File internal object LogCapture { @@ -28,12 +25,17 @@ internal object LogCapture { fun setupCapture(maxLine: Int = 200) { logCache = SizedCache(maxLine) + @Suppress("INVISIBLE_MEMBER") - DefaultFactoryOverrides.override { requester, identity -> - PlatformLogger( - identity ?: requester.kotlin.simpleName ?: requester.simpleName, - output - ) + MiraiLoggerFactoryImplementationBridge.wrapCurrent { + object : MiraiLogger.Factory { + override fun create(requester: Class<*>, identity: String?): MiraiLogger { + return PlatformLogger( + identity ?: requester.kotlin.simpleName ?: requester.simpleName, + output + ) + } + } } NetReplayHelperSettings.logger_console = PlatformLogger(