diff --git a/pom.xml b/pom.xml index f5a789c49..c30eec761 100644 --- a/pom.xml +++ b/pom.xml @@ -14,6 +14,7 @@ test-core tinylog-api + tinylog-api-kotlin tinylog-impl jboss-tinylog jcl-tinylog @@ -60,12 +61,17 @@ 3.12.1 1.2.0 8.18 + 3.8.1 1.2 + 0.9.17 1.4.199 1.1 3.3.2.Final 1.21 4.12 + 1.3.21 + 1.0.0 + 1.9.2 2.0.0 1.7.26 @@ -107,6 +113,14 @@ + + + jcenter + JCenter + https://jcenter.bintray.com/ + + + dev @@ -147,11 +161,22 @@ ${h2.version} test + + org.apache.commons + commons-lang3 + ${commons-lang.version} + commons-logging commons-logging ${commons-logging.version} + + io.mockk + mockk + ${mockk.version} + test + junit junit @@ -188,6 +213,17 @@ ${jacoco-maven-plugin.version} test + + org.jetbrains.kotlin + kotlin-reflect + ${kotlin.version} + test + + + org.jetbrains.kotlin + kotlin-stdlib + ${kotlin.version} + org.openjdk.jmh jmh-core @@ -284,6 +320,34 @@ + + org.eclipse.m2e + lifecycle-mapping + ${lifecycle-mapping.version} + + + + + + org.jetbrains.kotlin + kotlin-maven-plugin + [1.0.0,) + + compile + test-compile + + + + + true + true + + + + + + + org.jacoco jacoco-maven-plugin @@ -408,6 +472,19 @@ + + org.jetbrains.dokka + dokka-maven-plugin + ${dokka.version} + + + package + + dokka + + + + org.apache.maven.plugins maven-source-plugin diff --git a/tinylog-api-kotlin/pom.xml b/tinylog-api-kotlin/pom.xml new file mode 100644 index 000000000..43a3409c6 --- /dev/null +++ b/tinylog-api-kotlin/pom.xml @@ -0,0 +1,131 @@ + + + + 4.0.0 + bundle + + + org.tinylog + parent + 2.0-SNAPSHOT + + + tinylog-api-kotlin + tinylog Kotlin API + tinylog logging API for Kotlin + ${website.url} + + + ${git.url} + ${git.connection} + ${git.developerConnection} + + + + + org.apache.commons + commons-lang3 + + + org.jetbrains.kotlin + kotlin-reflect + + + org.jetbrains.kotlin + kotlin-stdlib + + + org.tinylog + tinylog-api + + + io.mockk + mockk + test + + + junit + junit + + + org.assertj + assertj-core + + + org.jacoco + org.jacoco.agent + runtime + + + org.tinylog + test-core + + + + + ${project.basedir}/src/main/kotlin + ${project.basedir}/src/test/kotlin + + + org.jetbrains.kotlin + kotlin-maven-plugin + ${kotlin.version} + + + compile + + compile + + + + test-compile + + test-compile + + + + + + org.jacoco + jacoco-maven-plugin + + + org.apache.felix + maven-bundle-plugin + + + !sun.reflect,* + + + + + org.apache.maven.plugins + maven-jar-plugin + + + org.jetbrains.dokka + dokka-maven-plugin + + + org.apache.maven.plugins + maven-source-plugin + + + org.codehaus.mojo + flatten-maven-plugin + + + org.apache.maven.plugins + maven-gpg-plugin + + + org.sonatype.plugins + nexus-staging-maven-plugin + + + + + diff --git a/tinylog-api-kotlin/src/main/kotlin/org/tinylog/kotlin/Logger.kt b/tinylog-api-kotlin/src/main/kotlin/org/tinylog/kotlin/Logger.kt new file mode 100644 index 000000000..961ece2b2 --- /dev/null +++ b/tinylog-api-kotlin/src/main/kotlin/org/tinylog/kotlin/Logger.kt @@ -0,0 +1,828 @@ +/* + * Copyright 2019 Martin Winandy + * + * Licensed 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.tinylog.kotlin + +import org.tinylog.Level +import org.tinylog.provider.ProviderRegistry; +import java.util.concurrent.ConcurrentHashMap + +/** + * Static logger for issuing log entries. + */ +object Logger { + + private const val STACKTRACE_DEPTH = 2 + + private val provider = ProviderRegistry.getLoggingProvider() + + // @formatter:off + private val MINIMUM_LEVEL_COVERS_TRACE = isCoveredByMinimumLevel(Level.TRACE) + private val MINIMUM_LEVEL_COVERS_DEBUG = isCoveredByMinimumLevel(Level.DEBUG) + private val MINIMUM_LEVEL_COVERS_INFO = isCoveredByMinimumLevel(Level.INFO) + private val MINIMUM_LEVEL_COVERS_WARN = isCoveredByMinimumLevel(Level.WARN) + private val MINIMUM_LEVEL_COVERS_ERROR = isCoveredByMinimumLevel(Level.ERROR) + // @formatter:on + + private val instance = TaggedLogger(null) + private val loggers = ConcurrentHashMap() + + /** + * Gets a tagged logger instance. Tags are case-sensitive. + * + * @param tag + * Tag for logger or `null` for receiving an untagged logger + * @return Logger instance + */ + fun tag(tag: String?): TaggedLogger { + if (tag == null || tag.isEmpty()) { + return instance + } else { + var logger = loggers[tag] + if (logger == null) { + logger = TaggedLogger(tag) + val existing = loggers.putIfAbsent(tag, logger) + return existing ?: logger + } else { + return logger + } + } + } + + /** + * Checks whether log entries at [TRACE][Level.TRACE] level will be output. + * + * @return `true` if [TRACE][Level.TRACE] level is enabled, `false` if disabled + */ + fun isTraceEnabled(): Boolean { + return MINIMUM_LEVEL_COVERS_TRACE && provider.isEnabled(STACKTRACE_DEPTH, null, Level.TRACE) + } + + /** + * Logs a message at [TRACE][Level.TRACE] level. + * + * @param message + * Any other object with meaningful [Any.toString] method + */ + fun trace(message: Any?) { + if (MINIMUM_LEVEL_COVERS_TRACE) { + provider.log(STACKTRACE_DEPTH, null, Level.TRACE, null, message) + } + } + + /** + * Logs a message at [TRACE][Level.TRACE] level. + * + * @param message + * Text message to log + */ + fun trace(message: String) { + if (MINIMUM_LEVEL_COVERS_TRACE) { + provider.log(STACKTRACE_DEPTH, null, Level.TRACE, null, message) + } + } + + /** + * Logs a lazy message at [TRACE][Level.TRACE] level. The message will be only evaluated if the log entry is + * really output. + * + * @param message + * Function that produces the message + */ + fun trace(message: () -> String) { + if (MINIMUM_LEVEL_COVERS_TRACE) { + provider.log(STACKTRACE_DEPTH, null, Level.TRACE, null, message.asSupplier()) + } + } + + /** + * Logs a formatted message at [TRACE][Level.TRACE] level. "{}" placeholders will be replaced by given + * arguments. + * + * @param message + * Formatted text message to log + * @param arguments + * Arguments for formatted text message + */ + fun trace(message: String, vararg arguments: Any?) { + if (MINIMUM_LEVEL_COVERS_TRACE) { + provider.log(STACKTRACE_DEPTH, null, Level.TRACE, null, message, *arguments) + } + } + + /** + * Logs a formatted message at [TRACE][Level.TRACE] level. "{}" placeholders will be replaced by given lazy + * arguments. The arguments will be only evaluated if the log entry is really output. + * + * @param message + * Formatted text message to log + * @param arguments + * Functions that produce the arguments for formatted text message + */ + fun trace(message: String, vararg arguments: () -> Any?) { + if (MINIMUM_LEVEL_COVERS_TRACE) { + provider.log(STACKTRACE_DEPTH, null, Level.TRACE, null, message, *arguments.asSuppliers()) + } + } + + /** + * Logs an exception at [TRACE][Level.TRACE] level. + * + * @param exception + * Caught exception or any other throwable to log + */ + fun trace(exception: Throwable) { + if (MINIMUM_LEVEL_COVERS_TRACE) { + provider.log(STACKTRACE_DEPTH, null, Level.TRACE, exception, null) + } + } + + /** + * Logs an exception with a custom message at [TRACE][Level.TRACE] level. + * + * @param exception + * Caught exception or any other throwable to log + * @param message + * Text message to log + */ + fun trace(exception: Throwable, message: String) { + if (MINIMUM_LEVEL_COVERS_TRACE) { + provider.log(STACKTRACE_DEPTH, null, Level.TRACE, exception, message) + } + } + + /** + * Logs an exception with a custom lazy message at [TRACE][Level.TRACE] level. The message will be only + * evaluated if the log entry is really output. + * + * @param exception + * Caught exception or any other throwable to log + * @param message + * Function that produces the message + */ + fun trace(exception: Throwable, message: () -> String) { + if (MINIMUM_LEVEL_COVERS_TRACE) { + provider.log(STACKTRACE_DEPTH, null, Level.TRACE, exception, message.asSupplier()) + } + } + + /** + * Logs an exception with a formatted custom message at [TRACE][Level.TRACE] level. "{}" placeholders will be + * replaced by given arguments. + * + * @param exception + * Caught exception or any other throwable to log + * @param message + * Formatted text message to log + * @param arguments + * Arguments for formatted text message + */ + fun trace(exception: Throwable, message: String, vararg arguments: Any?) { + if (MINIMUM_LEVEL_COVERS_TRACE) { + provider.log(STACKTRACE_DEPTH, null, Level.TRACE, exception, message, *arguments) + } + } + + /** + * Logs an exception with a formatted message at [TRACE][Level.TRACE] level. "{}" placeholders will be replaced + * by given lazy arguments. The arguments will be only evaluated if the log entry is really output. + * + * @param exception + * Caught exception or any other throwable to log + * @param message + * Formatted text message to log + * @param arguments + * Functions that produce the arguments for formatted text message + */ + fun trace(exception: Throwable, message: String, vararg arguments: () -> Any?) { + if (MINIMUM_LEVEL_COVERS_TRACE) { + provider.log(STACKTRACE_DEPTH, null, Level.TRACE, exception, message, *arguments.asSuppliers()) + } + } + + /** + * Checks whether log entries at [DEBUG][Level.DEBUG] level will be output. + * + * @return `true` if [DEBUG][Level.DEBUG] level is enabled, `false` if disabled + */ + fun isDebugEnabled(): Boolean { + return MINIMUM_LEVEL_COVERS_DEBUG && provider.isEnabled(STACKTRACE_DEPTH, null, Level.DEBUG) + } + + /** + * Logs a message at [DEBUG][Level.DEBUG] level. + * + * @param message + * Any other object with meaningful [Any.toString] method + */ + fun debug(message: Any?) { + if (MINIMUM_LEVEL_COVERS_DEBUG) { + provider.log(STACKTRACE_DEPTH, null, Level.DEBUG, null, message) + } + } + + /** + * Logs a message at [DEBUG][Level.DEBUG] level. + * + * @param message + * Text message to log + */ + fun debug(message: String) { + if (MINIMUM_LEVEL_COVERS_DEBUG) { + provider.log(STACKTRACE_DEPTH, null, Level.DEBUG, null, message) + } + } + + /** + * Logs a lazy message at [DEBUG][Level.DEBUG] level. The message will be only evaluated if the log entry is + * really output. + * + * @param message + * Function that produces the message + */ + fun debug(message: () -> String) { + if (MINIMUM_LEVEL_COVERS_DEBUG) { + provider.log(STACKTRACE_DEPTH, null, Level.DEBUG, null, message.asSupplier()) + } + } + + /** + * Logs a formatted message at [DEBUG][Level.DEBUG] level. "{}" placeholders will be replaced by given + * arguments. + * + * @param message + * Formatted text message to log + * @param arguments + * Arguments for formatted text message + */ + fun debug(message: String, vararg arguments: Any?) { + if (MINIMUM_LEVEL_COVERS_DEBUG) { + provider.log(STACKTRACE_DEPTH, null, Level.DEBUG, null, message, *arguments) + } + } + + /** + * Logs a formatted message at [DEBUG][Level.DEBUG] level. "{}" placeholders will be replaced by given lazy + * arguments. The arguments will be only evaluated if the log entry is really output. + * + * @param message + * Formatted text message to log + * @param arguments + * Functions that produce the arguments for formatted text message + */ + fun debug(message: String, vararg arguments: () -> Any?) { + if (MINIMUM_LEVEL_COVERS_DEBUG) { + provider.log(STACKTRACE_DEPTH, null, Level.DEBUG, null, message, *arguments.asSuppliers()) + } + } + + /** + * Logs an exception at [DEBUG][Level.DEBUG] level. + * + * @param exception + * Caught exception or any other throwable to log + */ + fun debug(exception: Throwable) { + if (MINIMUM_LEVEL_COVERS_DEBUG) { + provider.log(STACKTRACE_DEPTH, null, Level.DEBUG, exception, null) + } + } + + /** + * Logs an exception with a custom message at [DEBUG][Level.DEBUG] level. + * + * @param exception + * Caught exception or any other throwable to log + * @param message + * Text message to log + */ + fun debug(exception: Throwable, message: String) { + if (MINIMUM_LEVEL_COVERS_DEBUG) { + provider.log(STACKTRACE_DEPTH, null, Level.DEBUG, exception, message) + } + } + + /** + * Logs an exception with a custom lazy message at [DEBUG][Level.DEBUG] level. The message will be only + * evaluated if the log entry is really output. + * + * @param exception + * Caught exception or any other throwable to log + * @param message + * Function that produces the message + */ + fun debug(exception: Throwable, message: () -> String) { + if (MINIMUM_LEVEL_COVERS_DEBUG) { + provider.log(STACKTRACE_DEPTH, null, Level.DEBUG, exception, message.asSupplier()) + } + } + + /** + * Logs an exception with a formatted custom message at [DEBUG][Level.DEBUG] level. "{}" placeholders will be + * replaced by given arguments. + * + * @param exception + * Caught exception or any other throwable to log + * @param message + * Formatted text message to log + * @param arguments + * Arguments for formatted text message + */ + fun debug(exception: Throwable, message: String, vararg arguments: Any?) { + if (MINIMUM_LEVEL_COVERS_DEBUG) { + provider.log(STACKTRACE_DEPTH, null, Level.DEBUG, exception, message, *arguments) + } + } + + /** + * Logs an exception with a formatted message at [DEBUG][Level.DEBUG] level. "{}" placeholders will be replaced + * by given lazy arguments. The arguments will be only evaluated if the log entry is really output. + * + * @param exception + * Caught exception or any other throwable to log + * @param message + * Formatted text message to log + * @param arguments + * Functions that produce the arguments for formatted text message + */ + fun debug(exception: Throwable, message: String, vararg arguments: () -> Any?) { + if (MINIMUM_LEVEL_COVERS_DEBUG) { + provider.log(STACKTRACE_DEPTH, null, Level.DEBUG, exception, message, *arguments.asSuppliers()) + } + } + + /** + * Checks whether log entries at [INFO][Level.INFO] level will be output. + * + * @return `true` if [INFO][Level.INFO] level is enabled, `false` if disabled + */ + fun isInfoEnabled(): Boolean { + return MINIMUM_LEVEL_COVERS_INFO && provider.isEnabled(STACKTRACE_DEPTH, null, Level.INFO) + } + + /** + * Logs a message at [INFO][Level.INFO] level. + * + * @param message + * Any other object with meaningful [Any.toString] method + */ + fun info(message: Any?) { + if (MINIMUM_LEVEL_COVERS_INFO) { + provider.log(STACKTRACE_DEPTH, null, Level.INFO, null, message) + } + } + + /** + * Logs a message at [INFO][Level.INFO] level. + * + * @param message + * Text message to log + */ + fun info(message: String) { + if (MINIMUM_LEVEL_COVERS_INFO) { + provider.log(STACKTRACE_DEPTH, null, Level.INFO, null, message) + } + } + + /** + * Logs a lazy message at [INFO][Level.INFO] level. The message will be only evaluated if the log entry is + * really output. + * + * @param message + * Function that produces the message + */ + fun info(message: () -> String) { + if (MINIMUM_LEVEL_COVERS_INFO) { + provider.log(STACKTRACE_DEPTH, null, Level.INFO, null, message.asSupplier()) + } + } + + /** + * Logs a formatted message at [INFO][Level.INFO] level. "{}" placeholders will be replaced by given + * arguments. + * + * @param message + * Formatted text message to log + * @param arguments + * Arguments for formatted text message + */ + fun info(message: String, vararg arguments: Any?) { + if (MINIMUM_LEVEL_COVERS_INFO) { + provider.log(STACKTRACE_DEPTH, null, Level.INFO, null, message, *arguments) + } + } + + /** + * Logs a formatted message at [INFO][Level.INFO] level. "{}" placeholders will be replaced by given lazy + * arguments. The arguments will be only evaluated if the log entry is really output. + * + * @param message + * Formatted text message to log + * @param arguments + * Functions that produce the arguments for formatted text message + */ + fun info(message: String, vararg arguments: () -> Any?) { + if (MINIMUM_LEVEL_COVERS_INFO) { + provider.log(STACKTRACE_DEPTH, null, Level.INFO, null, message, *arguments.asSuppliers()) + } + } + + /** + * Logs an exception at [INFO][Level.INFO] level. + * + * @param exception + * Caught exception or any other throwable to log + */ + fun info(exception: Throwable) { + if (MINIMUM_LEVEL_COVERS_INFO) { + provider.log(STACKTRACE_DEPTH, null, Level.INFO, exception, null) + } + } + + /** + * Logs an exception with a custom message at [INFO][Level.INFO] level. + * + * @param exception + * Caught exception or any other throwable to log + * @param message + * Text message to log + */ + fun info(exception: Throwable, message: String) { + if (MINIMUM_LEVEL_COVERS_INFO) { + provider.log(STACKTRACE_DEPTH, null, Level.INFO, exception, message) + } + } + + /** + * Logs an exception with a custom lazy message at [INFO][Level.INFO] level. The message will be only + * evaluated if the log entry is really output. + * + * @param exception + * Caught exception or any other throwable to log + * @param message + * Function that produces the message + */ + fun info(exception: Throwable, message: () -> String) { + if (MINIMUM_LEVEL_COVERS_INFO) { + provider.log(STACKTRACE_DEPTH, null, Level.INFO, exception, message.asSupplier()) + } + } + + /** + * Logs an exception with a formatted custom message at [INFO][Level.INFO] level. "{}" placeholders will be + * replaced by given arguments. + * + * @param exception + * Caught exception or any other throwable to log + * @param message + * Formatted text message to log + * @param arguments + * Arguments for formatted text message + */ + fun info(exception: Throwable, message: String, vararg arguments: Any?) { + if (MINIMUM_LEVEL_COVERS_INFO) { + provider.log(STACKTRACE_DEPTH, null, Level.INFO, exception, message, *arguments) + } + } + + /** + * Logs an exception with a formatted message at [INFO][Level.INFO] level. "{}" placeholders will be replaced + * by given lazy arguments. The arguments will be only evaluated if the log entry is really output. + * + * @param exception + * Caught exception or any other throwable to log + * @param message + * Formatted text message to log + * @param arguments + * Functions that produce the arguments for formatted text message + */ + fun info(exception: Throwable, message: String, vararg arguments: () -> Any?) { + if (MINIMUM_LEVEL_COVERS_INFO) { + provider.log(STACKTRACE_DEPTH, null, Level.INFO, exception, message, *arguments.asSuppliers()) + } + } + + /** + * Checks whether log entries at [WARN][Level.WARN] level will be output. + * + * @return `true` if [WARN][Level.WARN] level is enabled, `false` if disabled + */ + fun isWarnEnabled(): Boolean { + return MINIMUM_LEVEL_COVERS_WARN && provider.isEnabled(STACKTRACE_DEPTH, null, Level.WARN) + } + + /** + * Logs a message at [WARN][Level.WARN] level. + * + * @param message + * Any other object with meaningful [Any.toString] method + */ + fun warn(message: Any?) { + if (MINIMUM_LEVEL_COVERS_WARN) { + provider.log(STACKTRACE_DEPTH, null, Level.WARN, null, message) + } + } + + /** + * Logs a message at [WARN][Level.WARN] level. + * + * @param message + * Text message to log + */ + fun warn(message: String) { + if (MINIMUM_LEVEL_COVERS_WARN) { + provider.log(STACKTRACE_DEPTH, null, Level.WARN, null, message) + } + } + + /** + * Logs a lazy message at [WARN][Level.WARN] level. The message will be only evaluated if the log entry is + * really output. + * + * @param message + * Function that produces the message + */ + fun warn(message: () -> String) { + if (MINIMUM_LEVEL_COVERS_WARN) { + provider.log(STACKTRACE_DEPTH, null, Level.WARN, null, message.asSupplier()) + } + } + + /** + * Logs a formatted message at [WARN][Level.WARN] level. "{}" placeholders will be replaced by given + * arguments. + * + * @param message + * Formatted text message to log + * @param arguments + * Arguments for formatted text message + */ + fun warn(message: String, vararg arguments: Any?) { + if (MINIMUM_LEVEL_COVERS_WARN) { + provider.log(STACKTRACE_DEPTH, null, Level.WARN, null, message, *arguments) + } + } + + /** + * Logs a formatted message at [WARN][Level.WARN] level. "{}" placeholders will be replaced by given lazy + * arguments. The arguments will be only evaluated if the log entry is really output. + * + * @param message + * Formatted text message to log + * @param arguments + * Functions that produce the arguments for formatted text message + */ + fun warn(message: String, vararg arguments: () -> Any?) { + if (MINIMUM_LEVEL_COVERS_WARN) { + provider.log(STACKTRACE_DEPTH, null, Level.WARN, null, message, *arguments.asSuppliers()) + } + } + + /** + * Logs an exception at [WARN][Level.WARN] level. + * + * @param exception + * Caught exception or any other throwable to log + */ + fun warn(exception: Throwable) { + if (MINIMUM_LEVEL_COVERS_WARN) { + provider.log(STACKTRACE_DEPTH, null, Level.WARN, exception, null) + } + } + + /** + * Logs an exception with a custom message at [WARN][Level.WARN] level. + * + * @param exception + * Caught exception or any other throwable to log + * @param message + * Text message to log + */ + fun warn(exception: Throwable, message: String) { + if (MINIMUM_LEVEL_COVERS_WARN) { + provider.log(STACKTRACE_DEPTH, null, Level.WARN, exception, message) + } + } + + /** + * Logs an exception with a custom lazy message at [WARN][Level.WARN] level. The message will be only + * evaluated if the log entry is really output. + * + * @param exception + * Caught exception or any other throwable to log + * @param message + * Function that produces the message + */ + fun warn(exception: Throwable, message: () -> String) { + if (MINIMUM_LEVEL_COVERS_WARN) { + provider.log(STACKTRACE_DEPTH, null, Level.WARN, exception, message.asSupplier()) + } + } + + /** + * Logs an exception with a formatted custom message at [WARN][Level.WARN] level. "{}" placeholders will be + * replaced by given arguments. + * + * @param exception + * Caught exception or any other throwable to log + * @param message + * Formatted text message to log + * @param arguments + * Arguments for formatted text message + */ + fun warn(exception: Throwable, message: String, vararg arguments: Any?) { + if (MINIMUM_LEVEL_COVERS_WARN) { + provider.log(STACKTRACE_DEPTH, null, Level.WARN, exception, message, *arguments) + } + } + + /** + * Logs an exception with a formatted message at [WARN][Level.WARN] level. "{}" placeholders will be replaced + * by given lazy arguments. The arguments will be only evaluated if the log entry is really output. + * + * @param exception + * Caught exception or any other throwable to log + * @param message + * Formatted text message to log + * @param arguments + * Functions that produce the arguments for formatted text message + */ + fun warn(exception: Throwable, message: String, vararg arguments: () -> Any?) { + if (MINIMUM_LEVEL_COVERS_WARN) { + provider.log(STACKTRACE_DEPTH, null, Level.WARN, exception, message, *arguments.asSuppliers()) + } + } + + /** + * Checks whether log entries at [ERROR][Level.ERROR] level will be output. + * + * @return `true` if [ERROR][Level.ERROR] level is enabled, `false` if disabled + */ + fun isErrorEnabled(): Boolean { + return MINIMUM_LEVEL_COVERS_ERROR && provider.isEnabled(STACKTRACE_DEPTH, null, Level.ERROR) + } + + /** + * Logs a message at [ERROR][Level.ERROR] level. + * + * @param message + * Any other object with meaningful [Any.toString] method + */ + fun error(message: Any?) { + if (MINIMUM_LEVEL_COVERS_ERROR) { + provider.log(STACKTRACE_DEPTH, null, Level.ERROR, null, message) + } + } + + /** + * Logs a message at [ERROR][Level.ERROR] level. + * + * @param message + * Text message to log + */ + fun error(message: String) { + if (MINIMUM_LEVEL_COVERS_ERROR) { + provider.log(STACKTRACE_DEPTH, null, Level.ERROR, null, message) + } + } + + /** + * Logs a lazy message at [ERROR][Level.ERROR] level. The message will be only evaluated if the log entry is + * really output. + * + * @param message + * Function that produces the message + */ + fun error(message: () -> String) { + if (MINIMUM_LEVEL_COVERS_ERROR) { + provider.log(STACKTRACE_DEPTH, null, Level.ERROR, null, message.asSupplier()) + } + } + + /** + * Logs a formatted message at [ERROR][Level.ERROR] level. "{}" placeholders will be replaced by given + * arguments. + * + * @param message + * Formatted text message to log + * @param arguments + * Arguments for formatted text message + */ + fun error(message: String, vararg arguments: Any?) { + if (MINIMUM_LEVEL_COVERS_ERROR) { + provider.log(STACKTRACE_DEPTH, null, Level.ERROR, null, message, *arguments) + } + } + + /** + * Logs a formatted message at [ERROR][Level.ERROR] level. "{}" placeholders will be replaced by given lazy + * arguments. The arguments will be only evaluated if the log entry is really output. + * + * @param message + * Formatted text message to log + * @param arguments + * Functions that produce the arguments for formatted text message + */ + fun error(message: String, vararg arguments: () -> Any?) { + if (MINIMUM_LEVEL_COVERS_ERROR) { + provider.log(STACKTRACE_DEPTH, null, Level.ERROR, null, message, *arguments.asSuppliers()) + } + } + + /** + * Logs an exception at [ERROR][Level.ERROR] level. + * + * @param exception + * Caught exception or any other throwable to log + */ + fun error(exception: Throwable) { + if (MINIMUM_LEVEL_COVERS_ERROR) { + provider.log(STACKTRACE_DEPTH, null, Level.ERROR, exception, null) + } + } + + /** + * Logs an exception with a custom message at [ERROR][Level.ERROR] level. + * + * @param exception + * Caught exception or any other throwable to log + * @param message + * Text message to log + */ + fun error(exception: Throwable, message: String) { + if (MINIMUM_LEVEL_COVERS_ERROR) { + provider.log(STACKTRACE_DEPTH, null, Level.ERROR, exception, message) + } + } + + /** + * Logs an exception with a custom lazy message at [ERROR][Level.ERROR] level. The message will be only + * evaluated if the log entry is really output. + * + * @param exception + * Caught exception or any other throwable to log + * @param message + * Function that produces the message + */ + fun error(exception: Throwable, message: () -> String) { + if (MINIMUM_LEVEL_COVERS_ERROR) { + provider.log(STACKTRACE_DEPTH, null, Level.ERROR, exception, message.asSupplier()) + } + } + + /** + * Logs an exception with a formatted custom message at [ERROR][Level.ERROR] level. "{}" placeholders will be + * replaced by given arguments. + * + * @param exception + * Caught exception or any other throwable to log + * @param message + * Formatted text message to log + * @param arguments + * Arguments for formatted text message + */ + fun error(exception: Throwable, message: String, vararg arguments: Any?) { + if (MINIMUM_LEVEL_COVERS_ERROR) { + provider.log(STACKTRACE_DEPTH, null, Level.ERROR, exception, message, *arguments) + } + } + + /** + * Logs an exception with a formatted message at [ERROR][Level.ERROR] level. "{}" placeholders will be replaced + * by given lazy arguments. The arguments will be only evaluated if the log entry is really output. + * + * @param exception + * Caught exception or any other throwable to log + * @param message + * Formatted text message to log + * @param arguments + * Functions that produce the arguments for formatted text message + */ + fun error(exception: Throwable, message: String, vararg arguments: () -> Any?) { + if (MINIMUM_LEVEL_COVERS_ERROR) { + provider.log(STACKTRACE_DEPTH, null, Level.ERROR, exception, message, *arguments.asSuppliers()) + } + } + + /** + * Checks if a given severity level is covered by the logging provider's minimum level. + * + * @param level + * Severity level to check + * @return `true` if given severity level is covered, otherwise `false` + */ + private fun isCoveredByMinimumLevel(level: Level): Boolean { + return provider.getMinimumLevel(null).ordinal <= level.ordinal + } + +} \ No newline at end of file diff --git a/tinylog-api-kotlin/src/main/kotlin/org/tinylog/kotlin/SupplierUtils.kt b/tinylog-api-kotlin/src/main/kotlin/org/tinylog/kotlin/SupplierUtils.kt new file mode 100644 index 000000000..7f9e40254 --- /dev/null +++ b/tinylog-api-kotlin/src/main/kotlin/org/tinylog/kotlin/SupplierUtils.kt @@ -0,0 +1,30 @@ +/* + * Copyright 2019 Martin Winandy + * + * Licensed 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.tinylog.kotlin + +import org.tinylog.Supplier + +/** + * Converts a function type into a supplier. + * + * @return Function type as supplier + */ +fun (() -> T).asSupplier(): Supplier = Supplier { invoke() } + +/** + * Converts an array of function types into an array of suppliers. + * + * @return Function types as suppliers + */ +fun (Array T>).asSuppliers(): Array> = map { it.asSupplier() }.toTypedArray() diff --git a/tinylog-api-kotlin/src/main/kotlin/org/tinylog/kotlin/TaggedLogger.kt b/tinylog-api-kotlin/src/main/kotlin/org/tinylog/kotlin/TaggedLogger.kt new file mode 100644 index 000000000..045a5e59a --- /dev/null +++ b/tinylog-api-kotlin/src/main/kotlin/org/tinylog/kotlin/TaggedLogger.kt @@ -0,0 +1,807 @@ +/* + * Copyright 2019 Martin Winandy + * + * Licensed 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.tinylog.kotlin + +import org.tinylog.Level +import org.tinylog.provider.ProviderRegistry + +/** + * Logger for issuing tagged log entries. Tagged loggers can be received by calling [Logger.tag]. + * + * @param tag + * Case-sensitive tag for logger instance + * @see Logger.tag + */ +class TaggedLogger(private val tag: String?) { + + private val stackTraceDepth = 2 + private val provider = ProviderRegistry.getLoggingProvider() + + // @formatter:off + private val minimumLevelCoversTrace = isCoveredByMinimumLevel(tag, Level.TRACE) + private val minimumLevelCoversDebug = isCoveredByMinimumLevel(tag, Level.TRACE) + private val minimumLevelCoversInfo = isCoveredByMinimumLevel(tag, Level.TRACE) + private val minimumLevelCoversWarn = isCoveredByMinimumLevel(tag, Level.TRACE) + private val minimumLevelCoversError = isCoveredByMinimumLevel(tag, Level.TRACE) + // @formatter:on + + /** + * Checks whether log entries at [TRACE][Level.TRACE] level will be output. + * + * @return `true` if [TRACE][Level.TRACE] level is enabled, `false` if disabled + */ + fun isTraceEnabled(): Boolean { + return minimumLevelCoversTrace && provider.isEnabled(stackTraceDepth, tag, Level.TRACE) + } + + /** + * Logs a message at [TRACE][Level.TRACE] level. + * + * @param message + * Any other object with meaningful [Any.toString] method + */ + fun trace(message: Any?) { + if (minimumLevelCoversTrace) { + provider.log(stackTraceDepth, tag, Level.TRACE, null, message) + } + } + + /** + * Logs a message at [TRACE][Level.TRACE] level. + * + * @param message + * Text message to log + */ + fun trace(message: String) { + if (minimumLevelCoversTrace) { + provider.log(stackTraceDepth, tag, Level.TRACE, null, message) + } + } + + /** + * Logs a lazy message at [TRACE][Level.TRACE] level. The message will be only evaluated if the log entry is + * really output. + * + * @param message + * Function that produces the message + */ + fun trace(message: () -> String) { + if (minimumLevelCoversTrace) { + provider.log(stackTraceDepth, tag, Level.TRACE, null, message.asSupplier()) + } + } + + /** + * Logs a formatted message at [TRACE][Level.TRACE] level. "{}" placeholders will be replaced by given + * arguments. + * + * @param message + * Formatted text message to log + * @param arguments + * Arguments for formatted text message + */ + fun trace(message: String, vararg arguments: Any?) { + if (minimumLevelCoversTrace) { + provider.log(stackTraceDepth, tag, Level.TRACE, null, message, *arguments) + } + } + + /** + * Logs a formatted message at [TRACE][Level.TRACE] level. "{}" placeholders will be replaced by given lazy + * arguments. The arguments will be only evaluated if the log entry is really output. + * + * @param message + * Formatted text message to log + * @param arguments + * Functions that produce the arguments for formatted text message + */ + fun trace(message: String, vararg arguments: () -> Any?) { + if (minimumLevelCoversTrace) { + provider.log(stackTraceDepth, tag, Level.TRACE, null, message, *arguments.asSuppliers()) + } + } + + /** + * Logs an exception at [TRACE][Level.TRACE] level. + * + * @param exception + * Caught exception or any other throwable to log + */ + fun trace(exception: Throwable) { + if (minimumLevelCoversTrace) { + provider.log(stackTraceDepth, tag, Level.TRACE, exception, null) + } + } + + /** + * Logs an exception with a custom message at [TRACE][Level.TRACE] level. + * + * @param exception + * Caught exception or any other throwable to log + * @param message + * Text message to log + */ + fun trace(exception: Throwable, message: String) { + if (minimumLevelCoversTrace) { + provider.log(stackTraceDepth, tag, Level.TRACE, exception, message) + } + } + + /** + * Logs an exception with a custom lazy message at [TRACE][Level.TRACE] level. The message will be only + * evaluated if the log entry is really output. + * + * @param exception + * Caught exception or any other throwable to log + * @param message + * Function that produces the message + */ + fun trace(exception: Throwable, message: () -> String) { + if (minimumLevelCoversTrace) { + provider.log(stackTraceDepth, tag, Level.TRACE, exception, message.asSupplier()) + } + } + + /** + * Logs an exception with a formatted custom message at [TRACE][Level.TRACE] level. "{}" placeholders will be + * replaced by given arguments. + * + * @param exception + * Caught exception or any other throwable to log + * @param message + * Formatted text message to log + * @param arguments + * Arguments for formatted text message + */ + fun trace(exception: Throwable, message: String, vararg arguments: Any?) { + if (minimumLevelCoversTrace) { + provider.log(stackTraceDepth, tag, Level.TRACE, exception, message, *arguments) + } + } + + /** + * Logs an exception with a formatted message at [TRACE][Level.TRACE] level. "{}" placeholders will be replaced + * by given lazy arguments. The arguments will be only evaluated if the log entry is really output. + * + * @param exception + * Caught exception or any other throwable to log + * @param message + * Formatted text message to log + * @param arguments + * Functions that produce the arguments for formatted text message + */ + fun trace(exception: Throwable, message: String, vararg arguments: () -> Any?) { + if (minimumLevelCoversTrace) { + provider.log(stackTraceDepth, tag, Level.TRACE, exception, message, *arguments.asSuppliers()) + } + } + + /** + * Checks whether log entries at [DEBUG][Level.DEBUG] level will be output. + * + * @return `true` if [DEBUG][Level.DEBUG] level is enabled, `false` if disabled + */ + fun isDebugEnabled(): Boolean { + return minimumLevelCoversDebug && provider.isEnabled(stackTraceDepth, tag, Level.DEBUG) + } + + /** + * Logs a message at [DEBUG][Level.DEBUG] level. + * + * @param message + * Any other object with meaningful [Any.toString] method + */ + fun debug(message: Any?) { + if (minimumLevelCoversDebug) { + provider.log(stackTraceDepth, tag, Level.DEBUG, null, message) + } + } + + /** + * Logs a message at [DEBUG][Level.DEBUG] level. + * + * @param message + * Text message to log + */ + fun debug(message: String) { + if (minimumLevelCoversDebug) { + provider.log(stackTraceDepth, tag, Level.DEBUG, null, message) + } + } + + /** + * Logs a lazy message at [DEBUG][Level.DEBUG] level. The message will be only evaluated if the log entry is + * really output. + * + * @param message + * Function that produces the message + */ + fun debug(message: () -> String) { + if (minimumLevelCoversDebug) { + provider.log(stackTraceDepth, tag, Level.DEBUG, null, message.asSupplier()) + } + } + + /** + * Logs a formatted message at [DEBUG][Level.DEBUG] level. "{}" placeholders will be replaced by given + * arguments. + * + * @param message + * Formatted text message to log + * @param arguments + * Arguments for formatted text message + */ + fun debug(message: String, vararg arguments: Any?) { + if (minimumLevelCoversDebug) { + provider.log(stackTraceDepth, tag, Level.DEBUG, null, message, *arguments) + } + } + + /** + * Logs a formatted message at [DEBUG][Level.DEBUG] level. "{}" placeholders will be replaced by given lazy + * arguments. The arguments will be only evaluated if the log entry is really output. + * + * @param message + * Formatted text message to log + * @param arguments + * Functions that produce the arguments for formatted text message + */ + fun debug(message: String, vararg arguments: () -> Any?) { + if (minimumLevelCoversDebug) { + provider.log(stackTraceDepth, tag, Level.DEBUG, null, message, *arguments.asSuppliers()) + } + } + + /** + * Logs an exception at [DEBUG][Level.DEBUG] level. + * + * @param exception + * Caught exception or any other throwable to log + */ + fun debug(exception: Throwable) { + if (minimumLevelCoversDebug) { + provider.log(stackTraceDepth, tag, Level.DEBUG, exception, null) + } + } + + /** + * Logs an exception with a custom message at [DEBUG][Level.DEBUG] level. + * + * @param exception + * Caught exception or any other throwable to log + * @param message + * Text message to log + */ + fun debug(exception: Throwable, message: String) { + if (minimumLevelCoversDebug) { + provider.log(stackTraceDepth, tag, Level.DEBUG, exception, message) + } + } + + /** + * Logs an exception with a custom lazy message at [DEBUG][Level.DEBUG] level. The message will be only + * evaluated if the log entry is really output. + * + * @param exception + * Caught exception or any other throwable to log + * @param message + * Function that produces the message + */ + fun debug(exception: Throwable, message: () -> String) { + if (minimumLevelCoversDebug) { + provider.log(stackTraceDepth, tag, Level.DEBUG, exception, message.asSupplier()) + } + } + + /** + * Logs an exception with a formatted custom message at [DEBUG][Level.DEBUG] level. "{}" placeholders will be + * replaced by given arguments. + * + * @param exception + * Caught exception or any other throwable to log + * @param message + * Formatted text message to log + * @param arguments + * Arguments for formatted text message + */ + fun debug(exception: Throwable, message: String, vararg arguments: Any?) { + if (minimumLevelCoversDebug) { + provider.log(stackTraceDepth, tag, Level.DEBUG, exception, message, *arguments) + } + } + + /** + * Logs an exception with a formatted message at [DEBUG][Level.DEBUG] level. "{}" placeholders will be replaced + * by given lazy arguments. The arguments will be only evaluated if the log entry is really output. + * + * @param exception + * Caught exception or any other throwable to log + * @param message + * Formatted text message to log + * @param arguments + * Functions that produce the arguments for formatted text message + */ + fun debug(exception: Throwable, message: String, vararg arguments: () -> Any?) { + if (minimumLevelCoversDebug) { + provider.log(stackTraceDepth, tag, Level.DEBUG, exception, message, *arguments.asSuppliers()) + } + } + + /** + * Checks whether log entries at [INFO][Level.INFO] level will be output. + * + * @return `true` if [INFO][Level.INFO] level is enabled, `false` if disabled + */ + fun isInfoEnabled(): Boolean { + return minimumLevelCoversInfo && provider.isEnabled(stackTraceDepth, tag, Level.INFO) + } + + /** + * Logs a message at [INFO][Level.INFO] level. + * + * @param message + * Any other object with meaningful [Any.toString] method + */ + fun info(message: Any?) { + if (minimumLevelCoversInfo) { + provider.log(stackTraceDepth, tag, Level.INFO, null, message) + } + } + + /** + * Logs a message at [INFO][Level.INFO] level. + * + * @param message + * Text message to log + */ + fun info(message: String) { + if (minimumLevelCoversInfo) { + provider.log(stackTraceDepth, tag, Level.INFO, null, message) + } + } + + /** + * Logs a lazy message at [INFO][Level.INFO] level. The message will be only evaluated if the log entry is + * really output. + * + * @param message + * Function that produces the message + */ + fun info(message: () -> String) { + if (minimumLevelCoversInfo) { + provider.log(stackTraceDepth, tag, Level.INFO, null, message.asSupplier()) + } + } + + /** + * Logs a formatted message at [INFO][Level.INFO] level. "{}" placeholders will be replaced by given + * arguments. + * + * @param message + * Formatted text message to log + * @param arguments + * Arguments for formatted text message + */ + fun info(message: String, vararg arguments: Any?) { + if (minimumLevelCoversInfo) { + provider.log(stackTraceDepth, tag, Level.INFO, null, message, *arguments) + } + } + + /** + * Logs a formatted message at [INFO][Level.INFO] level. "{}" placeholders will be replaced by given lazy + * arguments. The arguments will be only evaluated if the log entry is really output. + * + * @param message + * Formatted text message to log + * @param arguments + * Functions that produce the arguments for formatted text message + */ + fun info(message: String, vararg arguments: () -> Any?) { + if (minimumLevelCoversInfo) { + provider.log(stackTraceDepth, tag, Level.INFO, null, message, *arguments.asSuppliers()) + } + } + + /** + * Logs an exception at [INFO][Level.INFO] level. + * + * @param exception + * Caught exception or any other throwable to log + */ + fun info(exception: Throwable) { + if (minimumLevelCoversInfo) { + provider.log(stackTraceDepth, tag, Level.INFO, exception, null) + } + } + + /** + * Logs an exception with a custom message at [INFO][Level.INFO] level. + * + * @param exception + * Caught exception or any other throwable to log + * @param message + * Text message to log + */ + fun info(exception: Throwable, message: String) { + if (minimumLevelCoversInfo) { + provider.log(stackTraceDepth, tag, Level.INFO, exception, message) + } + } + + /** + * Logs an exception with a custom lazy message at [INFO][Level.INFO] level. The message will be only + * evaluated if the log entry is really output. + * + * @param exception + * Caught exception or any other throwable to log + * @param message + * Function that produces the message + */ + fun info(exception: Throwable, message: () -> String) { + if (minimumLevelCoversInfo) { + provider.log(stackTraceDepth, tag, Level.INFO, exception, message.asSupplier()) + } + } + + /** + * Logs an exception with a formatted custom message at [INFO][Level.INFO] level. "{}" placeholders will be + * replaced by given arguments. + * + * @param exception + * Caught exception or any other throwable to log + * @param message + * Formatted text message to log + * @param arguments + * Arguments for formatted text message + */ + fun info(exception: Throwable, message: String, vararg arguments: Any?) { + if (minimumLevelCoversInfo) { + provider.log(stackTraceDepth, tag, Level.INFO, exception, message, *arguments) + } + } + + /** + * Logs an exception with a formatted message at [INFO][Level.INFO] level. "{}" placeholders will be replaced + * by given lazy arguments. The arguments will be only evaluated if the log entry is really output. + * + * @param exception + * Caught exception or any other throwable to log + * @param message + * Formatted text message to log + * @param arguments + * Functions that produce the arguments for formatted text message + */ + fun info(exception: Throwable, message: String, vararg arguments: () -> Any?) { + if (minimumLevelCoversInfo) { + provider.log(stackTraceDepth, tag, Level.INFO, exception, message, *arguments.asSuppliers()) + } + } + + /** + * Checks whether log entries at [WARN][Level.WARN] level will be output. + * + * @return `true` if [WARN][Level.WARN] level is enabled, `false` if disabled + */ + fun isWarnEnabled(): Boolean { + return minimumLevelCoversWarn && provider.isEnabled(stackTraceDepth, tag, Level.WARN) + } + + /** + * Logs a message at [WARN][Level.WARN] level. + * + * @param message + * Any other object with meaningful [Any.toString] method + */ + fun warn(message: Any?) { + if (minimumLevelCoversWarn) { + provider.log(stackTraceDepth, tag, Level.WARN, null, message) + } + } + + /** + * Logs a message at [WARN][Level.WARN] level. + * + * @param message + * Text message to log + */ + fun warn(message: String) { + if (minimumLevelCoversWarn) { + provider.log(stackTraceDepth, tag, Level.WARN, null, message) + } + } + + /** + * Logs a lazy message at [WARN][Level.WARN] level. The message will be only evaluated if the log entry is + * really output. + * + * @param message + * Function that produces the message + */ + fun warn(message: () -> String) { + if (minimumLevelCoversWarn) { + provider.log(stackTraceDepth, tag, Level.WARN, null, message.asSupplier()) + } + } + + /** + * Logs a formatted message at [WARN][Level.WARN] level. "{}" placeholders will be replaced by given + * arguments. + * + * @param message + * Formatted text message to log + * @param arguments + * Arguments for formatted text message + */ + fun warn(message: String, vararg arguments: Any?) { + if (minimumLevelCoversWarn) { + provider.log(stackTraceDepth, tag, Level.WARN, null, message, *arguments) + } + } + + /** + * Logs a formatted message at [WARN][Level.WARN] level. "{}" placeholders will be replaced by given lazy + * arguments. The arguments will be only evaluated if the log entry is really output. + * + * @param message + * Formatted text message to log + * @param arguments + * Functions that produce the arguments for formatted text message + */ + fun warn(message: String, vararg arguments: () -> Any?) { + if (minimumLevelCoversWarn) { + provider.log(stackTraceDepth, tag, Level.WARN, null, message, *arguments.asSuppliers()) + } + } + + /** + * Logs an exception at [WARN][Level.WARN] level. + * + * @param exception + * Caught exception or any other throwable to log + */ + fun warn(exception: Throwable) { + if (minimumLevelCoversWarn) { + provider.log(stackTraceDepth, tag, Level.WARN, exception, null) + } + } + + /** + * Logs an exception with a custom message at [WARN][Level.WARN] level. + * + * @param exception + * Caught exception or any other throwable to log + * @param message + * Text message to log + */ + fun warn(exception: Throwable, message: String) { + if (minimumLevelCoversWarn) { + provider.log(stackTraceDepth, tag, Level.WARN, exception, message) + } + } + + /** + * Logs an exception with a custom lazy message at [WARN][Level.WARN] level. The message will be only + * evaluated if the log entry is really output. + * + * @param exception + * Caught exception or any other throwable to log + * @param message + * Function that produces the message + */ + fun warn(exception: Throwable, message: () -> String) { + if (minimumLevelCoversWarn) { + provider.log(stackTraceDepth, tag, Level.WARN, exception, message.asSupplier()) + } + } + + /** + * Logs an exception with a formatted custom message at [WARN][Level.WARN] level. "{}" placeholders will be + * replaced by given arguments. + * + * @param exception + * Caught exception or any other throwable to log + * @param message + * Formatted text message to log + * @param arguments + * Arguments for formatted text message + */ + fun warn(exception: Throwable, message: String, vararg arguments: Any?) { + if (minimumLevelCoversWarn) { + provider.log(stackTraceDepth, tag, Level.WARN, exception, message, *arguments) + } + } + + /** + * Logs an exception with a formatted message at [WARN][Level.WARN] level. "{}" placeholders will be replaced + * by given lazy arguments. The arguments will be only evaluated if the log entry is really output. + * + * @param exception + * Caught exception or any other throwable to log + * @param message + * Formatted text message to log + * @param arguments + * Functions that produce the arguments for formatted text message + */ + fun warn(exception: Throwable, message: String, vararg arguments: () -> Any?) { + if (minimumLevelCoversWarn) { + provider.log(stackTraceDepth, tag, Level.WARN, exception, message, *arguments.asSuppliers()) + } + } + + /** + * Checks whether log entries at [ERROR][Level.ERROR] level will be output. + * + * @return `true` if [ERROR][Level.ERROR] level is enabled, `false` if disabled + */ + fun isErrorEnabled(): Boolean { + return minimumLevelCoversError && provider.isEnabled(stackTraceDepth, tag, Level.ERROR) + } + + /** + * Logs a message at [ERROR][Level.ERROR] level. + * + * @param message + * Any other object with meaningful [Any.toString] method + */ + fun error(message: Any?) { + if (minimumLevelCoversError) { + provider.log(stackTraceDepth, tag, Level.ERROR, null, message) + } + } + + /** + * Logs a message at [ERROR][Level.ERROR] level. + * + * @param message + * Text message to log + */ + fun error(message: String) { + if (minimumLevelCoversError) { + provider.log(stackTraceDepth, tag, Level.ERROR, null, message) + } + } + + /** + * Logs a lazy message at [ERROR][Level.ERROR] level. The message will be only evaluated if the log entry is + * really output. + * + * @param message + * Function that produces the message + */ + fun error(message: () -> String) { + if (minimumLevelCoversError) { + provider.log(stackTraceDepth, tag, Level.ERROR, null, message.asSupplier()) + } + } + + /** + * Logs a formatted message at [ERROR][Level.ERROR] level. "{}" placeholders will be replaced by given + * arguments. + * + * @param message + * Formatted text message to log + * @param arguments + * Arguments for formatted text message + */ + fun error(message: String, vararg arguments: Any?) { + if (minimumLevelCoversError) { + provider.log(stackTraceDepth, tag, Level.ERROR, null, message, *arguments) + } + } + + /** + * Logs a formatted message at [ERROR][Level.ERROR] level. "{}" placeholders will be replaced by given lazy + * arguments. The arguments will be only evaluated if the log entry is really output. + * + * @param message + * Formatted text message to log + * @param arguments + * Functions that produce the arguments for formatted text message + */ + fun error(message: String, vararg arguments: () -> Any?) { + if (minimumLevelCoversError) { + provider.log(stackTraceDepth, tag, Level.ERROR, null, message, *arguments.asSuppliers()) + } + } + + /** + * Logs an exception at [ERROR][Level.ERROR] level. + * + * @param exception + * Caught exception or any other throwable to log + */ + fun error(exception: Throwable) { + if (minimumLevelCoversError) { + provider.log(stackTraceDepth, tag, Level.ERROR, exception, null) + } + } + + /** + * Logs an exception with a custom message at [ERROR][Level.ERROR] level. + * + * @param exception + * Caught exception or any other throwable to log + * @param message + * Text message to log + */ + fun error(exception: Throwable, message: String) { + if (minimumLevelCoversError) { + provider.log(stackTraceDepth, tag, Level.ERROR, exception, message) + } + } + + /** + * Logs an exception with a custom lazy message at [ERROR][Level.ERROR] level. The message will be only + * evaluated if the log entry is really output. + * + * @param exception + * Caught exception or any other throwable to log + * @param message + * Function that produces the message + */ + fun error(exception: Throwable, message: () -> String) { + if (minimumLevelCoversError) { + provider.log(stackTraceDepth, tag, Level.ERROR, exception, message.asSupplier()) + } + } + + /** + * Logs an exception with a formatted custom message at [ERROR][Level.ERROR] level. "{}" placeholders will be + * replaced by given arguments. + * + * @param exception + * Caught exception or any other throwable to log + * @param message + * Formatted text message to log + * @param arguments + * Arguments for formatted text message + */ + fun error(exception: Throwable, message: String, vararg arguments: Any?) { + if (minimumLevelCoversError) { + provider.log(stackTraceDepth, tag, Level.ERROR, exception, message, *arguments) + } + } + + /** + * Logs an exception with a formatted message at [ERROR][Level.ERROR] level. "{}" placeholders will be replaced + * by given lazy arguments. The arguments will be only evaluated if the log entry is really output. + * + * @param exception + * Caught exception or any other throwable to log + * @param message + * Formatted text message to log + * @param arguments + * Functions that produce the arguments for formatted text message + */ + fun error(exception: Throwable, message: String, vararg arguments: () -> Any?) { + if (minimumLevelCoversError) { + provider.log(stackTraceDepth, tag, Level.ERROR, exception, message, *arguments.asSuppliers()) + } + } + + /** + * Checks if a given tag and severity level is covered by the logging provider's minimum level. + * + * @param tag + * Tag to check + * @param level + * Severity level to check + * @return `true` if given severity level is covered, otherwise `false` + */ + private fun isCoveredByMinimumLevel(tag: String?, level: Level): Boolean { + return provider.getMinimumLevel(tag).ordinal <= level.ordinal + } + +} \ No newline at end of file diff --git a/tinylog-api-kotlin/src/test/kotlin/org/tinylog/kotlin/LoggerTest.kt b/tinylog-api-kotlin/src/test/kotlin/org/tinylog/kotlin/LoggerTest.kt new file mode 100644 index 000000000..be6f4269e --- /dev/null +++ b/tinylog-api-kotlin/src/test/kotlin/org/tinylog/kotlin/LoggerTest.kt @@ -0,0 +1,1009 @@ +/* + * Copyright 2019 Martin Winandy + * + * Licensed 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.tinylog.kotlin + +import io.mockk.every +import io.mockk.mockk +import io.mockk.verify +import java.util.ArrayList + +import org.junit.experimental.runners.Enclosed +import org.junit.runner.RunWith +import org.junit.runners.Parameterized +import org.junit.runners.Parameterized.Parameters +import org.tinylog.Level +import org.tinylog.provider.LoggingProvider +import org.tinylog.provider.ProviderRegistry +import org.tinylog.rules.SystemStreamCollector + +import org.assertj.core.api.Assertions.assertThat +import org.junit.* +import org.tinylog.Supplier + +/** + * Tests for [Logger]. + */ +@RunWith(Enclosed::class) +class LoggerTest { + + /** + * Tests for logging methods. + * + * @param level + * Actual severity level under test + * @param traceEnabled + * Determines if [TRACE][Level.TRACE] level is enabled + * @param debugEnabled + * Determines if [DEBUG][Level.DEBUG] level is enabled + * @param infoEnabled + * Determines if [INFO][Level.INFO] level is enabled + * @param warnEnabled + * Determines if [WARN][Level.WARN] level is enabled + * @param errorEnabled + * Determines if [ERROR][Level.ERROR] level is enabled + */ + @RunWith(Parameterized::class) + class Logging(private val level: Level, private val traceEnabled: Boolean, private val debugEnabled: Boolean, + private val infoEnabled: Boolean, private val warnEnabled: Boolean, private val errorEnabled: Boolean) { + + companion object { + + /** + * Returns for all severity levels which severity levels are enabled. + * + * @return Each object array contains the severity level itself and five booleans for [TRACE][Level.TRACE] + * ... [ERROR][Level.ERROR] to determine whether these severity levels are enabled + */ + @JvmStatic + @Parameters(name = "{0}") + fun getLevels(): Collection> { + val levels = ArrayList>() + + // @formatter:off + levels.add(arrayOf(Level.TRACE, true, true, true, true, true)) + levels.add(arrayOf(Level.DEBUG, false, true, true, true, true)) + levels.add(arrayOf(Level.INFO, false, false, true, true, true)) + levels.add(arrayOf(Level.WARN, false, false, false, true, true)) + levels.add(arrayOf(Level.ERROR, false, false, false, false, true)) + levels.add(arrayOf(Level.OFF, false, false, false, false, false)) + // @formatter:on + + return levels + } + + /** + * Resets the logging provider and all overridden fields in [Logger]. + */ + @JvmStatic + @AfterClass + fun resetLoggingProvider() { + Whitebox.setProperty(Logger, LoggingProvider::class, ProviderRegistry.getLoggingProvider()) + Whitebox.setProperty(Logger, "MINIMUM_LEVEL_COVERS_TRACE", isCoveredByMinimumLevel(Level.TRACE)) + Whitebox.setProperty(Logger, "MINIMUM_LEVEL_COVERS_DEBUG", isCoveredByMinimumLevel(Level.DEBUG)) + Whitebox.setProperty(Logger, "MINIMUM_LEVEL_COVERS_INFO", isCoveredByMinimumLevel(Level.INFO)) + Whitebox.setProperty(Logger, "MINIMUM_LEVEL_COVERS_WARN", isCoveredByMinimumLevel(Level.WARN)) + Whitebox.setProperty(Logger, "MINIMUM_LEVEL_COVERS_ERROR", isCoveredByMinimumLevel(Level.ERROR)) + } + + /** + * Invokes the private method [Logger.isCoveredByMinimumLevel]. + * + * @param level + * Severity level to check + * @return `true` if given severity level is covered, otherwise `false` + */ + private fun isCoveredByMinimumLevel(level: Level): Boolean { + return Whitebox.callMethod(Logger, "isCoveredByMinimumLevel", level) as Boolean + } + + } + + /** + * Redirects and collects system output streams. + */ + @JvmField + @Rule + public val systemStream = SystemStreamCollector(false) + + private val loggingProvider = mockk() + + /** + * Applies the mocked logging provider and overrides all depending fields. + */ + @Before + fun applyLoggingProvider() { + every { loggingProvider.getMinimumLevel(null) } returns level + + every { loggingProvider.isEnabled(any(), null, Level.TRACE) } returns traceEnabled + every { loggingProvider.isEnabled(any(), null, Level.DEBUG) } returns debugEnabled + every { loggingProvider.isEnabled(any(), null, Level.INFO) } returns infoEnabled + every { loggingProvider.isEnabled(any(), null, Level.WARN) } returns warnEnabled + every { loggingProvider.isEnabled(any(), null, Level.ERROR) } returns errorEnabled + + every { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } returns Unit + every { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } returns Unit + + Whitebox.setProperty(Logger, LoggingProvider::class, loggingProvider) + Whitebox.setProperty(Logger, "MINIMUM_LEVEL_COVERS_TRACE", traceEnabled) + Whitebox.setProperty(Logger, "MINIMUM_LEVEL_COVERS_DEBUG", debugEnabled) + Whitebox.setProperty(Logger, "MINIMUM_LEVEL_COVERS_INFO", infoEnabled) + Whitebox.setProperty(Logger, "MINIMUM_LEVEL_COVERS_WARN", warnEnabled) + Whitebox.setProperty(Logger, "MINIMUM_LEVEL_COVERS_ERROR", errorEnabled) + } + + /** + * Verifies evaluating whether a specific severity level is covered by the minimum severity level. + */ + @Test + fun coveredByMinimumLevel() { + assertThat(isCoveredByMinimumLevel(Level.TRACE)).isEqualTo(traceEnabled) + assertThat(isCoveredByMinimumLevel(Level.DEBUG)).isEqualTo(debugEnabled) + assertThat(isCoveredByMinimumLevel(Level.INFO)).isEqualTo(infoEnabled) + assertThat(isCoveredByMinimumLevel(Level.WARN)).isEqualTo(warnEnabled) + assertThat(isCoveredByMinimumLevel(Level.ERROR)).isEqualTo(errorEnabled) + } + + /** + * Verifies evaluating whether [TRACE][Level.TRACE] level is enabled. + */ + @Test + fun isTraceEnabled() { + assertThat(Logger.isTraceEnabled()).isEqualTo(traceEnabled) + } + + /** + * Verifies that an object will be logged correctly at [TRACE][Level.TRACE] level. + */ + @Test + fun traceObject() { + Logger.trace(42) + + if (traceEnabled) { + verify(exactly = 1) { loggingProvider.log(2, null, Level.TRACE, null, 42) } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that a plain text will be logged correctly at [TRACE][Level.TRACE] level. + */ + @Test + fun traceString() { + Logger.trace("Hello World!") + + if (traceEnabled) { + verify(exactly = 1) { loggingProvider.log(2, null, Level.TRACE, null, "Hello World!") } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that a lazy message supplier will be logged correctly at [TRACE][Level.TRACE] level. + */ + @Test + fun traceLazyMessage() { + Logger.trace{ "Hello World!" } + + if (traceEnabled) { + verify(exactly = 1) { loggingProvider.log(2, null, Level.TRACE, null, match(provide("Hello World!"))) } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that a formatted text message will be logged correctly at [TRACE][Level.TRACE] level. + */ + @Test + fun traceMessageAndArguments() { + Logger.trace("Hello {}!", "World") + + if (traceEnabled) { + verify(exactly = 1) { loggingProvider.log(2, null, Level.TRACE, null, "Hello {}!", "World") } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that a formatted text message with lazy argument suppliers will be logged correctly at + * [TRACE][Level.TRACE] level. + */ + @Test + fun traceMessageAndLazyArguments() { + Logger.trace("The number is {}", { 42 }) + + if (traceEnabled) { + verify(exactly = 1) { loggingProvider.log(2, null, Level.TRACE, null, "The number is {}", match(provide(42))) } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that an exception will be logged correctly at [TRACE][Level.TRACE] level. + */ + @Test + fun traceException() { + val exception = NullPointerException() + + Logger.trace(exception) + + if (traceEnabled) { + verify(exactly = 1) { loggingProvider.log(2, null, Level.TRACE, exception,null) } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that an exception with a custom message will be logged correctly at [TRACE][Level.TRACE] level. + */ + @Test + fun traceExceptionWithMessage() { + val exception = NullPointerException() + + Logger.trace(exception, "Hello World!") + + if (traceEnabled) { + verify(exactly = 1) { loggingProvider.log(2, null, Level.TRACE, exception,"Hello World!") } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that an exception with a custom lazy message supplier will be logged correctly at [ TRACE][Level.TRACE] level. + */ + @Test + fun traceExceptionWithLazyMessage() { + val exception = NullPointerException() + + Logger.trace(exception) { "Hello World!" } + + if (traceEnabled) { + verify(exactly = 1) { loggingProvider.log(2, null, Level.TRACE, exception, match(provide("Hello World!"))) } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that an exception with a formatted custom message will be logged correctly at [TRACE][Level.TRACE] level. + */ + @Test + fun traceExceptionWithMessageAndArguments() { + val exception = NullPointerException() + + Logger.trace(exception, "Hello {}!", "World") + + if (traceEnabled) { + verify(exactly = 1) { loggingProvider.log(2, null, Level.TRACE, exception, "Hello {}!", "World") } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that an exception with a formatted custom message and lazy argument suppliers will be logged + * correctly at [TRACE][Level.TRACE] level. + */ + @Test + fun traceExceptionWithMessageAndLazyArguments() { + val exception = NullPointerException() + + Logger.trace(exception, "The number is {}", { 42 }) + + if (traceEnabled) { + verify(exactly = 1) { loggingProvider.log(2, null, Level.TRACE, exception, "The number is {}", match(provide(42))) } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies evaluating whether [DEBUG][Level.DEBUG] level is enabled. + */ + @Test + fun isDebugEnabled() { + assertThat(Logger.isDebugEnabled()).isEqualTo(debugEnabled) + } + + /** + * Verifies that an object will be logged correctly at [DEBUG][Level.DEBUG] level. + */ + @Test + fun debugObject() { + Logger.debug(42) + + if (debugEnabled) { + verify(exactly = 1) { loggingProvider.log(2, null, Level.DEBUG, null, 42) } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that a plain text will be logged correctly at [DEBUG][Level.DEBUG] level. + */ + @Test + fun debugString() { + Logger.debug("Hello World!") + + if (debugEnabled) { + verify(exactly = 1) { loggingProvider.log(2, null, Level.DEBUG, null, "Hello World!") } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that a lazy message supplier will be logged correctly at [DEBUG][Level.DEBUG] level. + */ + @Test + fun debugLazyMessage() { + Logger.debug{ "Hello World!" } + + if (debugEnabled) { + verify(exactly = 1) { loggingProvider.log(2, null, Level.DEBUG, null, match(provide("Hello World!"))) } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that a formatted text message will be logged correctly at [DEBUG][Level.DEBUG] level. + */ + @Test + fun debugMessageAndArguments() { + Logger.debug("Hello {}!", "World") + + if (debugEnabled) { + verify(exactly = 1) { loggingProvider.log(2, null, Level.DEBUG, null, "Hello {}!", "World") } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that a formatted text message with lazy argument suppliers will be logged correctly at + * [DEBUG][Level.DEBUG] level. + */ + @Test + fun debugMessageAndLazyArguments() { + Logger.debug("The number is {}", { 42 }) + + if (debugEnabled) { + verify(exactly = 1) { loggingProvider.log(2, null, Level.DEBUG, null, "The number is {}", match(provide(42))) } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that an exception will be logged correctly at [DEBUG][Level.DEBUG] level. + */ + @Test + fun debugException() { + val exception = NullPointerException() + + Logger.debug(exception) + + if (debugEnabled) { + verify(exactly = 1) { loggingProvider.log(2, null, Level.DEBUG, exception,null) } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that an exception with a custom message will be logged correctly at [DEBUG][Level.DEBUG] level. + */ + @Test + fun debugExceptionWithMessage() { + val exception = NullPointerException() + + Logger.debug(exception, "Hello World!") + + if (debugEnabled) { + verify(exactly = 1) { loggingProvider.log(2, null, Level.DEBUG, exception,"Hello World!") } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that an exception with a custom lazy message supplier will be logged correctly at [ DEBUG][Level.DEBUG] level. + */ + @Test + fun debugExceptionWithLazyMessage() { + val exception = NullPointerException() + + Logger.debug(exception) { "Hello World!" } + + if (debugEnabled) { + verify(exactly = 1) { loggingProvider.log(2, null, Level.DEBUG, exception, match(provide("Hello World!"))) } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that an exception with a formatted custom message will be logged correctly at [DEBUG][Level.DEBUG] level. + */ + @Test + fun debugExceptionWithMessageAndArguments() { + val exception = NullPointerException() + + Logger.debug(exception, "Hello {}!", "World") + + if (debugEnabled) { + verify(exactly = 1) { loggingProvider.log(2, null, Level.DEBUG, exception, "Hello {}!", "World") } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that an exception with a formatted custom message and lazy argument suppliers will be logged + * correctly at [DEBUG][Level.DEBUG] level. + */ + @Test + fun debugExceptionWithMessageAndLazyArguments() { + val exception = NullPointerException() + + Logger.debug(exception, "The number is {}", { 42 }) + + if (debugEnabled) { + verify(exactly = 1) { loggingProvider.log(2, null, Level.DEBUG, exception, "The number is {}", match(provide(42))) } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies evaluating whether [INFO][Level.INFO] level is enabled. + */ + @Test + fun isInfoEnabled() { + assertThat(Logger.isInfoEnabled()).isEqualTo(infoEnabled) + } + + /** + * Verifies that an object will be logged correctly at [INFO][Level.INFO] level. + */ + @Test + fun infoObject() { + Logger.info(42) + + if (infoEnabled) { + verify(exactly = 1) { loggingProvider.log(2, null, Level.INFO, null, 42) } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that a plain text will be logged correctly at [INFO][Level.INFO] level. + */ + @Test + fun infoString() { + Logger.info("Hello World!") + + if (infoEnabled) { + verify(exactly = 1) { loggingProvider.log(2, null, Level.INFO, null, "Hello World!") } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that a lazy message supplier will be logged correctly at [INFO][Level.INFO] level. + */ + @Test + fun infoLazyMessage() { + Logger.info{ "Hello World!" } + + if (infoEnabled) { + verify(exactly = 1) { loggingProvider.log(2, null, Level.INFO, null, match(provide("Hello World!"))) } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that a formatted text message will be logged correctly at [INFO][Level.INFO] level. + */ + @Test + fun infoMessageAndArguments() { + Logger.info("Hello {}!", "World") + + if (infoEnabled) { + verify(exactly = 1) { loggingProvider.log(2, null, Level.INFO, null, "Hello {}!", "World") } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that a formatted text message with lazy argument suppliers will be logged correctly at + * [INFO][Level.INFO] level. + */ + @Test + fun infoMessageAndLazyArguments() { + Logger.info("The number is {}", { 42 }) + + if (infoEnabled) { + verify(exactly = 1) { loggingProvider.log(2, null, Level.INFO, null, "The number is {}", match(provide(42))) } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that an exception will be logged correctly at [INFO][Level.INFO] level. + */ + @Test + fun infoException() { + val exception = NullPointerException() + + Logger.info(exception) + + if (infoEnabled) { + verify(exactly = 1) { loggingProvider.log(2, null, Level.INFO, exception,null) } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that an exception with a custom message will be logged correctly at [INFO][Level.INFO] level. + */ + @Test + fun infoExceptionWithMessage() { + val exception = NullPointerException() + + Logger.info(exception, "Hello World!") + + if (infoEnabled) { + verify(exactly = 1) { loggingProvider.log(2, null, Level.INFO, exception,"Hello World!") } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that an exception with a custom lazy message supplier will be logged correctly at [ INFO][Level.INFO] level. + */ + @Test + fun infoExceptionWithLazyMessage() { + val exception = NullPointerException() + + Logger.info(exception) { "Hello World!" } + + if (infoEnabled) { + verify(exactly = 1) { loggingProvider.log(2, null, Level.INFO, exception, match(provide("Hello World!"))) } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that an exception with a formatted custom message will be logged correctly at [INFO][Level.INFO] level. + */ + @Test + fun infoExceptionWithMessageAndArguments() { + val exception = NullPointerException() + + Logger.info(exception, "Hello {}!", "World") + + if (infoEnabled) { + verify(exactly = 1) { loggingProvider.log(2, null, Level.INFO, exception, "Hello {}!", "World") } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that an exception with a formatted custom message and lazy argument suppliers will be logged + * correctly at [INFO][Level.INFO] level. + */ + @Test + fun infoExceptionWithMessageAndLazyArguments() { + val exception = NullPointerException() + + Logger.info(exception, "The number is {}", { 42 }) + + if (infoEnabled) { + verify(exactly = 1) { loggingProvider.log(2, null, Level.INFO, exception, "The number is {}", match(provide(42))) } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies evaluating whether [WARN][Level.WARN] level is enabled. + */ + @Test + fun isWarnEnabled() { + assertThat(Logger.isWarnEnabled()).isEqualTo(warnEnabled) + } + + /** + * Verifies that an object will be logged correctly at [WARN][Level.WARN] level. + */ + @Test + fun warnObject() { + Logger.warn(42) + + if (warnEnabled) { + verify(exactly = 1) { loggingProvider.log(2, null, Level.WARN, null, 42) } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that a plain text will be logged correctly at [WARN][Level.WARN] level. + */ + @Test + fun warnString() { + Logger.warn("Hello World!") + + if (warnEnabled) { + verify(exactly = 1) { loggingProvider.log(2, null, Level.WARN, null, "Hello World!") } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that a lazy message supplier will be logged correctly at [WARN][Level.WARN] level. + */ + @Test + fun warnLazyMessage() { + Logger.warn{ "Hello World!" } + + if (warnEnabled) { + verify(exactly = 1) { loggingProvider.log(2, null, Level.WARN, null, match(provide("Hello World!"))) } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that a formatted text message will be logged correctly at [WARN][Level.WARN] level. + */ + @Test + fun warnMessageAndArguments() { + Logger.warn("Hello {}!", "World") + + if (warnEnabled) { + verify(exactly = 1) { loggingProvider.log(2, null, Level.WARN, null, "Hello {}!", "World") } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that a formatted text message with lazy argument suppliers will be logged correctly at + * [WARN][Level.WARN] level. + */ + @Test + fun warnMessageAndLazyArguments() { + Logger.warn("The number is {}", { 42 }) + + if (warnEnabled) { + verify(exactly = 1) { loggingProvider.log(2, null, Level.WARN, null, "The number is {}", match(provide(42))) } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that an exception will be logged correctly at [WARN][Level.WARN] level. + */ + @Test + fun warnException() { + val exception = NullPointerException() + + Logger.warn(exception) + + if (warnEnabled) { + verify(exactly = 1) { loggingProvider.log(2, null, Level.WARN, exception,null) } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that an exception with a custom message will be logged correctly at [WARN][Level.WARN] level. + */ + @Test + fun warnExceptionWithMessage() { + val exception = NullPointerException() + + Logger.warn(exception, "Hello World!") + + if (warnEnabled) { + verify(exactly = 1) { loggingProvider.log(2, null, Level.WARN, exception,"Hello World!") } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that an exception with a custom lazy message supplier will be logged correctly at [ WARN][Level.WARN] level. + */ + @Test + fun warnExceptionWithLazyMessage() { + val exception = NullPointerException() + + Logger.warn(exception) { "Hello World!" } + + if (warnEnabled) { + verify(exactly = 1) { loggingProvider.log(2, null, Level.WARN, exception, match(provide("Hello World!"))) } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that an exception with a formatted custom message will be logged correctly at [WARN][Level.WARN] level. + */ + @Test + fun warnExceptionWithMessageAndArguments() { + val exception = NullPointerException() + + Logger.warn(exception, "Hello {}!", "World") + + if (warnEnabled) { + verify(exactly = 1) { loggingProvider.log(2, null, Level.WARN, exception, "Hello {}!", "World") } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that an exception with a formatted custom message and lazy argument suppliers will be logged + * correctly at [WARN][Level.WARN] level. + */ + @Test + fun warnExceptionWithMessageAndLazyArguments() { + val exception = NullPointerException() + + Logger.warn(exception, "The number is {}", { 42 }) + + if (warnEnabled) { + verify(exactly = 1) { loggingProvider.log(2, null, Level.WARN, exception, "The number is {}", match(provide(42))) } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies evaluating whether [ERROR][Level.ERROR] level is enabled. + */ + @Test + fun isErrorEnabled() { + assertThat(Logger.isErrorEnabled()).isEqualTo(errorEnabled) + } + + /** + * Verifies that an object will be logged correctly at [ERROR][Level.ERROR] level. + */ + @Test + fun errorObject() { + Logger.error(42) + + if (errorEnabled) { + verify(exactly = 1) { loggingProvider.log(2, null, Level.ERROR, null, 42) } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that a plain text will be logged correctly at [ERROR][Level.ERROR] level. + */ + @Test + fun errorString() { + Logger.error("Hello World!") + + if (errorEnabled) { + verify(exactly = 1) { loggingProvider.log(2, null, Level.ERROR, null, "Hello World!") } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that a lazy message supplier will be logged correctly at [ERROR][Level.ERROR] level. + */ + @Test + fun errorLazyMessage() { + Logger.error{ "Hello World!" } + + if (errorEnabled) { + verify(exactly = 1) { loggingProvider.log(2, null, Level.ERROR, null, match(provide("Hello World!"))) } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that a formatted text message will be logged correctly at [ERROR][Level.ERROR] level. + */ + @Test + fun errorMessageAndArguments() { + Logger.error("Hello {}!", "World") + + if (errorEnabled) { + verify(exactly = 1) { loggingProvider.log(2, null, Level.ERROR, null, "Hello {}!", "World") } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that a formatted text message with lazy argument suppliers will be logged correctly at + * [ERROR][Level.ERROR] level. + */ + @Test + fun errorMessageAndLazyArguments() { + Logger.error("The number is {}", { 42 }) + + if (errorEnabled) { + verify(exactly = 1) { loggingProvider.log(2, null, Level.ERROR, null, "The number is {}", match(provide(42))) } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that an exception will be logged correctly at [ERROR][Level.ERROR] level. + */ + @Test + fun errorException() { + val exception = NullPointerException() + + Logger.error(exception) + + if (errorEnabled) { + verify(exactly = 1) { loggingProvider.log(2, null, Level.ERROR, exception,null) } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that an exception with a custom message will be logged correctly at [ERROR][Level.ERROR] level. + */ + @Test + fun errorExceptionWithMessage() { + val exception = NullPointerException() + + Logger.error(exception, "Hello World!") + + if (errorEnabled) { + verify(exactly = 1) { loggingProvider.log(2, null, Level.ERROR, exception,"Hello World!") } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that an exception with a custom lazy message supplier will be logged correctly at [ ERROR][Level.ERROR] level. + */ + @Test + fun errorExceptionWithLazyMessage() { + val exception = NullPointerException() + + Logger.error(exception) { "Hello World!" } + + if (errorEnabled) { + verify(exactly = 1) { loggingProvider.log(2, null, Level.ERROR, exception, match(provide("Hello World!"))) } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that an exception with a formatted custom message will be logged correctly at [ERROR][Level.ERROR] level. + */ + @Test + fun errorExceptionWithMessageAndArguments() { + val exception = NullPointerException() + + Logger.error(exception, "Hello {}!", "World") + + if (errorEnabled) { + verify(exactly = 1) { loggingProvider.log(2, null, Level.ERROR, exception, "Hello {}!", "World") } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that an exception with a formatted custom message and lazy argument suppliers will be logged + * correctly at [ERROR][Level.ERROR] level. + */ + @Test + fun errorExceptionWithMessageAndLazyArguments() { + val exception = NullPointerException() + + Logger.error(exception, "The number is {}", { 42 }) + + if (errorEnabled) { + verify(exactly = 1) { loggingProvider.log(2, null, Level.ERROR, exception, "The number is {}", match(provide(42))) } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Creates a functions that checks whether a passed object is a [Supplier] that returns a defined value. + * + * @param + * Expected return value from supplier + * @return + * Match function + */ + private inline fun provide(value: Any?): (T) -> Boolean { + return { it is Supplier<*> && value == it.get() } + } + + } + + /** + * Tests for receiving tagged logger instances. + */ + class Tagging { + + /** + * Redirects and collects system output streams. + */ + @JvmField + @Rule + public val systemStream = SystemStreamCollector(false) + + /** + * Verifies that [Logger.tag] returns the same untagged instance of [TaggedLogger] for + * `null` and empty strings. + */ + @Test + fun untagged() { + val logger = Logger.tag(null) + + assertThat(logger).isSameAs(Logger.tag("")) + assertThat(Whitebox.getProperty(logger, "tag")).isNull() + } + + /** + * Verifies that [Logger.tag] returns the same tagged instance of [TaggedLogger] for each + * tag. + */ + @Test + fun tagged() { + val logger = Logger.tag("test") + + assertThat(logger).isSameAs(Logger.tag("test")).isNotSameAs(Logger.tag("other")) + assertThat(Whitebox.getProperty(logger, "tag")).isEqualTo("test") + } + + } + +} diff --git a/tinylog-api-kotlin/src/test/kotlin/org/tinylog/kotlin/TaggedLoggerTest.kt b/tinylog-api-kotlin/src/test/kotlin/org/tinylog/kotlin/TaggedLoggerTest.kt new file mode 100644 index 000000000..7b5d4aba2 --- /dev/null +++ b/tinylog-api-kotlin/src/test/kotlin/org/tinylog/kotlin/TaggedLoggerTest.kt @@ -0,0 +1,963 @@ +/* + * Copyright 2019 Martin Winandy + * + * Licensed 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.tinylog.kotlin + +import io.mockk.every +import io.mockk.mockk +import io.mockk.verify +import java.util.ArrayList + +import org.junit.runner.RunWith +import org.junit.runners.Parameterized +import org.junit.runners.Parameterized.Parameters +import org.tinylog.Level +import org.tinylog.provider.LoggingProvider +import org.tinylog.provider.ProviderRegistry +import org.tinylog.rules.SystemStreamCollector + +import org.assertj.core.api.Assertions.assertThat +import org.junit.* +import org.tinylog.Supplier + +/** + * Tests for [TaggedLogger]. + * + * @param level + * Actual severity level under test + * @param traceEnabled + * Determines if [TRACE][Level.TRACE] level is enabled + * @param debugEnabled + * Determines if [DEBUG][Level.DEBUG] level is enabled + * @param infoEnabled + * Determines if [INFO][Level.INFO] level is enabled + * @param warnEnabled + * Determines if [WARN][Level.WARN] level is enabled + * @param errorEnabled + * Determines if [ERROR][Level.ERROR] level is enabled + */ +@RunWith(Parameterized::class) +class TaggedLoggerTest(private val level: Level, private val traceEnabled: Boolean, private val debugEnabled: Boolean, + private val infoEnabled: Boolean, private val warnEnabled: Boolean, private val errorEnabled: Boolean) { + + companion object { + + /** + * Returns for all severity levels which severity levels are enabled. + * + * @return Each object array contains the severity level itself and five booleans for [TRACE][Level.TRACE] + * ... [ERROR][Level.ERROR] to determine whether these severity levels are enabled + */ + @JvmStatic + @Parameters(name = "{0}") + fun getLevels(): Collection> { + val levels = ArrayList>() + + // @formatter:off + levels.add(arrayOf(Level.TRACE, true, true, true, true, true)) + levels.add(arrayOf(Level.DEBUG, false, true, true, true, true)) + levels.add(arrayOf(Level.INFO, false, false, true, true, true)) + levels.add(arrayOf(Level.WARN, false, false, false, true, true)) + levels.add(arrayOf(Level.ERROR, false, false, false, false, true)) + levels.add(arrayOf(Level.OFF, false, false, false, false, false)) + // @formatter:on + + return levels + } + + } + + /** + * Redirects and collects system output streams. + */ + @JvmField + @Rule + public val systemStream = SystemStreamCollector(false) + + private val tag = "test" + private val loggingProvider = mockk() + private val logger = TaggedLogger(tag) + + /** + * Applies the mocked logging provider and overrides all depending fields. + */ + @Before + fun applyLoggingProvider() { + every { loggingProvider.getMinimumLevel(tag) } returns level + + every { loggingProvider.isEnabled(any(), tag, Level.TRACE) } returns traceEnabled + every { loggingProvider.isEnabled(any(), tag, Level.DEBUG) } returns debugEnabled + every { loggingProvider.isEnabled(any(), tag, Level.INFO) } returns infoEnabled + every { loggingProvider.isEnabled(any(), tag, Level.WARN) } returns warnEnabled + every { loggingProvider.isEnabled(any(), tag, Level.ERROR) } returns errorEnabled + + every { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } returns Unit + every { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } returns Unit + + Whitebox.setProperty(logger, LoggingProvider::class, loggingProvider) + Whitebox.setProperty(logger, "minimumLevelCoversTrace", traceEnabled) + Whitebox.setProperty(logger, "minimumLevelCoversDebug", debugEnabled) + Whitebox.setProperty(logger, "minimumLevelCoversInfo", infoEnabled) + Whitebox.setProperty(logger, "minimumLevelCoversWarn", warnEnabled) + Whitebox.setProperty(logger, "minimumLevelCoversError", errorEnabled) + } + + /** + * Resets the logging provider and all overridden fields in [TaggedLogger]. + */ + @After + fun resetLoggingProvider() { + Whitebox.setProperty(logger, LoggingProvider::class, ProviderRegistry.getLoggingProvider()) + Whitebox.setProperty(logger, "minimumLevelCoversTrace", isCoveredByMinimumLevel(Level.TRACE)) + Whitebox.setProperty(logger, "minimumLevelCoversDebug", isCoveredByMinimumLevel(Level.DEBUG)) + Whitebox.setProperty(logger, "minimumLevelCoversInfo", isCoveredByMinimumLevel(Level.INFO)) + Whitebox.setProperty(logger, "minimumLevelCoversWarn", isCoveredByMinimumLevel(Level.WARN)) + Whitebox.setProperty(logger, "minimumLevelCoversError", isCoveredByMinimumLevel(Level.ERROR)) + } + + /** + * Verifies evaluating whether a specific severity level is covered by the minimum severity level. + */ + @Test + fun coveredByMinimumLevel() { + assertThat(isCoveredByMinimumLevel(Level.TRACE)).isEqualTo(traceEnabled) + assertThat(isCoveredByMinimumLevel(Level.DEBUG)).isEqualTo(debugEnabled) + assertThat(isCoveredByMinimumLevel(Level.INFO)).isEqualTo(infoEnabled) + assertThat(isCoveredByMinimumLevel(Level.WARN)).isEqualTo(warnEnabled) + assertThat(isCoveredByMinimumLevel(Level.ERROR)).isEqualTo(errorEnabled) + } + + /** + * Verifies evaluating whether [TRACE][Level.TRACE] level is enabled. + */ + @Test + fun isTraceEnabled() { + assertThat(logger.isTraceEnabled()).isEqualTo(traceEnabled) + } + + /** + * Verifies that an object will be logged correctly at [TRACE][Level.TRACE] level. + */ + @Test + fun traceObject() { + logger.trace(42) + + if (traceEnabled) { + verify(exactly = 1) { loggingProvider.log(2, tag, Level.TRACE, null, 42) } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that a plain text will be logged correctly at [TRACE][Level.TRACE] level. + */ + @Test + fun traceString() { + logger.trace("Hello World!") + + if (traceEnabled) { + verify(exactly = 1) { loggingProvider.log(2, tag, Level.TRACE, null, "Hello World!") } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that a lazy message supplier will be logged correctly at [TRACE][Level.TRACE] level. + */ + @Test + fun traceLazyMessage() { + logger.trace{ "Hello World!" } + + if (traceEnabled) { + verify(exactly = 1) { loggingProvider.log(2, tag, Level.TRACE, null, match(provide("Hello World!"))) } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that a formatted text message will be logged correctly at [TRACE][Level.TRACE] level. + */ + @Test + fun traceMessageAndArguments() { + logger.trace("Hello {}!", "World") + + if (traceEnabled) { + verify(exactly = 1) { loggingProvider.log(2, tag, Level.TRACE, null, "Hello {}!", "World") } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that a formatted text message with lazy argument suppliers will be logged correctly at + * [TRACE][Level.TRACE] level. + */ + @Test + fun traceMessageAndLazyArguments() { + logger.trace("The number is {}", { 42 }) + + if (traceEnabled) { + verify(exactly = 1) { loggingProvider.log(2, tag, Level.TRACE, null, "The number is {}", match(provide(42))) } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that an exception will be logged correctly at [TRACE][Level.TRACE] level. + */ + @Test + fun traceException() { + val exception = NullPointerException() + + logger.trace(exception) + + if (traceEnabled) { + verify(exactly = 1) { loggingProvider.log(2, tag, Level.TRACE, exception,null) } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that an exception with a custom message will be logged correctly at [TRACE][Level.TRACE] level. + */ + @Test + fun traceExceptionWithMessage() { + val exception = NullPointerException() + + logger.trace(exception, "Hello World!") + + if (traceEnabled) { + verify(exactly = 1) { loggingProvider.log(2, tag, Level.TRACE, exception,"Hello World!") } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that an exception with a custom lazy message supplier will be logged correctly at [ TRACE][Level.TRACE] level. + */ + @Test + fun traceExceptionWithLazyMessage() { + val exception = NullPointerException() + + logger.trace(exception) { "Hello World!" } + + if (traceEnabled) { + verify(exactly = 1) { loggingProvider.log(2, tag, Level.TRACE, exception, match(provide("Hello World!"))) } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that an exception with a formatted custom message will be logged correctly at [TRACE][Level.TRACE] level. + */ + @Test + fun traceExceptionWithMessageAndArguments() { + val exception = NullPointerException() + + logger.trace(exception, "Hello {}!", "World") + + if (traceEnabled) { + verify(exactly = 1) { loggingProvider.log(2, tag, Level.TRACE, exception, "Hello {}!", "World") } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that an exception with a formatted custom message and lazy argument suppliers will be logged + * correctly at [TRACE][Level.TRACE] level. + */ + @Test + fun traceExceptionWithMessageAndLazyArguments() { + val exception = NullPointerException() + + logger.trace(exception, "The number is {}", { 42 }) + + if (traceEnabled) { + verify(exactly = 1) { loggingProvider.log(2, tag, Level.TRACE, exception, "The number is {}", match(provide(42))) } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies evaluating whether [DEBUG][Level.DEBUG] level is enabled. + */ + @Test + fun isDebugEnabled() { + assertThat(logger.isDebugEnabled()).isEqualTo(debugEnabled) + } + + /** + * Verifies that an object will be logged correctly at [DEBUG][Level.DEBUG] level. + */ + @Test + fun debugObject() { + logger.debug(42) + + if (debugEnabled) { + verify(exactly = 1) { loggingProvider.log(2, tag, Level.DEBUG, null, 42) } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that a plain text will be logged correctly at [DEBUG][Level.DEBUG] level. + */ + @Test + fun debugString() { + logger.debug("Hello World!") + + if (debugEnabled) { + verify(exactly = 1) { loggingProvider.log(2, tag, Level.DEBUG, null, "Hello World!") } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that a lazy message supplier will be logged correctly at [DEBUG][Level.DEBUG] level. + */ + @Test + fun debugLazyMessage() { + logger.debug{ "Hello World!" } + + if (debugEnabled) { + verify(exactly = 1) { loggingProvider.log(2, tag, Level.DEBUG, null, match(provide("Hello World!"))) } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that a formatted text message will be logged correctly at [DEBUG][Level.DEBUG] level. + */ + @Test + fun debugMessageAndArguments() { + logger.debug("Hello {}!", "World") + + if (debugEnabled) { + verify(exactly = 1) { loggingProvider.log(2, tag, Level.DEBUG, null, "Hello {}!", "World") } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that a formatted text message with lazy argument suppliers will be logged correctly at + * [DEBUG][Level.DEBUG] level. + */ + @Test + fun debugMessageAndLazyArguments() { + logger.debug("The number is {}", { 42 }) + + if (debugEnabled) { + verify(exactly = 1) { loggingProvider.log(2, tag, Level.DEBUG, null, "The number is {}", match(provide(42))) } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that an exception will be logged correctly at [DEBUG][Level.DEBUG] level. + */ + @Test + fun debugException() { + val exception = NullPointerException() + + logger.debug(exception) + + if (debugEnabled) { + verify(exactly = 1) { loggingProvider.log(2, tag, Level.DEBUG, exception,null) } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that an exception with a custom message will be logged correctly at [DEBUG][Level.DEBUG] level. + */ + @Test + fun debugExceptionWithMessage() { + val exception = NullPointerException() + + logger.debug(exception, "Hello World!") + + if (debugEnabled) { + verify(exactly = 1) { loggingProvider.log(2, tag, Level.DEBUG, exception,"Hello World!") } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that an exception with a custom lazy message supplier will be logged correctly at [ DEBUG][Level.DEBUG] level. + */ + @Test + fun debugExceptionWithLazyMessage() { + val exception = NullPointerException() + + logger.debug(exception) { "Hello World!" } + + if (debugEnabled) { + verify(exactly = 1) { loggingProvider.log(2, tag, Level.DEBUG, exception, match(provide("Hello World!"))) } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that an exception with a formatted custom message will be logged correctly at [DEBUG][Level.DEBUG] level. + */ + @Test + fun debugExceptionWithMessageAndArguments() { + val exception = NullPointerException() + + logger.debug(exception, "Hello {}!", "World") + + if (debugEnabled) { + verify(exactly = 1) { loggingProvider.log(2, tag, Level.DEBUG, exception, "Hello {}!", "World") } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that an exception with a formatted custom message and lazy argument suppliers will be logged + * correctly at [DEBUG][Level.DEBUG] level. + */ + @Test + fun debugExceptionWithMessageAndLazyArguments() { + val exception = NullPointerException() + + logger.debug(exception, "The number is {}", { 42 }) + + if (debugEnabled) { + verify(exactly = 1) { loggingProvider.log(2, tag, Level.DEBUG, exception, "The number is {}", match(provide(42))) } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies evaluating whether [INFO][Level.INFO] level is enabled. + */ + @Test + fun isInfoEnabled() { + assertThat(logger.isInfoEnabled()).isEqualTo(infoEnabled) + } + + /** + * Verifies that an object will be logged correctly at [INFO][Level.INFO] level. + */ + @Test + fun infoObject() { + logger.info(42) + + if (infoEnabled) { + verify(exactly = 1) { loggingProvider.log(2, tag, Level.INFO, null, 42) } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that a plain text will be logged correctly at [INFO][Level.INFO] level. + */ + @Test + fun infoString() { + logger.info("Hello World!") + + if (infoEnabled) { + verify(exactly = 1) { loggingProvider.log(2, tag, Level.INFO, null, "Hello World!") } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that a lazy message supplier will be logged correctly at [INFO][Level.INFO] level. + */ + @Test + fun infoLazyMessage() { + logger.info{ "Hello World!" } + + if (infoEnabled) { + verify(exactly = 1) { loggingProvider.log(2, tag, Level.INFO, null, match(provide("Hello World!"))) } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that a formatted text message will be logged correctly at [INFO][Level.INFO] level. + */ + @Test + fun infoMessageAndArguments() { + logger.info("Hello {}!", "World") + + if (infoEnabled) { + verify(exactly = 1) { loggingProvider.log(2, tag, Level.INFO, null, "Hello {}!", "World") } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that a formatted text message with lazy argument suppliers will be logged correctly at + * [INFO][Level.INFO] level. + */ + @Test + fun infoMessageAndLazyArguments() { + logger.info("The number is {}", { 42 }) + + if (infoEnabled) { + verify(exactly = 1) { loggingProvider.log(2, tag, Level.INFO, null, "The number is {}", match(provide(42))) } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that an exception will be logged correctly at [INFO][Level.INFO] level. + */ + @Test + fun infoException() { + val exception = NullPointerException() + + logger.info(exception) + + if (infoEnabled) { + verify(exactly = 1) { loggingProvider.log(2, tag, Level.INFO, exception,null) } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that an exception with a custom message will be logged correctly at [INFO][Level.INFO] level. + */ + @Test + fun infoExceptionWithMessage() { + val exception = NullPointerException() + + logger.info(exception, "Hello World!") + + if (infoEnabled) { + verify(exactly = 1) { loggingProvider.log(2, tag, Level.INFO, exception,"Hello World!") } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that an exception with a custom lazy message supplier will be logged correctly at [ INFO][Level.INFO] level. + */ + @Test + fun infoExceptionWithLazyMessage() { + val exception = NullPointerException() + + logger.info(exception) { "Hello World!" } + + if (infoEnabled) { + verify(exactly = 1) { loggingProvider.log(2, tag, Level.INFO, exception, match(provide("Hello World!"))) } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that an exception with a formatted custom message will be logged correctly at [INFO][Level.INFO] level. + */ + @Test + fun infoExceptionWithMessageAndArguments() { + val exception = NullPointerException() + + logger.info(exception, "Hello {}!", "World") + + if (infoEnabled) { + verify(exactly = 1) { loggingProvider.log(2, tag, Level.INFO, exception, "Hello {}!", "World") } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that an exception with a formatted custom message and lazy argument suppliers will be logged + * correctly at [INFO][Level.INFO] level. + */ + @Test + fun infoExceptionWithMessageAndLazyArguments() { + val exception = NullPointerException() + + logger.info(exception, "The number is {}", { 42 }) + + if (infoEnabled) { + verify(exactly = 1) { loggingProvider.log(2, tag, Level.INFO, exception, "The number is {}", match(provide(42))) } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies evaluating whether [WARN][Level.WARN] level is enabled. + */ + @Test + fun isWarnEnabled() { + assertThat(logger.isWarnEnabled()).isEqualTo(warnEnabled) + } + + /** + * Verifies that an object will be logged correctly at [WARN][Level.WARN] level. + */ + @Test + fun warnObject() { + logger.warn(42) + + if (warnEnabled) { + verify(exactly = 1) { loggingProvider.log(2, tag, Level.WARN, null, 42) } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that a plain text will be logged correctly at [WARN][Level.WARN] level. + */ + @Test + fun warnString() { + logger.warn("Hello World!") + + if (warnEnabled) { + verify(exactly = 1) { loggingProvider.log(2, tag, Level.WARN, null, "Hello World!") } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that a lazy message supplier will be logged correctly at [WARN][Level.WARN] level. + */ + @Test + fun warnLazyMessage() { + logger.warn{ "Hello World!" } + + if (warnEnabled) { + verify(exactly = 1) { loggingProvider.log(2, tag, Level.WARN, null, match(provide("Hello World!"))) } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that a formatted text message will be logged correctly at [WARN][Level.WARN] level. + */ + @Test + fun warnMessageAndArguments() { + logger.warn("Hello {}!", "World") + + if (warnEnabled) { + verify(exactly = 1) { loggingProvider.log(2, tag, Level.WARN, null, "Hello {}!", "World") } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that a formatted text message with lazy argument suppliers will be logged correctly at + * [WARN][Level.WARN] level. + */ + @Test + fun warnMessageAndLazyArguments() { + logger.warn("The number is {}", { 42 }) + + if (warnEnabled) { + verify(exactly = 1) { loggingProvider.log(2, tag, Level.WARN, null, "The number is {}", match(provide(42))) } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that an exception will be logged correctly at [WARN][Level.WARN] level. + */ + @Test + fun warnException() { + val exception = NullPointerException() + + logger.warn(exception) + + if (warnEnabled) { + verify(exactly = 1) { loggingProvider.log(2, tag, Level.WARN, exception,null) } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that an exception with a custom message will be logged correctly at [WARN][Level.WARN] level. + */ + @Test + fun warnExceptionWithMessage() { + val exception = NullPointerException() + + logger.warn(exception, "Hello World!") + + if (warnEnabled) { + verify(exactly = 1) { loggingProvider.log(2, tag, Level.WARN, exception,"Hello World!") } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that an exception with a custom lazy message supplier will be logged correctly at [ WARN][Level.WARN] level. + */ + @Test + fun warnExceptionWithLazyMessage() { + val exception = NullPointerException() + + logger.warn(exception) { "Hello World!" } + + if (warnEnabled) { + verify(exactly = 1) { loggingProvider.log(2, tag, Level.WARN, exception, match(provide("Hello World!"))) } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that an exception with a formatted custom message will be logged correctly at [WARN][Level.WARN] level. + */ + @Test + fun warnExceptionWithMessageAndArguments() { + val exception = NullPointerException() + + logger.warn(exception, "Hello {}!", "World") + + if (warnEnabled) { + verify(exactly = 1) { loggingProvider.log(2, tag, Level.WARN, exception, "Hello {}!", "World") } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that an exception with a formatted custom message and lazy argument suppliers will be logged + * correctly at [WARN][Level.WARN] level. + */ + @Test + fun warnExceptionWithMessageAndLazyArguments() { + val exception = NullPointerException() + + logger.warn(exception, "The number is {}", { 42 }) + + if (warnEnabled) { + verify(exactly = 1) { loggingProvider.log(2, tag, Level.WARN, exception, "The number is {}", match(provide(42))) } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies evaluating whether [ERROR][Level.ERROR] level is enabled. + */ + @Test + fun isErrorEnabled() { + assertThat(logger.isErrorEnabled()).isEqualTo(errorEnabled) + } + + /** + * Verifies that an object will be logged correctly at [ERROR][Level.ERROR] level. + */ + @Test + fun errorObject() { + logger.error(42) + + if (errorEnabled) { + verify(exactly = 1) { loggingProvider.log(2, tag, Level.ERROR, null, 42) } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that a plain text will be logged correctly at [ERROR][Level.ERROR] level. + */ + @Test + fun errorString() { + logger.error("Hello World!") + + if (errorEnabled) { + verify(exactly = 1) { loggingProvider.log(2, tag, Level.ERROR, null, "Hello World!") } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that a lazy message supplier will be logged correctly at [ERROR][Level.ERROR] level. + */ + @Test + fun errorLazyMessage() { + logger.error{ "Hello World!" } + + if (errorEnabled) { + verify(exactly = 1) { loggingProvider.log(2, tag, Level.ERROR, null, match(provide("Hello World!"))) } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that a formatted text message will be logged correctly at [ERROR][Level.ERROR] level. + */ + @Test + fun errorMessageAndArguments() { + logger.error("Hello {}!", "World") + + if (errorEnabled) { + verify(exactly = 1) { loggingProvider.log(2, tag, Level.ERROR, null, "Hello {}!", "World") } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that a formatted text message with lazy argument suppliers will be logged correctly at + * [ERROR][Level.ERROR] level. + */ + @Test + fun errorMessageAndLazyArguments() { + logger.error("The number is {}", { 42 }) + + if (errorEnabled) { + verify(exactly = 1) { loggingProvider.log(2, tag, Level.ERROR, null, "The number is {}", match(provide(42))) } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that an exception will be logged correctly at [ERROR][Level.ERROR] level. + */ + @Test + fun errorException() { + val exception = NullPointerException() + + logger.error(exception) + + if (errorEnabled) { + verify(exactly = 1) { loggingProvider.log(2, tag, Level.ERROR, exception,null) } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that an exception with a custom message will be logged correctly at [ERROR][Level.ERROR] level. + */ + @Test + fun errorExceptionWithMessage() { + val exception = NullPointerException() + + logger.error(exception, "Hello World!") + + if (errorEnabled) { + verify(exactly = 1) { loggingProvider.log(2, tag, Level.ERROR, exception,"Hello World!") } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that an exception with a custom lazy message supplier will be logged correctly at [ ERROR][Level.ERROR] level. + */ + @Test + fun errorExceptionWithLazyMessage() { + val exception = NullPointerException() + + logger.error(exception) { "Hello World!" } + + if (errorEnabled) { + verify(exactly = 1) { loggingProvider.log(2, tag, Level.ERROR, exception, match(provide("Hello World!"))) } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that an exception with a formatted custom message will be logged correctly at [ERROR][Level.ERROR] level. + */ + @Test + fun errorExceptionWithMessageAndArguments() { + val exception = NullPointerException() + + logger.error(exception, "Hello {}!", "World") + + if (errorEnabled) { + verify(exactly = 1) { loggingProvider.log(2, tag, Level.ERROR, exception, "Hello {}!", "World") } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Verifies that an exception with a formatted custom message and lazy argument suppliers will be logged + * correctly at [ERROR][Level.ERROR] level. + */ + @Test + fun errorExceptionWithMessageAndLazyArguments() { + val exception = NullPointerException() + + logger.error(exception, "The number is {}", { 42 }) + + if (errorEnabled) { + verify(exactly = 1) { loggingProvider.log(2, tag, Level.ERROR, exception, "The number is {}", match(provide(42))) } + } else { + verify(exactly = 0) { loggingProvider.log(any(), any(), any(), any(), any(), *anyVararg()) } + } + } + + /** + * Creates a functions that checks whether a passed object is a [Supplier] that returns a defined value. + * + * @param + * Expected return value from supplier + * @return + * Match function + */ + private inline fun provide(value: Any?): (T) -> Boolean { + return { it is Supplier<*> && value == it.get() } + } + + /** + * Invokes the private method [TaggedLogger.isCoveredByMinimumLevel]. + * + * @param level + * Severity level to check + * @return `true` if given severity level is covered, otherwise `false` + */ + private fun isCoveredByMinimumLevel(level: Level): Boolean { + return Whitebox.callMethod(logger, "isCoveredByMinimumLevel", tag, level) as Boolean + } + +} diff --git a/tinylog-api-kotlin/src/test/kotlin/org/tinylog/kotlin/Whitebox.kt b/tinylog-api-kotlin/src/test/kotlin/org/tinylog/kotlin/Whitebox.kt new file mode 100644 index 000000000..5db49587a --- /dev/null +++ b/tinylog-api-kotlin/src/test/kotlin/org/tinylog/kotlin/Whitebox.kt @@ -0,0 +1,115 @@ +/* + * Copyright 2019 Martin Winandy + * + * Licensed 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.tinylog.kotlin + +import org.apache.commons.lang3.reflect.FieldUtils +import java.lang.reflect.Field +import java.lang.reflect.Modifier +import kotlin.reflect.KClass +import kotlin.reflect.full.memberFunctions +import kotlin.reflect.full.memberProperties +import kotlin.reflect.jvm.isAccessible +import kotlin.reflect.jvm.javaField + +/** + * Reflection utility class for accessing private members. + */ +object Whitebox { + + /** + * Gets the value of a property by name. + * + * @param instance + * Instance of a type that has the requested property + * @param name + * Name of the property to get + * @return + * Current value + */ + fun getProperty(instance: T, name: String): Any? { + return instance::class.memberProperties.filter { it.name == name }.onEach { it.isAccessible = true }.first().call(instance) + } + + /** + * Sets a value to a property by type. + * + * @param instance + * Instance of a type that has the requested property + * @param type + * Type of the property to change + * @param value + * New value to set + */ + fun setProperty(instance: Any, type: KClass, value: T?) { + return instance::class.memberProperties.mapNotNull { it.javaField }.filter { it.type == type.java }.forEach { + setProperty(instance, it, value) + } + } + + /** + * Sets a value to a property by name. + * + * @param instance + * Instance of a type that has the requested property + * @param name + * Name of the property to change + * @param value + * New value to set + */ + fun setProperty(instance: Any, name: String, value: T?) { + return instance::class.memberProperties.filter { it.name == name }.mapNotNull { it.javaField }.forEach { + setProperty(instance, it, value) + } + } + + /** + * Calls a private method by name. + * + * @param instance + * Instance of a type that has the requested method + * @param name + * Name of the method to call + * @param arguments + * Arguments for the method + * @return + * Return value of the called method + */ + fun callMethod(instance: Any, name: String, vararg arguments: Any?): Any? { + return instance::class.memberFunctions.filter { + it.name == name + }.onEach { + it.isAccessible = true + }.first().call(instance, *arguments) + } + + /** + * Sets a value to a field. + * + * @param instance + * Object to update + * @param field + * Property to update + * @param value + * New value to set + */ + private fun setProperty(instance: Any, field: Field, value: T?) { + FieldUtils.removeFinalModifier(field, true) + if (Modifier.isStatic(field.modifiers)) { + FieldUtils.writeStaticField(field, value, true) + } else { + FieldUtils.writeField(field, instance, value, true) + } + } + +}