use crate::utils::{ParentSpanId, SpanId, TraceId, TraceState};
use alloc::boxed::Box;
use alloc::vec::Vec;
use core::fmt::{self, Display, Formatter};
use ref_str::StaticRefStr;
use super::{Report, SeverityState, types::AttachmentValue};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TraceEventLevel {
Trace,
Debug,
Info,
Warn,
Error,
}
impl TraceEventLevel {
pub fn as_str(&self) -> &'static str {
match self {
Self::Trace => "trace",
Self::Debug => "debug",
Self::Info => "info",
Self::Warn => "warn",
Self::Error => "error",
}
}
}
impl Display for TraceEventLevel {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.as_str())
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct TraceEventAttribute {
pub key: StaticRefStr,
pub value: AttachmentValue,
}
#[derive(Debug, Clone, PartialEq)]
pub struct TraceEvent {
pub name: StaticRefStr,
pub level: Option<TraceEventLevel>,
pub timestamp_unix_nano: Option<u64>,
pub attributes: Vec<TraceEventAttribute>,
}
impl Default for TraceEvent {
fn default() -> Self {
Self {
name: "".into(),
level: None,
timestamp_unix_nano: None,
attributes: Vec::new(),
}
}
}
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct TraceContext {
pub trace_id: Option<TraceId>,
pub span_id: Option<SpanId>,
pub parent_span_id: Option<ParentSpanId>,
pub sampled: Option<bool>,
pub trace_state: Option<TraceState>,
}
impl TraceContext {
pub fn is_empty(&self) -> bool {
self.trace_id.is_none()
&& self.span_id.is_none()
&& self.parent_span_id.is_none()
&& self.sampled.is_none()
&& self.trace_state.is_none()
}
}
#[derive(Debug, Default, Clone, PartialEq)]
pub(crate) struct ReportTraceInner {
context: TraceContext,
events: Vec<TraceEvent>,
}
impl ReportTraceInner {
fn is_empty(&self) -> bool {
self.context.is_empty() && self.events.is_empty()
}
}
#[derive(Debug, Clone, PartialEq, Default)]
pub struct ReportTrace {
inner: Option<Box<ReportTraceInner>>,
}
impl ReportTrace {
pub fn new() -> Self {
Self::default()
}
pub fn is_empty(&self) -> bool {
self.inner.as_ref().is_none_or(|inner| inner.is_empty())
}
pub fn context(&self) -> Option<&TraceContext> {
self.inner.as_ref().map(|inner| &inner.context)
}
pub fn events(&self) -> Option<&[TraceEvent]> {
self.inner.as_ref().map(|inner| inner.events.as_slice())
}
fn ensure_inner(&mut self) -> &mut ReportTraceInner {
self.inner
.get_or_insert_with(|| Box::new(ReportTraceInner::default()))
}
pub fn context_mut(&mut self) -> Option<&mut TraceContext> {
self.inner.as_mut().map(|inner| &mut inner.context)
}
pub fn set_context(mut self, context: TraceContext) -> Self {
self.ensure_inner().context = context;
self
}
pub fn with_context(mut self, context: TraceContext) -> Self {
if self.context().is_none() || self.context().is_none_or(|c| c.is_empty()) {
self.ensure_inner().context = context;
}
self
}
pub fn with_event(mut self, event: TraceEvent) -> Self {
self.ensure_inner().events.push(event);
self
}
pub fn set_trace_id(mut self, trace_id: TraceId) -> Self {
self.ensure_inner().context.trace_id = Some(trace_id);
self
}
pub fn with_trace_id(mut self, trace_id: TraceId) -> Self {
if self.context().and_then(|c| c.trace_id.as_ref()).is_none() {
self.ensure_inner().context.trace_id = Some(trace_id);
}
self
}
pub fn set_span_id(mut self, span_id: SpanId) -> Self {
self.ensure_inner().context.span_id = Some(span_id);
self
}
pub fn with_span_id(mut self, span_id: SpanId) -> Self {
if self.context().and_then(|c| c.span_id.as_ref()).is_none() {
self.ensure_inner().context.span_id = Some(span_id);
}
self
}
pub fn set_parent_span_id(mut self, parent_span_id: ParentSpanId) -> Self {
self.ensure_inner().context.parent_span_id = Some(parent_span_id);
self
}
pub fn with_parent_span_id(mut self, parent_span_id: ParentSpanId) -> Self {
if self
.context()
.and_then(|c| c.parent_span_id.as_ref())
.is_none()
{
self.ensure_inner().context.parent_span_id = Some(parent_span_id);
}
self
}
pub fn set_sampled(mut self, sampled: bool) -> Self {
let inner = self.ensure_inner();
inner.context.sampled = Some(sampled);
self
}
pub fn with_sampled(mut self, sampled: bool) -> Self {
if self.context().and_then(|c| c.sampled).is_none() {
let inner = self.ensure_inner();
inner.context.sampled = Some(sampled);
}
self
}
pub fn set_trace_state(mut self, trace_state: impl Into<StaticRefStr>) -> Self {
self.ensure_inner().context.trace_state = Some(TraceState::from(trace_state.into()));
self
}
pub fn with_trace_state(mut self, trace_state: impl Into<StaticRefStr>) -> Self {
if self
.context()
.and_then(|c| c.trace_state.as_ref())
.is_none()
{
self.ensure_inner().context.trace_state = Some(TraceState::from(trace_state.into()));
}
self
}
pub fn set_trace_id_opt(mut self, trace_id: Option<TraceId>) -> Self {
if let Some(tid) = trace_id
&& self.context().and_then(|c| c.trace_id.as_ref()).is_none()
{
self.ensure_inner().context.trace_id = Some(tid);
}
self
}
pub fn set_span_id_opt(mut self, span_id: Option<SpanId>) -> Self {
if let Some(sid) = span_id
&& self.context().and_then(|c| c.span_id.as_ref()).is_none()
{
self.ensure_inner().context.span_id = Some(sid);
}
self
}
pub fn set_parent_span_id_opt(mut self, parent_span_id: Option<ParentSpanId>) -> Self {
if let Some(psid) = parent_span_id
&& self
.context()
.and_then(|c| c.parent_span_id.as_ref())
.is_none()
{
self.ensure_inner().context.parent_span_id = Some(psid);
}
self
}
pub fn set_sampled_opt(mut self, sampled: Option<bool>) -> Self {
if let Some(s) = sampled
&& self.context().and_then(|c| c.sampled).is_none()
{
let inner = self.ensure_inner();
inner.context.sampled = Some(s);
}
self
}
pub fn set_trace_state_opt(mut self, trace_state: Option<TraceState>) -> Self {
if let Some(ts) = trace_state
&& self
.context()
.and_then(|c| c.trace_state.as_ref())
.is_none()
{
self.ensure_inner().context.trace_state = Some(ts);
}
self
}
fn events_mut(&mut self) -> &mut Vec<TraceEvent> {
&mut self.ensure_inner().events
}
}
impl<E, State> Report<E, State>
where
State: SeverityState,
{
pub fn trace(&self) -> &ReportTrace {
&self.data.trace
}
pub fn set_trace(mut self, trace: ReportTrace) -> Self {
self.data.trace = trace;
self
}
pub fn with_trace(mut self, trace: ReportTrace) -> Self {
if self.trace().is_empty() {
self.data.trace = trace;
}
self
}
pub fn set_trace_ids(mut self, trace_id: TraceId, span_id: SpanId) -> Self {
let inner = self.trace_mut().ensure_inner();
inner.context.trace_id = Some(trace_id);
inner.context.span_id = Some(span_id);
self
}
pub fn with_trace_ids(mut self, trace_id: TraceId, span_id: SpanId) -> Self {
let trace_ref = self.trace();
let needs_trace_id = trace_ref.is_empty()
|| trace_ref
.context()
.and_then(|c| c.trace_id.as_ref())
.is_none();
let needs_span_id = trace_ref.is_empty()
|| trace_ref
.context()
.and_then(|c| c.span_id.as_ref())
.is_none();
if needs_trace_id {
self.trace_mut().ensure_inner().context.trace_id = Some(trace_id);
}
if needs_span_id {
self.trace_mut().ensure_inner().context.span_id = Some(span_id);
}
self
}
pub fn set_parent_span_id(mut self, parent_span_id: ParentSpanId) -> Self {
self.trace_mut().ensure_inner().context.parent_span_id = Some(parent_span_id);
self
}
pub fn with_parent_span_id(mut self, parent_span_id: ParentSpanId) -> Self {
if self
.trace()
.context()
.and_then(|c| c.parent_span_id.as_ref())
.is_none()
{
self.trace_mut().ensure_inner().context.parent_span_id = Some(parent_span_id);
}
self
}
pub fn set_trace_sampled(mut self, sampled: bool) -> Self {
let inner = self.trace_mut().ensure_inner();
inner.context.sampled = Some(sampled);
self
}
pub fn with_trace_sampled(mut self, sampled: bool) -> Self {
if self.trace().context().and_then(|c| c.sampled).is_none() {
let inner = self.trace_mut().ensure_inner();
inner.context.sampled = Some(sampled);
}
self
}
pub fn set_trace_state(mut self, trace_state: impl Into<StaticRefStr>) -> Self {
self.trace_mut().ensure_inner().context.trace_state =
Some(TraceState::from(trace_state.into()));
self
}
pub fn with_trace_state(mut self, trace_state: impl Into<StaticRefStr>) -> Self {
if self
.trace()
.context()
.and_then(|c| c.trace_state.as_ref())
.is_none()
{
self.trace_mut().ensure_inner().context.trace_state =
Some(TraceState::from(trace_state.into()));
}
self
}
pub fn with_trace_event(mut self, event: TraceEvent) -> Self {
self.trace_mut().events_mut().push(event);
self
}
pub fn push_trace_event(mut self, name: impl Into<StaticRefStr>) -> Self {
self.trace_mut().events_mut().push(TraceEvent {
name: name.into(),
..TraceEvent::default()
});
self
}
pub fn push_trace_event_with(
mut self,
name: impl Into<StaticRefStr>,
level: Option<TraceEventLevel>,
timestamp_unix_nano: Option<u64>,
attributes: impl IntoIterator<Item = TraceEventAttribute>,
) -> Self {
self.trace_mut().events_mut().push(TraceEvent {
name: name.into(),
level,
timestamp_unix_nano,
attributes: attributes.into_iter().collect::<Vec<_>>(),
});
self
}
fn trace_mut(&mut self) -> &mut ReportTrace {
&mut self.data.trace
}
}