use std::collections::BTreeMap;
use std::time::Instant;
use crate::hub::Hub;
use crate::protocol::SpanRecord;
use crate::util;
pub struct Span {
trace_id: String,
span_id: String,
parent_span_id: Option<String>,
operation: String,
description: Option<String>,
status: Option<String>,
tags: BTreeMap<String, String>,
data: Option<serde_json::Value>,
start_millis: u64,
started: Instant,
finished: bool,
}
impl Span {
pub fn transaction(operation: impl Into<String>, name: impl Into<String>) -> Span {
Span {
trace_id: util::new_trace_id(),
span_id: util::new_span_id(),
parent_span_id: None,
operation: operation.into(),
description: Some(name.into()),
status: None,
tags: BTreeMap::new(),
data: None,
start_millis: util::now_millis(),
started: Instant::now(),
finished: false,
}
}
pub fn start_child(
&self,
operation: impl Into<String>,
description: impl Into<String>,
) -> Span {
Span {
trace_id: self.trace_id.clone(),
span_id: util::new_span_id(),
parent_span_id: Some(self.span_id.clone()),
operation: operation.into(),
description: Some(description.into()),
status: None,
tags: BTreeMap::new(),
data: None,
start_millis: util::now_millis(),
started: Instant::now(),
finished: false,
}
}
pub fn continued(
operation: impl Into<String>,
name: impl Into<String>,
trace_id: Option<String>,
parent_span_id: Option<String>,
) -> Span {
Span {
trace_id: trace_id.unwrap_or_else(util::new_trace_id),
span_id: util::new_span_id(),
parent_span_id,
operation: operation.into(),
description: Some(name.into()),
status: None,
tags: BTreeMap::new(),
data: None,
start_millis: util::now_millis(),
started: Instant::now(),
finished: false,
}
}
pub fn trace_id(&self) -> &str {
&self.trace_id
}
pub fn span_id(&self) -> &str {
&self.span_id
}
pub fn set_status(&mut self, status: impl Into<String>) {
self.status = Some(status.into());
}
pub fn set_tag(&mut self, key: impl Into<String>, value: impl Into<String>) {
self.tags.insert(key.into(), value.into());
}
pub fn set_data(&mut self, data: serde_json::Value) {
self.data = Some(data);
}
pub fn finish(mut self) {
self.send();
}
fn send(&mut self) {
if self.finished {
return;
}
self.finished = true;
let hub = Hub::current();
let Some(client) = hub.client() else {
return;
};
let opts = client.options();
let end_millis = util::now_millis();
let record = SpanRecord {
trace_id: self.trace_id.clone(),
span_id: self.span_id.clone(),
parent_span_id: self.parent_span_id.clone(),
operation: self.operation.clone(),
description: self.description.clone(),
status: self.status.clone(),
duration_ms: self.started.elapsed().as_millis() as u64,
start_time_millis: self.start_millis,
end_time_millis: end_millis,
service: opts.server_name.clone(),
environment: Some(opts.resolved_environment()),
tags: if self.tags.is_empty() {
None
} else {
Some(self.tags.clone())
},
data: self.data.clone(),
};
client.capture_span(record);
}
}
impl Drop for Span {
fn drop(&mut self) {
self.send();
}
}
pub fn start_transaction(operation: impl Into<String>, name: impl Into<String>) -> Span {
Span::transaction(operation, name)
}
pub fn start_span(operation: impl Into<String>, description: impl Into<String>) -> Span {
Span::transaction(operation, description)
}