use std::collections::HashMap;
use std::time::{SystemTime, UNIX_EPOCH};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SpanStatus {
Unset,
Ok,
Error,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SpanKind {
Internal,
Server,
Client,
Producer,
Consumer,
}
#[derive(Debug, Clone)]
pub struct Span {
pub trace_id: String,
pub span_id: String,
pub parent_span_id: Option<String>,
pub name: String,
pub kind: SpanKind,
pub start_time_ns: u64,
pub end_time_ns: Option<u64>,
pub status: SpanStatus,
pub status_message: Option<String>,
pub attributes: HashMap<String, AttributeValue>,
pub events: Vec<SpanEvent>,
}
#[derive(Debug, Clone)]
pub struct SpanEvent {
pub name: String,
pub timestamp_ns: u64,
pub attributes: HashMap<String, AttributeValue>,
}
#[derive(Debug, Clone)]
pub enum AttributeValue {
String(String),
Int(i64),
Float(f64),
Bool(bool),
StringArray(Vec<String>),
IntArray(Vec<i64>),
FloatArray(Vec<f64>),
BoolArray(Vec<bool>),
}
impl From<String> for AttributeValue {
fn from(s: String) -> Self {
AttributeValue::String(s)
}
}
impl From<&str> for AttributeValue {
fn from(s: &str) -> Self {
AttributeValue::String(s.to_string())
}
}
impl From<i64> for AttributeValue {
fn from(i: i64) -> Self {
AttributeValue::Int(i)
}
}
impl From<f64> for AttributeValue {
fn from(f: f64) -> Self {
AttributeValue::Float(f)
}
}
impl From<bool> for AttributeValue {
fn from(b: bool) -> Self {
AttributeValue::Bool(b)
}
}
impl Span {
pub fn new(name: impl Into<String>) -> Self {
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_nanos() as u64;
Self {
trace_id: generate_trace_id(),
span_id: generate_span_id(),
parent_span_id: None,
name: name.into(),
kind: SpanKind::Internal,
start_time_ns: now,
end_time_ns: None,
status: SpanStatus::Unset,
status_message: None,
attributes: HashMap::new(),
events: Vec::new(),
}
}
pub fn kind(mut self, kind: SpanKind) -> Self {
self.kind = kind;
self
}
pub fn parent(mut self, parent_span_id: impl Into<String>) -> Self {
self.parent_span_id = Some(parent_span_id.into());
self
}
pub fn attribute(mut self, key: impl Into<String>, value: impl Into<AttributeValue>) -> Self {
self.attributes.insert(key.into(), value.into());
self
}
pub fn event(mut self, name: impl Into<String>) -> Self {
self.add_event(name);
self
}
pub fn add_event(&mut self, name: impl Into<String>) {
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_nanos() as u64;
self.events.push(SpanEvent {
name: name.into(),
timestamp_ns: now,
attributes: HashMap::new(),
});
}
pub fn end_ok(mut self) -> Self {
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_nanos() as u64;
self.end_time_ns = Some(now);
self.status = SpanStatus::Ok;
self
}
pub fn end_error(mut self, message: impl Into<String>) -> Self {
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_nanos() as u64;
self.end_time_ns = Some(now);
self.status = SpanStatus::Error;
self.status_message = Some(message.into());
self
}
pub fn duration_ms(&self) -> Option<u64> {
self.end_time_ns
.map(|end| (end - self.start_time_ns) / 1_000_000)
}
}
pub(super) fn generate_trace_id() -> String {
use std::time::{SystemTime, UNIX_EPOCH};
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_nanos();
let random: u64 = (now as u64)
.wrapping_mul(6364136223846793005)
.wrapping_add(1);
format!("{:016x}{:016x}", now as u64, random)
}
pub(super) fn generate_span_id() -> String {
use std::time::{SystemTime, UNIX_EPOCH};
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_nanos();
let random: u64 = (now as u64)
.wrapping_mul(6364136223846793005)
.wrapping_add(1442695040888963407);
format!("{:016x}", random)
}