Skip to content

Commit

Permalink
opentelemetry-aws: add LambdaResourceDetector (#93)
Browse files Browse the repository at this point in the history
Signed-off-by: thomas.gouveia <thomas.gouveia@ioterop.com>
Signed-off-by: thomasgouveia <gouveia.thomas@outlook.fr>
  • Loading branch information
thomasgouveia authored Jul 26, 2024
1 parent 624e5bd commit 9cd566b
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 0 deletions.
4 changes: 4 additions & 0 deletions opentelemetry-aws/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## vNext

### Added

- `LambdaResourceDetector` has been added to the crate to detect AWS Lambda attributes. To enable it in your code, use the feature `detector-aws-lambda`.

### v0.12.0

- Bump opentelemetry and opentelemetry_sdk versions to 0.24.0
Expand Down
3 changes: 3 additions & 0 deletions opentelemetry-aws/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,18 @@ rustdoc-args = ["--cfg", "docsrs"]
[features]
default = ["trace"]
trace = ["opentelemetry/trace", "opentelemetry_sdk/trace"]
detector-aws-lambda = ["dep:opentelemetry-semantic-conventions"]

[dependencies]
once_cell = "1.12"
opentelemetry = { workspace = true }
opentelemetry_sdk = { workspace = true, optional = true }
opentelemetry-semantic-conventions = { workspace = true, optional = true }

[dev-dependencies]
opentelemetry_sdk = { workspace = true, features = ["testing"] }
opentelemetry-http = { workspace = true }
opentelemetry-stdout = { workspace = true, features = ["trace"] }
hyper = { version = "1.4.1" }
tokio = { version = "1.0", features = ["macros", "rt"] }
sealed_test = "1.1.0"
95 changes: 95 additions & 0 deletions opentelemetry-aws/src/detector/lambda.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
use opentelemetry::KeyValue;
use opentelemetry_sdk::resource::ResourceDetector;
use opentelemetry_sdk::Resource;
use opentelemetry_semantic_conventions as semconv;
use std::env;
use std::time::Duration;

// For a complete list of reserved environment variables in Lambda, see:
// https://docs.aws.amazon.com/lambda/latest/dg/configuration-envvars.html
const AWS_LAMBDA_FUNCTION_NAME_ENV_VAR: &str = "AWS_LAMBDA_FUNCTION_NAME";
const AWS_REGION_ENV_VAR: &str = "AWS_REGION";
const AWS_LAMBDA_FUNCTION_VERSION_ENV_VAR: &str = "AWS_LAMBDA_FUNCTION_VERSION";
const AWS_LAMBDA_LOG_STREAM_NAME_ENV_VAR: &str = "AWS_LAMBDA_LOG_STREAM_NAME";
const AWS_LAMBDA_MEMORY_LIMIT_ENV_VAR: &str = "AWS_LAMBDA_FUNCTION_MEMORY_SIZE";

/// Resource detector that collects resource information from AWS Lambda environment.
pub struct LambdaResourceDetector;

impl ResourceDetector for LambdaResourceDetector {
fn detect(&self, _: Duration) -> Resource {
let lambda_name = env::var(AWS_LAMBDA_FUNCTION_NAME_ENV_VAR).unwrap_or_default();
// If no lambda name is provided, it means that
// we're not on a Lambda environment, so we return empty resource.
if lambda_name.is_empty() {
return Resource::empty();
}

let aws_region = env::var(AWS_REGION_ENV_VAR).unwrap_or_default();
let function_version = env::var(AWS_LAMBDA_FUNCTION_VERSION_ENV_VAR).unwrap_or_default();
let function_memory_limit = env::var(AWS_LAMBDA_MEMORY_LIMIT_ENV_VAR).unwrap_or_default();
// Instance attributes corresponds to the log stream name for AWS Lambda;
// See the FaaS resource specification for more details.
let instance = env::var(AWS_LAMBDA_LOG_STREAM_NAME_ENV_VAR).unwrap_or_default();

let attributes = [
KeyValue::new(semconv::resource::CLOUD_PROVIDER, "aws"),
KeyValue::new(semconv::resource::CLOUD_REGION, aws_region),
KeyValue::new(semconv::resource::FAAS_INSTANCE, instance),
KeyValue::new(semconv::resource::FAAS_NAME, lambda_name),
KeyValue::new(semconv::resource::FAAS_VERSION, function_version),
KeyValue::new(semconv::resource::FAAS_MAX_MEMORY, function_memory_limit),
];

Resource::new(attributes)
}
}

#[cfg(test)]
mod tests {
use super::*;
use sealed_test::prelude::*;
use std::env::{remove_var, set_var};

#[sealed_test]
fn test_aws_lambda_detector() {
set_var(AWS_LAMBDA_FUNCTION_NAME_ENV_VAR, "my-lambda-function");
set_var(AWS_REGION_ENV_VAR, "eu-west-3");
set_var(AWS_LAMBDA_FUNCTION_VERSION_ENV_VAR, "$LATEST");
set_var(
AWS_LAMBDA_LOG_STREAM_NAME_ENV_VAR,
"2023/01/01/[$LATEST]5d1edb9e525d486696cf01a3503487bc",
);
set_var(AWS_LAMBDA_MEMORY_LIMIT_ENV_VAR, "128");

let expected = Resource::new([
KeyValue::new(semconv::resource::CLOUD_PROVIDER, "aws"),
KeyValue::new(semconv::resource::CLOUD_REGION, "eu-west-3"),
KeyValue::new(
semconv::resource::FAAS_INSTANCE,
"2023/01/01/[$LATEST]5d1edb9e525d486696cf01a3503487bc",
),
KeyValue::new(semconv::resource::FAAS_NAME, "my-lambda-function"),
KeyValue::new(semconv::resource::FAAS_VERSION, "$LATEST"),
KeyValue::new(semconv::resource::FAAS_MAX_MEMORY, "128"),
]);

let detector = LambdaResourceDetector {};
let got = detector.detect(Duration::from_secs(0));

assert_eq!(expected, got);

remove_var(AWS_LAMBDA_FUNCTION_NAME_ENV_VAR);
remove_var(AWS_REGION_ENV_VAR);
remove_var(AWS_LAMBDA_FUNCTION_VERSION_ENV_VAR);
remove_var(AWS_LAMBDA_LOG_STREAM_NAME_ENV_VAR);
remove_var(AWS_LAMBDA_MEMORY_LIMIT_ENV_VAR);
}

#[sealed_test]
fn test_aws_lambda_detector_returns_empty_if_no_lambda_environment() {
let detector = LambdaResourceDetector {};
let got = detector.detect(Duration::from_secs(0));
assert_eq!(Resource::empty(), got);
}
}
4 changes: 4 additions & 0 deletions opentelemetry-aws/src/detector/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#[cfg(feature = "detector-aws-lambda")]
mod lambda;
#[cfg(feature = "detector-aws-lambda")]
pub use lambda::LambdaResourceDetector;
1 change: 1 addition & 0 deletions opentelemetry-aws/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
pub mod detector;
pub mod trace;

0 comments on commit 9cd566b

Please sign in to comment.