-
Notifications
You must be signed in to change notification settings - Fork 74
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add some helpers for using Kleisli or IOLocal for logging context #676
base: main
Are you sure you want to change the base?
Changes from all commits
8709392
d8f1164
d2d340d
f0629f3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -76,6 +76,16 @@ lazy val noop = crossProject(JSPlatform, JVMPlatform, NativePlatform) | |
) | ||
.nativeSettings(commonNativeSettings) | ||
|
||
lazy val ce3 = crossProject(JSPlatform, JVMPlatform) | ||
.settings(commonSettings) | ||
.dependsOn(core) | ||
.settings( | ||
name := "log4cats-ce3", | ||
libraryDependencies ++= Seq( | ||
"org.typelevel" %%% "cats-effect" % catsEffectV | ||
) | ||
) | ||
|
||
Comment on lines
+79
to
+88
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Probably we can move this to the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Happy to go with whatever you guys think is most appropriate - just didn't add it to either of the existing ones because:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. With #678 we will be depending on cats-effect, and I see no other way to resolve the UUIDGen issue otherwise. We'll have to track how the dependency on CE in core goes there as well. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Now it depends on |
||
lazy val slf4j = project | ||
.settings(commonSettings) | ||
.dependsOn(core.jvm) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package org.typelevel.log4cats.ce3 | ||
|
||
import cats.effect.{IO, IOLocal} | ||
import org.typelevel.log4cats.{LoggerFactory, SelfAwareStructuredLogger} | ||
|
||
object IOLocalHelpers { | ||
def loggerWithContextFromIOLocal( | ||
sl: SelfAwareStructuredLogger[IO], | ||
local: IOLocal[Map[String, String]] | ||
): SelfAwareStructuredLogger[IO] = SelfAwareStructuredLogger.withContextF(sl)(local.get) | ||
|
||
def factoryWithContextFromIOLocal( | ||
lf: LoggerFactory[IO], | ||
local: IOLocal[Map[String, String]] | ||
): LoggerFactory[IO] = LoggerFactory.withContextF(lf)(local.get) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,12 +16,14 @@ | |
|
||
package org.typelevel.log4cats | ||
|
||
import cats.FlatMap | ||
import cats.Functor | ||
import cats.Monad | ||
import cats.data.EitherT | ||
import cats.data.Kleisli | ||
import cats.data.OptionT | ||
import cats.syntax.functor._ | ||
import cats.~> | ||
import cats.data.OptionT | ||
import cats.data.EitherT | ||
|
||
import scala.annotation.implicitNotFound | ||
|
||
|
@@ -60,4 +62,20 @@ object LoggerFactory extends LoggerFactoryGenCompanion { | |
fk(logger) | ||
} | ||
} | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I somehow missed that LoggerFactory was a thing - nice! |
||
def withContextF[F[_]: FlatMap]( | ||
lf: LoggerFactory[F] | ||
)(ctx: F[Map[String, String]]): LoggerFactory[F] = | ||
new LoggerFactory[F] { | ||
override def getLoggerFromName(name: String): SelfAwareStructuredLogger[F] = | ||
SelfAwareStructuredLogger.withContextF(lf.getLoggerFromName(name))(ctx) | ||
|
||
override def fromName(name: String): F[SelfAwareStructuredLogger[F]] = lf | ||
.fromName(name) | ||
.map(SelfAwareStructuredLogger.withContextF(_)(ctx)) | ||
} | ||
|
||
def withContextFromKleisli[F[_]: Monad]( | ||
lf: LoggerFactory[Kleisli[F, Map[String, String], *]] | ||
): LoggerFactory[Kleisli[F, Map[String, String], *]] = withContextF(lf)(Kleisli.ask) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,6 +18,8 @@ package org.typelevel.log4cats | |
|
||
import cats._ | ||
import cats.Show.Shown | ||
import cats.data.Kleisli | ||
import cats.implicits.{toFlatMapOps, toFunctorOps} | ||
|
||
trait SelfAwareStructuredLogger[F[_]] extends SelfAwareLogger[F] with StructuredLogger[F] { | ||
override def mapK[G[_]](fk: F ~> G): SelfAwareStructuredLogger[G] = | ||
|
@@ -94,6 +96,90 @@ object SelfAwareStructuredLogger { | |
sl.trace(modify(ctx), t)(message) | ||
} | ||
|
||
def withContextFromKleisli[F[_]: Monad]( | ||
sl: SelfAwareStructuredLogger[Kleisli[F, Map[String, String], *]] | ||
): SelfAwareStructuredLogger[Kleisli[F, Map[String, String], *]] = | ||
withContextF(sl)( | ||
Kleisli.ask[F, Map[String, String]] | ||
) | ||
def withContextF[F[_]: FlatMap]( | ||
sl: SelfAwareStructuredLogger[F] | ||
)(ctx: F[Map[String, String]]): SelfAwareStructuredLogger[F] = | ||
new ModifiedContextFSelfAwareStructuredLogger[F](sl)(existingCtx => ctx.map(_ ++ existingCtx)) | ||
Comment on lines
+105
to
+108
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think what we're missing is something like this: def fa: F[A]
def faWithContext: F[A] = StructuredLogger[F].locally(Map("k" -> "v"))(fa) so that you can wrap an effect, not just a logger. That would help propagate the context to whatever services you're calling, without a need to pass the context or the updated logger. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, I guess I was thinking of users doing that using
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How are you imagining something like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yeah, I think we'll need concrete implementations There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have come to believe that almost all uses of |
||
|
||
private class ModifiedContextFSelfAwareStructuredLogger[F[_]: FlatMap]( | ||
sl: SelfAwareStructuredLogger[F] | ||
)( | ||
modify: Map[String, String] => F[Map[String, String]] | ||
) extends SelfAwareStructuredLogger[F] { | ||
private lazy val defaultCtx: F[Map[String, String]] = modify(Map.empty) | ||
|
||
def error(message: => String): F[Unit] = defaultCtx.flatMap(sl.error(_)(message)) | ||
|
||
def warn(message: => String): F[Unit] = defaultCtx.flatMap(sl.warn(_)(message)) | ||
|
||
def info(message: => String): F[Unit] = defaultCtx.flatMap(sl.info(_)(message)) | ||
|
||
def debug(message: => String): F[Unit] = defaultCtx.flatMap(sl.debug(_)(message)) | ||
|
||
def trace(message: => String): F[Unit] = defaultCtx.flatMap(sl.trace(_)(message)) | ||
|
||
def trace(ctx: Map[String, String])(msg: => String): F[Unit] = | ||
modify(ctx).flatMap(sl.trace(_)(msg)) | ||
|
||
def debug(ctx: Map[String, String])(msg: => String): F[Unit] = | ||
modify(ctx).flatMap(sl.debug(_)(msg)) | ||
|
||
def info(ctx: Map[String, String])(msg: => String): F[Unit] = | ||
modify(ctx).flatMap(sl.info(_)(msg)) | ||
|
||
def warn(ctx: Map[String, String])(msg: => String): F[Unit] = | ||
modify(ctx).flatMap(sl.warn(_)(msg)) | ||
|
||
def error(ctx: Map[String, String])(msg: => String): F[Unit] = | ||
modify(ctx).flatMap(sl.error(_)(msg)) | ||
|
||
def isTraceEnabled: F[Boolean] = sl.isTraceEnabled | ||
|
||
def isDebugEnabled: F[Boolean] = sl.isDebugEnabled | ||
|
||
def isInfoEnabled: F[Boolean] = sl.isInfoEnabled | ||
|
||
def isWarnEnabled: F[Boolean] = sl.isWarnEnabled | ||
|
||
def isErrorEnabled: F[Boolean] = sl.isErrorEnabled | ||
|
||
def error(t: Throwable)(message: => String): F[Unit] = | ||
defaultCtx.flatMap(sl.error(_, t)(message)) | ||
|
||
def warn(t: Throwable)(message: => String): F[Unit] = | ||
defaultCtx.flatMap(sl.warn(_, t)(message)) | ||
|
||
def info(t: Throwable)(message: => String): F[Unit] = | ||
defaultCtx.flatMap(sl.info(_, t)(message)) | ||
|
||
def debug(t: Throwable)(message: => String): F[Unit] = | ||
defaultCtx.flatMap(sl.debug(_, t)(message)) | ||
|
||
def trace(t: Throwable)(message: => String): F[Unit] = | ||
defaultCtx.flatMap(sl.trace(_, t)(message)) | ||
|
||
def error(ctx: Map[String, String], t: Throwable)(message: => String): F[Unit] = | ||
modify(ctx).flatMap(sl.error(_, t)(message)) | ||
|
||
def warn(ctx: Map[String, String], t: Throwable)(message: => String): F[Unit] = | ||
modify(ctx).flatMap(sl.warn(_, t)(message)) | ||
|
||
def info(ctx: Map[String, String], t: Throwable)(message: => String): F[Unit] = | ||
modify(ctx).flatMap(sl.info(_, t)(message)) | ||
|
||
def debug(ctx: Map[String, String], t: Throwable)(message: => String): F[Unit] = | ||
modify(ctx).flatMap(sl.debug(_, t)(message)) | ||
|
||
def trace(ctx: Map[String, String], t: Throwable)(message: => String): F[Unit] = | ||
modify(ctx).flatMap(sl.trace(_, t)(message)) | ||
} | ||
|
||
private def withModifiedString[F[_]]( | ||
l: SelfAwareStructuredLogger[F], | ||
f: String => String | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would call this
log4cats-cats-effect
. Anything beyond3
will be a breaking change, and I'd avoid colloquial abbrevations in a jar name.