From 2a095b5aa4a357b7db0236702efdcc05ddf38bad Mon Sep 17 00:00:00 2001 From: Flavio Brasil Date: Sun, 27 Oct 2024 12:54:06 -0700 Subject: [PATCH 1/5] [core] Log improvements --- .../kyo/internal/LogPlatformSpecific.scala | 2 +- .../kyo/internal/LogPlatformSpecific.scala | 38 +++-- .../shared/src/main/scala/kyo/KyoApp.scala | 6 +- kyo-core/shared/src/main/scala/kyo/Log.scala | 133 ++++++++++++------ .../main/scala/kyo/scheduler/IOPromise.scala | 6 +- .../src/test/scala/kyo/KyoAppTest.scala | 6 +- .../shared/src/test/scala/kyo/LogTest.scala | 46 ++++-- .../main/scala/examples/ledger/db/DB.scala | 2 +- 8 files changed, 155 insertions(+), 84 deletions(-) diff --git a/kyo-core/js/src/main/scala/kyo/internal/LogPlatformSpecific.scala b/kyo-core/js/src/main/scala/kyo/internal/LogPlatformSpecific.scala index fb2ffde70..f8e56a78c 100644 --- a/kyo-core/js/src/main/scala/kyo/internal/LogPlatformSpecific.scala +++ b/kyo-core/js/src/main/scala/kyo/internal/LogPlatformSpecific.scala @@ -3,4 +3,4 @@ package kyo.internal import kyo.Log trait LogPlatformSpecific: - val unsafe: Log.Unsafe = Log.Unsafe.ConsoleLogger("kyo.logs") + val live: Log = Log(Log.Unsafe.ConsoleLogger("kyo.logs", Log.Level.Debug)) diff --git a/kyo-core/jvm/src/main/scala/kyo/internal/LogPlatformSpecific.scala b/kyo-core/jvm/src/main/scala/kyo/internal/LogPlatformSpecific.scala index 997f1d34f..58ee9c1a3 100644 --- a/kyo-core/jvm/src/main/scala/kyo/internal/LogPlatformSpecific.scala +++ b/kyo-core/jvm/src/main/scala/kyo/internal/LogPlatformSpecific.scala @@ -3,9 +3,10 @@ package kyo.internal import kyo.AllowUnsafe import kyo.Frame import kyo.Log +import kyo.Log.Level trait LogPlatformSpecific: - val unsafe: Log.Unsafe = LogPlatformSpecific.Unsafe.SLF4J("kyo.logs") + val live: Log = Log(LogPlatformSpecific.Unsafe.SLF4J("kyo.logs")) object LogPlatformSpecific: @@ -16,45 +17,42 @@ object LogPlatformSpecific: def apply(name: String) = new SLF4J(org.slf4j.LoggerFactory.getLogger(name)) class SLF4J(logger: org.slf4j.Logger) extends Log.Unsafe: - inline def traceEnabled: Boolean = logger.isTraceEnabled - - inline def debugEnabled: Boolean = logger.isDebugEnabled - - inline def infoEnabled: Boolean = logger.isInfoEnabled - - inline def warnEnabled: Boolean = logger.isWarnEnabled - - inline def errorEnabled: Boolean = logger.isErrorEnabled + def level = + if logger.isTraceEnabled() then Level.Trace + else if logger.isDebugEnabled() then Level.Debug + else if logger.isInfoEnabled() then Level.Info + else if logger.isWarnEnabled() then Level.Warn + else Level.Error inline def trace(msg: => String)(using frame: Frame, allow: AllowUnsafe): Unit = - if traceEnabled then logger.trace(s"[${frame.parse.position}] $msg") + if Level.Trace.enabled(level) then logger.trace(s"[${frame.parse.position}] $msg") inline def trace(msg: => String, t: => Throwable)(using frame: Frame, allow: AllowUnsafe): Unit = - if traceEnabled then logger.trace(s"[${frame.parse.position}] $msg", t) + if Level.Trace.enabled(level) then logger.trace(s"[${frame.parse.position}] $msg", t) inline def debug(msg: => String)(using frame: Frame, allow: AllowUnsafe): Unit = - if debugEnabled then logger.debug(s"[${frame.parse.position}] $msg") + if Level.Debug.enabled(level) then logger.debug(s"[${frame.parse.position}] $msg") inline def debug(msg: => String, t: => Throwable)(using frame: Frame, allow: AllowUnsafe): Unit = - if debugEnabled then logger.debug(s"[${frame.parse.position}] $msg", t) + if Level.Debug.enabled(level) then logger.debug(s"[${frame.parse.position}] $msg", t) inline def info(msg: => String)(using frame: Frame, allow: AllowUnsafe): Unit = - if infoEnabled then logger.info(s"[${frame.parse.position}] $msg") + if Level.Info.enabled(level) then logger.info(s"[${frame.parse.position}] $msg") inline def info(msg: => String, t: => Throwable)(using frame: Frame, allow: AllowUnsafe): Unit = - if infoEnabled then logger.info(s"[${frame.parse.position}] $msg", t) + if Level.Info.enabled(level) then logger.info(s"[${frame.parse.position}] $msg", t) inline def warn(msg: => String)(using frame: Frame, allow: AllowUnsafe): Unit = - if warnEnabled then logger.warn(s"[${frame.parse.position}] $msg") + if Level.Warn.enabled(level) then logger.warn(s"[${frame.parse.position}] $msg") inline def warn(msg: => String, t: => Throwable)(using frame: Frame, allow: AllowUnsafe): Unit = - if warnEnabled then logger.warn(s"[${frame.parse.position}] $msg", t) + if Level.Warn.enabled(level) then logger.warn(s"[${frame.parse.position}] $msg", t) inline def error(msg: => String)(using frame: Frame, allow: AllowUnsafe): Unit = - if errorEnabled then logger.error(s"[${frame.parse.position}] $msg") + if Level.Error.enabled(level) then logger.error(s"[${frame.parse.position}] $msg") inline def error(msg: => String, t: => Throwable)(using frame: Frame, allow: AllowUnsafe): Unit = - if errorEnabled then logger.error(s"[${frame.parse.position}] $msg", t) + if Level.Error.enabled(level) then logger.error(s"[${frame.parse.position}] $msg", t) end SLF4J end Unsafe end LogPlatformSpecific diff --git a/kyo-core/shared/src/main/scala/kyo/KyoApp.scala b/kyo-core/shared/src/main/scala/kyo/KyoApp.scala index 0133adf40..2149ad078 100644 --- a/kyo-core/shared/src/main/scala/kyo/KyoApp.scala +++ b/kyo-core/shared/src/main/scala/kyo/KyoApp.scala @@ -10,9 +10,9 @@ import scala.collection.mutable.ListBuffer * Note: This class and its methods are unsafe and should only be used as the entrypoint of an application. */ abstract class KyoApp extends KyoApp.Base[KyoApp.Effects]: - def log: Log.Unsafe = Log.unsafe - def random: Random = Random.live - def clock: Clock = Clock.live + def log: Log = Log.live + def random: Random = Random.live + def clock: Clock = Clock.live override protected def handle[A: Flat](v: A < KyoApp.Effects)(using Frame): Unit = import AllowUnsafe.embrace.danger diff --git a/kyo-core/shared/src/main/scala/kyo/Log.scala b/kyo-core/shared/src/main/scala/kyo/Log.scala index 7fda6dbd0..1df92deea 100644 --- a/kyo-core/shared/src/main/scala/kyo/Log.scala +++ b/kyo-core/shared/src/main/scala/kyo/Log.scala @@ -2,10 +2,28 @@ package kyo import kyo.internal.LogPlatformSpecific +final case class Log(unsafe: Log.Unsafe) extends AnyVal: + def level: Log.Level = unsafe.level + inline def trace(inline msg: => String)(using inline frame: Frame): Unit < IO = IO.Unsafe(unsafe.trace(msg)) + inline def trace(inline msg: => String, inline t: => Throwable)(using inline frame: Frame): Unit < IO = IO.Unsafe(unsafe.trace(msg, t)) + inline def debug(inline msg: => String)(using inline frame: Frame): Unit < IO = IO.Unsafe(unsafe.debug(msg)) + inline def debug(inline msg: => String, inline t: => Throwable)(using inline frame: Frame): Unit < IO = IO.Unsafe(unsafe.debug(msg, t)) + inline def info(inline msg: => String)(using inline frame: Frame): Unit < IO = IO.Unsafe(unsafe.info(msg)) + inline def info(inline msg: => String, inline t: => Throwable)(using inline frame: Frame): Unit < IO = IO.Unsafe(unsafe.info(msg, t)) + inline def warn(inline msg: => String)(using inline frame: Frame): Unit < IO = IO.Unsafe(unsafe.warn(msg)) + inline def warn(inline msg: => String, t: => Throwable)(using inline frame: Frame): Unit < IO = IO.Unsafe(unsafe.warn(msg, t)) + inline def error(inline msg: => String)(using inline frame: Frame): Unit < IO = IO.Unsafe(unsafe.error(msg)) + inline def error(inline msg: => String, t: => Throwable)(using inline frame: Frame): Unit < IO = IO.Unsafe(unsafe.error(msg, t)) +end Log + /** Logging utility object for Kyo applications. */ object Log extends LogPlatformSpecific: - private val local = Local.init[Unsafe](unsafe) + enum Level: + case Trace, Debug, Info, Warn, Error + def enabled(minLevel: Level): Boolean = this.ordinal >= minLevel.ordinal + + private val local = Local.init(live) /** Executes a function with a custom Unsafe logger. * @@ -16,16 +34,52 @@ object Log extends LogPlatformSpecific: * @return * The result of the function execution */ - def let[A, S](u: Unsafe)(f: => A < (IO & S))(using Frame): A < (IO & S) = - local.let(u)(f) + def let[A, S](log: Log)(f: => A < S)(using Frame): A < S = + local.let(log)(f) + + /** Gets the current logger from the local context. + * + * @return + * The current Log instance wrapped in an effect + */ + def get(using Frame): Log < Any = local.get + + /** Executes a function with access to the current logger. + * + * @param f + * The function to execute, which takes a Log instance as input + * @return + * The result of the function execution + */ + def use[A, S](f: Log => A < S)(using Frame): A < S = local.use(f) + + /** Executes an effect with a console logger using the default name "kyo.logs" and debug level. + * + * @param v + * The effect to execute with the console logger + * @return + * The result of executing the effect with the console logger + */ + def withConsoleLogger[A, S](v: A < S)(using Frame): A < S = + withConsoleLogger()(v) + + /** Executes an effect with a console logger using a custom name and log level. + * + * @param name + * The name to use for the console logger + * @param level + * The log level + * @param v + * The effect to execute with the console logger + * @return + * The result of executing the effect with the console logger + */ + def withConsoleLogger[A, S](name: String = "kyo.logs", level: Level = Level.Debug)(v: A < S)(using Frame): A < S = + let(Log(Unsafe.ConsoleLogger(name, level)))(v) /** WARNING: Low-level API meant for integrations, libraries, and performance-sensitive code. See AllowUnsafe for more details. */ abstract class Unsafe: - def traceEnabled: Boolean - def debugEnabled: Boolean - def infoEnabled: Boolean - def warnEnabled: Boolean - def errorEnabled: Boolean + def level: Level def trace(msg: => String)(using frame: Frame, allow: AllowUnsafe): Unit def trace(msg: => String, t: => Throwable)(using frame: Frame, allow: AllowUnsafe): Unit @@ -37,89 +91,82 @@ object Log extends LogPlatformSpecific: def warn(msg: => String, t: => Throwable)(using frame: Frame, allow: AllowUnsafe): Unit def error(msg: => String)(using frame: Frame, allow: AllowUnsafe): Unit def error(msg: => String, t: => Throwable)(using frame: Frame, allow: AllowUnsafe): Unit + + def safe: Log = Log(this) end Unsafe /** WARNING: Low-level API meant for integrations, libraries, and performance-sensitive code. See AllowUnsafe for more details. */ object Unsafe: - class ConsoleLogger(name: String) extends Log.Unsafe: - inline def traceEnabled: Boolean = true - - inline def debugEnabled: Boolean = true - - inline def infoEnabled: Boolean = true - - inline def warnEnabled: Boolean = true - - inline def errorEnabled: Boolean = true - + case class ConsoleLogger(name: String, level: Level) extends Log.Unsafe: inline def trace(msg: => String)( using frame: Frame, allow: AllowUnsafe - ): Unit = if traceEnabled then println(s"TRACE $name -- [${frame.parse.position}] $msg") + ): Unit = if Level.Trace.enabled(level) then println(s"TRACE $name -- [${frame.parse.position}] $msg") inline def trace(msg: => String, t: => Throwable)( using frame: Frame, allow: AllowUnsafe - ): Unit = if traceEnabled then println(s"TRACE $name -- [${frame.parse.position}] $msg $t") + ): Unit = if Level.Trace.enabled(level) then println(s"TRACE $name -- [${frame.parse.position}] $msg $t") inline def debug(msg: => String)( using frame: Frame, allow: AllowUnsafe - ): Unit = if debugEnabled then println(s"DEBUG $name -- [${frame.parse.position}] $msg") + ): Unit = + if Level.Debug.enabled(level) then println(s"DEBUG $name -- [${frame.parse.position}] $msg") inline def debug(msg: => String, t: => Throwable)( using frame: Frame, allow: AllowUnsafe - ): Unit = if debugEnabled then println(s"DEBUG $name -- [${frame.parse.position}] $msg $t") + ): Unit = if Level.Debug.enabled(level) then println(s"DEBUG $name -- [${frame.parse.position}] $msg $t") inline def info(msg: => String)( using frame: Frame, allow: AllowUnsafe - ): Unit = if infoEnabled then println(s"INFO $name -- [${frame.parse.position}] $msg") + ): Unit = if Level.Info.enabled(level) then println(s"INFO $name -- [${frame.parse.position}] $msg") inline def info(msg: => String, t: => Throwable)( using frame: Frame, allow: AllowUnsafe - ): Unit = if infoEnabled then println(s"INFO $name -- [${frame.parse.position}] $msg $t") + ): Unit = if Level.Info.enabled(level) then println(s"INFO $name -- [${frame.parse.position}] $msg $t") inline def warn(msg: => String)( using frame: Frame, allow: AllowUnsafe - ): Unit = if warnEnabled then println(s"WARN $name -- [${frame.parse.position}] $msg") + ): Unit = if Level.Warn.enabled(level) then println(s"WARN $name -- [${frame.parse.position}] $msg") inline def warn(msg: => String, t: => Throwable)( using frame: Frame, allow: AllowUnsafe - ): Unit = if warnEnabled then println(s"WARN $name -- [${frame.parse.position}] $msg $t") + ): Unit = if Level.Warn.enabled(level) then println(s"WARN $name -- [${frame.parse.position}] $msg $t") inline def error(msg: => String)( using frame: Frame, allow: AllowUnsafe - ): Unit = if errorEnabled then println(s"ERROR $name -- [${frame.parse.position}] $msg") + ): Unit = if Level.Error.enabled(level) then println(s"ERROR $name -- [${frame.parse.position}] $msg") inline def error(msg: => String, t: => Throwable)( using frame: Frame, allow: AllowUnsafe - ): Unit = if errorEnabled then println(s"ERROR $name -- [${frame.parse.position}] $msg $t") + ): Unit = if Level.Error.enabled(level) then println(s"ERROR $name -- [${frame.parse.position}] $msg $t") end ConsoleLogger end Unsafe - private inline def logWhen(inline enabled: Unsafe => Boolean)(inline log: AllowUnsafe ?=> Unsafe => Unit)(using + private inline def logWhen(inline level: Level)(inline doLog: Log => Unit < IO)(using inline frame: Frame ): Unit < IO = - local.use { unsafe => - if enabled(unsafe) then - IO.Unsafe(log(unsafe)) + use { log => + if level.enabled(log.level) then + IO.Unsafe(doLog(log)) else ( ) @@ -133,7 +180,7 @@ object Log extends LogPlatformSpecific: * An IO effect that logs the message */ inline def trace(inline msg: => String)(using inline frame: Frame): Unit < IO = - logWhen(_.traceEnabled)(_.trace(msg)) + logWhen(Level.Trace)(_.trace(msg)) /** Logs a trace message with an exception. * @@ -145,7 +192,7 @@ object Log extends LogPlatformSpecific: * An IO effect that logs the message and exception */ inline def trace(inline msg: => String, inline t: => Throwable)(using inline frame: Frame): Unit < IO = - logWhen(_.traceEnabled)(_.trace(msg, t)) + logWhen(Level.Trace)(_.trace(msg, t)) /** Logs a debug message. * @@ -155,7 +202,7 @@ object Log extends LogPlatformSpecific: * An IO effect that logs the message */ inline def debug(inline msg: => String)(using inline frame: Frame): Unit < IO = - logWhen(_.debugEnabled)(_.debug(msg)) + logWhen(Level.Debug)(_.debug(msg)) /** Logs a debug message with an exception. * @@ -167,7 +214,7 @@ object Log extends LogPlatformSpecific: * An IO effect that logs the message and exception */ inline def debug(inline msg: => String, inline t: => Throwable)(using inline frame: Frame): Unit < IO = - logWhen(_.debugEnabled)(_.debug(msg, t)) + logWhen(Level.Debug)(_.debug(msg, t)) /** Logs an info message. * @@ -177,7 +224,7 @@ object Log extends LogPlatformSpecific: * An IO effect that logs the message */ inline def info(inline msg: => String)(using inline frame: Frame): Unit < IO = - logWhen(_.infoEnabled)(_.info(msg)) + logWhen(Level.Info)(_.info(msg)) /** Logs an info message with an exception. * @@ -189,7 +236,7 @@ object Log extends LogPlatformSpecific: * An IO effect that logs the message and exception */ inline def info(inline msg: => String, inline t: => Throwable)(using inline frame: Frame): Unit < IO = - logWhen(_.infoEnabled)(_.info(msg, t)) + logWhen(Level.Info)(_.info(msg, t)) /** Logs a warning message. * @@ -199,7 +246,7 @@ object Log extends LogPlatformSpecific: * An IO effect that logs the message */ inline def warn(inline msg: => String)(using inline frame: Frame): Unit < IO = - logWhen(_.warnEnabled)(_.warn(msg)) + logWhen(Level.Warn)(_.warn(msg)) /** Logs a warning message with an exception. * @@ -211,7 +258,7 @@ object Log extends LogPlatformSpecific: * An IO effect that logs the message and exception */ inline def warn(inline msg: => String, inline t: => Throwable)(using inline frame: Frame): Unit < IO = - logWhen(_.warnEnabled)(_.warn(msg, t)) + logWhen(Level.Warn)(_.warn(msg, t)) /** Logs an error message. * @@ -221,7 +268,7 @@ object Log extends LogPlatformSpecific: * An IO effect that logs the message */ inline def error(inline msg: => String)(using inline frame: Frame): Unit < IO = - logWhen(_.errorEnabled)(_.error(msg)) + logWhen(Level.Error)(_.error(msg)) /** Logs an error message with an exception. * @@ -233,6 +280,6 @@ object Log extends LogPlatformSpecific: * An IO effect that logs the message and exception */ inline def error(inline msg: => String, inline t: => Throwable)(using inline frame: Frame): Unit < IO = - logWhen(_.errorEnabled)(_.error(msg, t)) + logWhen(Level.Error)(_.error(msg, t)) end Log diff --git a/kyo-core/shared/src/main/scala/kyo/scheduler/IOPromise.scala b/kyo-core/shared/src/main/scala/kyo/scheduler/IOPromise.scala index acab95321..4ed298699 100644 --- a/kyo-core/shared/src/main/scala/kyo/scheduler/IOPromise.scala +++ b/kyo-core/shared/src/main/scala/kyo/scheduler/IOPromise.scala @@ -58,7 +58,7 @@ private[kyo] class IOPromise[+E, +A](init: State[E, A]) extends Safepoint.Interc catch case ex if NonFatal(ex) => import AllowUnsafe.embrace.danger - Log.unsafe.error("uncaught exception", ex) + Log.live.unsafe.error("uncaught exception", ex) interruptsLoop(this) end interrupts @@ -135,7 +135,7 @@ private[kyo] class IOPromise[+E, +A](init: State[E, A]) extends Safepoint.Interc case ex if NonFatal(ex) => given Frame = Frame.internal import AllowUnsafe.embrace.danger - Log.unsafe.error("uncaught exception", ex) + Log.live.unsafe.error("uncaught exception", ex) onCompleteLoop(this) end onComplete @@ -256,7 +256,7 @@ private[kyo] object IOPromise extends IOPromisePlatformSpecific: case ex if NonFatal(ex) => given Frame = Frame.internal import AllowUnsafe.embrace.danger - Log.unsafe.error("uncaught exception", ex) + Log.live.unsafe.error("uncaught exception", ex) end try self end run diff --git a/kyo-core/shared/src/test/scala/kyo/KyoAppTest.scala b/kyo-core/shared/src/test/scala/kyo/KyoAppTest.scala index 454f1bea4..0f23414e1 100644 --- a/kyo-core/shared/src/test/scala/kyo/KyoAppTest.scala +++ b/kyo-core/shared/src/test/scala/kyo/KyoAppTest.scala @@ -110,9 +110,9 @@ class KyoAppTest extends Test: override def unsafe = ??? app = new KyoApp: - override val log: Log.Unsafe = Log.Unsafe.ConsoleLogger("ConsoleLogger") - override val clock: Clock = testClock - override val random: Random = testRandom + override val log: Log = Log(Log.Unsafe.ConsoleLogger("ConsoleLogger", Log.Level.Debug)) + override val clock: Clock = testClock + override val random: Random = testRandom run { for _ <- Clock.now.map(i => instantRef.update(_ => i)) diff --git a/kyo-core/shared/src/test/scala/kyo/LogTest.scala b/kyo-core/shared/src/test/scala/kyo/LogTest.scala index 390712e0d..a7df23a2c 100644 --- a/kyo-core/shared/src/test/scala/kyo/LogTest.scala +++ b/kyo-core/shared/src/test/scala/kyo/LogTest.scala @@ -24,16 +24,42 @@ class LogTest extends Test: "unsafe" in { import AllowUnsafe.embrace.danger - Log.unsafe.trace("trace") - Log.unsafe.debug("debug") - Log.unsafe.info("info") - Log.unsafe.warn("warn") - Log.unsafe.error("error") - Log.unsafe.trace("trace", ex) - Log.unsafe.debug("debug", ex) - Log.unsafe.info("info", ex) - Log.unsafe.warn("warn", ex) - Log.unsafe.error("error", ex) + Log.live.unsafe.trace("trace") + Log.live.unsafe.debug("debug") + Log.live.unsafe.info("info") + Log.live.unsafe.warn("warn") + Log.live.unsafe.error("error") + Log.live.unsafe.trace("trace", ex) + Log.live.unsafe.debug("debug", ex) + Log.live.unsafe.info("info", ex) + Log.live.unsafe.warn("warn", ex) + Log.live.unsafe.error("error", ex) succeed } + + "withConsoleLogger" in { + val output = new StringBuilder + scala.Console.withOut(new java.io.PrintStream(new java.io.OutputStream: + override def write(b: Int): Unit = output.append(b.toChar) + )) { + import AllowUnsafe.embrace.danger + IO.Unsafe.run { + for + _ <- Log.withConsoleLogger("test.logger", Log.Level.Debug) { + for + _ <- Log.trace("won't show up") + _ <- Log.debug("test message") + _ <- Log.info("info message") + _ <- Log.warn("warning", new Exception("test exception")) + yield () + } + yield + val logs = output.toString.trim.split("\n") + assert(logs.length == 3) + assert(logs(0).matches("DEBUG test.logger -- \\[.*\\] test message")) + assert(logs(1).matches("INFO test.logger -- \\[.*\\] info message")) + assert(logs(2).matches("WARN test.logger -- \\[.*\\] warning java.lang.Exception: test exception")) + }.eval + } + } end LogTest diff --git a/kyo-examples/jvm/src/main/scala/examples/ledger/db/DB.scala b/kyo-examples/jvm/src/main/scala/examples/ledger/db/DB.scala index b9b215d9d..b53f7eb85 100644 --- a/kyo-examples/jvm/src/main/scala/examples/ledger/db/DB.scala +++ b/kyo-examples/jvm/src/main/scala/examples/ledger/db/DB.scala @@ -30,7 +30,7 @@ object DB: Live(index, log) } - class Live(index: Index, log: Log) extends DB: + class Live(index: Index, log: db.Log) extends DB: def transaction(account: Int, amount: Int, desc: String): Result < IO = index.transaction(account, amount, desc).map { From 2a13e19f8268f9e1304c8d29a341da50e0e778be Mon Sep 17 00:00:00 2001 From: Flavio Brasil Date: Sun, 27 Oct 2024 20:01:42 -0700 Subject: [PATCH 2/5] address review feedback --- .../kyo/internal/LogPlatformSpecific.scala | 30 +++++------ kyo-core/shared/src/main/scala/kyo/Log.scala | 54 ++++++++++--------- .../src/test/scala/kyo/KyoAppTest.scala | 2 +- .../shared/src/test/scala/kyo/LogTest.scala | 2 +- 4 files changed, 47 insertions(+), 41 deletions(-) diff --git a/kyo-core/jvm/src/main/scala/kyo/internal/LogPlatformSpecific.scala b/kyo-core/jvm/src/main/scala/kyo/internal/LogPlatformSpecific.scala index 58ee9c1a3..7964b1b77 100644 --- a/kyo-core/jvm/src/main/scala/kyo/internal/LogPlatformSpecific.scala +++ b/kyo-core/jvm/src/main/scala/kyo/internal/LogPlatformSpecific.scala @@ -18,41 +18,41 @@ object LogPlatformSpecific: class SLF4J(logger: org.slf4j.Logger) extends Log.Unsafe: def level = - if logger.isTraceEnabled() then Level.Trace - else if logger.isDebugEnabled() then Level.Debug - else if logger.isInfoEnabled() then Level.Info - else if logger.isWarnEnabled() then Level.Warn - else Level.Error + if logger.isTraceEnabled() then Level.trace + else if logger.isDebugEnabled() then Level.debug + else if logger.isInfoEnabled() then Level.info + else if logger.isWarnEnabled() then Level.warn + else Level.error inline def trace(msg: => String)(using frame: Frame, allow: AllowUnsafe): Unit = - if Level.Trace.enabled(level) then logger.trace(s"[${frame.parse.position}] $msg") + if Level.trace.enabled(level) then logger.trace(s"[${frame.parse.position}] $msg") inline def trace(msg: => String, t: => Throwable)(using frame: Frame, allow: AllowUnsafe): Unit = - if Level.Trace.enabled(level) then logger.trace(s"[${frame.parse.position}] $msg", t) + if Level.trace.enabled(level) then logger.trace(s"[${frame.parse.position}] $msg", t) inline def debug(msg: => String)(using frame: Frame, allow: AllowUnsafe): Unit = - if Level.Debug.enabled(level) then logger.debug(s"[${frame.parse.position}] $msg") + if Level.debug.enabled(level) then logger.debug(s"[${frame.parse.position}] $msg") inline def debug(msg: => String, t: => Throwable)(using frame: Frame, allow: AllowUnsafe): Unit = - if Level.Debug.enabled(level) then logger.debug(s"[${frame.parse.position}] $msg", t) + if Level.debug.enabled(level) then logger.debug(s"[${frame.parse.position}] $msg", t) inline def info(msg: => String)(using frame: Frame, allow: AllowUnsafe): Unit = - if Level.Info.enabled(level) then logger.info(s"[${frame.parse.position}] $msg") + if Level.info.enabled(level) then logger.info(s"[${frame.parse.position}] $msg") inline def info(msg: => String, t: => Throwable)(using frame: Frame, allow: AllowUnsafe): Unit = - if Level.Info.enabled(level) then logger.info(s"[${frame.parse.position}] $msg", t) + if Level.info.enabled(level) then logger.info(s"[${frame.parse.position}] $msg", t) inline def warn(msg: => String)(using frame: Frame, allow: AllowUnsafe): Unit = - if Level.Warn.enabled(level) then logger.warn(s"[${frame.parse.position}] $msg") + if Level.warn.enabled(level) then logger.warn(s"[${frame.parse.position}] $msg") inline def warn(msg: => String, t: => Throwable)(using frame: Frame, allow: AllowUnsafe): Unit = - if Level.Warn.enabled(level) then logger.warn(s"[${frame.parse.position}] $msg", t) + if Level.warn.enabled(level) then logger.warn(s"[${frame.parse.position}] $msg", t) inline def error(msg: => String)(using frame: Frame, allow: AllowUnsafe): Unit = - if Level.Error.enabled(level) then logger.error(s"[${frame.parse.position}] $msg") + if Level.error.enabled(level) then logger.error(s"[${frame.parse.position}] $msg") inline def error(msg: => String, t: => Throwable)(using frame: Frame, allow: AllowUnsafe): Unit = - if Level.Error.enabled(level) then logger.error(s"[${frame.parse.position}] $msg", t) + if Level.error.enabled(level) then logger.error(s"[${frame.parse.position}] $msg", t) end SLF4J end Unsafe end LogPlatformSpecific diff --git a/kyo-core/shared/src/main/scala/kyo/Log.scala b/kyo-core/shared/src/main/scala/kyo/Log.scala index 1df92deea..4307201b8 100644 --- a/kyo-core/shared/src/main/scala/kyo/Log.scala +++ b/kyo-core/shared/src/main/scala/kyo/Log.scala @@ -19,9 +19,15 @@ end Log /** Logging utility object for Kyo applications. */ object Log extends LogPlatformSpecific: - enum Level: - case Trace, Debug, Info, Warn, Error - def enabled(minLevel: Level): Boolean = this.ordinal >= minLevel.ordinal + case class Level private (priority: Int) extends AnyVal: + def enabled(minLevel: Level) = priority >= minLevel.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) + end Level private val local = Local.init(live) @@ -74,7 +80,7 @@ object Log extends LogPlatformSpecific: * @return * The result of executing the effect with the console logger */ - def withConsoleLogger[A, S](name: String = "kyo.logs", level: Level = Level.Debug)(v: A < S)(using Frame): A < S = + def withConsoleLogger[A, S](name: String = "kyo.logs", level: Level = Level.debug)(v: A < S)(using Frame): A < S = let(Log(Unsafe.ConsoleLogger(name, level)))(v) /** WARNING: Low-level API meant for integrations, libraries, and performance-sensitive code. See AllowUnsafe for more details. */ @@ -102,62 +108,62 @@ object Log extends LogPlatformSpecific: using frame: Frame, allow: AllowUnsafe - ): Unit = if Level.Trace.enabled(level) then println(s"TRACE $name -- [${frame.parse.position}] $msg") + ): Unit = if Level.trace.enabled(level) then println(s"TRACE $name -- [${frame.parse.position}] $msg") inline def trace(msg: => String, t: => Throwable)( using frame: Frame, allow: AllowUnsafe - ): Unit = if Level.Trace.enabled(level) then println(s"TRACE $name -- [${frame.parse.position}] $msg $t") + ): Unit = if Level.trace.enabled(level) then println(s"TRACE $name -- [${frame.parse.position}] $msg $t") inline def debug(msg: => String)( using frame: Frame, allow: AllowUnsafe ): Unit = - if Level.Debug.enabled(level) then println(s"DEBUG $name -- [${frame.parse.position}] $msg") + if Level.debug.enabled(level) then println(s"DEBUG $name -- [${frame.parse.position}] $msg") inline def debug(msg: => String, t: => Throwable)( using frame: Frame, allow: AllowUnsafe - ): Unit = if Level.Debug.enabled(level) then println(s"DEBUG $name -- [${frame.parse.position}] $msg $t") + ): Unit = if Level.debug.enabled(level) then println(s"DEBUG $name -- [${frame.parse.position}] $msg $t") inline def info(msg: => String)( using frame: Frame, allow: AllowUnsafe - ): Unit = if Level.Info.enabled(level) then println(s"INFO $name -- [${frame.parse.position}] $msg") + ): Unit = if Level.info.enabled(level) then println(s"INFO $name -- [${frame.parse.position}] $msg") inline def info(msg: => String, t: => Throwable)( using frame: Frame, allow: AllowUnsafe - ): Unit = if Level.Info.enabled(level) then println(s"INFO $name -- [${frame.parse.position}] $msg $t") + ): Unit = if Level.info.enabled(level) then println(s"INFO $name -- [${frame.parse.position}] $msg $t") inline def warn(msg: => String)( using frame: Frame, allow: AllowUnsafe - ): Unit = if Level.Warn.enabled(level) then println(s"WARN $name -- [${frame.parse.position}] $msg") + ): Unit = if Level.warn.enabled(level) then println(s"WARN $name -- [${frame.parse.position}] $msg") inline def warn(msg: => String, t: => Throwable)( using frame: Frame, allow: AllowUnsafe - ): Unit = if Level.Warn.enabled(level) then println(s"WARN $name -- [${frame.parse.position}] $msg $t") + ): Unit = if Level.warn.enabled(level) then println(s"WARN $name -- [${frame.parse.position}] $msg $t") inline def error(msg: => String)( using frame: Frame, allow: AllowUnsafe - ): Unit = if Level.Error.enabled(level) then println(s"ERROR $name -- [${frame.parse.position}] $msg") + ): Unit = if Level.error.enabled(level) then println(s"ERROR $name -- [${frame.parse.position}] $msg") inline def error(msg: => String, t: => Throwable)( using frame: Frame, allow: AllowUnsafe - ): Unit = if Level.Error.enabled(level) then println(s"ERROR $name -- [${frame.parse.position}] $msg $t") + ): Unit = if Level.error.enabled(level) then println(s"ERROR $name -- [${frame.parse.position}] $msg $t") end ConsoleLogger end Unsafe @@ -180,7 +186,7 @@ object Log extends LogPlatformSpecific: * An IO effect that logs the message */ inline def trace(inline msg: => String)(using inline frame: Frame): Unit < IO = - logWhen(Level.Trace)(_.trace(msg)) + logWhen(Level.trace)(_.trace(msg)) /** Logs a trace message with an exception. * @@ -192,7 +198,7 @@ object Log extends LogPlatformSpecific: * An IO effect that logs the message and exception */ inline def trace(inline msg: => String, inline t: => Throwable)(using inline frame: Frame): Unit < IO = - logWhen(Level.Trace)(_.trace(msg, t)) + logWhen(Level.trace)(_.trace(msg, t)) /** Logs a debug message. * @@ -202,7 +208,7 @@ object Log extends LogPlatformSpecific: * An IO effect that logs the message */ inline def debug(inline msg: => String)(using inline frame: Frame): Unit < IO = - logWhen(Level.Debug)(_.debug(msg)) + logWhen(Level.debug)(_.debug(msg)) /** Logs a debug message with an exception. * @@ -214,7 +220,7 @@ object Log extends LogPlatformSpecific: * An IO effect that logs the message and exception */ inline def debug(inline msg: => String, inline t: => Throwable)(using inline frame: Frame): Unit < IO = - logWhen(Level.Debug)(_.debug(msg, t)) + logWhen(Level.debug)(_.debug(msg, t)) /** Logs an info message. * @@ -224,7 +230,7 @@ object Log extends LogPlatformSpecific: * An IO effect that logs the message */ inline def info(inline msg: => String)(using inline frame: Frame): Unit < IO = - logWhen(Level.Info)(_.info(msg)) + logWhen(Level.info)(_.info(msg)) /** Logs an info message with an exception. * @@ -236,7 +242,7 @@ object Log extends LogPlatformSpecific: * An IO effect that logs the message and exception */ inline def info(inline msg: => String, inline t: => Throwable)(using inline frame: Frame): Unit < IO = - logWhen(Level.Info)(_.info(msg, t)) + logWhen(Level.info)(_.info(msg, t)) /** Logs a warning message. * @@ -246,7 +252,7 @@ object Log extends LogPlatformSpecific: * An IO effect that logs the message */ inline def warn(inline msg: => String)(using inline frame: Frame): Unit < IO = - logWhen(Level.Warn)(_.warn(msg)) + logWhen(Level.warn)(_.warn(msg)) /** Logs a warning message with an exception. * @@ -258,7 +264,7 @@ object Log extends LogPlatformSpecific: * An IO effect that logs the message and exception */ inline def warn(inline msg: => String, inline t: => Throwable)(using inline frame: Frame): Unit < IO = - logWhen(Level.Warn)(_.warn(msg, t)) + logWhen(Level.warn)(_.warn(msg, t)) /** Logs an error message. * @@ -268,7 +274,7 @@ object Log extends LogPlatformSpecific: * An IO effect that logs the message */ inline def error(inline msg: => String)(using inline frame: Frame): Unit < IO = - logWhen(Level.Error)(_.error(msg)) + logWhen(Level.error)(_.error(msg)) /** Logs an error message with an exception. * @@ -280,6 +286,6 @@ object Log extends LogPlatformSpecific: * An IO effect that logs the message and exception */ inline def error(inline msg: => String, inline t: => Throwable)(using inline frame: Frame): Unit < IO = - logWhen(Level.Error)(_.error(msg, t)) + logWhen(Level.error)(_.error(msg, t)) end Log diff --git a/kyo-core/shared/src/test/scala/kyo/KyoAppTest.scala b/kyo-core/shared/src/test/scala/kyo/KyoAppTest.scala index 0f23414e1..883edd18c 100644 --- a/kyo-core/shared/src/test/scala/kyo/KyoAppTest.scala +++ b/kyo-core/shared/src/test/scala/kyo/KyoAppTest.scala @@ -110,7 +110,7 @@ class KyoAppTest extends Test: override def unsafe = ??? app = new KyoApp: - override val log: Log = Log(Log.Unsafe.ConsoleLogger("ConsoleLogger", Log.Level.Debug)) + override val log: Log = Log(Log.Unsafe.ConsoleLogger("ConsoleLogger", Log.Level.debug)) override val clock: Clock = testClock override val random: Random = testRandom run { diff --git a/kyo-core/shared/src/test/scala/kyo/LogTest.scala b/kyo-core/shared/src/test/scala/kyo/LogTest.scala index a7df23a2c..84a8409d9 100644 --- a/kyo-core/shared/src/test/scala/kyo/LogTest.scala +++ b/kyo-core/shared/src/test/scala/kyo/LogTest.scala @@ -45,7 +45,7 @@ class LogTest extends Test: import AllowUnsafe.embrace.danger IO.Unsafe.run { for - _ <- Log.withConsoleLogger("test.logger", Log.Level.Debug) { + _ <- Log.withConsoleLogger("test.logger", Log.Level.debug) { for _ <- Log.trace("won't show up") _ <- Log.debug("test message") From 9fa34f8303363abbd2b9b86db1c2b35c554b3fcb Mon Sep 17 00:00:00 2001 From: Flavio Brasil Date: Sun, 27 Oct 2024 20:26:36 -0700 Subject: [PATCH 3/5] fix js build --- .../js/src/main/scala/kyo/internal/LogPlatformSpecific.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kyo-core/js/src/main/scala/kyo/internal/LogPlatformSpecific.scala b/kyo-core/js/src/main/scala/kyo/internal/LogPlatformSpecific.scala index f8e56a78c..c8a57bae4 100644 --- a/kyo-core/js/src/main/scala/kyo/internal/LogPlatformSpecific.scala +++ b/kyo-core/js/src/main/scala/kyo/internal/LogPlatformSpecific.scala @@ -3,4 +3,4 @@ package kyo.internal import kyo.Log trait LogPlatformSpecific: - val live: Log = Log(Log.Unsafe.ConsoleLogger("kyo.logs", Log.Level.Debug)) + val live: Log = Log(Log.Unsafe.ConsoleLogger("kyo.logs", Log.Level.debug)) From 112a1a1d203503c6725cbb98ba28a7524ac3209d Mon Sep 17 00:00:00 2001 From: Flavio Brasil Date: Sun, 27 Oct 2024 21:40:15 -0700 Subject: [PATCH 4/5] don't expose level.priority --- kyo-core/shared/src/main/scala/kyo/Log.scala | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/kyo-core/shared/src/main/scala/kyo/Log.scala b/kyo-core/shared/src/main/scala/kyo/Log.scala index 4307201b8..e2cacb1c8 100644 --- a/kyo-core/shared/src/main/scala/kyo/Log.scala +++ b/kyo-core/shared/src/main/scala/kyo/Log.scala @@ -19,15 +19,16 @@ end Log /** Logging utility object for Kyo applications. */ object Log extends LogPlatformSpecific: - case class Level private (priority: Int) extends AnyVal: + final class Level private (private val priority: Int) extends AnyVal: def enabled(minLevel: Level) = priority >= minLevel.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) - end Level + end Level private val local = Local.init(live) From 716b0ed25c387a428f4f0865404c1b713dc87f49 Mon Sep 17 00:00:00 2001 From: Flavio Brasil Date: Sun, 27 Oct 2024 21:43:14 -0700 Subject: [PATCH 5/5] fix formatting --- kyo-core/shared/src/main/scala/kyo/Log.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kyo-core/shared/src/main/scala/kyo/Log.scala b/kyo-core/shared/src/main/scala/kyo/Log.scala index e2cacb1c8..8bd707c37 100644 --- a/kyo-core/shared/src/main/scala/kyo/Log.scala +++ b/kyo-core/shared/src/main/scala/kyo/Log.scala @@ -28,7 +28,7 @@ object Log extends LogPlatformSpecific: val info: Level = Level(30) val warn: Level = Level(40) val error: Level = Level(50) - end Level + end Level private val local = Local.init(live)