Log4j Kotlin API
依赖项
您需要在类路径中包含 org.apache.logging.log4j:log4j-api-kotlin
依赖项
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api-kotlin</artifactId>
<version>1.5.0-SNAPSHOT</version>
</dependency>
Java 模块名称和 OSGi Bundle-SymbolicName
设置为 org.apache.logging.log4j.api.kotlin
。
创建日志记录器
Logger
是用户与 Log4j Kotlin 交互的主要接口。您可以通过两种方式创建 Logger
创建类日志记录器
对于大多数应用程序,我们建议您为每个类定义创建一个单一的日志记录器实例,而不是 每个类实例!这不仅避免了为每个实例创建额外的日志记录器字段,其访问模式还透明地传达了实现:Logger
静态绑定到类定义。您可以通过以下几种方式创建类日志记录器
在伴随对象中创建日志记录器
这是创建类日志记录器的传统方法。它也是最有效的方法,因为日志记录器查找只执行一次,其结果存储在由该类所有实例共享的伴随对象中。
import org.apache.logging.log4j.kotlin.logger
class DbTableService {
companion object {
private val LOGGER = logger() (1)
}
fun truncateTable(tableName: String) {
LOGGER.warn { "truncating table `${tableName}`" }
db.truncate(tableName)
}
}
1 | 创建一个与所有类实例共享的静态类定义关联的 Logger |
从 Logging
扩展伴随对象
Logging
接口包含一个 logger
getter,您可以通过从 Logging
类扩展伴随对象来使用它
import org.apache.logging.log4j.kotlin.Logging
class DbTableService {
companion object: Logging (1)
fun truncateTable(tableName: String) {
logger.warn { "truncating table `${tableName}`" }
db.truncate(tableName)
}
}
1 | 从 Logging 扩展伴随对象实际上创建了一个单一的 Logger 实例
|
这种基于 getter 的方法会产生额外的开销(与 在伴随对象中创建日志记录器 相比),因为在运行时涉及日志记录器查找。 |
创建实例日志记录器
虽然我们建议您 创建类日志记录器,但在某些情况下(最显著的是在 Jakarta EE 环境中共享类 时),可能需要与每个实例关联的日志记录器。您可以通过以下方式实现这一点
在类中创建日志记录器
这是创建实例日志记录器的传统方法。它也是最有效的方法,因为日志记录器查找只执行一次,其结果存储在实例字段中。
import org.apache.logging.log4j.kotlin.logger
class DbTableService {
private val logger = logger() (1)
fun truncateTable(tableName: String) {
logger.warn { "truncating table `${tableName}`" }
db.truncate(tableName)
}
}
1 | 创建一个与类实例关联的 Logger |
从 Logging
扩展类
Logging
接口包含一个 logger
getter,您可以通过从 Logging
扩展类来使用它
import org.apache.logging.log4j.kotlin.Logging
class DbTableService: Logging { (1)
fun truncateTable(tableName: String) {
logger.warn { "truncating table `${tableName}`" }
db.truncate(tableName)
}
}
1 | 从 Logging 扩展类实际上创建了一个单一的 Logger 实例
|
这种基于 getter 的方法会产生额外的开销(与 在类中创建日志记录器 相比),因为在运行时涉及日志记录器查找。 |
使用 logger
扩展属性
您可以使用 logger
扩展属性在现场动态注入日志记录器
import org.apache.logging.log4j.kotlin.logger
class DbTableService {
fun truncateTable(tableName: String) {
logger.warn { "truncating table `${tableName}`" } (1)
db.truncate(tableName)
}
}
1 | logger 将查找与封装类的 Logger 实例关联的实例 |
这种基于 getter 的方法会产生额外的开销(与 在类中创建日志记录器 相比),因为在运行时涉及日志记录器查找。 |
线程上下文
ThreadContext
API 提供了两个外观对象:ContextMap
和 ContextStack
。
import org.apache.logging.log4j.kotlin.ContextMap
import org.apache.logging.log4j.kotlin.ContextStack
ContextMap["key"] = "value"
assert(ContextMap["key"] == "value")
assert("key" in ContextMap)
ContextMap += "anotherKey" to "anotherValue"
ContextMap -= "key"
ContextStack.push("message")
assert(!ContextStack.empty)
assert(ContextStack.depth == 1)
val message = ContextStack.peek()
assert(message == ContextStack.pop())
assert(ContextStack.empty)
提供了一个 CoroutineThreadContext
上下文元素,用于将日志记录上下文与协程集成。
我们提供了便利函数 loggingContext
和 additionalLoggingContext
,用于创建具有适当上下文数据的 CoroutineThreadContext
实例。这些函数的结果可以直接传递给协程构建器,以设置协程的上下文。
要设置上下文,忽略当前作用域中的任何上下文
launch(loggingContext(mapOf("myKey" to "myValue"), listOf("test"))) {
assertEquals("myValue", ContextMap["myKey"])
assertEquals("test", ContextStack.peek())
}
或者保留现有上下文并添加额外的日志记录上下文
launch(additionalLoggingContext(mapOf("myKey" to "myValue"), listOf("test"))) {
assertEquals("myValue", ContextMap["myKey"])
assertEquals("test", ContextStack.peek())
}
或者,要更改上下文而不启动新的协程,提供了 withLoggingContext
和 withAdditionalLoggingContext
函数
withAdditionalLoggingContext(mapOf("myKey" to "myValue"), listOf("test")) {
assertEquals("myValue", ContextMap["myKey"])
assertEquals("test", ContextStack.peek())
}
这些函数是 withContext(loggingContext(…))
或 withContext(additionalLoggingContext(…))
的简写。
参数替换
与 Java 不同,Kotlin 提供了对 字符串模板 的原生功能。但是,如果日志记录器级别未启用,使用字符串模板仍然会产生消息构造成本。为了避免这种情况,最好传递一个 lambda,它只有在必要时才会被评估
logger.debug { "Logging in user ${user.name} with birthday ${user.calcBirthday()}" }
日志记录器名称
大多数日志记录实现使用分层方案来匹配日志记录器名称与日志记录配置。
在此方案中,日志记录器名称层次结构由日志记录器名称中的 .
(点)字符表示,其方式与用于 Java/Kotlin 包名称的层次结构非常相似。Logging
接口添加的 Logger
属性遵循此约定:该接口确保 Logger
自动根据其使用所在的类进行命名。
调用 logger()
扩展方法时返回的值取决于扩展的接收器。当在对象内调用时,接收器是 this
,因此日志记录器将再次根据其使用所在的类进行命名。但是,也可以获得通过另一个类命名的日志记录器
import org.apache.logging.log4j.kotlin
class MyClass: BaseClass {
val logger = SomeOtherClass.logger()
// ...
}