Skip to content

Commit

Permalink
[core] Fix SLF4J log levels (#1046)
Browse files Browse the repository at this point in the history
I noticed that only `error` was enabled for my Kyo app, when I had it
set to info. Info logs should have shown up, but they didn't.

### Solution
Check the level of log level enabled in reverse order. In the previous
case, `error` was always enabled (unless `off`).

### Notes
- I changed `Level` to a `sealed abstract class`, enabling exhaustive
pattern matching. Avoiding `AnyVal` is generally better if we know we
will only have static instances.
- I also added a `CanEqual` for users.


### Checklist

- [X] Unit test all changes
- [X] Update scaladocs if needed
- [X] Update the README if needed

Co-authored-by: Flavio Brasil <[email protected]>
  • Loading branch information
hearnadam and fwbrasil authored Jan 22, 2025
1 parent 47ffbd5 commit 9621701
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,14 @@ object LogPlatformSpecific:
object SLF4J:
def apply(name: String) = new SLF4J(org.slf4j.LoggerFactory.getLogger(name))

class SLF4J(logger: org.slf4j.Logger) extends Log.Unsafe:
final class SLF4J(logger: org.slf4j.Logger) extends Log.Unsafe:
val level =
if logger.isErrorEnabled() then Level.error
else if logger.isWarnEnabled() then Level.warn
else if logger.isInfoEnabled() then Level.info
if logger.isTraceEnabled() then Level.trace
else if logger.isDebugEnabled() then Level.debug
else Level.trace
else if logger.isInfoEnabled() then Level.info
else if logger.isWarnEnabled() then Level.warn
else if logger.isErrorEnabled() then Level.error
else Level.silent

inline def trace(msg: => String)(using frame: Frame, allow: AllowUnsafe): Unit =
if Level.trace.enabled(level) then logger.trace(s"[${frame.position.show}] $msg")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package kyo.internal

import kyo.Log.Level
import kyo.Test
import org.slf4j.LoggerFactory

class LogPlatformSpecificTest extends Test:
private def loggerWithLevel(level: ch.qos.logback.classic.Level) =
val logger = LoggerFactory.getLogger("test")
logger.asInstanceOf[ch.qos.logback.classic.Logger].setLevel(level)
new LogPlatformSpecific.Unsafe.SLF4J(logger)
end loggerWithLevel

"SLF4J logger" - {
"trace" in {
assert(loggerWithLevel(ch.qos.logback.classic.Level.TRACE).level == Level.trace)
}

"debug" in {
assert(loggerWithLevel(ch.qos.logback.classic.Level.DEBUG).level == Level.debug)
}

"info" in {
assert(loggerWithLevel(ch.qos.logback.classic.Level.INFO).level == Level.info)
}

"warn" in {
assert(loggerWithLevel(ch.qos.logback.classic.Level.WARN).level == Level.warn)
}

"error" in {
assert(loggerWithLevel(ch.qos.logback.classic.Level.ERROR).level == Level.error)
}

"silent" in {
assert(loggerWithLevel(ch.qos.logback.classic.Level.OFF).level == Level.silent)
}
}
end LogPlatformSpecificTest
16 changes: 8 additions & 8 deletions kyo-core/shared/src/main/scala/kyo/Log.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,15 @@ end Log
/** Logging utility object for Kyo applications. */
object Log extends LogPlatformSpecific:

final case class Level private (private val priority: Int) extends AnyVal:
def enabled(maxLevel: Level) = maxLevel.priority <= priority

sealed abstract class Level(private val priority: Int) derives CanEqual:
def enabled(other: Level): Boolean = other.priority <= priority
object Level:
val trace: Level = Level(10)
val debug: Level = Level(20)
val info: Level = Level(30)
val warn: Level = Level(40)
val error: Level = Level(50)
case object trace extends Level(10)
case object debug extends Level(20)
case object info extends Level(30)
case object warn extends Level(40)
case object error extends Level(50)
case object silent extends Level(60)
end Level

private val local = Local.init(live)
Expand Down

0 comments on commit 9621701

Please sign in to comment.