From 0ba02059d575c78fa483215ac5013fb83d3a926c Mon Sep 17 00:00:00 2001 From: Brian Holt Date: Wed, 18 Sep 2024 13:15:44 -0500 Subject: [PATCH 1/4] Add AwsXRayIdGenerator --- build.sbt | 14 +++++ .../sdk/contrib/aws/AwsXRayIdGenerator.scala | 28 +++++++++ .../contrib/aws/AwsXRayIdGeneratorSuite.scala | 57 +++++++++++++++++++ 3 files changed, 99 insertions(+) create mode 100644 sdk-contrib/aws/xray/src/main/scala/org/typelevel/otel4s/sdk/contrib/aws/AwsXRayIdGenerator.scala create mode 100644 sdk-contrib/aws/xray/src/test/scala/org/typelevel/otel4s/sdk/contrib/aws/AwsXRayIdGeneratorSuite.scala diff --git a/build.sbt b/build.sbt index f37448aa1..8b632d7f5 100644 --- a/build.sbt +++ b/build.sbt @@ -106,6 +106,7 @@ lazy val root = tlCrossRootProject `sdk-exporter-metrics`, `sdk-exporter-trace`, `sdk-exporter`, + `sdk-contrib-aws-xray`, `sdk-contrib-aws-resource`, `sdk-contrib-aws-xray-propagator`, `oteljava-common`, @@ -476,6 +477,19 @@ lazy val `sdk-contrib-aws-xray-propagator` = .settings(scalafixSettings) .jsSettings(scalaJSLinkerSettings) +lazy val `sdk-contrib-aws-xray` = + crossProject(JVMPlatform, JSPlatform, NativePlatform) + .crossType(CrossType.Pure) + .in(file("sdk-contrib/aws/xray")) + .dependsOn(`sdk-trace` % "compile->compile;test->test") + .settings( + name := "otel4s-sdk-contrib-aws-xray", + startYear := Some(2024), + ) + .settings(munitDependencies) + .settings(scalafixSettings) + .jsSettings(scalaJSLinkerSettings) + // // OpenTelemetry Java // diff --git a/sdk-contrib/aws/xray/src/main/scala/org/typelevel/otel4s/sdk/contrib/aws/AwsXRayIdGenerator.scala b/sdk-contrib/aws/xray/src/main/scala/org/typelevel/otel4s/sdk/contrib/aws/AwsXRayIdGenerator.scala new file mode 100644 index 000000000..aafc34c48 --- /dev/null +++ b/sdk-contrib/aws/xray/src/main/scala/org/typelevel/otel4s/sdk/contrib/aws/AwsXRayIdGenerator.scala @@ -0,0 +1,28 @@ +package org.typelevel.otel4s.sdk.contrib.aws + +import cats.* +import cats.effect.* +import cats.effect.std.* +import cats.syntax.all.* +import org.typelevel.otel4s.sdk.trace.IdGenerator +import org.typelevel.otel4s.trace.SpanContext.{SpanId, TraceId} +import scodec.bits.ByteVector + +object AwsXRayIdGenerator { + def apply[F[_] : Applicative : Clock : Random]: AwsXRayIdGenerator[F] = new AwsXRayIdGenerator +} + +class AwsXRayIdGenerator[F[_] : Applicative : Clock : Random] extends IdGenerator[F] { + override def generateSpanId: F[ByteVector] = + Random[F] + .nextLong + .map(SpanId.fromLong) + + override def generateTraceId: F[ByteVector] = + (Clock[F].realTime.map(_.toSeconds), + Random[F].nextInt.map(_ & 0xFFFFFFFFL), + Random[F].nextLong + ).mapN { case (timestampSecs, hiRandom, lowRandom) => + TraceId.fromLongs(timestampSecs << 32 | hiRandom, lowRandom) + } +} diff --git a/sdk-contrib/aws/xray/src/test/scala/org/typelevel/otel4s/sdk/contrib/aws/AwsXRayIdGeneratorSuite.scala b/sdk-contrib/aws/xray/src/test/scala/org/typelevel/otel4s/sdk/contrib/aws/AwsXRayIdGeneratorSuite.scala new file mode 100644 index 000000000..bd8f816a3 --- /dev/null +++ b/sdk-contrib/aws/xray/src/test/scala/org/typelevel/otel4s/sdk/contrib/aws/AwsXRayIdGeneratorSuite.scala @@ -0,0 +1,57 @@ +package org.typelevel.otel4s.sdk.contrib.aws + +import cats.effect.* +import cats.effect.std.* +import cats.effect.testkit.TestControl +import munit.{CatsEffectSuite, ScalaCheckEffectSuite} +import org.scalacheck.Gen +import org.scalacheck.effect.PropF +import org.typelevel.otel4s.trace.SpanContext.{SpanId, TraceId} + +import scala.concurrent.duration.FiniteDuration + +class AwsXRayIdGeneratorSuite extends CatsEffectSuite with ScalaCheckEffectSuite { + + // TODO generate ids over time + test("Trace IDs should be a combination of the current timestamp with random numbers") { + PropF.forAllNoShrinkF { (seed: Long, + now: FiniteDuration) => + Random.scalaUtilRandomSeedLong[IO](seed).flatMap { controlRandom => + Random.scalaUtilRandomSeedLong[IO](seed).flatMap { implicit testRandom => + val expected = for { + hiRandom <- controlRandom.nextInt.map(_ & 0xFFFFFFFFL) + lowRandom <- controlRandom.nextLong + } yield TraceId.fromLongs(now.toSeconds << 32 | hiRandom, lowRandom) + + TestControl + .execute { + for { + _ <- IO.sleep(now) // advance time until the arbitrary time given to us by ScalaCheck + expected <- expected + output <- AwsXRayIdGenerator[IO].generateTraceId + } yield { + assertEquals(output, expected) + } + } + .flatMap(_.tickFor(now)) + } + } + } + } + + test("Span IDs should be randomly generated") { + PropF.forAllNoShrinkF(Gen.long, Gen.posNum[Int]) { (seed: Long, count: Int) => + Random.scalaUtilRandomSeedLong[IO](seed).flatMap { controlRandom => + Random.scalaUtilRandomSeedLong[IO](seed).flatMap { implicit testRandom => + for { + expected <- controlRandom.nextLong.map(SpanId.fromLong).replicateA(count) + output <- AwsXRayIdGenerator[IO].generateSpanId.replicateA(count) + } yield { + assertEquals(output, expected) + } + } + } + } + } + +} From 95736310b5a791ac7fb8bf1bc9a139afa6e15ec3 Mon Sep 17 00:00:00 2001 From: Brian Holt Date: Wed, 18 Sep 2024 14:32:20 -0500 Subject: [PATCH 2/4] test multiple AwsXRayIdGenerator generations over time --- .../contrib/aws/AwsXRayIdGeneratorSuite.scala | 45 ++++++++++++------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/sdk-contrib/aws/xray/src/test/scala/org/typelevel/otel4s/sdk/contrib/aws/AwsXRayIdGeneratorSuite.scala b/sdk-contrib/aws/xray/src/test/scala/org/typelevel/otel4s/sdk/contrib/aws/AwsXRayIdGeneratorSuite.scala index bd8f816a3..91804d048 100644 --- a/sdk-contrib/aws/xray/src/test/scala/org/typelevel/otel4s/sdk/contrib/aws/AwsXRayIdGeneratorSuite.scala +++ b/sdk-contrib/aws/xray/src/test/scala/org/typelevel/otel4s/sdk/contrib/aws/AwsXRayIdGeneratorSuite.scala @@ -1,50 +1,63 @@ package org.typelevel.otel4s.sdk.contrib.aws +import cats.syntax.all.* import cats.effect.* import cats.effect.std.* import cats.effect.testkit.TestControl import munit.{CatsEffectSuite, ScalaCheckEffectSuite} -import org.scalacheck.Gen +import org.scalacheck.* import org.scalacheck.effect.PropF import org.typelevel.otel4s.trace.SpanContext.{SpanId, TraceId} +import scodec.bits.ByteVector -import scala.concurrent.duration.FiniteDuration +import scala.concurrent.duration.* class AwsXRayIdGeneratorSuite extends CatsEffectSuite with ScalaCheckEffectSuite { - // TODO generate ids over time + /** + * Generate an arbitrary list of delays representing the times between pairs of + * calls to {{{AwsXRayIdGenerator[IO].generateTraceId}}} + */ + private val genTimeline: Gen[List[FiniteDuration]] = + Gen.posNum[Int] + .flatMap(Gen.listOfN(_, Gen.choose(0.seconds, 1.second))) + test("Trace IDs should be a combination of the current timestamp with random numbers") { - PropF.forAllNoShrinkF { (seed: Long, - now: FiniteDuration) => + PropF.forAllNoShrinkF(Gen.long, genTimeline) { (seed: Long, + timeline: List[FiniteDuration]) => Random.scalaUtilRandomSeedLong[IO](seed).flatMap { controlRandom => Random.scalaUtilRandomSeedLong[IO](seed).flatMap { implicit testRandom => - val expected = for { - hiRandom <- controlRandom.nextInt.map(_ & 0xFFFFFFFFL) - lowRandom <- controlRandom.nextLong - } yield TraceId.fromLongs(now.toSeconds << 32 | hiRandom, lowRandom) + val expecteds: IO[List[ByteVector]] = + timeline + .scanLeft(0.millis)(_ + _) // convert delays to absolute timestamps + .traverse { now => + for { + hiRandom <- controlRandom.nextInt.map(_ & 0xFFFFFFFFL) + lowRandom <- controlRandom.nextLong.iterateUntil(_ != 0L) + } yield TraceId.fromLongs(now.toSeconds << 32 | hiRandom, lowRandom) + } TestControl .execute { for { - _ <- IO.sleep(now) // advance time until the arbitrary time given to us by ScalaCheck - expected <- expected - output <- AwsXRayIdGenerator[IO].generateTraceId + expecteds <- expecteds + output <- timeline.traverse(AwsXRayIdGenerator[IO].generateTraceId.delayBy(_)) } yield { - assertEquals(output, expected) + assertEquals(output, expecteds) } } - .flatMap(_.tickFor(now)) + .flatMap(_.tickAll) } } } } - test("Span IDs should be randomly generated") { + test("Span IDs should be randomly generated but not equal 0") { PropF.forAllNoShrinkF(Gen.long, Gen.posNum[Int]) { (seed: Long, count: Int) => Random.scalaUtilRandomSeedLong[IO](seed).flatMap { controlRandom => Random.scalaUtilRandomSeedLong[IO](seed).flatMap { implicit testRandom => for { - expected <- controlRandom.nextLong.map(SpanId.fromLong).replicateA(count) + expected <- controlRandom.nextLong.iterateUntil(_ != 0L).map(SpanId.fromLong).replicateA(count) output <- AwsXRayIdGenerator[IO].generateSpanId.replicateA(count) } yield { assertEquals(output, expected) From 589c8366ff6bc613d654941e8b89f296a415bd8c Mon Sep 17 00:00:00 2001 From: Brian Holt Date: Wed, 18 Sep 2024 14:50:38 -0500 Subject: [PATCH 3/4] set AwsXRayIdGenerator.canSkipIdValidation to true --- .../contrib/aws/AwsXRayIdGenerator.scala | 9 +- .../contrib/aws/AwsXRayIdGeneratorSuite.scala | 70 ---------- .../contrib/aws/AwsXRayIdGeneratorSuite.scala | 123 ++++++++++++++++++ 3 files changed, 129 insertions(+), 73 deletions(-) rename sdk-contrib/aws/xray/src/main/scala/org/typelevel/otel4s/sdk/{ => trace}/contrib/aws/AwsXRayIdGenerator.scala (67%) delete mode 100644 sdk-contrib/aws/xray/src/test/scala/org/typelevel/otel4s/sdk/contrib/aws/AwsXRayIdGeneratorSuite.scala create mode 100644 sdk-contrib/aws/xray/src/test/scala/org/typelevel/otel4s/sdk/trace/contrib/aws/AwsXRayIdGeneratorSuite.scala diff --git a/sdk-contrib/aws/xray/src/main/scala/org/typelevel/otel4s/sdk/contrib/aws/AwsXRayIdGenerator.scala b/sdk-contrib/aws/xray/src/main/scala/org/typelevel/otel4s/sdk/trace/contrib/aws/AwsXRayIdGenerator.scala similarity index 67% rename from sdk-contrib/aws/xray/src/main/scala/org/typelevel/otel4s/sdk/contrib/aws/AwsXRayIdGenerator.scala rename to sdk-contrib/aws/xray/src/main/scala/org/typelevel/otel4s/sdk/trace/contrib/aws/AwsXRayIdGenerator.scala index aafc34c48..dd33b7722 100644 --- a/sdk-contrib/aws/xray/src/main/scala/org/typelevel/otel4s/sdk/contrib/aws/AwsXRayIdGenerator.scala +++ b/sdk-contrib/aws/xray/src/main/scala/org/typelevel/otel4s/sdk/trace/contrib/aws/AwsXRayIdGenerator.scala @@ -1,4 +1,4 @@ -package org.typelevel.otel4s.sdk.contrib.aws +package org.typelevel.otel4s.sdk.trace.contrib.aws import cats.* import cats.effect.* @@ -9,13 +9,14 @@ import org.typelevel.otel4s.trace.SpanContext.{SpanId, TraceId} import scodec.bits.ByteVector object AwsXRayIdGenerator { - def apply[F[_] : Applicative : Clock : Random]: AwsXRayIdGenerator[F] = new AwsXRayIdGenerator + def apply[F[_] : Monad : Clock : Random]: AwsXRayIdGenerator[F] = new AwsXRayIdGenerator } -class AwsXRayIdGenerator[F[_] : Applicative : Clock : Random] extends IdGenerator[F] { +class AwsXRayIdGenerator[F[_] : Monad : Clock : Random] extends IdGenerator[F] { override def generateSpanId: F[ByteVector] = Random[F] .nextLong + .iterateUntil(_ != 0L) .map(SpanId.fromLong) override def generateTraceId: F[ByteVector] = @@ -25,4 +26,6 @@ class AwsXRayIdGenerator[F[_] : Applicative : Clock : Random] extends IdGenerato ).mapN { case (timestampSecs, hiRandom, lowRandom) => TraceId.fromLongs(timestampSecs << 32 | hiRandom, lowRandom) } + + override private[trace] val canSkipIdValidation: Boolean = true } diff --git a/sdk-contrib/aws/xray/src/test/scala/org/typelevel/otel4s/sdk/contrib/aws/AwsXRayIdGeneratorSuite.scala b/sdk-contrib/aws/xray/src/test/scala/org/typelevel/otel4s/sdk/contrib/aws/AwsXRayIdGeneratorSuite.scala deleted file mode 100644 index 91804d048..000000000 --- a/sdk-contrib/aws/xray/src/test/scala/org/typelevel/otel4s/sdk/contrib/aws/AwsXRayIdGeneratorSuite.scala +++ /dev/null @@ -1,70 +0,0 @@ -package org.typelevel.otel4s.sdk.contrib.aws - -import cats.syntax.all.* -import cats.effect.* -import cats.effect.std.* -import cats.effect.testkit.TestControl -import munit.{CatsEffectSuite, ScalaCheckEffectSuite} -import org.scalacheck.* -import org.scalacheck.effect.PropF -import org.typelevel.otel4s.trace.SpanContext.{SpanId, TraceId} -import scodec.bits.ByteVector - -import scala.concurrent.duration.* - -class AwsXRayIdGeneratorSuite extends CatsEffectSuite with ScalaCheckEffectSuite { - - /** - * Generate an arbitrary list of delays representing the times between pairs of - * calls to {{{AwsXRayIdGenerator[IO].generateTraceId}}} - */ - private val genTimeline: Gen[List[FiniteDuration]] = - Gen.posNum[Int] - .flatMap(Gen.listOfN(_, Gen.choose(0.seconds, 1.second))) - - test("Trace IDs should be a combination of the current timestamp with random numbers") { - PropF.forAllNoShrinkF(Gen.long, genTimeline) { (seed: Long, - timeline: List[FiniteDuration]) => - Random.scalaUtilRandomSeedLong[IO](seed).flatMap { controlRandom => - Random.scalaUtilRandomSeedLong[IO](seed).flatMap { implicit testRandom => - val expecteds: IO[List[ByteVector]] = - timeline - .scanLeft(0.millis)(_ + _) // convert delays to absolute timestamps - .traverse { now => - for { - hiRandom <- controlRandom.nextInt.map(_ & 0xFFFFFFFFL) - lowRandom <- controlRandom.nextLong.iterateUntil(_ != 0L) - } yield TraceId.fromLongs(now.toSeconds << 32 | hiRandom, lowRandom) - } - - TestControl - .execute { - for { - expecteds <- expecteds - output <- timeline.traverse(AwsXRayIdGenerator[IO].generateTraceId.delayBy(_)) - } yield { - assertEquals(output, expecteds) - } - } - .flatMap(_.tickAll) - } - } - } - } - - test("Span IDs should be randomly generated but not equal 0") { - PropF.forAllNoShrinkF(Gen.long, Gen.posNum[Int]) { (seed: Long, count: Int) => - Random.scalaUtilRandomSeedLong[IO](seed).flatMap { controlRandom => - Random.scalaUtilRandomSeedLong[IO](seed).flatMap { implicit testRandom => - for { - expected <- controlRandom.nextLong.iterateUntil(_ != 0L).map(SpanId.fromLong).replicateA(count) - output <- AwsXRayIdGenerator[IO].generateSpanId.replicateA(count) - } yield { - assertEquals(output, expected) - } - } - } - } - } - -} diff --git a/sdk-contrib/aws/xray/src/test/scala/org/typelevel/otel4s/sdk/trace/contrib/aws/AwsXRayIdGeneratorSuite.scala b/sdk-contrib/aws/xray/src/test/scala/org/typelevel/otel4s/sdk/trace/contrib/aws/AwsXRayIdGeneratorSuite.scala new file mode 100644 index 000000000..fa102c0bb --- /dev/null +++ b/sdk-contrib/aws/xray/src/test/scala/org/typelevel/otel4s/sdk/trace/contrib/aws/AwsXRayIdGeneratorSuite.scala @@ -0,0 +1,123 @@ +package org.typelevel.otel4s.sdk.trace.contrib.aws + +import cats.MonadThrow +import cats.effect.* +import cats.effect.std.* +import cats.effect.testkit.TestControl +import cats.syntax.all.* +import munit.{CatsEffectSuite, ScalaCheckEffectSuite} +import org.scalacheck.* +import org.scalacheck.effect.PropF +import org.typelevel.otel4s.trace.SpanContext.{SpanId, TraceId} +import scodec.bits.ByteVector + +import scala.concurrent.duration.* + +class AwsXRayIdGeneratorSuite + extends CatsEffectSuite + with ScalaCheckEffectSuite { + + /** + * Generate an arbitrary list of delays representing the times between pairs of + * calls to {{{AwsXRayIdGenerator[IO].generateTraceId}}} + */ + private val genTimeline: Gen[List[FiniteDuration]] = + Gen.posNum[Int] + .flatMap(Gen.listOfN(_, Gen.choose(0.seconds, 1.second))) + + test("Trace IDs should be a combination of the current timestamp with random numbers") { + PropF.forAllNoShrinkF(Gen.long, genTimeline) { (seed: Long, + timeline: List[FiniteDuration]) => + IntermittentlyZeroRandom[IO](seed).flatMap { controlRandom => + IntermittentlyZeroRandom[IO](seed).flatMap { implicit testRandom => + val expecteds: IO[List[ByteVector]] = + timeline + .scanLeft(0.millis)(_ + _) // convert delays to absolute timestamps + .traverse { now => + for { + hiRandom <- controlRandom.nextInt.map(_ & 0xFFFFFFFFL) + lowRandom <- controlRandom.nextLong + } yield TraceId.fromLongs(now.toSeconds << 32 | hiRandom, lowRandom) + } + + TestControl + .execute { + for { + expecteds <- expecteds + output <- timeline.traverse(AwsXRayIdGenerator[IO].generateTraceId.delayBy(_)) + } yield { + assertEquals(output, expecteds) + assert(output.forall(TraceId.isValid)) + } + } + .flatMap(_.tickAll) + } + } + } + } + + test("Span IDs should be randomly generated but not equal 0") { + PropF.forAllNoShrinkF(Gen.long, Gen.posNum[Int]) { (seed: Long, count: Int) => + IntermittentlyZeroRandom[IO](seed).flatMap { controlRandom => + IntermittentlyZeroRandom[IO](seed).flatMap { implicit testRandom => + for { + expected <- controlRandom.nextLong.iterateUntil(_ != 0L).map(SpanId.fromLong).replicateA(count) + output <- AwsXRayIdGenerator[IO].generateSpanId.replicateA(count) + } yield { + assertEquals(output, expected) + assert(output.forall(SpanId.isValid)) + } + } + } + } + } + + test("canSkipIdValidation is true") { + PropF.forAllNoShrinkF(Gen.long) { + Random.scalaUtilRandomSeedLong[IO](_).flatMap { implicit testRandom => + IO { + assert(AwsXRayIdGenerator[IO].canSkipIdValidation) + } + } + } + } + +} + +object IntermittentlyZeroRandom { + def apply[F[_] : Sync](seed: Long): F[Random[F]] = + Random.scalaUtilRandomSeedLong[F](seed).map(new IntermittentlyZeroRandom[F](_)) +} + +class IntermittentlyZeroRandom[F[_] : MonadThrow](actualRandom: Random[F]) extends UnimplementedRandom[F] { + override def nextDouble: F[Double] = actualRandom.nextBoolean.ifM(0D.pure[F], actualRandom.nextDouble) + override def nextFloat: F[Float] = actualRandom.nextBoolean.ifM(0F.pure[F], actualRandom.nextFloat) + override def nextGaussian: F[Double] = actualRandom.nextBoolean.ifM(0D.pure[F], actualRandom.nextGaussian) + override def nextInt: F[Int] = actualRandom.nextBoolean.ifM(0.pure[F], actualRandom.nextInt) + override def nextIntBounded(n: Int): F[Int] = actualRandom.nextBoolean.ifM(0.pure[F], actualRandom.nextIntBounded(n)) + override def nextLong: F[Long] = actualRandom.nextBoolean.ifM(0L.pure[F], actualRandom.nextLong) + override def nextLongBounded(n: Long): F[Long] = actualRandom.nextBoolean.ifM(0L.pure[F], actualRandom.nextLongBounded(n)) +} + +class UnimplementedRandom[F[_] : MonadThrow] extends Random[F] { + private def notImplemented[A]: F[A] = (new NotImplementedError).raiseError[F, A] + + override def betweenDouble(minInclusive: Double, maxExclusive: Double): F[Double] = notImplemented + override def betweenFloat(minInclusive: Float, maxExclusive: Float): F[Float] = notImplemented + override def betweenInt(minInclusive: Int, maxExclusive: Int): F[Int] = notImplemented + override def betweenLong(minInclusive: Long, maxExclusive: Long): F[Long] = notImplemented + override def nextAlphaNumeric: F[Char] = notImplemented + override def nextBoolean: F[Boolean] = notImplemented + override def nextBytes(n: Int): F[Array[Byte]] = notImplemented + override def nextDouble: F[Double] = notImplemented + override def nextFloat: F[Float] = notImplemented + override def nextGaussian: F[Double] = notImplemented + override def nextInt: F[Int] = notImplemented + override def nextIntBounded(n: Int): F[Int] = notImplemented + override def nextLong: F[Long] = notImplemented + override def nextLongBounded(n: Long): F[Long] = notImplemented + override def nextPrintableChar: F[Char] = notImplemented + override def nextString(length: Int): F[String] = notImplemented + override def shuffleList[A](l: List[A]): F[List[A]] = notImplemented + override def shuffleVector[A](v: Vector[A]): F[Vector[A]] = notImplemented +} From ae4ae7a03fc5dffe33fc4e20cbfb67fcc5fe8f00 Mon Sep 17 00:00:00 2001 From: Brian Holt Date: Wed, 18 Sep 2024 14:51:49 -0500 Subject: [PATCH 4/4] run scalafmtAll, scalafixAll, headerCreate, and githubWorkflowGenerate --- .github/workflows/ci.yml | 4 +- .../contrib/aws/AwsXRayIdGenerator.scala | 46 +++++++++---- .../contrib/aws/AwsXRayIdGeneratorSuite.scala | 65 ++++++++++++------- 3 files changed, 74 insertions(+), 41 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e99c93fec..04720aebc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -92,11 +92,11 @@ jobs: - name: Make target directories if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main') - run: mkdir -p semconv/stable/.jvm/target oteljava/metrics/target sdk-exporter/common/.js/target sdk/common/native/target sdk/common/js/target core/trace/.js/target semconv/metrics/stable/.jvm/target semconv/metrics/experimental/.jvm/target semconv/metrics/stable/.native/target sdk-exporter/all/.jvm/target semconv/experimental/.js/target sdk/trace/.js/target core/common/.jvm/target sdk-exporter/common/.native/target oteljava/common-testkit/target sdk/metrics/.native/target sdk-exporter/metrics/.jvm/target sdk-exporter/trace/.jvm/target unidocs/target sdk-exporter/metrics/.native/target sdk-contrib/aws/resource/.jvm/target oteljava/trace-testkit/target core/metrics/.native/target core/all/.native/target sdk/trace-testkit/.jvm/target sdk/trace-testkit/.native/target sdk/testkit/.native/target sdk-contrib/aws/resource/.js/target semconv/experimental/.native/target core/metrics/.jvm/target core/all/.js/target sdk-exporter/proto/.jvm/target sdk-exporter/proto/.js/target sdk-exporter/metrics/.js/target semconv/stable/.native/target sdk/all/.native/target sdk/metrics-testkit/.js/target sdk-contrib/aws/xray-propagator/.native/target core/metrics/.js/target sdk/testkit/.js/target core/all/.jvm/target sdk-exporter/trace/.native/target sdk/common/jvm/target core/trace/.native/target oteljava/metrics-testkit/target sdk/trace/.native/target semconv/experimental/.jvm/target sdk/metrics-testkit/.native/target sdk/metrics/.jvm/target oteljava/common/target scalafix/rules/target sdk-exporter/proto/.native/target core/trace/.jvm/target sdk-exporter/common/.jvm/target sdk/metrics-testkit/.jvm/target sdk-contrib/aws/resource/.native/target sdk/metrics/.js/target sdk-exporter/trace/.js/target core/common/.native/target sdk/trace-testkit/.js/target core/common/.js/target oteljava/trace/target semconv/metrics/experimental/.native/target oteljava/testkit/target sdk/testkit/.jvm/target sdk-exporter/all/.js/target sdk-contrib/aws/xray-propagator/.js/target semconv/metrics/experimental/.js/target semconv/metrics/stable/.js/target sdk/all/.js/target sdk/all/.jvm/target sdk-exporter/all/.native/target oteljava/all/target sdk/trace/.jvm/target sdk-contrib/aws/xray-propagator/.jvm/target semconv/stable/.js/target project/target + run: mkdir -p semconv/stable/.jvm/target oteljava/metrics/target sdk-exporter/common/.js/target sdk/common/native/target sdk/common/js/target core/trace/.js/target semconv/metrics/stable/.jvm/target semconv/metrics/experimental/.jvm/target semconv/metrics/stable/.native/target sdk-exporter/all/.jvm/target semconv/experimental/.js/target sdk/trace/.js/target core/common/.jvm/target sdk-exporter/common/.native/target oteljava/common-testkit/target sdk/metrics/.native/target sdk-exporter/metrics/.jvm/target sdk-exporter/trace/.jvm/target unidocs/target sdk-exporter/metrics/.native/target sdk-contrib/aws/resource/.jvm/target oteljava/trace-testkit/target core/metrics/.native/target core/all/.native/target sdk/trace-testkit/.jvm/target sdk/trace-testkit/.native/target sdk/testkit/.native/target sdk-contrib/aws/resource/.js/target semconv/experimental/.native/target core/metrics/.jvm/target core/all/.js/target sdk-exporter/proto/.jvm/target sdk-exporter/proto/.js/target sdk-exporter/metrics/.js/target semconv/stable/.native/target sdk/all/.native/target sdk/metrics-testkit/.js/target sdk-contrib/aws/xray-propagator/.native/target core/metrics/.js/target sdk/testkit/.js/target core/all/.jvm/target sdk-exporter/trace/.native/target sdk/common/jvm/target core/trace/.native/target oteljava/metrics-testkit/target sdk/trace/.native/target semconv/experimental/.jvm/target sdk/metrics-testkit/.native/target sdk/metrics/.jvm/target oteljava/common/target scalafix/rules/target sdk-exporter/proto/.native/target core/trace/.jvm/target sdk-exporter/common/.jvm/target sdk/metrics-testkit/.jvm/target sdk-contrib/aws/resource/.native/target sdk/metrics/.js/target sdk-exporter/trace/.js/target core/common/.native/target sdk/trace-testkit/.js/target core/common/.js/target oteljava/trace/target semconv/metrics/experimental/.native/target oteljava/testkit/target sdk/testkit/.jvm/target sdk-exporter/all/.js/target sdk-contrib/aws/xray/.native/target sdk-contrib/aws/xray/.js/target sdk-contrib/aws/xray-propagator/.js/target semconv/metrics/experimental/.js/target semconv/metrics/stable/.js/target sdk/all/.js/target sdk/all/.jvm/target sdk-exporter/all/.native/target oteljava/all/target sdk/trace/.jvm/target sdk-contrib/aws/xray-propagator/.jvm/target semconv/stable/.js/target sdk-contrib/aws/xray/.jvm/target project/target - name: Compress target directories if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main') - run: tar cf targets.tar semconv/stable/.jvm/target oteljava/metrics/target sdk-exporter/common/.js/target sdk/common/native/target sdk/common/js/target core/trace/.js/target semconv/metrics/stable/.jvm/target semconv/metrics/experimental/.jvm/target semconv/metrics/stable/.native/target sdk-exporter/all/.jvm/target semconv/experimental/.js/target sdk/trace/.js/target core/common/.jvm/target sdk-exporter/common/.native/target oteljava/common-testkit/target sdk/metrics/.native/target sdk-exporter/metrics/.jvm/target sdk-exporter/trace/.jvm/target unidocs/target sdk-exporter/metrics/.native/target sdk-contrib/aws/resource/.jvm/target oteljava/trace-testkit/target core/metrics/.native/target core/all/.native/target sdk/trace-testkit/.jvm/target sdk/trace-testkit/.native/target sdk/testkit/.native/target sdk-contrib/aws/resource/.js/target semconv/experimental/.native/target core/metrics/.jvm/target core/all/.js/target sdk-exporter/proto/.jvm/target sdk-exporter/proto/.js/target sdk-exporter/metrics/.js/target semconv/stable/.native/target sdk/all/.native/target sdk/metrics-testkit/.js/target sdk-contrib/aws/xray-propagator/.native/target core/metrics/.js/target sdk/testkit/.js/target core/all/.jvm/target sdk-exporter/trace/.native/target sdk/common/jvm/target core/trace/.native/target oteljava/metrics-testkit/target sdk/trace/.native/target semconv/experimental/.jvm/target sdk/metrics-testkit/.native/target sdk/metrics/.jvm/target oteljava/common/target scalafix/rules/target sdk-exporter/proto/.native/target core/trace/.jvm/target sdk-exporter/common/.jvm/target sdk/metrics-testkit/.jvm/target sdk-contrib/aws/resource/.native/target sdk/metrics/.js/target sdk-exporter/trace/.js/target core/common/.native/target sdk/trace-testkit/.js/target core/common/.js/target oteljava/trace/target semconv/metrics/experimental/.native/target oteljava/testkit/target sdk/testkit/.jvm/target sdk-exporter/all/.js/target sdk-contrib/aws/xray-propagator/.js/target semconv/metrics/experimental/.js/target semconv/metrics/stable/.js/target sdk/all/.js/target sdk/all/.jvm/target sdk-exporter/all/.native/target oteljava/all/target sdk/trace/.jvm/target sdk-contrib/aws/xray-propagator/.jvm/target semconv/stable/.js/target project/target + run: tar cf targets.tar semconv/stable/.jvm/target oteljava/metrics/target sdk-exporter/common/.js/target sdk/common/native/target sdk/common/js/target core/trace/.js/target semconv/metrics/stable/.jvm/target semconv/metrics/experimental/.jvm/target semconv/metrics/stable/.native/target sdk-exporter/all/.jvm/target semconv/experimental/.js/target sdk/trace/.js/target core/common/.jvm/target sdk-exporter/common/.native/target oteljava/common-testkit/target sdk/metrics/.native/target sdk-exporter/metrics/.jvm/target sdk-exporter/trace/.jvm/target unidocs/target sdk-exporter/metrics/.native/target sdk-contrib/aws/resource/.jvm/target oteljava/trace-testkit/target core/metrics/.native/target core/all/.native/target sdk/trace-testkit/.jvm/target sdk/trace-testkit/.native/target sdk/testkit/.native/target sdk-contrib/aws/resource/.js/target semconv/experimental/.native/target core/metrics/.jvm/target core/all/.js/target sdk-exporter/proto/.jvm/target sdk-exporter/proto/.js/target sdk-exporter/metrics/.js/target semconv/stable/.native/target sdk/all/.native/target sdk/metrics-testkit/.js/target sdk-contrib/aws/xray-propagator/.native/target core/metrics/.js/target sdk/testkit/.js/target core/all/.jvm/target sdk-exporter/trace/.native/target sdk/common/jvm/target core/trace/.native/target oteljava/metrics-testkit/target sdk/trace/.native/target semconv/experimental/.jvm/target sdk/metrics-testkit/.native/target sdk/metrics/.jvm/target oteljava/common/target scalafix/rules/target sdk-exporter/proto/.native/target core/trace/.jvm/target sdk-exporter/common/.jvm/target sdk/metrics-testkit/.jvm/target sdk-contrib/aws/resource/.native/target sdk/metrics/.js/target sdk-exporter/trace/.js/target core/common/.native/target sdk/trace-testkit/.js/target core/common/.js/target oteljava/trace/target semconv/metrics/experimental/.native/target oteljava/testkit/target sdk/testkit/.jvm/target sdk-exporter/all/.js/target sdk-contrib/aws/xray/.native/target sdk-contrib/aws/xray/.js/target sdk-contrib/aws/xray-propagator/.js/target semconv/metrics/experimental/.js/target semconv/metrics/stable/.js/target sdk/all/.js/target sdk/all/.jvm/target sdk-exporter/all/.native/target oteljava/all/target sdk/trace/.jvm/target sdk-contrib/aws/xray-propagator/.jvm/target semconv/stable/.js/target sdk-contrib/aws/xray/.jvm/target project/target - name: Upload target directories if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main') diff --git a/sdk-contrib/aws/xray/src/main/scala/org/typelevel/otel4s/sdk/trace/contrib/aws/AwsXRayIdGenerator.scala b/sdk-contrib/aws/xray/src/main/scala/org/typelevel/otel4s/sdk/trace/contrib/aws/AwsXRayIdGenerator.scala index dd33b7722..8dc6c77b0 100644 --- a/sdk-contrib/aws/xray/src/main/scala/org/typelevel/otel4s/sdk/trace/contrib/aws/AwsXRayIdGenerator.scala +++ b/sdk-contrib/aws/xray/src/main/scala/org/typelevel/otel4s/sdk/trace/contrib/aws/AwsXRayIdGenerator.scala @@ -1,30 +1,48 @@ +/* + * Copyright 2024 Typelevel + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Based on https://github.com/Dwolla/dwolla-otel-natchez/blob/64c3ffffe4e7c949291c4ebaff205e66c4557d1c/aws-xray-id-generator/src/main/scala/com/dwolla/tracing/AwsXrayIdGenerator.scala, + * which was originally based on https://github.com/open-telemetry/opentelemetry-java-contrib/blob/eece7e8ef04170fb463ddf692f61d4527b50febf/aws-xray/src/main/java/io/opentelemetry/contrib/awsxray/AwsXrayIdGenerator.java + * SPDX-License-Identifier: Apache-2.0 + */ package org.typelevel.otel4s.sdk.trace.contrib.aws -import cats.* -import cats.effect.* -import cats.effect.std.* -import cats.syntax.all.* +import cats._ +import cats.effect._ +import cats.effect.std._ +import cats.syntax.all._ import org.typelevel.otel4s.sdk.trace.IdGenerator -import org.typelevel.otel4s.trace.SpanContext.{SpanId, TraceId} +import org.typelevel.otel4s.trace.SpanContext._ import scodec.bits.ByteVector object AwsXRayIdGenerator { - def apply[F[_] : Monad : Clock : Random]: AwsXRayIdGenerator[F] = new AwsXRayIdGenerator + def apply[F[_]: Monad: Clock: Random]: AwsXRayIdGenerator[F] = new AwsXRayIdGenerator } -class AwsXRayIdGenerator[F[_] : Monad : Clock : Random] extends IdGenerator[F] { +class AwsXRayIdGenerator[F[_]: Monad: Clock: Random] extends IdGenerator[F] { override def generateSpanId: F[ByteVector] = - Random[F] - .nextLong + Random[F].nextLong .iterateUntil(_ != 0L) .map(SpanId.fromLong) override def generateTraceId: F[ByteVector] = - (Clock[F].realTime.map(_.toSeconds), - Random[F].nextInt.map(_ & 0xFFFFFFFFL), - Random[F].nextLong - ).mapN { case (timestampSecs, hiRandom, lowRandom) => - TraceId.fromLongs(timestampSecs << 32 | hiRandom, lowRandom) + (Clock[F].realTime.map(_.toSeconds), Random[F].nextInt.map(_ & 0xffffffffL), Random[F].nextLong).mapN { + case (timestampSecs, hiRandom, lowRandom) => + TraceId.fromLongs(timestampSecs << 32 | hiRandom, lowRandom) } override private[trace] val canSkipIdValidation: Boolean = true diff --git a/sdk-contrib/aws/xray/src/test/scala/org/typelevel/otel4s/sdk/trace/contrib/aws/AwsXRayIdGeneratorSuite.scala b/sdk-contrib/aws/xray/src/test/scala/org/typelevel/otel4s/sdk/trace/contrib/aws/AwsXRayIdGeneratorSuite.scala index fa102c0bb..ed611416c 100644 --- a/sdk-contrib/aws/xray/src/test/scala/org/typelevel/otel4s/sdk/trace/contrib/aws/AwsXRayIdGeneratorSuite.scala +++ b/sdk-contrib/aws/xray/src/test/scala/org/typelevel/otel4s/sdk/trace/contrib/aws/AwsXRayIdGeneratorSuite.scala @@ -1,33 +1,47 @@ +/* + * Copyright 2024 Typelevel + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.typelevel.otel4s.sdk.trace.contrib.aws import cats.MonadThrow -import cats.effect.* -import cats.effect.std.* +import cats.effect._ +import cats.effect.std._ import cats.effect.testkit.TestControl -import cats.syntax.all.* -import munit.{CatsEffectSuite, ScalaCheckEffectSuite} -import org.scalacheck.* +import cats.syntax.all._ +import munit.CatsEffectSuite +import munit.ScalaCheckEffectSuite +import org.scalacheck._ import org.scalacheck.effect.PropF -import org.typelevel.otel4s.trace.SpanContext.{SpanId, TraceId} +import org.typelevel.otel4s.trace.SpanContext._ import scodec.bits.ByteVector -import scala.concurrent.duration.* +import scala.concurrent.duration._ -class AwsXRayIdGeneratorSuite - extends CatsEffectSuite - with ScalaCheckEffectSuite { +class AwsXRayIdGeneratorSuite extends CatsEffectSuite with ScalaCheckEffectSuite { - /** - * Generate an arbitrary list of delays representing the times between pairs of - * calls to {{{AwsXRayIdGenerator[IO].generateTraceId}}} - */ + /** Generate an arbitrary list of delays representing the times between pairs of calls to + * {{{AwsXRayIdGenerator[IO].generateTraceId}}} + */ private val genTimeline: Gen[List[FiniteDuration]] = - Gen.posNum[Int] + Gen + .posNum[Int] .flatMap(Gen.listOfN(_, Gen.choose(0.seconds, 1.second))) test("Trace IDs should be a combination of the current timestamp with random numbers") { - PropF.forAllNoShrinkF(Gen.long, genTimeline) { (seed: Long, - timeline: List[FiniteDuration]) => + PropF.forAllNoShrinkF(Gen.long, genTimeline) { (seed: Long, timeline: List[FiniteDuration]) => IntermittentlyZeroRandom[IO](seed).flatMap { controlRandom => IntermittentlyZeroRandom[IO](seed).flatMap { implicit testRandom => val expecteds: IO[List[ByteVector]] = @@ -35,7 +49,7 @@ class AwsXRayIdGeneratorSuite .scanLeft(0.millis)(_ + _) // convert delays to absolute timestamps .traverse { now => for { - hiRandom <- controlRandom.nextInt.map(_ & 0xFFFFFFFFL) + hiRandom <- controlRandom.nextInt.map(_ & 0xffffffffL) lowRandom <- controlRandom.nextLong } yield TraceId.fromLongs(now.toSeconds << 32 | hiRandom, lowRandom) } @@ -85,21 +99,22 @@ class AwsXRayIdGeneratorSuite } object IntermittentlyZeroRandom { - def apply[F[_] : Sync](seed: Long): F[Random[F]] = + def apply[F[_]: Sync](seed: Long): F[Random[F]] = Random.scalaUtilRandomSeedLong[F](seed).map(new IntermittentlyZeroRandom[F](_)) } -class IntermittentlyZeroRandom[F[_] : MonadThrow](actualRandom: Random[F]) extends UnimplementedRandom[F] { - override def nextDouble: F[Double] = actualRandom.nextBoolean.ifM(0D.pure[F], actualRandom.nextDouble) - override def nextFloat: F[Float] = actualRandom.nextBoolean.ifM(0F.pure[F], actualRandom.nextFloat) - override def nextGaussian: F[Double] = actualRandom.nextBoolean.ifM(0D.pure[F], actualRandom.nextGaussian) +class IntermittentlyZeroRandom[F[_]: MonadThrow](actualRandom: Random[F]) extends UnimplementedRandom[F] { + override def nextDouble: F[Double] = actualRandom.nextBoolean.ifM(0d.pure[F], actualRandom.nextDouble) + override def nextFloat: F[Float] = actualRandom.nextBoolean.ifM(0f.pure[F], actualRandom.nextFloat) + override def nextGaussian: F[Double] = actualRandom.nextBoolean.ifM(0d.pure[F], actualRandom.nextGaussian) override def nextInt: F[Int] = actualRandom.nextBoolean.ifM(0.pure[F], actualRandom.nextInt) override def nextIntBounded(n: Int): F[Int] = actualRandom.nextBoolean.ifM(0.pure[F], actualRandom.nextIntBounded(n)) override def nextLong: F[Long] = actualRandom.nextBoolean.ifM(0L.pure[F], actualRandom.nextLong) - override def nextLongBounded(n: Long): F[Long] = actualRandom.nextBoolean.ifM(0L.pure[F], actualRandom.nextLongBounded(n)) + override def nextLongBounded(n: Long): F[Long] = + actualRandom.nextBoolean.ifM(0L.pure[F], actualRandom.nextLongBounded(n)) } -class UnimplementedRandom[F[_] : MonadThrow] extends Random[F] { +class UnimplementedRandom[F[_]: MonadThrow] extends Random[F] { private def notImplemented[A]: F[A] = (new NotImplementedError).raiseError[F, A] override def betweenDouble(minInclusive: Double, maxExclusive: Double): F[Double] = notImplemented