Loggers.kt

  1. package com.hexagontk.core

  2. import com.hexagontk.core.text.stripAnsi
  3. import java.lang.System.Logger
  4. import java.lang.System.Logger.Level
  5. import java.lang.System.Logger.Level.*
  6. import kotlin.reflect.KClass

  7. const val HEXAGONTK_LOGGING_COLOR = "hexagontk_logging_color"
  8. const val HEXAGONTK_LOGGING_LOGGER_NAME = "hexagontk_logging_logger_name"

  9. internal val useColor: Boolean by lazy { Platform.systemFlag(HEXAGONTK_LOGGING_COLOR) }
  10. internal val defaultLoggerName: String by lazy {
  11.     Platform.systemSetting(HEXAGONTK_LOGGING_LOGGER_NAME, "com.hexagontk.core.logging")
  12. }

  13. /** Default logger for when you feel too lazy to declare one. */
  14. val logger: Logger by lazy { loggerOf(defaultLoggerName) }

  15. /**
  16.  * Use this [T] to log a message with a prefix using [TRACE] level.
  17.  *
  18.  * [com.hexagontk.core.logger] must have the [TRACE] level enabled.
  19.  *
  20.  * @receiver Object which string representation will be logged.
  21.  * @param T Type of the logged object.
  22.  * @param prefix Prefix for the logging message.
  23.  * @return The receiver reference for chaining methods.
  24.  */
  25. fun <T> T.trace(prefix: String = ""): T =
  26.     apply { logger.trace { "$prefix$this" } }

  27. /**
  28.  * Use this [T] to log a message with a prefix using [DEBUG] level.
  29.  *
  30.  * [com.hexagontk.core.logger] must have the [DEBUG] level enabled.
  31.  *
  32.  * @receiver Object which string representation will be logged.
  33.  * @param T Type of the logged object.
  34.  * @param prefix Prefix for the logging message.
  35.  * @return The receiver reference for chaining methods.
  36.  */
  37. fun <T> T.debug(prefix: String = ""): T =
  38.     apply { logger.debug { "$prefix$this" } }

  39. /**
  40.  * Use this [T] to log a message with a prefix using [INFO] level.
  41.  *
  42.  * [com.hexagontk.core.logger] must have the [INFO] level enabled.
  43.  *
  44.  * @receiver Object which string representation will be logged.
  45.  * @param T Type of the logged object.
  46.  * @param prefix Prefix for the logging message.
  47.  * @return The receiver reference for chaining methods.
  48.  */
  49. fun <T> T.info(prefix: String = ""): T =
  50.     apply { logger.info { "$prefix$this" } }

  51. /**
  52.  * Logger constructor function.
  53.  *
  54.  * @param type Logger type. It is shown in the logs messages and used for log filtering.
  55.  */
  56. fun loggerOf(type: KClass<*>): Logger =
  57.     loggerOf(type.qualifiedName ?: error("Cannot get qualified name of type"))

  58. /**
  59.  * Logger constructor function.
  60.  *
  61.  * @param name Logger name. It is shown in the logs messages and used for log filtering.
  62.  */
  63. fun loggerOf(name: String): Logger =
  64.     System.getLogger(name)

  65. /**
  66.  * Log a message, with associated exception information.
  67.  *
  68.  * @param level Level used in the log statement.
  69.  * @param exception The exception associated with log message.
  70.  * @param message The message supplier to use in the log statement.
  71.  */
  72. fun <E : Throwable> Logger.log(level: Level, exception: E, message: (E) -> Any?) {
  73.     val messageSupplier = { stripAnsi(message(exception), useColor) }
  74.     log(level, messageSupplier, exception)
  75. }

  76. /**
  77.  * Log a message using [TRACE] level.
  78.  *
  79.  * @param message The required message to log.
  80.  */
  81. fun Logger.trace(message: () -> Any?) {
  82.     logMessage(TRACE, message)
  83. }

  84. /**
  85.  * Log a message using [DEBUG] level.
  86.  *
  87.  * @param message The required message to log.
  88.  */
  89. fun Logger.debug(message: () -> Any?) {
  90.     logMessage(DEBUG, message)
  91. }

  92. /**
  93.  * Log a message using [INFO] level.
  94.  *
  95.  * @param message The required message to log.
  96.  */
  97. fun Logger.info(message: () -> Any?) {
  98.     logMessage(INFO, message)
  99. }

  100. /**
  101.  * Log a message using [WARNING] level.
  102.  *
  103.  * @param message The required message to log.
  104.  */
  105. fun Logger.warn(message: () -> Any?) {
  106.     logMessage(WARNING, message)
  107. }

  108. /**
  109.  * Log a message using [ERROR] level.
  110.  *
  111.  * @param message The required message to log.
  112.  */
  113. fun Logger.error(message: () -> Any?) {
  114.     logMessage(ERROR, message)
  115. }

  116. /**
  117.  * Log a message using [WARNING] level with associated exception information.
  118.  *
  119.  * @param exception The exception associated with log message.
  120.  * @param message The message to log (optional). If not supplied it will be empty.
  121.  */
  122. fun <E : Throwable> Logger.warn(exception: E?, message: (E?) -> Any? = { "" }) {
  123.     if (exception == null) log(WARNING) { message(null)?.toString() }
  124.     else log(WARNING, exception, message)
  125. }

  126. /**
  127.  * Log a message using [ERROR] level with associated exception information.
  128.  *
  129.  * @param exception The exception associated with log message.
  130.  * @param message The message to log (function to optional). If not supplied it will be empty.
  131.  */
  132. fun <E : Throwable> Logger.error(exception: E?, message: (E?) -> Any? = { "" }) {
  133.     if (exception == null) log(ERROR) { message(null)?.toString() }
  134.     else log(ERROR, exception, message)
  135. }

  136. internal fun <T> stripAnsi(receiver: T?, apply: Boolean): String? =
  137.     receiver?.toString()?.let { if (apply) it.stripAnsi() else it }

  138. private fun Logger.logMessage(level: Level, message: () -> Any?) {
  139.     val messageSupplier = { stripAnsi(message(), !useColor) }
  140.     log(level, messageSupplier)
  141. }