-
Notifications
You must be signed in to change notification settings - Fork 19
Open
Labels
Description
Currently, creating a logger is effectful because we create an IOLocal context.
object DefaultLogger:
def makeIo(output: Output[IO], outputs: Output[IO]*)(using Clock[IO], Printer, Filter): IO[DefaultLogger[IO]] =
for given StringLocal[IO] <- ioStringLocal
yield new DefaultLogger[IO](output, outputs*)
end DefaultLoggerThe actual interface requires a local instance to allow adding context to effects:
trait Logger[F[_]]:
val stringLocal: StringLocal[F]
def doLog(level: LogLevel, message: String)(using LogInfo): F[Unit]
...
end LoggerThis in turn allows us to scope context to an effect value whenever we have a logger in scope:
def program(using Logger[IO]): IO[Unit] =
for
_ <- Logger[IO].debug("This is some debug")
_ <- Logger[IO].info("HEY!")
_ <- Logger[IO].warn("I'm warning you")
_ <- Logger[IO].error("I give up")
yield ()
import Logger.*
val mainWithContext: IO[Unit] =
for
given Logger[IO] <- DefaultLogger.makeIo(consoleOutput)
_ <- program.withLogContext("trace-id", "4d334544-6462-43fa-b0b1-12846f871573")
_ <- Logger[IO].info("Now the context is gone")
yield ()This is very convenient and easy to use. There are 2 main drawbacks:
- The constructor of the logger is now effectful
- It's difficult to construct a correct instance of
Profunctorfor this interface
Challenges
If the Logger no longer has a reference to the local context, we place the burden on the user to pass around this context. It's not clear if there's a good solution to avoid this (maybe Odin has solved this?).
Proposal
Simplify the interface to something akin to:
case class LogLine(level: LogLevel, message: String, context: Map[String,String], info: LogInfo)
trait Logger[F[_]]:
def doLog(line: LogLine): F[Unit]
end Loggeri10416, etaque and dimitarg