在Kotlin登录的惯用方法

时间:2015-12-22 13:20:33

标签: kotlin kotlin-logging

Kotlin与Java中使用的静态字段概念不同。在Java中,普遍接受的日志记录方式是:

public class Foo {
    private static final Logger LOG = LoggerFactory.getLogger(Foo.class);
}

问题是什么是在Kotlin中执行日志记录的惯用方式?

18 个答案:

答案 0 :(得分:214)

在大多数成熟的Kotlin代码中,您将在下面找到其中一种模式。使用 Property Delegates 的方法利用Kotlin的强大功能来生成最小的代码。

注意:此处的代码适用于java.util.Logging,但同样的理论适用于任何日志记录库

类似静态(常见,相当于问题中的Java代码)

如果您无法信任日志记录系统中的哈希查找的性能,则可以通过使用可以保存实例并且感觉像是静态的伴随对象来获得与Java代码类似的行为。

class MyClass {
    companion object {
        val LOG = Logger.getLogger(MyClass::class.java.name) 
    }

    fun foo() {
        LOG.warning("Hello from MyClass")
    }
}  

创建输出:

  

2015年12月26日上午11:28:32 org.stackoverflow.kotlin.test.MyClass foo   信息:来自MyClass的Hello

此处有关配套对象的更多信息:Companion Objects ...另请注意,在上面的示例MyClass::class.java中,获取记录器类型Class<MyClass>的实例,而this.javaClass则会获取类型Class<MyClass.Companion>的实例。

每个班级的实例(常见)

但是,实际上没有理由避免在实例级别调用和获取记录器。您提到的惯用Java方式已经过时,并且基于对性能的恐惧,而每个类的记录器已经被几乎所有合理的日志系统缓存。只需创建一个成员来保存记录器对象。

class MyClass {
  val LOG = Logger.getLogger(this.javaClass.name)

  fun foo() {
        LOG.warning("Hello from MyClass")
  }
} 

创建输出:

  

2015年12月26日上午11:28:44 org.stackoverflow.kotlin.test.MyClass foo   信息:来自MyClass的Hello

您可以针对每个实例和每个类的变体进行性能测试,并查看大多数应用是否存在实际差异。

属性代表(常见,最优雅)

@Jire在另一个答案中提出的另一种方法是创建一个属性委托,然后您可以使用该委托在您想要的任何其他类中统一地执行逻辑。有一种更简单的方法可以做到这一点,因为Kotlin已经提供了Lazy委托,我们可以将它包装在一个函数中。这里的一个技巧是,如果我们想知道当前使用委托的类的类型,我们将它作为任何类的扩展函数:

fun <R : Any> R.logger(): Lazy<Logger> {
    return lazy { Logger.getLogger(unwrapCompanionClass(this.javaClass).name) }
}
// see code for unwrapCompanionClass() below in "Putting it all Together section"

此代码还确保如果您在Companion对象中使用它,则记录器名称将与您在类本身上使用它时相同。现在你可以简单地说:

class Something {
    val LOG by logger()

    fun foo() {
        LOG.info("Hello from Something")
    }
}

对于每个类实例,或者如果您希望它更加静态,每个类有一个实例:

class SomethingElse {
    companion object {
        val LOG by logger()

    }

    fun foo() {
        LOG.info("Hello from SomethingElse")
    }
}

在这两个类上调用foo()的输出将是:

  

2015年12月26日11:30:55 org.stackoverflow.kotlin.test.Something foo   信息:来自Something的问候

     

2015年12月26日11:30:55 org.stackoverflow.kotlin.test.SomethingElse foo   信息:来自SomethingElse的您好

