mirror of
https://github.com/tokio-rs/tracing.git
synced 2025-10-02 07:20:35 +00:00
subscriber: add span status fields (#1351)
## Motivation Allow users to set custom span status codes and messages to follow opentelemetry semantic conventions. ## Solution Support the status code and status message fields in `OpenTelemetrySubscriber` Proposal to close #1346
This commit is contained in:
parent
3d65a48526
commit
b3490ffc76
@ -12,8 +12,10 @@ use tracing_subscriber::layer::Context;
|
|||||||
use tracing_subscriber::registry::LookupSpan;
|
use tracing_subscriber::registry::LookupSpan;
|
||||||
use tracing_subscriber::Layer;
|
use tracing_subscriber::Layer;
|
||||||
|
|
||||||
static SPAN_NAME_FIELD: &str = "otel.name";
|
const SPAN_NAME_FIELD: &str = "otel.name";
|
||||||
static SPAN_KIND_FIELD: &str = "otel.kind";
|
const SPAN_KIND_FIELD: &str = "otel.kind";
|
||||||
|
const SPAN_STATUS_CODE_FIELD: &str = "otel.status_code";
|
||||||
|
const SPAN_STATUS_MESSAGE_FIELD: &str = "otel.status_message";
|
||||||
|
|
||||||
/// An [OpenTelemetry] propagation layer for use in a project that uses
|
/// An [OpenTelemetry] propagation layer for use in a project that uses
|
||||||
/// [tracing].
|
/// [tracing].
|
||||||
@ -85,18 +87,22 @@ impl WithContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn str_to_span_kind(s: &str) -> Option<otel::SpanKind> {
|
fn str_to_span_kind(s: &str) -> Option<otel::SpanKind> {
|
||||||
if s.eq_ignore_ascii_case("SERVER") {
|
match s {
|
||||||
Some(otel::SpanKind::Server)
|
s if s.eq_ignore_ascii_case("server") => Some(otel::SpanKind::Server),
|
||||||
} else if s.eq_ignore_ascii_case("CLIENT") {
|
s if s.eq_ignore_ascii_case("client") => Some(otel::SpanKind::Client),
|
||||||
Some(otel::SpanKind::Client)
|
s if s.eq_ignore_ascii_case("producer") => Some(otel::SpanKind::Producer),
|
||||||
} else if s.eq_ignore_ascii_case("PRODUCER") {
|
s if s.eq_ignore_ascii_case("consumer") => Some(otel::SpanKind::Consumer),
|
||||||
Some(otel::SpanKind::Producer)
|
s if s.eq_ignore_ascii_case("internal") => Some(otel::SpanKind::Internal),
|
||||||
} else if s.eq_ignore_ascii_case("CONSUMER") {
|
_ => None,
|
||||||
Some(otel::SpanKind::Consumer)
|
}
|
||||||
} else if s.eq_ignore_ascii_case("INTERNAL") {
|
}
|
||||||
Some(otel::SpanKind::Internal)
|
|
||||||
} else {
|
fn str_to_status_code(s: &str) -> Option<otel::StatusCode> {
|
||||||
None
|
match s {
|
||||||
|
s if s.eq_ignore_ascii_case("unset") => Some(otel::StatusCode::Unset),
|
||||||
|
s if s.eq_ignore_ascii_case("ok") => Some(otel::StatusCode::Ok),
|
||||||
|
s if s.eq_ignore_ascii_case("error") => Some(otel::StatusCode::Error),
|
||||||
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -200,16 +206,18 @@ impl<'a> field::Visit for SpanAttributeVisitor<'a> {
|
|||||||
///
|
///
|
||||||
/// [`Span`]: opentelemetry::trace::Span
|
/// [`Span`]: opentelemetry::trace::Span
|
||||||
fn record_str(&mut self, field: &field::Field, value: &str) {
|
fn record_str(&mut self, field: &field::Field, value: &str) {
|
||||||
if field.name() == SPAN_NAME_FIELD {
|
match field.name() {
|
||||||
self.0.name = value.to_string().into();
|
SPAN_NAME_FIELD => self.0.name = value.to_string().into(),
|
||||||
} else if field.name() == SPAN_KIND_FIELD {
|
SPAN_KIND_FIELD => self.0.span_kind = str_to_span_kind(value),
|
||||||
self.0.span_kind = str_to_span_kind(value);
|
SPAN_STATUS_CODE_FIELD => self.0.status_code = str_to_status_code(value),
|
||||||
} else {
|
SPAN_STATUS_MESSAGE_FIELD => self.0.status_message = Some(value.to_owned()),
|
||||||
let attribute = KeyValue::new(field.name(), value.to_string());
|
_ => {
|
||||||
if let Some(attributes) = &mut self.0.attributes {
|
let attribute = KeyValue::new(field.name(), value.to_string());
|
||||||
attributes.push(attribute);
|
if let Some(attributes) = &mut self.0.attributes {
|
||||||
} else {
|
attributes.push(attribute);
|
||||||
self.0.attributes = Some(vec![attribute]);
|
} else {
|
||||||
|
self.0.attributes = Some(vec![attribute]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -219,16 +227,20 @@ impl<'a> field::Visit for SpanAttributeVisitor<'a> {
|
|||||||
///
|
///
|
||||||
/// [`Span`]: opentelemetry::trace::Span
|
/// [`Span`]: opentelemetry::trace::Span
|
||||||
fn record_debug(&mut self, field: &field::Field, value: &dyn fmt::Debug) {
|
fn record_debug(&mut self, field: &field::Field, value: &dyn fmt::Debug) {
|
||||||
if field.name() == SPAN_NAME_FIELD {
|
match field.name() {
|
||||||
self.0.name = format!("{:?}", value).into();
|
SPAN_NAME_FIELD => self.0.name = format!("{:?}", value).into(),
|
||||||
} else if field.name() == SPAN_KIND_FIELD {
|
SPAN_KIND_FIELD => self.0.span_kind = str_to_span_kind(&format!("{:?}", value)),
|
||||||
self.0.span_kind = str_to_span_kind(&format!("{:?}", value));
|
SPAN_STATUS_CODE_FIELD => {
|
||||||
} else {
|
self.0.status_code = str_to_status_code(&format!("{:?}", value))
|
||||||
let attribute = Key::new(field.name()).string(format!("{:?}", value));
|
}
|
||||||
if let Some(attributes) = &mut self.0.attributes {
|
SPAN_STATUS_MESSAGE_FIELD => self.0.status_message = Some(format!("{:?}", value)),
|
||||||
attributes.push(attribute);
|
_ => {
|
||||||
} else {
|
let attribute = Key::new(field.name()).string(format!("{:?}", value));
|
||||||
self.0.attributes = Some(vec![attribute]);
|
if let Some(attributes) = &mut self.0.attributes {
|
||||||
|
attributes.push(attribute);
|
||||||
|
} else {
|
||||||
|
self.0.attributes = Some(vec![attribute]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -676,6 +688,43 @@ mod tests {
|
|||||||
assert_eq!(recorded_kind, Some(otel::SpanKind::Server))
|
assert_eq!(recorded_kind, Some(otel::SpanKind::Server))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn span_status_code() {
|
||||||
|
let tracer = TestTracer(Arc::new(Mutex::new(None)));
|
||||||
|
let subscriber =
|
||||||
|
tracing_subscriber::registry().with(subscriber().with_tracer(tracer.clone()));
|
||||||
|
|
||||||
|
tracing::collect::with_default(subscriber, || {
|
||||||
|
tracing::debug_span!("request", otel.status_code = ?otel::StatusCode::Ok);
|
||||||
|
});
|
||||||
|
let recorded_status_code = tracer.0.lock().unwrap().as_ref().unwrap().status_code;
|
||||||
|
assert_eq!(recorded_status_code, Some(otel::StatusCode::Ok))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn span_status_message() {
|
||||||
|
let tracer = TestTracer(Arc::new(Mutex::new(None)));
|
||||||
|
let subscriber =
|
||||||
|
tracing_subscriber::registry().with(subscriber().with_tracer(tracer.clone()));
|
||||||
|
|
||||||
|
let message = "message";
|
||||||
|
|
||||||
|
tracing::collect::with_default(subscriber, || {
|
||||||
|
tracing::debug_span!("request", otel.status_message = message);
|
||||||
|
});
|
||||||
|
|
||||||
|
let recorded_status_message = tracer
|
||||||
|
.0
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.status_message
|
||||||
|
.clone();
|
||||||
|
|
||||||
|
assert_eq!(recorded_status_message, Some(message.to_string()))
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn trace_id_from_existing_context() {
|
fn trace_id_from_existing_context() {
|
||||||
let tracer = TestTracer(Arc::new(Mutex::new(None)));
|
let tracer = TestTracer(Arc::new(Mutex::new(None)));
|
||||||
|
@ -23,8 +23,11 @@
|
|||||||
//! Setting this field is useful if you want to display non-static information
|
//! Setting this field is useful if you want to display non-static information
|
||||||
//! in your span name.
|
//! in your span name.
|
||||||
//! * `otel.kind`: Set the span kind to one of the supported OpenTelemetry [span kinds].
|
//! * `otel.kind`: Set the span kind to one of the supported OpenTelemetry [span kinds].
|
||||||
|
//! * `otel.status_code`: Set the span status code to one of the supported OpenTelemetry [span status codes].
|
||||||
|
//! * `otel.status_message`: Set the span status message.
|
||||||
//!
|
//!
|
||||||
//! [span kinds]: https://docs.rs/opentelemetry/latest/opentelemetry/trace/enum.SpanKind.html
|
//! [span kinds]: https://docs.rs/opentelemetry/latest/opentelemetry/trace/enum.SpanKind.html
|
||||||
|
//! [span status codes]: https://docs.rs/opentelemetry/latest/opentelemetry/trace/enum.StatusCode.html
|
||||||
//!
|
//!
|
||||||
//! ### Semantic Conventions
|
//! ### Semantic Conventions
|
||||||
//!
|
//!
|
||||||
|
@ -224,7 +224,7 @@ impl otel::Span for CompatSpan {
|
|||||||
|
|
||||||
fn set_status(&self, _code: otel::StatusCode, _message: String) {
|
fn set_status(&self, _code: otel::StatusCode, _message: String) {
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
panic!("OpenTelemetry and tracing APIs cannot be mixed, use `tracing::span!` macro or `span.record()` instead.");
|
panic!("OpenTelemetry and tracing APIs cannot be mixed, use `tracing::span!` macro or `span.record()` with `otel.status_code` and `otel.status_message` instead.");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_name(&self, _new_name: String) {
|
fn update_name(&self, _new_name: String) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user