Skip to content
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
59 changes: 48 additions & 11 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ lazy val loggerF = (project in file("."))
slf4jMdcJvm,
logbackMdcMonix3Jvm,
testLogbackMdcMonix3Jvm,
logbackMdcCatsEffect3Jvm,
testKitJvm,
// testKitJs,
catsEffectJvm,
Expand Down Expand Up @@ -109,7 +110,7 @@ lazy val core =
),
)
lazy val coreJvm = core.jvm
lazy val coreJs = core.js
lazy val coreJs = core.js.settings(commonJsSettings)

lazy val slf4jLogger = module(ProjectName("slf4j"), crossProject(JVMPlatform, JSPlatform))
.settings(
Expand All @@ -124,7 +125,7 @@ lazy val slf4jLogger = module(ProjectName("slf4j"), crossProject(JVMPlatform,
)
.dependsOn(core)
lazy val slf4jLoggerJvm = slf4jLogger.jvm
lazy val slf4jLoggerJs = slf4jLogger.js
lazy val slf4jLoggerJs = slf4jLogger.js.settings(commonJsSettings)

lazy val log4sLogger =
module(ProjectName("log4s"), crossProject(JVMPlatform, JSPlatform))
Expand All @@ -140,7 +141,7 @@ lazy val log4sLogger =
)
.dependsOn(core)
lazy val log4sLoggerJvm = log4sLogger.jvm
lazy val log4sLoggerJs = log4sLogger.js
lazy val log4sLoggerJs = log4sLogger.js.settings(commonJsSettings)

lazy val log4jLogger =
module(ProjectName("log4j"), crossProject(JVMPlatform, JSPlatform))
Expand Down Expand Up @@ -200,7 +201,7 @@ lazy val log4jLogger =
)
.dependsOn(core)
lazy val log4jLoggerJvm = log4jLogger.jvm
lazy val log4jLoggerJs = log4jLogger.js
lazy val log4jLoggerJs = log4jLogger.js.settings(commonJsSettings)

lazy val sbtLogging =
module(ProjectName("sbt-logging"), crossProject(JVMPlatform, JSPlatform))
Expand Down Expand Up @@ -232,7 +233,7 @@ lazy val sbtLogging =
)
.dependsOn(core)
lazy val sbtLoggingJvm = sbtLogging.jvm
lazy val sbtLoggingJs = sbtLogging.js
lazy val sbtLoggingJs = sbtLogging.js.settings(commonJsSettings)

lazy val cats =
module(ProjectName("cats"), crossProject(JVMPlatform, JSPlatform))
Expand All @@ -251,7 +252,7 @@ lazy val cats =
)
.dependsOn(core % props.IncludeTest)
lazy val catsJvm = cats.jvm
lazy val catsJs = cats.js
lazy val catsJs = cats.js.settings(commonJsSettings)

