diff --git a/core/src/main/scala/com/evolutiongaming/catshelper/Log.scala b/core/src/main/scala/com/evolutiongaming/catshelper/Log.scala index ebdae01a..7ad185c1 100644 --- a/core/src/main/scala/com/evolutiongaming/catshelper/Log.scala +++ b/core/src/main/scala/com/evolutiongaming/catshelper/Log.scala @@ -44,30 +44,66 @@ object Log { object Mdc { private object Empty extends Mdc - private final case class Context(values: NonEmptyMap[String, String]) extends Mdc { - override def toString: String = s"MDC(${values.toSortedMap.mkString(", ")})" - } + private final case class Context(getValues: () => Map[String, String]) extends Mdc { + lazy val values: SortedMap[String, String] = SortedMap(getValues().toSeq: _*) - val empty: Mdc = Empty + override def toString: String = s"MDC(${values.mkString(", ")})" - def apply(head: (String, String), tail: (String, String)*): Mdc = Context(NonEmptyMap.of(head, tail: _*)) + override def hashCode(): Int = values.hashCode() - def fromSeq(seq: Seq[(String, String)]): Mdc = - NonEmptyMap.fromMap(SortedMap(seq: _*)).fold(empty){ nem => Context(nem) } + override def equals(obj: Any): Boolean = obj match { + case that: Context => this.values.equals(that.values) + case _ => false + } + } - def fromMap(map: Map[String, String]): Mdc = fromSeq(map.toSeq) + val empty: Mdc = Empty + + type Record = (String, String) + + @deprecated("Use Mdc.Lazy.apply. If it's not enough - Mdc.Lazy.fromMap", "3.9.0") + def apply(head: Record, tail: Record*): Mdc = Lazy.fromMap( (head +: tail).toMap) + + @deprecated("Use Mdc.Lazy.fromSeq", "3.9.0") + def fromSeq(seq: Seq[Record]): Mdc = Lazy.fromSeq(seq) + + @deprecated("Use Mdc.Lazy.fromMap", "3.9.0") + def fromMap(map: Map[String, String]): Mdc = Lazy.fromMap(map) + + object Lazy { + def apply(v1: => Record): Mdc = fromMap(Map(v1)) + def apply(v1: => Record, v2: => Record): Mdc = fromMap(Map(v1, v2)) + def apply(v1: => Record, v2: => Record, v3: => Record): Mdc = fromMap(Map(v1, v2, v3)) + def apply(v1: => Record, v2: => Record, v3: => Record, v4: => Record): Mdc = fromMap(Map(v1, v2, v3, v4)) + def apply(v1: => Record, v2: => Record, v3: => Record, v4: => Record, v5: => Record): Mdc = + fromMap(Map(v1, v2, v3, v4, v5)) + def apply(v1: => Record, v2: => Record, v3: => Record, v4: => Record, v5: => Record, v6: => Record): Mdc = + fromMap(Map(v1, v2, v3, v4, v5, v6)) + def apply(v1: => Record, v2: => Record, v3: => Record, v4: => Record, v5: => Record, v6: => Record, v7: => Record): Mdc = + fromMap(Map(v1, v2, v3, v4, v5, v6, v7)) + def apply(v1: => Record, v2: => Record, v3: => Record, v4: => Record, v5: => Record, v6: => Record, v7: => Record, v8: => Record): Mdc = + fromMap(Map(v1, v2, v3, v4, v5, v6, v7, v8)) + def apply(v1: => Record, v2: => Record, v3: => Record, v4: => Record, v5: => Record, v6: => Record, v7: => Record, v8: => Record, v9: => Record): Mdc = + fromMap(Map(v1, v2, v3, v4, v5, v6, v7, v8, v9)) + def apply(v1: => Record, v2: => Record, v3: => Record, v4: => Record, v5: => Record, v6: => Record, v7: => Record, v8: => Record, v9: => Record, v10: => Record): Mdc = + fromMap(Map(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10)) + + def fromSeq(seq: => Seq[Record]): Mdc = fromMap(seq.toMap) + + def fromMap(map: => Map[String, String]): Mdc = Context(() => map) + } implicit final val mdcSemigroup: Semigroup[Mdc] = Semigroup.instance { case (Empty, right) => right case (left, Empty) => left - case (Context(v1), Context(v2)) => Context(v1 ++ v2) + case (Context(v1), Context(v2)) => Context(() => v1() ++ v2()) } implicit final class MdcOps(val mdc: Mdc) extends AnyVal { def context: Option[NonEmptyMap[String, String]] = mdc match { case Empty => None - case Context(values) => Some(values) + case c: Context => NonEmptyMap.fromMap(c.values) } } } diff --git a/core/src/test/scala/com/evolutiongaming/catshelper/LogSpec.scala b/core/src/test/scala/com/evolutiongaming/catshelper/LogSpec.scala index 4776210e..9a978250 100644 --- a/core/src/test/scala/com/evolutiongaming/catshelper/LogSpec.scala +++ b/core/src/test/scala/com/evolutiongaming/catshelper/LogSpec.scala @@ -46,25 +46,25 @@ class LogSpec extends AnyFunSuite with Matchers { val stateT = for { log0 <- logOf("source") log = log0.prefixed(">").mapK(FunctionK.id) - _ <- log.trace("trace", Log.Mdc(mdc)) - _ <- log.debug("debug", Log.Mdc(mdc)) - _ <- log.info("info", Log.Mdc(mdc)) - _ <- log.warn("warn", Log.Mdc(mdc)) - _ <- log.warn("warn", Error, Log.Mdc(mdc)) - _ <- log.error("error", Log.Mdc(mdc)) - _ <- log.error("error", Error, Log.Mdc(mdc)) + _ <- log.trace("trace", Log.Mdc.Lazy(mdc)) + _ <- log.debug("debug", Log.Mdc.Lazy(mdc)) + _ <- log.info("info", Log.Mdc.Lazy(mdc)) + _ <- log.warn("warn", Log.Mdc.Lazy(mdc)) + _ <- log.warn("warn", Error, Log.Mdc.Lazy(mdc)) + _ <- log.error("error", Log.Mdc.Lazy(mdc)) + _ <- log.error("error", Error, Log.Mdc.Lazy(mdc)) } yield {} val (state, _) = stateT.run(State(Nil)) state shouldEqual State(List( - Action.Error1("> error", Error, Log.Mdc(mdc)), - Action.Error0("> error", Log.Mdc(mdc)), - Action.Warn1("> warn", Error, Log.Mdc(mdc)), - Action.Warn0("> warn", Log.Mdc(mdc)), - Action.Info("> info", Log.Mdc(mdc)), - Action.Debug("> debug", Log.Mdc(mdc)), - Action.Trace("> trace", Log.Mdc(mdc)), + Action.Error1("> error", Error, Log.Mdc.Lazy(mdc)), + Action.Error0("> error", Log.Mdc.Lazy(mdc)), + Action.Warn1("> warn", Error, Log.Mdc.Lazy(mdc)), + Action.Warn0("> warn", Log.Mdc.Lazy(mdc)), + Action.Info("> info", Log.Mdc.Lazy(mdc)), + Action.Debug("> debug", Log.Mdc.Lazy(mdc)), + Action.Trace("> trace", Log.Mdc.Lazy(mdc)), Action.OfStr("source"))) } @@ -73,7 +73,7 @@ class LogSpec extends AnyFunSuite with Matchers { val io = for { logOf <- LogOf.slf4j[IO] log <- logOf(getClass) - _ <- log.info("whatever", Log.Mdc("k" -> "v")) + _ <- log.info("whatever", Log.Mdc.Lazy("k" -> "v")) } yield org.slf4j.MDC.getCopyOfContextMap io.unsafeRunSync() shouldEqual null diff --git a/logback/src/test/scala/com/evolutiongaming/catshelper/LogOfFromLogbackSpec.scala b/logback/src/test/scala/com/evolutiongaming/catshelper/LogOfFromLogbackSpec.scala index b4b1a0e4..8d312ef0 100644 --- a/logback/src/test/scala/com/evolutiongaming/catshelper/LogOfFromLogbackSpec.scala +++ b/logback/src/test/scala/com/evolutiongaming/catshelper/LogOfFromLogbackSpec.scala @@ -10,7 +10,7 @@ class LogOfFromLogbackSpec extends AnyFunSuite with Matchers { val io = for { logOf <- LogOfFromLogback[IO] log <- logOf(getClass) - _ <- log.info("hello from logback", Log.Mdc("k" -> "test value for K")) + _ <- log.info("hello from logback", Log.Mdc.Lazy("k" -> "test value for K")) } yield () io.unsafeRunSync() diff --git a/version.sbt b/version.sbt index 0f987b7f..15c5dfcf 100644 --- a/version.sbt +++ b/version.sbt @@ -1 +1 @@ -ThisBuild / version := "2.14.1-SNAPSHOT" +ThisBuild / version := "2.15.0-SNAPSHOT"