From e986af998ed9a3a6386f7c4ab3c648173d86f256 Mon Sep 17 00:00:00 2001 From: "typelevel-steward[bot]" <106827141+typelevel-steward[bot]@users.noreply.github.com> Date: Wed, 4 Sep 2024 16:21:48 +0000 Subject: [PATCH 1/3] Update scala-library to 2.12.20 --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index bb913f8..470ee11 100644 --- a/build.sbt +++ b/build.sbt @@ -2,7 +2,7 @@ ThisBuild / tlBaseVersion := "0.6" val http4sVersion = "0.23.27" val natchezVersion = "0.3.6" -val scala212Version = "2.12.19" +val scala212Version = "2.12.20" val scala213Version = "2.13.14" val scala3Version = "3.3.3" val slf4jVersion = "2.0.16" From f9146b1493081f19ec64be144dca50a0d18ca29d Mon Sep 17 00:00:00 2001 From: Brian Holt Date: Fri, 6 Sep 2024 14:19:07 -0500 Subject: [PATCH 2/3] Add methods to override Span.Options on the span created by the client middleware This would allow callers to set the Span Creation Policy, etc., on the span created by the middleware. --- .../natchez/http4s/NatchezMiddleware.scala | 41 +++++++++- .../http4s/NatchezMiddlewareSuite.scala | 81 +++++++++++++++---- 2 files changed, 106 insertions(+), 16 deletions(-) diff --git a/modules/http4s/src/main/scala/natchez/http4s/NatchezMiddleware.scala b/modules/http4s/src/main/scala/natchez/http4s/NatchezMiddleware.scala index 6736dd8..39a0649 100644 --- a/modules/http4s/src/main/scala/natchez/http4s/NatchezMiddleware.scala +++ b/modules/http4s/src/main/scala/natchez/http4s/NatchezMiddleware.scala @@ -9,7 +9,7 @@ import cats.syntax.all._ import cats.effect.{MonadCancel, MonadCancelThrow, Outcome, Resource} import cats.effect.syntax.all._ import Outcome._ -import natchez.{Tags, Trace, TraceValue} +import natchez.{Span, Tags, Trace, TraceValue} import natchez.Span.Options.Defaults import natchez.Span.SpanKind import org.http4s.client.Client @@ -114,6 +114,42 @@ object NatchezMiddleware { (additionalAttributes: (String, TraceValue)*): Client[F] = NatchezMiddleware.client(client, (_: Request[F]) => additionalAttributes.pure[F]) + /** + * A middleware that adds the current span's kernel to outgoing requests, performs requests in + * a span called `http4s-client-request`, and adds the following fields to that span. + * + * - "client.http.method" -> "GET", "PUT", etc. + * - "client.http.uri" -> request URI + * - "client.http.status_code" -> "200", "403", etc. // why is this a string? + * + * @param client the `Client[F]` to be enhanced + * @param additionalAttributes additional attributes to be added to the span + * @tparam F An effect with instances of `Trace[F]` and `MonadCancelThrow[F]` + * @return the enhanced `Client[F]` + */ + def clientWithAttributes[F[_] : Trace : MonadCancelThrow](client: Client[F], + spanOptions: Span.Options) + (additionalAttributes: (String, TraceValue)*): Client[F] = + NatchezMiddleware.client(client, spanOptions, (_: Request[F]) => additionalAttributes.pure[F]) + + /** + * A middleware that adds the current span's kernel to outgoing requests, performs requests in + * a span called `http4s-client-request`, and adds the following fields to that span. + * + * - "client.http.method" -> "GET", "PUT", etc. + * - "client.http.uri" -> request URI + * - "client.http.status_code" -> "200", "403", etc. // why is this a string? + * + * @param client the `Client[F]` to be enhanced + * @param additionalAttributesF a function that takes the `Request[F]` and returns any additional attributes to be added to the span + * @tparam F An effect with instances of `Trace[F]` and `MonadCancelThrow[F]` + * @return the enhanced `Client[F]` + */ + def client[F[_] : Trace : MonadCancelThrow](client: Client[F], + additionalAttributesF: Request[F] => F[Seq[(String, TraceValue)]], + ): Client[F] = + NatchezMiddleware.client(client, Defaults.withSpanKind(SpanKind.Client), additionalAttributesF) + /** * A middleware that adds the current span's kernel to outgoing requests, performs requests in * a span called `http4s-client-request`, and adds the following fields to that span. @@ -128,11 +164,12 @@ object NatchezMiddleware { * @return the enhanced `Client[F]` */ def client[F[_] : Trace : MonadCancelThrow](client: Client[F], + spanOptions: Span.Options, additionalAttributesF: Request[F] => F[Seq[(String, TraceValue)]], ): Client[F] = Client { req => Resource.applyFull {poll => - Trace[F].span("http4s-client-request", Defaults.withSpanKind(SpanKind.Client)) { + Trace[F].span("http4s-client-request", spanOptions) { for { knl <- Trace[F].kernel _ <- Trace[F].put( diff --git a/modules/http4s/src/test/scala/natchez/http4s/NatchezMiddlewareSuite.scala b/modules/http4s/src/test/scala/natchez/http4s/NatchezMiddlewareSuite.scala index c0a7e50..ba573ef 100644 --- a/modules/http4s/src/test/scala/natchez/http4s/NatchezMiddlewareSuite.scala +++ b/modules/http4s/src/test/scala/natchez/http4s/NatchezMiddlewareSuite.scala @@ -8,19 +8,20 @@ import cats.Monad import cats.data.{Chain, Kleisli} import cats.effect.{IO, MonadCancelThrow, Resource} import munit.ScalaCheckEffectSuite +import natchez.Span.Options.SpanCreationPolicy import natchez.Span.SpanKind -import natchez.{InMemory, Kernel, Span, Trace, TraceValue} import natchez.TraceValue.StringValue -import natchez.http4s.syntax.entrypoint._ -import org.http4s._ -import org.http4s.headers._ +import natchez.http4s.syntax.entrypoint.* +import natchez.* +import org.http4s.* import org.http4s.client.Client -import org.http4s.dsl.request._ -import org.http4s.syntax.literals._ -import org.scalacheck.{Arbitrary, Gen} +import org.http4s.dsl.request.* +import org.http4s.headers.* +import org.http4s.syntax.literals.* import org.scalacheck.Arbitrary.arbitrary import org.scalacheck.effect.PropF -import org.typelevel.ci._ +import org.scalacheck.{Arbitrary, Gen} +import org.typelevel.ci.* class NatchezMiddlewareSuite extends InMemorySuite @@ -44,6 +45,47 @@ class NatchezMiddlewareSuite } yield key -> value } + private implicit val arbCIString: Arbitrary[CIString] = Arbitrary { + Gen.alphaLowerStr.map(CIString(_)) + } + + private implicit val arbKernel: Arbitrary[Kernel] = Arbitrary { + arbitrary[Map[CIString, String]].map(Kernel(_)) + } + + private implicit val arbSpanCreationPolicy: Arbitrary[SpanCreationPolicy] = Arbitrary { + Gen.oneOf(SpanCreationPolicy.Default, SpanCreationPolicy.Coalesce, SpanCreationPolicy.Suppress) + } + + private implicit val arbSpanKind: Arbitrary[SpanKind] = Arbitrary { + Gen.oneOf( + SpanKind.Internal, + SpanKind.Client, + SpanKind.Server, + SpanKind.Producer, + SpanKind.Consumer, + ) + } + + private implicit val arbSpanOptions: Arbitrary[Span.Options] = Arbitrary { + for { + parentKernel <- arbitrary[Option[Kernel]] + spanCreationPolicy <- arbitrary[SpanCreationPolicy] + spanKind <- arbitrary[SpanKind] + links <- arbitrary[List[Kernel]].map(Chain.fromSeq) + } yield { + links.foldLeft { + parentKernel.foldLeft { + Span + .Options + .Defaults + .withSpanKind(spanKind) + .withSpanCreationPolicy(spanCreationPolicy) + }(_.withParentKernel(_)) + }(_.withLink(_)) + } + } + test("do not leak security and payload headers to the client request") { val headers = Headers( // security @@ -76,7 +118,7 @@ class NatchezMiddlewareSuite for { ref <- IO.ref(Chain.empty[(Lineage, NatchezCommand)]) ep <- IO.pure(new InMemory.EntryPoint(ref)) - routes <- IO.pure(ep.liftT(httpRoutes[Kleisli[IO, natchez.Span[IO], *]]())) + routes <- IO.pure(ep.liftT(httpRoutes[Kleisli[IO, natchez.Span[IO], *]](None))) response <- routes.orNotFound.run(request) } yield { assertEquals(response.status.code, 200) @@ -85,7 +127,8 @@ class NatchezMiddlewareSuite } test("generate proper tracing history") { - PropF.forAllF { (userSpecifiedTags: List[(String, TraceValue)]) => + PropF.forAllF { (userSpecifiedTags: List[(String, TraceValue)], + maybeSpanOptions: Option[Span.Options]) => val request = Request[IO]( method = Method.GET, uri = uri"/hello/some-name", @@ -118,10 +161,13 @@ class NatchezMiddlewareSuite "http.status_code" -> StringValue("200") ) + val spanOptions = maybeSpanOptions.getOrElse(Span.Options.Defaults.withSpanKind(SpanKind.Client)) + val kernel = maybeSpanOptions.flatMap(_.parentKernel) + List( (Lineage.Root, NatchezCommand.CreateRootSpan("/hello/some-name", requestKernel, Span.Options.Defaults)), (Lineage.Root("/hello/some-name"), NatchezCommand.CreateSpan("call-proxy", None, Span.Options.Defaults)), - (Lineage.Root("/hello/some-name") / "call-proxy", NatchezCommand.CreateSpan("http4s-client-request", None, Span.Options.Defaults.withSpanKind(SpanKind.Client))), + (Lineage.Root("/hello/some-name") / "call-proxy", NatchezCommand.CreateSpan("http4s-client-request", kernel, spanOptions)), (Lineage.Root("/hello/some-name") / "call-proxy" / "http4s-client-request", NatchezCommand.AskKernel(requestKernel)), (Lineage.Root("/hello/some-name") / "call-proxy" / "http4s-client-request", NatchezCommand.Put(clientRequestTags)), (Lineage.Root("/hello/some-name") / "call-proxy" / "http4s-client-request", NatchezCommand.Put(userSpecifiedTags)), @@ -136,15 +182,22 @@ class NatchezMiddlewareSuite for { ep <- InMemory.EntryPoint.create[IO] - routes <- IO.pure(ep.liftT(httpRoutes[Kleisli[IO, natchez.Span[IO], *]](userSpecifiedTags: _*))) + routes <- IO.pure(ep.liftT(httpRoutes[Kleisli[IO, natchez.Span[IO], *]](maybeSpanOptions, userSpecifiedTags *))) _ <- routes.orNotFound.run(request) history <- ep.ref.get } yield assertEquals(history.toList, expectedHistory) } } - private def httpRoutes[F[_]: MonadCancelThrow: Trace](additionalAttributes: (String, TraceValue)*): HttpRoutes[F] = { - val client = NatchezMiddleware.clientWithAttributes(echoHeadersClient[F])(additionalAttributes: _*) + private def httpRoutes[F[_]: MonadCancelThrow: Trace](maybeSpanOptions: Option[Span.Options], + additionalAttributes: (String, TraceValue)*): HttpRoutes[F] = { + val client = maybeSpanOptions match { + case Some(spanOptions) => + NatchezMiddleware.clientWithAttributes(echoHeadersClient[F], spanOptions)(additionalAttributes *) + case None => + NatchezMiddleware.clientWithAttributes(echoHeadersClient[F])(additionalAttributes *) + } + val server = NatchezMiddleware.server(proxyRoutes(client)) server } From 026b2679399fd3a92675439fed3d54989f4a5d30 Mon Sep 17 00:00:00 2001 From: "typelevel-steward[bot]" <106827141+typelevel-steward[bot]@users.noreply.github.com> Date: Fri, 27 Sep 2024 12:23:13 +0000 Subject: [PATCH 3/3] Update scala3-library, ... to 3.3.4 --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index bb913f8..3b99e88 100644 --- a/build.sbt +++ b/build.sbt @@ -4,7 +4,7 @@ val http4sVersion = "0.23.27" val natchezVersion = "0.3.6" val scala212Version = "2.12.19" val scala213Version = "2.13.14" -val scala3Version = "3.3.3" +val scala3Version = "3.3.4" val slf4jVersion = "2.0.16" val munitCEVersion = "2.0.0" val scalacheckEffectVersion = "2.0.0-M2"