diff --git a/mirai-console/backend/mirai-console/src/MiraiConsoleImplementation.kt b/mirai-console/backend/mirai-console/src/MiraiConsoleImplementation.kt index 23bdbc080..1fc0d090f 100644 --- a/mirai-console/backend/mirai-console/src/MiraiConsoleImplementation.kt +++ b/mirai-console/backend/mirai-console/src/MiraiConsoleImplementation.kt @@ -23,10 +23,10 @@ import net.mamoe.mirai.console.data.PluginData import net.mamoe.mirai.console.data.PluginDataStorage import net.mamoe.mirai.console.extension.ComponentStorage import net.mamoe.mirai.console.fontend.DefaultLoggingProcessProgress +import net.mamoe.mirai.console.fontend.ProcessProgress import net.mamoe.mirai.console.internal.MiraiConsoleImplementationBridge import net.mamoe.mirai.console.internal.command.CommandManagerImpl import net.mamoe.mirai.console.internal.data.builtins.ConsoleDataScopeImpl -import net.mamoe.mirai.console.fontend.ProcessProgress import net.mamoe.mirai.console.internal.logging.LoggerControllerImpl import net.mamoe.mirai.console.internal.plugin.BuiltInJvmPluginLoaderImpl import net.mamoe.mirai.console.internal.pluginManagerImpl @@ -228,15 +228,6 @@ public interface MiraiConsoleImplementation : CoroutineScope { return DefaultLoggingProcessProgress() } - /** - * 创建一个 [MiraiLogger.Factory]. 在返回的实例中必须调用 [platformImplementation] 来适配平台日志实现. - * - * @param platformImplementation 平台的日志实现, 这可能是使用 SLF4J 等日志框架转接的实例. - * - * @since 2.13 - */ - public fun createLoggerFactory(platformImplementation: MiraiLogger.Factory): MiraiLogger.Factory - /** * 该前端是否支持使用 Ansi 输出彩色信息 @@ -390,6 +381,36 @@ public interface MiraiConsoleImplementation : CoroutineScope { get() = backendAccessInstance + /////////////////////////////////////////////////////////////////////////// + // Logging + /////////////////////////////////////////////////////////////////////////// + + /** + * 创建一个 [MiraiLogger.Factory]. + * + * @since 2.13 + */ + public fun createLoggerFactory(context: FrontendLoggingInitContext): MiraiLogger.Factory + + /** + * 前端 [MiraiLogger.Factory] 加载的上下文 + * + * 全局的日志工厂的初始化可以分为如下几步 + * + * 1. 接管 stdout (见 [System.setOut]), 将 stdout 重定向至屏幕. + * 之后平台日志实现会将日志通过被接管的 stdout 输出至屏幕 + * 2. 前端返回 [platformImplementation][acquirePlatformImplementation] 或者返回适配的 [MiraiLogger.Factory] + */ + @ConsoleFrontEndImplementation + public interface FrontendLoggingInitContext { + /** + * 平台的日志实现, 这可能是使用 SLF4J 等日志框架转接的实例. + * + * 调用此函数会立即初始化平台日志实现. 在未完成准备工作前切勿使用此方法 + */ + public fun acquirePlatformImplementation(): MiraiLogger.Factory + } + /////////////////////////////////////////////////////////////////////////// // ConsoleLaunchOptions /////////////////////////////////////////////////////////////////////////// diff --git a/mirai-console/backend/mirai-console/src/internal/MiraiConsoleImplementationBridge.kt b/mirai-console/backend/mirai-console/src/internal/MiraiConsoleImplementationBridge.kt index 11fdc9e28..6f042957a 100644 --- a/mirai-console/backend/mirai-console/src/internal/MiraiConsoleImplementationBridge.kt +++ b/mirai-console/backend/mirai-console/src/internal/MiraiConsoleImplementationBridge.kt @@ -117,6 +117,10 @@ internal class MiraiConsoleImplementationBridge( } } + override fun createLoggerFactory(context: MiraiConsoleImplementation.FrontendLoggingInitContext): MiraiLogger.Factory { + error("Duplicated logger factory initalization is not allowed. Use MiraiLogger.Factory instead.") + } + init { // When writing a log: // 1. ControlledLoggerFactory checks if that log level is enabled @@ -124,12 +128,20 @@ internal class MiraiConsoleImplementationBridge( // ... 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)) + val context = object : MiraiConsoleImplementation.FrontendLoggingInitContext { + override fun acquirePlatformImplementation(): MiraiLogger.Factory { + @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") + return MiraiLoggerFactoryImplementationBridge.instance + } } + + val response = externalImplementation.createLoggerFactory(context) + @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") + MiraiLoggerFactoryImplementationBridge.setInstance(ControlledLoggerFactory(response)) + @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") MiraiLoggerFactoryImplementationBridge.freeze() // forbid any further overrides + } diff --git a/mirai-console/backend/mirai-console/test/testFramework/MockConsoleImplementation.kt b/mirai-console/backend/mirai-console/test/testFramework/MockConsoleImplementation.kt index a36341ae5..c8cc5a743 100644 --- a/mirai-console/backend/mirai-console/test/testFramework/MockConsoleImplementation.kt +++ b/mirai-console/backend/mirai-console/test/testFramework/MockConsoleImplementation.kt @@ -85,7 +85,7 @@ open class MockConsoleImplementation : MiraiConsoleImplementation { return PlatformLogger(identity) } - override fun createLoggerFactory(platformImplementation: MiraiLogger.Factory): MiraiLogger.Factory { + override fun createLoggerFactory(context: MiraiConsoleImplementation.FrontendLoggingInitContext): MiraiLogger.Factory { return object : MiraiLogger.Factory { override fun create(requester: Class<*>, identity: String?): MiraiLogger { return PlatformLogger(identity) diff --git a/mirai-console/frontend/mirai-console-terminal/src/MiraiConsoleImplementationTerminal.kt b/mirai-console/frontend/mirai-console-terminal/src/MiraiConsoleImplementationTerminal.kt index bf341b3de..2295b17fb 100644 --- a/mirai-console/frontend/mirai-console-terminal/src/MiraiConsoleImplementationTerminal.kt +++ b/mirai-console/frontend/mirai-console-terminal/src/MiraiConsoleImplementationTerminal.kt @@ -117,7 +117,7 @@ open class MiraiConsoleImplementationTerminal // }) } - override fun createLoggerFactory(platformImplementation: MiraiLogger.Factory): MiraiLogger.Factory { + override fun createLoggerFactory(context: MiraiConsoleImplementation.FrontendLoggingInitContext): MiraiLogger.Factory { // platformImplementation is not used by Terminal return object : MiraiLogger.Factory { diff --git a/mirai-core-api/src/jvmBaseMain/kotlin/utils/MiraiLogger.kt b/mirai-core-api/src/jvmBaseMain/kotlin/utils/MiraiLogger.kt index edc9692cb..f7077c8b4 100644 --- a/mirai-core-api/src/jvmBaseMain/kotlin/utils/MiraiLogger.kt +++ b/mirai-core-api/src/jvmBaseMain/kotlin/utils/MiraiLogger.kt @@ -265,9 +265,10 @@ public actual interface MiraiLogger { * @since 2.13 */ internal object MiraiLoggerFactoryImplementationBridge : MiraiLogger.Factory { - @Volatile - var instance: MiraiLogger.Factory = createPlatformInstance() - private set + @Suppress("ObjectPropertyName") + private var _instance by lateinitMutableProperty { createPlatformInstance() } + + internal val instance get() = _instance fun createPlatformInstance() = loadService(MiraiLogger.Factory::class) { DefaultFactory() } @@ -280,7 +281,7 @@ internal object MiraiLoggerFactoryImplementationBridge : MiraiLogger.Factory { @TestOnly fun reinit() { frozen.loop { value -> - instance = createPlatformInstance() + _instance = createPlatformInstance() if (frozen.compareAndSet(value, false)) return } } @@ -297,7 +298,7 @@ internal object MiraiLoggerFactoryImplementationBridge : MiraiLogger.Factory { "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 + this._instance = instance } inline fun wrapCurrent(mapper: (current: MiraiLogger.Factory) -> MiraiLogger.Factory) {