扩展函数(在这种情况下不常见,因为&#34;污染&#34;任何命名空间)

Kotlin有一些隐藏的技巧,让你可以让这些代码更小一些。您可以在类上创建扩展函数,从而为它们提供其他功能。上述评论中的一个建议是使用记录器功能扩展Any。每当有人在任何类的IDE中使用代码完成时,这都会产生噪音。但扩展Any或其他标记接口有一个秘密的好处:你可以暗示你正在扩展自己的类,从而检测你所在的类。咦?为了减少混淆,这里是代码:

// extend any class with the ability to get a logger
fun <T: Any> T.logger(): Logger {
     return Logger.getLogger(unwrapCompanionClass(this.javaClass).name)
}

现在在一个类(或伴随对象)中,我可以在我自己的类上调用此扩展名:

class SomethingDifferent {
    val LOG = logger()

    fun foo() {
        LOG.info("Hello from SomethingDifferent")
    }
}

制作输出:

  

2015年12月26日上午11:29:12 org.stackoverflow.kotlin.test.SomethingDifferent foo   信息:来自SomethingDifferent的你好

基本上,代码被视为对扩展程序Something.logger()的调用。问题是,以下情况也可能是真实的,造成污染&#34;在其他课程上:

val LOG1 = "".logger()
val LOG2 = Date().logger()
val LOG3 = 123.logger()

标记界面上的扩展功能(不确定常见的,但#34; traits&#34的常见模型;)

为了使扩展名更清洁并减少污染,您可以使用标记界面进行扩展:

interface Loggable {} 

fun Loggable.logger(): Logger {
     return Logger.getLogger(unwrapCompanionClass(this.javaClass).name)
}    

甚至可以使用默认实现将该方法作为接口的一部分:

interface Loggable {
    public fun logger(): Logger {
        return Logger.getLogger(unwrapCompanionClass(this.javaClass).name)
    }
}

在课堂上使用以下任何一种变体:

class MarkedClass: Loggable {
    val LOG = logger()
}

制作输出:

  

2015年12月26日上午11:41:01 org.stackoverflow.kotlin.test.MarkedClass foo   信息:来自MarkedClass的Hello

如果您想强制创建统一字段来保存记录器,那么在使用此界面时,您可以轻松地要求实施者拥有LOG等字段:

interface Loggable {
    val LOG: Logger  // abstract required field

    public fun logger(): Logger {
        return Logger.getLogger(unwrapCompanionClass(this.javaClass).name)
    }
}

现在接口的实现者必须如下所示:

class MarkedClass: Loggable {
    override val LOG: Logger = logger()
}

当然,抽象基类也可以这样做,可以选择接口和实现该接口的抽象类,从而实现灵活性和统一性:

abstract class WithLogging: Loggable {
    override val LOG: Logger = logger()
}

// using the logging from the base class
class MyClass1: WithLogging() {
    // ... already has logging!
}

// providing own logging compatible with marker interface
class MyClass2: ImportantBaseClass(), Loggable {
    // ... has logging that we can understand, but doesn't change my hierarchy
    override val LOG: Logger = logger()
}

// providing logging from the base class via a companion object so our class hierarchy is not affected
class MyClass3: ImportantBaseClass() {
    companion object : WithLogging() {
       // we have the LOG property now!
    }
}

全部放在一起(一个小帮手库)

这是一个小帮手库,可以使上面的任何选项都易于使用。在Kotlin中,扩展API以使它们更符合您的喜好是很常见的。在扩展或顶级功能中。下面是一个混合,为您提供如何创建记录器的选项,以及显示所有变体的示例:

// Return logger for Java class, if companion object fix the name
fun <T: Any> logger(forClass: Class<T>): Logger {
    return Logger.getLogger(unwrapCompanionClass(forClass).name)
}

// unwrap companion class to enclosing class given a Java Class
fun <T : Any> unwrapCompanionClass(ofClass: Class<T>): Class<*> { 
   return ofClass.enclosingClass?.takeIf { 
      ofClass.enclosingClass.kotlin.companionObject?.java == ofClass 
   } ?: ofClass 
}

// unwrap companion class to enclosing class given a Kotlin Class
fun <T: Any> unwrapCompanionClass(ofClass: KClass<T>): KClass<*> {
   return unwrapCompanionClass(ofClass.java).kotlin
}

// Return logger for Kotlin class
fun <T: Any> logger(forClass: KClass<T>): Logger {
    return logger(forClass.java)
}

// return logger from extended class (or the enclosing class)
fun <T: Any> T.logger(): Logger {
    return logger(this.javaClass)
}

// return a lazy logger property delegate for enclosing class
fun <R : Any> R.lazyLogger(): Lazy<Logger> {
    return lazy { logger(this.javaClass) }
}

// return a logger property delegate for enclosing class
fun <R : Any> R.injectLogger(): Lazy<Logger> {
    return lazyOf(logger(this.javaClass))
}

// marker interface and related extension (remove extension for Any.logger() in favour of this)
interface Loggable {}
fun Loggable.logger(): Logger = logger(this.javaClass)

// abstract base class to provide logging, intended for companion objects more than classes but works for either
abstract class WithLogging: Loggable {
    val LOG = logger()
}

选择您要保留的那些,以下是所有正在使用的选项:

class MixedBagOfTricks {
    companion object {
        val LOG1 by lazyLogger()          // lazy delegate, 1 instance per class
        val LOG2 by injectLogger()        // immediate, 1 instance per class
        val LOG3 = logger()               // immediate, 1 instance per class
        val LOG4 = logger(this.javaClass) // immediate, 1 instance per class
    }

    val LOG5 by lazyLogger()              // lazy delegate, 1 per instance of class
    val LOG6 by injectLogger()            // immediate, 1 per instance of class
    val LOG7 = logger()                   // immediate, 1 per instance of class
    val LOG8 = logger(this.javaClass)     // immediate, 1 instance per class
}

val LOG9 = logger(MixedBagOfTricks::class)  // top level variable in package

// or alternative for marker interface in class
class MixedBagOfTricks : Loggable {
    val LOG10 = logger()
}

// or alternative for marker interface in companion object of class
class MixedBagOfTricks {
    companion object : Loggable {
        val LOG11 = logger()
    }
}

// or alternative for abstract base class for companion object of class
class MixedBagOfTricks {
    companion object: WithLogging() {} // instance 12

    fun foo() {
       LOG.info("Hello from MixedBagOfTricks")
    }
}

// or alternative for abstract base class for our actual class
class MixedBagOfTricks : WithLogging() { // instance 13
    fun foo() {
       LOG.info("Hello from MixedBagOfTricks")
    }
}

此示例中创建的所有13个记录器实例将生成相同的记录器名称,并输出:

  

2015年12月26日上午11:39:00 org.stackoverflow.kotlin.test.MixedBagOfTricks foo   信息:来自MixedBagOfTricks的你好

注意: unwrapCompanionClass()方法确保我们不生成以伴随对象命名的记录器,而是生成封闭类。这是当前推荐的查找包含伴随对象的类的方法。剥离&#34; $ Companion &#34;使用removeSuffix()的名称不起作用,因为可以为伴随对象指定自定义名称。

答案 1 :(得分:25)

查看kotlin-logging库。
它允许这样记录:

private val logger = KotlinLogging.logger {}

class Foo {
  logger.info{"wohoooo $wohoooo"}
}

或者那样:

class FooWithLogging {
  companion object: KLogging()
  fun bar() {
    logger.info{"wohoooo $wohoooo"}
  }
}

我还撰写了一篇博文,将其与AnkoLogger进行比较:Logging in Kotlin & Android: AnkoLogger vs kotlin-logging

免责声明:我是该图书馆的维护者。

编辑:kotlin-logging现在具有多平台支持:https://github.com/MicroUtils/kotlin-logging/wiki/Multiplatform-support

答案 2 :(得分:7)

作为日志记录实现的一个很好的例子,我想提一下Anko,它使用一个需要日志记录的类应该实现的特殊接口chai.expect(_.intersection([1, 2], [2, 3])).not.to.be.empty 。在界面内部有代码,可以为类生成日志记录标记。然后通过扩展函数完成日志记录,扩展函数可以在interace实现中调用,无需前缀甚至创建记录器实例。

我认为这不是惯用,但它似乎是一个很好的方法,因为它需要最少的代码,只需将接口添加到类声明中,并且您可以使用不同类的不同标记进行日志记录

<小时/> 下面的代码基本上是AnkoLogger,简化并重写为Android无关的用法。

首先,有一个界面,其行为类似于标记界面:

AnkoLogger

它允许其实现在代码中使用interface MyLogger { val tag: String get() = javaClass.simpleName } 的扩展函数,只需在MyLogger上调用它们。它还包含日志标记。

接下来,有一个不同日志记录方法的一般入口点:

this

它将通过记录方法调用。它从private inline fun log(logger: MyLogger, message: Any?, throwable: Throwable?, level: Int, handler: (String, String) -> Unit, throwableHandler: (String, String, Throwable) -> Unit ) { val tag = logger.tag if (isLoggingEnabled(tag, level)) { val messageString = message?.toString() ?: "null" if (throwable != null) throwableHandler(tag, messageString, throwable) else handler(tag, messageString) } } 实现获取标记,检查日志记录设置,然后调用两个处理程序之一,一个MyLogger参数和一个没有。

然后您可以通过以下方式定义任意数量的日志记录方法:

Throwable

这些定义只记录一条消息并记录fun MyLogger.info(message: Any?, throwable: Throwable? = null) = log(this, message, throwable, LoggingLevels.INFO, { tag, message -> println("INFO: $tag # $message") }, { tag, message, thr -> println("INFO: $tag # $message # $throwable"); thr.printStackTrace() }) ,这是通过可选的Throwable参数完成的。

作为throwablehandler传递的函数对于不同的日志记录方法可能不同,例如,它们可以将日志写入文件或将其上载到某处。为简洁起见,省略了throwableHandlerisLoggingEnabled,但使用它们可以提供更大的灵活性。

<小时/> 它允许以下用法:

LoggingLevels

有一个小缺点:登录包级函数需要一个记录器对象:

class MyClass : MyLogger {
    fun myFun() {
        info("Info message")
    }
}

答案 3 :(得分:6)

KISS:对于迁移到Kotlin的Java团队

如果你不介意在记录器的每个实例化中提供类名(就像java一样),你可以通过在项目的某个地方将其定义为顶级函数来保持简单:

import org.slf4j.LoggerFactory

inline fun <reified T:Any> logger() = LoggerFactory.getLogger(T::class.java)

这使用了Kotlin reified type parameter

现在,您可以按如下方式使用它:

class SomeClass {
  // or within a companion object for one-instance-per-class
  val log = logger<SomeClass>()
  ...
}

这种方法非常简单,接近于Java等价物,但只是添加了一些语法糖。

下一步:扩展程序或代理

我个人更喜欢更进一步,使用扩展或委托方法。这在@ JaysonMinard的回答中得到了很好的总结,但这里是TL;&#34;代表&#34;使用log4j2 API的方法( UPDATE :不再需要手动编写此代码,因为它已作为log4j2项目的官方模块发布,请参见下文)。由于log4j2与slf4j不同,支持使用Supplier进行日志记录,因此我还添加了一个委托来简化这些方法。

import org.apache.logging.log4j.LogManager
import org.apache.logging.log4j.Logger
import org.apache.logging.log4j.util.Supplier
import kotlin.reflect.companionObject

/**
 * An adapter to allow cleaner syntax when calling a logger with a Kotlin lambda. Otherwise calling the
 * method with a lambda logs the lambda itself, and not its evaluation. We specify the Lambda SAM type as a log4j2 `Supplier`
 * to avoid this. Since we are using the log4j2 api here, this does not evaluate the lambda if the level
 * is not enabled.
 */
class FunctionalLogger(val log: Logger): Logger by log {
  inline fun debug(crossinline supplier: () -> String) {
    log.debug(Supplier { supplier.invoke() })
  }

  inline fun debug(t: Throwable, crossinline supplier: () -> String) {
    log.debug(Supplier { supplier.invoke() }, t)
  }

  inline fun info(crossinline supplier: () -> String) {
    log.info(Supplier { supplier.invoke() })
  }

  inline fun info(t: Throwable, crossinline supplier: () -> String) {
    log.info(Supplier { supplier.invoke() }, t)
  }

  inline fun warn(crossinline supplier: () -> String) {
    log.warn(Supplier { supplier.invoke() })
  }

  inline fun warn(t: Throwable, crossinline supplier: () -> String) {
    log.warn(Supplier { supplier.invoke() }, t)
  }

  inline fun error(crossinline supplier: () -> String) {
    log.error(Supplier { supplier.invoke() })
  }

  inline fun error(t: Throwable, crossinline supplier: () -> String) {
    log.error(Supplier { supplier.invoke() }, t)
  }
}

/**
 * A delegate-based lazy logger instantiation. Use: `val log by logger()`.
 */
@Suppress("unused")
inline fun <reified T : Any> T.logger(): Lazy<FunctionalLogger> =
  lazy { FunctionalLogger(LogManager.getLogger(unwrapCompanionClass(T::class.java))) }

// unwrap companion class to enclosing class given a Java Class
fun <T : Any> unwrapCompanionClass(ofClass: Class<T>): Class<*> {
  return if (ofClass.enclosingClass != null && ofClass.enclosingClass.kotlin.companionObject?.java == ofClass) {
    ofClass.enclosingClass
  } else {
    ofClass
  }
}

Log4j2 Kotlin Logging API

前一部分的大部分内容已直接适用于生成Kotlin Logging API模块,该模块现在是Log4j2的官方部分(免责声明:我是主要作者)。您可以下载此directly from Apache,或通过Maven Central

Usage基本上如上所述,但该模块支持基于接口的记录器访问,logger上的Any扩展功能,用于定义this的情况,以及在没有定义this的情况下使用的命名记录器函数(例如顶级函数)。

答案 4 :(得分:4)

这样的事情对你有用吗?

class LoggerDelegate {

    private var logger: Logger? = null

    operator fun getValue(thisRef: Any?, property: KProperty<*>): Logger {
        if (logger == null) logger = Logger.getLogger(thisRef!!.javaClass.name)
        return logger!!
    }

}

fun logger() = LoggerDelegate()

class Foo { // (by the way, everything in Kotlin is public by default)
    companion object { val logger by logger() }
}

答案 5 :(得分:4)

安口

您可以使用Anko库来执行此操作。您将拥有如下代码:

class MyActivity : Activity(), AnkoLogger {
    private fun someMethod() {
        info("This is my first app and it's awesome")
        debug(1234) 
        warn("Warning")
    }
}

科特林-登录

kotlin-logging(Github project - kotlin-logging)库允许您编写如下的日志记录代码:

class FooWithLogging {
  companion object: KLogging()
  fun bar() {
    logger.info{"Item $item"}
  }
}

StaticLog

或者您也可以使用这个名为StaticLog的Kotlin库中的小写,然后您的代码看起来像:

Log.info("This is an info message")
Log.debug("This is a debug message")
Log.warn("This is a warning message","WithACustomTag")
Log.error("This is an error message with an additional Exception for output", "AndACustomTag", exception )

Log.logLevel = LogLevel.WARN
Log.info("This message will not be shown")\

如果您想为日志记录方法定义输出格式,第二种解决方案可能会更好:

Log.newFormat {
    line(date("yyyy-MM-dd HH:mm:ss"), space, level, text("/"), tag, space(2), message, space(2), occurrence)
}

或使用过滤器,例如:

Log.filterTag = "filterTag"
Log.info("This log will be filtered out", "otherTag")
Log.info("This log has the right tag", "filterTag")

timberkt

如果您已经使用过Jake Wharton的Timber日志记录库,请检查timberkt

  

这个库以Timber为基础,使用了一个更容易在Kotlin中使用的API。您可以传递一个仅在记录消息时进行评估的lambda,而不是使用格式化参数。

代码示例:

// Standard timber
Timber.d("%d %s", intVar + 3, stringFun())

// Kotlin extensions
Timber.d { "${intVar + 3} ${stringFun()}" }
// or
d { "${intVar + 3} ${stringFun()}" }

同时检查:Logging in Kotlin & Android: AnkoLogger vs kotlin-logging

希望它会有所帮助

答案 6 :(得分:1)

Class上的扩展函数怎么样?这样你最终会得到:

public fun KClass.logger(): Logger = LoggerFactory.getLogger(this.java)

class SomeClass {
    val LOG = SomeClass::class.logger()
}

注意 - 我根本没有对此进行测试,因此可能不太正确。

答案 7 :(得分:1)

首先,您可以为记录器创建添加扩展功能。

inline fun <reified T : Any> getLogger() = LoggerFactory.getLogger(T::class.java)
fun <T : Any> T.getLogger() = LoggerFactory.getLogger(javaClass)

然后,您将能够使用以下代码创建记录器。

private val logger1 = getLogger<SomeClass>()
private val logger2 = getLogger()

其次,您可以定义一个提供记录器及其mixin实现的接口。

interface LoggerAware {
  val logger: Logger
}

class LoggerAwareMixin(containerClass: Class<*>) : LoggerAware {
  override val logger: Logger = LoggerFactory.getLogger(containerClass)
}

inline fun <reified T : Any> loggerAware() = LoggerAwareMixin(T::class.java)

此界面可以按以下方式使用。

class SomeClass : LoggerAware by loggerAware<SomeClass>() {
  // Now you can use a logger here.
}

答案 8 :(得分:1)

我听说过这方面没有成语。 越简单越好,所以我会使用顶级属性

val logger = Logger.getLogger("package_name")

这种做法在Python中很有用,并且可能与Kotlin和Python不同,我相信它们在“精神”(说成语)方面非常相似。

答案 9 :(得分:1)

创建伴随对象并使用@JvmStatic注释标记相应的字段

答案 10 :(得分:0)

Slf4j例子,其他人也一样。这甚至适用于创建包级别记录器

/**  
  * Get logger by current class name.  
  */ 

fun getLogger(c: () -> Unit): Logger = 
        LoggerFactory.getLogger(c.javaClass.enclosingClass)

用法:

val logger = getLogger { }

答案 11 :(得分:0)

对于 Kotlin Multiplaform 日志记录,我找不到具有我需要的所有功能的库,因此我最终编写了一个。请查看KmLogging。它实现的功能是:

  • 在每个平台上使用特定于平台的日志记录:在 Android 上登录,在 iOS 上使用 os_log,在 JavaScript 上使用控制台。
  • 高性能。禁用时只有 1 个布尔检查。我喜欢放入大量日志记录,并希望在发布时关闭所有日志记录,并且不想为拥有大量日志记录支付太多开销。此外,当登录时,它需要非常高效。
  • 可扩展。需要能够添加其他记录器,例如记录到 Crashlytics 等。
  • 每个记录器可以在不同的级别进行记录。例如,您可能只希望将信息及以上信息发送到 Crashlytics 并在生产中禁用所有其他记录器。

使用:

val log = logging()
log.i { "some message" }

答案 12 :(得分:0)

可以使用内联属性。

缺点是KClass.log.info()有效

inline var <reified T> T.log: Logger
    get() = LoggerFactory.getLogger(T::class.java)
    set(value) {}

答案 13 :(得分:0)

您可以简单地构建自己的实用程序“库”。您不需要大型库来执行此任务,这会使您的项目更重,更复杂。

例如,您可以使用Kotlin Reflection获取任何类属性的名称,类型和值。

首先,确保已在build.gradle中解决了元依赖性:

function checkCollision(){

    let boxesCollision = false;

    staticCollideMesh.some( function(object){ 

      if(object.BBox.intersectsBox( ball.BBox ) ){

        boxesCollision = true;
        if( boxesCollision ){

          switch (object) {

            case wall:
              createDebris();
              break;

            default:
              break;
          }
        }
      }
    });
  }

然后,您可以简单地将此代码复制并粘贴到您的项目中:

dependencies {
    implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
}

用法示例:

import kotlin.reflect.full.declaredMemberProperties

class LogUtil {
    companion object {
        /**
         * Receives an [instance] of a class.
         * @return the name and value of any member property.
         */
        fun classToString(instance: Any): String {
            val sb = StringBuilder()

            val clazz = instance.javaClass.kotlin
            clazz.declaredMemberProperties.forEach {
                sb.append("${it.name}: (${it.returnType}) ${it.get(instance)}, ")
            }

            return marshalObj(sb)
        }

        private fun marshalObj(sb: StringBuilder): String {
            sb.insert(0, "{ ")
            sb.setLength(sb.length - 2)
            sb.append(" }")

            return sb.toString()
        }
    }
}

答案 14 :(得分:0)

这仍然是WIP(几乎已完成),所以我想分享一下: https://github.com/leandronunes85/log-format-enforcer#kotlin-soon-to-come-in-version-14

该库的主要目标是在整个项目中实施某种日志样式。通过生成Kotlin代码,我试图解决此问题中提到的一些问题。关于原始问题,我通常倾向于做的是:

private val LOG = LogFormatEnforcer.loggerFor<Foo>()
class Foo {

}

答案 15 :(得分:0)

这里已经有很多不错的答案了,但是所有这些答案都涉及到在类中添加记录器,但是如何记录顶级函数呢?

这种方法通用且简单,足以在两个类,伴随对象和顶级函数中正常工作:

package nieldw.test

import org.apache.logging.log4j.LogManager
import org.apache.logging.log4j.Logger
import org.junit.jupiter.api.Test

fun logger(lambda: () -> Unit): Lazy<Logger> = lazy { LogManager.getLogger(getClassName(lambda.javaClass)) }
private fun <T : Any> getClassName(clazz: Class<T>): String = clazz.name.replace(Regex("""\$.*$"""), "")

val topLog by logger { }

class TopLevelLoggingTest {
    val classLog by logger { }

    @Test
    fun `What is the javaClass?`() {
        topLog.info("THIS IS IT")
        classLog.info("THIS IS IT")
    }
}

答案 16 :(得分:0)

fun <R : Any> R.logger(): Lazy<Logger> = lazy { 
    LoggerFactory.getLogger((if (javaClass.kotlin.isCompanion) javaClass.enclosingClass else javaClass).name) 
}

class Foo {
    val logger by logger()
}

class Foo {
    companion object {
        val logger by logger()
    }
}

答案 17 :(得分:0)

一般来说,伴侣对象的用途是什么:替换静态内容。

相关问题