Loggers.kt
package com.hexagontk.core
import com.hexagontk.core.text.stripAnsi
import java.lang.System.Logger
import java.lang.System.Logger.Level
import java.lang.System.Logger.Level.*
import kotlin.reflect.KClass
const val HEXAGONTK_LOGGING_COLOR = "hexagontk_logging_color"
const val HEXAGONTK_LOGGING_LOGGER_NAME = "hexagontk_logging_logger_name"
internal val useColor: Boolean by lazy { Platform.systemFlag(HEXAGONTK_LOGGING_COLOR) }
internal val defaultLoggerName: String by lazy {
Platform.systemSetting(HEXAGONTK_LOGGING_LOGGER_NAME, "com.hexagontk.core.logging")
}
/** Default logger for when you feel too lazy to declare one. */
val logger: Logger by lazy { loggerOf(defaultLoggerName) }
/**
* Use this [T] to log a message with a prefix using [TRACE] level.
*
* [com.hexagontk.core.logger] must have the [TRACE] level enabled.
*
* @receiver Object which string representation will be logged.
* @param T Type of the logged object.
* @param prefix Prefix for the logging message.
* @return The receiver reference for chaining methods.
*/
fun <T> T.trace(prefix: String = ""): T =
apply { logger.trace { "$prefix$this" } }
/**
* Use this [T] to log a message with a prefix using [DEBUG] level.
*
* [com.hexagontk.core.logger] must have the [DEBUG] level enabled.
*
* @receiver Object which string representation will be logged.
* @param T Type of the logged object.
* @param prefix Prefix for the logging message.
* @return The receiver reference for chaining methods.
*/
fun <T> T.debug(prefix: String = ""): T =
apply { logger.debug { "$prefix$this" } }
/**
* Use this [T] to log a message with a prefix using [INFO] level.
*
* [com.hexagontk.core.logger] must have the [INFO] level enabled.
*
* @receiver Object which string representation will be logged.
* @param T Type of the logged object.
* @param prefix Prefix for the logging message.
* @return The receiver reference for chaining methods.
*/
fun <T> T.info(prefix: String = ""): T =
apply { logger.info { "$prefix$this" } }
/**
* Logger constructor function.
*
* @param type Logger type. It is shown in the logs messages and used for log filtering.
*/
fun loggerOf(type: KClass<*>): Logger =
loggerOf(type.qualifiedName ?: error("Cannot get qualified name of type"))
/**
* Logger constructor function.
*
* @param name Logger name. It is shown in the logs messages and used for log filtering.
*/
fun loggerOf(name: String): Logger =
System.getLogger(name)
/**
* Log a message, with associated exception information.
*
* @param level Level used in the log statement.
* @param exception The exception associated with log message.
* @param message The message supplier to use in the log statement.
*/
fun <E : Throwable> Logger.log(level: Level, exception: E, message: (E) -> Any?) {
val messageSupplier = { stripAnsi(message(exception), useColor) }
log(level, messageSupplier, exception)
}
/**
* Log a message using [TRACE] level.
*
* @param message The required message to log.
*/
fun Logger.trace(message: () -> Any?) {
logMessage(TRACE, message)
}
/**
* Log a message using [DEBUG] level.
*
* @param message The required message to log.
*/
fun Logger.debug(message: () -> Any?) {
logMessage(DEBUG, message)
}
/**
* Log a message using [INFO] level.
*
* @param message The required message to log.
*/
fun Logger.info(message: () -> Any?) {
logMessage(INFO, message)
}
/**
* Log a message using [WARNING] level.
*
* @param message The required message to log.
*/
fun Logger.warn(message: () -> Any?) {
logMessage(WARNING, message)
}
/**
* Log a message using [ERROR] level.
*
* @param message The required message to log.
*/
fun Logger.error(message: () -> Any?) {
logMessage(ERROR, message)
}
/**
* Log a message using [WARNING] level with associated exception information.
*
* @param exception The exception associated with log message.
* @param message The message to log (optional). If not supplied it will be empty.
*/
fun <E : Throwable> Logger.warn(exception: E?, message: (E?) -> Any? = { "" }) {
if (exception == null) log(WARNING) { message(null)?.toString() }
else log(WARNING, exception, message)
}
/**
* Log a message using [ERROR] level with associated exception information.
*
* @param exception The exception associated with log message.
* @param message The message to log (function to optional). If not supplied it will be empty.
*/
fun <E : Throwable> Logger.error(exception: E?, message: (E?) -> Any? = { "" }) {
if (exception == null) log(ERROR) { message(null)?.toString() }
else log(ERROR, exception, message)
}
internal fun <T> stripAnsi(receiver: T?, apply: Boolean): String? =
receiver?.toString()?.let { if (apply) it.stripAnsi() else it }
private fun Logger.logMessage(level: Level, message: () -> Any?) {
val messageSupplier = { stripAnsi(message(), !useColor) }
log(level, messageSupplier)
}