Skip to content

Commit

Permalink
Fold deferred log messages into existing log message class
Browse files Browse the repository at this point in the history
  • Loading branch information
morgen-peschke committed May 31, 2024
1 parent d885fd5 commit 366d80e
Show file tree
Hide file tree
Showing 7 changed files with 311 additions and 295 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import cats.effect.kernel.Resource.ExitCase
import cats.effect.kernel.{Concurrent, Ref, Resource}
import cats.syntax.all.*
import org.typelevel.log4cats.Logger
import org.typelevel.log4cats.extras.DeferredLogger.DeferredLogMessage

/**
* `Logger` that does not immediately log.
Expand Down Expand Up @@ -49,13 +48,13 @@ import org.typelevel.log4cats.extras.DeferredLogger.DeferredLogMessage
* >>> WARNING: READ BEFORE USAGE! <<<
*/
trait DeferredLogger[F[_]] extends Logger[F] {

/**
* View the logs in the buffer.
*
* This is primarily useful for testing, and will not effect the behavior
* of calls to `log`
* This is primarily useful for testing, and will not effect the behavior of calls to `log`
*/
def inspect: F[Chain[DeferredLogMessage]]
def inspect: F[Chain[LogMessage]]

/**
* Log the deferred messages
Expand All @@ -67,70 +66,47 @@ trait DeferredLogger[F[_]] extends Logger[F] {
object DeferredLogger {
def apply[F[_]](logger: Logger[F])(implicit F: Concurrent[F]): Resource[F, DeferredLogger[F]] =
Resource
.makeCase(Ref.empty[F, Chain[DeferredLogMessage]]) { (ref, exitCase) =>
.makeCase(Ref.empty[F, Chain[LogMessage]]) { (ref, exitCase) =>
exitCase match {
case ExitCase.Succeeded => F.unit
case _ => ref.get.flatMap(_.traverse_(_.log(logger)))
case _ => ref.get.flatMap(_.traverse_(LogMessage.log(_, logger)))
}
}
.map { ref =>
new DeferredLogger[F] {
private def save(lm: DeferredLogMessage): F[Unit] = ref.update(_.append(lm))
private def save(lm: LogMessage): F[Unit] = ref.update(_.append(lm))

override def trace(t: Throwable)(msg: => String): F[Unit] = save(Trace(() => msg, t.some))
override def debug(t: Throwable)(msg: => String): F[Unit] = save(Debug(() => msg, t.some))
override def info(t: Throwable)(msg: => String): F[Unit] = save(Info(() => msg, t.some))
override def warn(t: Throwable)(msg: => String): F[Unit] = save(Warn(() => msg, t.some))
override def error(t: Throwable)(msg: => String): F[Unit] = save(Error(() => msg, t.some))
private def trace(msg: => String, tOpt: Option[Throwable]): F[Unit] =
save(LogMessage(LogLevel.Trace, msg, tOpt, Map.empty))

override def trace(msg: => String): F[Unit] = save(Trace(() => msg, none))
override def debug(msg: => String): F[Unit] = save(Debug(() => msg, none))
override def info(msg: => String): F[Unit] = save(Info(() => msg, none))
override def warn(msg: => String): F[Unit] = save(Warn(() => msg, none))
override def error(msg: => String): F[Unit] = save(Error(() => msg, none))
private def debug(msg: => String, tOpt: Option[Throwable]): F[Unit] =
save(LogMessage(LogLevel.Debug, msg, tOpt, Map.empty))

override def inspect: F[Chain[DeferredLogMessage]] = ref.get
private def info(msg: => String, tOpt: Option[Throwable]): F[Unit] =
save(LogMessage(LogLevel.Info, msg, tOpt, Map.empty))

override def log: F[Unit] = ref.getAndSet(Chain.empty).flatMap(_.traverse_(_.log(logger)))
}
}
private def warn(msg: => String, tOpt: Option[Throwable]): F[Unit] =
save(LogMessage(LogLevel.Warn, msg, tOpt, Map.empty))

private def error(msg: => String, tOpt: Option[Throwable]): F[Unit] =
save(LogMessage(LogLevel.Error, msg, tOpt, Map.empty))

sealed trait DeferredLogMessage {
def message: () => String
def throwOpt: Option[Throwable]
override def trace(t: Throwable)(msg: => String): F[Unit] = trace(msg, t.some)
override def debug(t: Throwable)(msg: => String): F[Unit] = debug(msg, t.some)
override def info(t: Throwable)(msg: => String): F[Unit] = info(msg, t.some)
override def warn(t: Throwable)(msg: => String): F[Unit] = warn(msg, t.some)
override def error(t: Throwable)(msg: => String): F[Unit] = error(msg, t.some)

def log[F[_]](logger: Logger[F]): F[Unit] = this match {
case Trace(message, Some(e)) => logger.trace(e)(message())
case Trace(message, None) => logger.trace(message())
case Debug(message, Some(e)) => logger.debug(e)(message())
case Debug(message, None) => logger.debug(message())
case Info(message, Some(e)) => logger.info(e)(message())
case Info(message, None) => logger.info(message())
case Warn(message, Some(e)) => logger.warn(e)(message())
case Warn(message, None) => logger.warn(message())
case Error(message, Some(e)) => logger.error(e)(message())
case Error(message, None) => logger.error(message())
}
}
override def trace(msg: => String): F[Unit] = trace(msg, none)
override def debug(msg: => String): F[Unit] = debug(msg, none)
override def info(msg: => String): F[Unit] = info(msg, none)
override def warn(msg: => String): F[Unit] = warn(msg, none)
override def error(msg: => String): F[Unit] = error(msg, none)

final case class Trace(
message: () => String,
throwOpt: Option[Throwable]
) extends DeferredLogMessage
final case class Debug(
message: () => String,
throwOpt: Option[Throwable]
) extends DeferredLogMessage
final case class Info(
message: () => String,
throwOpt: Option[Throwable]
) extends DeferredLogMessage
final case class Warn(
message: () => String,
throwOpt: Option[Throwable]
) extends DeferredLogMessage
final case class Error(
message: () => String,
throwOpt: Option[Throwable]
) extends DeferredLogMessage
override def inspect: F[Chain[LogMessage]] = ref.get

override def log: F[Unit] =
ref.getAndSet(Chain.empty).flatMap(_.traverse_(LogMessage.log(_, logger)))
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,30 +21,29 @@ import cats.data.Chain
import cats.effect.kernel.{Concurrent, Resource}
import cats.syntax.all.*
import cats.{~>, Functor}
import org.typelevel.log4cats.extras.DeferredStructuredLogger.DeferredStructuredLogMessage
import org.typelevel.log4cats.{LoggerFactory, SelfAwareStructuredLogger}

/**
* A `LoggerFactory` that does not immediately log.
*
* Effectively a `LoggerFactory` equivalent to `DeferredSelfAwareStructuredLogger`.
* As an implementation note, the `LoggerFactory` trait is constrained in such a way that
* this will produce `SelfAwareStructuredLogger`, rather than `DeferredSelfAwareStructuredLogger`,
* so if logging is desired it needs to be triggered using the `DeferredLoggerFactory`, rather than
* being able to trigger it from any of the produced loggers.
* Effectively a `LoggerFactory` equivalent to `DeferredSelfAwareStructuredLogger`. As an
* implementation note, the `LoggerFactory` trait is constrained in such a way that this will
* produce `SelfAwareStructuredLogger`, rather than `DeferredSelfAwareStructuredLogger`, so if
* logging is desired it needs to be triggered using the `DeferredLoggerFactory`, rather than being
* able to trigger it from any of the produced loggers.
*
* >>> WARNING: READ BEFORE USAGE! <<<
* https://github.com/typelevel/log4cats/blob/main/core/shared/src/main/scala/org/typelevel/log4cats/extras/README.md
* >>> WARNING: READ BEFORE USAGE! <<<
*/
trait DeferredLoggerFactory[F[_]] extends LoggerFactory[F] {

/**
* View the logs in the buffer.
*
* This is primarily useful for testing, and will not effect the behavior
* of calls to `log`
* This is primarily useful for testing, and will not effect the behavior of calls to `log`
*/
def inspect: F[Chain[DeferredStructuredLogMessage]]
def inspect: F[Chain[LogMessage]]

/**
* Log the deferred messages
Expand Down Expand Up @@ -80,14 +79,12 @@ object DeferredLoggerFactory {
): Resource[F, DeferredLoggerFactory[F]] =
DeferredSelfAwareStructuredLogger.makeCache[F].map { cache =>
new DeferredLoggerFactory[F] {
override def inspect: F[Chain[DeferredStructuredLogMessage]] = cache.get.map(_._1F)
override def inspect: F[Chain[LogMessage]] = cache.get.map(_._1F)

override def log: F[Unit] = {
cache
.getAndSet(Chain.empty)
.flatMap(_.traverse_ { case (msg, logger) =>
msg.log(logger)
})
.flatMap(_.traverse_((LogMessage.logStructured[F] _).tupled))
}

override def fromName(name: String): F[SelfAwareStructuredLogger[F]] =
Expand All @@ -102,9 +99,8 @@ object DeferredLoggerFactory {
fk: F ~> G
)(lf: DeferredLoggerFactory[F]): DeferredLoggerFactory[G] =
new DeferredLoggerFactory[G] {
override def inspect: G[Chain[DeferredStructuredLogger.DeferredStructuredLogMessage]] = fk(
lf.inspect
)
override def inspect: G[Chain[LogMessage]] = fk(lf.inspect)

override def log: G[Unit] = fk(lf.log)

override def getLoggerFromName(name: String): SelfAwareStructuredLogger[G] =
Expand All @@ -120,8 +116,7 @@ object DeferredLoggerFactory {
ctx: Map[String, String]
): DeferredLoggerFactory[F] =
new DeferredLoggerFactory[F] {
override def inspect: F[Chain[DeferredStructuredLogger.DeferredStructuredLogMessage]] =
lf.inspect
override def inspect: F[Chain[LogMessage]] = lf.inspect
override def log: F[Unit] = lf.log

override def getLoggerFromName(name: String): SelfAwareStructuredLogger[F] =
Expand All @@ -136,8 +131,7 @@ object DeferredLoggerFactory {
f: String => String
): DeferredLoggerFactory[F] =
new DeferredLoggerFactory[F] {
override def inspect: F[Chain[DeferredStructuredLogger.DeferredStructuredLogMessage]] =
lf.inspect
override def inspect: F[Chain[LogMessage]] = lf.inspect
override def log: F[Unit] = lf.log

override def getLoggerFromName(name: String): SelfAwareStructuredLogger[F] =
Expand Down
Loading

0 comments on commit 366d80e

Please sign in to comment.