use crate::span::v04::{Span, SpanEvent, SpanLink};
use crate::span::{TraceData, SPAN_LINK_FLAGS_SET_SENTINEL};
use serde::ser::{SerializeSeq, SerializeStruct};
use serde::{Serialize, Serializer};
use std::borrow::Borrow;
const HEX_DIGITS: &[u8; 16] = b"0123456789abcdef";
fn fill_hex(out: &mut [u8], value: u64) {
for i in 0..16 {
out[15 - i] = HEX_DIGITS[((value >> (i * 4)) & 0xf) as usize];
}
}
fn serialize_ascii_hex<S: Serializer>(serializer: S, buf: &[u8]) -> Result<S::Ok, S::Error> {
match std::str::from_utf8(buf) {
Ok(text) => serializer.serialize_str(text),
Err(_) => serializer.serialize_str(""),
}
}
pub(super) struct HexU64(pub(super) u64);
impl Serialize for HexU64 {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut buf = [0u8; 16];
fill_hex(&mut buf, self.0);
serialize_ascii_hex(serializer, &buf)
}
}
pub(super) struct HexTraceId(pub(super) u128);
impl Serialize for HexTraceId {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let high = (self.0 >> 64) as u64;
let low = self.0 as u64;
if high == 0 {
let mut buf = [0u8; 16];
fill_hex(&mut buf, low);
serialize_ascii_hex(serializer, &buf)
} else {
let mut buf = [0u8; 32];
fill_hex(&mut buf[0..16], high);
fill_hex(&mut buf[16..32], low);
serialize_ascii_hex(serializer, &buf)
}
}
}
struct LogSpanLink<'a, T: TraceData>(&'a SpanLink<T>);
impl<T: TraceData> Serialize for LogSpanLink<'_, T> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let link = self.0;
let has_attributes = !link.attributes.is_empty();
let has_tracestate = !Borrow::<str>::borrow(&link.tracestate).is_empty();
let has_flags = link.flags != 0;
let mut len = 3;
len += has_attributes as usize;
len += has_tracestate as usize;
len += has_flags as usize;
let mut state = serializer.serialize_struct("span_link", len)?;
state.serialize_field("trace_id", &link.trace_id)?;
state.serialize_field("trace_id_high", &link.trace_id_high)?;
state.serialize_field("span_id", &link.span_id)?;
if has_attributes {
state.serialize_field("attributes", &link.attributes)?;
}
if has_tracestate {
state.serialize_field("tracestate", &link.tracestate)?;
}
if has_flags {
state.serialize_field("flags", &(link.flags & !SPAN_LINK_FLAGS_SET_SENTINEL))?;
}
state.end()
}
}
struct LogSpanEvent<'a, T: TraceData>(&'a SpanEvent<T>);
impl<T: TraceData> Serialize for LogSpanEvent<'_, T> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let event = self.0;
let has_attributes = !event.attributes.is_empty();
let mut len = 2;
len += has_attributes as usize;
let mut state = serializer.serialize_struct("span_event", len)?;
state.serialize_field("time_unix_nano", &event.time_unix_nano)?;
state.serialize_field("name", &event.name)?;
if has_attributes {
state.serialize_field("attributes", &event.attributes)?;
}
state.end()
}
}
struct LogSeq<'a, I, W>(&'a [I], fn(&'a I) -> W);
impl<I, W: Serialize> Serialize for LogSeq<'_, I, W> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut seq = serializer.serialize_seq(Some(self.0.len()))?;
for item in self.0 {
seq.serialize_element(&(self.1)(item))?;
}
seq.end()
}
}
pub(crate) struct LogSpan<'a, T: TraceData>(pub &'a Span<T>);
impl<T: TraceData> Serialize for LogSpan<'_, T> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let span = self.0;
let has_type = !Borrow::<str>::borrow(&span.r#type).is_empty();
let has_meta = !span.meta.is_empty();
let has_metrics = !span.metrics.is_empty();
let has_links = !span.span_links.is_empty();
let has_events = !span.span_events.is_empty();
let mut len = 9;
len += has_type as usize;
len += has_meta as usize;
len += has_metrics as usize;
len += has_links as usize;
len += has_events as usize;
let mut state = serializer.serialize_struct("span", len)?;
state.serialize_field("trace_id", &HexTraceId(span.trace_id))?;
state.serialize_field("span_id", &HexU64(span.span_id))?;
state.serialize_field("parent_id", &HexU64(span.parent_id))?;
state.serialize_field("service", &span.service)?;
state.serialize_field("name", &span.name)?;
state.serialize_field("resource", &span.resource)?;
if has_type {
state.serialize_field("type", &span.r#type)?;
}
state.serialize_field("error", &span.error)?;
state.serialize_field("start", &span.start)?;
state.serialize_field("duration", &span.duration)?;
if has_meta {
state.serialize_field("meta", &span.meta)?;
}
if has_metrics {
state.serialize_field("metrics", &span.metrics)?;
}
if has_links {
state.serialize_field("span_links", &LogSeq(&span.span_links, LogSpanLink))?;
}
if has_events {
state.serialize_field("span_events", &LogSeq(&span.span_events, LogSpanEvent))?;
}
state.end()
}
}