[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)
This commit is contained in:
Him188 2022-08-27 18:46:16 +08:00 committed by GitHub
parent 2d393ee0b0
commit f0651c81c6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 314 additions and 127 deletions

View File

@ -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 许可证的约束, 可以在以下链接找到该许可证. * 此源代码的使用受 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. * 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 * https://github.com/mamoe/mirai/blob/dev/LICENSE
*/ */
@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
package net.mamoe.mirai.utils.logging package net.mamoe.mirai.utils.logging
import io.ktor.client.* import io.ktor.client.*
@ -20,18 +22,6 @@ import kotlin.test.assertFalse
import kotlin.test.assertIs import kotlin.test.assertIs
internal class MiraiLog4JAdapterTest { 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<net.mamoe.mirai.internal.utils.Log4jLoggerAdapter>(MiraiLogger.Factory.create(this::class))
}
@Test @Test
fun `using log4j`() { fun `using log4j`() {
assertIs<MiraiLog4JFactory>(loadService(MiraiLogger.Factory::class)) assertIs<MiraiLog4JFactory>(loadService(MiraiLogger.Factory::class))

View File

@ -128,8 +128,17 @@ public interface MiraiConsole : CoroutineScope {
get() = throw UnsupportedOperationException("PluginCenter is not supported yet") 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 @ConsoleExperimentalApi
public fun createLogger(identity: String?): MiraiLogger public fun createLogger(identity: String?): MiraiLogger
@ -204,6 +213,7 @@ public interface MiraiConsole : CoroutineScope {
mainLogger.verbose { "Renaming $deviceInfoInWorkingDir to $deviceInWorkingDir" } mainLogger.verbose { "Renaming $deviceInfoInWorkingDir to $deviceInWorkingDir" }
deviceInfoInWorkingDir.renameTo(deviceInWorkingDir) deviceInfoInWorkingDir.renameTo(deviceInWorkingDir)
} }
deviceInRoot.exists() -> { deviceInRoot.exists() -> {
// copy root/device.json to bots/id/device.json // copy root/device.json to bots/id/device.json
mainLogger.verbose { "Coping $deviceInRoot to $deviceInWorkingDir" } mainLogger.verbose { "Coping $deviceInRoot to $deviceInWorkingDir" }

View File

@ -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.ConsoleExperimentalApi
import net.mamoe.mirai.console.util.ConsoleInput import net.mamoe.mirai.console.util.ConsoleInput
import net.mamoe.mirai.message.data.Message import net.mamoe.mirai.message.data.Message
import net.mamoe.mirai.utils.BotConfiguration import net.mamoe.mirai.utils.*
import net.mamoe.mirai.utils.LoginSolver
import net.mamoe.mirai.utils.MiraiLogger
import net.mamoe.mirai.utils.NotStableForInheritance
import java.nio.file.Path import java.nio.file.Path
import java.util.* import java.util.*
import java.util.concurrent.locks.ReentrantLock import java.util.concurrent.locks.ReentrantLock
@ -216,9 +213,14 @@ public interface MiraiConsoleImplementation : CoroutineScope {
/** /**
* 创建一个 [MiraiLogger]. * 创建一个 [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] * 因此不要在 [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 public fun createLogger(identity: String?): MiraiLogger
/** @see [MiraiConsole.newProcessProgress] */ /** @see [MiraiConsole.newProcessProgress] */
@ -226,6 +228,16 @@ public interface MiraiConsoleImplementation : CoroutineScope {
return DefaultLoggingProcessProgress() return DefaultLoggingProcessProgress()
} }
/**
* 创建一个 [MiraiLogger.Factory]. 在返回的实例中必须调用 [platformImplementation] 来适配平台日志实现.
*
* @param platformImplementation 平台的日志实现, 这可能是使用 SLF4J 等日志框架转接的实例.
*
* @since 2.13
*/
public fun createLoggerFactory(platformImplementation: MiraiLogger.Factory): MiraiLogger.Factory
/** /**
* 该前端是否支持使用 Ansi 输出彩色信息 * 该前端是否支持使用 Ansi 输出彩色信息
* *

View File

@ -59,9 +59,9 @@ import net.mamoe.mirai.utils.*
import java.time.Instant import java.time.Instant
import java.time.ZoneId import java.time.ZoneId
import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatter
import java.util.concurrent.atomic.AtomicBoolean
import kotlin.contracts.InvocationKind import kotlin.contracts.InvocationKind
import kotlin.contracts.contract import kotlin.contracts.contract
import kotlin.reflect.KClass
import kotlin.reflect.KProperty import kotlin.reflect.KProperty
import kotlin.reflect.KProperty0 import kotlin.reflect.KProperty0
@ -100,22 +100,49 @@ internal class MiraiConsoleImplementationBridge(
externalImplementation.loggerController 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 * Delegates the [platformImplementation] with [loggerController].
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") */
DefaultFactoryOverrides.override { requester, identity -> private inner class ControlledLoggerFactory(
return@override createLogger( private val platformImplementation: MiraiLogger.Factory,
identity ?: requester.kotlin.simpleName ?: requester.simpleName ) : 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 { override fun createLogger(identity: String?): MiraiLogger {
val controller = loggerController return MiraiLogger.Factory.create(MiraiConsole::class, identity)
return MiraiConsoleLogger(controller, externalImplementation.createLogger(identity))
} }
@Suppress("RemoveRedundantBackticks") @Suppress("RemoveRedundantBackticks")
@ -286,6 +313,7 @@ internal class MiraiConsoleImplementationBridge(
PLAIN -> { PLAIN -> {
MiraiConsole.addBot(id, account.password.value, BotConfiguration::configBot) MiraiConsole.addBot(id, account.password.value, BotConfiguration::configBot)
} }
MD5 -> { MD5 -> {
val md5 = kotlin.runCatching { val md5 = kotlin.runCatching {
account.password.value.hexToBytes() account.password.value.hexToBytes()
@ -339,4 +367,5 @@ internal class MiraiConsoleImplementationBridge(
override fun postPhase(phase: String) { override fun postPhase(phase: String) {
externalImplementation.postPhase(phase) externalImplementation.postPhase(phase)
} }
} }

View File

@ -11,7 +11,6 @@ package net.mamoe.mirai.console.internal.command
import kotlinx.atomicfu.locks.withLock import kotlinx.atomicfu.locks.withLock
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import net.mamoe.mirai.console.MiraiConsole
import net.mamoe.mirai.console.MiraiConsoleImplementation.ConsoleDataScope.Companion.get import net.mamoe.mirai.console.MiraiConsoleImplementation.ConsoleDataScope.Companion.get
import net.mamoe.mirai.console.command.* import net.mamoe.mirai.console.command.*
import net.mamoe.mirai.console.command.Command.Companion.allNames import net.mamoe.mirai.console.command.Command.Companion.allNames
@ -37,7 +36,7 @@ internal class CommandManagerImpl(
parentCoroutineContext: CoroutineContext parentCoroutineContext: CoroutineContext
) : CommandManager, CoroutineScope by parentCoroutineContext.childScope("CommandManagerImpl") { ) : CommandManager, CoroutineScope by parentCoroutineContext.childScope("CommandManagerImpl") {
private val logger: MiraiLogger by lazy { private val logger: MiraiLogger by lazy {
MiraiConsole.createLogger("command") MiraiLogger.Factory.create(CommandManager::class, "command")
} }
@Suppress("ObjectPropertyName") @Suppress("ObjectPropertyName")

View File

@ -41,7 +41,8 @@ internal class BuiltInJvmPluginLoaderImpl(
JvmPluginLoader { JvmPluginLoader {
companion object { companion object {
internal val logger: MiraiLogger = MiraiConsole.createLogger(JvmPluginLoader::class.simpleName!!) internal val logger: MiraiLogger =
MiraiLogger.Factory.create(JvmPluginLoader::class)
} }
fun pluginsFilesSequence( fun pluginsFilesSequence(

View File

@ -10,7 +10,6 @@
package net.mamoe.mirai.console.internal.plugin 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.ExportManager
import net.mamoe.mirai.console.plugin.jvm.JvmPluginClasspath import net.mamoe.mirai.console.plugin.jvm.JvmPluginClasspath
import net.mamoe.mirai.utils.* import net.mamoe.mirai.utils.*
@ -197,7 +196,12 @@ internal class JvmPluginClassLoaderN : URLClassLoader {
private val file_: File private val file_: File
get() = 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<String>() val undefinedDependencies = mutableSetOf<String>()
@Suppress("UNUSED_PARAMETER") @Suppress("UNUSED_PARAMETER")

View File

@ -65,7 +65,7 @@ internal abstract class JvmPluginInternal(
// region JvmPlugin // region JvmPlugin
final override val logger: MiraiLogger by lazy { final override val logger: MiraiLogger by lazy {
BuiltInJvmPluginLoaderImpl.logger.runCatchingLog { BuiltInJvmPluginLoaderImpl.logger.runCatchingLog {
MiraiConsole.createLogger(this.description.name) MiraiLogger.Factory.create(this@JvmPluginInternal::class, this.description.name)
}.getOrThrow() }.getOrThrow()
} }

View File

@ -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.loader.PluginLoader
import net.mamoe.mirai.console.plugin.name import net.mamoe.mirai.console.plugin.name
import net.mamoe.mirai.console.util.SemVersion import net.mamoe.mirai.console.util.SemVersion
import net.mamoe.mirai.utils.TestOnly import net.mamoe.mirai.utils.*
import net.mamoe.mirai.utils.cast
import net.mamoe.mirai.utils.childScope
import net.mamoe.mirai.utils.info
import java.io.File import java.io.File
import java.nio.file.Path import java.nio.file.Path
import java.util.concurrent.CopyOnWriteArrayList import java.util.concurrent.CopyOnWriteArrayList
@ -60,7 +57,7 @@ internal class PluginManagerImpl(
builtInLoaders.toMutableList() builtInLoaders.toMutableList()
} }
private val logger = MiraiConsole.createLogger("plugin") private val logger = MiraiLogger.Factory.create(PluginManager::class, "plugin")
@JvmField @JvmField
internal val resolvedPlugins: MutableList<Plugin> = internal val resolvedPlugins: MutableList<Plugin> =

View File

@ -22,6 +22,11 @@ import org.junit.jupiter.api.AfterEach
import kotlin.test.BeforeTest import kotlin.test.BeforeTest
abstract class AbstractConsoleInstanceTest { abstract class AbstractConsoleInstanceTest {
init {
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
net.mamoe.mirai.utils.MiraiLoggerFactoryImplementationBridge.reinit()
}
val mockPlugin by lazy { mockKotlinPlugin() } val mockPlugin by lazy { mockKotlinPlugin() }
private lateinit var implementation: MiraiConsoleImplementation private lateinit var implementation: MiraiConsoleImplementation
val consoleImplementation: MiraiConsoleImplementation by ::implementation val consoleImplementation: MiraiConsoleImplementation by ::implementation

View File

@ -76,10 +76,23 @@ open class MockConsoleImplementation : MiraiConsoleImplementation {
override fun createLoginSolver(requesterBot: Long, configuration: BotConfiguration): LoginSolver = override fun createLoginSolver(requesterBot: Long, configuration: BotConfiguration): LoginSolver =
LoginSolver.Default!! 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 { override fun createLogger(identity: String?): MiraiLogger {
return PlatformLogger(identity) 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 { override val consoleDataScope: MiraiConsoleImplementation.ConsoleDataScope by lazy {
MiraiConsoleImplementation.ConsoleDataScope.createDefault( MiraiConsoleImplementation.ConsoleDataScope.createDefault(
coroutineContext, coroutineContext,

View File

@ -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 许可证的约束, 可以在以下链接找到该许可证. * 此源代码的使用受 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. * 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( @file:Suppress(
@ -55,6 +55,7 @@ import java.nio.file.Path
import java.nio.file.Paths import java.nio.file.Paths
import kotlin.concurrent.withLock import kotlin.concurrent.withLock
import kotlin.coroutines.Continuation import kotlin.coroutines.Continuation
import kotlin.reflect.KClass
import kotlin.reflect.KProperty import kotlin.reflect.KProperty
/** /**
@ -102,14 +103,45 @@ open class MiraiConsoleImplementationTerminal
return StandardCharImageLoginSolver(input = { requestInput("LOGIN> ") }) 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 { override fun createLogger(identity: String?): MiraiLogger {
return PlatformLogger(identity = identity, output = { line -> return MiraiLogger.Factory.create(MiraiConsoleImplementationTerminal::class, identity)
val text = line + ANSI_RESET // return PlatformLogger(identity = identity, output = { line ->
prePrintNewLog() // val text = line + ANSI_RESET
lineReader.printAbove(text) // lineReader.printAbove(text)
postPrintNewLog() // logService.pushLine(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 { init {

View File

@ -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 许可证的约束, 可以在以下链接找到该许可证. * 此源代码的使用受 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. * 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( @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.ConsoleExperimentalApi
import net.mamoe.mirai.console.util.ConsoleInternalApi import net.mamoe.mirai.console.util.ConsoleInternalApi
import net.mamoe.mirai.message.data.Message import net.mamoe.mirai.message.data.Message
import net.mamoe.mirai.utils.childScope import net.mamoe.mirai.utils.*
import net.mamoe.mirai.utils.debug
import net.mamoe.mirai.utils.info
import net.mamoe.mirai.utils.verbose
import org.jline.utils.Signals import org.jline.utils.Signals
import java.io.FileDescriptor import java.io.FileDescriptor
import java.io.FileOutputStream import java.io.FileOutputStream
@ -48,6 +45,9 @@ import kotlin.system.exitProcess
* mirai-console-terminal CLI 入口点 * mirai-console-terminal CLI 入口点
*/ */
object MiraiConsoleTerminalLoader { 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 @JvmStatic
fun main(args: Array<String>) { fun main(args: Array<String>) {
parse(args, exitProcess = true) parse(args, exitProcess = true)
@ -123,19 +123,24 @@ object MiraiConsoleTerminalLoader {
if (exitProcess) exitProcess(0) if (exitProcess) exitProcess(0)
return return
} }
"--no-console" -> { "--no-console" -> {
ConsoleTerminalSettings.noConsole = true ConsoleTerminalSettings.noConsole = true
} }
"--dont-setup-terminal-ansi" -> { "--dont-setup-terminal-ansi" -> {
ConsoleTerminalSettings.setupAnsi = false ConsoleTerminalSettings.setupAnsi = false
} }
"--no-logging" -> { "--no-logging" -> {
ConsoleTerminalSettings.noLogging = true ConsoleTerminalSettings.noLogging = true
} }
"--no-ansi" -> { "--no-ansi" -> {
ConsoleTerminalSettings.noAnsi = true ConsoleTerminalSettings.noAnsi = true
ConsoleTerminalSettings.setupAnsi = false ConsoleTerminalSettings.setupAnsi = false
} }
"--reading-replacement" -> { "--reading-replacement" -> {
ConsoleTerminalSettings.noConsoleSafeReading = true ConsoleTerminalSettings.noConsoleSafeReading = true
if (iterator.hasNext()) { if (iterator.hasNext()) {
@ -148,9 +153,11 @@ object MiraiConsoleTerminalLoader {
return return
} }
} }
"--safe-reading" -> { "--safe-reading" -> {
ConsoleTerminalSettings.noConsoleSafeReading = true ConsoleTerminalSettings.noConsoleSafeReading = true
} }
else -> { else -> {
println("Unknown option `$option`") println("Unknown option `$option`")
printHelpMessage() printHelpMessage()
@ -287,7 +294,7 @@ internal fun overrideSTD(terminal: MiraiConsoleImplementation) {
System.setOut( System.setOut(
PrintStream( PrintStream(
BufferedOutputStream( BufferedOutputStream(
logger = terminal.createLogger("stdout")::info logger = MiraiLogger.Factory.create(terminal::class, "stdout")::info
), ),
false, false,
"UTF-8" "UTF-8"
@ -296,7 +303,7 @@ internal fun overrideSTD(terminal: MiraiConsoleImplementation) {
System.setErr( System.setErr(
PrintStream( PrintStream(
BufferedOutputStream( BufferedOutputStream(
logger = terminal.createLogger("stderr")::warning logger = MiraiLogger.Factory.create(terminal::class, "stderr")::warning
), ),
false, false,
"UTF-8" "UTF-8"

View File

@ -6162,7 +6162,7 @@ public abstract interface class net/mamoe/mirai/utils/MiraiLogger {
public fun isVerboseEnabled ()Z public fun isVerboseEnabled ()Z
public fun isWarningEnabled ()Z public fun isWarningEnabled ()Z
public synthetic fun plus (Lnet/mamoe/mirai/utils/MiraiLogger;)Lnet/mamoe/mirai/utils/MiraiLogger; 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 synthetic fun setFollower (Lnet/mamoe/mirai/utils/MiraiLogger;)V
public abstract fun verbose (Ljava/lang/String;)V public abstract fun verbose (Ljava/lang/String;)V
public abstract fun verbose (Ljava/lang/String;Ljava/lang/Throwable;)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 class net/mamoe/mirai/utils/MiraiLogger$Companion {
public final synthetic fun create (Ljava/lang/String;)Lnet/mamoe/mirai/utils/MiraiLogger; 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 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 { public abstract interface class net/mamoe/mirai/utils/MiraiLogger$Factory {

View File

@ -6162,7 +6162,7 @@ public abstract interface class net/mamoe/mirai/utils/MiraiLogger {
public fun isVerboseEnabled ()Z public fun isVerboseEnabled ()Z
public fun isWarningEnabled ()Z public fun isWarningEnabled ()Z
public synthetic fun plus (Lnet/mamoe/mirai/utils/MiraiLogger;)Lnet/mamoe/mirai/utils/MiraiLogger; 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 synthetic fun setFollower (Lnet/mamoe/mirai/utils/MiraiLogger;)V
public abstract fun verbose (Ljava/lang/String;)V public abstract fun verbose (Ljava/lang/String;)V
public abstract fun verbose (Ljava/lang/String;Ljava/lang/Throwable;)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 class net/mamoe/mirai/utils/MiraiLogger$Companion {
public final synthetic fun create (Ljava/lang/String;)Lnet/mamoe/mirai/utils/MiraiLogger; 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 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 { public abstract interface class net/mamoe/mirai/utils/MiraiLogger$Factory {

View File

@ -32,9 +32,16 @@ public object LoggerAdapters {
*/ */
@JvmStatic @JvmStatic
public fun useLog4j2() { public fun useLog4j2() {
DefaultFactoryOverrides.override { requester, identity -> MiraiLoggerFactoryImplementationBridge.wrapCurrent {
val logger = LogManager.getLogger(requester) object : MiraiLogger.Factory {
Log4jLoggerAdapter(logger, MarkerManager.getMarker(identity ?: logger.name).addParents(MARKER_MIRAI)) override fun create(requester: Class<*>, identity: String?): MiraiLogger {
val logger = LogManager.getLogger(requester)
return Log4jLoggerAdapter(
logger,
MarkerManager.getMarker(identity ?: logger.name).addParents(MARKER_MIRAI)
)
}
}
} }
} }

View File

@ -12,8 +12,13 @@
package net.mamoe.mirai.utils 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 net.mamoe.mirai.utils.*
import java.util.* import java.util.*
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
import kotlin.reflect.KClass import kotlin.reflect.KClass
/** /**
@ -27,7 +32,7 @@ import kotlin.reflect.KClass
* *
* ## 使用第三方日志库接管 Mirai 日志系统 * ## 使用第三方日志库接管 Mirai 日志系统
* *
* 使用 [LoggerAdapters], 将第三方日志 `Logger` 转为 [MiraiLogger]. 然后通过 [MiraiLogger.setDefaultLoggerCreator] 全局覆盖日志. * 使用 [LoggerAdapters], 将第三方日志 `Logger` 转为 [MiraiLogger]. 然后通过 [MiraiLogger.Factory] 提供实现.
* *
* ## 实现或使用 [MiraiLogger] * ## 实现或使用 [MiraiLogger]
* *
@ -79,7 +84,8 @@ public actual interface MiraiLogger {
*/ */
public fun create(requester: Class<*>): MiraiLogger = create(requester, null) 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 { public actual companion object {
@ -95,21 +101,21 @@ public actual interface MiraiLogger {
/** /**
* 已弃用, 请实现 service [net.mamoe.mirai.utils.MiraiLogger.Factory] 并以 [ServiceLoader] 支持的方式提供. * 已弃用, 请实现 service [net.mamoe.mirai.utils.MiraiLogger.Factory] 并以 [ServiceLoader] 支持的方式提供.
*/ */
@Suppress("DeprecatedCallableAddReplaceWith")
@Deprecated( @Deprecated(
"Please set factory by providing an service of type net.mamoe.mirai.utils.MiraiLogger.Factory", "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 ) // deprecated since 2.7
@JvmStatic @JvmStatic
@DeprecatedSinceMirai(warningSince = "2.7", errorSince = "2.10") // left ERROR intentionally, for internal uses. @DeprecatedSinceMirai(warningSince = "2.7", errorSince = "2.10", hiddenSince = "2.13")
public fun setDefaultLoggerCreator(creator: (identity: String?) -> MiraiLogger) { public fun setDefaultLoggerCreator(@Suppress("UNUSED_PARAMETER") creator: (identity: String?) -> MiraiLogger) {
DefaultFactoryOverrides.override { _, identity -> creator(identity) } // nop
// DefaultFactoryOverrides.override { _, identity -> creator(identity) }
} }
/** /**
* 旧版本用于创建 [MiraiLogger]. 已弃用. 请使用 [MiraiLogger.Factory.INSTANCE.create]. * 旧版本用于创建 [MiraiLogger]. 已弃用. 请使用 [MiraiLogger.Factory.INSTANCE.create].
*
* @see setDefaultLoggerCreator
*/ */
@Deprecated( @Deprecated(
"Please use MiraiLogger.Factory.create", ReplaceWith( "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 = public actual fun call(priority: SimpleLogger.LogPriority, message: String?, e: Throwable?): Unit =
priority.correspondingFunction(this, message, e) priority.correspondingFunction(this, message, e)
@Suppress("DeprecatedCallableAddReplaceWith")
@Deprecated("plus 设计不佳, 请避免使用.", level = DeprecationLevel.HIDDEN) // deprecated since 2.7 @Deprecated("plus 设计不佳, 请避免使用.", level = DeprecationLevel.HIDDEN) // deprecated since 2.7
@DeprecatedSinceMirai(warningSince = "2.7", errorSince = "2.10", hiddenSince = "2.11") @DeprecatedSinceMirai(warningSince = "2.7", errorSince = "2.10", hiddenSince = "2.11")
public operator fun <T : MiraiLogger> plus(follower: T): T = follower public operator fun <T : MiraiLogger> 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 { fun createPlatformInstance() = loadService(MiraiLogger.Factory::class) { DefaultFactory() }
var override: ((requester: Class<*>, identity: String?) -> MiraiLogger)? =
null // 支持 LoggerAdapters 以及兼容旧版本
@JvmStatic private val frozen = atomic(false)
fun override(lambda: (requester: Class<*>, identity: String?) -> MiraiLogger) {
override = lambda fun freeze(): Boolean {
return frozen.compareAndSet(expect = false, update = true)
} }
@JvmStatic @TestOnly
fun clearOverride() { fun reinit() {
override = null 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 { override fun create(requester: Class<*>, identity: String?): MiraiLogger {
val override = DefaultFactoryOverrides.override return instance.create(requester, identity)
return if (override != null) override(requester, identity) else PlatformLogger( }
identity ?: requester.kotlin.simpleName ?: requester.simpleName
) 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)
} }
} }

View File

@ -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()) }
}
}

View File

@ -11,26 +11,25 @@ package net.mamoe.mirai.logging
import net.mamoe.mirai.Bot import net.mamoe.mirai.Bot
import net.mamoe.mirai.internal.utils.* import net.mamoe.mirai.internal.utils.*
import net.mamoe.mirai.utils.DefaultFactoryOverrides
import net.mamoe.mirai.utils.LoggerAdapters.asMiraiLogger import net.mamoe.mirai.utils.LoggerAdapters.asMiraiLogger
import net.mamoe.mirai.utils.MiraiLogger import net.mamoe.mirai.utils.MiraiLogger
import net.mamoe.mirai.utils.MiraiLoggerFactoryImplementationBridge
import org.apache.logging.log4j.LogManager import org.apache.logging.log4j.LogManager
import org.junit.jupiter.api.AfterEach
import kotlin.test.* import kotlin.test.*
internal class Log4j2LoggingTest { internal class Log4j2LoggingTest : AbstractLoggingTest() {
@BeforeTest @BeforeTest
fun init() { fun init() {
DefaultFactoryOverrides.override { requester, identity -> MiraiLoggerFactoryImplementationBridge.wrapCurrent {
LogManager.getLogger(requester).asMiraiLogger(Marker(identity ?: requester.simpleName, MARKER_MIRAI)) 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 private fun MiraiLogger.cast(): Log4jLoggerAdapter = this as Log4jLoggerAdapter
@Test @Test

View File

@ -10,20 +10,26 @@
package net.mamoe.mirai.logging package net.mamoe.mirai.logging
import net.mamoe.mirai.utils.MiraiLogger import net.mamoe.mirai.utils.MiraiLogger
import net.mamoe.mirai.utils.MiraiLoggerFactoryImplementationBridge
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertIs import kotlin.test.assertIs
internal class LoggingCompatibilityTest { internal class LoggingCompatibilityTest : AbstractLoggingTest() {
@Suppress("DEPRECATION_ERROR") @Suppress("DEPRECATION_ERROR")
@Test @Test
fun `legacy overrides are still working if no services are found`() { fun `legacy overrides are still working if no services are found`() {
val messages = StringBuilder() val messages = StringBuilder()
MiraiLogger.setDefaultLoggerCreator { MiraiLoggerFactoryImplementationBridge.wrapCurrent {
net.mamoe.mirai.utils.SimpleLogger("my logger") { message: String?, _: Throwable? -> object : MiraiLogger.Factory {
messages.append(message) override fun create(requester: Class<*>, identity: String?): MiraiLogger {
return net.mamoe.mirai.utils.SimpleLogger("my logger") { message: String?, _: Throwable? ->
messages.append(message)
}
}
} }
} }

View File

@ -13,7 +13,7 @@ import net.mamoe.mirai.utils.MiraiLogger
import org.bouncycastle.jce.provider.BouncyCastleProvider import org.bouncycastle.jce.provider.BouncyCastleProvider
import java.security.Security import java.security.Security
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertTrue import kotlin.test.assertIs
internal actual fun initPlatform() { internal actual fun initPlatform() {
init init
@ -32,9 +32,7 @@ internal actual class PlatformInitializationTest : AbstractTest() {
@Test @Test
actual fun test() { actual fun test() {
assertTrue { @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") assertIs<net.mamoe.mirai.internal.utils.StdoutLogger>(MiraiLogger.Factory.create(this::class, "1"))
MiraiLogger.Factory.create(this::class, "1") is net.mamoe.mirai.internal.utils.StdoutLogger
}
} }
} }

View File

@ -51,9 +51,17 @@ internal actual abstract class AbstractTest actual constructor() : CommonAbstrac
DebugProbes.install() DebugProbes.install()
@Suppress("DEPRECATION_ERROR") @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
MiraiLogger.setDefaultLoggerCreator { net.mamoe.mirai.utils.MiraiLoggerFactoryImplementationBridge.wrapCurrent {
SynchronizedStdoutLogger(it) 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") setSystemProp("mirai.network.packet.logger", "true")

View File

@ -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 许可证的约束, 可以在以下链接找到该许可证. * 此源代码的使用受 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. * 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 package net.mamoe.mirai.internal.netinternalkit
import kotlinx.atomicfu.locks.withLock import kotlinx.atomicfu.locks.withLock
import net.mamoe.mirai.utils.DefaultFactoryOverrides import net.mamoe.mirai.utils.*
import net.mamoe.mirai.utils.PlatformLogger
import net.mamoe.mirai.utils.SizedCache
import net.mamoe.mirai.utils.currentTimeMillis
import java.io.File import java.io.File
internal object LogCapture { internal object LogCapture {
@ -28,12 +25,17 @@ internal object LogCapture {
fun setupCapture(maxLine: Int = 200) { fun setupCapture(maxLine: Int = 200) {
logCache = SizedCache(maxLine) logCache = SizedCache(maxLine)
@Suppress("INVISIBLE_MEMBER") @Suppress("INVISIBLE_MEMBER")
DefaultFactoryOverrides.override { requester, identity -> MiraiLoggerFactoryImplementationBridge.wrapCurrent {
PlatformLogger( object : MiraiLogger.Factory {
identity ?: requester.kotlin.simpleName ?: requester.simpleName, override fun create(requester: Class<*>, identity: String?): MiraiLogger {
output return PlatformLogger(
) identity ?: requester.kotlin.simpleName ?: requester.simpleName,
output
)
}
}
} }
NetReplayHelperSettings.logger_console = PlatformLogger( NetReplayHelperSettings.logger_console = PlatformLogger(