Skip to content
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

Close #461 - Add LoggerFLogHandler to support doobie's LogHandler #462

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ jobs:
strategy:
matrix:
scala:
- { name: "Scala 2", version: "2.12.13", binary-version: "2.12", java-version: "11", java-distribution: "temurin", report: "" }
- { name: "Scala 2", version: "2.13.6", binary-version: "2.13", java-version: "11", java-distribution: "temurin", report: "report" }
- { name: "Scala 3", version: "3.0.2", binary-version: "3", java-version: "11", java-distribution: "temurin", report: "" }
- { name: "Scala 2", version: "2.12.18", binary-version: "2.12", java-version: "11", java-distribution: "temurin", report: "" }
- { name: "Scala 2", version: "2.13.11", binary-version: "2.13", java-version: "11", java-distribution: "temurin", report: "report" }
- { name: "Scala 3", version: "3.3.0", binary-version: "3", java-version: "11", java-distribution: "temurin", report: "" }

steps:
- uses: actions/checkout@v4
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
strategy:
matrix:
scala:
- { name: "Scala 2", version: "2.13.6", binary-version: "2.13", java-version: "11", java-distribution: "temurin", report: "report" }
- { name: "Scala 2", version: "2.13.11", binary-version: "2.13", java-version: "11", java-distribution: "temurin", report: "report" }

steps:
- uses: actions/checkout@v4
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ jobs:
strategy:
matrix:
scala:
- { name: "Scala 2", version: "2.12.13", binary-version: "2.12", java-version: "11", java-distribution: "temurin", report: "" }
- { name: "Scala 2", version: "2.13.6", binary-version: "2.13", java-version: "11", java-distribution: "temurin", report: "" }
- { name: "Scala 3", version: "3.0.2", binary-version: "3", java-version: "11", java-distribution: "temurin", report: "" }
- { name: "Scala 2", version: "2.12.18", binary-version: "2.12", java-version: "11", java-distribution: "temurin", report: "" }
- { name: "Scala 2", version: "2.13.11", binary-version: "2.13", java-version: "11", java-distribution: "temurin", report: "" }
- { name: "Scala 3", version: "3.3.0", binary-version: "3", java-version: "11", java-distribution: "temurin", report: "" }

steps:
- uses: actions/checkout@v4
Expand Down
43 changes: 33 additions & 10 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ lazy val loggerF = (project in file("."))
catsJs,
logbackMdcMonix3Jvm,
logbackMdcMonix3Js,
doobie1Jvm,
doobie1Js,
testKitJvm,
testKitJs,
catsEffectJvm,
Expand Down Expand Up @@ -276,6 +278,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 @@ -514,8 +538,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 @@ -550,6 +574,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 @@ -593,10 +619,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 @@ -625,14 +655,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
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -44,18 +44,18 @@ object Monix3MdcAdapter extends Monix3MdcAdapterOps

trait Monix3MdcAdapterOps {

@SuppressWarnings(Array("org.wartremover.warts.Null"))
@SuppressWarnings(Array("org.wartremover.warts.Null", "scalafix:DisableSyntax.null"))
protected def initialize0(monix3MdcAdapter: Monix3MdcAdapter): Monix3MdcAdapter = {
val field = classOf[MDC].getDeclaredField("mdcAdapter")
field.setAccessible(true)
field.set(null, monix3MdcAdapter) // scalafix:ok DisableSyntax.null
field.set(null, monix3MdcAdapter)
field.setAccessible(false)
monix3MdcAdapter
}

@SuppressWarnings(Array("org.wartremover.warts.AsInstanceOf"))
@SuppressWarnings(Array("org.wartremover.warts.AsInstanceOf", "scalafix:DisableSyntax.asInstanceOf"))
protected def getLoggerContext(): LoggerContext =
LoggerFactory.getILoggerFactory.asInstanceOf[LoggerContext] // scalafix:ok DisableSyntax.asInstanceOf
LoggerFactory.getILoggerFactory.asInstanceOf[LoggerContext]

def initialize(): Monix3MdcAdapter =
initializeWithMonix3MdcAdapterAndLoggerContext(new Monix3MdcAdapter, getLoggerContext())
Expand Down
Loading
Loading