Skip to content

Commit

Permalink
Bump cats-effect to 3.6.0-RC1 and make the necessary changes accordin…
Browse files Browse the repository at this point in the history
…gly.
  • Loading branch information
kevin-lee committed Jan 2, 2025
1 parent 1de40bd commit f6ad481
Show file tree
Hide file tree
Showing 7 changed files with 1,119 additions and 25 deletions.
8 changes: 3 additions & 5 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -284,15 +284,15 @@ lazy val logbackMdcCatsEffect3 = module(ProjectName("logback-mdc-cats-effect3"),
libraryDependencies ++= Seq(
libs.logbackClassic,
libs.logbackScalaInterop,
libs.catsEffect3Eap,
libs.catsEffect3,
libs.tests.effectieCatsEffect3,
libs.tests.extrasHedgehogCatsEffect3,
) ++ libs.tests.hedgehogLibs,
libraryDependencies := libraryDependenciesRemoveScala3Incompatible(
scalaVersion.value,
libraryDependencies.value,
),
javaOptions += "-Dcats.effect.ioLocalPropagation=true",
javaOptions += "-Dcats.effect.trackFiberContext=true",
)
.dependsOn(
core,
Expand Down Expand Up @@ -572,7 +572,7 @@ lazy val props =

final val CatsVersion = "2.7.0"

val CatsEffect3Version = "3.3.14"
val CatsEffect3Version = "3.6.0-RC1"

val Monix3Version = "3.4.0"

Expand Down Expand Up @@ -607,8 +607,6 @@ 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
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package loggerf.logger.logback

import cats.effect.unsafe.IOLocals
import cats.effect.{IOLocal, SyncIO}
import cats.syntax.all._
import ch.qos.logback.classic.LoggerContext
Expand All @@ -18,7 +17,7 @@ class Ce3MdcAdapter extends JLoggerFMdcAdapter {

private[this] val localContext: IOLocal[Map[String, String]] =
IOLocal[Map[String, String]](Map.empty[String, String])
.syncStep(1)
.syncStep(100)
.flatMap(
_.leftMap(_ =>
new Error(
Expand All @@ -28,27 +27,32 @@ class Ce3MdcAdapter extends JLoggerFMdcAdapter {
)
.unsafeRunSync()

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

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

override def remove(key: String): Unit = IOLocals.update(localContext)(_ - key)
override def remove(key: String): Unit = {
val unsafeThreadLocal = localContext.unsafeThreadLocal()
unsafeThreadLocal.set(unsafeThreadLocal.get - key)
}

override def clear(): Unit = IOLocals.reset(localContext)
override def clear(): Unit = localContext.unsafeThreadLocal().set(Map.empty[String, String])

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

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

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

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

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

}
object Ce3MdcAdapter {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package loggerf.logger.logback

import cats.effect.{IOLocal, unsafe}
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 Ce3MdcAdapterWithIoRuntime(private val ioRuntime: unsafe.IORuntime) extends JLoggerFMdcAdapter {

private[this] val localContext: IOLocal[Map[String, String]] =
IOLocal[Map[String, String]](Map.empty[String, String])
.unsafeRunSync()(ioRuntime)

override def put(key: String, `val`: String): Unit = {
val unsafeThreadLocal = localContext.unsafeThreadLocal()
unsafeThreadLocal.set(unsafeThreadLocal.get + (key -> `val`))
}

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

override def remove(key: String): Unit = {
val unsafeThreadLocal = localContext.unsafeThreadLocal()
unsafeThreadLocal.set(unsafeThreadLocal.get - key)
}

override def clear(): Unit = localContext.unsafeThreadLocal().set(Map.empty[String, String])

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

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

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

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

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

}
object Ce3MdcAdapterWithIoRuntime {

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

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

def initializeWithLoggerContext(loggerContext: LoggerContext)(implicit ioRuntime: unsafe.IORuntime): Ce3MdcAdapterWithIoRuntime = {
val adapter = initialize0(ioRuntime)
try {
val field = classOf[LoggerContext].getDeclaredField("mdcAdapter")
field.setAccessible(true)
field.set(loggerContext, adapter)
field.setAccessible(false)
adapter
} catch {
case NonFatal(_) => adapter
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ import scala.jdk.CollectionConverters._
* @since 2023-07-07
*/
object Ce3MdcAdapterSpec extends Properties {
println(s"cats.effect.ioLocalPropagation=${sys.props.getOrElse("cats.effect.ioLocalPropagation", "")}")
sys.props.put("cats.effect.ioLocalPropagation", "true")
println(s"cats.effect.ioLocalPropagation=${sys.props.getOrElse("cats.effect.ioLocalPropagation", "")}")
println(
s"${this.getClass.getSimpleName.stripSuffix("$")}[B] cats.effect.trackFiberContext=${sys.props.getOrElse("cats.effect.trackFiberContext", "")}"
)

implicit val ioRuntime: IORuntime = cats.effect.unsafe.implicits.global

Expand All @@ -29,6 +29,10 @@ object Ce3MdcAdapterSpec extends Properties {
"IO - MDC should be able to put and get with isolated nested modifications",
testPutAndGetMultipleIsolatedNestedModifications,
),
property(
"IO - MDC should be able to put and get with isolated nested modifications - more complex case",
testPutAndGetMultipleIsolatedNestedModifications2,
),
property("IO - MDC: It should be able to set a context map", testSetContextMap),
property("IO - MDC should be able to remove the value for the existing key", testRemove),
property("IO - MDC should be able to remove the multiple values for the existing keys", testRemoveMultiple),
Expand Down Expand Up @@ -137,14 +141,182 @@ object Ce3MdcAdapterSpec extends Properties {
isolated2After,
key1Result,
key2Result,
// (MDC.get("key-1") ==== a).log(s"""${Thread.currentThread().getName}:After: MDC.get("key-1") is not $a"""),
// (MDC.get("key-2") ==== null).log("""After: MDC.get("key-2") is not null"""), // scalafix:ok DisableSyntax.null
// (MDC.get("key-1") ==== a).log(s"""${Thread.currentThread().getName}:After: MDC.get("key-1") is not $a"""),
// (MDC.get("key-2") ==== null).log("""After: MDC.get("key-2") is not null"""), // scalafix:ok DisableSyntax.null
)
)

test.unsafeRunSync()
}

@SuppressWarnings(Array("org.wartremover.warts.Null"))
def testPutAndGetMultipleIsolatedNestedModifications2: Property =
for {
a <- Gen.string(Gen.alpha, Range.linear(1, 2)).map("a:" + _).log("a")
a2 <- Gen.string(Gen.alpha, Range.linear(1, 2)).map("a2:" + a + _).log("a2")
b1 <- Gen.string(Gen.alpha, Range.linear(3, 4)).map("b1:" + _).log("b1")
c2 <- Gen.string(Gen.alpha, Range.linear(5, 6)).map("c1:" + _).log("c1")
a3 <- Gen.string(Gen.alpha, Range.linear(7, 8)).map("a3:" + _).log("a3")
b3 <- Gen.string(Gen.alpha, Range.linear(9, 10)).map("b3:" + _).log("b3")
c3 <- Gen.string(Gen.alpha, Range.linear(11, 12)).map("c3:" + _).log("c3")
} yield {

before()

val beforeSet = (MDC.get("key-1") ==== null).log("before set") // scalafix:ok DisableSyntax.null
MDC.put("key-1", a2)
val beforeAndAfterSet =
(MDC.get("key-1") ==== a2).log(s"""before: MDC.get("key-1") should be $a2""") // scalafix:ok DisableSyntax.null

val test = for {
beforeSet2 <- IO((MDC.get("key-1") ==== a2).log("before set2")) // scalafix:ok DisableSyntax.null
_ <- IO(MDC.put("key-1", a))
before <-
IO {
val actual1 = MDC.get("key-1")
val actual2 = MDC.get("key-2")
val actual3 = MDC.get("key-3")
List(
(actual1 ==== a).log(s"""before: MDC.get("key-1") should be $a, but it is $actual1"""),
(actual2 ==== null).log(
s"""before: MDC.get("key-2") should be null, but it is $actual2"""
), // scalafix:ok DisableSyntax.null
(actual3 ==== null).log(
s"""before: MDC.get("key-3") should be null, but it is $actual3"""
), // scalafix:ok DisableSyntax.null
)
}
beforeIsolated <- IO {
val actual = MDC.get("key-1")
(actual ==== a).log(s"""beforeIsolated: MDC.get("key-1") should be $a, but it is $actual""")
}
.start
.flatMap(_.joinWithNever)

isolated1 <-
(
IO {
val actual = MDC.get("key-1")
(actual ==== a).log(s"""isolated1Before: MDC.get("key-1") should be $a, but it is $actual""")
}.flatMap { isolated1Before =>
IO(
MDC.put("key-1", b1)
) *> IO {
val actual = MDC.get("key-1")
List(
isolated1Before,
(actual ==== b1).log(s"""isolated1After: MDC.get("key-1") should be $b1, but it is $actual"""),
)
}
}
).start
isolated2 <-
(
IO {
val actual1 = MDC.get("key-1")
val actual2 = MDC.get("key-2")
val actual3 = MDC.get("key-3")
List(
(actual1 ==== a).log(s"""isolated2Before: MDC.get("key-1") should be $a, but it is $actual1"""),
(actual2 ==== null).log(
s"""isolated2Before: MDC.get("key-2") should be null, but it is $actual2"""
), // scalafix:ok DisableSyntax.null
(actual3 ==== null).log(
s"""isolated2Before: MDC.get("key-3") should be null, but it is $actual3"""
), // scalafix:ok DisableSyntax.null
)
}.flatMap { isolated2Before =>
IO(
MDC.put("key-2", c2)
) *> IO {
val actual1 = MDC.get("key-1")
val actual2 = MDC.get("key-2")
val actual3 = MDC.get("key-3")
isolated2Before ++
List(
(actual1 ==== a).log(s"""isolated2After: MDC.get("key-1") should be $a, but it is $actual1"""),
(actual2 ==== c2).log(
s"""isolated2After: MDC.get("key-2") should be $c2, but it is $actual2"""
), // scalafix:ok DisableSyntax.null
(actual3 ==== null).log(
s"""isolated2After: MDC.get("key-3") should be null, but it is $actual3"""
), // scalafix:ok DisableSyntax.null
)
}
}
).start
isolated3 <-
(
IO {
val actual1 = MDC.get("key-1")
val actual2 = MDC.get("key-2")
val actual3 = MDC.get("key-3")
List(
(actual1 ==== a).log(s"""isolated3Before: MDC.get("key-1") should be $a, but it is $actual1"""),
(actual2 ==== null).log(
s"""isolated3Before: MDC.get("key-2") should be null, but it is $actual2"""
), // scalafix:ok DisableSyntax.null
(actual3 ==== null).log(
s"""isolated3Before: MDC.get("key-3") should be null, but it is $actual3"""
), // scalafix:ok DisableSyntax.null
)
}.flatMap { isolated3Before =>
IO(
MDC.put("key-1", b3)
) *> IO(
MDC.put("key-2", c3)
) *> IO(
MDC.put("key-3", a3)
) *> IO {
val actual1 = MDC.get("key-1")
val actual2 = MDC.get("key-2")
val actual3 = MDC.get("key-3")
isolated3Before ++ List(
(actual1 ==== b3).log(s"""isolated3After: MDC.get("key-1") should be $b3, but it is $actual1"""),
(actual2 ==== c3).log(s"""isolated3After: MDC.get("key-2") should be $c3, but it is $actual2"""),
(actual3 ==== a3).log(s"""isolated3After: MDC.get("key-3") should be $a3, but it is $actual3"""),
)
}
}
).start

joinedIsolated1 <- isolated1.joinWithNever
joinedIsolated2 <- isolated2.joinWithNever
joinedIsolated3 <- isolated3.joinWithNever

key1Result <- IO((MDC.get("key-1") ==== a).log(s"""After: MDC.get("key-1") is not $a"""))
key2Result <- IO(
(MDC.get("key-2") ==== null).log("""After: MDC.get("key-2") is not null""")
) // scalafix:ok DisableSyntax.null
key3Result <- IO(
(MDC.get("key-3") ==== null).log("""After: MDC.get("key-3") is not null""")
) // scalafix:ok DisableSyntax.null
} yield List(
beforeSet,
beforeAndAfterSet,
beforeSet2,
) ++ before ++ List(
beforeIsolated
) ++
joinedIsolated1 ++
joinedIsolated2 ++
joinedIsolated3 ++ List(
key1Result,
key2Result,
key3Result,
// (MDC.get("key-1") ==== a).log(s"""${Thread.currentThread().getName}:After: MDC.get("key-1") is not $a"""),
// (MDC.get("key-2") ==== null).log("""After: MDC.get("key-2") is not null"""), // scalafix:ok DisableSyntax.null
)

val afterIo = MDC.get("key-1") ==== a2
Result.all(
test.unsafeRunSync() ++
List(
afterIo
)
)
}

def testSetContextMap: Property =
for {
someContext <- Gens.genSomeContext.log("someContext")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,7 @@ import scala.jdk.CollectionConverters._
* @since 2023-07-07
*/
object Ce3MdcAdapterSpec2 extends Properties {
println(s"cats.effect.ioLocalPropagation=${sys.props.getOrElse("cats.effect.ioLocalPropagation", "")}")
sys.props.put("cats.effect.ioLocalPropagation", "true")
println(s"cats.effect.ioLocalPropagation=${sys.props.getOrElse("cats.effect.ioLocalPropagation", "")}")
println(s"${this.getClass.getSimpleName.stripSuffix("$")}[B] cats.effect.trackFiberContext=${sys.props.getOrElse("cats.effect.trackFiberContext", "")}")

implicit val ioRuntime: IORuntime = cats.effect.unsafe.implicits.global

Expand Down
Loading

0 comments on commit f6ad481

Please sign in to comment.