Merge branch 'LOG4J2-3086' into master
diff --git a/log4j-api-kotlin-benchmark/src/main/kotlin/org/apache/logging/log4j/kotlin/benchmark/LoggingBenchmark.kt b/log4j-api-kotlin-benchmark/src/main/kotlin/org/apache/logging/log4j/kotlin/benchmark/LoggingBenchmark.kt
index 776c4db..b892fb2 100644
--- a/log4j-api-kotlin-benchmark/src/main/kotlin/org/apache/logging/log4j/kotlin/benchmark/LoggingBenchmark.kt
+++ b/log4j-api-kotlin-benchmark/src/main/kotlin/org/apache/logging/log4j/kotlin/benchmark/LoggingBenchmark.kt
@@ -18,9 +18,9 @@
import org.apache.logging.log4j.LogManager
import org.apache.logging.log4j.Logger
+import org.apache.logging.log4j.kotlin.Logging
import org.apache.logging.log4j.kotlin.contextName
import org.apache.logging.log4j.kotlin.logger
-//import org.apache.logging.log4j.kotlin.logger1
import org.apache.logging.log4j.util.Supplier
import org.openjdk.jmh.annotations.*
import java.util.concurrent.TimeUnit
@@ -34,7 +34,7 @@
@Warmup(iterations = 3, time = 5)
@Measurement(iterations = 5, time = 1)
open class LoggingBenchmark {
- companion object {
+ companion object: Logging {
@JvmStatic
val LOGGER3 = logger()
val LOGGER4: Logger = LogManager.getLogger()
@@ -79,4 +79,14 @@
fun companionObjectLog4jLoggerDirect() {
LOGGER4.info("Test")
}
+
+ @Benchmark
+ fun companionObjectLookupInterfaceFunctional() {
+ logger.info {"Test" }
+ }
+
+ @Benchmark
+ fun companionObjectLookupInterfaceDirect() {
+ logger.info("Test")
+ }
}
diff --git a/log4j-api-kotlin/src/main/kotlin/org/apache/logging/log4j/kotlin/Logging.kt b/log4j-api-kotlin/src/main/kotlin/org/apache/logging/log4j/kotlin/Logging.kt
index 8bd11cd..ff7d76f 100644
--- a/log4j-api-kotlin/src/main/kotlin/org/apache/logging/log4j/kotlin/Logging.kt
+++ b/log4j-api-kotlin/src/main/kotlin/org/apache/logging/log4j/kotlin/Logging.kt
@@ -41,9 +41,15 @@
* }
*
* ```
+ *
+ * Note that this is significantly slower than creating a logger explicitly, as it requires a lookup of the
+ * logger on each call via the property getter, since we cannot store any state in an interface. We attempt to
+ * minimize the overhead of this by caching the loggers, but according to microbenchmarks, it is still about
+ * 3.5 times slower than creating a logger once and using it (about 4.2 nanoseconds per call instead of 1.2
+ * nanoseconds).
*/
interface Logging {
@Suppress("unused")
val logger
- get() = loggerOf(this.javaClass)
+ get() = cachedLoggerOf(this.javaClass)
}
diff --git a/log4j-api-kotlin/src/main/kotlin/org/apache/logging/log4j/kotlin/LoggingFactory.kt b/log4j-api-kotlin/src/main/kotlin/org/apache/logging/log4j/kotlin/LoggingFactory.kt
index 7488bf6..175939c 100644
--- a/log4j-api-kotlin/src/main/kotlin/org/apache/logging/log4j/kotlin/LoggingFactory.kt
+++ b/log4j-api-kotlin/src/main/kotlin/org/apache/logging/log4j/kotlin/LoggingFactory.kt
@@ -18,6 +18,7 @@
import org.apache.logging.log4j.LogManager
import org.apache.logging.log4j.spi.ExtendedLogger
+import java.util.*
import kotlin.reflect.full.companionObject
/**
@@ -67,6 +68,10 @@
return KotlinLogger(loggerDelegateOf(ofClass))
}
+fun cachedLoggerOf(ofClass: Class<*>): KotlinLogger {
+ return loggerCache.getOrPut(ofClass) { loggerOf(ofClass) }
+}
+
// unwrap companion class to enclosing class given a Java Class
private fun <T : Any> unwrapCompanionClass(ofClass: Class<T>): Class<*> {
return if (ofClass.enclosingClass?.kotlin?.companionObject?.java == ofClass) {
@@ -75,3 +80,14 @@
ofClass
}
}
+
+private val loggerCache = Collections.synchronizedMap(SimpleLoggerLruCache(100))
+
+/**
+ * A very simple cache for loggers, to be used with [cachedLoggerOf].
+ */
+private class SimpleLoggerLruCache(private val maxEntries: Int): LinkedHashMap<Class<*>, KotlinLogger>(maxEntries, 1f) {
+ override fun removeEldestEntry(eldest: MutableMap.MutableEntry<Class<*>, KotlinLogger>): Boolean {
+ return size > maxEntries
+ }
+}