use emit_core::{
and::And,
clock::Clock,
ctxt::Ctxt,
empty::Empty,
event::{Event, ToEvent},
extent::{Extent, ToExtent},
filter::Filter,
path::Path,
props::{ErasedProps, Props},
rng::Rng,
str::{Str, ToStr},
template::{self, Template},
timestamp::Timestamp,
value::{FromValue, ToValue, Value},
well_known::{
KEY_EVT_KIND, KEY_SPAN_ID, KEY_SPAN_NAME, KEY_SPAN_PARENT, KEY_TRACE_ID, SPAN_KIND_CLIENT,
SPAN_KIND_CONSUMER, SPAN_KIND_INTERNAL, SPAN_KIND_PRODUCER, SPAN_KIND_SERVER,
},
};
use crate::{kind::Kind, Frame, Timer};
use core::{
fmt, mem,
num::{NonZeroU128, NonZeroU64},
ops::ControlFlow,
str::{self, FromStr},
};
pub use self::completion::Completion;
#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct TraceId(NonZeroU128);
impl fmt::Debug for TraceId {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(str::from_utf8(&self.to_hex()).unwrap(), f)
}
}
impl fmt::Display for TraceId {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(str::from_utf8(&self.to_hex()).unwrap())
}
}
impl FromStr for TraceId {
type Err = ParseIdError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::try_from_hex_slice(s.as_bytes())
}
}
impl ToValue for TraceId {
fn to_value(&self) -> Value<'_> {
Value::capture_display(self)
}
}
impl<'v> FromValue<'v> for TraceId {
fn from_value(value: Value<'v>) -> Option<Self> {
value
.downcast_ref::<TraceId>()
.copied()
.or_else(|| u128::from_value(value.by_ref()).and_then(TraceId::from_u128))
.or_else(|| TraceId::try_from_hex(value).ok())
}
}
#[cfg(feature = "sval")]
impl sval::Value for TraceId {
fn stream<'sval, S: sval::Stream<'sval> + ?Sized>(&'sval self, stream: &mut S) -> sval::Result {
sval::stream_display(stream, self)
}
}
#[cfg(feature = "serde")]
impl serde::Serialize for TraceId {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.collect_str(self)
}
}
impl TraceId {
pub fn random<R: Rng>(rng: R) -> Option<Self> {
Some(TraceId::new(NonZeroU128::new(rng.gen_u128()?)?))
}
pub const fn new(v: NonZeroU128) -> Self {
TraceId(v)
}
pub fn from_u128(v: u128) -> Option<Self> {
Some(TraceId(NonZeroU128::new(v)?))
}
pub const fn to_u128(&self) -> u128 {
self.0.get()
}
pub fn from_bytes(v: [u8; 16]) -> Option<Self> {
Self::from_u128(u128::from_be_bytes(v))
}
pub fn to_bytes(&self) -> [u8; 16] {
self.0.get().to_be_bytes()
}
pub fn to_hex(&self) -> [u8; 32] {
let mut dst = [0; 32];
let src: [u8; 16] = self.0.get().to_be_bytes();
for i in 0..src.len() {
let b = src[i];
dst[i * 2] = HEX_ENCODE_TABLE[(b >> 4) as usize];
dst[i * 2 + 1] = HEX_ENCODE_TABLE[(b & 0x0f) as usize];
}
dst
}
pub fn try_from_hex_slice(hex: &[u8]) -> Result<Self, ParseIdError> {
let hex: &[u8; 32] = hex.try_into().map_err(|_| ParseIdError {})?;
let mut dst = [0; 16];
let mut i = 0;
while i < 16 {
let h1 = HEX_DECODE_TABLE[hex[i * 2] as usize];
let h2 = HEX_DECODE_TABLE[hex[i * 2 + 1] as usize];
if h1 | h2 == 0xff {
return Err(ParseIdError {});
}
dst[i] = SHL4_TABLE[h1 as usize] | h2;
i += 1;
}
Ok(TraceId::new(
NonZeroU128::new(u128::from_be_bytes(dst)).ok_or_else(|| ParseIdError {})?,
))
}
pub fn try_from_hex(hex: impl fmt::Display) -> Result<Self, ParseIdError> {
let mut buf = crate::buf::Buffer::<32>::new();
Self::try_from_hex_slice(buf.buffer(hex).map_err(|_| ParseIdError {})?)
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct SpanId(NonZeroU64);
impl fmt::Debug for SpanId {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(str::from_utf8(&self.to_hex()).unwrap(), f)
}
}
impl fmt::Display for SpanId {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(str::from_utf8(&self.to_hex()).unwrap())
}
}
impl FromStr for SpanId {
type Err = ParseIdError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::try_from_hex_slice(s.as_bytes())
}
}
impl ToValue for SpanId {
fn to_value(&self) -> Value<'_> {
Value::capture_display(self)
}
}
impl<'v> FromValue<'v> for SpanId {
fn from_value(value: Value<'v>) -> Option<Self> {
value
.downcast_ref::<SpanId>()
.copied()
.or_else(|| u64::from_value(value.by_ref()).and_then(SpanId::from_u64))
.or_else(|| SpanId::try_from_hex(value).ok())
}
}
#[cfg(feature = "sval")]
impl sval::Value for SpanId {
fn stream<'sval, S: sval::Stream<'sval> + ?Sized>(&'sval self, stream: &mut S) -> sval::Result {
sval::stream_display(stream, self)
}
}
#[cfg(feature = "serde")]
impl serde::Serialize for SpanId {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.collect_str(self)
}
}
impl SpanId {
pub fn random<R: Rng>(rng: R) -> Option<Self> {
Some(SpanId::new(NonZeroU64::new(rng.gen_u64()?)?))
}
pub const fn new(v: NonZeroU64) -> Self {
SpanId(v)
}
pub fn from_u64(v: u64) -> Option<Self> {
Some(SpanId(NonZeroU64::new(v)?))
}
pub const fn to_u64(&self) -> u64 {
self.0.get()
}
pub fn from_bytes(v: [u8; 8]) -> Option<Self> {
Self::from_u64(u64::from_be_bytes(v))
}
pub fn to_bytes(&self) -> [u8; 8] {
self.0.get().to_be_bytes()
}
pub fn to_hex(&self) -> [u8; 16] {
let mut dst = [0; 16];
let src: [u8; 8] = self.0.get().to_be_bytes();
for i in 0..src.len() {
let b = src[i];
dst[i * 2] = HEX_ENCODE_TABLE[(b >> 4) as usize];
dst[i * 2 + 1] = HEX_ENCODE_TABLE[(b & 0x0f) as usize];
}
dst
}
pub fn try_from_hex_slice(hex: &[u8]) -> Result<Self, ParseIdError> {
let hex: &[u8; 16] = hex.try_into().map_err(|_| ParseIdError {})?;
let mut dst = [0; 8];
let mut i = 0;
while i < 8 {
let h1 = HEX_DECODE_TABLE[hex[i * 2] as usize];
let h2 = HEX_DECODE_TABLE[hex[i * 2 + 1] as usize];
if h1 | h2 == 0xff {
return Err(ParseIdError {});
}
dst[i] = SHL4_TABLE[h1 as usize] | h2;
i += 1;
}
Ok(SpanId::new(
NonZeroU64::new(u64::from_be_bytes(dst)).ok_or_else(|| ParseIdError {})?,
))
}
pub fn try_from_hex(hex: impl fmt::Display) -> Result<Self, ParseIdError> {
let mut buf = crate::buf::Buffer::<16>::new();
Self::try_from_hex_slice(buf.buffer(hex).map_err(|_| ParseIdError {})?)
}
}
const HEX_ENCODE_TABLE: [u8; 16] = [
b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'a', b'b', b'c', b'd', b'e', b'f',
];
const HEX_DECODE_TABLE: &[u8; 256] = &{
let mut buf = [0; 256];
let mut i: u8 = 0;
loop {
buf[i as usize] = match i {
b'0'..=b'9' => i - b'0',
b'a'..=b'f' => i - b'a' + 10,
b'A'..=b'F' => i - b'A' + 10,
_ => 0xff,
};
if i == 255 {
break buf;
}
i += 1
}
};
const SHL4_TABLE: &[u8; 256] = &{
let mut buf = [0; 256];
let mut i: u8 = 0;
loop {
buf[i as usize] = i.wrapping_shl(4);
if i == 255 {
break buf;
}
i += 1;
}
};
#[derive(Debug)]
pub struct ParseIdError {}
impl fmt::Display for ParseIdError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "the input was not a valid id")
}
}
#[cfg(feature = "std")]
impl std::error::Error for ParseIdError {}
pub struct Span<'a, P> {
mdl: Path<'a>,
name: Str<'a>,
extent: Option<Extent>,
tpl: Option<Template<'a>>,
props: P,
}
impl<'a, P: Props> Span<'a, P> {
pub fn new(
mdl: impl Into<Path<'a>>,
name: impl Into<Str<'a>>,
extent: impl ToExtent,
props: P,
) -> Self {
Span {
mdl: mdl.into(),
extent: extent.to_extent(),
name: name.into(),
tpl: None,
props,
}
}
pub fn mdl(&self) -> &Path<'a> {
&self.mdl
}
pub fn with_mdl(mut self, mdl: impl Into<Path<'a>>) -> Self {
self.mdl = mdl.into();
self
}
pub fn name(&self) -> &Str<'a> {
&self.name
}
pub fn with_name(mut self, name: impl Into<Str<'a>>) -> Self {
self.name = name.into();
self
}
pub fn extent(&self) -> Option<&Extent> {
self.extent.as_ref()
}
pub fn with_extent(mut self, extent: impl ToExtent) -> Self {
self.extent = extent.to_extent();
self
}
pub fn ts(&self) -> Option<&Timestamp> {
self.extent.as_ref().map(|extent| extent.as_point())
}
pub fn ts_start(&self) -> Option<&Timestamp> {
self.extent
.as_ref()
.and_then(|extent| extent.as_range())
.map(|span| &span.start)
}
pub fn props(&self) -> &P {
&self.props
}
pub fn props_mut(&mut self) -> &mut P {
&mut self.props
}
pub fn with_props<U>(self, props: U) -> Span<'a, U> {
Span {
mdl: self.mdl,
extent: self.extent,
name: self.name,
tpl: self.tpl,
props,
}
}
pub fn map_props<U>(self, map: impl FnOnce(P) -> U) -> Span<'a, U> {
Span {
mdl: self.mdl,
extent: self.extent,
name: self.name,
tpl: self.tpl,
props: map(self.props),
}
}
pub fn tpl(&self) -> &Template<'a> {
self.tpl.as_ref().unwrap_or(&END_TEMPLATE)
}
pub fn with_tpl(mut self, tpl: impl Into<Template<'a>>) -> Self {
self.tpl = Some(tpl.into());
self
}
pub fn erase<'b>(&'b self) -> Span<'b, &'b dyn ErasedProps> {
Span {
mdl: self.mdl.by_ref(),
extent: self.extent.clone(),
name: self.name.by_ref(),
tpl: self.tpl.as_ref().map(|tpl| tpl.by_ref()),
props: &self.props,
}
}
}
impl<'a, P: Props> fmt::Debug for Span<'a, P> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self.to_event(), f)
}
}
const START_TEMPLATE_PARTS: &'static [template::Part<'static>] = &[
template::Part::hole("span_name"),
template::Part::text(" started"),
];
static START_TEMPLATE: Template<'static> = Template::new(&START_TEMPLATE_PARTS);
const END_TEMPLATE_PARTS: &'static [template::Part<'static>] = &[
template::Part::hole("span_name"),
template::Part::text(" completed"),
];
static END_TEMPLATE: Template<'static> = Template::new(&END_TEMPLATE_PARTS);
impl<'a, P: Props> ToEvent for Span<'a, P> {
type Props<'b>
= &'b Self
where
Self: 'b;
fn to_event<'b>(&'b self) -> Event<'b, Self::Props<'b>> {
Event::new(
self.mdl.by_ref(),
self.tpl().by_ref(),
self.extent.clone(),
&self,
)
}
}
impl<'a, P: Props> ToExtent for Span<'a, P> {
fn to_extent(&self) -> Option<Extent> {
self.extent().cloned()
}
}
impl<'a, P: Props> Props for Span<'a, P> {
fn for_each<'kv, F: FnMut(Str<'kv>, Value<'kv>) -> ControlFlow<()>>(
&'kv self,
mut for_each: F,
) -> ControlFlow<()> {
for_each(KEY_EVT_KIND.to_str(), Kind::Span.to_value())?;
for_each(KEY_SPAN_NAME.to_str(), self.name.to_value())?;
self.props.for_each(&mut for_each)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct SpanCtxt {
trace_id: Option<TraceId>,
span_parent: Option<SpanId>,
span_id: Option<SpanId>,
}
impl SpanCtxt {
pub const fn new(
trace_id: Option<TraceId>,
span_parent: Option<SpanId>,
span_id: Option<SpanId>,
) -> Self {
SpanCtxt {
trace_id,
span_parent,
span_id,
}
}
pub const fn empty() -> Self {
Self {
trace_id: None,
span_parent: None,
span_id: None,
}
}
pub fn new_root(rng: impl Rng) -> Self {
let trace_id = TraceId::random(&rng);
let span_parent = None;
let span_id = SpanId::random(&rng);
SpanCtxt::new(trace_id, span_parent, span_id)
}
pub fn current(ctxt: impl Ctxt) -> Self {
ctxt.with_current(|current| {
SpanCtxt::new(
current.pull::<TraceId, _>(KEY_TRACE_ID),
current.pull::<SpanId, _>(KEY_SPAN_PARENT),
current.pull::<SpanId, _>(KEY_SPAN_ID),
)
})
}
pub fn new_child(&self, rng: impl Rng) -> Self {
let trace_id = self.trace_id.or_else(|| TraceId::random(&rng));
let span_parent = self.span_id;
let span_id = SpanId::random(&rng);
SpanCtxt::new(trace_id, span_parent, span_id)
}
pub fn trace_id(&self) -> Option<&TraceId> {
self.trace_id.as_ref()
}
pub fn span_parent(&self) -> Option<&SpanId> {
self.span_parent.as_ref()
}
pub fn span_id(&self) -> Option<&SpanId> {
self.span_id.as_ref()
}
pub fn push<T: Ctxt>(&self, ctxt: T) -> Frame<T> {
Frame::push(ctxt, self)
}
}
impl Props for SpanCtxt {
fn for_each<'kv, F: FnMut(Str<'kv>, Value<'kv>) -> ControlFlow<()>>(
&'kv self,
mut for_each: F,
) -> ControlFlow<()> {
if let Some(ref trace_id) = self.trace_id {
for_each(KEY_TRACE_ID.to_str(), trace_id.to_value())?;
}
if let Some(ref span_id) = self.span_id {
for_each(KEY_SPAN_ID.to_str(), span_id.to_value())?;
}
if let Some(ref span_parent) = self.span_parent {
for_each(KEY_SPAN_PARENT.to_str(), span_parent.to_value())?;
}
ControlFlow::Continue(())
}
}
#[derive(Debug)]
pub struct ParseKindError {}
impl fmt::Display for ParseKindError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "the input was not a valid kind")
}
}
#[cfg(feature = "std")]
impl std::error::Error for ParseKindError {}
#[non_exhaustive]
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum SpanKind {
Internal,
Server,
Client,
Producer,
Consumer,
}
impl SpanKind {
pub fn try_from_str(s: &str) -> Result<Self, ParseKindError> {
s.parse()
}
pub fn as_str(&self) -> &'static str {
match self {
SpanKind::Internal => SPAN_KIND_INTERNAL,
SpanKind::Server => SPAN_KIND_SERVER,
SpanKind::Client => SPAN_KIND_CLIENT,
SpanKind::Producer => SPAN_KIND_PRODUCER,
SpanKind::Consumer => SPAN_KIND_CONSUMER,
}
}
}
impl FromStr for SpanKind {
type Err = ParseKindError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let s = s.trim();
if s.eq_ignore_ascii_case(SPAN_KIND_INTERNAL) {
return Ok(SpanKind::Internal);
}
if s.eq_ignore_ascii_case(SPAN_KIND_SERVER) {
return Ok(SpanKind::Server);
}
if s.eq_ignore_ascii_case(SPAN_KIND_CLIENT) {
return Ok(SpanKind::Client);
}
if s.eq_ignore_ascii_case(SPAN_KIND_PRODUCER) {
return Ok(SpanKind::Producer);
}
if s.eq_ignore_ascii_case(SPAN_KIND_CONSUMER) {
return Ok(SpanKind::Consumer);
}
Err(ParseKindError {})
}
}
impl fmt::Debug for SpanKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "\"{}\"", self)
}
}
impl fmt::Display for SpanKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
#[cfg(feature = "sval")]
impl sval::Value for SpanKind {
fn stream<'sval, S: sval::Stream<'sval> + ?Sized>(&'sval self, stream: &mut S) -> sval::Result {
stream.value(self.as_str())
}
}
#[cfg(feature = "serde")]
impl serde::Serialize for SpanKind {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
self.as_str().serialize(serializer)
}
}
impl ToValue for SpanKind {
fn to_value(&self) -> Value<'_> {
Value::capture_display(self)
}
}
impl<'v> FromValue<'v> for SpanKind {
fn from_value(value: Value<'v>) -> Option<Self> {
value
.downcast_ref::<SpanKind>()
.copied()
.or_else(|| value.parse())
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct SpanLink {
trace_id: TraceId,
span_id: SpanId,
}
impl SpanLink {
pub const fn new(trace_id: TraceId, span_id: SpanId) -> Self {
SpanLink { trace_id, span_id }
}
pub fn try_from_str(s: &str) -> Result<Self, ParseLinkError> {
Self::try_from_slice(s.as_bytes())
}
fn try_from_slice(s: &[u8]) -> Result<Self, ParseLinkError> {
if s.len() != 49 {
return Err(ParseLinkError {});
}
if s[32] != b'-' {
return Err(ParseLinkError {});
}
let trace_id = TraceId::try_from_hex_slice(&s[0..32]).map_err(|_| ParseLinkError {})?;
let span_id = SpanId::try_from_hex_slice(&s[33..49]).map_err(|_| ParseLinkError {})?;
Ok(SpanLink::new(trace_id, span_id))
}
pub fn parse(s: impl fmt::Display) -> Result<Self, ParseLinkError> {
let mut buf = crate::buf::Buffer::<49>::new();
Self::try_from_slice(buf.buffer(s).map_err(|_| ParseLinkError {})?)
}
pub fn trace_id(&self) -> &TraceId {
&self.trace_id
}
pub fn span_id(&self) -> &SpanId {
&self.span_id
}
}
impl FromStr for SpanLink {
type Err = ParseLinkError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::try_from_str(s)
}
}
impl fmt::Debug for SpanLink {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "\"{}\"", self)
}
}
impl fmt::Display for SpanLink {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut buf = [0; 49];
buf[0..32].copy_from_slice(&self.trace_id.to_hex());
buf[32] = b'-';
buf[33..49].copy_from_slice(&self.span_id.to_hex());
f.write_str(str::from_utf8(&buf).unwrap())
}
}
#[cfg(feature = "sval")]
impl sval::Value for SpanLink {
fn stream<'sval, S: sval::Stream<'sval> + ?Sized>(&'sval self, stream: &mut S) -> sval::Result {
sval::stream_display(stream, self)
}
}
#[cfg(feature = "serde")]
impl serde::Serialize for SpanLink {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.collect_str(self)
}
}
impl ToValue for SpanLink {
fn to_value(&self) -> Value<'_> {
Value::capture_display(self)
}
}
impl<'v> FromValue<'v> for SpanLink {
fn from_value(value: Value<'v>) -> Option<Self> {
value
.downcast_ref::<SpanLink>()
.copied()
.or_else(|| SpanLink::parse(value).ok())
}
}
#[derive(Debug)]
pub struct ParseLinkError {}
impl fmt::Display for ParseLinkError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "the input was not a valid span link")
}
}
#[cfg(feature = "std")]
impl std::error::Error for ParseLinkError {}
#[cfg(feature = "alloc")]
pub mod span_link_set {
use super::*;
#[cfg(not(feature = "std"))]
use alloc::collections::{btree_set as set, BTreeSet as Set};
#[cfg(feature = "std")]
use std::collections::{hash_set as set, HashSet as Set};
use core::{fmt::Write, str::FromStr};
use crate::buf::trim_start;
use emit_core::value::{FromValue, ToValue, Value};
#[derive(Debug)]
pub struct ParseSpanLinkSetError {}
impl fmt::Display for ParseSpanLinkSetError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "the input was not a valid span link set")
}
}
#[cfg(feature = "std")]
impl std::error::Error for ParseSpanLinkSetError {}
#[derive(Clone, PartialEq, Eq)]
pub struct SpanLinkSet {
links: Set<SpanLink>,
}
impl fmt::Debug for SpanLinkSet {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(self, f)
}
}
impl fmt::Display for SpanLinkSet {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_char('[')?;
let mut first = true;
for link in &self.links {
if !first {
f.write_char(',')?;
}
first = false;
fmt::Display::fmt(link, f)?;
}
f.write_char(']')
}
}
impl SpanLinkSet {
pub fn new() -> Self {
SpanLinkSet { links: Set::new() }
}
pub fn try_from_str(s: &str) -> Result<Self, ParseSpanLinkSetError> {
Self::try_from_slice(s.as_bytes())
}
fn try_from_slice(mut s: &[u8]) -> Result<Self, ParseSpanLinkSetError> {
let mut set = SpanLinkSet::new();
if s.len() < 2 {
return Err(ParseSpanLinkSetError {});
}
match (s.first(), s.last()) {
(Some(&b'['), Some(&b']')) => (),
(Some(&b'('), Some(&b')')) => (),
(Some(&b'{'), Some(&b'}')) => (),
_ => return Err(ParseSpanLinkSetError {}),
};
s = &s[1..];
s = trim_start(s);
let mut first = true;
while s.len() > 1 {
if !first {
if s.first() != Some(&b',') {
return Err(ParseSpanLinkSetError {});
}
s = &s[1..];
s = trim_start(s);
}
first = false;
let link = match s.first() {
Some(&b'"') => {
if s.get(50) != Some(&b'"') {
return Err(ParseSpanLinkSetError {});
}
let link = &s[1..50];
s = &s[51..];
link
}
_ => {
if s.len() < 49 {
return Err(ParseSpanLinkSetError {});
}
let link = &s[0..49];
s = &s[49..];
link
}
};
let link = SpanLink::try_from_slice(link).map_err(|_| ParseSpanLinkSetError {})?;
if !set.links.insert(link) {
return Err(ParseSpanLinkSetError {});
}
s = trim_start(s);
}
if s.len() != 1 {
return Err(ParseSpanLinkSetError {});
}
Ok(set)
}
pub fn insert(&mut self, link: SpanLink) -> bool {
self.links.insert(link)
}
pub fn len(&self) -> usize {
self.links.len()
}
pub fn is_empty(&self) -> bool {
self.links.is_empty()
}
pub fn clear(&mut self) {
self.links.clear();
}
pub fn contains(&self, link: SpanLink) -> bool {
self.links.contains(&link)
}
pub fn iter(&self) -> Iter<'_> {
Iter(self.links.iter())
}
}
impl Default for SpanLinkSet {
fn default() -> Self {
Self::new()
}
}
impl<'a> IntoIterator for &'a SpanLinkSet {
type IntoIter = Iter<'a>;
type Item = SpanLink;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<'a> FromIterator<SpanLink> for SpanLinkSet {
fn from_iter<I: IntoIterator<Item = SpanLink>>(iter: I) -> Self {
let mut set = SpanLinkSet::new();
set.extend(iter);
set
}
}
impl<'a> Extend<SpanLink> for SpanLinkSet {
fn extend<I: IntoIterator<Item = SpanLink>>(&mut self, iter: I) {
for link in iter {
self.insert(link);
}
}
}
impl<'a> FromIterator<(TraceId, SpanId)> for SpanLinkSet {
fn from_iter<I: IntoIterator<Item = (TraceId, SpanId)>>(iter: I) -> Self {
Self::from_iter(
iter.into_iter()
.map(|(trace_id, span_id)| SpanLink::new(trace_id, span_id)),
)
}
}
impl<'a> Extend<(TraceId, SpanId)> for SpanLinkSet {
fn extend<I: IntoIterator<Item = (TraceId, SpanId)>>(&mut self, iter: I) {
self.extend(
iter.into_iter()
.map(|(trace_id, span_id)| SpanLink::new(trace_id, span_id)),
)
}
}
pub struct Iter<'a>(set::Iter<'a, SpanLink>);
impl<'a> Iterator for Iter<'a> {
type Item = SpanLink;
fn next(&mut self) -> Option<Self::Item> {
self.0.next().copied()
}
}
#[cfg(feature = "sval")]
impl sval::Value for SpanLinkSet {
fn stream<'sval, S: sval::Stream<'sval> + ?Sized>(
&'sval self,
stream: &mut S,
) -> sval::Result {
stream.seq_begin(Some(self.links.len()))?;
for link in &self.links {
stream.seq_value_begin()?;
stream.value(link)?;
stream.seq_value_end()?;
}
stream.seq_end()
}
}
#[cfg(feature = "serde")]
impl serde::Serialize for SpanLinkSet {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
use serde::ser::SerializeSeq as _;
let mut seq = serializer.serialize_seq(Some(self.links.len()))?;
for link in &self.links {
seq.serialize_element(link)?;
}
seq.end()
}
}
impl FromStr for SpanLinkSet {
type Err = ParseSpanLinkSetError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::try_from_str(s)
}
}
impl ToValue for SpanLinkSet {
fn to_value(&self) -> Value<'_> {
#[cfg(feature = "sval")]
{
Value::capture_sval(self)
}
#[cfg(all(feature = "serde", not(feature = "sval")))]
{
Value::capture_serde(self)
}
#[cfg(all(not(feature = "serde"), not(feature = "sval")))]
{
Value::capture_display(self)
}
}
}
impl<'a> FromValue<'a> for SpanLinkSet {
fn from_value(v: Value<'a>) -> Option<Self> {
if let Some(link_set) = v.downcast_ref::<Self>() {
return Some(link_set.clone());
}
#[cfg(feature = "sval")]
{
if let Some(link_set) = from_sval(v.by_ref()) {
return Some(link_set);
}
}
#[cfg(all(not(feature = "sval"), feature = "serde"))]
{
if let Some(link_set) = from_serde(v.by_ref()) {
return Some(link_set);
}
}
v.parse()
}
}
#[cfg(any(feature = "sval", feature = "serde"))]
struct Extract {
depth: usize,
links: Set<SpanLink>,
text_buf: crate::buf::Buffer<49>,
}
#[cfg(any(feature = "sval", feature = "serde"))]
impl Default for Extract {
fn default() -> Self {
Extract {
depth: 0,
links: Set::new(),
text_buf: crate::buf::Buffer::new(),
}
}
}
#[cfg(any(feature = "sval", feature = "serde"))]
#[derive(Debug)]
struct Incompatible;
#[cfg(any(feature = "sval", feature = "serde"))]
impl Extract {
fn push_link_fragment(&mut self, fragment: &str) -> Result<(), Incompatible> {
if self.depth != 1 {
return Err(Incompatible);
}
if !self.text_buf.push_str(fragment) {
return Err(Incompatible);
}
Ok(())
}
fn push_link(&mut self) -> Result<(), Incompatible> {
if self.depth != 1 {
return Err(Incompatible);
}
let link_bytes = self.text_buf.as_bytes();
let link = SpanLink::try_from_slice(link_bytes).map_err(|_| Incompatible)?;
self.links.insert(link);
self.text_buf.reset();
Ok(())
}
fn down(&mut self) -> Result<(), Incompatible> {
self.depth += 1;
if self.depth > 1 {
Err(Incompatible)
} else {
Ok(())
}
}
fn up(&mut self) -> Result<(), Incompatible> {
self.depth -= 1;
Ok(())
}
fn end(self) -> SpanLinkSet {
SpanLinkSet { links: self.links }
}
}
#[cfg(feature = "sval")]
fn from_sval(value: Value) -> Option<SpanLinkSet> {
#[allow(non_local_definitions)]
impl From<Incompatible> for sval::Error {
fn from(_: Incompatible) -> sval::Error {
sval::Error::new()
}
}
#[allow(non_local_definitions)]
impl<'sval> sval::Stream<'sval> for Extract {
fn null(&mut self) -> sval::Result {
sval::error()
}
fn bool(&mut self, _: bool) -> sval::Result {
sval::error()
}
fn text_begin(&mut self, _: Option<usize>) -> sval::Result {
Ok(())
}
fn text_fragment_computed(&mut self, fragment: &str) -> sval::Result {
self.push_link_fragment(fragment).map_err(Into::into)
}
fn text_end(&mut self) -> sval::Result {
Ok(self.push_link()?)
}
fn i64(&mut self, _: i64) -> sval::Result {
sval::error()
}
fn f64(&mut self, _: f64) -> sval::Result {
sval::error()
}
fn seq_begin(&mut self, _: Option<usize>) -> sval::Result {
Ok(self.down()?)
}
fn seq_value_begin(&mut self) -> sval::Result {
Ok(())
}
fn seq_value_end(&mut self) -> sval::Result {
Ok(())
}
fn seq_end(&mut self) -> sval::Result {
Ok(self.up()?)
}
}
let mut extract = Extract::default();
sval::stream(&mut extract, &value).ok()?;
Some(extract.end())
}
#[cfg(all(not(feature = "sval"), feature = "serde"))]
fn from_serde(value: Value) -> Option<SpanLinkSet> {
use serde::Serialize as _;
#[allow(non_local_definitions)]
impl fmt::Display for Incompatible {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("incompatible")
}
}
#[allow(non_local_definitions)]
impl serde::ser::StdError for Incompatible {}
#[allow(non_local_definitions)]
impl serde::ser::Error for Incompatible {
fn custom<T>(_: T) -> Self
where
T: fmt::Display,
{
Incompatible
}
}
#[allow(non_local_definitions)]
impl<'a> serde::Serializer for &'a mut Extract {
type Ok = ();
type Error = Incompatible;
type SerializeSeq = Self;
type SerializeTuple = Self;
type SerializeTupleStruct = Self;
type SerializeTupleVariant = Self;
type SerializeMap = Self;
type SerializeStruct = Self;
type SerializeStructVariant = Self;
fn serialize_bool(self, _: bool) -> Result<Self::Ok, Self::Error> {
Err(Incompatible)
}
fn serialize_i8(self, _: i8) -> Result<Self::Ok, Self::Error> {
Err(Incompatible)
}
fn serialize_i16(self, _: i16) -> Result<Self::Ok, Self::Error> {
Err(Incompatible)
}
fn serialize_i32(self, _: i32) -> Result<Self::Ok, Self::Error> {
Err(Incompatible)
}
fn serialize_i64(self, _: i64) -> Result<Self::Ok, Self::Error> {
Err(Incompatible)
}
fn serialize_u8(self, _: u8) -> Result<Self::Ok, Self::Error> {
Err(Incompatible)
}
fn serialize_u16(self, _: u16) -> Result<Self::Ok, Self::Error> {
Err(Incompatible)
}
fn serialize_u32(self, _: u32) -> Result<Self::Ok, Self::Error> {
Err(Incompatible)
}
fn serialize_u64(self, _: u64) -> Result<Self::Ok, Self::Error> {
Err(Incompatible)
}
fn serialize_f32(self, _: f32) -> Result<Self::Ok, Self::Error> {
Err(Incompatible)
}
fn serialize_f64(self, _: f64) -> Result<Self::Ok, Self::Error> {
Err(Incompatible)
}
fn serialize_char(self, _: char) -> Result<Self::Ok, Self::Error> {
Err(Incompatible)
}
fn serialize_str(self, value: &str) -> Result<Self::Ok, Self::Error> {
if self.depth == 1 {
self.text_buf.reset();
self.push_link_fragment(value)?;
self.push_link()
} else {
Err(Incompatible)
}
}
fn serialize_bytes(self, _: &[u8]) -> Result<Self::Ok, Self::Error> {
Err(Incompatible)
}
fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
Err(Incompatible)
}
fn serialize_some<T>(self, value: &T) -> Result<Self::Ok, Self::Error>
where
T: ?Sized + serde::Serialize,
{
value.serialize(self)
}
fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
Err(Incompatible)
}
fn serialize_unit_struct(self, name: &'static str) -> Result<Self::Ok, Self::Error> {
name.serialize(self)
}
fn serialize_newtype_struct<T>(
self,
_: &'static str,
value: &T,
) -> Result<Self::Ok, Self::Error>
where
T: ?Sized + serde::Serialize,
{
value.serialize(self)
}
fn serialize_newtype_variant<T>(
self,
_: &'static str,
_: u32,
_: &'static str,
value: &T,
) -> Result<Self::Ok, Self::Error>
where
T: ?Sized + serde::Serialize,
{
value.serialize(self)
}
fn serialize_seq(self, _: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
self.down()?;
Ok(self)
}
fn serialize_tuple(self, _: usize) -> Result<Self::SerializeTuple, Self::Error> {
self.down()?;
Ok(self)
}
fn serialize_tuple_struct(
self,
_: &'static str,
_: usize,
) -> Result<Self::SerializeTupleStruct, Self::Error> {
self.down()?;
Ok(self)
}
fn serialize_tuple_variant(
self,
_: &'static str,
_: u32,
_: &'static str,
_: usize,
) -> Result<Self::SerializeTupleVariant, Self::Error> {
self.down()?;
Ok(self)
}
fn serialize_map(self, _: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
self.down()?;
Ok(self)
}
fn serialize_struct(
self,
_: &'static str,
_: usize,
) -> Result<Self::SerializeStruct, Self::Error> {
self.down()?;
Ok(self)
}
fn serialize_struct_variant(
self,
_: &'static str,
_: u32,
_: &'static str,
_: usize,
) -> Result<Self::SerializeStructVariant, Self::Error> {
self.down()?;
Ok(self)
}
fn serialize_unit_variant(
self,
_: &'static str,
_: u32,
variant: &'static str,
) -> Result<Self::Ok, Self::Error> {
variant.serialize(self)
}
}
#[allow(non_local_definitions)]
impl<'a> serde::ser::SerializeSeq for &'a mut Extract {
type Ok = ();
type Error = Incompatible;
fn serialize_element<T>(&mut self, value: &T) -> Result<(), Self::Error>
where
T: ?Sized + serde::Serialize,
{
value.serialize(&mut **self)
}
fn end(self) -> Result<Self::Ok, Self::Error> {
self.up()?;
Ok(())
}
}
#[allow(non_local_definitions)]
impl<'a> serde::ser::SerializeTuple for &'a mut Extract {
type Ok = ();
type Error = Incompatible;
fn serialize_element<T>(&mut self, value: &T) -> Result<(), Self::Error>
where
T: ?Sized + serde::Serialize,
{
value.serialize(&mut **self)
}
fn end(self) -> Result<Self::Ok, Self::Error> {
self.up()?;
Ok(())
}
}
#[allow(non_local_definitions)]
impl<'a> serde::ser::SerializeTupleStruct for &'a mut Extract {
type Ok = ();
type Error = Incompatible;
fn serialize_field<T>(&mut self, value: &T) -> Result<(), Self::Error>
where
T: ?Sized + serde::Serialize,
{
value.serialize(&mut **self)
}
fn end(self) -> Result<Self::Ok, Self::Error> {
self.up()?;
Ok(())
}
}
#[allow(non_local_definitions)]
impl<'a> serde::ser::SerializeTupleVariant for &'a mut Extract {
type Ok = ();
type Error = Incompatible;
fn serialize_field<T>(&mut self, value: &T) -> Result<(), Self::Error>
where
T: ?Sized + serde::Serialize,
{
value.serialize(&mut **self)
}
fn end(self) -> Result<Self::Ok, Self::Error> {
self.up()?;
Ok(())
}
}
#[allow(non_local_definitions)]
impl<'a> serde::ser::SerializeMap for &'a mut Extract {
type Ok = ();
type Error = Incompatible;
fn serialize_key<T>(&mut self, _: &T) -> Result<(), Self::Error>
where
T: ?Sized + serde::Serialize,
{
Err(Incompatible)
}
fn serialize_value<T>(&mut self, _: &T) -> Result<(), Self::Error>
where
T: ?Sized + serde::Serialize,
{
Err(Incompatible)
}
fn end(self) -> Result<Self::Ok, Self::Error> {
self.up()?;
Ok(())
}
}
#[allow(non_local_definitions)]
impl<'a> serde::ser::SerializeStruct for &'a mut Extract {
type Ok = ();
type Error = Incompatible;
fn serialize_field<T>(&mut self, _: &'static str, _: &T) -> Result<(), Self::Error>
where
T: ?Sized + serde::Serialize,
{
Err(Incompatible)
}
fn end(self) -> Result<Self::Ok, Self::Error> {
self.up()?;
Ok(())
}
}
#[allow(non_local_definitions)]
impl<'a> serde::ser::SerializeStructVariant for &'a mut Extract {
type Ok = ();
type Error = Incompatible;
fn serialize_field<T>(&mut self, _: &'static str, _: &T) -> Result<(), Self::Error>
where
T: ?Sized + serde::Serialize,
{
Err(Incompatible)
}
fn end(self) -> Result<Self::Ok, Self::Error> {
self.up()?;
Ok(())
}
}
let mut extract = Extract::default();
value.serialize(&mut extract).ok()?;
Some(extract.end())
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::BTreeSet;
#[test]
fn span_link_set() {
let mut set = SpanLinkSet::new();
assert_eq!(0, set.len());
assert!(set.is_empty());
assert!(!set.contains(SpanLink::new(
TraceId::from_u128(0x0123456789abcdef0123456789abcdef).unwrap(),
SpanId::from_u64(0x0123456789abcdef).unwrap(),
)));
set.insert(SpanLink::new(
TraceId::from_u128(0x0123456789abcdef0123456789abcdef).unwrap(),
SpanId::from_u64(0x0123456789abcdef).unwrap(),
));
assert_eq!(1, set.len());
assert!(set.contains(SpanLink::new(
TraceId::from_u128(0x0123456789abcdef0123456789abcdef).unwrap(),
SpanId::from_u64(0x0123456789abcdef).unwrap(),
)));
set.insert(SpanLink::new(
TraceId::from_u128(0xfedcba9876543210fedcba9876543210).unwrap(),
SpanId::from_u64(0xfedcba9876543210).unwrap(),
));
assert_eq!(2, set.len());
assert!(!set.insert(SpanLink::new(
TraceId::from_u128(0x0123456789abcdef0123456789abcdef).unwrap(),
SpanId::from_u64(0x0123456789abcdef).unwrap(),
)));
assert_eq!(2, set.len());
set.clear();
assert_eq!(0, set.len());
assert!(set.is_empty());
}
#[test]
fn span_link_set_roundtrip() {
for case in [
SpanLinkSet::new(),
{
let mut set = SpanLinkSet::new();
set.insert(SpanLink::new(
TraceId::from_u128(0x0123456789abcdef0123456789abcdef).unwrap(),
SpanId::from_u64(0x0123456789abcdef).unwrap(),
));
set
},
{
let mut set = SpanLinkSet::new();
set.insert(SpanLink::new(
TraceId::from_u128(0x0123456789abcdef0123456789abcdef).unwrap(),
SpanId::from_u64(0x0123456789abcdef).unwrap(),
));
set.insert(SpanLink::new(
TraceId::from_u128(0xfedcba9876543210fedcba9876543210).unwrap(),
SpanId::from_u64(0xfedcba9876543210).unwrap(),
));
set
},
] {
let fmt = case.to_string();
assert_eq!(Some(case), SpanLinkSet::try_from_str(&fmt).ok(), "{fmt}");
}
}
#[test]
fn span_link_set_from_iter() {
let mut set = SpanLinkSet::from_iter([
SpanLink::new(
TraceId::from_u128(0x0123456789abcdef0123456789abcdef).unwrap(),
SpanId::from_u64(0x0123456789abcdef).unwrap(),
),
SpanLink::new(
TraceId::from_u128(0x0123456789abcdef0123456789abcdef).unwrap(),
SpanId::from_u64(0x0123456789abcdef).unwrap(),
),
]);
assert!(set.contains(SpanLink::new(
TraceId::from_u128(0x0123456789abcdef0123456789abcdef).unwrap(),
SpanId::from_u64(0x0123456789abcdef).unwrap(),
)));
set.extend([SpanLink::new(
TraceId::from_u128(0xfedcba9876543210fedcba9876543210).unwrap(),
SpanId::from_u64(0xfedcba9876543210).unwrap(),
)]);
assert!(set.contains(SpanLink::new(
TraceId::from_u128(0x0123456789abcdef0123456789abcdef).unwrap(),
SpanId::from_u64(0x0123456789abcdef).unwrap(),
)));
assert!(set.contains(SpanLink::new(
TraceId::from_u128(0xfedcba9876543210fedcba9876543210).unwrap(),
SpanId::from_u64(0xfedcba9876543210).unwrap(),
)));
}
#[test]
fn span_link_set_parse() {
for (case, expected) in [
(
format!("{:?}", [
SpanLink::new(
TraceId::from_u128(0x0123456789abcdef0123456789abcdef).unwrap(),
SpanId::from_u64(0x0123456789abcdef).unwrap(),
), SpanLink::new(
TraceId::from_u128(0xfedcba9876543210fedcba9876543210).unwrap(),
SpanId::from_u64(0xfedcba9876543210).unwrap(),
)]
),
{
let mut set = SpanLinkSet::new();
set.insert(SpanLink::new(
TraceId::from_u128(0x0123456789abcdef0123456789abcdef).unwrap(),
SpanId::from_u64(0x0123456789abcdef).unwrap(),
));
set.insert(SpanLink::new(
TraceId::from_u128(0xfedcba9876543210fedcba9876543210).unwrap(),
SpanId::from_u64(0xfedcba9876543210).unwrap(),
));
set
},
),
(
format!("{:?}", ["0123456789abcdef0123456789abcdef-0123456789abcdef", "fedcba9876543210fedcba9876543210-fedcba9876543210"]),
{
let mut set = SpanLinkSet::new();
set.insert(SpanLink::new(
TraceId::from_u128(0x0123456789abcdef0123456789abcdef).unwrap(),
SpanId::from_u64(0x0123456789abcdef).unwrap(),
));
set.insert(SpanLink::new(
TraceId::from_u128(0xfedcba9876543210fedcba9876543210).unwrap(),
SpanId::from_u64(0xfedcba9876543210).unwrap(),
));
set
},
),
(
format!("{:?}", (
SpanLink::new(
TraceId::from_u128(0x0123456789abcdef0123456789abcdef).unwrap(),
SpanId::from_u64(0x0123456789abcdef).unwrap(),
), SpanLink::new(
TraceId::from_u128(0xfedcba9876543210fedcba9876543210).unwrap(),
SpanId::from_u64(0xfedcba9876543210).unwrap(),
))
),
{
let mut set = SpanLinkSet::new();
set.insert(SpanLink::new(
TraceId::from_u128(0x0123456789abcdef0123456789abcdef).unwrap(),
SpanId::from_u64(0x0123456789abcdef).unwrap(),
));
set.insert(SpanLink::new(
TraceId::from_u128(0xfedcba9876543210fedcba9876543210).unwrap(),
SpanId::from_u64(0xfedcba9876543210).unwrap(),
));
set
},
),
(
format!("{:?}", {
let mut set = BTreeSet::new();
set.insert(SpanLink::new(
TraceId::from_u128(0x0123456789abcdef0123456789abcdef).unwrap(),
SpanId::from_u64(0x0123456789abcdef).unwrap(),
));
set.insert(SpanLink::new(
TraceId::from_u128(0xfedcba9876543210fedcba9876543210).unwrap(),
SpanId::from_u64(0xfedcba9876543210).unwrap(),
));
set
}),
{
let mut set = SpanLinkSet::new();
set.insert(SpanLink::new(
TraceId::from_u128(0x0123456789abcdef0123456789abcdef).unwrap(),
SpanId::from_u64(0x0123456789abcdef).unwrap(),
));
set.insert(SpanLink::new(
TraceId::from_u128(0xfedcba9876543210fedcba9876543210).unwrap(),
SpanId::from_u64(0xfedcba9876543210).unwrap(),
));
set
},
),
(
format!("{:?}", {
let mut set = BTreeSet::new();
set.insert("0123456789abcdef0123456789abcdef-0123456789abcdef");
set.insert("fedcba9876543210fedcba9876543210-fedcba9876543210");
set
}),
{
let mut set = SpanLinkSet::new();
set.insert(SpanLink::new(
TraceId::from_u128(0x0123456789abcdef0123456789abcdef).unwrap(),
SpanId::from_u64(0x0123456789abcdef).unwrap(),
));
set.insert(SpanLink::new(
TraceId::from_u128(0xfedcba9876543210fedcba9876543210).unwrap(),
SpanId::from_u64(0xfedcba9876543210).unwrap(),
));
set
},
),
(
"[ 0123456789abcdef0123456789abcdef-0123456789abcdef , fedcba9876543210fedcba9876543210-fedcba9876543210 ]".to_string(),
{
let mut set = SpanLinkSet::new();
set.insert(SpanLink::new(
TraceId::from_u128(0x0123456789abcdef0123456789abcdef).unwrap(),
SpanId::from_u64(0x0123456789abcdef).unwrap(),
));
set.insert(SpanLink::new(
TraceId::from_u128(0xfedcba9876543210fedcba9876543210).unwrap(),
SpanId::from_u64(0xfedcba9876543210).unwrap(),
));
set
},
),
(
"[ \"0123456789abcdef0123456789abcdef-0123456789abcdef\" , \"fedcba9876543210fedcba9876543210-fedcba9876543210\" ]".to_string(),
{
let mut set = SpanLinkSet::new();
set.insert(SpanLink::new(
TraceId::from_u128(0x0123456789abcdef0123456789abcdef).unwrap(),
SpanId::from_u64(0x0123456789abcdef).unwrap(),
));
set.insert(SpanLink::new(
TraceId::from_u128(0xfedcba9876543210fedcba9876543210).unwrap(),
SpanId::from_u64(0xfedcba9876543210).unwrap(),
));
set
},
),
("[]".to_string(), SpanLinkSet::new()),
] {
assert_eq!(
Some(expected),
SpanLinkSet::try_from_str(&case).ok(),
"{case}"
);
}
}
#[test]
fn span_link_set_to_from_value() {
for case in [{
let mut set = SpanLinkSet::new();
set.insert(SpanLink::new(
TraceId::from_u128(0x0123456789abcdef0123456789abcdef).unwrap(),
SpanId::from_u64(0x0123456789abcdef).unwrap(),
));
set.insert(SpanLink::new(
TraceId::from_u128(0xfedcba9876543210fedcba9876543210).unwrap(),
SpanId::from_u64(0xfedcba9876543210).unwrap(),
));
set
}] {
assert_eq!(case, SpanLinkSet::from_value(case.to_value()).unwrap());
}
}
#[test]
fn span_link_set_from_value_string() {
for (case, expected) in [
(
"[0123456789abcdef0123456789abcdef-0123456789abcdef,fedcba9876543210fedcba9876543210-fedcba9876543210]",
{
let mut set = SpanLinkSet::new();
set.insert(SpanLink::new(
TraceId::from_u128(0x0123456789abcdef0123456789abcdef).unwrap(),
SpanId::from_u64(0x0123456789abcdef).unwrap(),
));
set.insert(SpanLink::new(
TraceId::from_u128(0xfedcba9876543210fedcba9876543210).unwrap(),
SpanId::from_u64(0xfedcba9876543210).unwrap(),
));
set
},
),
] {
assert_eq!(expected, Value::from(case).cast().unwrap());
}
}
#[test]
fn span_link_set_from_value_structured() {
#[cfg(feature = "sval")]
trait CaseSval: sval::Value {}
#[cfg(feature = "sval")]
impl<T: sval::Value> CaseSval for T {}
#[cfg(not(feature = "sval"))]
trait CaseSval {}
#[cfg(not(feature = "sval"))]
impl<T> CaseSval for T {}
#[cfg(feature = "serde")]
trait CaseSerde: serde::Serialize {}
#[cfg(feature = "serde")]
impl<T: serde::Serialize> CaseSerde for T {}
#[cfg(not(feature = "serde"))]
trait CaseSerde {}
#[cfg(not(feature = "serde"))]
impl<T> CaseSerde for T {}
trait Case: CaseSval + CaseSerde + fmt::Debug {}
impl<T: fmt::Debug + CaseSval + CaseSerde> Case for T {}
fn case(case: &impl Case, expected: &SpanLinkSet) {
assert_eq!(expected, &Value::from_debug(case).cast().unwrap());
#[cfg(feature = "sval")]
{
assert_eq!(expected, &Value::from_sval(case).cast().unwrap());
}
#[cfg(feature = "serde")]
{
assert_eq!(expected, &Value::from_serde(case).cast().unwrap());
}
}
let expected = {
let mut set = SpanLinkSet::new();
set.insert(SpanLink::new(
TraceId::from_u128(0x0123456789abcdef0123456789abcdef).unwrap(),
SpanId::from_u64(0x0123456789abcdef).unwrap(),
));
set.insert(SpanLink::new(
TraceId::from_u128(0xfedcba9876543210fedcba9876543210).unwrap(),
SpanId::from_u64(0xfedcba9876543210).unwrap(),
));
set
};
case(
&[
"0123456789abcdef0123456789abcdef-0123456789abcdef",
"fedcba9876543210fedcba9876543210-fedcba9876543210",
],
&expected,
);
case(
&[
SpanLink::new(
TraceId::from_u128(0x0123456789abcdef0123456789abcdef).unwrap(),
SpanId::from_u64(0x0123456789abcdef).unwrap(),
),
SpanLink::new(
TraceId::from_u128(0xfedcba9876543210fedcba9876543210).unwrap(),
SpanId::from_u64(0xfedcba9876543210).unwrap(),
),
],
&expected,
);
let mut btree_set = alloc::collections::BTreeSet::new();
btree_set.insert(SpanLink::new(
TraceId::from_u128(0x0123456789abcdef0123456789abcdef).unwrap(),
SpanId::from_u64(0x0123456789abcdef).unwrap(),
));
btree_set.insert(SpanLink::new(
TraceId::from_u128(0xfedcba9876543210fedcba9876543210).unwrap(),
SpanId::from_u64(0xfedcba9876543210).unwrap(),
));
case(&btree_set, &expected);
}
#[test]
fn err_span_link_set_invalid() {
for case in [
"",
"<>",
"0123456789abcdef0123456789abcdef-0123456789abcdef",
"[0123456789abcdef0123456789abcdef-0123456789abcdef,",
"[0123456789abcdef0123456789abcdef-0123456789abcdef)",
"[,]",
"[,,]",
"[invalid]",
"[0123456789abcdef0123456789abcdef-0123456789abcdef,invalid]",
"{]",
"([",
"[0123456789abcdef0123456789abcdef-0123456789abcdef,0123456789abcdef0123456789abcdef-0123456789abcdef]",
"{0123456789abcdef0123456789Cbcdef-0123456A89abcdef, fba9876543210fedcba9876543210-fedcba9876543210\"}",
] {
assert!(SpanLinkSet::try_from_str(case).is_err(), "{case}");
}
}
}
}
#[cfg(feature = "alloc")]
pub use self::span_link_set::SpanLinkSet;
pub struct SpanGuard<'a, T: Clock, P: Props, F: Completion> {
state: SpanGuardState<T>,
data: Option<SpanGuardData<'a, P>>,
completion: Option<F>,
}
struct SpanGuardData<'a, P: Props> {
mdl: Path<'a>,
name: Str<'a>,
ctxt: SpanCtxt,
props: P,
}
enum SpanGuardState<T: Clock> {
Initial(T),
Started(Timer<T>),
Completed,
}
impl<T: Clock> SpanGuardState<T> {
fn take(&mut self) -> Self {
mem::replace(self, SpanGuardState::Completed)
}
}
impl<'a, T: Clock, P: Props, F: Completion> Drop for SpanGuard<'a, T, P, F> {
fn drop(&mut self) {
self.complete_default();
}
}
impl<'a, T: Clock, P: Props, F: Completion> SpanGuard<'a, T, P, F> {
pub fn new<C: Ctxt>(
filter: impl Filter,
ctxt: C,
clock: T,
rng: impl Rng,
completion: F,
ctxt_props: impl Props,
span_mdl: impl Into<Path<'a>>,
span_name: impl Into<Str<'a>>,
span_props: P,
) -> (Self, Frame<C>) {
let span_mdl = span_mdl.into();
let span_name = span_name.into();
let span_ctxt = SpanCtxt::current(&ctxt).new_child(rng);
let is_enabled = ctxt.with_current(|current_ctxt_props| {
filter.matches(
Span::new(
span_mdl.by_ref(),
span_name.by_ref(),
Empty,
(&span_props)
.and_props(&ctxt_props)
.and_props(span_ctxt)
.and_props(current_ctxt_props),
)
.to_event()
.with_tpl(START_TEMPLATE.by_ref()),
)
});
let guard = SpanGuard {
state: SpanGuardState::Initial(clock),
data: Some(SpanGuardData {
mdl: span_mdl,
ctxt: span_ctxt,
name: span_name,
props: span_props,
}),
completion: if is_enabled { Some(completion) } else { None },
};
let frame = guard.push_ctxt(ctxt, ctxt_props);
(guard, frame)
}
fn push_ctxt<C: Ctxt>(&self, ctxt: C, ctxt_props: impl Props) -> Frame<C> {
let span_ctxt = self.data.as_ref().expect("span is already complete").ctxt;
if self.is_enabled() {
Frame::push(ctxt, ctxt_props.and_props(span_ctxt))
} else {
Frame::disabled(ctxt, ctxt_props.and_props(span_ctxt))
}
}
pub fn start(&mut self) {
let state = mem::replace(&mut self.state, SpanGuardState::Completed);
let SpanGuardState::Initial(clock) = state else {
self.state = state;
return;
};
self.state = SpanGuardState::Started(Timer::start(clock));
}
pub fn is_enabled(&self) -> bool {
self.completion.is_some()
}
#[must_use = "this method returns a new `SpanGuard` that will be immediately dropped unless used"]
pub fn with_completion<U: Completion>(mut self, completion: U) -> SpanGuard<'a, T, P, U> {
self.completion.take();
SpanGuard {
state: self.state.take(),
data: self.data.take(),
completion: Some(completion),
}
}
#[must_use = "this method returns a new `SpanGuard` that will be immediately dropped unless used"]
pub fn with_mdl(mut self, mdl: impl Into<Path<'a>>) -> Self {
if let Some(ref mut data) = self.data {
data.mdl = mdl.into();
}
self
}
#[must_use = "this method returns a new `SpanGuard` that will be immediately dropped unless used"]
pub fn with_name(mut self, name: impl Into<Str<'a>>) -> Self {
if let Some(ref mut data) = self.data {
data.name = name.into();
}
self
}
#[must_use = "this method returns a new `SpanGuard` that will be immediately dropped unless used"]
pub fn with_props<U: Props>(self, props: U) -> SpanGuard<'a, T, U, F> {
self.map_props(|_| props)
}
#[must_use = "this method returns a new `SpanGuard` that will be immediately dropped unless used"]
pub fn map_props<U: Props>(mut self, map: impl FnOnce(P) -> U) -> SpanGuard<'a, T, U, F> {
let data = self.data.take().map(|data| SpanGuardData {
mdl: data.mdl,
name: data.name,
ctxt: data.ctxt,
props: map(data.props),
});
SpanGuard {
state: self.state.take(),
data,
completion: self.completion.take(),
}
}
#[must_use = "this method returns a new `SpanGuard` that will be immediately dropped unless used"]
pub fn push_prop<K: ToStr, V: ToValue>(
self,
key: K,
value: V,
) -> SpanGuard<'a, T, And<(K, V), P>, F> {
self.push_props((key, value))
}
#[must_use = "this method returns a new `SpanGuard` that will be immediately dropped unless used"]
pub fn push_props<U: Props>(self, props: U) -> SpanGuard<'a, T, And<U, P>, F> {
self.map_props(|current| props.and_props(current))
}
pub fn props_mut(&mut self) -> Option<&mut P> {
self.data.as_mut().map(|data| &mut data.props)
}
pub fn complete(mut self) -> bool {
self.complete_default()
}
fn complete_default(&mut self) -> bool {
if let (SpanGuardState::Started(timer), Some(data), Some(completion)) =
(self.state.take(), self.data.take(), self.completion.take())
{
completion.complete(Span::new(data.mdl, data.name, timer, data.props));
true
} else {
false
}
}
pub fn complete_with(mut self, completion: impl Completion) -> bool {
if let (SpanGuardState::Started(timer), Some(data), Some(_)) =
(self.state.take(), self.data.take(), self.completion.take())
{
completion.complete(Span::new(data.mdl, data.name, timer, data.props));
true
} else {
false
}
}
}
pub mod completion {
use emit_core::{
ctxt::Ctxt,
emitter::Emitter,
empty::Empty,
event::ToEvent,
props::{ErasedProps, Props},
template::Template,
value::{ToValue, Value},
well_known::{KEY_ERR, KEY_LVL},
};
use crate::{level::Level, span::Span};
use core::fmt;
pub trait Completion {
fn complete<P: Props>(&self, span: Span<P>);
}
impl<'a, C: Completion + ?Sized> Completion for &'a C {
fn complete<P: Props>(&self, span: Span<P>) {
(**self).complete(span)
}
}
impl Completion for Empty {
fn complete<P: Props>(&self, _: Span<P>) {}
}
pub struct Default<'a, E, C, L = Level> {
emitter: E,
ctxt: C,
tpl: Option<Template<'a>>,
lvl: Option<L>,
panic_lvl: Option<L>,
}
impl<'a, E: Emitter, C: Ctxt, L: ToValue> Completion for Default<'a, E, C, L> {
fn complete<P: Props>(&self, span: Span<P>) {
fn is_panicking() -> bool {
#[cfg(feature = "std")]
{
std::thread::panicking()
}
#[cfg(not(feature = "std"))]
{
false
}
}
let completion_props = if is_panicking() {
[
self.panic_lvl
.as_ref()
.map(|lvl| Value::from_any(lvl))
.or_else(|| Some(Value::from_any(&Level::Error)))
.map(|lvl| (KEY_LVL, lvl)),
Some((KEY_ERR, Value::from_any(&PanicError::UNKNOWN))),
]
} else {
[
self.lvl
.as_ref()
.map(|lvl| Value::from_any(lvl))
.map(|lvl| (KEY_LVL, lvl)),
None,
]
};
let tpl = self.tpl.as_ref().unwrap_or_else(|| span.tpl()).by_ref();
let evt = span
.to_event()
.with_tpl(tpl)
.map_props(|span_props| completion_props.and_props(span_props));
emit_core::emit(&self.emitter, Empty, &self.ctxt, Empty, evt);
}
}
impl<'a, E, C, L> Default<'a, E, C, L> {
pub const fn new(emitter: E, ctxt: C) -> Self {
Default {
emitter,
ctxt,
tpl: None,
lvl: None,
panic_lvl: None,
}
}
pub fn with_lvl(self, lvl: L) -> Self {
Default {
emitter: self.emitter,
ctxt: self.ctxt,
tpl: self.tpl,
lvl: Some(lvl),
panic_lvl: self.panic_lvl,
}
}
pub fn with_panic_lvl(self, lvl: L) -> Self {
Default {
emitter: self.emitter,
ctxt: self.ctxt,
tpl: self.tpl,
lvl: self.lvl,
panic_lvl: Some(lvl),
}
}
pub fn with_tpl<'b>(self, tpl: impl Into<Template<'b>>) -> Default<'b, E, C, L> {
Default {
emitter: self.emitter,
ctxt: self.ctxt,
tpl: Some(tpl.into()),
lvl: self.lvl,
panic_lvl: self.panic_lvl,
}
}
}
pub const fn default<'a, E: Emitter, C: Ctxt>(emitter: E, ctxt: C) -> Default<'a, E, C> {
Default::new(emitter, ctxt)
}
pub(crate) struct PanicError {
#[cfg(feature = "std")]
payload: Option<std::borrow::Cow<'static, str>>,
#[cfg(not(feature = "std"))]
payload: Option<&'static str>,
}
impl PanicError {
const UNKNOWN: Self = PanicError { payload: None };
#[cfg(feature = "std")]
pub(crate) fn extract(payload: &Box<dyn std::any::Any + Send>) -> Self {
if let Some(payload) = payload.downcast_ref::<&str>() {
return PanicError {
payload: Some(std::borrow::Cow::Borrowed(payload)),
};
}
if let Some(payload) = payload.downcast_ref::<std::string::String>() {
return PanicError {
payload: Some(std::borrow::Cow::Owned(payload.clone())),
};
}
PanicError { payload: None }
}
pub(crate) fn get(&self) -> &str {
let Some(ref payload) = self.payload else {
return "panicked";
};
payload
}
}
impl fmt::Debug for PanicError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(self.get(), f)
}
}
impl fmt::Display for PanicError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(self.get(), f)
}
}
#[cfg(feature = "std")]
impl std::error::Error for PanicError {}
impl ToValue for PanicError {
fn to_value(&self) -> Value<'_> {
#[cfg(feature = "std")]
{
Value::capture_error(self)
}
#[cfg(not(feature = "std"))]
{
Value::capture_display(self)
}
}
}
pub struct FromEmitter<E>(E);
impl<E: Emitter> Completion for FromEmitter<E> {
fn complete<P: Props>(&self, span: Span<P>) {
self.0.emit(span)
}
}
impl<E> FromEmitter<E> {
pub const fn new(emitter: E) -> Self {
FromEmitter(emitter)
}
}
pub const fn from_emitter<E: Emitter>(emitter: E) -> FromEmitter<E> {
FromEmitter(emitter)
}
pub struct FromFn<F = fn(Span<&dyn ErasedProps>)>(F);
pub const fn from_fn<F: Fn(Span<&dyn ErasedProps>)>(f: F) -> FromFn<F> {
FromFn(f)
}
impl<F> FromFn<F> {
pub const fn new(completion: F) -> FromFn<F> {
FromFn(completion)
}
}
impl<F: Fn(Span<&dyn ErasedProps>)> Completion for FromFn<F> {
fn complete<P: Props>(&self, span: Span<P>) {
(self.0)(span.erase())
}
}
mod internal {
use super::*;
pub trait DispatchCompletion {
fn dispatch_complete(&self, span: Span<&dyn ErasedProps>);
}
pub trait SealedCompletion {
fn erase_completion(&self) -> crate::internal::Erased<&dyn DispatchCompletion>;
}
}
pub trait ErasedCompletion: internal::SealedCompletion {}
impl<T: Completion> ErasedCompletion for T {}
impl<T: Completion> internal::SealedCompletion for T {
fn erase_completion(&self) -> crate::internal::Erased<&dyn internal::DispatchCompletion> {
crate::internal::Erased(self)
}
}
impl<T: Completion> internal::DispatchCompletion for T {
fn dispatch_complete(&self, span: Span<&dyn ErasedProps>) {
self.complete(span)
}
}
impl<'a> Completion for dyn ErasedCompletion + 'a {
fn complete<P: Props>(&self, span: Span<P>) {
self.erase_completion().0.dispatch_complete(span.erase())
}
}
impl<'a> Completion for dyn ErasedCompletion + Send + Sync + 'a {
fn complete<P: Props>(&self, span: Span<P>) {
(self as &(dyn ErasedCompletion + 'a)).complete(span)
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::cell::Cell;
use emit_core::{emitter, path::Path};
#[test]
fn from_fn_completion() {
let called = Cell::new(false);
let completion = from_fn(|span| {
assert_eq!("test", span.name());
called.set(true);
});
completion.complete(Span::new(Path::new_raw("test"), "test", Empty, Empty));
assert!(called.get());
}
#[test]
fn erased_completion() {
let called = Cell::new(false);
let completion = from_fn(|span| {
assert_eq!("test", span.name());
called.set(true);
});
let completion = &completion as &dyn ErasedCompletion;
completion.complete(Span::new(Path::new_raw("test"), "test", Empty, Empty));
assert!(called.get());
}
#[test]
fn default_completion() {
let called = Cell::new(false);
let completion = default(
emitter::from_fn(|_| {
called.set(true);
}),
Empty,
);
completion.complete(Span::new(Path::new_raw("test"), "test", Empty, Empty));
assert!(called.get());
}
#[test]
fn default_completion_uses_lvl() {
let called = Cell::new(false);
let completion = default(
emitter::from_fn(|evt| {
assert_eq!(Level::Info, evt.props().pull("lvl").unwrap());
called.set(true);
}),
Empty,
)
.with_lvl(Level::Info);
completion.complete(Span::new(Path::new_raw("test"), "test", Empty, Empty));
assert!(called.get());
}
#[test]
fn default_completion_uses_tpl() {
let called = Cell::new(false);
let completion = default(
emitter::from_fn(|evt| {
assert_eq!("test template", evt.msg().to_string());
called.set(true);
}),
Empty,
)
.with_lvl(Level::Info)
.with_tpl(Template::literal("test template"));
completion.complete(Span::new(Path::new_raw("test"), "test", Empty, Empty));
assert!(called.get());
}
#[cfg(feature = "std")]
#[cfg(not(target_arch = "wasm32"))]
struct Guard<T: Completion>(T);
#[cfg(feature = "std")]
#[cfg(not(target_arch = "wasm32"))]
impl<T: Completion> Drop for Guard<T> {
fn drop(&mut self) {
self.0
.complete(Span::new(Path::new_raw("test"), "test", Empty, Empty));
}
}
#[test]
#[cfg(feature = "std")]
#[cfg(not(target_arch = "wasm32"))]
fn default_completion_detects_panics() {
let called = Cell::new(false);
let completion = default(
emitter::from_fn(|evt| {
assert_eq!(Level::Error, evt.props().pull("lvl").unwrap());
assert!(evt.props().get("err").is_some());
called.set(true);
}),
Empty,
);
let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(move || {
let _guard = Guard(completion);
panic!("explicit panic")
}));
assert!(called.get());
}
#[test]
#[cfg(feature = "std")]
#[cfg(not(target_arch = "wasm32"))]
fn default_completion_uses_panic_lvl() {
let called = Cell::new(false);
let completion = default(
emitter::from_fn(|evt| {
assert_eq!(Level::Warn, evt.props().pull("lvl").unwrap());
called.set(true);
}),
Empty,
)
.with_panic_lvl(Level::Warn);
let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(move || {
let _guard = Guard(completion);
panic!("explicit panic")
}));
assert!(called.get());
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[cfg(all(feature = "std", feature = "rand", not(miri)))]
use emit_core::filter;
use std::time::Duration;
#[cfg(all(feature = "std", feature = "rand", not(miri)))]
use std::cell::Cell;
use crate::Timestamp;
#[test]
fn span_id_parse() {
for (case, expected) in [
(
"0123456789abcdef",
Ok(SpanId::from_u64(0x0123456789abcdef).unwrap()),
),
(
"0000000000000001",
Ok(SpanId::from_u64(0x0000000000000001).unwrap()),
),
("0000000000000000", Err(ParseIdError {})),
("0x00000000000001", Err(ParseIdError {})),
("0x0000000000000001", Err(ParseIdError {})),
("1", Err(ParseIdError {})),
("", Err::<SpanId, ParseIdError>(ParseIdError {})),
] {
match expected {
Ok(expected) => {
assert_eq!(expected, SpanId::try_from_hex(case).unwrap());
assert_eq!(expected, SpanId::try_from_hex(case).unwrap());
}
Err(e) => assert_eq!(
e.to_string(),
SpanId::try_from_hex(case).unwrap_err().to_string()
),
}
}
}
#[test]
fn trace_id_parse() {
for (case, expected) in [
(
"0123456789abcdef0123456789abcdef",
Ok(TraceId::from_u128(0x0123456789abcdef0123456789abcdef).unwrap()),
),
(
"00000000000000000000000000000001",
Ok(TraceId::from_u128(0x00000000000000000000000000000001).unwrap()),
),
("00000000000000000000000000000000", Err(ParseIdError {})),
("0x000000000000000000000000000001", Err(ParseIdError {})),
("0x00000000000000000000000000000001", Err(ParseIdError {})),
("1", Err(ParseIdError {})),
("", Err::<TraceId, ParseIdError>(ParseIdError {})),
] {
match expected {
Ok(expected) => assert_eq!(expected, TraceId::try_from_hex(case).unwrap()),
Err(e) => assert_eq!(
e.to_string(),
TraceId::try_from_hex(case).unwrap_err().to_string()
),
}
}
}
#[test]
fn span_id_fmt() {
for (case, expected) in [
(SpanId::from_u64(1).unwrap(), "0000000000000001"),
(
SpanId::from_u64(0x0123456789abcdef).unwrap(),
"0123456789abcdef",
),
] {
assert_eq!(expected, case.to_string());
assert_eq!(expected, str::from_utf8(&case.to_hex()).unwrap());
}
}
#[test]
fn trace_id_fmt() {
for (case, expected) in [
(
TraceId::from_u128(1).unwrap(),
"00000000000000000000000000000001",
),
(
TraceId::from_u128(0x0123456789abcdef0123456789abcdef).unwrap(),
"0123456789abcdef0123456789abcdef",
),
] {
assert_eq!(expected, case.to_string());
assert_eq!(expected, str::from_utf8(&case.to_hex()).unwrap());
}
}
#[test]
fn span_id_roundtrip() {
let id = SpanId::new(NonZeroU64::new(u64::MAX / 2).unwrap());
let fmt = id.to_string();
let parsed: SpanId = fmt.parse().unwrap();
assert_eq!(id, parsed, "{}", fmt);
}
#[test]
fn trace_id_roundtrip() {
let id = TraceId::new(NonZeroU128::new(u128::MAX / 2).unwrap());
let fmt = id.to_string();
let parsed: TraceId = fmt.parse().unwrap();
assert_eq!(id, parsed, "{}", fmt);
}
#[test]
fn span_id_random_empty() {
assert!(SpanId::random(Empty).is_none());
}
#[test]
#[cfg(feature = "rand")]
fn span_id_random_rand() {
assert!(SpanId::random(crate::platform::DefaultRng::new()).is_some());
}
#[test]
fn trace_id_random_empty() {
assert!(TraceId::random(Empty).is_none());
}
#[test]
#[cfg(feature = "rand")]
fn trace_id_random_rand() {
assert!(TraceId::random(crate::platform::DefaultRng::new()).is_some());
}
#[test]
fn span_id_to_from_value() {
let id = SpanId::from_u64(u64::MAX / 2).unwrap();
assert_eq!(id, SpanId::from_value(id.to_value()).unwrap());
}
#[test]
fn span_id_from_value_string() {
assert_eq!(
SpanId::from_u64(0x0123456789abcdef).unwrap(),
Value::from("0123456789abcdef").cast().unwrap()
);
}
#[test]
fn span_id_from_value_u64() {
assert_eq!(
SpanId::from_u64(0x0123456789abcdef).unwrap(),
Value::from(0x0123456789abcdefu64).cast().unwrap()
);
}
#[test]
fn trace_id_to_from_value() {
let id = TraceId::from_u128(u128::MAX / 2).unwrap();
assert_eq!(id, TraceId::from_value(id.to_value()).unwrap());
}
#[test]
fn trace_id_from_value_string() {
assert_eq!(
TraceId::from_u128(0x0123456789abcdef0123456789abcdef).unwrap(),
Value::from("0123456789abcdef0123456789abcdef")
.cast()
.unwrap()
);
}
#[test]
fn trace_id_from_value_u128() {
assert_eq!(
TraceId::from_u128(0x0123456789abcdef0123456789abcdef).unwrap(),
Value::from(0x0123456789abcdef0123456789abcdefu128)
.cast()
.unwrap()
);
}
#[cfg(feature = "sval")]
#[test]
fn span_id_stream() {
sval_test::assert_tokens(
&SpanId::from_u64(0x0123456789abcdef).unwrap(),
&[
sval_test::Token::TextBegin(None),
sval_test::Token::TextFragmentComputed("0123456789abcdef".to_owned()),
sval_test::Token::TextEnd,
],
);
}
#[cfg(feature = "serde")]
#[test]
fn span_id_serialize() {
serde_test::assert_ser_tokens(
&SpanId::from_u64(0x0123456789abcdef).unwrap(),
&[serde_test::Token::Str("0123456789abcdef")],
);
}
#[cfg(feature = "sval")]
#[test]
fn trace_id_stream() {
sval_test::assert_tokens(
&TraceId::from_u128(0x0123456789abcdef0123456789abcdef).unwrap(),
&[
sval_test::Token::TextBegin(None),
sval_test::Token::TextFragmentComputed(
"0123456789abcdef0123456789abcdef".to_owned(),
),
sval_test::Token::TextEnd,
],
);
}
#[cfg(feature = "serde")]
#[test]
fn trace_id_serialize() {
serde_test::assert_ser_tokens(
&TraceId::from_u128(0x0123456789abcdef0123456789abcdef).unwrap(),
&[serde_test::Token::Str("0123456789abcdef0123456789abcdef")],
);
}
#[test]
fn span_link_parse() {
for (case, expected) in [
(
"0123456789abcdef0123456789abcdef-0123456789abcdef",
Ok::<SpanLink, ParseLinkError>(SpanLink::new(
TraceId::from_u128(0x0123456789abcdef0123456789abcdef).unwrap(),
SpanId::from_u64(0x0123456789abcdef).unwrap(),
)),
),
(
"00000000000000000000000000000001-0000000000000001",
Ok::<SpanLink, ParseLinkError>(SpanLink::new(
TraceId::from_u128(0x1).unwrap(),
SpanId::from_u64(0x1).unwrap(),
)),
),
(
"00000000000000000000000000000000-0000000000000000",
Err(ParseLinkError {}),
),
(
"0123456789abcdef0123456789abcde-0123456789abcdef",
Err(ParseLinkError {}),
),
(
"0123456789abcdef0123456789abcdef-0123456789abcde",
Err(ParseLinkError {}),
),
(
"0123456789abcdef0123456789abcdef 0123456789abcdef",
Err(ParseLinkError {}),
),
(
"0123456789abcdef0123456789abcdef0123456789abcdef",
Err(ParseLinkError {}),
),
] {
match expected {
Ok(expected) => assert_eq!(expected, SpanLink::try_from_str(case).unwrap()),
Err(e) => assert_eq!(
e.to_string(),
SpanLink::try_from_str(case).unwrap_err().to_string()
),
}
}
}
#[test]
fn span_link_fmt() {
let link = SpanLink::new(
TraceId::from_u128(0x0123456789abcdef0123456789abcdef).unwrap(),
SpanId::from_u64(0x0123456789abcdef).unwrap(),
);
assert_eq!(
"0123456789abcdef0123456789abcdef-0123456789abcdef",
link.to_string()
);
}
#[test]
fn span_link_roundtrip() {
let link = SpanLink::new(
TraceId::from_u128(0x0123456789abcdef0123456789abcdef).unwrap(),
SpanId::from_u64(0x0123456789abcdef).unwrap(),
);
assert_eq!(link, SpanLink::parse(link).unwrap());
}
#[test]
fn span_link_to_from_value() {
let link = SpanLink::new(
TraceId::from_u128(0x0123456789abcdef0123456789abcdef).unwrap(),
SpanId::from_u64(0x0123456789abcdef).unwrap(),
);
assert_eq!(link, SpanLink::from_value(link.to_value()).unwrap());
}
#[test]
fn span_link_from_value_string() {
assert_eq!(
SpanLink::new(
TraceId::from_u128(0x0123456789abcdef0123456789abcdef).unwrap(),
SpanId::from_u64(0x0123456789abcdef).unwrap(),
),
Value::from("0123456789abcdef0123456789abcdef-0123456789abcdef")
.cast()
.unwrap()
);
}
#[cfg(feature = "sval")]
#[test]
fn span_link_stream() {
sval_test::assert_tokens(
&SpanLink::new(
TraceId::from_u128(0x0123456789abcdef0123456789abcdef).unwrap(),
SpanId::from_u64(0x0123456789abcdef).unwrap(),
),
&[
sval_test::Token::TextBegin(None),
sval_test::Token::TextFragmentComputed(
"0123456789abcdef0123456789abcdef-0123456789abcdef".to_owned(),
),
sval_test::Token::TextEnd,
],
);
}
#[cfg(feature = "serde")]
#[test]
fn span_link_serialize() {
serde_test::assert_ser_tokens(
&SpanLink::new(
TraceId::from_u128(0x0123456789abcdef0123456789abcdef).unwrap(),
SpanId::from_u64(0x0123456789abcdef).unwrap(),
),
&[serde_test::Token::Str(
"0123456789abcdef0123456789abcdef-0123456789abcdef",
)],
);
}
#[test]
fn span_kind_parse() {
for (case, expected) in [
("internal", Some(SpanKind::Internal)),
(" internal ", Some(SpanKind::Internal)),
("server", Some(SpanKind::Server)),
("client", Some(SpanKind::Client)),
("producer", Some(SpanKind::Producer)),
("consumer", Some(SpanKind::Consumer)),
("", None),
("int", None),
("internalx", None),
] {
assert_eq!(expected, SpanKind::try_from_str(case).ok());
}
}
#[test]
fn span_kind_roundtrip() {
for case in [
SpanKind::Internal,
SpanKind::Server,
SpanKind::Client,
SpanKind::Producer,
SpanKind::Consumer,
] {
assert_eq!(case, SpanKind::try_from_str(&case.to_string()).unwrap());
}
}
#[cfg(feature = "sval")]
#[test]
fn span_kind_stream() {
sval_test::assert_tokens(
&SpanKind::Internal,
&[
sval_test::Token::TextBegin(Some(8)),
sval_test::Token::TextFragment("internal"),
sval_test::Token::TextEnd,
],
);
}
#[cfg(feature = "serde")]
#[test]
fn span_kind_serialize() {
serde_test::assert_ser_tokens(&SpanKind::Internal, &[serde_test::Token::Str("internal")]);
}
#[test]
#[cfg(all(feature = "std", feature = "rand", not(miri)))]
fn span_ctxt_new() {
let rng = crate::platform::DefaultRng::new();
let ctxt = crate::platform::DefaultCtxt::new();
let root = SpanCtxt::current(&ctxt);
assert_eq!(SpanCtxt::empty(), root);
let root = SpanCtxt::new_root(&rng);
assert!(root.span_id.is_some());
assert!(root.trace_id.is_some());
assert!(root.span_parent.is_none());
let mut frame = ctxt.open_push(root);
ctxt.enter(&mut frame);
let current = SpanCtxt::current(&ctxt);
assert_eq!(root, current);
let root = current;
let child = SpanCtxt::new_child(&root, &rng);
assert_eq!(root.trace_id, child.trace_id);
assert_ne!(root.span_id, child.span_id);
assert!(child.span_id.is_some());
assert_eq!(root.span_id, child.span_parent);
ctxt.exit(&mut frame);
ctxt.close(frame);
}
#[test]
fn span_new() {
let span = Span::new(
Path::new_raw("test"),
"my span",
Timestamp::from_unix(Duration::from_secs(1)),
("span_prop", true),
);
assert_eq!("test", span.mdl());
assert_eq!(
Timestamp::from_unix(Duration::from_secs(1)).unwrap(),
span.extent().unwrap().as_point()
);
assert_eq!("my span", span.name());
assert_eq!(true, span.props().pull::<bool, _>("span_prop").unwrap());
}
#[test]
fn span_to_event() {
let span = Span::new(
Path::new_raw("test"),
"my span",
Timestamp::from_unix(Duration::from_secs(1)),
("span_prop", true),
);
let evt = span.to_event();
assert_eq!("test", evt.mdl());
assert_eq!(
Timestamp::from_unix(Duration::from_secs(1)).unwrap(),
evt.extent().unwrap().as_point()
);
assert_eq!("my span completed", evt.msg().to_string());
assert_eq!(
"my span",
evt.props().pull::<Str, _>(KEY_SPAN_NAME).unwrap()
);
assert_eq!(true, evt.props().pull::<bool, _>("span_prop").unwrap());
assert_eq!(
Kind::Span,
evt.props().pull::<Kind, _>(KEY_EVT_KIND).unwrap()
);
}
#[test]
fn span_to_event_uses_tpl() {
assert_eq!(
"test",
Span::new(
Path::new_raw("test"),
"my span",
Timestamp::from_unix(Duration::from_secs(1)),
("span_prop", true),
)
.with_tpl(Template::literal("test"))
.to_event()
.msg()
.to_string(),
);
}
#[test]
fn span_to_extent() {
for (case, expected) in [
(
Some(Timestamp::from_unix(Duration::from_secs(1)).unwrap()),
Some(Extent::point(
Timestamp::from_unix(Duration::from_secs(1)).unwrap(),
)),
),
(None, None),
] {
let span = Span::new(Path::new_raw("test"), "my span", case, ("span_prop", true));
let extent = span.to_extent();
assert_eq!(
expected.map(|extent| extent.as_range().cloned()),
extent.map(|extent| extent.as_range().cloned())
);
}
}
#[cfg(all(feature = "std", feature = "rand", not(miri)))]
struct MyClock(Cell<u64>);
#[cfg(all(feature = "std", feature = "rand", not(miri)))]
impl Clock for MyClock {
fn now(&self) -> Option<crate::Timestamp> {
let ts = crate::Timestamp::from_unix(Duration::from_secs(self.0.get()));
self.0.set(self.0.get() + 1);
ts
}
}
#[test]
#[cfg(all(feature = "std", feature = "rand", not(miri)))]
fn active_span_new() {
let clock = MyClock(Cell::new(0));
let rng = crate::platform::DefaultRng::new();
let ctxt = crate::platform::DefaultCtxt::new();
let complete_called = Cell::new(false);
let (mut guard, frame) = SpanGuard::new(
filter::from_fn(|evt| {
assert_eq!(2, evt.props().pull::<usize, _>("ctxt_prop").unwrap());
assert!(evt.props().get("trace_id").is_some());
assert!(evt.props().get("span_id").is_some());
true
}),
&ctxt,
&clock,
&rng,
completion::from_fn(|evt| {
assert_eq!(
Timestamp::from_unix(Duration::from_secs(0)).unwrap(),
evt.extent().unwrap().as_range().unwrap().start
);
assert_eq!(
Timestamp::from_unix(Duration::from_secs(1)).unwrap(),
evt.extent().unwrap().as_range().unwrap().end
);
assert_eq!("test", evt.mdl());
assert_eq!("span", evt.name());
assert_eq!(1, evt.props().pull::<usize, _>("event_prop").unwrap());
ctxt.with_current(|props| {
assert_eq!(2, props.pull::<usize, _>("ctxt_prop").unwrap());
});
let current_ctxt = SpanCtxt::current(&ctxt);
assert_ne!(current_ctxt, SpanCtxt::empty());
complete_called.set(true);
}),
("ctxt_prop", 2),
Path::new_raw("test"),
"span",
("event_prop", 1),
);
assert!(guard.is_enabled());
frame.call(move || {
guard.start();
drop(guard);
});
assert!(complete_called.get());
}
#[test]
#[cfg(all(feature = "std", feature = "rand", not(miri)))]
fn active_span_unstarted_complete() {
let clock = MyClock(Cell::new(0));
let rng = crate::platform::DefaultRng::new();
let ctxt = crate::platform::DefaultCtxt::new();
let complete_called = Cell::new(false);
let (guard, frame) = SpanGuard::new(
filter::from_fn(|_| true),
&ctxt,
&clock,
&rng,
completion::from_fn(|_| {}),
Empty,
Path::new_raw("test"),
"span",
Empty,
);
assert!(guard.is_enabled());
frame.call(move || {
drop(guard);
});
assert!(!complete_called.get());
}
#[test]
#[cfg(all(feature = "std", feature = "rand", not(miri)))]
fn active_span_new_disabled() {
let rng = crate::platform::DefaultRng::new();
let clock = crate::platform::DefaultClock::new();
let ctxt = crate::platform::DefaultCtxt::new();
let complete_called = Cell::new(false);
let (mut guard, frame) = SpanGuard::new(
filter::from_fn(|_| false),
&ctxt,
&clock,
&rng,
completion::from_fn(|_| {
complete_called.set(true);
}),
Empty,
Path::new_raw("test"),
"span",
Empty,
);
assert!(!guard.is_enabled());
frame.call(move || {
guard.start();
drop(guard);
});
assert!(!complete_called.get());
}
#[test]
#[cfg(all(feature = "std", feature = "rand", not(miri)))]
fn active_span_custom_complete() {
let ctxt = crate::platform::DefaultCtxt::new();
let clock = crate::platform::DefaultClock::new();
let rng = crate::platform::DefaultRng::new();
let custom_complete_called = Cell::new(false);
let default_complete_called = Cell::new(false);
let (mut guard, _) = SpanGuard::new(
filter::from_fn(|_| true),
&ctxt,
&clock,
&rng,
completion::from_fn(|_| {
default_complete_called.set(true);
}),
Empty,
Path::new_raw("test"),
"span",
Empty,
);
assert!(guard.is_enabled());
guard.start();
guard.complete_with(completion::from_fn(|_| {
custom_complete_called.set(true);
}));
assert!(!default_complete_called.get());
assert!(custom_complete_called.get());
}
#[test]
#[cfg(all(feature = "std", feature = "rand", not(miri)))]
fn active_span_with_props() {
let clock = MyClock(Cell::new(0));
let rng = crate::platform::DefaultRng::new();
let ctxt = crate::platform::DefaultCtxt::new();
let complete_called = Cell::new(false);
let (mut guard, frame) = SpanGuard::new(
filter::from_fn(|_| true),
&ctxt,
&clock,
&rng,
completion::from_fn(|evt| {
assert_eq!(2, evt.props().pull::<usize, _>("event_prop").unwrap());
complete_called.set(true);
}),
Empty,
Path::new_raw("test"),
"span",
("event_prop", 1),
);
frame.call(move || {
guard.start();
let guard = guard.with_props(("event_prop", 2));
drop(guard);
});
assert!(complete_called.get());
}
}