Skip to content

Commit 8dc4e60

Browse files
committed
Close #612 - [logger-f-slf4j-mdc][logger-f-logback-mdc-monix3] SetMdcAdapterand Monix3MdcAdapter should be compatible with older slf4j and logback
- Downgrade slf4j to 2.0.12 - Downgrade logback to 1.5.0 for MDC adapter - Downgrade logback-scala-interop to 1.0.0
1 parent b488cf7 commit 8dc4e60

File tree

5 files changed

+141
-24
lines changed

5 files changed

+141
-24
lines changed

build.sbt

Lines changed: 57 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,8 @@ lazy val loggerF = (project in file("."))
8282
catsJvm,
8383
// catsJs,
8484
slf4jMdcJvm,
85-
// slf4jMdcJs,
8685
logbackMdcMonix3Jvm,
87-
// logbackMdcMonix3Js,
86+
testLogbackMdcMonix3Jvm,
8887
testKitJvm,
8988
// testKitJs,
9089
catsEffectJvm,
@@ -255,7 +254,7 @@ lazy val cats =
255254
lazy val catsJvm = cats.jvm
256255
lazy val catsJs = cats.js
257256

258-
lazy val slf4jMdc = module(ProjectName("slf4j-mdc"), crossProject(JVMPlatform, JSPlatform))
257+
lazy val slf4jMdc = module(ProjectName("slf4j-mdc"), crossProject(JVMPlatform))
259258
.settings(
260259
description := "Logger for F[_] - A tool to set MDC's MDCAdapter",
261260
libraryDependencies ++= Seq(
@@ -271,15 +270,15 @@ lazy val slf4jMdc = module(ProjectName("slf4j-mdc"), crossProject(JVMPlatform
271270
core
272271
)
273272
lazy val slf4jMdcJvm = slf4jMdc.jvm
274-
lazy val slf4jMdcJs = slf4jMdc.js
275273

276-
lazy val logbackMdcMonix3 = module(ProjectName("logback-mdc-monix3"), crossProject(JVMPlatform, JSPlatform))
274+
lazy val logbackMdcMonix3 = module(ProjectName("logback-mdc-monix3"), crossProject(JVMPlatform))
277275
.settings(
278276
description := "Logger for F[_] - logback MDC context map support for Monix 3",
279277
libraryDependencies ++= Seq(
280278
libs.logbackClassic,
281279
libs.logbackScalaInterop,
282280
libs.monix3Execution,
281+
libs.slf4jApi % Test,
283282
libs.tests.monix,
284283
libs.tests.effectieMonix3,
285284
) ++ libs.tests.hedgehogLibs,
@@ -295,7 +294,31 @@ lazy val logbackMdcMonix3 = module(ProjectName("logback-mdc-monix3"), crossPr
295294
slf4jLogger % Test,
296295
)
297296
lazy val logbackMdcMonix3Jvm = logbackMdcMonix3.jvm
298-
lazy val logbackMdcMonix3Js = logbackMdcMonix3.js
297+
298+
lazy val testLogbackMdcMonix3 = testProject(ProjectName("logback-mdc-monix3"), crossProject(JVMPlatform))
299+
.settings(
300+
description := "Logger for F[_] - testing logback MDC context map support for Monix 3",
301+
libraryDependencies ++= Seq(
302+
libs.slf4jApiLatest % Test,
303+
libs.logbackClassicLatest % Test,
304+
libs.logbackScalaInteropLatest % Test,
305+
libs.monix3Execution % Test,
306+
libs.tests.monix,
307+
libs.tests.effectieMonix3,
308+
) ++ libs.tests.hedgehogLibs,
309+
libraryDependencies := libraryDependenciesRemoveScala3Incompatible(
310+
scalaVersion.value,
311+
libraryDependencies.value,
312+
),
313+
)
314+
.dependsOn(
315+
core % Test,
316+
slf4jMdc % Test,
317+
logbackMdcMonix3 % "test->test",
318+
monix % Test,
319+
slf4jLogger % Test,
320+
)
321+
lazy val testLogbackMdcMonix3Jvm = testLogbackMdcMonix3.jvm
299322

300323
lazy val testKit =
301324
module(ProjectName("test-kit"), crossProject(JVMPlatform, JSPlatform))
@@ -575,21 +598,28 @@ lazy val props =
575598

576599
final val ExtrasVersion = "0.25.0"
577600

578-
final val Slf4JVersion = "2.0.17"
579-
final val LogbackVersion = "1.5.17"
601+
val Slf4JVersion = "2.0.12"
602+
val Slf4JLatestVersion = "2.0.17"
603+
604+
val LogbackVersion = "1.5.0"
605+
val LogbackLatestVersion = "1.5.17"
580606

581607
final val Log4sVersion = "1.10.0"
582608

583609
final val Log4JVersion = "2.19.0"
584610

585-
val LogbackScalaInteropVersion = "1.17.0"
611+
val LogbackScalaInteropVersion = "1.0.0"
612+
val LogbackScalaInteropLatestVersion = "1.17.0"
586613
}
587614

588615
lazy val libs =
589616
new {
590617

591-
lazy val slf4jApi: ModuleID = "org.slf4j" % "slf4j-api" % props.Slf4JVersion
592-
lazy val logbackClassic: ModuleID = "ch.qos.logback" % "logback-classic" % props.LogbackVersion
618+
lazy val slf4jApi: ModuleID = "org.slf4j" % "slf4j-api" % props.Slf4JVersion
619+
lazy val slf4jApiLatest: ModuleID = "org.slf4j" % "slf4j-api" % props.Slf4JLatestVersion
620+
621+
lazy val logbackClassic: ModuleID = "ch.qos.logback" % "logback-classic" % props.LogbackVersion
622+
lazy val logbackClassicLatest: ModuleID = "ch.qos.logback" % "logback-classic" % props.LogbackLatestVersion
593623

594624
lazy val log4sLib: ModuleID = "org.log4s" %% "log4s" % props.Log4sVersion
595625

@@ -612,7 +642,9 @@ lazy val libs =
612642

613643
lazy val effectieMonix: ModuleID = "io.kevinlee" %% "effectie-monix3" % props.EffectieVersion
614644

615-
lazy val logbackScalaInterop = "io.kevinlee" % "logback-scala-interop" % props.LogbackScalaInteropVersion
645+
lazy val logbackScalaInterop = "io.kevinlee" % "logback-scala-interop" % props.LogbackScalaInteropVersion
646+
lazy val logbackScalaInteropLatest =
647+
"io.kevinlee" % "logback-scala-interop" % props.LogbackScalaInteropLatestVersion
616648

617649
lazy val tests = new {
618650

@@ -670,6 +702,19 @@ def module(projectName: ProjectName, crossProject: CrossProject.Builder): CrossP
670702
def testProject(projectName: ProjectName, crossProject: CrossProject.Builder): CrossProject = {
671703
val prefixedName = s"test-${prefixedProjectName(projectName.projectName)}"
672704
projectCommonSettings(prefixedName, crossProject)
705+
.settings(
706+
// Disable publishing tasks
707+
publish / skip := true,
708+
publish := {},
709+
publishLocal := {},
710+
// Prevent artifact generation for publishing
711+
publishArtifact := false,
712+
packagedArtifacts := Map.empty,
713+
// Disable specific packaging tasks
714+
packageBin / publishArtifact := false,
715+
packageDoc / publishArtifact := false,
716+
packageSrc / publishArtifact := false,
717+
)
673718
}
674719

675720
def projectCommonSettings(projectName: String, crossProject: CrossProject.Builder): CrossProject =

modules/logger-f-logback-mdc-monix3/shared/src/main/scala/loggerf/logger/logback/Monix3MdcAdapter.scala

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,13 +62,29 @@ trait Monix3MdcAdapterOps {
6262
def initializeWithLoggerContext(loggerContext: LoggerContext): Monix3MdcAdapter =
6363
initializeWithMonix3MdcAdapterAndLoggerContext(new Monix3MdcAdapter, loggerContext)
6464

65+
@SuppressWarnings(Array("org.wartremover.warts.Equals"))
6566
def initializeWithMonix3MdcAdapterAndLoggerContext(
6667
monix3MdcAdapter: Monix3MdcAdapter,
6768
loggerContext: LoggerContext,
6869
): Monix3MdcAdapter = {
6970
val adapter = initialize0(monix3MdcAdapter)
71+
7072
loggerContext.setMDCAdapter(adapter)
71-
adapter
73+
if (loggerContext.getMDCAdapter == adapter) {
74+
// println("[LoggerContext] It's set by setMDCAdapter.")
75+
adapter
76+
} else {
77+
// println(
78+
// "[LoggerContext] The old setMDCAdapter doesn't replace `mdcAdapter` if it has already been set, " +
79+
// "so it will use reflection to set it in the `mdcAdapter` field."
80+
// )
81+
val loggerContextClass = classOf[LoggerContext]
82+
val field = loggerContextClass.getDeclaredField("mdcAdapter")
83+
field.setAccessible(true)
84+
field.set(loggerContext, adapter)
85+
field.setAccessible(false)
86+
adapter
87+
}
7288
}
7389

7490
}

modules/logger-f-logback-mdc-monix3/shared/src/test/scala/loggerf/logger/logback/Monix3MdcAdapterSpec.scala

Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@ import cats.syntax.all._
44
import hedgehog._
55
import hedgehog.runner._
66
import monix.eval.{Task, TaskLocal}
7-
import org.slf4j.spi.MDCAdapter
8-
import org.slf4j.{MDC, SetMdcAdapter}
7+
import org.slf4j.{LoggerFactory, MDC, SetMdcAdapter}
98

109
import java.io.{PrintWriter, StringWriter}
1110
import java.time.Instant
@@ -16,7 +15,8 @@ import scala.util.control.NonFatal
1615
/** @author Kevin Lee
1716
* @since 2023-07-03
1817
*/
19-
object Monix3MdcAdapterSpec extends Properties {
18+
trait Monix3MdcAdapterSpecsOnly {
19+
import Monix3MdcAdapterSpec._
2020

2121
/*
2222
* Task.defaultOptions.enableLocalContextPropagation is the same as
@@ -37,12 +37,12 @@ object Monix3MdcAdapterSpec extends Properties {
3737
.err
3838
.println(
3939
s"""Error when initializing Monix3MdcAdapter: ${writer.toString}
40-
|""".stripMargin
40+
|""".stripMargin
4141
)
4242
throw ex // scalafix:ok DisableSyntax.throw
4343
}
4444

45-
override def tests: List[Test] = List(
45+
def tests: List[Test] = List(
4646
property("Task - MDC should be able to put and get a value", testPutAndGet),
4747
property("Task - MDC should be able to put and get multiple values concurrently", testPutAndGetMultiple),
4848
property(
@@ -59,7 +59,8 @@ object Monix3MdcAdapterSpec extends Properties {
5959
property("Task - MDC: It should return context map for getCopyOfContextMap", testGetCopyOfContextMap),
6060
property("Task - MDC: It should return context map for getPropertyMap", testGetPropertyMap),
6161
property("Task - MDC: It should return context map for getKeys", testGetKeys),
62-
property("test SetMdcAdapter", testSetMdcAdapter),
62+
property("test SetMdcAdapter for MDC", testSetMdcAdapterForMDC),
63+
property("test LoggerContext.setMDCAdapter", testLoggerContextSetMdcAdapter),
6364
)
6465

6566
def before(): Unit =
@@ -487,7 +488,7 @@ object Monix3MdcAdapterSpec extends Properties {
487488
@SuppressWarnings(Array("org.wartremover.warts.ToString"))
488489
private def productToMap[A <: Product](product: A): Map[String, String] = {
489490
// This doesn't work for Scala 2.12
490-
// val fields = product.productElementNames.toVector
491+
// val fields = product.productElementNames.toVector
491492

492493
val fields = product.getClass.getDeclaredFields.map(_.getName)
493494
val length = product.productArity
@@ -506,7 +507,7 @@ object Monix3MdcAdapterSpec extends Properties {
506507
}
507508

508509
@SuppressWarnings(Array("org.wartremover.warts.Equals"))
509-
def testSetMdcAdapter: Property = for {
510+
def testSetMdcAdapterForMDC: Property = for {
510511
mdcAdapterName1 <- Gen.string(Gen.ascii, Range.linear(5, 5)).log("mdcAdapterName1")
511512
mdcAdapterName2 <- Gen.string(Gen.ascii, Range.linear(5, 5)).log("mdcAdapterName2")
512513
mdcAdapterName <- Gen.constant(mdcAdapterName1 + mdcAdapterName2).log("mdcAdapterName")
@@ -526,8 +527,37 @@ object Monix3MdcAdapterSpec extends Properties {
526527
)
527528
}
528529

530+
@SuppressWarnings(Array("org.wartremover.warts.Equals"))
531+
def testLoggerContextSetMdcAdapter: Property = for {
532+
mdcAdapterName1 <- Gen.string(Gen.ascii, Range.linear(5, 5)).log("mdcAdapterName1")
533+
mdcAdapterName2 <- Gen.string(Gen.ascii, Range.linear(5, 5)).log("mdcAdapterName2")
534+
mdcAdapterName <- Gen.constant(mdcAdapterName1 + mdcAdapterName2).log("mdcAdapterName")
535+
} yield {
536+
val mdcAdapter = TestMdcAdapter(mdcAdapterName)
537+
@SuppressWarnings(Array("org.wartremover.warts.AsInstanceOf"))
538+
val loggerContext =
539+
LoggerFactory
540+
.getILoggerFactory
541+
.asInstanceOf[ch.qos.logback.classic.LoggerContext] // scalafix:ok DisableSyntax.asInstanceOf
542+
543+
val before = loggerContext.getMDCAdapter()
544+
545+
val _ = Monix3MdcAdapter.initializeWithMonix3MdcAdapterAndLoggerContext(mdcAdapter, loggerContext)
546+
547+
val after = loggerContext.getMDCAdapter()
548+
549+
Result.all(
550+
List(
551+
Result.diffNamed("before should not be equal to the new MDCAdapter", before, mdcAdapter)(_ != _),
552+
(after ==== mdcAdapter).log("after SetMdcAdapter should be equal to the new MDCAdapter"),
553+
)
554+
)
555+
}
556+
}
557+
object Monix3MdcAdapterSpec extends Properties with Monix3MdcAdapterSpecsOnly {
558+
529559
@SuppressWarnings(Array("org.wartremover.warts.Var"))
530-
final case class TestMdcAdapter(name: String) extends MDCAdapter {
560+
final case class TestMdcAdapter(name: String) extends Monix3MdcAdapter {
531561

532562
override def put(key: String, `val`: String): Unit = ???
533563

@@ -539,7 +569,7 @@ object Monix3MdcAdapterSpec extends Properties {
539569

540570
override def getCopyOfContextMap: util.Map[String, String] = ???
541571

542-
override def setContextMap(contextMap: util.Map[String, String]): Unit = ???
572+
override def setContextMap0(contextMap: util.Map[String, String]): Unit = ???
543573

544574
override def pushByKey(key: String, value: String): Unit = ???
545575

modules/logger-f-slf4j-mdc/shared/src/main/scala/org/slf4j/SetMdcAdapter.scala

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package org.slf4j
22

33
import org.slf4j.spi.MDCAdapter
44

5+
import scala.util.control.NonFatal
6+
57
/** @author Kevin Lee
68
* @since 2025-03-09
79
*/
@@ -10,7 +12,23 @@ trait SetMdcAdapter {
1012
}
1113

1214
object SetMdcAdapter {
15+
@SuppressWarnings(Array("org.wartremover.warts.Null"))
1316
def apply(mdcAdapter: MDCAdapter): Unit = {
14-
MDC.setMDCAdapter(mdcAdapter)
17+
val mdcClass = classOf[MDC]
18+
try {
19+
val method = mdcClass.getDeclaredMethod("setMDCAdapter", classOf[MDCAdapter])
20+
val _ = method.invoke(null, mdcAdapter) // scalafix:ok DisableSyntax.null
21+
// println("[MDC] A method named `setMDCAdapter` was found, so it was set using `setMDCAdapter` with reflection.")
22+
()
23+
} catch {
24+
case NonFatal(_) =>
25+
// println(
26+
// "[MDC] No method named `setMDCAdapter` was found, so it will instead be set to the `mdcAdapter` field using reflection."
27+
// )
28+
val field = mdcClass.getDeclaredField("mdcAdapter")
29+
field.setAccessible(true)
30+
field.set(null, mdcAdapter) // scalafix:ok DisableSyntax.null
31+
field.setAccessible(false)
32+
}
1533
}
1634
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package loggerf.logger.logback
2+
3+
import hedgehog.runner._
4+
5+
/** @author Kevin Lee
6+
* @since 2025-03-11
7+
*/
8+
object Monix3MdcAdapterForLogback1_5_17_PlusAndSlf4j2_0_17Spec extends Properties with Monix3MdcAdapterSpecsOnly

0 commit comments

Comments
 (0)