Skip to content

Commit

Permalink
Merge pull request #759 from iRevive/contrib/aws/x-ray-lambda-propagator
Browse files Browse the repository at this point in the history
sdk-contrib: add `AWSXRayLambdaPropagator` module
  • Loading branch information
iRevive authored Sep 9, 2024
2 parents 3429306 + bb31246 commit 65b9c1e
Show file tree
Hide file tree
Showing 4 changed files with 426 additions and 6 deletions.
89 changes: 86 additions & 3 deletions docs/sdk/aws-xray-propagator.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ object TelemetryApp extends IOApp.Simple {
.autoConfigured[IO](
// register OTLP exporters configurer
_.addExportersConfigurer(OtlpExportersAutoConfigure[IO])
// add AWS X-Ray Propagator propagator
// add AWS X-Ray Propagator
.addTracerProviderCustomizer((b, _) =>
b.addTextMapPropagators(AWSXRayPropagator())
)
Expand Down Expand Up @@ -100,7 +100,7 @@ object TelemetryApp extends IOApp.Simple {
.autoConfigured[IO](
// register OTLP exporters configurer
_.addExporterConfigurer(OtlpSpanExporterAutoConfigure[IO])
// add AWS X-Ray Propagator propagator
// add AWS X-Ray Propagator
.addTracerProviderCustomizer((b, _) =>
b.addTextMapPropagators(AWSXRayPropagator())
)
Expand All @@ -118,4 +118,87 @@ object TelemetryApp extends IOApp.Simple {

@:@

[xray-concepts]: https://docs.aws.amazon.com/xray/latest/devguide/xray-concepts.html#xray-concepts-tracingheader
## AWS Lambda

AWS Lambda can [utilize][lambda-xray-envvars] `_X_AMZN_TRACE_ID` environment variable or
`com.amazonaws.xray.traceHeader` system property to set the X-Ray tracing header.

Use `AWSXRayLambdaPropagator` in such a case.

@:select(sdk-entry-point)

@:choice(sdk)

`OpenTelemetrySdk.autoConfigured` configures both `MeterProvider` and `TracerProvider`:

```scala mdoc:silent:reset
import cats.effect.{IO, IOApp}
import org.typelevel.otel4s.metrics.MeterProvider
import org.typelevel.otel4s.sdk.OpenTelemetrySdk
import org.typelevel.otel4s.sdk.contrib.aws.context.propagation._
import org.typelevel.otel4s.sdk.exporter.otlp.autoconfigure.OtlpExportersAutoConfigure
import org.typelevel.otel4s.trace.TracerProvider

object TelemetryApp extends IOApp.Simple {

def run: IO[Unit] =
OpenTelemetrySdk
.autoConfigured[IO](
// register OTLP exporters configurer
_.addExportersConfigurer(OtlpExportersAutoConfigure[IO])
// add AWS X-Ray Lambda Propagator
.addTracerProviderCustomizer((b, _) =>
b.addTextMapPropagators(AWSXRayLambdaPropagator())
)
)
.use { autoConfigured =>
val sdk = autoConfigured.sdk
program(sdk.meterProvider, sdk.tracerProvider)
}

def program(
meterProvider: MeterProvider[IO],
tracerProvider: TracerProvider[IO]
): IO[Unit] =
???
}
```

@:choice(traces)

`SdkTraces` configures only `TracerProvider`:

```scala mdoc:silent:reset
import cats.effect.{IO, IOApp}
import org.typelevel.otel4s.sdk.contrib.aws.context.propagation._
import org.typelevel.otel4s.sdk.exporter.otlp.trace.autoconfigure.OtlpSpanExporterAutoConfigure
import org.typelevel.otel4s.sdk.trace.SdkTraces
import org.typelevel.otel4s.trace.TracerProvider

object TelemetryApp extends IOApp.Simple {

def run: IO[Unit] =
SdkTraces
.autoConfigured[IO](
// register OTLP exporters configurer
_.addExporterConfigurer(OtlpSpanExporterAutoConfigure[IO])
// add AWS X-Ray Lambda Propagator
.addTracerProviderCustomizer((b, _) =>
b.addTextMapPropagators(AWSXRayLambdaPropagator())
)
)
.use { autoConfigured =>
program(autoConfigured.tracerProvider)
}

def program(
tracerProvider: TracerProvider[IO]
): IO[Unit] =
???
}
```

@:@

[xray-concepts]: https://docs.aws.amazon.com/xray/latest/devguide/xray-concepts.html#xray-concepts-tracingheader
[lambda-xray-envvars]: https://docs.aws.amazon.com/lambda/latest/dg/configuration-envvars.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
/*
* 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.contrib.aws.context.propagation

import org.typelevel.otel4s.context.propagation.TextMapGetter
import org.typelevel.otel4s.context.propagation.TextMapPropagator
import org.typelevel.otel4s.context.propagation.TextMapUpdater
import org.typelevel.otel4s.sdk.autoconfigure.AutoConfigure
import org.typelevel.otel4s.sdk.context.Context
import org.typelevel.otel4s.sdk.trace.SdkContextKeys

/** An example of the AWS X-Ray Tracing Header:
* {{{
* X-Amzn-Trace-Id: Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1
* }}}
*
* If the header is missing, the Lambda's `com.amazonaws.xray.traceHeader`
* system property or `_X_AMZN_TRACE_ID` environment variable will be used as a
* fallback.
*
* @see
* [[https://docs.aws.amazon.com/xray/latest/devguide/xray-concepts.html#xray-concepts-tracingheader]]
*
* @see
* [[https://docs.aws.amazon.com/lambda/latest/dg/configuration-envvars.html]]
*/
private final class AWSXRayLambdaPropagator private[propagation] (
getProp: String => Option[String],
getEnv: String => Option[String]
) extends TextMapPropagator[Context] {
import AWSXRayLambdaPropagator.Const

private val propagator = AWSXRayPropagator()

def fields: Iterable[String] = propagator.fields

def extract[A: TextMapGetter](ctx: Context, carrier: A): Context = {
val xRayContext = propagator.extract(ctx, carrier)
xRayContext.get(SdkContextKeys.SpanContextKey) match {
case Some(_) =>
xRayContext

case None =>
val headerOpt =
getProp(Const.TraceHeaderSystemProp)
.orElse(getEnv(Const.TraceHeaderEnvVar))

headerOpt match {
case Some(header) =>
propagator.extract(
ctx,
Map(AWSXRayPropagator.Headers.TraceId -> header)
)

case None =>
xRayContext
}
}
}

def inject[A: TextMapUpdater](ctx: Context, carrier: A): A =
propagator.inject(ctx, carrier)

override def toString: String = "AWSXRayLambdaPropagator"

}

object AWSXRayLambdaPropagator {
private val Propagator =
new AWSXRayLambdaPropagator(sys.props.get, sys.env.get)

private object Const {
val name = "xray-lambda"

val TraceHeaderEnvVar = "_X_AMZN_TRACE_ID"
val TraceHeaderSystemProp = "com.amazonaws.xray.traceHeader"
}

/** Returns an instance of the AWSXRayLambdaPropagator.
*
* The propagator utilizes `X-Amzn-Trace-Id` header to extract and inject
* tracing details.
*
* If the header is missing, the Lambda's `com.amazonaws.xray.traceHeader`
* system property or `_X_AMZN_TRACE_ID` environment variable will be used as
* a fallback.
*
* An example of the AWS X-Ray Tracing Header:
* {{{
* X-Amzn-Trace-Id: Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1
* }}}
*
* @example
* {{{
* OpenTelemetrySdk.autoConfigured[IO](
* _.addTracerProviderCustomizer((b, _) => b.addTextMapPropagators(AWSXRayLambdaPropagator())
* )
* }}}
* @see
* [[https://docs.aws.amazon.com/xray/latest/devguide/xray-concepts.html#xray-concepts-tracingheader]]
*/
def apply(): TextMapPropagator[Context] = Propagator

/** Returns the named configurer `xray-lambda`. You can use it to dynamically
* enable AWS X-Ray lambda propagator via environment variable or system
* properties.
*
* @example
* {{{
* OpenTelemetrySdk.autoConfigured[IO](
* _.addTextMapPropagatorConfigurer(AWSXRayLambdaPropagator.configurer[IO])
* )
* }}}
*
* Enable propagator via environment variable:
* {{{
* OTEL_PROPAGATORS=xray-lambda
* }}}
* or system property:
* {{{
* -Dotel.propagators=xray-lambda
* }}}
*/
def configurer[F[_]]: AutoConfigure.Named[F, TextMapPropagator[Context]] =
AutoConfigure.Named.const(Const.name, Propagator)

}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import org.typelevel.otel4s.trace.TraceFlags
import org.typelevel.otel4s.trace.TraceState
import scodec.bits.ByteVector

/** An example of the AWS Xray Tracing Header:
/** An example of the AWS X-Ray Tracing Header:
* {{{
* X-Amzn-Trace-Id: Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1
* }}}
Expand Down Expand Up @@ -156,7 +156,7 @@ private final class AWSXRayPropagator extends TextMapPropagator[Context] {
object AWSXRayPropagator {
private val Propagator = new AWSXRayPropagator

private object Headers {
private[propagation] object Headers {
val TraceId = "X-Amzn-Trace-Id"
}

Expand All @@ -181,7 +181,7 @@ object AWSXRayPropagator {
* The propagator utilizes `X-Amzn-Trace-Id` header to extract and inject
* tracing details.
*
* An example of the AWS Xray Tracing Header:
* An example of the AWS X-Ray Tracing Header:
* {{{
* X-Amzn-Trace-Id: Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1
* }}}
Expand Down
Loading

0 comments on commit 65b9c1e

Please sign in to comment.