我正在学习整个Cats-Effect FP框架,我想知道如何最好地实现日志记录。我正在努力寻找一种优雅的方法来使用返回无副作用类型的日志登录方法,特别是类构造函数。例如:
case class Limit(name: String, value: Int)
object Limit {
def max(name: String): Limit = Limit(name, Int.MaxValue)
}
现在,如果我想在构造中添加日志记录,这显然是一个副作用,我必须将方法签名更改为如下内容:
object Limit {
def max[F[_] : Sync](name: String): F[Limit] = {
for {
_ <- Slf4jLogger.getLogger[F].debug("Creating max limit.")
} yield {
Limit(name, Int.MaxValue)
}
}
}
这样的改变破坏了代码兼容性,现在每个使用Limit.max
的地方都需要重构来处理类型签名、映射和/或调用.unsafeRun
。对于添加一个日志行这样的小事情来说,这是一个相当彻底的改变。
有没有更优雅的方法?我只是放弃log4cats并使用常规的、不安全的SLF4J吗?或者我只需要接受在PF/Cats-Effect世界中,从任何地方返回F[..]
都更安全,无论该方法多么小或微不足道?
我认为使用log4cats库会更好。如果你使用cats-effect,可能你的Main类返回IO[Exit],几乎整个逻辑都不需要任何不安全的调用。
但与此同时,我看到很多项目只是使用普通的日志记录,而没有在IO中包装日志。这取决于你和/或你的团队/项目。我个人更喜欢FP方式。
我在这里留下了一个使用log4cats的示例:
implicit def logger[F[_]: Sync]: Logger[F] = Slf4jLogger.getLogger[F]
case class Limit(name: String, value: Int)
object Limit {
def max[F[_] : Functor: Logger](name: String): F[Limit] = {
for {
_ <- Logger[F].info("Creating max limit.")
} yield {
Limit(name, Int.MaxValue)
}
}
}
构建。sbt文件:
libraryDependencies ++= Seq(
"org.typelevel" %% "log4cats-core" % "2.5.0", // Only if you want to Support Any Backend
"org.typelevel" %% "log4cats-slf4j" % "2.5.0", // Direct Slf4j Support - Recommended
// and some backend such as logback or smth
)
一般来说-不要在简单的代码中添加日志记录,只是构造纯case类.
必须使用效果签名,因为记录是一个效果—您关心它何时发生,以及发生了多少次。为了使它在正确的时间和正确的次数发生,它必须被工作到执行的控制流中。
在这种情况下,我建议不要将日志记录添加到有效的类构造函数中,而是将日志记录写为实用程序方法而不是
def logConfiguration[F[_]: Logger](foo: FooConfig) = Logger[F].info(s"Using $foo")
然后您可以在调用站点合并已经正在做控制流并使用效果的调用站点。