AWS | Resource detectors

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

1. aws-lambda

The detector relies on the AWS_REGION, AWS_LAMBDA_FUNCTION_NAME, and AWS_LAMBDA_FUNCTION_VERSION environment variables to configure the telemetry resource. Either AWS_LAMBDA_FUNCTION_NAME or AWS_LAMBDA_FUNCTION_VERSION must be present.

Environment:

AWS_REGION=eu-west-1
AWS_LAMBDA_FUNCTION_NAME=function
AWS_LAMBDA_FUNCTION_VERSION=0.0.1

Detected resource:

cloud.platform: aws_lambda
cloud.provider: aws
cloud.region: eu-west-1
faas.name: function
faas.version: 0.0.1

2. aws-ec2

The detector fetches instance metadata from the http://169.254.169.254 endpoint. See AWS documentation for more details.

The http://169.254.169.254/latest/dynamic/instance-identity/document response:

{
  "accountId" : "1234567890",
  "architecture" : "x86_64",
  "availabilityZone" : "eu-west-1a",
  "imageId" : "ami-abc123de",
  "instanceId" : "i-abc321de",
  "instanceType" : "t3.small",
  "privateIp" : "10.0.0.1",
  "region" : "eu-west-1",
  "version" : "2017-09-30"
}

The http://169.254.169.254/latest/meta-data/hostname response:

ip-10-0-0-1.eu-west-1.compute.internal

Detected resource:

cloud.account.id: 1234567890
cloud.availability_zone: eu-west-1a
cloud.platform: aws_ec2
cloud.provider: aws
cloud.region: eu-west-1
host.id: i-abc321de
host.image.id: ami-abc123de
host.name: ip-10-0-0-1.eu-west-1.compute.internal
host.type: t3.small

3. 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 for more details.

The http://169.254.170.2/v4/5fb8fcdd-29f2-490f-8229-c1269d11a9d9 response:

{
  "DockerId" : "83b2af5973dc...ee1e1",
  "Name" : "server",
  "DockerName" : "ecs-service-production-11-server-e4e7efbceda7b7c68601",
  "Image" : "1234567890.dkr.ecr.eu-west-1.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" : {
    "status" : "HEALTHY"
  },
  "LogDriver" : "awslogs",
  "LogOptions" : {
    "awslogs-group" : "/ecs/production/service",
    "awslogs-region" : "eu-west-1",
    "awslogs-stream" : "ecs/server/5e1b...86980"
  },
  "ContainerARN" : "arn:aws:ecs:eu-west-1:1234567890:task/production/5e1b...86980/1a1c23fe-1718-4eed-9833-c3dc2dad712c"
}

The http://169.254.170.2/v4/5fb8fcdd-29f2-490f-8229-c1269d11a9d9/task response:

{
  "Cluster" : "production",
  "TaskARN" : "arn:aws:ecs:eu-west-1:1234567890:task/production/5e1b...86980",
  "Family" : "service-production",
  "Revision" : "11",
  "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"
}

Detected resource:

aws.ecs.cluster.arn: arn:aws:ecs:eu-west-1:1234567890:cluster/production
aws.ecs.container.arn: arn:aws:ecs:eu-west-1:1234567890:task/production/5e1b...86980/1a1c23fe-1718-4eed-9833-c3dc2dad712c
aws.ecs.container.image.id: sha256:7382b7779e6038...11f2d7d522d
aws.ecs.launchtype: EC2
aws.ecs.task.arn: arn:aws:ecs:eu-west-1:1234567890:task/production/5e1b...86980
aws.ecs.task.family: service-production
aws.ecs.task.revision: 11
aws.log.group.arns: List(arn:aws:logs:eu-west-1:1234567890:log-group:/ecs/production/service)
aws.log.group.names: List(/ecs/production/service)
aws.log.stream.arns: List(arn:aws:logs:eu-west-1:1234567890:log-group:/ecs/production/service:log-stream:ecs/server/5e1b...86980)
aws.log.stream.names: List(ecs/server/5e1b...86980)
cloud.account.id: 1234567890
cloud.availability_zone: eu-west-1a
cloud.platform: aws_ecs
cloud.provider: aws
cloud.region: eu-west-1
cloud.resource_id: arn:aws:ecs:eu-west-1:1234567890:task/production/5e1b...86980/1a1c23fe-1718-4eed-9833-c3dc2dad712c
container.id: 83b2af5973dc...ee1e1
container.image.name: 1234567890.dkr.ecr.eu-west-1.amazonaws.com/internal/repository
container.image.tags: List(8abab2a5)
container.name: ecs-service-production-11-server-e4e7efbceda7b7c68601

