PatternFormat.kt

  1. package com.hexagontk.jul

  2. import com.hexagontk.core.text.AnsiColor.DEFAULT
  3. import com.hexagontk.core.text.AnsiColor.YELLOW
  4. import com.hexagontk.core.text.AnsiColor.BLUE
  5. import com.hexagontk.core.text.AnsiColor.BRIGHT_BLACK
  6. import com.hexagontk.core.text.AnsiColor.CYAN
  7. import com.hexagontk.core.text.AnsiColor.MAGENTA
  8. import com.hexagontk.core.text.AnsiColor.RED
  9. import com.hexagontk.core.text.AnsiEffect.BOLD
  10. import com.hexagontk.core.text.Ansi.RESET
  11. import com.hexagontk.core.text.eol
  12. import com.hexagontk.core.fail
  13. import com.hexagontk.core.toText
  14. import java.time.Instant
  15. import java.time.LocalDateTime
  16. import java.time.ZoneId
  17. import java.util.logging.Formatter
  18. import java.util.logging.Level
  19. import java.util.logging.LogRecord

  20. /**
  21.  * A Formatter implements [Formatter] provides support for formatting Logs.
  22.  *
  23.  * @property useColor Use colors in log messages.
  24.  */
  25. class PatternFormat(
  26.     private val useColor: Boolean,
  27.     private val messageOnly: Boolean = false,
  28. ) : Formatter() {

  29.     internal companion object {
  30.         private const val TIMESTAMP = "%tH:%<tM:%<tS,%<tL"
  31.         private const val LEVEL = "%-5s"
  32.         private const val THREAD = "[%-15s]"
  33.         private const val LOGGER = "%-30s"

  34.         const val PATTERN = "$TIMESTAMP $LEVEL $THREAD $LOGGER | %s%n"
  35.         const val COLOR_PATTERN =
  36.             "$BRIGHT_BLACK$TIMESTAMP %s$LEVEL$RESET $MAGENTA$THREAD $CYAN$LOGGER$RESET | %s%n$RESET"
  37.     }

  38.     private val pattern: String = if (useColor) COLOR_PATTERN else PATTERN

  39.     private val levelColors: Map<Level, String> = mapOf(
  40.         Level.FINER to DEFAULT,
  41.         Level.FINE to DEFAULT,
  42.         Level.INFO to BLUE,
  43.         Level.WARNING to YELLOW,
  44.         Level.SEVERE to RED + BOLD
  45.     )

  46.     private val levelNames: Map<Level, String> = mapOf(
  47.         Level.FINER to "TRACE",
  48.         Level.FINE to "DEBUG",
  49.         Level.INFO to "INFO",
  50.         Level.WARNING to "WARN",
  51.         Level.SEVERE to "ERROR"
  52.     )

  53.     override fun format(record: LogRecord): String {
  54.         if (messageOnly)
  55.             return record.message + "\n"

  56.         val instant = Instant.ofEpochMilli(record.millis)
  57.         val dateTime = LocalDateTime.ofInstant(instant, ZoneId.systemDefault())
  58.         val thrown = record.thrown
  59.         val trace = when {
  60.             thrown == null -> ""
  61.             useColor -> "$eol${thrown.toText()}".replace("\n", "\n$RED")
  62.             else -> "$eol${thrown.toText()}"
  63.         }
  64.         val level = record.level
  65.         val levelName = levelNames[level] ?: fail
  66.         val levelColor = levelColors[level] ?: BLUE
  67.         val message = record.message
  68.         val loggerName = record.loggerName
  69.         val threadName = Thread.currentThread().name

  70.         return if (useColor)
  71.             pattern.format(dateTime, levelColor, levelName, threadName, loggerName, message + trace)
  72.         else
  73.             pattern.format(dateTime, levelName, threadName, loggerName, message + trace)
  74.     }
  75. }