Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 61 additions & 3 deletions bottlecap/src/otlp/transform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ pub const KEY_DATADOG_STATS_COMPUTED: &str = "_dd.stats_computed";

// const SPAN_TYPE_GENERIC_DB: &str = "db";

// AWS Lambda OTEL instrumentation scope name
const AWS_LAMBDA_INSTRUMENTATION_SCOPE: &str = "opentelemetry.instrumentation.aws_lambda";

// TODO: add mappings
#[allow(dead_code)]
static DB_SYSTEM_MAP: LazyLock<HashMap<String, String>> = LazyLock::new(HashMap::new);
Expand Down Expand Up @@ -251,7 +254,7 @@ fn get_otel_operation_name_v1(
}

#[allow(clippy::too_many_lines)]
fn get_otel_operation_name_v2(otel_span: &OtelSpan) -> String {
fn get_otel_operation_name_v2(otel_span: &OtelSpan, lib: &OtelInstrumentationScope) -> String {
let operation_name =
get_otel_attribute_value_as_string(&otel_span.attributes, "operation.name", false);
if !operation_name.is_empty() {
Expand All @@ -261,6 +264,12 @@ fn get_otel_operation_name_v2(otel_span: &OtelSpan) -> String {
let is_client = otel_span.kind() == SpanKind::Client;
let is_server = otel_span.kind() == SpanKind::Server;

// AWS Lambda: Check if this is the root Lambda invocation span
// Only applies to Server spans from the AWS Lambda OTEL instrumentation
if is_server && lib.name == AWS_LAMBDA_INSTRUMENTATION_SCOPE {
return "aws.lambda".to_string();
}

// HTTP
let method =
get_otel_attribute_value_as_string(&otel_span.attributes, "http.request.method", false);
Expand Down Expand Up @@ -894,7 +903,7 @@ pub fn otel_span_to_dd_span(
}

if otel_operation_and_resource_v2_enabled(config.clone()) {
dd_span.name = get_otel_operation_name_v2(otel_span);
dd_span.name = get_otel_operation_name_v2(otel_span, lib);
} else {
dd_span.name = get_otel_operation_name_v1(
otel_span,
Expand Down Expand Up @@ -1325,6 +1334,55 @@ mod tests {
get_otel_operation_name_v1(&otel_span, &lib, false, &HashMap::new(), true),
"opentelemetry_instrumentation_aws_lambda.server"
);
assert_eq!(get_otel_operation_name_v2(&otel_span), "server.request");
// With a non-matching lib name, should return server.request
assert_eq!(
get_otel_operation_name_v2(&otel_span, &lib),
"server.request"
);
}

#[test]
fn test_otel_operation_name_aws_lambda() {
// Test that AWS Lambda OTEL instrumentation gets aws.lambda operation name
let otel_span = OtelSpan {
name: "handler.handler".to_string(),
kind: SpanKind::Server as i32,
..Default::default()
};
let aws_lambda_lib = OtelInstrumentationScope {
name: "opentelemetry.instrumentation.aws_lambda".to_string(),
version: "0.42b0".to_string(),
attributes: [].to_vec(),
dropped_attributes_count: 0,
};

// AWS Lambda Server span should return aws.lambda
assert_eq!(
get_otel_operation_name_v2(&otel_span, &aws_lambda_lib),
"aws.lambda"
);

// Non-server span from AWS Lambda instrumentation should NOT return aws.lambda
let internal_span = OtelSpan {
name: "my-function".to_string(),
kind: SpanKind::Internal as i32,
..Default::default()
};
assert_eq!(
get_otel_operation_name_v2(&internal_span, &aws_lambda_lib),
"SPAN_KIND_INTERNAL"
);

// Server span from different instrumentation should return server.request
let other_lib = OtelInstrumentationScope {
name: "handler".to_string(),
version: String::new(),
attributes: [].to_vec(),
dropped_attributes_count: 0,
};
assert_eq!(
get_otel_operation_name_v2(&otel_span, &other_lib),
"server.request"
);
}
}
Loading