diff --git a/logging/derivation/src/main/scala/tofu/logging/derivation/loggingMid.scala b/logging/derivation/src/main/scala/tofu/logging/derivation/loggingMid.scala index ee539333c..39bd2ff31 100644 --- a/logging/derivation/src/main/scala/tofu/logging/derivation/loggingMid.scala +++ b/logging/derivation/src/main/scala/tofu/logging/derivation/loggingMid.scala @@ -9,22 +9,43 @@ import tofu.logging.bi.LoggingBiMidBuilder import derevo.PassTypeArgs import derevo.ParamRequire +/** Default logging derivation mechanism unary effect algebras,, + * adds logging around successful invocation of each method at DEBUG level + * class name is not printed by default + * + * for customization create object with same parents and abstract type member Result + * and redefine [onEnter] and [onLeave] methods of the LoggingMidBuilder trait + */ object loggingMid extends LoggingMidBuilder.Default with DerivationKN3[LoggingMid.Of] with PassTypeArgs with ParamRequire[Loggable] { type Result[A] = LoggingMid[A] def instance[U[f[_]]]: U[LoggingMid] = macro HigherKindedMacros.factorizeThis[U] } +/** Default logging with errors derivation mechanism for unary effect algebras, + * adds logging around invocation of each method at DEBUG level and error alert at ERROR level + * class name is not printed by default + * + * for customization create object with same parents and abstract type member Result + * and redefine [onEnter], [onLeave] and [onFault] methods of the LoggingErrMidBuilder trait + */ object loggingMidTry extends LoggingErrMidBuilder.DefaultImpl[Throwable] with DerivationKN3[LoggingErrMid.Try] with PassTypeArgs - with ParamRequire[Loggable] { + with ParamRequire[Loggable] { type Result[A] = LoggingErrMid[Throwable, A] def instance[U[f[_]]]: U[Result] = macro HigherKindedMacros.factorizeThis[U] } +/** Default logging with errors derivation mechanism for binary effect algebras, + * adds logging around invocation of each method at DEBUG level and error alert at ERROR level + * class name is not printed by default + * + * for customization create object with same parents and abstract type member Result + * and redefine [onEnter], [onLeave] methods of the LoggingBiMidBuilder trait + */ object loggingBiMid extends LoggingBiMidBuilder.Default with DerivationKN11[LoggingBiMid.Of] with PassTypeArgs - with ParamRequire[Loggable] { + with ParamRequire[Loggable] { type Result[E, A] = LoggingBiMid[E, A] def instance[U[f[_, _]]]: U[LoggingBiMid] = macro HigherKindedMacros.bifactorizeThis[U] diff --git a/logging/derivation/src/test/scala/tofu/logging/derivation/LoggingMidSuite.scala b/logging/derivation/src/test/scala/tofu/logging/derivation/LoggingMidSuite.scala index 2ddc51c83..93e888dc0 100644 --- a/logging/derivation/src/test/scala/tofu/logging/derivation/LoggingMidSuite.scala +++ b/logging/derivation/src/test/scala/tofu/logging/derivation/LoggingMidSuite.scala @@ -59,8 +59,8 @@ class LoggingMidSuite extends AnyFunSuite { assert(result === Left(MissingName())) assert( logs === Vector( - s"[Debug] <$GreeterName> entering $GreeterName.hello ()", - s"[Error] <$GreeterName> error during $GreeterName.hello () error is $ErrName" + s"[Debug] <$GreeterName> entering hello ()", + s"[Error] <$GreeterName> error during hello () error is $ErrName" ) ) } @@ -71,10 +71,10 @@ class LoggingMidSuite extends AnyFunSuite { assert( logs === Vector( - s"[Debug] <$GreeterName> entering $GreeterName.setName (name = Tofu)", - s"[Debug] <$GreeterName> leaving $GreeterName.setName (name = Tofu) with result ()", - s"[Debug] <$GreeterName> entering $GreeterName.hello ()", - s"[Debug] <$GreeterName> leaving $GreeterName.hello () with result Hello, Tofu" + s"[Debug] <$GreeterName> entering setName (name = Tofu)", + s"[Debug] <$GreeterName> leaving setName (name = Tofu) with result ()", + s"[Debug] <$GreeterName> entering hello ()", + s"[Debug] <$GreeterName> leaving hello () with result Hello, Tofu" ) ) } diff --git a/logging/structured/src/main/scala/tofu/logging/Logging.scala b/logging/structured/src/main/scala/tofu/logging/Logging.scala index b9adac82d..5eff7b369 100644 --- a/logging/structured/src/main/scala/tofu/logging/Logging.scala +++ b/logging/structured/src/main/scala/tofu/logging/Logging.scala @@ -106,7 +106,8 @@ object Logging { type ForService[F[_], Svc] <: Logging[F] - type Safe[F[_, _]] = Logging[F[Nothing, *]] + type Safe[F[_, _]] = Logging[F[Nothing, *]] + type SafeBase[F[_, _]] = LoggingBase[F[Nothing, *]] def apply[F[_]](implicit logging: Logging[F]): Logging[F] = logging diff --git a/logging/structured/src/main/scala/tofu/logging/LoggingMid.scala b/logging/structured/src/main/scala/tofu/logging/LoggingMid.scala index 613aedc38..8b7a2a96d 100644 --- a/logging/structured/src/main/scala/tofu/logging/LoggingMid.scala +++ b/logging/structured/src/main/scala/tofu/logging/LoggingMid.scala @@ -10,7 +10,11 @@ import tofu.higherKind.derived.HigherKindedMacros import tofu.Errors import tofu.syntax.handle._ -/** Logging middleware */ +/** Logging middleware + * Alg[LoggingMid] is a special form of implicit evidence of injectable logging support + * generally you don't need `Logging` instance to derive this + * so choice of logging postponed until this middleware is attached to the core instance + */ abstract class LoggingMid[A] { def around[F[_]: Monad: LoggingBase](fa: F[A]): F[A] @@ -25,10 +29,12 @@ object LoggingMid extends LoggingMidBuilder.DefaultImpl { } /** Logging middleware generator */ - trait LoggingMidBuilder { + + /** do some logging upon enter to method invocation */ def onEnter[F[_]: LoggingBase](cls: Class[_], method: String, args: Seq[(String, LoggedValue)]): F[Unit] + /** do some logging after leaving method invocation with known result */ def onLeave[F[_]: LoggingBase]( cls: Class[_], method: String, @@ -73,7 +79,11 @@ object LoggingMidBuilder { class DefaultImpl extends Default } -/** Logging middleware */ +/** Logging middleware supporting error reporting + * Alg[LoggingErrMid[E, *]] is a special form of implicit evidence of injectable logging support + * generally you don't need `Logging` instance to derive this + * so choice of logging postponed until this middleware is attached to the core instance + */ abstract class LoggingErrMid[E, A] extends LoggingMid[A] { def aroundErr[F[_]: Monad: Errors[*[_], E]: LoggingBase](fa: F[A]): F[A] diff --git a/logging/structured/src/main/scala/tofu/logging/bi/LoggingBiMid.scala b/logging/structured/src/main/scala/tofu/logging/bi/LoggingBiMid.scala index a60f235e2..d2e4fde63 100644 --- a/logging/structured/src/main/scala/tofu/logging/bi/LoggingBiMid.scala +++ b/logging/structured/src/main/scala/tofu/logging/bi/LoggingBiMid.scala @@ -7,11 +7,15 @@ import tofu.higherKind.bi.BiMid import tofu.logging.{LogRenderer, Loggable, LoggedValue, Logging} import tofu.syntax.bindInv._ -/** logging middleware for binary tc parameterized traits */ +/** Logging middleware for binary typeconstructor parameterized algebras + * Alg[LoggingBiMid] is a special form of implicit evidence of injectable logging support + * generally you don't need `Logging` instance to derive this + * so choice of logging postponed until this middleware is attached to the core instance + */ abstract class LoggingBiMid[E, A] { - def around[F[+_, +_]: Bind: Logging.Safe](fa: F[E, A]): F[E, A] + def around[F[+_, +_]: Bind: Logging.SafeBase](fa: F[E, A]): F[E, A] - def toMid[F[+_, +_]: Bind: Logging.Safe]: BiMid[F, E, A] = fx => around(fx) + def toMid[F[+_, +_]: Bind: Logging.SafeBase]: BiMid[F, E, A] = fx => around(fx) } object LoggingBiMid extends LoggingBiMidBuilder.Default { @@ -19,13 +23,16 @@ object LoggingBiMid extends LoggingBiMidBuilder.Default { } abstract class LoggingBiMidBuilder { - def onEnter[F[+_, +_]: Logging.Safe]( + + /** do some logging upon enter to method invocation */ + def onEnter[F[+_, +_]: Logging.SafeBase]( cls: Class[_], method: String, args: Seq[(String, LoggedValue)] ): F[Nothing, Unit] - def onLeave[F[+_, +_]: Logging.Safe]( + /** do some logging after leaving method invocation with known result or error */ + def onLeave[F[+_, +_]: Logging.SafeBase]( cls: Class[_], method: String, args: Seq[(String, LoggedValue)], @@ -45,7 +52,7 @@ abstract class LoggingBiMidBuilder { def result: LoggingBiMid[Err, Res] = new LoggingBiMid[Err, Res] { private[this] val argSeq = args.toSeq - def around[F[+_, +_]: Bind: Logging.Safe](fa: F[Err, Res]): F[Err, Res] = + def around[F[+_, +_]: Bind: Logging.SafeBase](fa: F[Err, Res]): F[Err, Res] = onEnter(cls, method, argSeq) *> fa.tapBoth( err => onLeave(cls, method, argSeq, err, ok = false), @@ -62,8 +69,8 @@ abstract class LoggingBiMidBuilder { object LoggingBiMidBuilder { class Default extends LoggingBiMidBuilder { def onEnter[F[_, _]](cls: Class[_], method: String, args: Seq[(String, LoggedValue)])(implicit - F: Logging.Safe[F] - ): F[Nothing, Unit] = F.debug("entering {}.{} {}", cls.getName(), method, new ArgsLoggable(args)) + F: Logging.SafeBase[F] + ): F[Nothing, Unit] = F.debug("entering {} {}", cls.getName(), method, new ArgsLoggable(args)) def onLeave[F[_, _]]( cls: Class[_], @@ -72,9 +79,12 @@ object LoggingBiMidBuilder { res: LoggedValue, ok: Boolean, )(implicit - F: Logging.Safe[F] + F: Logging.SafeBase[F] ): F[Nothing, Unit] = - F.debug("leaving {}.{} {}", cls.getName(), method, new ArgsLoggable(args)) + if (ok) + F.debug("leaving {} {} result is {}", method, new ArgsLoggable(args), res) + else + F.error("error during {} {} error is {}", method, new ArgsLoggable(args), res) } class ArgsLoggable(values: Seq[(String, LoggedValue)]) extends LoggedValue {