diff --git a/build.sbt b/build.sbt index 461428c0bf..3b8f31440d 100644 --- a/build.sbt +++ b/build.sbt @@ -147,12 +147,11 @@ val scalaTest = libraryDependencies ++= Seq("freespec", "funsuite", "flatspec", "org.scalatest" %%% s"scalatest-$m" % "3.2.16" % Test ) -val zio1Version = "1.0.17" +val zio1Version = "1.0.18" val zio2Version = "2.0.14" val zio1InteropRsVersion = "1.3.12" val zio2InteropRsVersion = "2.0.2" - val sttpModelVersion = "1.6.0" val sttpSharedVersion = "1.3.15" @@ -217,7 +216,6 @@ lazy val allAggregates = projectsWithOptionalNative ++ playJson.projectRefs ++ prometheusBackend.projectRefs ++ openTelemetryMetricsBackend.projectRefs ++ - openTelemetryTracingZio1Backend.projectRefs ++ openTelemetryTracingZioBackend.projectRefs ++ finagleBackend.projectRefs ++ armeriaBackend.projectRefs ++ @@ -913,21 +911,6 @@ lazy val openTelemetryMetricsBackend = (projectMatrix in file("observability/ope .jvmPlatform(scalaVersions = scala2 ++ scala3) .dependsOn(core) -lazy val openTelemetryTracingZio1Backend = (projectMatrix in file("observability/opentelemetry-tracing-zio1-backend")) - .settings(commonJvmSettings) - .settings( - name := "opentelemetry-tracing-zio1-backend", - libraryDependencies ++= Seq( - "dev.zio" %% "zio-opentelemetry" % "1.0.0", - "org.scala-lang.modules" %% "scala-collection-compat" % "2.10.0", - "io.opentelemetry" % "opentelemetry-sdk-testing" % openTelemetryVersion % Test - ), - scalaTest - ) - .jvmPlatform(scalaVersions = scala2 ++ scala3) - .dependsOn(zio1 % compileAndTest) - .dependsOn(core) - lazy val openTelemetryTracingZioBackend = (projectMatrix in file("observability/opentelemetry-tracing-zio-backend")) .settings(commonJvmSettings) .settings( diff --git a/observability/opentelemetry-tracing-zio1-backend/src/main/scala/sttp/client4/opentelemetry/zio/OpenTelemetryTracingZioBackend.scala b/observability/opentelemetry-tracing-zio1-backend/src/main/scala/sttp/client4/opentelemetry/zio/OpenTelemetryTracingZioBackend.scala deleted file mode 100644 index ced2b53d86..0000000000 --- a/observability/opentelemetry-tracing-zio1-backend/src/main/scala/sttp/client4/opentelemetry/zio/OpenTelemetryTracingZioBackend.scala +++ /dev/null @@ -1,93 +0,0 @@ -package sttp.client4.opentelemetry.zio - -import io.opentelemetry.api.trace.{SpanKind, StatusCode} -import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator -import io.opentelemetry.context.propagation.{TextMapPropagator, TextMapSetter} -import sttp.capabilities.Effect -import sttp.client4._ -import sttp.client4.wrappers.DelegateBackend -import zio._ -import zio.telemetry.opentelemetry.TracingSyntax.OpenTelemetryZioOps -import zio.telemetry.opentelemetry._ - -import scala.collection.mutable - -private class OpenTelemetryTracingZioBackend[+P]( - delegate: GenericBackend[Task, P], - tracer: OpenTelemetryZioTracer, - tracing: Tracing -) extends DelegateBackend[Task, P](delegate) - with Backend[Task] { - def send[T](request: GenericRequest[T, P with Effect[Task]]): Task[Response[T]] = { - val carrier: mutable.Map[String, String] = mutable.Map().empty - val propagator: TextMapPropagator = W3CTraceContextPropagator.getInstance() - val setter: TextMapSetter[mutable.Map[String, String]] = (carrier, key, value) => carrier.update(key, value) - - (for { - _ <- Tracing.inject(propagator, carrier, setter) - _ <- tracer.before(request) - resp <- delegate.send(request.headers(carrier.toMap)) - _ <- tracer.after(resp) - } yield resp) - .span(tracer.spanName(request), SpanKind.CLIENT, { case _ => StatusCode.ERROR }) - .provide(tracing) - } -} - -object OpenTelemetryTracingZioBackend { - def apply(other: Backend[Task], tracing: Tracing.Service): Backend[Task] = - apply(other, tracing, OpenTelemetryZioTracer.Default) - - def apply(other: WebSocketBackend[Task], tracing: Tracing.Service): WebSocketBackend[Task] = - apply(other, tracing, OpenTelemetryZioTracer.Default) - - def apply[S](other: StreamBackend[Task, S], tracing: Tracing.Service): StreamBackend[Task, S] = - apply(other, tracing, OpenTelemetryZioTracer.Default) - - def apply[S](other: WebSocketStreamBackend[Task, S], tracing: Tracing.Service): WebSocketStreamBackend[Task, S] = - apply(other, tracing, OpenTelemetryZioTracer.Default) - - def apply(other: Backend[Task], tracing: Tracing.Service, tracer: OpenTelemetryZioTracer): Backend[Task] = - new OpenTelemetryTracingZioBackend(other, tracer, Has(tracing)) - - def apply( - other: WebSocketBackend[Task], - tracing: Tracing.Service, - tracer: OpenTelemetryZioTracer - ): WebSocketBackend[Task] = - new OpenTelemetryTracingZioBackend(other, tracer, Has(tracing)) with WebSocketBackend[Task] - - def apply[S]( - other: StreamBackend[Task, S], - tracing: Tracing.Service, - tracer: OpenTelemetryZioTracer - ): StreamBackend[Task, S] = - new OpenTelemetryTracingZioBackend(other, tracer, Has(tracing)) with StreamBackend[Task, S] - - def apply[S]( - other: WebSocketStreamBackend[Task, S], - tracing: Tracing.Service, - tracer: OpenTelemetryZioTracer - ): WebSocketStreamBackend[Task, S] = - new OpenTelemetryTracingZioBackend(other, tracer, Has(tracing)) with WebSocketStreamBackend[Task, S] - -} - -trait OpenTelemetryZioTracer { - def spanName[T](request: GenericRequest[T, Nothing]): String - def before[T](request: GenericRequest[T, Nothing]): RIO[Tracing, Unit] - def after[T](response: Response[T]): RIO[Tracing, Unit] -} - -object OpenTelemetryZioTracer { - val Default: OpenTelemetryZioTracer = new OpenTelemetryZioTracer { - override def spanName[T](request: GenericRequest[T, Nothing]): String = s"HTTP ${request.method.method}" - override def before[T](request: GenericRequest[T, Nothing]): RIO[Tracing, Unit] = - Tracing.setAttribute("http.method", request.method.method) *> - Tracing.setAttribute("http.url", request.uri.toString()) *> - ZIO.unit - override def after[T](response: Response[T]): RIO[Tracing, Unit] = - Tracing.setAttribute("http.status_code", response.code.code.toLong) *> - ZIO.unit - } -} diff --git a/observability/opentelemetry-tracing-zio1-backend/src/test/scala/sttp/client4/opentelemetry/zio/OpenTelemetryTracingZioBackendTest.scala b/observability/opentelemetry-tracing-zio1-backend/src/test/scala/sttp/client4/opentelemetry/zio/OpenTelemetryTracingZioBackendTest.scala deleted file mode 100644 index 79b1148703..0000000000 --- a/observability/opentelemetry-tracing-zio1-backend/src/test/scala/sttp/client4/opentelemetry/zio/OpenTelemetryTracingZioBackendTest.scala +++ /dev/null @@ -1,76 +0,0 @@ -package sttp.client4.opentelemetry.zio - -import io.opentelemetry.sdk.testing.exporter.InMemorySpanExporter -import io.opentelemetry.sdk.trace.SdkTracerProvider -import io.opentelemetry.sdk.trace.`export`.SimpleSpanProcessor -import org.scalatest.BeforeAndAfter -import org.scalatest.flatspec.AnyFlatSpec -import org.scalatest.matchers.should.Matchers -import sttp.client4.impl.zio.{RIOMonadAsyncError, ZioTestBase} -import sttp.client4.testing.BackendStub -import sttp.client4.{basicRequest, Backend, GenericRequest, Response, UriContext} -import sttp.model.StatusCode -import zio.Task -import zio.telemetry.opentelemetry.Tracing -import scala.collection.JavaConverters._ - -import scala.collection.mutable - -class OpenTelemetryTracingZioBackendTest extends AnyFlatSpec with Matchers with BeforeAndAfter with ZioTestBase { - - private val recordedRequests = mutable.ListBuffer[GenericRequest[_, _]]() - - private val spanExporter = InMemorySpanExporter.create() - - private val mockTracer = - SdkTracerProvider.builder().addSpanProcessor(SimpleSpanProcessor.create(spanExporter)).build().get(getClass.getName) - private val mockTracing = runtime.unsafeRun(Tracing.managed(mockTracer).useNow) - - private val backend: Backend[Task] = - OpenTelemetryTracingZioBackend( - BackendStub(new RIOMonadAsyncError[Any]).whenRequestMatchesPartial { - case r if r.uri.toString.contains("echo") => - recordedRequests += r - Response.ok("") - case r if r.uri.toString.contains("error") => - throw new RuntimeException("something went wrong") - }, - mockTracing - ) - - before { - recordedRequests.clear() - spanExporter.reset() - } - - "ZioTelemetryOpenTelemetryBackend" should "record spans for requests" in { - val response = runtime.unsafeRun(basicRequest.post(uri"http://stub/echo").send(backend)) - response.code shouldBe StatusCode.Ok - - val spans = spanExporter.getFinishedSpanItems.asScala - spans should have size 1 - spans.head.getName shouldBe "HTTP POST" - } - - it should "propagate span" in { - val response = runtime.unsafeRun(basicRequest.post(uri"http://stub/echo").send(backend)) - response.code shouldBe StatusCode.Ok - - val spans = spanExporter.getFinishedSpanItems.asScala - spans should have size 1 - - val spanId = spans.head.getSpanId - val traceId = spans.head.getTraceId - recordedRequests(0).header("traceparent") shouldBe Some(s"00-${traceId}-${spanId}-01") - } - - it should "set span status in case of error" in { - runtime.unsafeRunSync(basicRequest.post(uri"http://stub/error").send(backend)) - - val spans = spanExporter.getFinishedSpanItems.asScala - spans should have size 1 - - spans.head.getStatus.getStatusCode shouldBe io.opentelemetry.api.trace.StatusCode.ERROR - } - -}