Skip to content

Commit

Permalink
Close #461 - Add LoggerFLogHandler to support doobie's LogHandler
Browse files Browse the repository at this point in the history
  • Loading branch information
kevin-lee committed Jul 29, 2023
1 parent 67163e7 commit bff747e
Show file tree
Hide file tree
Showing 6 changed files with 266 additions and 15 deletions.
43 changes: 33 additions & 10 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ lazy val loggerF = (project in file("."))
catsJs,
logbackMdcMonix3Jvm,
logbackMdcMonix3Js,
doobie1Jvm,
doobie1Js,
testKitJvm,
testKitJs,
catsEffectJvm,
Expand Down Expand Up @@ -273,6 +275,28 @@ lazy val logbackMdcMonix3 = module(ProjectName("logback-mdc-monix3"), crossPr
lazy val logbackMdcMonix3Jvm = logbackMdcMonix3.jvm
lazy val logbackMdcMonix3Js = logbackMdcMonix3.js

lazy val doobie1 = module(ProjectName("doobie1"), crossProject(JVMPlatform, JSPlatform))
.settings(
description := "Logger for F[_] - for Doobie v1",
libraryDependencies ++= Seq(
libs.doobieFree,
libs.tests.effectieCatsEffect3,
libs.tests.extrasHedgehogCatsEffect3,
) ++ libs.tests.hedgehogLibs,
libraryDependencies := libraryDependenciesRemoveScala3Incompatible(
scalaVersion.value,
libraryDependencies.value,
),
)
.dependsOn(
core,
cats,
testKit % Test,
slf4jLogger % Test,
)
lazy val doobie1Jvm = doobie1.jvm
lazy val doobie1Js = doobie1.js

lazy val testKit =
module(ProjectName("test-kit"), crossProject(JVMPlatform, JSPlatform))
.settings(
Expand Down Expand Up @@ -509,8 +533,8 @@ lazy val props =
final val GitHubUsername = "Kevin-Lee"
final val RepoName = "logger-f"

final val Scala3Versions = List("3.0.2")
final val Scala2Versions = List("2.13.6", "2.12.13")
final val Scala3Versions = List("3.3.0")
final val Scala2Versions = List("2.13.11", "2.12.18")

// final val ProjectScalaVersion = Scala3Versions.head
final val ProjectScalaVersion = Scala2Versions.head
Expand Down Expand Up @@ -545,6 +569,8 @@ lazy val props =

val Monix3Version = "3.4.0"

val Doobie1Version = "1.0.0-RC4"

final val LoggerF1Version = "1.20.0"

final val ExtrasVersion = "0.25.0"
Expand Down Expand Up @@ -587,10 +613,14 @@ lazy val libs =

lazy val logbackScalaInterop = "io.kevinlee" % "logback-scala-interop" % props.LogbackScalaInteropVersion

lazy val doobieFree = "org.tpolecat" %% "doobie-free" % props.Doobie1Version

lazy val tests = new {

lazy val monix = "io.monix" %% "monix" % props.Monix3Version % Test

lazy val effectieCatsEffect3 = "io.kevinlee" %% "effectie-cats-effect3" % props.EffectieVersion % Test

lazy val effectieMonix3 = "io.kevinlee" %% "effectie-monix3" % props.EffectieVersion % Test

lazy val hedgehogLibs: List[ModuleID] = List(
Expand Down Expand Up @@ -619,14 +649,7 @@ def prefixedProjectName(name: String) = s"${props.RepoName}${if (name.isEmpty) "
def libraryDependenciesRemoveScala3Incompatible(
scalaVersion: String,
libraries: Seq[ModuleID],
): Seq[ModuleID] =
(
if (scalaVersion.startsWith("3."))
libraries
.filterNot(props.removeDottyIncompatible)
else
libraries
)
): Seq[ModuleID] = libraries

lazy val mavenCentralPublishSettings: SettingsDefinition = List(
/* Publish to Maven Central { */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ import loggerf.core.ToLog
* @since 2022-02-19
*/
trait show {
inline given showToLog[A: Show]: ToLog[A] = Show[A].show(_)
given showToLog[A: Show]: ToLog[A] with {
inline def toLogMessage(a: A): String = Show[A].show(a)
}
}

object show extends show
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,7 @@ object ToLog {
@SuppressWarnings(Array("org.wartremover.warts.ToString"))
def fromToString[A]: ToLog[A] = _.toString

inline given stringToLog: ToLog[String] = identity(_)
given stringToLog: ToLog[String] with {
inline def toLogMessage(a: String): String = a
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package loggerf.doobie1

import cats.syntax.all._
import doobie.util.log
import doobie.util.log.LogHandler
import loggerf.core.Log
import loggerf.syntax.all._

/** @author Kevin Lee
* @since 2023-07-28
*/
object LoggerFLogHandler {
def apply[F[*]: Log]: LogHandler[F] = new LoggerFLogHandlerF[F]

// format: off
final private class LoggerFLogHandlerF[F[*]: Log] extends LogHandler[F] {
override def run(logEvent: log.LogEvent): F[Unit] = logEvent match {
case log.Success(sql, args, label, e1, e2) =>
logS_(
show"""Successful Statement Execution:
|
| ${sql.linesIterator.dropWhile(_.trim.isEmpty).mkString("\n ")}
|
| arguments = [${args.mkString(", ")}]
| label = $label
| elapsed = ${e1.toMillis} ms exec + ${e2.toMillis} ms processing (${(e1 + e2).toMillis} ms total)
|""".stripMargin
)(info)

case log.ProcessingFailure(sql, args, label, e1, e2, failure) =>
logS_(
show"""Failed Resultset Processing:
|
| ${sql.linesIterator.dropWhile(_.trim.isEmpty).mkString("\n ")}
|
| arguments = [${args.mkString(", ")}]
| label = $label
| elapsed = ${e1.toMillis} ms exec + ${e2.toMillis} ms processing (failed) (${(e1 + e2).toMillis} ms total)
| failure = ${failure.getMessage}
|""".stripMargin
)(error)

case log.ExecFailure(sql, args, label, e1, failure) =>
logS_(
show"""Failed Statement Execution:
|
| ${sql.linesIterator.dropWhile(_.trim.isEmpty).mkString("\n ")}
|
| arguments = [${args.mkString(", ")}]
| label = $label
| elapsed = ${e1.toMillis} ms exec (failed)
| failure = ${failure.getMessage}
|""".stripMargin
)(error)
}
}
// format: on
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
package loggerf.doobie1

import cats.effect._
import doobie.util.log.{ExecFailure, ProcessingFailure, Success}
import effectie.instances.ce3.fx.ioFx
import extras.hedgehog.ce3.syntax.runner._
import hedgehog._
import hedgehog.runner._
import loggerf.instances.cats.logF
import loggerf.testing.CanLog4Testing
import loggerf.testing.CanLog4Testing.OrderedMessages

import scala.concurrent.duration._

/** @author Kevin Lee
* @since 2023-07-29
*/
object LoggerFLogHandlerSpec extends Properties {

override def tests: List[Prop] = List(
property("testSuccess", testSuccess),
property("testExecFailure", testExecFailure),
property("testProcessingFailure", testProcessingFailure),
)

def testSuccess: Property =
for {
columns <- Gen.string(Gen.alpha, Range.linear(3, 10)).list(Range.linear(1, 5)).log("columns")
table <- Gen.string(Gen.alpha, Range.linear(3, 10)).log("table")
args <- Gen.string(Gen.alpha, Range.linear(3, 10)).list(Range.linear(0, 5)).log("args")
label <- Gen.string(Gen.alpha, Range.linear(3, 10)).log("label")
exec <- Gen.int(Range.linear(10, 3000)).log("exec")
processing <- Gen.int(Range.linear(10, 3000)).log("processing")
} yield runIO {
implicit val canLog: CanLog4Testing = CanLog4Testing()

val expected =
OrderedMessages(
Vector(
(
0,
loggerf.Level.info,
s"""Successful Statement Execution:
|
| SELECT ${columns.mkString(", ")} FROM $table
|
| arguments = ${args.mkString("[", ", ", "]")}
| label = $label
| elapsed = ${exec.toString} ms exec + ${processing.toString} ms processing (${(exec + processing).toString} ms total)
|""".stripMargin,
)
)
)

LoggerFLogHandler[IO]
.run(
Success(
s"SELECT ${columns.mkString(", ")} FROM $table",
args,
label,
exec.milliseconds,
processing.milliseconds,
)
) *> IO {
val actual = canLog.getOrderedMessages
actual ==== expected
}
}

def testExecFailure: Property =
for {
columns <- Gen.string(Gen.alpha, Range.linear(3, 10)).list(Range.linear(1, 5)).log("columns")
table <- Gen.string(Gen.alpha, Range.linear(3, 10)).log("table")
args <- Gen.string(Gen.alpha, Range.linear(3, 10)).list(Range.linear(0, 5)).log("args")
label <- Gen.string(Gen.alpha, Range.linear(3, 10)).log("label")
exec <- Gen.int(Range.linear(10, 3000)).log("exec")
errMessage <- Gen.string(Gen.alpha, Range.linear(3, 10)).log("errMessage")
} yield runIO {
implicit val canLog: CanLog4Testing = CanLog4Testing()

val expectedException = new RuntimeException(errMessage)

val expected =
OrderedMessages(
Vector(
(
0,
loggerf.Level.error,
s"""Failed Statement Execution:
|
| SELECT ${columns.mkString(", ")} FROM $table
|
| arguments = ${args.mkString("[", ", ", "]")}
| label = $label
| elapsed = ${exec.toString} ms exec (failed)
| failure = ${expectedException.getMessage}
|""".stripMargin,
)
)
)

LoggerFLogHandler[IO]
.run(
ExecFailure(
s"SELECT ${columns.mkString(", ")} FROM $table",
args,
label,
exec.milliseconds,
expectedException,
)
) *> IO {
val actual = canLog.getOrderedMessages
actual ==== expected
}
}

def testProcessingFailure: Property =
for {
columns <- Gen.string(Gen.alpha, Range.linear(3, 10)).list(Range.linear(1, 5)).log("columns")
table <- Gen.string(Gen.alpha, Range.linear(3, 10)).log("table")
args <- Gen.string(Gen.alpha, Range.linear(3, 10)).list(Range.linear(0, 5)).log("args")
label <- Gen.string(Gen.alpha, Range.linear(3, 10)).log("label")
exec <- Gen.int(Range.linear(10, 3000)).log("exec")
processing <- Gen.int(Range.linear(10, 3000)).log("processing")
errMessage <- Gen.string(Gen.alpha, Range.linear(3, 10)).log("errMessage")
} yield runIO {
implicit val canLog: CanLog4Testing = CanLog4Testing()

val expectedException = new RuntimeException(errMessage)

val expected =
OrderedMessages(
Vector(
(
0,
loggerf.Level.error,
s"""Failed Resultset Processing:
|
| SELECT ${columns.mkString(", ")} FROM $table
|
| arguments = ${args.mkString("[", ", ", "]")}
| label = $label
| elapsed = ${exec.toString} ms exec + ${processing.toString} ms processing (failed) (${(exec + processing).toString} ms total)
| failure = ${expectedException.getMessage}
|""".stripMargin,
)
)
)

LoggerFLogHandler[IO]
.run(
ProcessingFailure(
s"SELECT ${columns.mkString(", ")} FROM $table",
args,
label,
exec.milliseconds,
processing.milliseconds,
expectedException,
)
) *> IO {
val actual = canLog.getOrderedMessages
actual ==== expected
}
}

}
6 changes: 3 additions & 3 deletions project/plugins.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ logLevel := sbt.Level.Warn
addDependencyTreePlugin

addSbtPlugin("com.github.sbt" % "sbt-ci-release" % "1.5.10")
addSbtPlugin("org.wartremover" % "sbt-wartremover" % "2.4.15")
addSbtPlugin("org.wartremover" % "sbt-wartremover" % "3.1.3")
//addSbtPlugin("org.wartremover" % "sbt-wartremover" % "3.0.5")
addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.10.4")
addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.4.6")
Expand All @@ -12,8 +12,8 @@ addSbtPlugin("org.scoverage" % "sbt-coveralls" % "1.3.2")
addSbtPlugin("org.scalameta" % "sbt-mdoc" % "2.3.2")
addSbtPlugin("io.kevinlee" % "sbt-docusaur" % "0.13.0")

addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.9.0")
addSbtPlugin("org.portable-scala" % "sbt-scalajs-crossproject" % "1.1.0")
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.13.2")
addSbtPlugin("org.portable-scala" % "sbt-scalajs-crossproject" % "1.3.2")

val sbtDevOopsVersion = "2.24.0"
addSbtPlugin("io.kevinlee" % "sbt-devoops-scala" % sbtDevOopsVersion)
Expand Down

0 comments on commit bff747e

Please sign in to comment.