From 85aff0eaffc6c515016ad925749d091143459be9 Mon Sep 17 00:00:00 2001 From: Simon Schenk Date: Sat, 14 Mar 2020 23:35:47 +0100 Subject: [PATCH] Upgrade to 1.0.0-RC18 (#105) --- .gitignore | 2 + build.sbt | 12 +- .../main/scala/zio/telemetry/Telemetry.scala | 25 --- .../telemetry/opentracing/OpenTracing.scala | 69 +++++-- .../zio/telemetry/opentracing/package.scala | 94 ++++----- .../main/scala/zio/telemetry/package.scala | 58 ------ .../scala/zio/telemetry/TelemetryTest.scala | 173 ---------------- .../opentracing/OpenTracingTest.scala | 184 ++++++++++++++++++ .../zio/telemetry/example/BackendServer.scala | 2 +- .../zio/telemetry/example/JaegerTracer.scala | 12 +- .../zio/telemetry/example/ProxyServer.scala | 2 +- .../example/config/Configuration.scala | 22 +-- .../telemetry/example/config/package.scala | 8 +- .../example/http/StatusService.scala | 13 +- .../example/http/StatusesService.scala | 16 +- 15 files changed, 328 insertions(+), 364 deletions(-) delete mode 100644 modules/core/src/main/scala/zio/telemetry/Telemetry.scala delete mode 100644 modules/core/src/main/scala/zio/telemetry/package.scala delete mode 100644 modules/core/src/test/scala/zio/telemetry/TelemetryTest.scala create mode 100644 modules/core/src/test/scala/zio/telemetry/opentracing/OpenTracingTest.scala diff --git a/.gitignore b/.gitignore index 7e3a1de8..2dba7f5a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ **/.DS_Store project/metals.sbt target +.bloop +.metals diff --git a/build.sbt b/build.sbt index 0223d397..4f75c98c 100644 --- a/build.sbt +++ b/build.sbt @@ -31,6 +31,8 @@ inThisBuild( ) ) +Global / onChangedBuildSource := ReloadOnSourceChanges + addCommandAlias("fmt", "all scalafmtSbt scalafmt test:scalafmt") addCommandAlias("check", "all scalafmtSbtCheck scalafmtCheck test:scalafmtCheck") @@ -41,11 +43,11 @@ lazy val root = .aggregate(core, example) val http4sVersion = "0.21.1" -val jaegerVersion = "1.1.0" -val sttpVersion = "2.0.1" +val jaegerVersion = "1.2.0" +val sttpVersion = "2.0.5" val opentracingVersion = "0.33.0" val zipkinVersion = "2.12.1" -val zioVersion = "1.0.0-RC17" +val zioVersion = "1.0.0-RC18-2" lazy val core = project @@ -62,7 +64,7 @@ lazy val core = ) ) -testFrameworks := Seq(new TestFramework("zio.test.sbt.ZTestFramework")) +Global / testFrameworks := Seq(new TestFramework("zio.test.sbt.ZTestFramework")) lazy val example = project @@ -83,7 +85,7 @@ lazy val example = "com.github.pureconfig" %% "pureconfig" % "0.12.3", "com.softwaremill.sttp.client" %% "async-http-client-backend-zio" % sttpVersion, "com.softwaremill.sttp.client" %% "circe" % sttpVersion, - "dev.zio" %% "zio-interop-cats" % "2.0.0.0-RC10", + "dev.zio" %% "zio-interop-cats" % "2.0.0.0-RC12", "io.zipkin.reporter2" % "zipkin-reporter" % zipkinVersion, "io.zipkin.reporter2" % "zipkin-sender-okhttp3" % zipkinVersion ) diff --git a/modules/core/src/main/scala/zio/telemetry/Telemetry.scala b/modules/core/src/main/scala/zio/telemetry/Telemetry.scala deleted file mode 100644 index 7a507026..00000000 --- a/modules/core/src/main/scala/zio/telemetry/Telemetry.scala +++ /dev/null @@ -1,25 +0,0 @@ -package zio.telemetry - -import zio.Cause -import zio.FiberRef -import zio.UIO -import zio.URIO -import zio.clock.Clock - -trait Telemetry extends Serializable { - def telemetry: Telemetry.Service -} - -object Telemetry { - - trait Service { - type A - - def currentSpan: FiberRef[A] - def root(opName: String): URIO[Clock, A] - def span(span: A, opName: String): URIO[Clock, A] - def finish(span: A): URIO[Clock, Unit] - def error(span: A, cause: Cause[_], tagError: Boolean, logError: Boolean): UIO[Unit] - } - -} diff --git a/modules/core/src/main/scala/zio/telemetry/opentracing/OpenTracing.scala b/modules/core/src/main/scala/zio/telemetry/opentracing/OpenTracing.scala index 6d7a0d06..00065b3f 100644 --- a/modules/core/src/main/scala/zio/telemetry/opentracing/OpenTracing.scala +++ b/modules/core/src/main/scala/zio/telemetry/opentracing/OpenTracing.scala @@ -4,36 +4,65 @@ import io.opentracing.propagation.Format import io.opentracing.Span import io.opentracing.SpanContext import io.opentracing.Tracer -import zio.URIO -import zio.ZIO -import zio.clock.Clock -import zio.telemetry._ -import zio.Task import java.util.concurrent.TimeUnit -import zio.UIO import scala.jdk.CollectionConverters._ - -trait OpenTracing extends Telemetry { - override def telemetry: OpenTracing.Service -} +import zio._ +import zio.clock.Clock object OpenTracing { - trait Service extends Telemetry.Service { - override type A = Span + trait Service { private[opentracing] val tracer: Tracer + def currentSpan: FiberRef[Span] + def root(opName: String): UIO[Span] + def span(span: Span, opName: String): UIO[Span] + def finish(span: Span): UIO[Unit] + def error(span: Span, cause: Cause[_], tagError: Boolean, logError: Boolean): UIO[Unit] } - def spanFrom[R, R1 <: R with Clock with OpenTracing, E, A, C <: Object]( + def live(tracer: Tracer, rootOpName: String = "ROOT"): ZLayer[Clock, Nothing, OpenTracing] = + ZLayer.fromManaged(managed(tracer, rootOpName)) + + private[opentracing] def managed(tracer: Tracer, rootOpName: String): ZManaged[Clock, Nothing, OpenTracing.Service] = + ZManaged.make( + for { + span <- UIO(tracer.buildSpan(rootOpName).start()) + ref <- FiberRef.make(span) + env <- ZIO.environment[Clock] + clock = env.get[Clock.Service] + tracer_ = tracer + } yield new OpenTracing.Service { + override val tracer: Tracer = tracer_ + override val currentSpan: FiberRef[Span] = ref + + override def root(opName: String): UIO[Span] = + UIO(tracer.buildSpan(opName).start()) + + override def span(span: Span, opName: String): UIO[Span] = + for { + old <- currentSpan.get + child <- UIO(tracer.buildSpan(opName).asChildOf(old).start()) + } yield child + + override def finish(span: Span): UIO[Unit] = + clock.currentTime(TimeUnit.MICROSECONDS).map(span.finish) + + override def error(span: Span, cause: Cause[_], tagError: Boolean, logError: Boolean): UIO[Unit] = + UIO(span.setTag("error", true)).when(tagError) *> + UIO(span.log(Map("error.object" -> cause, "stack" -> cause.prettyPrint).asJava)).when(logError) + } + )(_.currentSpan.get.flatMap(span => UIO(span.finish()))) + + def spanFrom[R, R1 <: R with Clock with OpenTracing, E, Span, C <: Object]( format: Format[C], carrier: C, - zio: ZIO[R, E, A], + zio: ZIO[R, E, Span], opName: String, tagError: Boolean = true, logError: Boolean = true - ): ZIO[R1, E, A] = + ): ZIO[R1, E, Span] = ZIO.accessM { env => - val telemetry = env.telemetry + val telemetry = env.get[OpenTracing.Service] Task(telemetry.tracer.extract(format, carrier)) .fold(_ => None, Option.apply) .flatMap { @@ -49,14 +78,14 @@ object OpenTracing { def inject[C <: Object](format: Format[C], carrier: C): ZIO[OpenTracing, Nothing, Unit] = ZIO.accessM { env => - val telemetry = env.telemetry + val telemetry = env.get[OpenTracing.Service] telemetry.currentSpan.get.flatMap { span => ZIO.effectTotal(telemetry.tracer.inject(span.context(), format, carrier)).unit } } def context: ZIO[OpenTracing, Nothing, SpanContext] = - ZIO.accessM { _.telemetry.currentSpan.get.map(_.context) } + ZIO.accessM(_.get.currentSpan.get.map(_.context)) def getBaggageItem(key: String): URIO[OpenTracing, Option[String]] = getSpan.map(_.getBaggageItem(key)).map(Option(_)) @@ -88,8 +117,8 @@ object OpenTracing { } yield () private def getSpan: URIO[OpenTracing, Span] = - ZIO.accessM { _.telemetry.currentSpan.get } + ZIO.accessM(_.get.currentSpan.get) private def getCurrentTimeMicros: ZIO[Clock, Nothing, Long] = - ZIO.accessM(_.clock.currentTime(TimeUnit.MICROSECONDS)) + ZIO.accessM(_.get.currentTime(TimeUnit.MICROSECONDS)) } diff --git a/modules/core/src/main/scala/zio/telemetry/opentracing/package.scala b/modules/core/src/main/scala/zio/telemetry/opentracing/package.scala index 167befd5..ac807f86 100644 --- a/modules/core/src/main/scala/zio/telemetry/opentracing/package.scala +++ b/modules/core/src/main/scala/zio/telemetry/opentracing/package.scala @@ -1,57 +1,63 @@ package zio.telemetry -import java.util.concurrent.TimeUnit - import io.opentracing.Span -import io.opentracing.Tracer import io.opentracing.propagation.Format -import zio.Cause -import zio.FiberRef -import zio.UIO -import zio.URIO -import zio.ZIO -import zio.ZManaged +import zio._ import zio.clock.Clock -import scala.jdk.CollectionConverters._ - package object opentracing { + type OpenTracing = Has[OpenTracing.Service] - def managed(tracer: Tracer, rootOpName: String = "ROOT"): ZManaged[Clock, Nothing, OpenTracing] = - ZManaged.make( - for { - span <- UIO(tracer.buildSpan(rootOpName).start()) - ref <- FiberRef.make(span) - tracer_ = tracer - } yield new OpenTracing { - - override val telemetry: OpenTracing.Service = new OpenTracing.Service { - override type A = Span - - override val tracer: Tracer = tracer_ - override val currentSpan: FiberRef[Span] = ref - - override def root(opName: String): URIO[Clock, Span] = - UIO(tracer.buildSpan(opName).start()) - - override def span(span: Span, opName: String): URIO[Clock, Span] = - for { - old <- currentSpan.get - child <- UIO(tracer.buildSpan(opName).asChildOf(old).start()) - } yield child - - override def finish(span: Span): URIO[Clock, Unit] = - URIO.accessM(_.clock.currentTime(TimeUnit.MICROSECONDS).map(span.finish)) - - override def error(span: Span, cause: Cause[_], tagError: Boolean, logError: Boolean): UIO[Unit] = - UIO(span.setTag("error", true)).when(tagError) *> - UIO(span.log(Map("error.object" -> cause, "stack" -> cause.prettyPrint).asJava)).when(logError) + implicit final class OpenTracingZioOps[R, E, A](val zio: ZIO[R, E, A]) extends AnyVal { - } - } - )(_.telemetry.currentSpan.get.flatMap(span => UIO(span.finish()))) + def root[R1 <: R with Clock with OpenTracing]( + opName: String, + tagError: Boolean = true, + logError: Boolean = true + ): ZIO[R1, E, A] = + for { + telemetry <- getTelemetry + root <- telemetry.root(opName) + r <- span(telemetry)(root, tagError, logError) + } yield r - implicit final class OpenTracingZioOps[R, E, A](val zio: ZIO[R, E, A]) extends AnyVal { + def span[R1 <: R with Clock with OpenTracing]( + opName: String, + tagError: Boolean = true, + logError: Boolean = true + ): ZIO[R1, E, A] = + for { + telemetry <- getTelemetry + old <- getSpan(telemetry) + child <- telemetry.span(old, opName) + r <- span(telemetry)(child, tagError, logError) + } yield r + + def span[R1 <: R with Clock](telemetry: OpenTracing.Service)( + span: Span, + tagError: Boolean, + logError: Boolean + ): ZIO[R1, E, A] = + for { + old <- getSpan(telemetry) + r <- (setSpan(telemetry)(span) *> + zio.catchAllCause { cause => + telemetry.error(span, cause, tagError, logError) *> + IO.done(Exit.Failure(cause)) + }).ensuring( + telemetry.finish(span) *> + setSpan(telemetry)(old) + ) + } yield r + + private def setSpan(telemetry: OpenTracing.Service)(span: Span): UIO[Unit] = + telemetry.currentSpan.set(span) + + private def getSpan(telemetry: OpenTracing.Service): UIO[Span] = + telemetry.currentSpan.get + + private def getTelemetry: ZIO[OpenTracing, Nothing, OpenTracing.Service] = + ZIO.environment[OpenTracing].map(_.get[OpenTracing.Service]) def spanFrom[R1 <: R with Clock with OpenTracing, C <: Object]( format: Format[C], diff --git a/modules/core/src/main/scala/zio/telemetry/package.scala b/modules/core/src/main/scala/zio/telemetry/package.scala deleted file mode 100644 index 9f279d3f..00000000 --- a/modules/core/src/main/scala/zio/telemetry/package.scala +++ /dev/null @@ -1,58 +0,0 @@ -package zio - -import zio.clock.Clock - -package object telemetry { - - implicit class TelemetryOps[R, E, A](private val zio: ZIO[R, E, A]) extends AnyVal { - - def root[R1 <: R with Clock with Telemetry]( - opName: String, - tagError: Boolean = true, - logError: Boolean = true - ): ZIO[R1, E, A] = - for { - telemetry <- getTelemetry - root <- telemetry.root(opName) - r <- span(telemetry)(root, tagError, logError) - } yield r - - def span[R1 <: R with Clock with Telemetry]( - opName: String, - tagError: Boolean = true, - logError: Boolean = true - ): ZIO[R1, E, A] = - for { - telemetry <- getTelemetry - old <- getSpan(telemetry) - child <- telemetry.span(old, opName) - r <- span(telemetry)(child, tagError, logError) - } yield r - - def span[R1 <: R with Clock](telemetry: Telemetry.Service)( - span: telemetry.A, - tagError: Boolean, - logError: Boolean - ): ZIO[R1, E, A] = - for { - old <- getSpan(telemetry) - r <- (setSpan(telemetry)(span) *> - zio.catchAllCause { cause => - telemetry.error(span, cause, tagError, logError) *> - IO.done(Exit.Failure(cause)) - }).ensuring( - telemetry.finish(span) *> - setSpan(telemetry)(old) - ) - } yield r - - private def setSpan(telemetry: Telemetry.Service)(span: telemetry.A): UIO[Unit] = - telemetry.currentSpan.set(span) - - private def getSpan(telemetry: Telemetry.Service): UIO[telemetry.A] = - telemetry.currentSpan.get - - private def getTelemetry: ZIO[Telemetry, Nothing, Telemetry.Service] = - ZIO.environment[Telemetry].map(_.telemetry) - } -} diff --git a/modules/core/src/test/scala/zio/telemetry/TelemetryTest.scala b/modules/core/src/test/scala/zio/telemetry/TelemetryTest.scala deleted file mode 100644 index cc98dc34..00000000 --- a/modules/core/src/test/scala/zio/telemetry/TelemetryTest.scala +++ /dev/null @@ -1,173 +0,0 @@ -package zio.telemetry - -import io.opentracing.mock.MockSpan -import io.opentracing.mock.MockTracer -import io.opentracing.propagation.Format -import io.opentracing.propagation.TextMapAdapter -import scala.collection.mutable -import scala.jdk.CollectionConverters._ -import zio.duration._ -import zio.telemetry.opentracing._ -import zio.telemetry.TelemetryTestUtils._ -import zio.test._ -import zio.test.Assertion._ -import zio.test.DefaultRunnableSpec -import zio.test.environment.TestClock -import zio.UIO -import zio.ZIO -import zio.ZManaged - -object TelemetryTest - extends DefaultRunnableSpec( - suite("zio opentracing")( - testM("managedService") { - makeTracer.flatMap { tracer => - managed(tracer) - .use_(UIO.unit) - .map(_ => - assert(tracer.finishedSpans.asScala, hasSize(equalTo(1))) && assert( - tracer.finishedSpans().get(0), - hasField[MockSpan, String]( - "operationName", - _.operationName(), - equalTo("ROOT") - ) && - hasField[MockSpan, Long]( - "parent", - _.parentId, - equalTo(0L) - ) - ) - ) - } - }, - testM("childSpan") { - for { - tracer <- makeTracer - _ <- makeService(tracer).use(UIO.unit.span("Child").provide) - } yield { - val spans = tracer.finishedSpans.asScala - val root = spans.find(_.operationName() == "ROOT") - val child = spans.find(_.operationName() == "Child") - assert(root, isSome(anything)) && - assert( - child, - isSome( - hasField[MockSpan, Long]( - "parent", - _.parentId, - equalTo(root.get.context().spanId()) - ) - ) - ) - } - }, - testM("rootSpan") { - for { - tracer <- makeTracer - _ <- makeService(tracer).use(UIO.unit.root("ROOT2").provide) - } yield { - val spans = tracer.finishedSpans.asScala - val root = spans.find(_.operationName() == "ROOT") - val child = spans.find(_.operationName() == "ROOT2") - assert(root, isSome(anything)) && - assert( - child, - isSome( - hasField[MockSpan, Long]( - "parent", - _.parentId, - equalTo(0L) - ) - ) - ) - } - }, - testM("inject - extract roundtrip") { - for { - tracer <- makeTracer - tm = new TextMapAdapter(mutable.Map.empty.asJava) - _ <- (OpenTracing.inject(Format.Builtin.TEXT_MAP, tm).span("foo") *> - OpenTracing - .spanFrom(Format.Builtin.TEXT_MAP, tm, UIO.unit, "baz") - .span("bar")).provideSomeManaged(makeService(tracer)) - } yield { - val spans = tracer.finishedSpans().asScala - val root = spans.find(_.operationName() == "ROOT") - val foo = spans.find(_.operationName() == "foo") - val bar = spans.find(_.operationName() == "bar") - val baz = spans.find(_.operationName() == "baz") - assert(root, isSome(anything)) && - assert(foo, isSome(anything)) && - assert(bar, isSome(anything)) && - assert(baz, isSome(anything)) && - assert(foo.get.parentId(), equalTo(root.get.context().spanId())) && - assert(bar.get.parentId(), equalTo(root.get.context().spanId())) && - assert(baz.get.parentId(), equalTo(foo.get.context().spanId())) - } - }, - testM("tagging") { - for { - tracer <- makeTracer - _ <- makeService(tracer).use( - UIO.unit - .tag("boolean", true) - .tag("int", 1) - .tag("string", "foo") - .provide - ) - } yield { - val tags = tracer.finishedSpans().asScala.head.tags.asScala.toMap - val expected = Map[String, Any]("boolean" -> true, "int" -> 1, "string" -> "foo") - assert(tags, equalTo(expected)) - } - }, - testM("logging") { - for { - tracer <- makeTracer - _ <- makeService(tracer).use( - (UIO.unit.log("message") *> TestClock.adjust(1000.micros)) - .log(Map("msg" -> "message", "size" -> 1)) - .provide - ) - } yield { - val tags = - tracer.finishedSpans().asScala.head.logEntries.asScala.map(le => le.timestampMicros -> le.fields.asScala) - val expected = List( - 0L -> Map("event" -> "message"), - 1000L -> Map[String, Any]("msg" -> "message", "size" -> 1) - ) - assert(tags, equalTo(expected)) - } - }, - testM("baggage") { - val test = - for { - _ <- OpenTracing.setBaggageItem("foo", "bar") - _ <- OpenTracing.setBaggageItem("bar", "baz") - fooBag <- OpenTracing.getBaggageItem("foo") - barBag <- OpenTracing.getBaggageItem("bar") - } yield assert(fooBag, isSome(equalTo("bar"))) && - assert(barBag, isSome(equalTo("baz"))) - test.provideSomeManaged(makeTracer.toManaged_.flatMap(makeService)) - } - ) - ) - -object TelemetryTestUtils { - - def makeTracer: UIO[MockTracer] = UIO.succeed(new MockTracer) - - def makeService( - tracer: MockTracer - ): ZManaged[TestClock, Nothing, TestClock with OpenTracing] = - for { - clockService <- ZIO.environment[TestClock].toManaged_ - telemetry_ <- managed(tracer) - } yield new TestClock with OpenTracing { - override val clock: TestClock.Service[Any] = clockService.clock - override val scheduler: TestClock.Service[Any] = clockService.scheduler - override def telemetry: OpenTracing.Service = telemetry_.telemetry - } - -} diff --git a/modules/core/src/test/scala/zio/telemetry/opentracing/OpenTracingTest.scala b/modules/core/src/test/scala/zio/telemetry/opentracing/OpenTracingTest.scala new file mode 100644 index 00000000..b59e0429 --- /dev/null +++ b/modules/core/src/test/scala/zio/telemetry/opentracing/OpenTracingTest.scala @@ -0,0 +1,184 @@ +package zio.telemetry.opentracing + +import io.opentracing.mock.MockSpan +import io.opentracing.mock.MockTracer +import io.opentracing.propagation.Format +import io.opentracing.propagation.TextMapAdapter +import scala.collection.mutable +import scala.jdk.CollectionConverters._ +import zio._ +import zio.clock.Clock +import zio.duration._ +import zio.test._ +import zio.test.Assertion._ +import zio.test.environment.TestClock + +object OpenTracingTest extends DefaultRunnableSpec { + + type HasMockTracer = Has[MockTracer] + + val mockTracer: Layer[Nothing, HasMockTracer] = + ZLayer.fromEffect(UIO(new MockTracer)) + + val testService: URLayer[HasMockTracer with Clock, OpenTracing] = + ZLayer.fromServiceManaged(tracer => OpenTracing.managed(tracer, "ROOT")) + + val customLayer = mockTracer ++ ((mockTracer ++ Clock.any) >>> testService) + + def spec = + suite("zio opentracing")( + testM("managedService") { + val tracer = new MockTracer + OpenTracing + .managed(tracer, "ROOT") + .use_(UIO.unit) + .map(_ => + assert(tracer.finishedSpans.asScala)(hasSize(equalTo(1))) && assert(tracer.finishedSpans().get(0))( + hasField[MockSpan, String]( + "operationName", + _.operationName(), + equalTo("ROOT") + ) && + hasField[MockSpan, Long]( + "parent", + _.parentId, + equalTo(0L) + ) + ) + ) + }, + suite("spans")( + testM("childSpan") { + for { + env <- ZIO.environment[HasMockTracer] + tracer = env.get[MockTracer] + _ <- UIO.unit.span("Child").span("ROOT") + } yield { + val spans = tracer.finishedSpans.asScala + val root = spans.find(_.operationName() == "ROOT") + val child = spans.find(_.operationName() == "Child") + assert(root)(isSome(anything)) && + assert(child)( + isSome( + hasField[MockSpan, Long]( + "parent", + _.parentId, + equalTo(root.get.context().spanId()) + ) + ) + ) + } + }, + testM("rootSpan") { + for { + env <- ZIO.environment[HasMockTracer] + tracer = env.get[MockTracer] + _ <- UIO.unit.root("ROOT2").root("ROOT") + } yield { + val spans = tracer.finishedSpans.asScala + val root = spans.find(_.operationName() == "ROOT") + val child = spans.find(_.operationName() == "ROOT2") + assert(root)(isSome(anything)) && + assert(child)( + isSome( + hasField[MockSpan, Long]( + "parent", + _.parentId, + equalTo(0L) + ) + ) + ) + } + }, + testM("inject - extract roundtrip") { + val tm = new TextMapAdapter(mutable.Map.empty.asJava) + val injectExtract = OpenTracing.inject(Format.Builtin.TEXT_MAP, tm).span("foo") *> + OpenTracing + .spanFrom(Format.Builtin.TEXT_MAP, tm, UIO.unit, "baz") + .span("bar") + for { + env <- ZIO.environment[HasMockTracer] + tracer = env.get[MockTracer] + _ <- injectExtract.span("ROOT") + } yield { + val spans = tracer.finishedSpans().asScala + val root = spans.find(_.operationName() == "ROOT") + val foo = spans.find(_.operationName() == "foo") + val bar = spans.find(_.operationName() == "bar") + val baz = spans.find(_.operationName() == "baz") + assert(root)(isSome(anything)) && + assert(foo)(isSome(anything)) && + assert(bar)(isSome(anything)) && + assert(baz)(isSome(anything)) && + assert(foo.get.parentId())(equalTo(root.get.context().spanId())) && + assert(bar.get.parentId())(equalTo(root.get.context().spanId())) && + assert(baz.get.parentId())(equalTo(foo.get.context().spanId())) + } + }, + testM("tagging") { + for { + env <- ZIO.environment[HasMockTracer] + tracer = env.get[MockTracer] + _ <- UIO.unit + .tag("boolean", true) + .tag("int", 1) + .tag("string", "foo") + .span("foo") + } yield { + val tags = tracer.finishedSpans().asScala.head.tags.asScala.toMap + val expected = Map[String, Any]("boolean" -> true, "int" -> 1, "string" -> "foo") + assert(tags)(equalTo(expected)) + } + }, + testM("logging") { + val duration = 1000.micros + + /* + * TODO: + * Explicit sleep has been introduced due to the change in behavior of TestClock.adjust + * which made it affect only "wall" clock while leaving the fiber one intact. That being + * said, this piece of code should be replaced as soon as there's a better suited combinator + * available. + */ + val log = + for { + _ <- UIO.unit.log("message") + _ <- TestClock.adjust(duration) + _ <- ZIO.sleep(duration).log(Map("msg" -> "message", "size" -> 1)) + } yield () + + for { + env <- ZIO.environment[HasMockTracer] + tracer = env.get[MockTracer] + _ <- log.span("foo") + } yield { + val tags = + tracer + .finishedSpans() + .asScala + .collect { + case span if span.operationName == "foo" => + span.logEntries().asScala.map(le => le.timestampMicros -> le.fields.asScala.toMap) + } + .flatten + .toList + + val expected = List( + 0L -> Map("event" -> "message"), + 1000L -> Map[String, Any]("msg" -> "message", "size" -> 1) + ) + assert(tags)(equalTo(expected)) + } + }, + testM("baggage") { + for { + _ <- OpenTracing.setBaggageItem("foo", "bar") + _ <- OpenTracing.setBaggageItem("bar", "baz") + fooBag <- OpenTracing.getBaggageItem("foo") + barBag <- OpenTracing.getBaggageItem("bar") + } yield assert(fooBag)(isSome(equalTo("bar"))) && + assert(barBag)(isSome(equalTo("baz"))) + } + ).provideCustomLayer(customLayer) + ) +} diff --git a/modules/example/src/main/scala/zio/telemetry/example/BackendServer.scala b/modules/example/src/main/scala/zio/telemetry/example/BackendServer.scala index 7d99136f..79d7807b 100644 --- a/modules/example/src/main/scala/zio/telemetry/example/BackendServer.scala +++ b/modules/example/src/main/scala/zio/telemetry/example/BackendServer.scala @@ -14,7 +14,7 @@ object BackendServer extends CatsApp { override def run(args: List[String]): ZIO[ZEnv, Nothing, Int] = (for { - conf <- config.load.provide(Configuration.Live) + conf <- Configuration.load.provideLayer(Configuration.live) service = makeService(conf.tracer.host, "zio-backend") router = Router[AppTask]("/" -> StatusService.status(service)).orNotFound result <- BlazeServerBuilder[AppTask] diff --git a/modules/example/src/main/scala/zio/telemetry/example/JaegerTracer.scala b/modules/example/src/main/scala/zio/telemetry/example/JaegerTracer.scala index 8cd58a6f..e90a6f07 100644 --- a/modules/example/src/main/scala/zio/telemetry/example/JaegerTracer.scala +++ b/modules/example/src/main/scala/zio/telemetry/example/JaegerTracer.scala @@ -4,15 +4,15 @@ import io.jaegertracing.Configuration import io.jaegertracing.internal.samplers.ConstSampler import io.jaegertracing.zipkin.ZipkinV2Reporter import org.apache.http.client.utils.URIBuilder -import zio.ZManaged +import zio.ZLayer import zio.clock.Clock -import zio.telemetry.opentracing.{ managed, OpenTracing } +import zio.telemetry.opentracing.OpenTracing import zipkin2.reporter.AsyncReporter import zipkin2.reporter.okhttp3.OkHttpSender object JaegerTracer { - def makeService(host: String, serviceName: String): ZManaged[Clock, Throwable, Clock with OpenTracing] = { + def makeService(host: String, serviceName: String): ZLayer[Clock, Throwable, Clock with OpenTracing] = { val url = new URIBuilder().setScheme("http").setHost(host).setPath("/api/v2/spans").build.toString val senderBuilder = OkHttpSender.newBuilder.compressionEnabled(true).endpoint(url) @@ -21,10 +21,6 @@ object JaegerTracer { .withReporter(new ZipkinV2Reporter(AsyncReporter.create(senderBuilder.build))) .build - managed(tracer).map { telemetryService => - new Clock.Live with OpenTracing { - override def telemetry: OpenTracing.Service = telemetryService.telemetry - } - } + OpenTracing.live(tracer) ++ Clock.live } } diff --git a/modules/example/src/main/scala/zio/telemetry/example/ProxyServer.scala b/modules/example/src/main/scala/zio/telemetry/example/ProxyServer.scala index f820344e..d187f2c4 100644 --- a/modules/example/src/main/scala/zio/telemetry/example/ProxyServer.scala +++ b/modules/example/src/main/scala/zio/telemetry/example/ProxyServer.scala @@ -15,7 +15,7 @@ object ProxyServer extends CatsApp { override def run(args: List[String]): ZIO[ZEnv, Nothing, Int] = (for { - conf <- config.load.provide(Configuration.Live) + conf <- Configuration.load.provideLayer(Configuration.live) service = makeService(conf.tracer.host, "zio-proxy") backendUrl <- ZIO.fromEither(Uri.safeApply(conf.backend.host, conf.backend.port)) router = Router[AppTask]("/" -> StatusesService.statuses(backendUrl, service)).orNotFound diff --git a/modules/example/src/main/scala/zio/telemetry/example/config/Configuration.scala b/modules/example/src/main/scala/zio/telemetry/example/config/Configuration.scala index 066de7ce..54e7f259 100644 --- a/modules/example/src/main/scala/zio/telemetry/example/config/Configuration.scala +++ b/modules/example/src/main/scala/zio/telemetry/example/config/Configuration.scala @@ -2,24 +2,22 @@ package zio.telemetry.example.config import pureconfig.ConfigSource import pureconfig.generic.auto._ -import zio.{ RIO, Task } - -trait Configuration extends Serializable { - val config: Configuration.Service[Any] -} +import zio.Task +import zio.ZLayer +import zio.ZIO object Configuration { - trait Service[R] { - val load: RIO[R, Config] + trait Service { + val load: Task[Config] } - trait Live extends Configuration { - override val config: Service[Any] = new Service[Any] { - val load: Task[Config] = Task.effect(ConfigSource.default.loadOrThrow[Config]) - } + object Live extends Service { + val load: Task[Config] = Task.effect(ConfigSource.default.loadOrThrow[Config]) } - object Live extends Live + val live: ZLayer[Any, Throwable, Configuration] = ZLayer.succeed(Live) + + val load: ZIO[Configuration, Throwable, Config] = ZIO.accessM[Configuration] { _.get.load } } diff --git a/modules/example/src/main/scala/zio/telemetry/example/config/package.scala b/modules/example/src/main/scala/zio/telemetry/example/config/package.scala index 08fc18f2..0f541f80 100644 --- a/modules/example/src/main/scala/zio/telemetry/example/config/package.scala +++ b/modules/example/src/main/scala/zio/telemetry/example/config/package.scala @@ -1,7 +1,9 @@ package zio.telemetry.example -import zio.RIO +import zio.Has + +package object config { + + type Configuration = Has[Configuration.Service] -package object config extends Configuration.Service[Configuration] { - val load: RIO[Configuration, Config] = RIO.accessM(_.config.load) } diff --git a/modules/example/src/main/scala/zio/telemetry/example/http/StatusService.scala b/modules/example/src/main/scala/zio/telemetry/example/http/StatusService.scala index 4e26ce14..8a4db404 100644 --- a/modules/example/src/main/scala/zio/telemetry/example/http/StatusService.scala +++ b/modules/example/src/main/scala/zio/telemetry/example/http/StatusService.scala @@ -11,7 +11,8 @@ import zio.clock.Clock import zio.interop.catz._ import zio.telemetry.example.http.{ Status => ServiceStatus } import zio.telemetry.opentracing._ -import zio.{ ZIO, ZManaged } +import zio.ZIO +import zio.ZLayer import scala.jdk.CollectionConverters._ @@ -22,15 +23,13 @@ object StatusService { implicit def encoder[A: Encoder]: EntityEncoder[AppTask, A] = jsonEncoderOf[AppTask, A] - def status(service: ZManaged[Clock, Throwable, Clock with OpenTracing]): HttpRoutes[AppTask] = + def status(service: ZLayer[Clock, Throwable, Clock with OpenTracing]): HttpRoutes[AppTask] = HttpRoutes.of[AppTask] { case request @ GET -> Root / "status" => val headers = request.headers.toList.map(h => h.name.value -> h.value).toMap - service.use { env => - ZIO.unit - .spanFrom(HttpHeadersFormat, new TextMapAdapter(headers.asJava), "/status") - .provide(env) *> Ok(ServiceStatus.up("backend").asJson) - } + ZIO.unit + .spanFrom(HttpHeadersFormat, new TextMapAdapter(headers.asJava), "/status") + .provideLayer(service) *> Ok(ServiceStatus.up("backend").asJson) } } diff --git a/modules/example/src/main/scala/zio/telemetry/example/http/StatusesService.scala b/modules/example/src/main/scala/zio/telemetry/example/http/StatusesService.scala index 95f04614..bdb48134 100644 --- a/modules/example/src/main/scala/zio/telemetry/example/http/StatusesService.scala +++ b/modules/example/src/main/scala/zio/telemetry/example/http/StatusesService.scala @@ -11,14 +11,16 @@ import sttp.model.Uri import zio.clock.Clock import zio.interop.catz._ import zio.telemetry.opentracing.OpenTracing -import zio.{ UIO, ZManaged } +import zio.UIO +import zio.ZIO +import zio.ZLayer import scala.collection.mutable import scala.jdk.CollectionConverters._ object StatusesService { - def statuses(backendUri: Uri, service: ZManaged[Clock, Throwable, Clock with OpenTracing]): HttpRoutes[AppTask] = { + def statuses(backendUri: Uri, service: ZLayer[Clock, Throwable, Clock with OpenTracing]): HttpRoutes[AppTask] = { val dsl: Http4sDsl[AppTask] = Http4sDsl[AppTask] import dsl._ @@ -26,9 +28,10 @@ object StatusesService { HttpRoutes.of[AppTask] { case GET -> Root / "statuses" => - service.use { env => - val zio = for { - _ <- env.telemetry.root("/statuses") + val zio = + for { + env <- ZIO.environment[OpenTracing] + _ <- env.get.root("/statuses") _ <- OpenTracing.tag(Tags.SPAN_KIND.getKey, Tags.SPAN_KIND_CLIENT) _ <- OpenTracing.tag(Tags.HTTP_METHOD.getKey, GET.name) _ <- OpenTracing.setBaggageItem("proxy-baggage-item-key", "proxy-baggage-item-value") @@ -45,8 +48,7 @@ object StatusesService { } } yield res - zio.provideSomeManaged(service) - } + zio.provideLayer(service) } }