Skip to content

Commit

Permalink
sdk-contrib: add AWSECSDetector
Browse files Browse the repository at this point in the history
  • Loading branch information
iRevive committed Sep 14, 2024
1 parent c6a43dd commit 45afd70
Show file tree
Hide file tree
Showing 4 changed files with 617 additions and 6 deletions.
11 changes: 10 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -446,10 +446,18 @@ lazy val `sdk-contrib-aws-resource` =
.dependsOn(`sdk-common`, `semconv-experimental` % Test)
.settings(
name := "otel4s-sdk-contrib-aws-resource",
startYear := Some(2024)
startYear := Some(2024),
libraryDependencies ++= Seq(
"org.http4s" %%% "http4s-ember-client" % Http4sVersion,
"org.http4s" %%% "http4s-circe" % Http4sVersion,
"org.http4s" %%% "http4s-dsl" % Http4sVersion % Test
)
)
.settings(munitDependencies)
.settings(scalafixSettings)
.jsSettings(scalaJSLinkerSettings)
.nativeEnablePlugins(ScalaNativeBrewedConfigPlugin)
.nativeSettings(scalaNativeSettings)

lazy val `sdk-contrib-aws-xray-propagator` =
crossProject(JVMPlatform, JSPlatform, NativePlatform)
Expand Down Expand Up @@ -735,6 +743,7 @@ lazy val docs = project
libraryDependencies ++= Seq(
"org.apache.pekko" %% "pekko-http" % PekkoHttpVersion,
"org.http4s" %% "http4s-client" % Http4sVersion,
"org.http4s" %% "http4s-dsl" % Http4sVersion,
"io.opentelemetry" % "opentelemetry-sdk-extension-autoconfigure" % OpenTelemetryVersion,
"io.opentelemetry.instrumentation" % "opentelemetry-instrumentation-annotations" % OpenTelemetryInstrumentationVersion,
"io.opentelemetry.instrumentation" % "opentelemetry-runtime-telemetry-java8" % OpenTelemetryInstrumentationAlphaVersion,
Expand Down
116 changes: 111 additions & 5 deletions docs/sdk/aws-resource-detectors.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# AWS | Resource detectors

Resource detectors can add environment-specific attributes to the telemetry resource.
Resource detectors can add environment-specific attributes to the telemetry resource.
AWS detectors are implemented as a third-party library, and you need to enable them manually.

## The list of detectors
Expand Down Expand Up @@ -45,6 +45,108 @@ AWSLambdaDetector[IO].detect.unsafeRunSync().foreach { resource =>
println("```")
```

### 4. aws-ecs

The detector fetches ECS container and task metadata.
The base URI is obtained from `ECS_CONTAINER_METADATA_URI_V4` or `ECS_CONTAINER_METADATA_URI` env variable.

See [AWS documentation](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-metadata-endpoint-v4.html) for more
details.

```scala mdoc:reset:passthrough
import cats.effect.IO
import cats.effect.std.Env
import cats.effect.unsafe.implicits.global
import io.circe.Json
import io.circe.syntax._
import org.http4s._
import org.http4s.circe.jsonEncoder
import org.http4s.client.Client
import org.http4s.dsl.io._
import org.typelevel.otel4s.sdk.contrib.aws.resource._
import scala.collection.immutable

val envEntries = Map(
"ECS_CONTAINER_METADATA_URI_V4" -> "http://169.254.170.2/v4/5fb8fcdd-29f2-490f-8229-c1269d11a9d9"
)

implicit val env: Env[IO] = new Env[IO] {
def get(name: String): IO[Option[String]] = IO.pure(envEntries.get(name))
def entries: IO[immutable.Iterable[(String, String)]] = IO.pure(envEntries)
}

val accountId = "1234567890"
val region = "eu-west-1"
val taskId = "5e1b...86980"
val family = "service-production"
val cluster = "production"
val revision = "11"
val taskArn = s"arn:aws:ecs:$region:$accountId:task/$cluster/$taskId"

val container = Json.obj(
"DockerId" := "83b2af5973dc...ee1e1",
"Name" := "server",
"DockerName" := s"ecs-$family-$revision-server-e4e7efbceda7b7c68601",
"Image" := s"$accountId.dkr.ecr.$region.amazonaws.com/internal/repository:8abab2a5",
"ImageID" := "sha256:7382b7779e6038...11f2d7d522d",
"DesiredStatus" := "RUNNING",
"CreatedAt" := "2024-09-12T18:08:55.593944224Z",
"StartedAt" := "2024-09-12T18:08:56.524454503Z",
"Type" := "NORMAL",
"Health" := Json.obj("status" := "HEALTHY"),
"LogDriver" := "awslogs",
"LogOptions" := Json.obj(
"awslogs-group" := s"/ecs/$cluster/service",
"awslogs-region" := region,
"awslogs-stream" := s"ecs/server/$taskId"
),
"ContainerARN" := s"$taskArn/1a1c23fe-1718-4eed-9833-c3dc2dad712c"
)

val task = Json.obj(
"Cluster" := cluster,
"TaskARN" := taskArn,
"Family" := family,
"Revision" := revision,
"DesiredStatus" := "RUNNING",
"KnownStatus" := "RUNNING",
"PullStartedAt" := "2024-09-12T18:08:55.307387715Z",
"PullStoppedAt" := "2024-09-12T18:08:55.564707417Z",
"AvailabilityZone" := "eu-west-1a",
"LaunchType" := "EC2",
"VPCID" := "vpc-123",
"ServiceName" := "service"
)

val client = Client.fromHttpApp[IO](
HttpRoutes
.of[IO] {
case GET -> Root / "v4" / "5fb8fcdd-29f2-490f-8229-c1269d11a9d9" => Ok(container)
case GET -> Root / "v4" / "5fb8fcdd-29f2-490f-8229-c1269d11a9d9" / "task" => Ok(task)
}
.orNotFound
)

println("The `http://169.254.170.2/v4/5fb8fcdd-29f2-490f-8229-c1269d11a9d9` response: ")
println("```json")
println(container)
println("```")

println("The `http://169.254.170.2/v4/5fb8fcdd-29f2-490f-8229-c1269d11a9d9/task` response:")
println("```json")
println(task)
println("```")

println("Detected resource: ")
println("```yaml")
AWSECSDetector[IO](client).detect.unsafeRunSync().foreach { resource =>
resource.attributes.toList.sortBy(_.key.name).foreach { attribute =>
println(attribute.key.name + ": " + attribute.value)
}
}
println("```")
```

## Getting Started

@:select(build-tool)
Expand Down Expand Up @@ -104,6 +206,8 @@ object TelemetryApp extends IOApp.Simple {
_.addExportersConfigurer(OtlpExportersAutoConfigure[IO])
// register AWS Lambda detector
.addResourceDetector(AWSLambdaDetector[IO])
// register AWS ECS detector
.addResourceDetector(AWSECSDetector[IO])
)
.use { autoConfigured =>
val sdk = autoConfigured.sdk
Expand Down Expand Up @@ -138,6 +242,8 @@ object TelemetryApp extends IOApp.Simple {
_.addExporterConfigurer(OtlpSpanExporterAutoConfigure[IO])
// register AWS Lambda detector
.addResourceDetector(AWSLambdaDetector[IO])
// register AWS ECS detector
.addResourceDetector(AWSECSDetector[IO])
)
.use { autoConfigured =>
program(autoConfigured.tracerProvider)
Expand Down Expand Up @@ -166,21 +272,21 @@ There are several ways to configure the options:
Add settings to the `build.sbt`:

```scala
javaOptions += "-Dotel.otel4s.resource.detectors.enabled=aws-lambda"
envVars ++= Map("OTEL_OTEL4S_RESOURCE_DETECTORS_ENABLE" -> "aws-lambda")
javaOptions += "-Dotel.otel4s.resource.detectors.enabled=aws-lambda,aws-ecs"
envVars ++= Map("OTEL_OTEL4S_RESOURCE_DETECTORS_ENABLE" -> "aws-lambda,aws-ecs")
```

@:choice(scala-cli)

Add directives to the `*.scala` file:

```scala
//> using javaOpt -Dotel.otel4s.resource.detectors.enabled=aws-lambda
//> using javaOpt -Dotel.otel4s.resource.detectors.enabled=aws-lambda,aws-ecs
```

@:choice(shell)

```shell
$ export OTEL_OTEL4S_RESOURCE_DETECTORS_ENABLED=aws-lambda
$ export OTEL_OTEL4S_RESOURCE_DETECTORS_ENABLED=aws-lambda,aws-ecs
```
@:@
Loading

0 comments on commit 45afd70

Please sign in to comment.