Skip to content

Commit 5af656c

Browse files
fix: OTLP Database Span Naming (#847)
Names OpenTelemetry Database spans according to the convention outlined in https://opentelemetry.io/docs/specs/semconv/database/database-spans/ Addresses #808
1 parent bbf5b04 commit 5af656c

3 files changed

Lines changed: 47 additions & 43 deletions

File tree

bottlecap/Cargo.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

bottlecap/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ futures = { version = "0.3.31", default-features = false }
4545
serde-aux = { version = "4.7", default-features = false }
4646
serde_html_form = { version = "0.2", default-features = false }
4747
opentelemetry-proto = { version = "0.29", features = ["trace", "with-serde", "gen-tonic"] }
48-
opentelemetry-semantic-conventions = { version = "0.29", features = ["semconv_experimental"] }
48+
opentelemetry-semantic-conventions = { version = "0.30", features = ["semconv_experimental"] }
4949
rustls-native-certs = { version = "0.8.1", optional = true }
5050
axum = { version = "0.8.4", default-features = false, features = ["default"] }
5151
ustr = { version = "1.0.0", default-features = false }

bottlecap/src/otlp/transform.rs

Lines changed: 44 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,12 @@ use opentelemetry_proto::tonic::trace::v1::{
1515
status::StatusCode,
1616
};
1717
use opentelemetry_semantic_conventions::attribute::{
18-
DB_QUERY_TEXT, DB_STATEMENT, DB_SYSTEM_NAME, DEPLOYMENT_ENVIRONMENT,
19-
DEPLOYMENT_ENVIRONMENT_NAME, FAAS_INVOKED_NAME, FAAS_INVOKED_PROVIDER, FAAS_TRIGGER,
20-
GRAPHQL_OPERATION_NAME, GRAPHQL_OPERATION_TYPE, HTTP_ROUTE, HTTP_STATUS_CODE,
21-
MESSAGING_DESTINATION_NAME, MESSAGING_OPERATION, MESSAGING_SYSTEM, OTEL_LIBRARY_NAME,
22-
OTEL_LIBRARY_VERSION, OTEL_STATUS_CODE, OTEL_STATUS_DESCRIPTION, RPC_METHOD, RPC_SERVICE,
23-
RPC_SYSTEM,
18+
DB_COLLECTION_NAME, DB_NAMESPACE, DB_OPERATION_NAME, DB_QUERY_SUMMARY, DB_QUERY_TEXT,
19+
DB_STORED_PROCEDURE_NAME, DB_SYSTEM_NAME, DEPLOYMENT_ENVIRONMENT_NAME, FAAS_INVOKED_NAME,
20+
FAAS_INVOKED_PROVIDER, FAAS_TRIGGER, GRAPHQL_OPERATION_NAME, GRAPHQL_OPERATION_TYPE,
21+
HTTP_RESPONSE_STATUS_CODE, HTTP_ROUTE, MESSAGING_DESTINATION_NAME, MESSAGING_OPERATION_TYPE,
22+
MESSAGING_SYSTEM, OTEL_SCOPE_NAME, OTEL_SCOPE_VERSION, OTEL_STATUS_CODE,
23+
OTEL_STATUS_DESCRIPTION, RPC_METHOD, RPC_SERVICE, RPC_SYSTEM, SERVER_ADDRESS, SERVER_PORT,
2424
};
2525
use opentelemetry_semantic_conventions::resource::{SERVICE_NAME, SERVICE_VERSION}; // CONTAINER_ID, SERVICE_VERSION, TELEMETRY_SDK_LANGUAGE, TELEMETRY_SDK_VERSION,
2626
use opentelemetry_semantic_conventions::trace::{
@@ -246,6 +246,7 @@ fn get_otel_operation_name_v1(
246246
operation_name
247247
}
248248

249+
#[allow(clippy::too_many_lines)]
249250
fn get_otel_operation_name_v2(otel_span: &OtelSpan) -> String {
250251
let operation_name =
251252
get_otel_attribute_value_as_string(&otel_span.attributes, "operation.name", false);
@@ -257,32 +258,57 @@ fn get_otel_operation_name_v2(otel_span: &OtelSpan) -> String {
257258
let is_server = otel_span.kind() == SpanKind::Server;
258259

259260
// HTTP
260-
//
261-
// As opposed to Go's implementation, we don't check for the semantic convention
262-
// of `HTTP_METHOD` which mapped to `http.method` as that is deprecated.
263261
let method =
264262
get_otel_attribute_value_as_string(&otel_span.attributes, "http.request.method", false);
265263
if !method.is_empty() {
266264
if is_server {
267265
return "http.server.request".to_string();
268266
}
269-
270267
if is_client {
271268
return "http.client.request".to_string();
272269
}
273270
}
274271

275272
// Database
273+
let db_summary =
274+
get_otel_attribute_value_as_string(&otel_span.attributes, DB_QUERY_SUMMARY, true);
275+
if !db_summary.is_empty() {
276+
return db_summary;
277+
}
278+
279+
let get = |k| {
280+
let v = get_otel_attribute_value_as_string(&otel_span.attributes, k, true);
281+
(!v.is_empty()).then_some(v)
282+
};
283+
let target = get(DB_COLLECTION_NAME)
284+
.or_else(|| get(DB_STORED_PROCEDURE_NAME))
285+
.or_else(|| get(DB_NAMESPACE))
286+
.or_else(|| {
287+
let addr = get(SERVER_ADDRESS)?;
288+
let port = get(SERVER_PORT)?;
289+
Some(format!("{addr}:{port}"))
290+
})
291+
.unwrap_or_default();
292+
293+
let db_operation =
294+
get_otel_attribute_value_as_string(&otel_span.attributes, DB_OPERATION_NAME, true);
295+
if !target.is_empty() {
296+
if !db_operation.is_empty() {
297+
return format!("{db_operation} {target}");
298+
}
299+
return target;
300+
}
301+
276302
let db_system = get_otel_attribute_value_as_string(&otel_span.attributes, DB_SYSTEM_NAME, true);
277-
if !db_system.is_empty() && is_client {
278-
return format!("{db_system}.query");
303+
if !db_system.is_empty() {
304+
return db_system;
279305
}
280306

281307
// Messaging
282308
let messaging_system =
283309
get_otel_attribute_value_as_string(&otel_span.attributes, MESSAGING_SYSTEM, true);
284310
let messaging_operation =
285-
get_otel_attribute_value_as_string(&otel_span.attributes, MESSAGING_OPERATION, true);
311+
get_otel_attribute_value_as_string(&otel_span.attributes, MESSAGING_OPERATION_TYPE, true);
286312
if !messaging_system.is_empty() && !messaging_operation.is_empty() {
287313
match otel_span.kind() {
288314
SpanKind::Client | SpanKind::Server | SpanKind::Consumer | SpanKind::Producer => {
@@ -303,7 +329,6 @@ fn get_otel_operation_name_v2(otel_span: &OtelSpan) -> String {
303329
if !service.is_empty() {
304330
return format!("aws.{service}.request");
305331
}
306-
307332
return "aws.client.request".to_string();
308333
}
309334

@@ -403,7 +428,7 @@ pub fn get_otel_resource(otel_span: &OtelSpan, otel_res: &OtelResource) -> Strin
403428
let messaging_operation = get_otel_attribute_value_as_string_from_resource_or_span(
404429
otel_res,
405430
otel_span,
406-
MESSAGING_OPERATION,
431+
MESSAGING_OPERATION_TYPE,
407432
false,
408433
);
409434
if !messaging_operation.is_empty() {
@@ -467,16 +492,6 @@ pub fn get_otel_resource(otel_span: &OtelSpan, otel_res: &OtelResource) -> Strin
467492
false,
468493
);
469494
if !database_system.is_empty() {
470-
let database_statement = get_otel_attribute_value_as_string_from_resource_or_span(
471-
otel_res,
472-
otel_span,
473-
DB_STATEMENT,
474-
false,
475-
);
476-
if !database_statement.is_empty() {
477-
return database_statement;
478-
}
479-
480495
let database_query = get_otel_attribute_value_as_string_from_resource_or_span(
481496
otel_res,
482497
otel_span,
@@ -541,7 +556,7 @@ fn get_otel_status_code(otel_span: &OtelSpan) -> u32 {
541556
}
542557

543558
status_code =
544-
get_otel_attribute_value_as_string(&otel_span.attributes, HTTP_STATUS_CODE, false);
559+
get_otel_attribute_value_as_string(&otel_span.attributes, HTTP_RESPONSE_STATUS_CODE, false);
545560
if !status_code.is_empty() {
546561
if let Ok(status_code) = status_code.parse::<u32>() {
547562
return status_code;
@@ -926,24 +941,13 @@ pub fn otel_span_to_dd_span(
926941
.insert("http.status_code".to_string(), f64::from(http_status_code));
927942
}
928943

929-
// Map OTEL deployment.environment to Datadog env field with fallback
930944
if !dd_span.meta.contains_key("env") {
931-
// Try new standard first: deployment.environment.name
932-
let mut env = get_otel_attribute_value_as_string(
945+
let env = get_otel_attribute_value_as_string(
933946
&otel_res.attributes,
934947
DEPLOYMENT_ENVIRONMENT_NAME,
935948
true,
936949
);
937950

938-
// Fallback to deprecated deployment.environment if new standard not found
939-
if env.is_empty() {
940-
env = get_otel_attribute_value_as_string(
941-
&otel_res.attributes,
942-
DEPLOYMENT_ENVIRONMENT,
943-
true,
944-
);
945-
}
946-
947951
if !env.is_empty() {
948952
dd_span.meta.insert("env".to_string(), env);
949953
}
@@ -1072,13 +1076,13 @@ pub fn otel_span_to_dd_span(
10721076
if !lib.name.is_empty() {
10731077
dd_span
10741078
.meta
1075-
.insert(OTEL_LIBRARY_NAME.to_string(), lib.name.clone());
1079+
.insert(OTEL_SCOPE_NAME.to_string(), lib.name.clone());
10761080
}
10771081

10781082
if !lib.version.is_empty() {
10791083
dd_span
10801084
.meta
1081-
.insert(OTEL_LIBRARY_VERSION.to_string(), lib.version.clone());
1085+
.insert(OTEL_SCOPE_VERSION.to_string(), lib.version.clone());
10821086
}
10831087

10841088
if let Some(status) = &otel_span.status {

0 commit comments

Comments
 (0)