Skip to content

Commit 002497b

Browse files
authored
opentelemetry: set span kind through otel.kind (#890)
OpenTelemetry supports a span kind as special metadata to spans. The span kind defines relationships between spans (e.g. server vs client). Some distributed tracing providers use this information to enrich their UIs. This makes it possible to set the span kind by adding a special attribute called `otel.kind`, similar to the existing `otel.name`.
1 parent 0da8e3f commit 002497b

File tree

2 files changed

+41
-1
lines changed

2 files changed

+41
-1
lines changed

tracing-opentelemetry/src/layer.rs

+34
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use tracing_subscriber::registry::LookupSpan;
1313
use tracing_subscriber::Layer;
1414

1515
static SPAN_NAME_FIELD: &str = "otel.name";
16+
static SPAN_KIND_FIELD: &str = "otel.kind";
1617

1718
/// An [OpenTelemetry] propagation layer for use in a project that uses
1819
/// [tracing].
@@ -133,6 +134,22 @@ pub(crate) fn build_span_context(
133134
api::SpanContext::new(trace_id, span_id, trace_flags, false)
134135
}
135136

137+
fn str_to_span_kind(s: &str) -> Option<api::SpanKind> {
138+
if s.eq_ignore_ascii_case("SERVER") {
139+
Some(api::SpanKind::Server)
140+
} else if s.eq_ignore_ascii_case("CLIENT") {
141+
Some(api::SpanKind::Client)
142+
} else if s.eq_ignore_ascii_case("PRODUCER") {
143+
Some(api::SpanKind::Producer)
144+
} else if s.eq_ignore_ascii_case("CONSUMER") {
145+
Some(api::SpanKind::Consumer)
146+
} else if s.eq_ignore_ascii_case("INTERNAL") {
147+
Some(api::SpanKind::Internal)
148+
} else {
149+
None
150+
}
151+
}
152+
136153
struct SpanEventVisitor<'a>(&'a mut api::Event);
137154

138155
impl<'a> field::Visit for SpanEventVisitor<'a> {
@@ -179,6 +196,8 @@ impl<'a> field::Visit for SpanAttributeVisitor<'a> {
179196
fn record_str(&mut self, field: &field::Field, value: &str) {
180197
if field.name() == SPAN_NAME_FIELD {
181198
self.0.name = value.to_string();
199+
} else if field.name() == SPAN_KIND_FIELD {
200+
self.0.span_kind = str_to_span_kind(value);
182201
} else {
183202
let attribute = api::KeyValue::new(field.name(), value);
184203
if let Some(attributes) = &mut self.0.attributes {
@@ -196,6 +215,8 @@ impl<'a> field::Visit for SpanAttributeVisitor<'a> {
196215
fn record_debug(&mut self, field: &field::Field, value: &dyn fmt::Debug) {
197216
if field.name() == SPAN_NAME_FIELD {
198217
self.0.name = format!("{:?}", value);
218+
} else if field.name() == SPAN_KIND_FIELD {
219+
self.0.span_kind = str_to_span_kind(&format!("{:?}", value));
199220
} else {
200221
let attribute = api::Key::new(field.name()).string(format!("{:?}", value));
201222
if let Some(attributes) = &mut self.0.attributes {
@@ -594,4 +615,17 @@ mod tests {
594615
let recorded_name = tracer.0.lock().unwrap().as_ref().map(|b| b.name.clone());
595616
assert_eq!(recorded_name, Some(dynamic_name))
596617
}
618+
619+
#[test]
620+
fn span_kind() {
621+
let tracer = TestTracer(Arc::new(Mutex::new(None)));
622+
let subscriber = tracing_subscriber::registry().with(layer().with_tracer(tracer.clone()));
623+
624+
tracing::subscriber::with_default(subscriber, || {
625+
tracing::debug_span!("request", otel.kind = "Server");
626+
});
627+
628+
let recorded_kind = tracer.0.lock().unwrap().as_ref().unwrap().span_kind.clone();
629+
assert_eq!(recorded_kind, Some(api::SpanKind::Server))
630+
}
597631
}

tracing-opentelemetry/src/lib.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,18 @@
1818
//! * `otel.name`: Override the span name sent to OpenTelemetry exporters.
1919
//! Setting this field is useful if you want to display non-static information
2020
//! in your span name.
21+
//! * `otel.kind`: Set the span kind to one of the supported OpenTelemetry
22+
//! [span kinds]. The value should be a string of any of the supported values:
23+
//! `SERVER`, `CLIENT`, `PRODUCER`, `CONSUMER` or `INTERNAL`. Other values are
24+
//! silently ignored.
25+
//!
26+
//! [span kinds]: https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/api.md#spankind
2127
//!
2228
//! ### Semantic Conventions
2329
//!
2430
//! OpenTelemetry defines conventional names for attributes of common
2531
//! operations. These names can be assigned directly as fields, e.g.
26-
//! `trace_span!("request", "span.kind" = "client", "http.url" = ..)`, and they
32+
//! `trace_span!("request", "otel.kind" = "client", "http.url" = ..)`, and they
2733
//! will be passed through to your configured OpenTelemetry exporter. You can
2834
//! find the full list of the operations and their expected field names in the
2935
//! [semantic conventions] spec.

0 commit comments

Comments
 (0)