Merge branch 'master' of github.com:rocketraman/logging-log4j-kotlin into api-rocketraman
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..3d542f9
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,12 @@
+.project
+.idea
+**/*.iml
+**/target
+target/
+.settings
+.classpath
+.cache-main
+.cache-tests
+velocity.log
+felix-cache/
+bin/
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..f343b24
--- /dev/null
+++ b/README.md
@@ -0,0 +1,23 @@
+# [Apache Log4j 2 Kotlin API](http://logging.apache.org/log4j/2.x/)
+
+## License
+
+Apache Log4j 2 is distributed under the [Apache License, version 2.0](http://www.apache.org/licenses/LICENSE-2.0.html).
+
+## Download
+
+[How to download Log4j](http://logging.apache.org/log4j/2.x/download.html),
+and [how to use it from SBT, Maven, Ivy and Gradle](http://logging.apache.org/log4j/2.x/maven-artifacts.html).
+
+## Issue Tracking
+
+Issues, bugs, and feature requests should be submitted to the
+[JIRA issue tracking system for this project](https://issues.apache.org/jira/browse/LOG4J2).
+
+Pull request on GitHub are welcome, but please open a ticket in the JIRA issue tracker first, and mention the
+JIRA issue in the Pull Request.
+
+## Status
+
+This is work in progress to integrate Kotlin into log4j2 as a module. See issue
+https://issues.apache.org/jira/browse/LOG4J2-1705 for tracking information.
diff --git a/log4j-api-kotlin-sample/pom.xml b/log4j-api-kotlin-sample/pom.xml
new file mode 100644
index 0000000..810f338
--- /dev/null
+++ b/log4j-api-kotlin-sample/pom.xml
@@ -0,0 +1,79 @@
+<?xml version="1.0"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-kotlin</artifactId>
+ <version>2.10.1-SNAPSHOT</version>
+ <relativePath>../</relativePath>
+ </parent>
+ <artifactId>log4j-api-kotlin-sample</artifactId>
+ <packaging>jar</packaging>
+ <name>Kotlin API samples</name>
+ <url>http://http://logging.apache.org/log4j/2.x/</url>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-api-kotlin</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-core</artifactId>
+ <scope>runtime</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <sourceDirectory>src/main/kotlin</sourceDirectory>
+ <plugins>
+ <plugin>
+ <artifactId>kotlin-maven-plugin</artifactId>
+ <groupId>org.jetbrains.kotlin</groupId>
+ <version>${kotlin.version}</version>
+ <executions>
+ <execution>
+ <id>compile</id>
+ <goals>
+ <goal>compile</goal>
+ </goals>
+ </execution>
+ <execution>
+ <id>test-compile</id>
+ <goals>
+ <goal>test-compile</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>exec-maven-plugin</artifactId>
+ <version>1.5.0</version>
+ <configuration>
+ <mainClass>org.apache.logging.log4j.kotlin.sample.LoggingApp</mainClass>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/log4j-api-kotlin-sample/src/main/kotlin/org/apache/logging/log4j/kotlin/sample/LoggingApp.kt b/log4j-api-kotlin-sample/src/main/kotlin/org/apache/logging/log4j/kotlin/sample/LoggingApp.kt
new file mode 100644
index 0000000..9a72427
--- /dev/null
+++ b/log4j-api-kotlin-sample/src/main/kotlin/org/apache/logging/log4j/kotlin/sample/LoggingApp.kt
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.kotlin.sample
+
+import org.apache.logging.log4j.kotlin.logger
+import java.util.*
+
+object LoggingApp {
+ val log = logger()
+
+ @JvmStatic
+ fun main(args: Array<String>) {
+ val s1 = "foo"
+ val s2 = "bar"
+
+ log.info { "Hello, world: $s1 $s2" }
+
+ log.trace("Regular trace")
+
+ log.runInTrace {
+ log.info("Inside trace extension!")
+ }
+
+ log.runInTrace(log.traceEntry({ "param1" }, { "param2" })) {
+ log.info("Inside trace extension with params suppliers!")
+ }
+
+ fun getKey(): Int = log.runInTrace {
+ Random().nextInt(10)
+ }
+
+ fun getKeyError(): Int = log.runInTrace {
+ throw Exception("Oops!")
+ }
+
+ log.info { "Key was ${getKey()}" }
+ try {
+ log.info { "Key was ${getKeyError()}" }
+ } catch(e: Exception) {
+ log.info { "Key threw ${e.message}" }
+ }
+ }
+}
diff --git a/log4j-api-kotlin-sample/src/main/kotlin/org/apache/logging/log4j/kotlin/sample/LoggingAppMixin.kt b/log4j-api-kotlin-sample/src/main/kotlin/org/apache/logging/log4j/kotlin/sample/LoggingAppMixin.kt
new file mode 100644
index 0000000..4d96e44
--- /dev/null
+++ b/log4j-api-kotlin-sample/src/main/kotlin/org/apache/logging/log4j/kotlin/sample/LoggingAppMixin.kt
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.kotlin.sample
+
+import org.apache.logging.log4j.kotlin.Logging
+import java.util.*
+
+object LoggingAppMixin: Logging {
+ @JvmStatic
+ fun main(args: Array<String>) {
+ val s1 = "foo"
+ val s2 = "bar"
+
+ logger.info { "Hello, world: $s1 $s2" }
+
+ logger.trace("Regular trace")
+
+ logger.runInTrace {
+ logger.info("Inside trace extension!")
+ }
+
+ logger.runInTrace(logger.traceEntry({ "param1" }, { "param2" })) {
+ logger.info("Inside trace extension with params suppliers!")
+ }
+
+ fun getKey(): Int = logger.runInTrace {
+ Random().nextInt(10)
+ }
+
+ fun getKeyError(): Int = logger.runInTrace {
+ throw Exception("Oops!")
+ }
+
+ logger.info { "Key was ${getKey()}" }
+ try {
+ logger.info { "Key was ${getKeyError()}" }
+ } catch(e: Exception) {
+ logger.info { "Key threw ${e.message}" }
+ }
+ }
+}
diff --git a/log4j-api-kotlin-sample/src/main/resources/log4j2.xml b/log4j-api-kotlin-sample/src/main/resources/log4j2.xml
new file mode 100644
index 0000000..04682ff
--- /dev/null
+++ b/log4j-api-kotlin-sample/src/main/resources/log4j2.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<Configuration name="KotlinApiSample" status="error">
+ <Appenders>
+ <Console name="Console">
+ <PatternLayout>
+ <Pattern>%d %5p %c{1} %X %F:%L - %m%n</Pattern>
+ </PatternLayout>
+ </Console>
+ </Appenders>
+ <Loggers>
+ <Root level="TRACE">
+ <AppenderRef ref="Console"/>
+ </Root>
+ </Loggers>
+</Configuration>
diff --git a/log4j-api-kotlin/pom.xml b/log4j-api-kotlin/pom.xml
new file mode 100644
index 0000000..c79537a
--- /dev/null
+++ b/log4j-api-kotlin/pom.xml
@@ -0,0 +1,162 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-kotlin</artifactId>
+ <version>2.10.1-SNAPSHOT</version>
+ <relativePath>../</relativePath>
+ </parent>
+ <artifactId>log4j-api-kotlin</artifactId>
+ <packaging>jar</packaging>
+ <name>Kotlin wrapper for Log4j API</name>
+ <description>Kotlin wrapper for Log4j API</description>
+
+ <properties>
+ <log4jParentDir>${basedir}/..</log4jParentDir>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.jetbrains.kotlin</groupId>
+ <artifactId>kotlin-stdlib</artifactId>
+ <version>${kotlin.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.jetbrains.kotlin</groupId>
+ <artifactId>kotlin-reflect</artifactId>
+ <version>${kotlin.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-api</artifactId>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-core</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-core</artifactId>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.nhaarman</groupId>
+ <artifactId>mockito-kotlin</artifactId>
+ <version>1.2.0</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <sourceDirectory>src/main/kotlin</sourceDirectory>
+ <testSourceDirectory>src/test/kotlin</testSourceDirectory>
+ <plugins>
+ <plugin>
+ <artifactId>kotlin-maven-plugin</artifactId>
+ <groupId>org.jetbrains.kotlin</groupId>
+ <version>${kotlin.version}</version>
+ <executions>
+ <execution>
+ <id>compile</id>
+ <goals>
+ <goal>compile</goal>
+ </goals>
+ </execution>
+ <execution>
+ <id>test-compile</id>
+ <goals>
+ <goal>test-compile</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ </plugin>
+ <!-- Include the standard NOTICE and LICENSE -->
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-remote-resources-plugin</artifactId>
+ <executions>
+ <execution>
+ <goals>
+ <goal>process</goal>
+ </goals>
+ <configuration>
+ <skip>false</skip>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ </plugin>
+ </plugins>
+ </build>
+
+ <reporting>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-changes-plugin</artifactId>
+ <version>${changes.plugin.version}</version>
+ <reportSets>
+ <reportSet>
+ <reports>
+ <report>changes-report</report>
+ </reports>
+ </reportSet>
+ </reportSets>
+ <configuration>
+ <issueLinkTemplate>%URL%/show_bug.cgi?id=%ISSUE%</issueLinkTemplate>
+ <useJql>true</useJql>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-pmd-plugin</artifactId>
+ <version>${pmd.plugin.version}</version>
+ <configuration>
+ <targetJdk>${maven.compile.target}</targetJdk>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.jetbrains.kotlin</groupId>
+ <artifactId>kotlin-maven-plugin</artifactId>
+ <version>${kotlin.version}</version>
+ </plugin>
+ </plugins>
+ </reporting>
+</project>
diff --git a/log4j-api-kotlin/src/main/kotlin/org/apache/logging/log4j/kotlin/KotlinLogger.kt b/log4j-api-kotlin/src/main/kotlin/org/apache/logging/log4j/kotlin/KotlinLogger.kt
new file mode 100644
index 0000000..a29deaa
--- /dev/null
+++ b/log4j-api-kotlin/src/main/kotlin/org/apache/logging/log4j/kotlin/KotlinLogger.kt
@@ -0,0 +1,562 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.kotlin
+
+import org.apache.logging.log4j.Level
+import org.apache.logging.log4j.Logger
+import org.apache.logging.log4j.Marker
+import org.apache.logging.log4j.message.EntryMessage
+import org.apache.logging.log4j.message.Message
+import org.apache.logging.log4j.message.SimpleMessage
+import org.apache.logging.log4j.spi.ExtendedLogger
+
+/**
+ * An adapter supporting cleaner syntax when calling a logger via Kotlin. This does not implement
+ * the Log4j2 [Logger] interface, but instead limits logging methods to those that would be natural
+ * to use from Kotlin. For example, the various logging-parameter methods necessary for Java are
+ * eschewed in favor of Kotlin lambdas and String interpolation.
+ *
+ * If you do need access to the underlying [Logger] or [ExtendedLogger], it may be accessed via the
+ * `delegate` property.
+ *
+ * One can use Kotlin's String interpolation for logging without the performance impact of
+ * evaluating the parameters if the level is not enabled e.g.:
+ *
+ * ```
+ * log.debug { "Value a = $a" }
+ * ```
+ *
+ * In addition, the overloads provide methods in which the lambda is the *last* parameter rather than
+ * the first as in the regular Log4j2 API. This means one can use Kotlin's last parameter lambda
+ * outside of parentheses syntax e.g.:
+ *
+ * ```
+ * log.error(exc) { "Unexpected exception evaluating $whatever." }
+ * ```
+ *
+ * The adapter also provides a `runInTrace` utility that avoids having to call traceEnter and traceExit
+ * and catch manually. Rather, simply call the `trace` method, passing in an [EntryMessage] and the block to
+ * execute within trace enter/exit/catch calls. Location-awareness is currently broken for trace logging with this
+ * method as the ExtendedLogger does not expose the enter/exit/catch calls with the FQCN parameter.
+ *
+ * We also use Kotlin's nullability features to specify unambiguously which parameters must be non-null
+ * when passed.
+ *
+ * Lastly, the ExtendedLogger delegate is available if the underlying Log4j Logger is needed for some reason.
+ * Access it via the `delegate` property.
+ *
+ * TODO: The ExtendedLogger delegate does not yet have support for trace entry and exit with FQCN specification.
+ * Therefore, until the Log4j2 API is updated and then this code is updated to match, location awareness will not
+ * work for these calls.
+ */
+@Suppress("UNUSED", "MemberVisibilityCanBePrivate")
+class KotlinLogger(val delegate: ExtendedLogger) {
+ companion object {
+ val FQCN: String = KotlinLogger::class.java.name
+ }
+
+ fun log(level: Level, marker: Marker, msg: Message) {
+ delegate.logIfEnabled(FQCN, level, marker, msg, null)
+ }
+
+ fun log(level: Level, marker: Marker, msg: Message, t: Throwable?) {
+ delegate.logIfEnabled(FQCN, level, marker, msg, t)
+ }
+
+ fun log(level: Level, marker: Marker, msg: CharSequence) {
+ delegate.logIfEnabled(FQCN, level, marker, msg, null)
+ }
+
+ fun log(level: Level, marker: Marker, msg: CharSequence, t: Throwable?) {
+ delegate.logIfEnabled(FQCN, level, marker, msg, t)
+ }
+
+ fun log(level: Level, marker: Marker, msg: Any) {
+ delegate.logIfEnabled(FQCN, level, marker, msg, null)
+ }
+
+ fun log(level: Level, marker: Marker, msg: Any, t: Throwable?) {
+ delegate.logIfEnabled(FQCN, level, marker, msg, t)
+ }
+
+ fun log(level: Level, msg: Message) {
+ delegate.logIfEnabled(FQCN, level, null, msg, null)
+ }
+
+ fun log(level: Level, msg: Message, t: Throwable?) {
+ delegate.logIfEnabled(FQCN, level, null, msg, t)
+ }
+
+ fun log(level: Level, msg: CharSequence) {
+ delegate.logIfEnabled(FQCN, level, null, msg, null)
+ }
+
+ fun log(level: Level, msg: CharSequence, t: Throwable?) {
+ delegate.logIfEnabled(FQCN, level, null, msg, t)
+ }
+
+ fun log(level: Level, msg: Any) {
+ delegate.logIfEnabled(FQCN, level, null, msg, null)
+ }
+
+ fun log(level: Level, msg: Any, t: Throwable?) {
+ delegate.logIfEnabled(FQCN, level, null, msg, t)
+ }
+
+ fun log(level: Level, supplier: () -> Any?) {
+ delegate.logIfEnabled(FQCN, level, null, supplier.asLog4jSupplier(), null)
+ }
+
+ fun log(level: Level, t: Throwable, supplier: () -> Any?) {
+ delegate.logIfEnabled(FQCN, level, null, supplier.asLog4jSupplier(), t)
+ }
+
+ fun log(level: Level, marker: Marker, supplier: () -> Any?) {
+ delegate.logIfEnabled(FQCN, level, marker, supplier.asLog4jSupplier(), null)
+ }
+
+ fun log(level: Level, marker: Marker, t: Throwable?, supplier: () -> Any?) {
+ delegate.logIfEnabled(FQCN, level, marker, supplier.asLog4jSupplier(), t)
+ }
+
+ fun trace(marker: Marker, msg: Message) {
+ delegate.logIfEnabled(FQCN, Level.TRACE, marker, msg, null)
+ }
+
+ fun trace(marker: Marker, msg: Message, t: Throwable?) {
+ delegate.logIfEnabled(FQCN, Level.TRACE, marker, msg, t)
+ }
+
+ fun trace(marker: Marker, msg: CharSequence) {
+ delegate.logIfEnabled(FQCN, Level.TRACE, marker, msg, null)
+ }
+
+ fun trace(marker: Marker, msg: CharSequence, t: Throwable?) {
+ delegate.logIfEnabled(FQCN, Level.TRACE, marker, msg, t)
+ }
+
+ fun trace(marker: Marker, msg: Any) {
+ delegate.logIfEnabled(FQCN, Level.TRACE, marker, msg, null)
+ }
+
+ fun trace(marker: Marker, msg: Any, t: Throwable?) {
+ delegate.logIfEnabled(FQCN, Level.TRACE, marker, msg, t)
+ }
+
+ fun trace(msg: Message) {
+ delegate.logIfEnabled(FQCN, Level.TRACE, null, msg, null)
+ }
+
+ fun trace(msg: Message, t: Throwable?) {
+ delegate.logIfEnabled(FQCN, Level.TRACE, null, msg, t)
+ }
+
+ fun trace(msg: CharSequence) {
+ delegate.logIfEnabled(FQCN, Level.TRACE, null, msg, null)
+ }
+
+ fun trace(msg: CharSequence, t: Throwable?) {
+ delegate.logIfEnabled(FQCN, Level.TRACE, null, msg, t)
+ }
+
+ fun trace(msg: Any) {
+ delegate.logIfEnabled(FQCN, Level.TRACE, null, msg, null)
+ }
+
+ fun trace(msg: Any, t: Throwable?) {
+ delegate.logIfEnabled(FQCN, Level.TRACE, null, msg, t)
+ }
+
+ fun trace(supplier: () -> Any?) {
+ delegate.logIfEnabled(FQCN, Level.TRACE, null, supplier.asLog4jSupplier(), null)
+ }
+
+ fun trace(t: Throwable, supplier: () -> Any?) {
+ delegate.logIfEnabled(FQCN, Level.TRACE, null, supplier.asLog4jSupplier(), t)
+ }
+
+ fun trace(marker: Marker, supplier: () -> Any?) {
+ delegate.logIfEnabled(FQCN, Level.TRACE, marker, supplier.asLog4jSupplier(), null)
+ }
+
+ fun trace(marker: Marker, t: Throwable?, supplier: () -> Any?) {
+ delegate.logIfEnabled(FQCN, Level.TRACE, marker, supplier.asLog4jSupplier(), t)
+ }
+
+ // TODO entry with fqcn is not part of the ExtendedLogger interface, location-awareness will be broken
+ fun traceEntry(msg: CharSequence): EntryMessage {
+ return delegate.traceEntry(SimpleMessage(msg))
+ }
+
+ // TODO entry with fqcn is not part of the ExtendedLogger interface, location-awareness will be broken
+ fun traceEntry(supplier: () -> CharSequence): EntryMessage? {
+ return if(delegate.isTraceEnabled) delegate.traceEntry(SimpleMessage(supplier())) else null
+ }
+
+ fun traceEntry(vararg paramSuppliers: () -> Any?): EntryMessage {
+ return delegate.traceEntry(*paramSuppliers.asLog4jSuppliers())
+ }
+
+ fun traceEntry(vararg params: Any?): EntryMessage {
+ return delegate.traceEntry(null, params)
+ }
+
+ // TODO entry with fqcn is not part of the ExtendedLogger interface, location-awareness will be broken
+ fun traceEntry(message: Message): EntryMessage {
+ return delegate.traceEntry(message)
+ }
+
+ fun <R : Any?> runInTrace(block: () -> R): R {
+ return runInTrace(delegate.traceEntry(), block)
+ }
+
+ // TODO exit and catching with fqcn is not part of the ExtendedLogger interface, location-awareness will be broken
+ fun <R : Any?> runInTrace(entryMessage: EntryMessage, block: () -> R): R {
+ return try {
+ val result = block()
+ when(result) {
+ Unit -> delegate.traceExit(entryMessage)
+ else -> delegate.traceExit(entryMessage, result)
+ }
+ result
+ } catch (e: Throwable) {
+ delegate.catching(e)
+ throw e
+ }
+ }
+
+ fun debug(marker: Marker, msg: Message) {
+ delegate.logIfEnabled(FQCN, Level.DEBUG, marker, msg, null)
+ }
+
+ fun debug(marker: Marker, msg: Message, t: Throwable?) {
+ delegate.logIfEnabled(FQCN, Level.DEBUG, marker, msg, t)
+ }
+
+ fun debug(marker: Marker, msg: CharSequence) {
+ delegate.logIfEnabled(FQCN, Level.DEBUG, marker, msg, null)
+ }
+
+ fun debug(marker: Marker, msg: CharSequence, t: Throwable?) {
+ delegate.logIfEnabled(FQCN, Level.DEBUG, marker, msg, t)
+ }
+
+ fun debug(marker: Marker, msg: Any) {
+ delegate.logIfEnabled(FQCN, Level.DEBUG, marker, msg, null)
+ }
+
+ fun debug(marker: Marker, msg: Any, t: Throwable?) {
+ delegate.logIfEnabled(FQCN, Level.DEBUG, marker, msg, t)
+ }
+
+ fun debug(msg: Message) {
+ delegate.logIfEnabled(FQCN, Level.DEBUG, null, msg, null)
+ }
+
+ fun debug(msg: Message, t: Throwable?) {
+ delegate.logIfEnabled(FQCN, Level.DEBUG, null, msg, t)
+ }
+
+ fun debug(msg: CharSequence) {
+ delegate.logIfEnabled(FQCN, Level.DEBUG, null, msg, null)
+ }
+
+ fun debug(msg: CharSequence, t: Throwable?) {
+ delegate.logIfEnabled(FQCN, Level.DEBUG, null, msg, t)
+ }
+
+ fun debug(msg: Any) {
+ delegate.logIfEnabled(FQCN, Level.DEBUG, null, msg, null)
+ }
+
+ fun debug(msg: Any, t: Throwable?) {
+ delegate.logIfEnabled(FQCN, Level.DEBUG, null, msg, t)
+ }
+
+ fun debug(supplier: () -> Any?) {
+ delegate.logIfEnabled(FQCN, Level.DEBUG, null, supplier.asLog4jSupplier(), null)
+ }
+
+ fun debug(t: Throwable, supplier: () -> Any?) {
+ delegate.logIfEnabled(FQCN, Level.DEBUG, null, supplier.asLog4jSupplier(), t)
+ }
+
+ fun debug(marker: Marker, supplier: () -> Any?) {
+ delegate.logIfEnabled(FQCN, Level.DEBUG, marker, supplier.asLog4jSupplier(), null)
+ }
+
+ fun debug(marker: Marker, t: Throwable?, supplier: () -> Any?) {
+ delegate.logIfEnabled(FQCN, Level.DEBUG, marker, supplier.asLog4jSupplier(), t)
+ }
+
+ fun info(marker: Marker, msg: Message) {
+ delegate.logIfEnabled(FQCN, Level.INFO, marker, msg, null)
+ }
+
+ fun info(marker: Marker, msg: Message, t: Throwable?) {
+ delegate.logIfEnabled(FQCN, Level.INFO, marker, msg, t)
+ }
+
+ fun info(marker: Marker, msg: CharSequence) {
+ delegate.logIfEnabled(FQCN, Level.INFO, marker, msg, null)
+ }
+
+ fun info(marker: Marker, msg: CharSequence, t: Throwable?) {
+ delegate.logIfEnabled(FQCN, Level.INFO, marker, msg, t)
+ }
+
+ fun info(marker: Marker, msg: Any) {
+ delegate.logIfEnabled(FQCN, Level.INFO, marker, msg, null)
+ }
+
+ fun info(marker: Marker, msg: Any, t: Throwable?) {
+ delegate.logIfEnabled(FQCN, Level.INFO, marker, msg, t)
+ }
+
+ fun info(msg: Message) {
+ delegate.logIfEnabled(FQCN, Level.INFO, null, msg, null)
+ }
+
+ fun info(msg: Message, t: Throwable?) {
+ delegate.logIfEnabled(FQCN, Level.INFO, null, msg, t)
+ }
+
+ fun info(msg: CharSequence) {
+ delegate.logIfEnabled(FQCN, Level.INFO, null, msg, null)
+ }
+
+ fun info(msg: CharSequence, t: Throwable?) {
+ delegate.logIfEnabled(FQCN, Level.INFO, null, msg, t)
+ }
+
+ fun info(msg: Any) {
+ delegate.logIfEnabled(FQCN, Level.INFO, null, msg, null)
+ }
+
+ fun info(msg: Any, t: Throwable?) {
+ delegate.logIfEnabled(FQCN, Level.INFO, null, msg, t)
+ }
+
+ fun info(supplier: () -> Any?) {
+ delegate.logIfEnabled(FQCN, Level.INFO, null, supplier.asLog4jSupplier(), null)
+ }
+
+ fun info(t: Throwable, supplier: () -> Any?) {
+ delegate.logIfEnabled(FQCN, Level.INFO, null, supplier.asLog4jSupplier(), t)
+ }
+
+ fun info(marker: Marker, supplier: () -> Any?) {
+ delegate.logIfEnabled(FQCN, Level.INFO, marker, supplier.asLog4jSupplier(), null)
+ }
+
+ fun info(marker: Marker, t: Throwable?, supplier: () -> Any?) {
+ delegate.logIfEnabled(FQCN, Level.INFO, marker, supplier.asLog4jSupplier(), t)
+ }
+
+ fun warn(marker: Marker, msg: Message) {
+ delegate.logIfEnabled(FQCN, Level.WARN, marker, msg, null)
+ }
+
+ fun warn(marker: Marker, msg: Message, t: Throwable?) {
+ delegate.logIfEnabled(FQCN, Level.WARN, marker, msg, t)
+ }
+
+ fun warn(marker: Marker, msg: CharSequence) {
+ delegate.logIfEnabled(FQCN, Level.WARN, marker, msg, null)
+ }
+
+ fun warn(marker: Marker, msg: CharSequence, t: Throwable?) {
+ delegate.logIfEnabled(FQCN, Level.WARN, marker, msg, t)
+ }
+
+ fun warn(marker: Marker, msg: Any) {
+ delegate.logIfEnabled(FQCN, Level.WARN, marker, msg, null)
+ }
+
+ fun warn(marker: Marker, msg: Any, t: Throwable?) {
+ delegate.logIfEnabled(FQCN, Level.WARN, marker, msg, t)
+ }
+
+ fun warn(msg: Message) {
+ delegate.logIfEnabled(FQCN, Level.WARN, null, msg, null)
+ }
+
+ fun warn(msg: Message, t: Throwable?) {
+ delegate.logIfEnabled(FQCN, Level.WARN, null, msg, t)
+ }
+
+ fun warn(msg: CharSequence) {
+ delegate.logIfEnabled(FQCN, Level.WARN, null, msg, null)
+ }
+
+ fun warn(msg: CharSequence, t: Throwable?) {
+ delegate.logIfEnabled(FQCN, Level.WARN, null, msg, t)
+ }
+
+ fun warn(msg: Any) {
+ delegate.logIfEnabled(FQCN, Level.WARN, null, msg, null)
+ }
+
+ fun warn(msg: Any, t: Throwable?) {
+ delegate.logIfEnabled(FQCN, Level.WARN, null, msg, t)
+ }
+
+ fun warn(supplier: () -> Any?) {
+ delegate.logIfEnabled(FQCN, Level.WARN, null, supplier.asLog4jSupplier(), null)
+ }
+
+ fun warn(t: Throwable, supplier: () -> Any?) {
+ delegate.logIfEnabled(FQCN, Level.WARN, null, supplier.asLog4jSupplier(), t)
+ }
+
+ fun warn(marker: Marker, supplier: () -> Any?) {
+ delegate.logIfEnabled(FQCN, Level.WARN, marker, supplier.asLog4jSupplier(), null)
+ }
+
+ fun warn(marker: Marker, t: Throwable?, supplier: () -> Any?) {
+ delegate.logIfEnabled(FQCN, Level.WARN, marker, supplier.asLog4jSupplier(), t)
+ }
+
+ fun error(marker: Marker, msg: Message) {
+ delegate.logIfEnabled(FQCN, Level.ERROR, marker, msg, null)
+ }
+
+ fun error(marker: Marker, msg: Message, t: Throwable?) {
+ delegate.logIfEnabled(FQCN, Level.ERROR, marker, msg, t)
+ }
+
+ fun error(marker: Marker, msg: CharSequence) {
+ delegate.logIfEnabled(FQCN, Level.ERROR, marker, msg, null)
+ }
+
+ fun error(marker: Marker, msg: CharSequence, t: Throwable?) {
+ delegate.logIfEnabled(FQCN, Level.ERROR, marker, msg, t)
+ }
+
+ fun error(marker: Marker, msg: Any) {
+ delegate.logIfEnabled(FQCN, Level.ERROR, marker, msg, null)
+ }
+
+ fun error(marker: Marker, msg: Any, t: Throwable?) {
+ delegate.logIfEnabled(FQCN, Level.ERROR, marker, msg, t)
+ }
+
+ fun error(msg: Message) {
+ delegate.logIfEnabled(FQCN, Level.ERROR, null, msg, null)
+ }
+
+ fun error(msg: Message, t: Throwable?) {
+ delegate.logIfEnabled(FQCN, Level.ERROR, null, msg, t)
+ }
+
+ fun error(msg: CharSequence) {
+ delegate.logIfEnabled(FQCN, Level.ERROR, null, msg, null)
+ }
+
+ fun error(msg: CharSequence, t: Throwable?) {
+ delegate.logIfEnabled(FQCN, Level.ERROR, null, msg, t)
+ }
+
+ fun error(msg: Any) {
+ delegate.logIfEnabled(FQCN, Level.ERROR, null, msg, null)
+ }
+
+ fun error(msg: Any, t: Throwable?) {
+ delegate.logIfEnabled(FQCN, Level.ERROR, null, msg, t)
+ }
+
+ fun error(supplier: () -> Any?) {
+ delegate.logIfEnabled(FQCN, Level.ERROR, null, supplier.asLog4jSupplier(), null)
+ }
+
+ fun error(t: Throwable, supplier: () -> Any?) {
+ delegate.logIfEnabled(FQCN, Level.ERROR, null, supplier.asLog4jSupplier(), t)
+ }
+
+ fun error(marker: Marker, supplier: () -> Any?) {
+ delegate.logIfEnabled(FQCN, Level.ERROR, marker, supplier.asLog4jSupplier(), null)
+ }
+
+ fun error(marker: Marker, t: Throwable?, supplier: () -> Any?) {
+ delegate.logIfEnabled(FQCN, Level.ERROR, marker, supplier.asLog4jSupplier(), t)
+ }
+
+ fun fatal(marker: Marker, msg: Message) {
+ delegate.logIfEnabled(FQCN, Level.FATAL, marker, msg, null)
+ }
+
+ fun fatal(marker: Marker, msg: Message, t: Throwable?) {
+ delegate.logIfEnabled(FQCN, Level.FATAL, marker, msg, t)
+ }
+
+ fun fatal(marker: Marker, msg: CharSequence) {
+ delegate.logIfEnabled(FQCN, Level.FATAL, marker, msg, null)
+ }
+
+ fun fatal(marker: Marker, msg: CharSequence, t: Throwable?) {
+ delegate.logIfEnabled(FQCN, Level.FATAL, marker, msg, t)
+ }
+
+ fun fatal(marker: Marker, msg: Any) {
+ delegate.logIfEnabled(FQCN, Level.FATAL, marker, msg, null)
+ }
+
+ fun fatal(marker: Marker, msg: Any, t: Throwable?) {
+ delegate.logIfEnabled(FQCN, Level.FATAL, marker, msg, t)
+ }
+
+ fun fatal(msg: Message) {
+ delegate.logIfEnabled(FQCN, Level.FATAL, null, msg, null)
+ }
+
+ fun fatal(msg: Message, t: Throwable?) {
+ delegate.logIfEnabled(FQCN, Level.FATAL, null, msg, t)
+ }
+
+ fun fatal(msg: CharSequence) {
+ delegate.logIfEnabled(FQCN, Level.FATAL, null, msg, null)
+ }
+
+ fun fatal(msg: CharSequence, t: Throwable?) {
+ delegate.logIfEnabled(FQCN, Level.FATAL, null, msg, t)
+ }
+
+ fun fatal(msg: Any) {
+ delegate.logIfEnabled(FQCN, Level.FATAL, null, msg, null)
+ }
+
+ fun fatal(msg: Any, t: Throwable?) {
+ delegate.logIfEnabled(FQCN, Level.FATAL, null, msg, t)
+ }
+
+ fun fatal(supplier: () -> Any?) {
+ delegate.logIfEnabled(FQCN, Level.FATAL, null, supplier.asLog4jSupplier(), null)
+ }
+
+ fun fatal(t: Throwable, supplier: () -> Any?) {
+ delegate.logIfEnabled(FQCN, Level.FATAL, null, supplier.asLog4jSupplier(), t)
+ }
+
+ fun fatal(marker: Marker, supplier: () -> Any?) {
+ delegate.logIfEnabled(FQCN, Level.FATAL, marker, supplier.asLog4jSupplier(), null)
+ }
+
+ fun fatal(marker: Marker, t: Throwable?, supplier: () -> Any?) {
+ delegate.logIfEnabled(FQCN, Level.FATAL, marker, supplier.asLog4jSupplier(), t)
+ }
+
+}
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
new file mode 100644
index 0000000..8bd11cd
--- /dev/null
+++ b/log4j-api-kotlin/src/main/kotlin/org/apache/logging/log4j/kotlin/Logging.kt
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.kotlin
+
+/**
+ * An interface-based "mixin" to easily add a log val to a class, named by the enclosing class. This allows
+ * code like this:
+ *
+ * ```
+ * import org.apache.logging.log4j.kotlin.Logging
+ *
+ * class MyClass: Logging {
+ * // use `logger` as necessary
+ * }
+ *
+ * ```
+ *
+ * Or declaring the interface on a companion object works just as well:
+ *
+ * ```
+ * import org.apache.logging.log4j.kotlin.logger
+ *
+ * class MyClass {
+ * companion object: Logging
+ *
+ * // use `logger` as necessary
+ * }
+ *
+ * ```
+ */
+interface Logging {
+ @Suppress("unused")
+ val logger
+ get() = loggerOf(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
new file mode 100644
index 0000000..7a4126c
--- /dev/null
+++ b/log4j-api-kotlin/src/main/kotlin/org/apache/logging/log4j/kotlin/LoggingFactory.kt
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.kotlin
+
+import org.apache.logging.log4j.LogManager
+import org.apache.logging.log4j.spi.ExtendedLogger
+import kotlin.reflect.full.companionObject
+
+/**
+ * Logger instantiation by function. Use: `val log = logger()`.
+ */
+@Suppress("unused")
+inline fun <reified T : Any> T.logger() = loggerOf(T::class.java)
+
+fun loggerDelegateOf(ofClass: Class<*>): ExtendedLogger {
+ return LogManager.getContext(ofClass.classLoader, false).getLogger(unwrapCompanionClass(ofClass).name)
+}
+
+fun loggerOf(ofClass: Class<*>): KotlinLogger {
+ return KotlinLogger(loggerDelegateOf(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) {
+ ofClass.enclosingClass
+ } else {
+ ofClass
+ }
+}
diff --git a/log4j-api-kotlin/src/main/kotlin/org/apache/logging/log4j/kotlin/Suppliers.kt b/log4j-api-kotlin/src/main/kotlin/org/apache/logging/log4j/kotlin/Suppliers.kt
new file mode 100644
index 0000000..57bbf55
--- /dev/null
+++ b/log4j-api-kotlin/src/main/kotlin/org/apache/logging/log4j/kotlin/Suppliers.kt
@@ -0,0 +1,7 @@
+package org.apache.logging.log4j.kotlin
+
+import org.apache.logging.log4j.util.Supplier
+
+fun <T: Any?> (() -> T).asLog4jSupplier(): Supplier<T> = Supplier { invoke() }
+
+fun <T: Any?> (Array<out () -> T>).asLog4jSuppliers(): Array<Supplier<T>> = map { it.asLog4jSupplier() }.toTypedArray()
diff --git a/log4j-api-kotlin/src/test/kotlin/org.apache.logging.log4j.kotlin/LoggerCompanionTest.kt b/log4j-api-kotlin/src/test/kotlin/org.apache.logging.log4j.kotlin/LoggerCompanionTest.kt
new file mode 100644
index 0000000..5718259
--- /dev/null
+++ b/log4j-api-kotlin/src/test/kotlin/org.apache.logging.log4j.kotlin/LoggerCompanionTest.kt
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.kotlin
+
+import org.apache.logging.log4j.Level
+import org.apache.logging.log4j.kotlin.support.withListAppender
+import org.junit.Test
+import kotlin.test.assertEquals
+
+class LoggerCompanionTest {
+ companion object {
+ val log = logger()
+ }
+
+ // note: using LoggerContextRule here to init the config does nothing as the initialization happens in the companion
+ // log4j will fall back to the default config
+
+ @Test
+ fun `Logging from a function instantiation via companion logs the correct class name`() {
+ val msg = "This is an error log."
+ val msgs = withListAppender { _, _ ->
+ log.error(msg)
+ }
+
+ assertEquals(1, msgs.size.toLong())
+
+ msgs.first().also {
+ assertEquals(Level.ERROR, it.level)
+ assertEquals(msg, it.message.format)
+ assertEquals(LoggerCompanionTest::class.qualifiedName, it.loggerName)
+ }
+ }
+}
diff --git a/log4j-api-kotlin/src/test/kotlin/org.apache.logging.log4j.kotlin/LoggerMixinCompanionExtendsTest.kt b/log4j-api-kotlin/src/test/kotlin/org.apache.logging.log4j.kotlin/LoggerMixinCompanionExtendsTest.kt
new file mode 100644
index 0000000..cd2bbfe
--- /dev/null
+++ b/log4j-api-kotlin/src/test/kotlin/org.apache.logging.log4j.kotlin/LoggerMixinCompanionExtendsTest.kt
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.kotlin
+
+import org.apache.logging.log4j.Level
+import org.apache.logging.log4j.kotlin.support.withListAppender
+import org.junit.Test
+import kotlin.test.assertEquals
+
+class LoggerMixinCompanionExtendsTest {
+
+ companion object : Logging
+
+ // note: using LoggerContextRule here to init the config does nothing as the initialization happens in the companion
+ // log4j will fall back to the default config
+
+ @Test
+ fun `Logging from an interface mix-in via companion logs the correct class name`() {
+ val msg = "This is an error log."
+ val msgs = withListAppender { _, _ ->
+ logger.error(msg)
+ }
+
+ assertEquals(1, msgs.size.toLong())
+
+ msgs.first().also {
+ assertEquals(Level.ERROR, it.level)
+ assertEquals(msg, it.message.format)
+ assertEquals(LoggerMixinCompanionExtendsTest::class.qualifiedName, it.loggerName)
+ }
+ }
+}
diff --git a/log4j-api-kotlin/src/test/kotlin/org.apache.logging.log4j.kotlin/LoggerMixinExtendsTest.kt b/log4j-api-kotlin/src/test/kotlin/org.apache.logging.log4j.kotlin/LoggerMixinExtendsTest.kt
new file mode 100644
index 0000000..082c944
--- /dev/null
+++ b/log4j-api-kotlin/src/test/kotlin/org.apache.logging.log4j.kotlin/LoggerMixinExtendsTest.kt
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.kotlin
+
+import org.apache.logging.log4j.Level.ERROR
+import org.apache.logging.log4j.junit.LoggerContextRule
+import org.apache.logging.log4j.kotlin.support.withListAppender
+import org.junit.Rule
+import org.junit.Test
+import kotlin.test.assertEquals
+
+class LoggerMixinExtendsTest : Logging {
+
+ @Rule @JvmField var init = LoggerContextRule("InfoLogger.xml")
+
+ @Test
+ fun `Logging using an interface mix-in logs the correct class name`() {
+ val msg = "This is an error log."
+ val msgs = withListAppender { _, _ ->
+ logger.error(msg)
+ }
+
+ assertEquals(1, msgs.size.toLong())
+
+ msgs.first().also {
+ assertEquals(ERROR, it.level)
+ assertEquals(msg, it.message.format)
+ assertEquals(LoggerMixinExtendsTest::class.qualifiedName, it.loggerName)
+ }
+ }
+}
diff --git a/log4j-api-kotlin/src/test/kotlin/org.apache.logging.log4j.kotlin/LoggerTest.kt b/log4j-api-kotlin/src/test/kotlin/org.apache.logging.log4j.kotlin/LoggerTest.kt
new file mode 100644
index 0000000..2a981f4
--- /dev/null
+++ b/log4j-api-kotlin/src/test/kotlin/org.apache.logging.log4j.kotlin/LoggerTest.kt
@@ -0,0 +1,199 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.kotlin
+
+import com.nhaarman.mockito_kotlin.*
+import org.apache.logging.log4j.Level
+import org.apache.logging.log4j.MarkerManager
+import org.apache.logging.log4j.junit.LoggerContextRule
+import org.apache.logging.log4j.message.DefaultFlowMessageFactory
+import org.apache.logging.log4j.message.MessageFactory2
+import org.apache.logging.log4j.message.ParameterizedMessage
+import org.apache.logging.log4j.message.ParameterizedMessageFactory
+import org.apache.logging.log4j.spi.ExtendedLogger
+import org.junit.Rule
+import org.junit.Test
+import org.mockito.ArgumentMatchers.anyString
+import kotlin.test.assertFailsWith
+import kotlin.test.assertTrue
+
+data class Custom(val i: Int)
+
+interface Manager {
+ fun fetchValue(): Int
+}
+
+typealias LoggerStubbingFn = KStubbing<ExtendedLogger>.() -> Unit
+
+class LoggerTest {
+ companion object {
+ val msg = ParameterizedMessage("msg {}", 17)
+ val entryMsg = DefaultFlowMessageFactory().newEntryMessage(msg)
+ val cseqMsg: CharSequence = StringBuilder().append("cseq msg")
+ val objectMsg = Custom(17)
+ val cause = RuntimeException("cause")
+ val marker = MarkerManager.getMarker("marker")
+ val result = "foo"
+ val managerValue: Int = 4711
+ }
+
+ @Rule @JvmField var init = LoggerContextRule("InfoLogger.xml")
+
+ class Fixture(stubbing: LoggerStubbingFn? = null) {
+ val mockLogger = mock<ExtendedLogger> {
+ on { getMessageFactory<MessageFactory2>() } doReturn ParameterizedMessageFactory()
+ if(stubbing != null) stubbing()
+ }
+
+ val manager = mock<Manager> {
+ on { fetchValue() } doReturn managerValue
+ }
+ }
+
+ fun withFixture(stubbing: LoggerStubbingFn?, level: Level, returning: Boolean, block: Fixture.(KotlinLogger) -> Unit): Fixture {
+ val f = Fixture(stubbing)
+ whenever(f.mockLogger.isEnabled(level)).thenReturn(returning)
+ val logger = KotlinLogger(f.mockLogger)
+ block(f, logger)
+ return f
+ }
+
+ fun withLevelFixture(level: Level, returning: Boolean, block: Fixture.(KotlinLogger) -> Unit): Fixture {
+ return withFixture({
+ on { isEnabled(level) } doReturn returning
+ }, level, returning, block)
+ }
+
+ @Test
+ fun `Logging works!`() {
+ val f = withLevelFixture(Level.ERROR, true) {
+ it.error(result)
+ }
+ verify(f.mockLogger).logIfEnabled(anyString(), eq(Level.ERROR), isNull(), eq<CharSequence>(result), isNull())
+ }
+
+ @Test
+ fun `Level fatal enabled with String message`() {
+ val f = withLevelFixture(Level.FATAL, true) {
+ it.fatal("string msg with value: ${manager.fetchValue()}")
+ }
+ verify(f.mockLogger).logIfEnabled(anyString(), eq(Level.FATAL), isNull(), eq<CharSequence>("string msg with value: $managerValue"), isNull())
+ verify(f.manager).fetchValue()
+ }
+
+
+ @Test
+ fun `Level fatal disabled with String message`() {
+ val f = withLevelFixture(Level.FATAL, false) {
+ it.fatal("string msg with value: ${manager.fetchValue()}")
+ }
+
+ verify(f.mockLogger).logIfEnabled(anyString(), eq(Level.FATAL), isNull(), eq<CharSequence>("string msg with value: $managerValue"), isNull())
+ // one might expect times(0), but we just delegate so times(1), we don't have any extra macro-based logic like the Scala api
+ // use the lambda approach to not evaluate the message if the level is not enabled
+ verify(f.manager, times(1)).fetchValue()
+ }
+
+ @Test
+ fun `Lambda functions are evaluated if the level is high enough`() {
+ var count = 0
+ fun lamdaFun(): String {
+ count++
+ return result
+ }
+ val log = logger()
+ log.info { lamdaFun() }
+ assertTrue { count == 1 }
+ }
+
+ @Test
+ fun `Lambda functions are not evaluated if the level is low enough`() {
+ var count = 0
+ fun lamdaFun(): String {
+ count++
+ return result
+ }
+ val log = logger()
+ log.debug { lamdaFun() }
+ assertTrue { count == 0 }
+ }
+
+ @Test
+ fun `CharSequence messages are logged`() {
+ val f = withLevelFixture(Level.INFO, true) {
+ it.info(cseqMsg)
+ }
+ verify(f.mockLogger).logIfEnabled(anyString(), eq(Level.INFO), isNull(), eq(cseqMsg), isNull())
+ }
+
+ @Test
+ fun `Object messages are logged`() {
+ val f = withLevelFixture(Level.INFO, true) {
+ it.info(objectMsg)
+ }
+ verify(f.mockLogger).logIfEnabled(anyString(), eq(Level.INFO), isNull(), eq(objectMsg), isNull())
+ }
+
+ @Test
+ fun `Markers are logged`() {
+ val f = withLevelFixture(Level.INFO, true) {
+ it.info(marker, result)
+ }
+ verify(f.mockLogger).logIfEnabled(anyString(), eq(Level.INFO), eq(marker), eq<CharSequence>(result), isNull())
+ }
+
+ @Test
+ fun `Run in trace with no result`() {
+ var count = 0
+ val f = withLevelFixture(Level.INFO, true) {
+ it.runInTrace(entryMsg) {
+ ++count
+ Unit
+ }
+ }
+ assertTrue { count == 1 }
+ verify(f.mockLogger).traceExit(eq(entryMsg))
+ }
+
+ @Test
+ fun `Run in trace with result`() {
+ var count = 0
+ val f = withLevelFixture(Level.INFO, true) {
+ it.runInTrace(entryMsg) {
+ ++count
+ }
+ }
+ assertTrue { count == 1 }
+ verify(f.mockLogger).traceExit(eq(entryMsg), eq(1))
+ }
+
+ @Test
+ fun `Run in trace with Exception`() {
+ var count = 0
+ val f = withLevelFixture(Level.INFO, true) {
+ assertFailsWith<RuntimeException> {
+ it.runInTrace(entryMsg) {
+ ++count
+ throw cause
+ }
+ }
+ }
+ assertTrue { count == 1 }
+ verify(f.mockLogger, times(0)).traceExit(any())
+ verify(f.mockLogger).catching(argThat { message == "cause" })
+ }
+}
diff --git a/log4j-api-kotlin/src/test/kotlin/org.apache.logging.log4j.kotlin/support/LoggerTests.kt b/log4j-api-kotlin/src/test/kotlin/org.apache.logging.log4j.kotlin/support/LoggerTests.kt
new file mode 100644
index 0000000..a3ccf94
--- /dev/null
+++ b/log4j-api-kotlin/src/test/kotlin/org.apache.logging.log4j.kotlin/support/LoggerTests.kt
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.kotlin.support
+
+import org.apache.logging.log4j.LogManager
+import org.apache.logging.log4j.core.LogEvent
+import org.apache.logging.log4j.core.Logger
+import org.apache.logging.log4j.test.appender.ListAppender
+
+fun rootLogger() = LogManager.getRootLogger() as Logger
+
+fun withListAppender(block: (rootLogger: Logger, appender: ListAppender) -> Unit): List<LogEvent> {
+ val appender = ListAppender("List").apply { start() }
+ val root = rootLogger().apply { addAppender(appender) }
+
+ try {
+ block(root, appender)
+ return appender.events
+ } finally {
+ appender.stop()
+ root.removeAppender(appender)
+ }
+}
diff --git a/log4j-api-kotlin/src/test/resources/InfoLogger.xml b/log4j-api-kotlin/src/test/resources/InfoLogger.xml
new file mode 100644
index 0000000..ba3c948
--- /dev/null
+++ b/log4j-api-kotlin/src/test/resources/InfoLogger.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<Configuration name="KotlinApiTests" status="warn">
+ <Appenders>
+ <Console name="Console">
+ <PatternLayout>
+ <Pattern>%d %5p %c{1} %X %F:%L - %m%n</Pattern>
+ </PatternLayout>
+ </Console>
+ </Appenders>
+ <Loggers>
+ <Root level="INFO">
+ <AppenderRef ref="Console"/>
+ </Root>
+ </Loggers>
+</Configuration>
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..bc2b2dd
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,107 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Licensed to the Apache Software Foundation (ASF) under one or more
+ ~ contributor license agreements. See the NOTICE file distributed with
+ ~ this work for additional information regarding copyright ownership.
+ ~ The ASF licenses this file to You under the Apache license, Version 2.0
+ ~ (the "License"); you may not use this file except in compliance with
+ ~ the License. You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the license for the specific language governing permissions and
+ ~ limitations under the license.
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <artifactId>log4j-kotlin</artifactId>
+ <packaging>pom</packaging>
+ <name>Apache Log4j 2 Kotlin API</name>
+ <version>2.10.1-SNAPSHOT</version>
+ <parent>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j</artifactId>
+ <version>2.10.1-SNAPSHOT</version>
+ </parent>
+ <description>Apache Log4j 2 Kotlin API</description>
+ <url>http://logging.apache.org/log4j/2.x/</url>
+ <scm>
+ <connection>scm:git:http://git-wip-us.apache.org/repos/asf/logging-log4j-kotlin.git</connection>
+ <developerConnection>scm:git:https://git-wip-us.apache.org/repos/asf/logging-log4j-kotlin.git</developerConnection>
+ <url>https://git-wip-us.apache.org/repos/asf?p=logging-log4j-kotlin.git;a=summary</url>
+ <tag>log4j-${Log4jReleaseVersion}</tag>
+ </scm>
+
+ <properties>
+ <kotlin.version>1.2.21</kotlin.version>
+ </properties>
+
+ <dependencyManagement>
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-api-kotlin</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ </dependencies>
+ </dependencyManagement>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.jetbrains.kotlin</groupId>
+ <artifactId>kotlin-stdlib</artifactId>
+ <version>${kotlin.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.jetbrains.kotlin</groupId>
+ <artifactId>kotlin-test</artifactId>
+ <version>${kotlin.version}</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.jetbrains.kotlin</groupId>
+ <artifactId>kotlin-maven-plugin</artifactId>
+ <version>${kotlin.version}</version>
+ <executions>
+ <execution>
+ <id>compile</id>
+ <phase>compile</phase>
+ <goals>
+ <goal>compile</goal>
+ </goals>
+ </execution>
+ <execution>
+ <id>test-compile</id>
+ <phase>test-compile</phase>
+ <goals>
+ <goal>test-compile</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+
+ <repositories>
+ <repository>
+ <id>apache.snapshots</id>
+ <name>Apache Snapshot Repository</name>
+ <url>https://repository.apache.org/snapshots</url>
+ <releases>
+ <enabled>false</enabled>
+ </releases>
+ </repository>
+ </repositories>
+
+ <modules>
+ <module>log4j-api-kotlin</module>
+ <module>log4j-api-kotlin-sample</module>
+ </modules>
+</project>