Skip to content

Commit

Permalink
Merge pull request #787 from Dwolla/aws-xray
Browse files Browse the repository at this point in the history
Add AwsXRayIdGenerator
  • Loading branch information
iRevive authored Sep 24, 2024
2 parents ff25785 + ae4ae7a commit 28b19c6
Show file tree
Hide file tree
Showing 4 changed files with 203 additions and 2 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down
14 changes: 14 additions & 0 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -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`,
Expand Down Expand Up @@ -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
//
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* 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 org.typelevel.otel4s.sdk.trace.IdGenerator
import org.typelevel.otel4s.trace.SpanContext._
import scodec.bits.ByteVector

object AwsXRayIdGenerator {
def apply[F[_]: Monad: Clock: Random]: AwsXRayIdGenerator[F] = new AwsXRayIdGenerator
}

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] =
(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
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
/*
* 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.testkit.TestControl
import cats.syntax.all._
import munit.CatsEffectSuite
import munit.ScalaCheckEffectSuite
import org.scalacheck._
import org.scalacheck.effect.PropF
import org.typelevel.otel4s.trace.SpanContext._
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
}

0 comments on commit 28b19c6

Please sign in to comment.