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>