4. aws-beanstalk

The detector parses environment details from the /var/elasticbeanstalk/xray/environment.conf file to configure the telemetry resource.

Expected configuration attributes:

The content of the /var/elasticbeanstalk/xray/environment.conf file:

{"deployment_id":2,"version_label":"1.1","environment_name":"production-eu-west"}

Detected resource:

cloud.platform: aws_elastic_beanstalk
cloud.provider: aws
service.instance.id: 2
service.namespace: production-eu-west
service.version: 1.1

Getting Started

Add settings to the build.sbt:

libraryDependencies ++= Seq(
  "org.typelevel" %%% "otel4s-sdk" % "0.9.0", // <1>
  "org.typelevel" %%% "otel4s-sdk-exporter" % "0.9.0", // <2>
  "org.typelevel" %%% "otel4s-sdk-contrib-aws-resource" % "0.9.0" // <3>
)

Add directives to the *.scala file:

//> using lib "org.typelevel::otel4s-sdk::0.9.0" // <1>
//> using lib "org.typelevel::otel4s-sdk-exporter::0.9.0" // <2>
//> using lib "org.typelevel::otel4s-sdk-contrib-aws-resource::0.9.0" // <3>
  1. Add the otel4s-sdk library
  2. Add the otel4s-sdk-exporter library. Without the exporter, the application will crash
  3. Add the otel4s-sdk-contrib-aws-resource library

Then autoconfigure the SDK:

OpenTelemetrySdk.autoConfigured configures both MeterProvider and TracerProvider:

import cats.effect.{IO, IOApp}
import org.typelevel.otel4s.metrics.MeterProvider
import org.typelevel.otel4s.sdk.OpenTelemetrySdk
import org.typelevel.otel4s.sdk.contrib.aws.resource._
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])
        // register AWS Lambda detector
         .addResourceDetector(AwsLambdaDetector[IO])
        // register AWS EC2 detector
         .addResourceDetector(AwsEc2Detector[IO])
        // register AWS ECS detector
         .addResourceDetector(AwsEcsDetector[IO])
        // register AWS Beanstalk detector
         .addResourceDetector(AwsBeanstalkDetector[IO])
      )
      .use { autoConfigured =>
        val sdk = autoConfigured.sdk
        program(sdk.meterProvider, sdk.tracerProvider)
      }

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

SdkTraces configures only TracerProvider:

import cats.effect.{IO, IOApp}
import org.typelevel.otel4s.sdk.contrib.aws.resource._
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])
        // register AWS Lambda detector
         .addResourceDetector(AwsLambdaDetector[IO])
        // register AWS EC2 detector
         .addResourceDetector(AwsEc2Detector[IO])
        // register AWS ECS detector
         .addResourceDetector(AwsEcsDetector[IO])
        // register AWS Beanstalk detector
         .addResourceDetector(AwsBeanstalkDetector[IO])
      )
      .use { autoConfigured =>
        program(autoConfigured.tracerProvider)
      }

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

Configuration

The OpenTelemetrySdk.autoConfigured(...) and SdkTraces.autoConfigured(...) rely on the environment variables and system properties to configure the SDK. Check out the configuration details.

There are several ways to configure the options:

Add settings to the build.sbt:

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

Add directives to the *.scala file:

//> using javaOpt -Dotel.otel4s.resource.detectors.enabled=aws-lambda,aws-ec2,aws-ecs,aws-beanstalk
$ export OTEL_OTEL4S_RESOURCE_DETECTORS_ENABLED=aws-lambda,aws-ec2,aws-ecs,aws-beanstalk