diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2f7978fe5..282826fb2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -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 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 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 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/metrics/.js/target sdk-exporter/trace/.js/target core/common/.native/target sdk/trace-testkit/.js/target core/common/.js/target oteljava/trace/target oteljava/testkit/target sdk/testkit/.jvm/target sdk-exporter/all/.js/target sdk-contrib/aws/xray-propagator/.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 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 oteljava/testkit/target sdk/testkit/.jvm/target sdk-exporter/all/.js/target sdk-contrib/aws/xray-propagator/.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 - 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 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 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 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/metrics/.js/target sdk-exporter/trace/.js/target core/common/.native/target sdk/trace-testkit/.js/target core/common/.js/target oteljava/trace/target oteljava/testkit/target sdk/testkit/.jvm/target sdk-exporter/all/.js/target sdk-contrib/aws/xray-propagator/.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 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 oteljava/testkit/target sdk/testkit/.jvm/target sdk-exporter/all/.js/target sdk-contrib/aws/xray-propagator/.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 - name: Upload target directories if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main') diff --git a/build.sbt b/build.sbt index 87718b874..117af2d41 100644 --- a/build.sbt +++ b/build.sbt @@ -108,6 +108,7 @@ lazy val root = tlCrossRootProject `sdk-exporter-metrics`, `sdk-exporter-trace`, `sdk-exporter`, + `sdk-contrib-aws-resource`, `sdk-contrib-aws-xray-propagator`, `oteljava-common`, `oteljava-common-testkit`, @@ -437,6 +438,22 @@ lazy val `sdk-exporter` = crossProject(JVMPlatform, JSPlatform, NativePlatform) // SDK contrib modules // +lazy val `sdk-contrib-aws-resource` = + crossProject(JVMPlatform, JSPlatform, NativePlatform) + .crossType(CrossType.Pure) + .in(file("sdk-contrib/aws/resource")) + .dependsOn(`sdk-common`, `semconv-experimental` % Test) + .settings( + name := "otel4s-sdk-contrib-aws-resource", + startYear := Some(2024) + ) + .settings(munitDependencies) + .settings(scalafixSettings) + +// +// SDK contrib modules +// + lazy val `sdk-contrib-aws-xray-propagator` = crossProject(JVMPlatform, JSPlatform, NativePlatform) .crossType(CrossType.Pure) @@ -447,8 +464,7 @@ lazy val `sdk-contrib-aws-xray-propagator` = ) .settings( name := "otel4s-sdk-contrib-aws-xray-propagator", - startYear := Some(2024), - mimaPreviousArtifacts ~= { _.filterNot(_.revision.startsWith("0.9")) } + startYear := Some(2024) ) .settings(munitDependencies) .settings(scalafixSettings) @@ -680,6 +696,7 @@ lazy val docs = project oteljava, sdk.jvm, `sdk-exporter`.jvm, + `sdk-contrib-aws-resource`.jvm, `sdk-contrib-aws-xray-propagator`.jvm ) .settings( @@ -753,6 +770,7 @@ lazy val unidocs = project `sdk-exporter-metrics`.jvm, `sdk-exporter-trace`.jvm, `sdk-exporter`.jvm, + `sdk-contrib-aws-resource`.jvm, `sdk-contrib-aws-xray-propagator`.jvm, `oteljava-common`, `oteljava-common-testkit`, diff --git a/docs/sdk/aws-resource-detectors.md b/docs/sdk/aws-resource-detectors.md new file mode 100644 index 000000000..fbdfa6398 --- /dev/null +++ b/docs/sdk/aws-resource-detectors.md @@ -0,0 +1,186 @@ +# 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. + +```scala mdoc:passthrough +import cats.effect.IO +import cats.effect.std.Env +import cats.effect.unsafe.implicits.global +import org.typelevel.otel4s.sdk.contrib.aws.resource._ +import scala.collection.immutable + +val envEntries = Map( + "AWS_REGION" -> "eu-west-1", + "AWS_LAMBDA_FUNCTION_NAME" -> "function", + "AWS_LAMBDA_FUNCTION_VERSION" -> "0.0.1" +) + +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) + } + +println("Environment: ") +println("```") +envEntries.foreach { case (k, v) => println(s"${k.replace("_", "_")}=$v") } +println("```") + +println("Detected resource: ") +println("```") +AWSLambdaDetector[IO].detect.unsafeRunSync().foreach { resource => + resource.attributes.toList.sortBy(_.key.name).foreach { attribute => + println(attribute.key.name + ": " + attribute.value) + } +} +println("```") +``` + +## Getting Started + +@:select(build-tool) + +@:choice(sbt) + +Add settings to the `build.sbt`: + +```scala +libraryDependencies ++= Seq( + "org.typelevel" %%% "otel4s-sdk" % "@VERSION@", // <1> + "org.typelevel" %%% "otel4s-sdk-exporter" % "@VERSION@", // <2> + "org.typelevel" %%% "otel4s-sdk-contrib-aws-resource" % "@VERSION@" // <3> +) +``` + +@:choice(scala-cli) + +Add directives to the `*.scala` file: + +```scala +//> using lib "org.typelevel::otel4s-sdk::@VERSION@" // <1> +//> using lib "org.typelevel::otel4s-sdk-exporter::@VERSION@" // <2> +//> using lib "org.typelevel::otel4s-sdk-contrib-aws-resource::@VERSION@" // <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: + +@: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.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]) + ) + .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.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]) + ) + .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](configuration.md#telemetry-resource-detectors). + +There are several ways to configure the options: + +@:select(sdk-options-source) + +@:choice(sbt) + +Add settings to the `build.sbt`: + +```scala +javaOptions += "-Dotel.otel4s.resource.detectors.enabled=aws-lambda" +envVars ++= Map("OTEL_OTEL4S_RESOURCE_DETECTORS_ENABLE" -> "aws-lambda") +``` + +@:choice(scala-cli) + +Add directives to the `*.scala` file: + +```scala +//> using javaOpt -Dotel.otel4s.resource.detectors.enabled=aws-lambda +``` + +@:choice(shell) + +```shell +$ export OTEL_OTEL4S_RESOURCE_DETECTORS_ENABLED=aws-lambda +``` +@:@ diff --git a/docs/sdk/directory.conf b/docs/sdk/directory.conf index 3649433e5..18b7b11ae 100644 --- a/docs/sdk/directory.conf +++ b/docs/sdk/directory.conf @@ -3,5 +3,6 @@ laika.title = SDK laika.navigationOrder = [ overview.md configuration.md + aws-resource-detectors.md aws-xray-propagator.md ] diff --git a/sdk-contrib/aws/resource/src/main/scala/org/typelevel/otel4s/sdk/contrib/aws/resource/AWSLambdaDetector.scala b/sdk-contrib/aws/resource/src/main/scala/org/typelevel/otel4s/sdk/contrib/aws/resource/AWSLambdaDetector.scala new file mode 100644 index 000000000..0c5b18a6e --- /dev/null +++ b/sdk-contrib/aws/resource/src/main/scala/org/typelevel/otel4s/sdk/contrib/aws/resource/AWSLambdaDetector.scala @@ -0,0 +1,104 @@ +/* + * 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.resource + +import cats.Monad +import cats.effect.std.Env +import cats.syntax.flatMap._ +import cats.syntax.functor._ +import org.typelevel.otel4s.AttributeKey +import org.typelevel.otel4s.Attributes +import org.typelevel.otel4s.sdk.TelemetryResource +import org.typelevel.otel4s.sdk.resource.TelemetryResourceDetector +import org.typelevel.otel4s.semconv.SchemaUrls + +private class AWSLambdaDetector[F[_]: Env: Monad] + extends TelemetryResourceDetector[F] { + + import AWSLambdaDetector.Const + import AWSLambdaDetector.Keys + + def name: String = Const.Name + + def detect: F[Option[TelemetryResource]] = + for { + region <- Env[F].get("AWS_REGION") + functionName <- Env[F].get("AWS_LAMBDA_FUNCTION_NAME") + functionVersion <- Env[F].get("AWS_LAMBDA_FUNCTION_VERSION") + } yield build(region, functionName, functionVersion) + + private def build( + region: Option[String], + functionName: Option[String], + functionVersion: Option[String] + ): Option[TelemetryResource] = + Option.when(functionName.nonEmpty || functionVersion.nonEmpty) { + val builder = Attributes.newBuilder + + builder.addOne(Keys.CloudProvider, Const.CloudProvider) + builder.addOne(Keys.CloudPlatform, Const.CloudPlatform) + + region.foreach(r => builder.addOne(Keys.CloudRegion, r)) + functionName.foreach(name => builder.addOne(Keys.FaasName, name)) + functionVersion.foreach(v => builder.addOne(Keys.FaasVersion, v)) + + TelemetryResource(builder.result(), Some(SchemaUrls.Current)) + } +} + +object AWSLambdaDetector { + + private object Const { + val Name = "aws-lambda" + val CloudProvider = "aws" + val CloudPlatform = "aws_lambda" + } + + private object Keys { + val CloudProvider: AttributeKey[String] = AttributeKey("cloud.provider") + val CloudPlatform: AttributeKey[String] = AttributeKey("cloud.platform") + val CloudRegion: AttributeKey[String] = AttributeKey("cloud.region") + val FaasName: AttributeKey[String] = AttributeKey("faas.name") + val FaasVersion: AttributeKey[String] = AttributeKey("faas.version") + } + + /** 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. + * + * @example + * {{{ + * OpenTelemetrySdk + * .autoConfigured[IO]( + * // register OTLP exporters configurer + * _.addExportersConfigurer(OtlpExportersAutoConfigure[IO]) + * // register AWS Lambda detector + * .addResourceDetector(AWSLambdaDetector[IO]) + * ) + * .use { autoConfigured => + * val sdk = autoConfigured.sdk + * ??? + * } + * }}} + */ + def apply[F[_]: Env: Monad]: TelemetryResourceDetector[F] = + new AWSLambdaDetector[F] + +} diff --git a/sdk-contrib/aws/resource/src/test/scala/org/typelevel/otel4s/sdk/contrib/aws/resource/AWSLambdaDetectorSuite.scala b/sdk-contrib/aws/resource/src/test/scala/org/typelevel/otel4s/sdk/contrib/aws/resource/AWSLambdaDetectorSuite.scala new file mode 100644 index 000000000..77f51421a --- /dev/null +++ b/sdk-contrib/aws/resource/src/test/scala/org/typelevel/otel4s/sdk/contrib/aws/resource/AWSLambdaDetectorSuite.scala @@ -0,0 +1,85 @@ +/* + * 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.resource + +import cats.effect.IO +import cats.effect.std.Env +import munit.CatsEffectSuite +import org.typelevel.otel4s.Attributes +import org.typelevel.otel4s.sdk.TelemetryResource +import org.typelevel.otel4s.semconv.SchemaUrls +import org.typelevel.otel4s.semconv.experimental.attributes.CloudExperimentalAttributes._ +import org.typelevel.otel4s.semconv.experimental.attributes.FaasExperimentalAttributes._ + +import scala.collection.immutable + +class AWSLambdaDetectorSuite extends CatsEffectSuite { + + test("add only defined attributes") { + implicit val env: Env[IO] = + constEnv("AWS_LAMBDA_FUNCTION_NAME" -> "function") + + val expected = TelemetryResource( + Attributes( + CloudProvider(CloudProviderValue.Aws.value), + CloudPlatform(CloudPlatformValue.AwsLambda.value), + FaasName("function") + ), + Some(SchemaUrls.Current) + ) + + AWSLambdaDetector[IO].detect.assertEquals(Some(expected)) + } + + test("add all attributes") { + implicit val env: Env[IO] = constEnv( + "AWS_REGION" -> "eu-west-1", + "AWS_LAMBDA_FUNCTION_NAME" -> "function", + "AWS_LAMBDA_FUNCTION_VERSION" -> "0.0.1" + ) + + val expected = TelemetryResource( + Attributes( + CloudProvider(CloudProviderValue.Aws.value), + CloudPlatform(CloudPlatformValue.AwsLambda.value), + CloudRegion("eu-west-1"), + FaasName("function"), + FaasVersion("0.0.1") + ), + Some(SchemaUrls.Current) + ) + + AWSLambdaDetector[IO].detect.assertEquals(Some(expected)) + } + + test("return None when both NAME and VERSION are undefined") { + implicit val env: Env[IO] = constEnv("AWS_REGION" -> "eu-west-1") + + AWSLambdaDetector[IO].detect.assertEquals(None) + } + + private def constEnv(pairs: (String, String)*): Env[IO] = + new Env[IO] { + private val params = pairs.toMap + + def get(name: String): IO[Option[String]] = + IO.pure(params.get(name)) + + def entries: IO[immutable.Iterable[(String, String)]] = + IO.pure(params) + } +}