From 0c736cd12919991edfdac2882187b839308fb8ec Mon Sep 17 00:00:00 2001 From: Brian Holt Date: Fri, 8 Mar 2024 14:47:02 -0600 Subject: [PATCH] inline AwsXrayIdGenerator from io.opentelemetry.contrib:opentelemetry-aws-xray to minimize transitive dependencies --- .github/workflows/ci.yml | 4 +-- .mergify.yml | 8 +++++ .../dwolla/tracing/AwsXrayIdGenerator.scala | 36 +++++++++++++++++++ build.sbt | 21 +++++++++-- .../tracing/OpenTelemetryAtDwolla.scala | 28 +++++++-------- 5 files changed, 78 insertions(+), 19 deletions(-) create mode 100644 aws-xray-id-generator/src/main/scala/com/dwolla/tracing/AwsXrayIdGenerator.scala diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 06570a2..6fc0ea3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -68,11 +68,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 core/target project/target + run: mkdir -p aws-xray-id-generator/target core/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 core/target project/target + run: tar cf targets.tar aws-xray-id-generator/target core/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/.mergify.yml b/.mergify.yml index 99159f7..3233aa1 100644 --- a/.mergify.yml +++ b/.mergify.yml @@ -17,6 +17,14 @@ pull_request_rules: - status-success=Build and Test (ubuntu-latest, 2.12, temurin@17, rootJVM) actions: merge: {} +- name: Label aws-xray-id-generator PRs + conditions: + - files~=^aws-xray-id-generator/ + actions: + label: + add: + - aws-xray-id-generator + remove: [] - name: Label core PRs conditions: - files~=^core/ diff --git a/aws-xray-id-generator/src/main/scala/com/dwolla/tracing/AwsXrayIdGenerator.scala b/aws-xray-id-generator/src/main/scala/com/dwolla/tracing/AwsXrayIdGenerator.scala new file mode 100644 index 0000000..fba3d1e --- /dev/null +++ b/aws-xray-id-generator/src/main/scala/com/dwolla/tracing/AwsXrayIdGenerator.scala @@ -0,0 +1,36 @@ +/* + * Copyright 2024 Dwolla, Inc + * SPDX-License-Identifier: Apache-2.0 + * Based on https://github.com/open-telemetry/opentelemetry-java-contrib/blob/eece7e8ef04170fb463ddf692f61d4527b50febf/aws-xray/src/main/java/io/opentelemetry/contrib/awsxray/AwsXrayIdGenerator.java + */ +package com.dwolla.tracing + +import cats.effect.* +import cats.effect.std.* +import cats.* +import cats.syntax.all.* +import io.opentelemetry.api.trace.{SpanId, TraceId} +import io.opentelemetry.sdk.trace.IdGenerator + +object AwsXrayIdGenerator { + def apply[F[_] : Applicative : Clock : Random](dispatcher: Dispatcher[F]): AwsXrayIdGenerator[F] = + new AwsXrayIdGenerator(dispatcher) +} + +class AwsXrayIdGenerator[F[_] : Applicative : Clock : Random](dispatcher: Dispatcher[F]) extends IdGenerator { + override def generateSpanId(): String = + dispatcher.unsafeRunSync { + Random[F] + .nextLong + .map(SpanId.fromLong) + } + + override def generateTraceId(): String = dispatcher.unsafeRunSync { + (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/build.sbt b/build.sbt index 7aec80b..30db87c 100644 --- a/build.sbt +++ b/build.sbt @@ -1,5 +1,5 @@ // https://typelevel.org/sbt-typelevel/faq.html#what-is-a-base-version-anyway -ThisBuild / tlBaseVersion := "0.2" +ThisBuild / tlBaseVersion := "0.3" ThisBuild / organization := "com.dwolla" ThisBuild / organizationName := "Dwolla" @@ -24,7 +24,10 @@ ThisBuild / mergifyStewardConfig ~= { _.map(_.copy( mergeMinors = true, ))} -lazy val root = tlCrossRootProject.aggregate(core) +lazy val root = tlCrossRootProject.aggregate( + core, + `aws-xray-id-generator`, +) lazy val core = project.in(file("core")) .settings( @@ -51,6 +54,18 @@ lazy val core = project.in(file("core")) "io.opentelemetry.semconv" % "opentelemetry-semconv" % "1.23.1-alpha", "io.opentelemetry.contrib" % "opentelemetry-aws-resources" % "1.32.0-alpha", "io.opentelemetry.contrib" % "opentelemetry-aws-xray-propagator" % "1.32.0-alpha", - "io.opentelemetry.contrib" % "opentelemetry-aws-xray" % "1.32.0", + ) + ) + .dependsOn(`aws-xray-id-generator`) + +lazy val `aws-xray-id-generator` = project + .in(file("aws-xray-id-generator")) + .settings( + name := "otel-aws-xray-id-generator", + description := "Generate OTel trace IDs compatible with AWS X-Ray with minimal dependencies", + libraryDependencies ++= Seq( + "org.typelevel" %% "cats-effect" % "3.5.2", + "io.opentelemetry" % "opentelemetry-api" % "1.33.0", + "io.opentelemetry" % "opentelemetry-sdk-trace" % "1.33.0", ) ) diff --git a/core/src/main/scala/com/dwolla/tracing/OpenTelemetryAtDwolla.scala b/core/src/main/scala/com/dwolla/tracing/OpenTelemetryAtDwolla.scala index 47a2f05..889b8cc 100644 --- a/core/src/main/scala/com/dwolla/tracing/OpenTelemetryAtDwolla.scala +++ b/core/src/main/scala/com/dwolla/tracing/OpenTelemetryAtDwolla.scala @@ -8,7 +8,6 @@ import io.opentelemetry.api.common.Attributes import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator import io.opentelemetry.context.propagation.{ContextPropagators, TextMapPropagator} import io.opentelemetry.contrib.aws.resource.{Ec2Resource, EcsResource} -import io.opentelemetry.contrib.awsxray.AwsXrayIdGenerator import io.opentelemetry.contrib.awsxray.propagator.AwsXrayPropagator import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter import io.opentelemetry.extension.trace.propagation.B3Propagator @@ -21,30 +20,31 @@ import natchez.opentelemetry.OpenTelemetry import org.typelevel.log4cats.LoggerFactory object OpenTelemetryAtDwolla { - def apply[F[_] : Sync : Env](serviceName: String, - env: DwollaEnvironment): Resource[F, EntryPoint[F]] = - buildOtel(serviceName, env, None) - - def apply[F[_] : Async : Env : LoggerFactory](serviceName: String, - env: DwollaEnvironment, - logTraces: Boolean): Resource[F, EntryPoint[F]] = + def apply[F[_] : Async : Env : LoggerFactory : Random](serviceName: String, + env: DwollaEnvironment, + logTraces: Boolean, + dispatcher: Dispatcher[F]): Resource[F, EntryPoint[F]] = logTraces .guard[Option] .traverse { _ => LoggerFactory[F] .create .toResource - .flatMap { implicit logger => - Dispatcher.sequential(true) - .map(new LoggingSpanExporter(_)) - .map(SimpleSpanProcessor.create) + .map { implicit logger => + SimpleSpanProcessor.create(new LoggingSpanExporter(dispatcher)) } } - .flatMap(buildOtel(serviceName, env, _)) + .flatMap(buildOtel(serviceName, env, _, AwsXrayIdGenerator(dispatcher))) + + def apply[F[_] : Async : Env : LoggerFactory : Random](serviceName: String, + env: DwollaEnvironment, + logTraces: Boolean): Resource[F, EntryPoint[F]] = + Dispatcher.sequential(true).flatMap(OpenTelemetryAtDwolla(serviceName, env, logTraces, _)) private def buildOtel[F[_] : Sync : Env](serviceName: String, env: DwollaEnvironment, loggingProcessor: Option[SpanProcessor], + awsXrayIdGenerator: AwsXrayIdGenerator[F], ): Resource[F, EntryPoint[F]] = OpenTelemetry.entryPoint(globallyRegister = true) { sdkBuilder => // TODO consider whether to use the OpenTelemetry SDK Autoconfigure module to support all the environment variables https://github.com/open-telemetry/opentelemetry-java/tree/main/sdk-extensions/autoconfigure @@ -86,7 +86,7 @@ object OpenTelemetryAtDwolla { .merge(Ec2Resource.get()) .merge(EcsResource.get()) } - .setIdGenerator(AwsXrayIdGenerator.getInstance()) + .setIdGenerator(awsXrayIdGenerator) }(_ addSpanProcessor _) .build() }