diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..e9e018b --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[*.{kt,kts,gradle}] +indent_style = space +indent_size = 4 +continuation_indent_size = 4 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f14298e --- /dev/null +++ b/.gitignore @@ -0,0 +1,27 @@ +# Hidden files +.* + +# Temporary files +*~ + +# Git +!.git* + +# EditorConfig +!.editorconfig + +# IntelliJ Idea +out/ +*.iml +*.ipr +*.iws + +# Travis CI +!.travis.yml + +# Gradle +build/ + +# JVM error logs +hs_err_pid*.log +replay_pid*.log diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..562264f --- /dev/null +++ b/.travis.yml @@ -0,0 +1,6 @@ +language: java +jdk: + - oraclejdk8 + +notifications: + email: false diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..82d8083 --- /dev/null +++ b/LICENSE @@ -0,0 +1,13 @@ +Copyright (c) 2018 Michael Bull (https://www.michael-bull.com) + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..0bfbd28 --- /dev/null +++ b/README.md @@ -0,0 +1,150 @@ +# kotlin-inline-logger + +[![Release](https://api.bintray.com/packages/michaelbull/maven/kotlin-inline-logger/images/download.svg)](https://bintray.com/michaelbull/maven/kotlin-inline-logger/_latestVersion) [![Build Status](https://travis-ci.org/michaelbull/kotlin-inline-logger.svg?branch=master)](https://travis-ci.org/michaelbull/kotlin-inline-logger) [![License](https://img.shields.io/github/license/michaelbull/kotlin-inline-logger.svg)](https://github.com/michaelbull/kotlin-inline-logger/blob/master/LICENSE) + +A logger facilitating lazily-evaluated log calls via Kotlin's inline [classes][inline-classes] & [functions][inline-functions]. + +## Installation + +```groovy +repositories { + maven { url = 'https://dl.bintray.com/michaelbull/maven' } +} + +dependencies { + compile 'com.michael-bull.kotlin-inline-logger:kotlin-inline-logger-jvm:1.0.0' +} +``` + +## Introduction + +`kotlin-inline-logger` has been structured as a [`multiplatform project`][mpp]. +Currently the only implementation supports the JVM, utilising [SLF4J][slf4j], +however in future other platforms can also be supported by implementing the +necessary platform-specific APIs found in [`src/commonMain`](src/commonMain). + +If you would like to implement support for a platform, please don't hesitate +to open a pull request on [GitHub][github]. + +### JVM + +On the JVM, `kotlin-inline-logger` provides inline functions that delegate +to [SLF4J][slf4j]. Users migrating from an existing SLF4J solution will find +their existing calls to methods, e.g. `logger.info("Example")` now marked as +`@Deprecated`, prompting them to replace their existing code with the +lazily-evaluated alternatives. + +![ReplaceWith example](replacewith-example.gif) + +#### Creating loggers + +By default, passing no argument when creating an `InlineLogger()` will utilise +`MethodHandles.lookup()`, introduced in JDK7, to derive the name of the logger +from the class that created it. This is described in SLF4J's documentation as +an [idiomatic method of declaring a logger][slf4j-idiom] that is resistant to +cut-and-pasting between classes. + +Named loggers can be created by passing a name: + +```kotlin +val transactionLogger = InlineLogger("DatabaseTransactions") +``` + +Class loggers can be created by passing the `::class` reference: + +```kotlin +class TransactionExecutor + +val transactionLogger = InlineLogger(TransactionExecutor::class) +``` + + +#### Effectiveness + +The code examples below illustrate how the Kotlin you write is compiled to +interoperable Java, demonstrating how the `expensiveCalculation()` function is +only invoked and interpolated with the log message if warn-level logging is +enabled. + +This allows you to replace usages of SLF4J's [MessageFormatter][slf4j-formatter], +with the more idiomatic [string templates][string-templates] Kotlin provides. + +##### Kotlin: + +```kotlin +import com.github.michaelbull.logging.InlineLogger + +val logger = InlineLogger() + +fun expensiveCalculation(): Int { + return 1234 * 5678 +} + +fun main() { + logger.warn { "The result is: ${expensiveCalculation()}" } +} +``` + +##### Compiled Java: + +```java +import com.github.michaelbull.logging.InlineLogger; +import java.lang.invoke.MethodHandles; +import kotlin.Metadata; +import org.jetbrains.annotations.NotNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public final class ExampleKt { + @NotNull + private static final Logger logger; + + @NotNull + public static final Logger getLogger() { + return logger; + } + + public static final int expensiveCalculation() { + return 1234 * 5678; + } + + public static final void main() { + Logger $this$iv = logger; + boolean $i$f$warn = false; + if (InlineLogger.isWarnEnabled-impl((Logger)$this$iv)) { + Logger logger = $this$iv; + boolean bl = false; + String string = "The result is: " + ExampleKt.expensiveCalculation(); + logger.warn(String.valueOf(string)); + } + } + + public static /* synthetic */ void main(String[] arrstring) { + ExampleKt.main(); + } + + static { + boolean $i$f$InlineLogger = false; + Logger delegate$iv = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + logger = InlineLogger.constructor-impl((Logger)delegate$iv); + } +} +``` + +## Contributing + +Bug reports and pull requests are welcome on [GitHub][github]. + +## License + +This project is available under the terms of the ISC license. See the +[`LICENSE`](LICENSE) file for the copyright information and licensing terms. + +[inline-classes]: https://kotlinlang.org/docs/reference/inline-classes.html +[inline-functions]: https://kotlinlang.org/docs/reference/inline-functions.html +[mpp]: https://kotlinlang.org/docs/reference/multiplatform.html +[string-templates]: https://kotlinlang.org/docs/reference/basic-types.html#string-templates +[github]: https://github.com/michaelbull/kotlin-inline-logger +[slf4j]: https://www.slf4j.org/ +[slf4j-idiom]: https://www.slf4j.org/faq.html#declaration_pattern +[slf4j-formatter]: https://www.slf4j.org/api/org/slf4j/helpers/MessageFormatter.html diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 0000000..af23ddc --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,94 @@ +import com.jfrog.bintray.gradle.BintrayExtension +import com.jfrog.bintray.gradle.tasks.BintrayUploadTask + +description = "A logger facilitating lazily-evaluated log calls via Kotlin's inline classes & functions." + +plugins { + `maven-publish` + kotlin("multiplatform") version ("1.3.31") + id("com.github.ben-manes.versions") version ("0.21.0") + id("com.jfrog.bintray") version ("1.8.4") + id("net.researchgate.release") version ("2.8.0") +} + +repositories { + mavenCentral() + jcenter() +} + +kotlin { + sourceSets { + named("commonMain") { + dependencies { + implementation(kotlin("stdlib-common")) + } + } + + named("commonTest") { + dependencies { + implementation(kotlin("test-common")) + implementation(kotlin("test-annotations-common")) + } + } + } + + jvm { + compilations.named("main") { + kotlinOptions { + jvmTarget = "1.8" + freeCompilerArgs = listOf("-Xno-call-assertions", "-Xno-receiver-assertions", "-Xno-param-assertions") + } + + dependencies { + implementation(kotlin("stdlib-jdk8")) + implementation("org.slf4j:slf4j-api:1.7.26") + } + } + + compilations.named("test") { + kotlinOptions { + jvmTarget = "1.8" + freeCompilerArgs = listOf("-Xno-call-assertions", "-Xno-receiver-assertions", "-Xno-param-assertions") + } + + dependencies { + implementation(kotlin("test")) + implementation(kotlin("test-junit")) + } + } + } +} + +fun BintrayExtension.pkg(configure: BintrayExtension.PackageConfig.() -> Unit) { + pkg(delegateClosureOf(configure)) +} + +val bintrayUser: String? by project +val bintrayKey: String? by project + +bintray { + user = bintrayUser + key = bintrayKey + setPublications("jvm", "metadata") + + pkg { + repo = "maven" + name = project.name + desc = project.description + websiteUrl = "https://github.com/michaelbull/kotlin-inline-logger" + issueTrackerUrl = "https://github.com/michaelbull/kotlin-inline-logger/issues" + vcsUrl = "git@github.com:michaelbull/kotlin-inline-logger.git" + githubRepo = "michaelbull/kotlin-inline-logger" + setLicenses("ISC") + } +} + +val bintrayUpload by tasks.existing(BintrayUploadTask::class) { + dependsOn("build") + dependsOn("generatePomFileForJvmPublication") + dependsOn("generatePomFileForMetadataPublication") +} + +tasks.named("afterReleaseBuild") { + dependsOn(bintrayUpload) +} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..81d7e0f --- /dev/null +++ b/gradle.properties @@ -0,0 +1,2 @@ +group=com.michael-bull.kotlin-inline-logger +version=1.0.0 diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..29953ea Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..ee69dd6 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100755 index 0000000..cccdd3d --- /dev/null +++ b/gradlew @@ -0,0 +1,172 @@ +#!/usr/bin/env sh + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..f955316 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/replacewith-example.gif b/replacewith-example.gif new file mode 100644 index 0000000..f0be189 Binary files /dev/null and b/replacewith-example.gif differ diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 0000000..e18f8ea --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1 @@ +rootProject.name = "kotlin-inline-logger" diff --git a/src/commonMain/kotlin/com/github/michaelbull/logging/InlineLogger.kt b/src/commonMain/kotlin/com/github/michaelbull/logging/InlineLogger.kt new file mode 100644 index 0000000..d298dd1 --- /dev/null +++ b/src/commonMain/kotlin/com/github/michaelbull/logging/InlineLogger.kt @@ -0,0 +1,398 @@ +package com.github.michaelbull.logging + +import kotlin.reflect.KClass + +expect inline fun InlineLogger(): InlineLogger +expect inline fun InlineLogger(name: String): InlineLogger +expect inline fun InlineLogger(clazz: KClass): InlineLogger + +@Suppress("unused", "DeprecatedCallableAddReplaceWith") +inline class InlineLogger(val delegate: Logger) { + + val name: String get() = delegate.getName() + + val isTraceEnabled: Boolean get() = delegate.isTraceEnabled() + fun isTraceEnabled(marker: Marker?): Boolean = delegate.isTraceEnabled(marker) + + val isDebugEnabled: Boolean get() = delegate.isDebugEnabled() + fun isDebugEnabled(marker: Marker?): Boolean = delegate.isDebugEnabled(marker) + + val isInfoEnabled: Boolean get() = delegate.isInfoEnabled() + fun isInfoEnabled(marker: Marker?): Boolean = delegate.isInfoEnabled(marker) + + val isWarnEnabled: Boolean get() = delegate.isWarnEnabled() + fun isWarnEnabled(marker: Marker?): Boolean = delegate.isWarnEnabled(marker) + + val isErrorEnabled: Boolean get() = delegate.isErrorEnabled() + fun isErrorEnabled(marker: Marker?): Boolean = delegate.isErrorEnabled(marker) + + inline fun trace(msg: () -> Any?) { + if (isTraceEnabled) { + delegate.trace(msg().toString()) + } + } + + inline fun trace(t: Throwable?, msg: () -> Any?) { + if (isTraceEnabled) { + delegate.trace(msg().toString(), t) + } + } + + inline fun trace(marker: Marker?, msg: () -> Any?) { + if (isTraceEnabled(marker)) { + delegate.trace(marker, msg().toString()) + } + } + + inline fun trace(marker: Marker?, t: Throwable?, msg: () -> Any?) { + if (isTraceEnabled(marker)) { + delegate.trace(marker, msg().toString(), t) + } + } + + inline fun debug(msg: () -> Any?) { + if (isDebugEnabled) { + delegate.debug(msg().toString()) + } + } + + inline fun debug(t: Throwable?, msg: () -> Any?) { + if (isDebugEnabled) { + delegate.debug(msg().toString(), t) + } + } + + inline fun debug(marker: Marker?, msg: () -> Any?) { + if (isDebugEnabled(marker)) { + delegate.debug(marker, msg().toString()) + } + } + + inline fun debug(marker: Marker?, t: Throwable?, msg: () -> Any?) { + if (isDebugEnabled(marker)) { + delegate.debug(marker, msg().toString(), t) + } + } + + inline fun info(msg: () -> Any?) { + if (isInfoEnabled) { + delegate.info(msg().toString()) + } + } + + inline fun info(t: Throwable?, msg: () -> Any?) { + if (isInfoEnabled) { + delegate.info(msg().toString(), t) + } + } + + inline fun info(marker: Marker?, msg: () -> Any?) { + if (isInfoEnabled(marker)) { + delegate.info(marker, msg().toString()) + } + } + + inline fun info(marker: Marker?, t: Throwable?, msg: () -> Any?) { + if (isInfoEnabled(marker)) { + delegate.info(marker, msg().toString(), t) + } + } + + inline fun warn(msg: () -> Any?) { + if (isWarnEnabled) { + delegate.warn(msg().toString()) + } + } + + inline fun warn(t: Throwable?, msg: () -> Any?) { + if (isWarnEnabled) { + delegate.warn(msg().toString(), t) + } + } + + inline fun warn(marker: Marker?, msg: () -> Any?) { + if (isWarnEnabled(marker)) { + delegate.warn(marker, msg().toString()) + } + } + + inline fun warn(marker: Marker?, t: Throwable?, msg: () -> Any?) { + if (isWarnEnabled(marker)) { + delegate.warn(marker, msg().toString(), t) + } + } + + inline fun error(msg: () -> Any?) { + if (isErrorEnabled) { + delegate.error(msg().toString()) + } + } + + inline fun error(t: Throwable?, msg: () -> Any?) { + if (isErrorEnabled) { + delegate.error(msg().toString(), t) + } + } + + inline fun error(marker: Marker?, msg: () -> Any?) { + if (isErrorEnabled(marker)) { + delegate.error(marker, msg().toString()) + } + } + + inline fun error(marker: Marker?, t: Throwable?, msg: () -> Any?) { + if (isErrorEnabled(marker)) { + delegate.error(marker, msg().toString(), t) + } + } + + @Deprecated(message = "Replace with lazy equivalent", replaceWith = ReplaceWith("trace { msg }")) + fun trace(msg: String?) { + delegate.trace(msg) + } + + @Deprecated(message = "Replace with lazy equivalent", replaceWith = ReplaceWith("trace(t) { msg }")) + fun trace(msg: String?, t: Throwable?) { + delegate.trace(msg, t) + } + + @Deprecated(message = "Replace with lazy equivalent", replaceWith = ReplaceWith("trace(marker) { msg }")) + fun trace(marker: Marker?, msg: String?) { + delegate.trace(marker, msg) + } + + @Deprecated(message = "Replace with lazy equivalent", replaceWith = ReplaceWith("trace(marker, t) { msg }")) + fun trace(marker: Marker?, msg: String?, t: Throwable?) { + delegate.trace(marker, msg, t) + } + + @Deprecated(message = "Replace with lazy equivalent") + fun trace(format: String?, arg: Any?) { + delegate.trace(format, arg) + } + + @Deprecated(message = "Replace with lazy equivalent") + fun trace(format: String?, arg1: Any?, arg2: Any?) { + delegate.trace(format, arg1, arg2) + } + + @Deprecated(message = "Replace with lazy equivalent") + fun trace(format: String?, vararg arguments: Any?) { + delegate.trace(format, *arguments) + } + + @Deprecated(message = "Replace with lazy equivalent") + fun trace(marker: Marker?, format: String?, arg: Any?) { + delegate.trace(marker, format, arg) + } + + @Deprecated(message = "Replace with lazy equivalent") + fun trace(marker: Marker?, format: String?, arg1: Any?, arg2: Any?) { + delegate.trace(marker, format, arg1, arg2) + } + + @Deprecated(message = "Replace with lazy equivalent") + fun trace(marker: Marker?, format: String?, vararg arguments: Any?) { + delegate.trace(marker, format, *arguments) + } + + @Deprecated(message = "Replace with lazy equivalent", replaceWith = ReplaceWith("debug { msg }")) + fun debug(msg: String?) { + delegate.debug(msg) + } + + @Deprecated(message = "Replace with lazy equivalent", replaceWith = ReplaceWith("debug(t) { msg }")) + fun debug(msg: String?, t: Throwable?) { + delegate.debug(msg, t) + } + + @Deprecated(message = "Replace with lazy equivalent", replaceWith = ReplaceWith("debug(marker) { msg }")) + fun debug(marker: Marker?, msg: String?) { + delegate.debug(marker, msg) + } + + @Deprecated(message = "Replace with lazy equivalent", replaceWith = ReplaceWith("debug(marker, t) { msg }")) + fun debug(marker: Marker?, msg: String?, t: Throwable?) { + delegate.debug(marker, msg, t) + } + + @Deprecated(message = "Replace with lazy equivalent") + fun debug(format: String?, arg: Any?) { + delegate.debug(format, arg) + } + + @Deprecated(message = "Replace with lazy equivalent") + fun debug(format: String?, arg1: Any?, arg2: Any?) { + delegate.debug(format, arg1, arg2) + } + + @Deprecated(message = "Replace with lazy equivalent") + fun debug(format: String?, vararg arguments: Any?) { + delegate.debug(format, *arguments) + } + + @Deprecated(message = "Replace with lazy equivalent") + fun debug(marker: Marker?, format: String?, arg: Any?) { + delegate.debug(marker, format, arg) + } + + @Deprecated(message = "Replace with lazy equivalent") + fun debug(marker: Marker?, format: String?, arg1: Any?, arg2: Any?) { + delegate.debug(marker, format, arg1, arg2) + } + + @Deprecated(message = "Replace with lazy equivalent") + fun debug(marker: Marker?, format: String?, vararg arguments: Any?) { + delegate.debug(marker, format, *arguments) + } + + @Deprecated(message = "Replace with lazy equivalent", replaceWith = ReplaceWith("info { msg }")) + fun info(msg: String?) { + delegate.info(msg) + } + + @Deprecated(message = "Replace with lazy equivalent", replaceWith = ReplaceWith("info(t) { msg }")) + fun info(msg: String?, t: Throwable?) { + delegate.info(msg, t) + } + + @Deprecated(message = "Replace with lazy equivalent", replaceWith = ReplaceWith("info(marker) { msg }")) + fun info(marker: Marker?, msg: String?) { + delegate.info(marker, msg) + } + + @Deprecated(message = "Replace with lazy equivalent", replaceWith = ReplaceWith("info(marker, t) { msg }")) + fun info(marker: Marker?, msg: String?, t: Throwable?) { + delegate.info(marker, msg, t) + } + + @Deprecated(message = "Replace with lazy equivalent") + fun info(format: String?, arg: Any?) { + delegate.info(format, arg) + } + + @Deprecated(message = "Replace with lazy equivalent") + fun info(format: String?, arg1: Any?, arg2: Any?) { + delegate.info(format, arg1, arg2) + } + + @Deprecated(message = "Replace with lazy equivalent") + fun info(format: String?, vararg arguments: Any?) { + delegate.info(format, *arguments) + } + + @Deprecated(message = "Replace with lazy equivalent") + fun info(marker: Marker?, format: String?, arg: Any?) { + delegate.info(marker, format, arg) + } + + @Deprecated(message = "Replace with lazy equivalent") + fun info(marker: Marker?, format: String?, arg1: Any?, arg2: Any?) { + delegate.info(marker, format, arg1, arg2) + } + + @Deprecated(message = "Replace with lazy equivalent") + fun info(marker: Marker?, format: String?, vararg arguments: Any?) { + delegate.info(marker, format, *arguments) + } + + @Deprecated(message = "Replace with lazy equivalent", replaceWith = ReplaceWith("warn { msg }")) + fun warn(msg: String?) { + delegate.warn(msg) + } + + @Deprecated(message = "Replace with lazy equivalent", replaceWith = ReplaceWith("warn(t) { msg }")) + fun warn(msg: String?, t: Throwable?) { + delegate.warn(msg, t) + } + + @Deprecated(message = "Replace with lazy equivalent", replaceWith = ReplaceWith("warn(marker) { msg }")) + fun warn(marker: Marker?, msg: String?) { + delegate.warn(marker, msg) + } + + @Deprecated(message = "Replace with lazy equivalent", replaceWith = ReplaceWith("warn(marker, t) { msg }")) + fun warn(marker: Marker?, msg: String?, t: Throwable?) { + delegate.warn(marker, msg, t) + } + + @Deprecated(message = "Replace with lazy equivalent") + fun warn(format: String?, arg: Any?) { + delegate.warn(format, arg) + } + + @Deprecated(message = "Replace with lazy equivalent") + fun warn(format: String?, arg1: Any?, arg2: Any?) { + delegate.warn(format, arg1, arg2) + } + + @Deprecated(message = "Replace with lazy equivalent") + fun warn(format: String?, vararg arguments: Any?) { + delegate.warn(format, *arguments) + } + + @Deprecated(message = "Replace with lazy equivalent") + fun warn(marker: Marker?, format: String?, arg: Any?) { + delegate.warn(marker, format, arg) + } + + @Deprecated(message = "Replace with lazy equivalent") + fun warn(marker: Marker?, format: String?, arg1: Any?, arg2: Any?) { + delegate.warn(marker, format, arg1, arg2) + } + + @Deprecated(message = "Replace with lazy equivalent") + fun warn(marker: Marker?, format: String?, vararg arguments: Any?) { + delegate.warn(marker, format, *arguments) + } + + @Deprecated(message = "Replace with lazy equivalent", replaceWith = ReplaceWith("error { msg }")) + fun error(msg: String?) { + delegate.error(msg) + } + + @Deprecated(message = "Replace with lazy equivalent", replaceWith = ReplaceWith("error(t) { msg }")) + fun error(msg: String?, t: Throwable?) { + delegate.error(msg, t) + } + + @Deprecated(message = "Replace with lazy equivalent", replaceWith = ReplaceWith("error(marker) { msg }")) + fun error(marker: Marker?, msg: String?) { + delegate.error(marker, msg) + } + + @Deprecated(message = "Replace with lazy equivalent", replaceWith = ReplaceWith("error(marker, t) { msg }")) + fun error(marker: Marker?, msg: String?, t: Throwable?) { + delegate.error(marker, msg, t) + } + + @Deprecated(message = "Replace with lazy equivalent") + fun error(format: String?, arg: Any?) { + delegate.error(format, arg) + } + + @Deprecated(message = "Replace with lazy equivalent") + fun error(format: String?, arg1: Any?, arg2: Any?) { + delegate.error(format, arg1, arg2) + } + + @Deprecated(message = "Replace with lazy equivalent") + fun error(format: String?, vararg arguments: Any?) { + delegate.error(format, *arguments) + } + + @Deprecated(message = "Replace with lazy equivalent") + fun error(marker: Marker?, format: String?, arg: Any?) { + delegate.error(marker, format, arg) + } + + @Deprecated(message = "Replace with lazy equivalent") + fun error(marker: Marker?, format: String?, arg1: Any?, arg2: Any?) { + delegate.error(marker, format, arg1, arg2) + } + + @Deprecated(message = "Replace with lazy equivalent") + fun error(marker: Marker?, format: String?, vararg arguments: Any?) { + delegate.error(marker, format, *arguments) + } +} diff --git a/src/commonMain/kotlin/com/github/michaelbull/logging/Logger.kt b/src/commonMain/kotlin/com/github/michaelbull/logging/Logger.kt new file mode 100644 index 0000000..9b8eaa2 --- /dev/null +++ b/src/commonMain/kotlin/com/github/michaelbull/logging/Logger.kt @@ -0,0 +1,76 @@ +package com.github.michaelbull.logging + +expect interface Logger { + + fun getName(): String + + fun isTraceEnabled(): Boolean + fun isTraceEnabled(marker: Marker?): Boolean + + fun isDebugEnabled(): Boolean + fun isDebugEnabled(marker: Marker?): Boolean + + fun isInfoEnabled(): Boolean + fun isInfoEnabled(marker: Marker?): Boolean + + fun isWarnEnabled(): Boolean + fun isWarnEnabled(marker: Marker?): Boolean + + fun isErrorEnabled(): Boolean + fun isErrorEnabled(marker: Marker?): Boolean + + fun trace(msg: String?) + fun trace(msg: String?, t: Throwable?) + fun trace(marker: Marker?, msg: String?) + fun trace(marker: Marker?, msg: String?, t: Throwable?) + fun trace(format: String?, arg: Any?) + fun trace(format: String?, arg1: Any?, arg2: Any?) + fun trace(format: String?, vararg arguments: Any?) + fun trace(marker: Marker?, format: String?, arg: Any?) + fun trace(marker: Marker?, format: String?, arg1: Any?, arg2: Any?) + fun trace(marker: Marker?, format: String?, vararg arguments: Any?) + + fun debug(msg: String?) + fun debug(msg: String?, t: Throwable?) + fun debug(marker: Marker?, msg: String?) + fun debug(marker: Marker?, msg: String?, t: Throwable?) + fun debug(format: String?, arg: Any?) + fun debug(format: String?, arg1: Any?, arg2: Any?) + fun debug(format: String?, vararg arguments: Any?) + fun debug(marker: Marker?, format: String?, arg: Any?) + fun debug(marker: Marker?, format: String?, arg1: Any?, arg2: Any?) + fun debug(marker: Marker?, format: String?, vararg arguments: Any?) + + fun info(msg: String?) + fun info(msg: String?, t: Throwable?) + fun info(marker: Marker?, msg: String?) + fun info(marker: Marker?, msg: String?, t: Throwable?) + fun info(format: String?, arg: Any?) + fun info(format: String?, arg1: Any?, arg2: Any?) + fun info(format: String?, vararg arguments: Any?) + fun info(marker: Marker?, format: String?, arg: Any?) + fun info(marker: Marker?, format: String?, arg1: Any?, arg2: Any?) + fun info(marker: Marker?, format: String?, vararg arguments: Any?) + + fun warn(msg: String?) + fun warn(msg: String?, t: Throwable?) + fun warn(marker: Marker?, msg: String?) + fun warn(marker: Marker?, msg: String?, t: Throwable?) + fun warn(format: String?, arg: Any?) + fun warn(format: String?, arg1: Any?, arg2: Any?) + fun warn(format: String?, vararg arguments: Any?) + fun warn(marker: Marker?, format: String?, arg: Any?) + fun warn(marker: Marker?, format: String?, arg1: Any?, arg2: Any?) + fun warn(marker: Marker?, format: String?, vararg arguments: Any?) + + fun error(msg: String?) + fun error(msg: String?, t: Throwable?) + fun error(marker: Marker?, msg: String?) + fun error(marker: Marker?, msg: String?, t: Throwable?) + fun error(format: String?, arg: Any?) + fun error(format: String?, arg1: Any?, arg2: Any?) + fun error(format: String?, vararg arguments: Any?) + fun error(marker: Marker?, format: String?, arg: Any?) + fun error(marker: Marker?, format: String?, arg1: Any?, arg2: Any?) + fun error(marker: Marker?, format: String?, vararg arguments: Any?) +} diff --git a/src/commonMain/kotlin/com/github/michaelbull/logging/Marker.kt b/src/commonMain/kotlin/com/github/michaelbull/logging/Marker.kt new file mode 100644 index 0000000..f0b6c0a --- /dev/null +++ b/src/commonMain/kotlin/com/github/michaelbull/logging/Marker.kt @@ -0,0 +1,3 @@ +package com.github.michaelbull.logging + +expect interface Marker diff --git a/src/jvmMain/kotlin/com/github/michaelbull/logging/InlineLogger.kt b/src/jvmMain/kotlin/com/github/michaelbull/logging/InlineLogger.kt new file mode 100644 index 0000000..d34e8bc --- /dev/null +++ b/src/jvmMain/kotlin/com/github/michaelbull/logging/InlineLogger.kt @@ -0,0 +1,25 @@ +@file:Suppress("unused") + +package com.github.michaelbull.logging + +import org.slf4j.LoggerFactory +import java.lang.invoke.MethodHandles +import kotlin.reflect.KClass + +@Suppress("NOTHING_TO_INLINE") +actual inline fun InlineLogger(): InlineLogger { + val delegate = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()) + return InlineLogger(delegate) +} + +@Suppress("NOTHING_TO_INLINE") +actual inline fun InlineLogger(name: String): InlineLogger { + val delegate = LoggerFactory.getLogger(name) + return InlineLogger(delegate) +} + +@Suppress("NOTHING_TO_INLINE") +actual inline fun InlineLogger(clazz: KClass): InlineLogger { + val delegate = LoggerFactory.getLogger(clazz.java) + return InlineLogger(delegate) +} diff --git a/src/jvmMain/kotlin/com/github/michaelbull/logging/Logger.kt b/src/jvmMain/kotlin/com/github/michaelbull/logging/Logger.kt new file mode 100644 index 0000000..c191d53 --- /dev/null +++ b/src/jvmMain/kotlin/com/github/michaelbull/logging/Logger.kt @@ -0,0 +1,3 @@ +package com.github.michaelbull.logging + +actual typealias Logger = org.slf4j.Logger diff --git a/src/jvmMain/kotlin/com/github/michaelbull/logging/Marker.kt b/src/jvmMain/kotlin/com/github/michaelbull/logging/Marker.kt new file mode 100644 index 0000000..845dad0 --- /dev/null +++ b/src/jvmMain/kotlin/com/github/michaelbull/logging/Marker.kt @@ -0,0 +1,3 @@ +package com.github.michaelbull.logging + +actual typealias Marker = org.slf4j.Marker