diff --git a/core/src/event.rs b/core/src/event.rs index b524fc5..349031a 100644 --- a/core/src/event.rs +++ b/core/src/event.rs @@ -121,7 +121,7 @@ impl<'a, P: Props> fmt::Debug for Event<'a, P> { let mut f = f.debug_struct(""); self.0.for_each(|k, v| { - f.field(k.as_str(), &v); + f.field(k.get(), &v); ControlFlow::Continue(()) }); diff --git a/core/src/path.rs b/core/src/path.rs index 5364c79..e903bc5 100644 --- a/core/src/path.rs +++ b/core/src/path.rs @@ -1,4 +1,4 @@ -use core::fmt; +use core::{fmt, str}; use crate::{ str::Str, @@ -34,30 +34,36 @@ impl<'a> FromValue<'a> for Path<'a> { impl Path<'static> { pub const fn new(path: &'static str) -> Self { - Path(Str::new(path)) + Path::new_str(Str::new(path)) } } impl<'a> Path<'a> { pub const fn new_ref(path: &'a str) -> Self { - Path(Str::new_ref(path)) + Self::new_str(Str::new_ref(path)) + } + + pub const fn new_str(path: Str<'a>) -> Self { + Path(path) } pub fn by_ref<'b>(&'b self) -> Path<'b> { Path(self.0.by_ref()) } - pub fn segments(&self) -> impl Iterator { - self.0.as_str().split("::") + pub fn segments(&self) -> Segments { + Segments { + inner: self.0.get().split("::"), + } } - pub fn as_str(&self) -> Option<&str> { - Some(self.0.as_str()) + pub fn as_str(&self) -> Option<&Str<'a>> { + Some(&self.0) } pub fn is_child_of<'b>(&self, other: &Path<'b>) -> bool { - let child = self.0.as_str(); - let parent = other.0.as_str(); + let child = self.0.get(); + let parent = other.0.get(); if child.len() >= parent.len() && child.is_char_boundary(parent.len()) { let (child_prefix, child_suffix) = child.split_at(parent.len()); @@ -69,6 +75,18 @@ impl<'a> Path<'a> { } } +pub struct Segments<'a> { + inner: str::Split<'a, &'static str>, +} + +impl<'a> Iterator for Segments<'a> { + type Item = Str<'a>; + + fn next(&mut self) -> Option { + self.inner.next().map(Str::new_ref) + } +} + impl<'a> Eq for Path<'a> {} impl<'a, 'b> PartialEq> for Path<'a> { diff --git a/core/src/str.rs b/core/src/str.rs index 0a8f5cd..b0f6dd6 100644 --- a/core/src/str.rs +++ b/core/src/str.rs @@ -13,13 +13,13 @@ pub struct Str<'k> { impl<'k> fmt::Debug for Str<'k> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(self.as_str(), f) + fmt::Debug::fmt(self.get(), f) } } impl<'k> fmt::Display for Str<'k> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(self.as_str(), f) + fmt::Display::fmt(self.get(), f) } } @@ -100,24 +100,24 @@ impl<'k> Str<'k> { } } - pub const fn as_str(&self) -> &str { + pub const fn get(&self) -> &str { unsafe { &(*self.value) } } - pub const fn as_static_str(&self) -> Option<&'static str> { + pub const fn get_static(&self) -> Option<&'static str> { self.value_static } } impl<'a> hash::Hash for Str<'a> { fn hash(&self, state: &mut H) { - self.as_str().hash(state) + self.get().hash(state) } } impl<'a, 'b> PartialEq> for Str<'a> { fn eq(&self, other: &Str<'b>) -> bool { - self.as_str() == other.as_str() + self.get() == other.get() } } @@ -125,49 +125,49 @@ impl<'a> Eq for Str<'a> {} impl<'a> PartialEq for Str<'a> { fn eq(&self, other: &str) -> bool { - self.as_str() == other + self.get() == other } } impl<'a> PartialEq> for str { fn eq(&self, other: &Str<'a>) -> bool { - self == other.as_str() + self == other.get() } } impl<'a, 'b> PartialEq<&'b str> for Str<'a> { fn eq(&self, other: &&'b str) -> bool { - self.as_str() == *other + self.get() == *other } } impl<'a, 'b> PartialEq> for &'a str { fn eq(&self, other: &Str<'b>) -> bool { - *self == other.as_str() + *self == other.get() } } impl<'a, 'b> PartialOrd> for Str<'a> { fn partial_cmp(&self, other: &Str<'b>) -> Option { - self.as_str().partial_cmp(other.as_str()) + self.get().partial_cmp(other.get()) } } impl<'a> Ord for Str<'a> { fn cmp(&self, other: &Self) -> core::cmp::Ordering { - self.as_str().cmp(other.as_str()) + self.get().cmp(other.get()) } } impl<'k> Borrow for Str<'k> { fn borrow(&self) -> &str { - self.as_str() + self.get() } } impl<'k> AsRef for Str<'k> { fn as_ref(&self) -> &str { - self.as_str() + self.get() } } @@ -179,7 +179,7 @@ impl<'a> From<&'a str> for Str<'a> { impl<'k> ToValue for Str<'k> { fn to_value(&self) -> Value { - Value::from(self.as_str()) + Value::from(self.get()) } } @@ -230,10 +230,10 @@ impl<'k> sval::Value for Str<'k> { #[cfg(feature = "sval")] impl<'k> sval_ref::ValueRef<'k> for Str<'k> { fn stream_ref + ?Sized>(&self, stream: &mut S) -> sval::Result { - if let Some(k) = self.as_static_str() { + if let Some(k) = self.get_static() { stream.value(k) } else { - stream.value_computed(self.as_str()) + stream.value_computed(self.get()) } } } @@ -241,7 +241,7 @@ impl<'k> sval_ref::ValueRef<'k> for Str<'k> { #[cfg(feature = "serde")] impl<'k> serde::Serialize for Str<'k> { fn serialize(&self, serializer: S) -> Result { - self.as_str().serialize(serializer) + self.get().serialize(serializer) } } @@ -275,14 +275,14 @@ mod alloc_support { pub fn to_cow(&self) -> Cow<'static, str> { match self.value_static { Some(key) => Cow::Borrowed(key), - None => Cow::Owned(self.as_str().to_owned()), + None => Cow::Owned(self.get().to_owned()), } } pub fn to_owned(&self) -> Str<'static> { match self.value_static { Some(key) => Str::new(key), - None => Str::new_owned(self.as_str()), + None => Str::new_owned(self.get()), } } } diff --git a/core/src/template.rs b/core/src/template.rs index ad777f3..dc94be7 100644 --- a/core/src/template.rs +++ b/core/src/template.rs @@ -22,7 +22,7 @@ enum TemplateKind<'a> { } impl<'a> TemplateKind<'a> { - fn parts(&self) -> &[Part] { + fn parts(&self) -> &[Part<'a>] { match self { TemplateKind::Literal(ref parts) => parts, TemplateKind::Parts(parts) => parts, @@ -78,20 +78,13 @@ impl<'a> Template<'a> { } } - pub fn as_str(&self) -> Option<&str> { + pub fn as_str(&'_ self) -> Option<&'_ Str<'a>> { match self.0.parts() { [part] => part.as_str(), _ => None, } } - pub fn as_static_str(&self) -> Option<&'static str> { - match self.0.parts() { - [Part(PartKind::Text { value, .. })] => value.as_static_str(), - _ => None, - } - } - pub fn render<'b, P>(&'b self, props: P) -> Render<'b, P> { Render { tpl: self.by_ref(), @@ -103,7 +96,7 @@ impl<'a> Template<'a> { impl<'a> ToValue for Template<'a> { fn to_value(&self) -> Value { if let Some(tpl) = self.as_str() { - Value::from(tpl) + Value::from_any(tpl) } else { Value::from_display(self) } @@ -131,8 +124,8 @@ impl<'a, 'b> PartialEq> for Template<'a> { match (&ap.0, &bp.0) { (PartKind::Text { value: ref a }, PartKind::Text { value: ref b }) => { - let a = a.as_str(); - let b = b.as_str(); + let a = a.get(); + let b = b.get(); let at = &a[ati..]; let bt = &b[bti..]; @@ -181,7 +174,7 @@ impl<'a, 'b> PartialEq> for Template<'a> { return false; }; - if !value.as_str().is_empty() { + if !value.get().is_empty() { return false; } } @@ -206,13 +199,9 @@ impl<'a, P> Render<'a, P> { } } - pub fn as_str(&self) -> Option<&str> { + pub fn as_str(&'_ self) -> Option<&'_ Str<'a>> { self.tpl.as_str() } - - pub fn as_static_str(&self) -> Option<&'static str> { - self.tpl.as_static_str() - } } impl<'a, P: Props> Render<'a, P> { @@ -228,7 +217,7 @@ impl<'a, P: Props> Render<'a, P> { impl<'a, P: Props> ToValue for Render<'a, P> { fn to_value(&self) -> Value { if let Some(tpl) = self.as_str() { - Value::from(tpl) + Value::from_any(tpl) } else { Value::from_display(self) } @@ -407,9 +396,9 @@ impl<'a> Part<'a> { }) } - pub const fn as_str(&self) -> Option<&str> { + pub const fn as_str(&'_ self) -> Option<&'_ Str<'a>> { match self.0 { - PartKind::Text { ref value, .. } => Some(value.as_str()), + PartKind::Text { ref value, .. } => Some(value), _ => None, } } @@ -444,13 +433,13 @@ impl<'a> Part<'a> { fn write(&self, mut writer: impl Write, props: impl Props) -> fmt::Result { match self.0 { - PartKind::Text { ref value, .. } => writer.write_text(value.as_str()), + PartKind::Text { ref value, .. } => writer.write_text(value.get()), PartKind::Hole { ref label, ref formatter, .. } => { - let label = label.as_str(); + let label = label.get(); if let Some(value) = props.get(label) { if let Some(formatter) = formatter { diff --git a/core/src/value.rs b/core/src/value.rs index 5174737..1bb3bed 100644 --- a/core/src/value.rs +++ b/core/src/value.rs @@ -20,6 +20,35 @@ impl<'v> Value<'v> { Value(value_bag::ValueBag::from_debug(value)) } + #[cfg(feature = "serde")] + pub fn capture_serde(value: &'v (impl serde::Serialize + 'static)) -> Self { + Value(value_bag::ValueBag::capture_serde1(value)) + } + + #[cfg(feature = "serde")] + pub fn from_serde(value: &'v impl serde::Serialize) -> Self { + Value(value_bag::ValueBag::from_serde1(value)) + } + + #[cfg(feature = "sval")] + pub fn capture_sval(value: &'v (impl sval::Value + 'static)) -> Self { + Value(value_bag::ValueBag::capture_sval2(value)) + } + + #[cfg(feature = "sval")] + pub fn from_sval(value: &'v impl sval::Value) -> Self { + Value(value_bag::ValueBag::from_sval2(value)) + } + + #[cfg(feature = "std")] + pub fn capture_error(value: &'v (impl std::error::Error + 'static)) -> Self { + Value(value_bag::ValueBag::capture_error(value)) + } + + pub fn from_any(value: &'v impl ToValue) -> Self { + value.to_value() + } + pub fn null() -> Self { Value(value_bag::ValueBag::empty()) } @@ -40,8 +69,19 @@ impl<'v> Value<'v> { struct Extract(Option); impl<'v, T: FromStr> value_bag::visit::Visit<'v> for Extract { - fn visit_any(&mut self, _: value_bag::ValueBag) -> Result<(), value_bag::Error> { - Ok(()) + fn visit_any(&mut self, value: value_bag::ValueBag) -> Result<(), value_bag::Error> { + #[cfg(feature = "alloc")] + { + self.0 = value.to_string().parse().ok(); + + Ok(()) + } + #[cfg(not(feature = "alloc"))] + { + let _ = value; + + Ok(()) + } } fn visit_str(&mut self, value: &str) -> Result<(), value_bag::Error> { @@ -147,18 +187,6 @@ impl<'v> FromValue<'v> for Value<'v> { } } -impl<'v> ToValue for value_bag::ValueBag<'v> { - fn to_value(&self) -> Value { - Value(self.by_ref()) - } -} - -impl<'v> From> for Value<'v> { - fn from(value: value_bag::ValueBag<'v>) -> Self { - Value(value) - } -} - impl ToValue for str { fn to_value(&self) -> Value { Value::from(self) @@ -255,6 +283,44 @@ impl<'v, const N: usize> From<&'v [i64; N]> for Value<'v> { } } +impl ToValue for dyn fmt::Debug { + fn to_value(&self) -> Value { + Value(value_bag::ValueBag::from_dyn_debug(self)) + } +} + +impl<'v> From<&'v dyn fmt::Debug> for Value<'v> { + fn from(value: &'v dyn fmt::Debug) -> Self { + Value(value_bag::ValueBag::from_dyn_debug(value)) + } +} + +impl ToValue for dyn fmt::Display { + fn to_value(&self) -> Value { + Value(value_bag::ValueBag::from_dyn_display(self)) + } +} + +impl<'v> From<&'v dyn fmt::Display> for Value<'v> { + fn from(value: &'v dyn fmt::Display) -> Self { + Value(value_bag::ValueBag::from_dyn_display(value)) + } +} + +#[cfg(feature = "std")] +impl ToValue for (dyn std::error::Error + 'static) { + fn to_value(&self) -> Value { + Value(value_bag::ValueBag::from_dyn_error(self)) + } +} + +#[cfg(feature = "std")] +impl<'v> From<&'v (dyn std::error::Error + 'static)> for Value<'v> { + fn from(value: &'v (dyn std::error::Error + 'static)) -> Self { + Value(value_bag::ValueBag::from_dyn_error(value)) + } +} + #[cfg(feature = "alloc")] mod alloc_support { use super::*; @@ -304,24 +370,12 @@ mod alloc_support { } } - impl From for OwnedValue { - fn from(value: value_bag::OwnedValueBag) -> Self { - OwnedValue(value) - } - } - impl ToValue for OwnedValue { fn to_value(&self) -> Value { self.by_ref() } } - impl ToValue for value_bag::OwnedValueBag { - fn to_value(&self) -> Value { - Value(self.by_ref()) - } - } - #[cfg(feature = "sval")] impl sval::Value for OwnedValue { fn stream<'sval, S: sval::Stream<'sval> + ?Sized>( diff --git a/src/lib.rs b/src/lib.rs index a60eb29..7f9c35a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -218,7 +218,11 @@ The [`Timer`] type provides a convenient way to produce an extent that covers a # Ambient context -Emit doesn't require threading loggers through your program directly. You can store ambient state you want events to carry in the current _context_. Emit's context is a stack that can be managed either directly for synchronous operations, or through a future for asynchronous ones. +`emit` doesn't require threading loggers through your program directly. You can store ambient state you want events to carry in the ambient [`Ctxt`]. `emit`'s context is a stack that can be managed either directly for synchronous operations, or through a future for asynchronous ones. + +The [`Frame`] type is a wrapper over a [`Ctxt`] that handles pushing and popping a set of properties automatically. + +Any properties captured by the span macros will be pushed onto the current context. ## Observability signals diff --git a/src/macro_hooks.rs b/src/macro_hooks.rs index 87f3769..8043504 100644 --- a/src/macro_hooks.rs +++ b/src/macro_hooks.rs @@ -135,13 +135,13 @@ where T: fmt::Display + Any, { fn capture(&self) -> Option { - Some(value_bag::ValueBag::capture_display(self).into()) + Some(Value::capture_display(self)) } } impl Capture for dyn fmt::Display { fn capture(&self) -> Option { - Some(value_bag::ValueBag::from_dyn_display(self).into()) + Some(Value::from(self)) } } @@ -150,7 +150,7 @@ where T: fmt::Display, { fn capture(&self) -> Option { - Some(value_bag::ValueBag::from_display(self).into()) + Some(Value::from_display(self)) } } @@ -159,13 +159,13 @@ where T: fmt::Debug + Any, { fn capture(&self) -> Option { - Some(value_bag::ValueBag::capture_debug(self).into()) + Some(Value::capture_debug(self)) } } impl Capture for dyn fmt::Debug { fn capture(&self) -> Option { - Some(value_bag::ValueBag::from_dyn_debug(self).into()) + Some(Value::from(self)) } } @@ -174,7 +174,7 @@ where T: fmt::Debug, { fn capture(&self) -> Option { - Some(value_bag::ValueBag::from_debug(self).into()) + Some(Value::from_debug(self)) } } @@ -202,7 +202,7 @@ where T: sval::Value + Any, { fn capture(&self) -> Option { - Some(value_bag::ValueBag::capture_sval2(self).into()) + Some(Value::capture_sval(self)) } } @@ -212,7 +212,7 @@ where T: sval::Value, { fn capture(&self) -> Option { - Some(value_bag::ValueBag::from_sval2(self).into()) + Some(Value::from_sval(self)) } } @@ -222,7 +222,7 @@ where T: serde::Serialize + Any, { fn capture(&self) -> Option { - Some(value_bag::ValueBag::capture_serde1(self).into()) + Some(Value::capture_serde(self)) } } @@ -232,7 +232,7 @@ where T: serde::Serialize, { fn capture(&self) -> Option { - Some(value_bag::ValueBag::from_serde1(self).into()) + Some(Value::from_serde(self)) } } @@ -242,14 +242,14 @@ where T: Error + 'static, { fn capture(&self) -> Option { - Some(value_bag::ValueBag::capture_error(self).into()) + Some(Value::capture_error(self)) } } #[cfg(feature = "std")] impl<'a> Capture for (dyn Error + 'static) { fn capture(&self) -> Option { - Some(value_bag::ValueBag::from_dyn_error(self).into()) + Some(Value::from(self)) } } diff --git a/targets/file/src/lib.rs b/targets/file/src/lib.rs index e704729..bc1668f 100644 --- a/targets/file/src/lib.rs +++ b/targets/file/src/lib.rs @@ -186,9 +186,9 @@ impl<'a, P: emit::Props> sval::Value for EventValue<'a, P> { self.0.props().for_each(|k, v| { match (|| { - stream.record_value_begin(None, &sval::Label::new_computed(k.as_str()))?; + stream.record_value_begin(None, &sval::Label::new_computed(k.get()))?; stream.value_computed(&v)?; - stream.record_value_end(None, &sval::Label::new_computed(k.as_str()))?; + stream.record_value_end(None, &sval::Label::new_computed(k.get()))?; Ok::<(), sval::Error>(()) })() { diff --git a/targets/log/src/lib.rs b/targets/log/src/lib.rs index fc63d6f..1146f61 100644 --- a/targets/log/src/lib.rs +++ b/targets/log/src/lib.rs @@ -59,7 +59,7 @@ impl log::kv::Source for EmitSource { ) -> Result<(), log::kv::Error> { for (key, value) in &self.0 { visitor.visit_pair( - log::kv::Key::from_str(key.as_str()), + log::kv::Key::from_str(key.get()), log::kv::Value::from_sval(value), )?; } diff --git a/targets/otlp/src/data/logs/log_record.rs b/targets/otlp/src/data/logs/log_record.rs index 896dabd..1631647 100644 --- a/targets/otlp/src/data/logs/log_record.rs +++ b/targets/otlp/src/data/logs/log_record.rs @@ -85,7 +85,7 @@ impl sval::Value for PropsLogRecordAttributes

{ &LOG_RECORD_ATTRIBUTES_LABEL, &LOG_RECORD_ATTRIBUTES_INDEX, |stream| { - stream_attributes(stream, &self.0, |k, v| match k.as_str() { + stream_attributes(stream, &self.0, |k, v| match k.get() { emit::well_known::KEY_LVL => { level = v.by_ref().cast::().unwrap_or_default(); true diff --git a/targets/otlp/src/data/traces/span.rs b/targets/otlp/src/data/traces/span.rs index 37ff677..a23b0dd 100644 --- a/targets/otlp/src/data/traces/span.rs +++ b/targets/otlp/src/data/traces/span.rs @@ -102,7 +102,7 @@ impl sval::Value for PropsSpanAttributes

{ &SPAN_ATTRIBUTES_LABEL, &SPAN_ATTRIBUTES_INDEX, |stream| { - stream_attributes(stream, &self.props, |k, v| match k.as_str() { + stream_attributes(stream, &self.props, |k, v| match k.get() { emit::well_known::KEY_LVL => { level = v.by_ref().cast().unwrap_or_default(); true