lambda_otel_utils/
resource.rs

1use opentelemetry::KeyValue;
2use opentelemetry_aws::detector::LambdaResourceDetector;
3use opentelemetry_sdk::{resource::ResourceDetector, Resource};
4use opentelemetry_semantic_conventions as semconv;
5use std::env;
6use std::time::Duration;
7
8/// Retrieves the Lambda resource with the service name.
9///
10/// This function attempts to retrieve the service name from the `OTEL_SERVICE_NAME` environment variable.
11/// If that variable is not set, it falls back to the `AWS_LAMBDA_FUNCTION_NAME` environment variable.
12/// If neither variable is set, it defaults to "unknown-service".
13///
14/// The function then creates a new `Resource` with the detected Lambda resource information
15/// and merges it with a new `Resource` containing the service name key-value pair.
16///
17/// # Returns
18///
19/// A `Resource` representing the Lambda resource with the service name.
20pub fn get_lambda_resource() -> Resource {
21    let service_name =
22        match env::var("OTEL_SERVICE_NAME").or_else(|_| env::var("AWS_LAMBDA_FUNCTION_NAME")) {
23            Ok(name) => name,
24            Err(_) => "unknown-service".to_string(),
25        };
26    Resource::default()
27        .merge(&LambdaResourceDetector.detect(Duration::default()))
28        .merge(&Resource::new(vec![
29            KeyValue::new(semconv::resource::SERVICE_NAME, service_name),
30            KeyValue::new(semconv::resource::PROCESS_RUNTIME_NAME, "rust"),
31            KeyValue::new(
32                semconv::resource::PROCESS_RUNTIME_VERSION,
33                rustc_version_runtime::version().to_string(),
34            ),
35        ]))
36}
37
38#[cfg(test)]
39mod tests {
40    use super::*;
41    use sealed_test::prelude::*;
42
43    #[sealed_test(env = [
44        ("OTEL_SERVICE_NAME", "test-service"),
45        ("AWS_LAMBDA_FUNCTION_NAME", "test-function"),
46    ])]
47    fn test_get_lambda_resource_with_otel_service_name() {
48        let resource = get_lambda_resource();
49        assert_eq!(
50            resource.get(opentelemetry_semantic_conventions::resource::SERVICE_NAME.into()),
51            Some("test-service".into())
52        );
53    }
54
55    #[sealed_test(env = [
56        ("AWS_LAMBDA_FUNCTION_NAME", "test-function"),
57    ])]
58    fn test_get_lambda_resource_with_aws_lambda_function_name() {
59        let resource = get_lambda_resource();
60        assert_eq!(
61            resource.get(opentelemetry_semantic_conventions::resource::SERVICE_NAME.into()),
62            Some("test-function".into())
63        );
64    }
65
66    #[sealed_test]
67    fn test_get_lambda_resource_without_env_vars() {
68        let resource = get_lambda_resource();
69        assert_eq!(
70            resource.get(opentelemetry_semantic_conventions::resource::SERVICE_NAME.into()),
71            Some("unknown-service".into())
72        );
73    }
74
75    #[test]
76    fn test_runtime_attributes() {
77        let resource = get_lambda_resource();
78
79        // Test process.runtime.name
80        assert_eq!(
81            resource.get(opentelemetry_semantic_conventions::resource::PROCESS_RUNTIME_NAME.into()),
82            Some("rust".into())
83        );
84
85        // Test process.runtime.version is present and follows semver format
86        let version = resource
87            .get(opentelemetry_semantic_conventions::resource::PROCESS_RUNTIME_VERSION.into())
88            .expect("Runtime version should be present");
89        assert!(
90            version.to_string().contains('.'),
91            "Version should be in semver format"
92        );
93    }
94}