lazy val slf4jMdc = module(ProjectName("slf4j-mdc"), crossProject(JVMPlatform))
.settings(
Expand Down Expand Up @@ -319,6 +320,29 @@ lazy val testLogbackMdcMonix3 = testProject(ProjectName("logback-mdc-monix3")
)
lazy val testLogbackMdcMonix3Jvm = testLogbackMdcMonix3.jvm

lazy val logbackMdcCatsEffect3 = module(ProjectName("logback-mdc-cats-effect3"), crossProject(JVMPlatform))
.settings(
description := "Logger for F[_] - logback MDC context map support for Cats Effect 3",
libraryDependencies ++= Seq(
libs.logbackClassic,
libs.logbackScalaInterop,
libs.catsEffect3Eap,
libs.tests.effectieCatsEffect3,
libs.tests.extrasHedgehogCatsEffect3,
) ++ libs.tests.hedgehogLibs,
libraryDependencies := libraryDependenciesRemoveScala3Incompatible(
scalaVersion.value,
libraryDependencies.value,
),
javaOptions += "-Dcats.effect.ioLocalPropagation=true",
)
.dependsOn(
core,
monix % Test,
slf4jLogger % Test,
)
lazy val logbackMdcCatsEffect3Jvm = logbackMdcCatsEffect3.jvm

lazy val testKit =
module(ProjectName("test-kit"), crossProject(JVMPlatform, JSPlatform))
.settings(
Expand All @@ -335,7 +359,7 @@ lazy val testKit =
)
.dependsOn(core % props.IncludeTest)
lazy val testKitJvm = testKit.jvm
lazy val testKitJs = testKit.js
lazy val testKitJs = testKit.js.settings(commonJsSettings)

lazy val catsEffect =
module(ProjectName("cats-effect"), crossProject(JVMPlatform, JSPlatform))
Expand All @@ -350,14 +374,14 @@ lazy val catsEffect =
.settings(noPublish)
.dependsOn(core % props.IncludeTest, cats)
lazy val catsEffectJvm = catsEffect.jvm
lazy val catsEffectJs = catsEffect.js
lazy val catsEffectJs = catsEffect.js.settings(commonJsSettings)

lazy val catsEffect3 =
module(ProjectName("cats-effect3"), crossProject(JVMPlatform, JSPlatform))
.settings(
description := "Logger for F[_] - Cats Effect 3",
libraryDependencies ++= libs.tests.hedgehogLibs ++ List(
libs.effectieCatsEffect3 % Test,
libs.tests.effectieCatsEffect3,
libs.tests.extrasHedgehogCatsEffect3,
),
libraryDependencies := libraryDependenciesRemoveScala3Incompatible(
Expand All @@ -368,7 +392,7 @@ lazy val catsEffect3 =
.settings(noPublish)
.dependsOn(core % props.IncludeTest, cats)
lazy val catsEffect3Jvm = catsEffect3.jvm
lazy val catsEffect3Js = catsEffect3.js
lazy val catsEffect3Js = catsEffect3.js.settings(commonJsSettings)

lazy val monix =
module(ProjectName("monix"), crossProject(JVMPlatform, JSPlatform))
Expand All @@ -383,7 +407,7 @@ lazy val monix =
.settings(noPublish)
.dependsOn(core % props.IncludeTest, cats)
lazy val monixJvm = monix.jvm
lazy val monixJs = monix.js
lazy val monixJs = monix.js.settings(commonJsSettings)

lazy val testCore =
testProject(
Expand Down Expand Up @@ -683,6 +707,8 @@ lazy val libs =

lazy val catsEffect3 = "org.typelevel" %% "cats-effect" % props.CatsEffect3Version

lazy val catsEffect3Eap = "org.typelevel" %% "cats-effect" % "3.6-02a43a6"

lazy val monix3Execution = "io.monix" %% "monix-execution" % props.Monix3Version

lazy val effectieCore: ModuleID = "io.kevinlee" %% "effectie-core" % props.EffectieVersion
Expand Down Expand Up @@ -719,6 +745,8 @@ lazy val libs =

lazy val extrasTestingTools = "io.kevinlee" %% "extras-testing-tools" % props.ExtrasVersion % Test

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

lazy val extrasConcurrent = "io.kevinlee" %% "extras-concurrent" % props.ExtrasVersion % Test
lazy val extrasConcurrentTesting = "io.kevinlee" %% "extras-concurrent-testing" % props.ExtrasVersion % Test

Expand Down Expand Up @@ -781,6 +809,7 @@ def projectCommonSettings(projectName: String, crossProject: CrossProject.Builde
// , Compile / compile / wartremoverErrors ++= commonWarts((update / scalaBinaryVersion).value)
// , Test / compile / wartremoverErrors ++= commonWarts((update / scalaBinaryVersion).value)
wartremoverErrors ++= commonWarts((update / scalaBinaryVersion).value),
fork := true,
Compile / console / wartremoverErrors := List.empty,
Compile / console / wartremoverWarnings := List.empty,
Compile / console / scalacOptions :=
Expand All @@ -804,3 +833,11 @@ def projectCommonSettings(projectName: String, crossProject: CrossProject.Builde
}),
/* } Coveralls */
)

lazy val commonJsSettings: SettingsDefinition = List(
Test / fork := false
// Test / scalacOptions ++= (if (scalaVersion.value.startsWith("3")) List.empty
// else List("-P:scalajs:nowarnGlobalExecutionContext")),
// Test / compile / scalacOptions ++= (if (scalaVersion.value.startsWith("3")) List.empty
// else List("-P:scalajs:nowarnGlobalExecutionContext")),
)
4 changes: 4 additions & 0 deletions changelogs/2.2.0-beta1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
## [2.2.0-beta1](https://github.com/Kevin-Lee/logger-f/issues?q=is%3Aissue+milestone%3Av2-m3) - 2025-01-03

* Add `MdcAdapter` for Cats Effect 3 to properly share context through `MDC` with `IO` and `IOLocal` from Cats Effect 3 (#441)
* This is a beta released for testing the new feature with cats-effect `3.6-02a43a6`.
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@ trait ConcurrentSupport {
val stringWriter = new StringWriter()
val printWriter = new PrintWriter(stringWriter)
th.printStackTrace(printWriter)
logger(s"⚠️ Error in Executor: ${stringWriter.toString}")

@SuppressWarnings(Array("org.wartremover.warts.ToString"))
val message = stringWriter.toString
logger(s"⚠️ Error in Executor: $message")
},
)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package loggerf.logger.logback

import cats.effect.unsafe.IOLocals
import cats.effect.{IOLocal, SyncIO}
import cats.syntax.all._
import ch.qos.logback.classic.LoggerContext
import logback_scala_interop.JLoggerFMdcAdapter
import org.slf4j.{LoggerFactory, MDC}

import java.util.{Map => JMap, Set => JSet}
import scala.jdk.CollectionConverters._
import scala.util.control.NonFatal

/** @author Kevin Lee
* @since 2023-07-07
*/
class Ce3MdcAdapter extends JLoggerFMdcAdapter {

private[this] val localContext: IOLocal[Map[String, String]] =
IOLocal[Map[String, String]](Map.empty[String, String])
.syncStep(1)
.flatMap(
_.leftMap(_ =>
new Error(
"Failed to initialize the local context of the Ce3MdcAdapter."
)
).liftTo[SyncIO]
)
.unsafeRunSync()

override def put(key: String, `val`: String): Unit =
IOLocals.update(localContext)(_ + (key -> `val`))

@SuppressWarnings(Array("org.wartremover.warts.Null"))
override def get(key: String): String =
IOLocals.get(localContext).getOrElse(key, null) // scalafix:ok DisableSyntax.null

override def remove(key: String): Unit = IOLocals.update(localContext)(_ - key)

override def clear(): Unit = IOLocals.reset(localContext)

override def getCopyOfContextMap: JMap[String, String] = getPropertyMap0

override def setContextMap0(contextMap: JMap[String, String]): Unit =
IOLocals.set(localContext, contextMap.asScala.toMap)

private def getPropertyMap0: JMap[String, String] = IOLocals.get(localContext).asJava

override def getPropertyMap: JMap[String, String] = getPropertyMap0

override def getKeys: JSet[String] = IOLocals.get(localContext).keySet.asJava

}
object Ce3MdcAdapter {

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

@SuppressWarnings(Array("org.wartremover.warts.AsInstanceOf", "scalafix:DisableSyntax.asInstanceOf"))
def initialize(): Ce3MdcAdapter = {
val loggerContext =
LoggerFactory.getILoggerFactory.asInstanceOf[LoggerContext]
initializeWithLoggerContext(loggerContext)
}

def initializeWithLoggerContext(loggerContext: LoggerContext): Ce3MdcAdapter = {
val adapter = initialize0()
try {
val field = classOf[LoggerContext].getDeclaredField("mdcAdapter")
field.setAccessible(true)
field.set(loggerContext, adapter)
field.setAccessible(false)
adapter
} catch {
case NonFatal(_) => adapter
}
}
}
Loading
Loading