From 7c86d6a6b0ff76aad88b7585c2fbfcabb1ab0905 Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Fri, 12 Apr 2024 09:26:55 +1000 Subject: [PATCH] work on span API --- core/src/well_known.rs | 1 + macros/src/module.rs | 2 +- macros/src/props.rs | 2 - macros/src/span.rs | 52 +++++-- macros/src/template.rs | 28 +++- src/kind.rs | 86 +++++++++++ src/level.rs | 2 +- src/lib.rs | 28 +--- src/macro_hooks.rs | 122 ++++++--------- src/metric.rs | 8 +- src/{trace.rs => span.rs} | 188 ++++++++++++++--------- src/timer.rs | 4 + targets/file/src/lib.rs | 6 +- targets/opentelemetry/src/lib.rs | 12 +- targets/otlp/src/client.rs | 4 +- targets/otlp/src/client/http.rs | 4 +- targets/otlp/src/data.rs | 28 ++-- targets/otlp/src/data/logs/log_record.rs | 8 +- targets/otlp/src/data/traces/span.rs | 10 +- targets/term/src/lib.rs | 8 +- tests/smoke-test/main.rs | 47 ++++-- 21 files changed, 409 insertions(+), 241 deletions(-) create mode 100644 src/kind.rs rename src/{trace.rs => span.rs} (75%) diff --git a/core/src/well_known.rs b/core/src/well_known.rs index f2e3c00..35dba9c 100644 --- a/core/src/well_known.rs +++ b/core/src/well_known.rs @@ -21,6 +21,7 @@ pub const LVL_ERROR: &'static str = "error"; pub const KEY_ERR: &'static str = "err"; // Trace +pub const KEY_SPAN_NAME: &'static str = "span_name"; pub const KEY_TRACE_ID: &'static str = "trace_id"; pub const KEY_SPAN_ID: &'static str = "span_id"; pub const KEY_SPAN_PARENT: &'static str = "span_parent"; diff --git a/macros/src/module.rs b/macros/src/module.rs index 46bd1a6..099d193 100644 --- a/macros/src/module.rs +++ b/macros/src/module.rs @@ -1,5 +1,5 @@ use proc_macro2::TokenStream; pub(crate) fn module_tokens() -> TokenStream { - quote!(emit::__private::__private_module!()) + quote!(emit::module!()) } diff --git a/macros/src/props.rs b/macros/src/props.rs index b11be00..352d08c 100644 --- a/macros/src/props.rs +++ b/macros/src/props.rs @@ -35,7 +35,6 @@ pub struct KeyValue { span: Span, pub interpolated: bool, pub captured: bool, - pub label: String, pub cfg_attr: Option, pub attrs: Vec, } @@ -166,7 +165,6 @@ impl Props { match_bound_tokens: quote_spanned!(fv.span()=> #cfg_attr (#match_bound_ident.0, #match_bound_ident.1)), direct_bound_tokens: quote_spanned!(fv.span()=> #key_value_tokens), span: fv.span(), - label, cfg_attr, attrs, captured, diff --git a/macros/src/span.rs b/macros/src/span.rs index 234a244..b0dd61a 100644 --- a/macros/src/span.rs +++ b/macros/src/span.rs @@ -155,21 +155,31 @@ fn inject_sync( let ctxt_props_tokens = ctxt_props.props_tokens(); let evt_props_tokens = evt_props.props_tokens(); let template_tokens = template.template_tokens(); + let template_literal_tokens = template.template_literal_tokens(); quote!({ - let (mut __ctxt, __timer, __span_props) = emit::__private::__private_push_span_ctxt(#rt_tokens, #module_tokens, #when_tokens, #template_tokens, #ctxt_props_tokens, #evt_props_tokens); + let (mut __ctxt, __span_arg) = emit::__private::__private_begin_span( + #rt_tokens, + #module_tokens, + #when_tokens, + #template_tokens, + #ctxt_props_tokens, + #evt_props_tokens, + #template_literal_tokens, + |extent, props| { + emit::__private::__private_emit( + #rt_tokens, + #module_tokens, + emit::__private::__private_filter_span_complete(), + extent, + #template_tokens, + emit::Props::chain(props, #evt_props_tokens), + ) + } + ); let __ctxt_guard = __ctxt.enter(); - let #span_arg = emit::__private::__private_begin_span(__timer, __span_props, |extent, props| { - emit::__private::__private_emit( - #rt_tokens, - #module_tokens, - emit::__private::__private_filter_span_complete(), - extent, - #template_tokens, - emit::Props::chain(props, #evt_props_tokens), - ) - }); + let #span_arg = __span_arg; #body }) @@ -188,12 +198,18 @@ fn inject_async( let ctxt_props_tokens = ctxt_props.props_tokens(); let evt_props_tokens = evt_props.props_tokens(); let template_tokens = template.template_tokens(); + let template_literal_tokens = template.template_literal_tokens(); quote!({ - let (__ctxt, __timer, __span_props) = emit::__private::__private_push_span_ctxt(#rt_tokens, #module_tokens, #when_tokens, #template_tokens, #ctxt_props_tokens, #evt_props_tokens); - - __ctxt.in_future(async { - let #span_arg = emit::__private::__private_begin_span(__timer, __span_props, |extent, props| { + let (__ctxt, __span_arg) = emit::__private::__private_begin_span( + #rt_tokens, + #module_tokens, + #when_tokens, + #template_tokens, + #ctxt_props_tokens, + #evt_props_tokens, + #template_literal_tokens, + |extent, props| { emit::__private::__private_emit( #rt_tokens, #module_tokens, @@ -202,7 +218,11 @@ fn inject_async( #template_tokens, emit::Props::chain(props, #evt_props_tokens), ) - }); + } + ); + + __ctxt.in_future(async { + let #span_arg = __span_arg; async #body.await }).await diff --git a/macros/src/template.rs b/macros/src/template.rs index b8c0c77..a6f16b5 100644 --- a/macros/src/template.rs +++ b/macros/src/template.rs @@ -73,13 +73,15 @@ pub fn parse2( } // A runtime representation of the template - let template_tokens = { + let (template_parts_tokens, template_literal_tokens) = { let mut template_visitor = TemplateVisitor { props: &props, parts: Ok(Vec::new()), + literal: String::new(), }; template.visit_literal(&mut template_visitor); let template_parts = template_visitor.parts?; + let literal = template_visitor.literal; /* Ideally this would be: @@ -98,15 +100,19 @@ pub fn parse2( Once that is stable then we'll be able to use it here and avoid "value doesn't live long enough" errors in `let x = tpl!(..);`. */ - quote!([ - #(#template_parts),* - ]) + ( + quote!([ + #(#template_parts),* + ]), + quote!(#literal), + ) }; Ok(( args, Template { - template_parts_tokens: template_tokens, + template_parts_tokens, + template_literal_tokens, }, props, )) @@ -114,6 +120,7 @@ pub fn parse2( pub struct Template { template_parts_tokens: TokenStream, + template_literal_tokens: TokenStream, } impl Template { @@ -121,6 +128,10 @@ impl Template { self.template_parts_tokens.clone() } + pub fn template_literal_tokens(&self) -> TokenStream { + self.template_literal_tokens.clone() + } + pub fn template_tokens(&self) -> TokenStream { let template_parts = &self.template_parts_tokens; @@ -131,6 +142,7 @@ impl Template { struct TemplateVisitor<'a> { props: &'a Props, parts: syn::Result>, + literal: String, } impl<'a> fv_template::LiteralVisitor for TemplateVisitor<'a> { @@ -146,6 +158,10 @@ impl<'a> fv_template::LiteralVisitor for TemplateVisitor<'a> { debug_assert!(field.interpolated); + self.literal.push_str("{"); + self.literal.push_str(&label); + self.literal.push_str("}"); + match fmt::template_hole_with_hook(&field.attrs, &hole, true, field.captured) { Ok(hole_tokens) => match field.cfg_attr { Some(ref cfg_attr) => parts.push(quote!(#cfg_attr { #hole_tokens })), @@ -162,6 +178,8 @@ impl<'a> fv_template::LiteralVisitor for TemplateVisitor<'a> { return; }; + self.literal.push_str(text); + parts.push(quote!(emit::template::Part::text(#text))); } } diff --git a/src/kind.rs b/src/kind.rs new file mode 100644 index 0000000..23c4eb1 --- /dev/null +++ b/src/kind.rs @@ -0,0 +1,86 @@ +use core::{fmt, str::FromStr}; + +use emit_core::{ + event::Event, + filter::Filter, + props::Props, + value::{FromValue, ToValue, Value}, + well_known::{EVENT_KIND_METRIC, EVENT_KIND_SPAN, KEY_EVENT_KIND}, +}; + +#[non_exhaustive] +#[derive(PartialEq, Eq, Hash, Clone, Copy)] +pub enum Kind { + Span, + Metric, +} + +impl fmt::Debug for Kind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "\"{}\"", self) + } +} + +impl fmt::Display for Kind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Kind::Span => f.write_str(EVENT_KIND_SPAN), + Kind::Metric => f.write_str(EVENT_KIND_METRIC), + } + } +} + +impl ToValue for Kind { + fn to_value(&self) -> Value { + Value::capture_display(self) + } +} + +impl<'v> FromValue<'v> for Kind { + fn from_value(value: Value<'v>) -> Option { + value + .downcast_ref::() + .copied() + .or_else(|| value.parse()) + } +} + +impl FromStr for Kind { + type Err = ParseKindError; + + fn from_str(s: &str) -> Result { + if s.eq_ignore_ascii_case(EVENT_KIND_SPAN) { + return Ok(Kind::Span); + } + + if s.eq_ignore_ascii_case(EVENT_KIND_METRIC) { + return Ok(Kind::Metric); + } + + Err(ParseKindError {}) + } +} + +pub struct ParseKindError {} + +pub struct IsKind(Kind); + +impl IsKind { + pub fn new(kind: Kind) -> Self { + IsKind(kind) + } +} + +pub fn is_span_filter() -> IsKind { + IsKind::new(Kind::Span) +} + +pub fn is_metric_filter() -> IsKind { + IsKind::new(Kind::Metric) +} + +impl Filter for IsKind { + fn matches(&self, evt: &Event

) -> bool { + evt.props().pull::(KEY_EVENT_KIND) == Some(self.0) + } +} diff --git a/src/level.rs b/src/level.rs index fc8bcff..f895ed9 100644 --- a/src/level.rs +++ b/src/level.rs @@ -107,7 +107,7 @@ impl<'v> FromValue<'v> for Level { } } -pub fn min_level(min: Level) -> MinLevel { +pub fn min_level_filter(min: Level) -> MinLevel { MinLevel::new(min) } diff --git a/src/lib.rs b/src/lib.rs index 1af03fc..b6bb006 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -335,6 +335,8 @@ extern crate alloc; use emit_core::extent::ToExtent; +pub use std::module_path as module; + #[doc(inline)] pub use emit_macros::*; @@ -345,15 +347,16 @@ pub use emit_core::{ }; pub mod frame; +pub mod kind; pub mod level; pub mod metric; +pub mod span; pub mod timer; -pub mod trace; pub use self::{ clock::Clock, ctxt::Ctxt, emitter::Emitter, event::Event, extent::Extent, filter::Filter, - frame::Frame, level::Level, path::Path, props::Props, rng::Rng, str::Str, template::Template, - timer::Timer, timestamp::Timestamp, value::Value, + frame::Frame, level::Level, metric::Metric, path::Path, props::Props, rng::Rng, span::Span, + str::Str, template::Template, timer::Timer, timestamp::Timestamp, value::Value, }; mod macro_hooks; @@ -364,25 +367,6 @@ pub mod setup; #[cfg(feature = "std")] pub use setup::{setup, Setup}; -#[track_caller] -fn base_emit( - to: impl Emitter, - source: Path, - when: impl Filter, - ctxt: impl Ctxt, - ts: impl ToExtent, - tpl: Template, - props: impl Props, -) { - ctxt.with_current(|ctxt| { - let evt = Event::new(source, ts, tpl, props.chain(ctxt)); - - if when.matches(&evt) { - to.emit(&evt); - } - }); -} - #[doc(hidden)] pub mod __private { pub use crate::macro_hooks::*; diff --git a/src/macro_hooks.rs b/src/macro_hooks.rs index 3c5971a..1892285 100644 --- a/src/macro_hooks.rs +++ b/src/macro_hooks.rs @@ -15,22 +15,15 @@ use emit_core::{ value::{ToValue, Value}, }; -#[cfg(feature = "alloc")] -use emit_core::{ - empty::Empty, - event::Event, - well_known::{KEY_SPAN_ID, KEY_TRACE_ID}, -}; +use emit_core::{empty::Empty, event::Event}; -#[cfg(feature = "alloc")] use crate::frame::Frame; #[cfg(feature = "std")] use std::error::Error; use crate::{ - base_emit, - trace::{Span, SpanId, SpanProps, TraceId}, + span::{Span, SpanCtxtProps, SpanEventProps, SpanId, TraceId}, Level, Timer, }; @@ -504,85 +497,72 @@ pub fn __private_filter_span_complete() -> Option( +pub fn __private_emit<'a, 'b, E: Emitter, F: Filter, C: Ctxt, T: Clock, R: Rng>( rt: &'a Runtime, - source: impl Into>, + module: impl Into>, when: Option, extent: impl ToExtent, - tpl: Template, + tpl: Template<'b>, props: impl Props, ) { - base_emit( - rt.emitter(), - source.into(), - FirstDefined(when, rt.filter()), - rt.ctxt(), - extent.to_extent().or_else(|| rt.clock().now().to_extent()), - tpl, - props, - ); + rt.ctxt().with_current(|ctxt| { + let evt = Event::new( + module, + extent.to_extent().or_else(|| rt.clock().now().to_extent()), + tpl, + props.chain(ctxt), + ); + + if FirstDefined(when, rt.filter()).matches(&evt) { + rt.emitter().emit(&evt); + } + }); } #[track_caller] -#[cfg(feature = "alloc")] -pub fn __private_push_span_ctxt<'a, 'b, E: Emitter, F: Filter, C: Ctxt, T: Clock, R: Rng>( +pub fn __private_begin_span< + 'a, + 'b, + E: Emitter, + F: Filter, + C: Ctxt, + T: Clock, + R: Rng, + S: FnOnce(Option, SpanEventProps), +>( rt: &'a Runtime, module: impl Into>, when: Option, tpl: Template<'b>, ctxt_props: impl Props, evt_props: impl Props, -) -> ( - Frame>, - Option>, - Option>, -) { - let (mut trace_id, span_parent) = rt.ctxt().with_current(|current| { - ( - current.pull::(KEY_TRACE_ID), - current.pull::(KEY_SPAN_ID), - ) - }); - - trace_id = trace_id.or_else(|| TraceId::random(rt.rng())); - let span_id = SpanId::random(rt.rng()); - - if let (Some(trace_id), Some(span_id)) = (trace_id, span_id) { - let timer = Timer::start(rt.clock()); - - let span_props = SpanProps::new(trace_id, span_parent, span_id, Empty); - - if FirstDefined(when, rt.filter()).matches(&Event::new( - module, - timer.extent().map(|extent| *extent.as_point()), - tpl, - ctxt_props.by_ref().chain(&span_props).chain(&evt_props), - )) { - return ( - Frame::push(Some(rt.ctxt()), span_props.ctxt().chain(ctxt_props)), - Some(timer), - Some(span_props), - ); - } - } - - (Frame::push(None, Empty), None, None) -} + name: impl Into>, + default_complete: S, +) -> (Frame>, Span<'static, &'a T, Empty, S>) { + let span = Span::filtered_new( + Timer::start(rt.clock()), + name, + SpanCtxtProps::current(rt.ctxt()).new_child(rt.rng()), + Empty, + |extent, props| { + FirstDefined(when, rt.filter()).matches(&Event::new( + module, + extent, + tpl, + ctxt_props.by_ref().chain(props).chain(&evt_props), + )) + }, + default_complete, + ); -#[track_caller] -pub fn __private_begin_span, SpanProps

)>( - timer: Option>, - props: Option>, - default_complete: F, -) -> Span { - if let (Some(timer), Some(props)) = (timer, props) { - Span::new(timer, props, default_complete) + let frame = if span.is_enabled() { + Frame::push(Some(rt.ctxt()), span.ctxt().chain(ctxt_props)) } else { - Span::disabled() - } -} + Frame::push(None, Empty) + }; -pub use core::module_path as __private_module; + (frame, span) +} #[repr(transparent)] pub struct __PrivateMacroProps<'a>([(Str<'a>, Option>)]); diff --git a/src/metric.rs b/src/metric.rs index b29e423..d968b1b 100644 --- a/src/metric.rs +++ b/src/metric.rs @@ -8,11 +8,11 @@ use emit_core::{ str::{Str, ToStr}, template::{self, Template}, value::{ToValue, Value}, - well_known::{ - EVENT_KIND_METRIC, KEY_EVENT_KIND, KEY_METRIC_AGG, KEY_METRIC_NAME, KEY_METRIC_VALUE, - }, + well_known::{KEY_EVENT_KIND, KEY_METRIC_AGG, KEY_METRIC_NAME, KEY_METRIC_VALUE}, }; +use crate::kind::Kind; + pub struct Metric<'a, P> { module: Path<'a>, extent: Option, @@ -155,7 +155,7 @@ impl<'a, P: Props> Props for Metric<'a, P> { &'kv self, mut for_each: F, ) -> ControlFlow<()> { - for_each(KEY_EVENT_KIND.to_str(), EVENT_KIND_METRIC.to_value())?; + for_each(KEY_EVENT_KIND.to_str(), Kind::Metric.to_value())?; for_each(KEY_METRIC_NAME.to_str(), self.name.to_value())?; for_each(KEY_METRIC_AGG.to_str(), self.agg.to_value())?; for_each(KEY_METRIC_VALUE.to_str(), self.value.by_ref())?; diff --git a/src/trace.rs b/src/span.rs similarity index 75% rename from src/trace.rs rename to src/span.rs index 3b38dba..5251b5b 100644 --- a/src/trace.rs +++ b/src/span.rs @@ -1,14 +1,16 @@ use emit_core::{ clock::Clock, - extent::Extent, + ctxt::Ctxt, + extent::{Extent, ToExtent}, props::Props, rng::Rng, str::{Str, ToStr}, value::FromValue, - well_known::{EVENT_KIND_SPAN, KEY_EVENT_KIND, KEY_SPAN_ID, KEY_SPAN_PARENT, KEY_TRACE_ID}, + well_known::{KEY_EVENT_KIND, KEY_SPAN_ID, KEY_SPAN_NAME, KEY_SPAN_PARENT, KEY_TRACE_ID}, }; use crate::{ + kind::Kind, value::{ToValue, Value}, Timer, }; @@ -338,45 +340,84 @@ impl fmt::Write for Buffer { } } -pub struct Span, SpanProps

)> { - value: Option<(Timer, SpanProps

)>, +pub struct Span<'a, C: Clock, P: Props, F: FnOnce(Option, SpanEventProps<'a, P>)> { + value: Option<(Timer, SpanEventProps<'a, P>)>, on_drop: Option, } -pub struct SpanProps

{ - ctxt: SpanCtxt, +#[derive(Debug, Clone)] +pub struct SpanEventProps<'a, P> { + ctxt: SpanCtxtProps, + name: Str<'a>, props: P, } -pub struct SpanCtxt { - trace_id: TraceId, +impl<'a, P> SpanEventProps<'a, P> { + pub fn ctxt(&self) -> &SpanCtxtProps { + &self.ctxt + } + + pub fn name(&self) -> &Str<'a> { + &self.name + } + + pub fn props(&self) -> &P { + &self.props + } +} + +#[derive(Debug, Clone, Copy)] +pub struct SpanCtxtProps { + trace_id: Option, span_parent: Option, - span_id: SpanId, + span_id: Option, } -impl SpanCtxt { - pub fn new(trace_id: TraceId, span_parent: Option, span_id: SpanId) -> Self { - SpanCtxt { +impl SpanCtxtProps { + pub const fn new( + trace_id: Option, + span_parent: Option, + span_id: Option, + ) -> Self { + SpanCtxtProps { trace_id, span_parent, span_id, } } - pub fn trace_id(&self) -> &TraceId { - &self.trace_id + pub fn current(ctxt: impl Ctxt) -> Self { + ctxt.with_current(|current| { + SpanCtxtProps::new( + current.pull::(KEY_TRACE_ID), + current.pull::(KEY_SPAN_PARENT), + current.pull::(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); + + SpanCtxtProps::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) -> &SpanId { - &self.span_id + pub fn span_id(&self) -> Option<&SpanId> { + self.span_id.as_ref() } } -impl Props for SpanCtxt { +impl Props for SpanCtxtProps { fn for_each<'kv, F: FnMut(Str<'kv>, Value<'kv>) -> ControlFlow<()>>( &'kv self, mut for_each: F, @@ -392,48 +433,21 @@ impl Props for SpanCtxt { } } -impl

SpanProps

{ - pub fn new(trace_id: TraceId, span_parent: Option, span_id: SpanId, props: P) -> Self { - SpanProps { - ctxt: SpanCtxt::new(trace_id, span_parent, span_id), - props, - } - } - - pub fn trace_id(&self) -> &TraceId { - &self.ctxt.trace_id - } - - pub fn span_parent(&self) -> Option<&SpanId> { - self.ctxt.span_parent.as_ref() - } - - pub fn span_id(&self) -> &SpanId { - &self.ctxt.span_id - } - - pub fn props(&self) -> &P { - &self.props - } - - pub fn ctxt(&self) -> &SpanCtxt { - &self.ctxt - } -} - -impl Props for SpanProps

{ +impl<'a, P: Props> Props for SpanEventProps<'a, P> { fn for_each<'kv, F: FnMut(Str<'kv>, Value<'kv>) -> ControlFlow<()>>( &'kv self, mut for_each: F, ) -> ControlFlow<()> { - for_each(KEY_EVENT_KIND.to_str(), EVENT_KIND_SPAN.to_value())?; - - self.ctxt().for_each(&mut for_each)?; - self.props.for_each(for_each) + for_each(KEY_EVENT_KIND.to_str(), Kind::Span.to_value())?; + for_each(KEY_SPAN_NAME.to_str(), self.name.to_value())?; + self.ctxt.for_each(&mut for_each)?; + self.props.for_each(&mut for_each) } } -impl, SpanProps

)> Drop for Span { +impl<'a, C: Clock, P: Props, F: FnOnce(Option, SpanEventProps<'a, P>)> Drop + for Span<'a, C, P, F> +{ fn drop(&mut self) { if let Some((timer, props)) = self.value.take() { if let Some(on_drop) = self.on_drop.take() { @@ -443,14 +457,41 @@ impl, SpanProps

)> Drop for Span< } } -impl, SpanProps

)> Span { - pub fn new(timer: Timer, props: SpanProps

, default_complete: F) -> Self { - Span { - value: Some((timer, props)), - on_drop: Some(default_complete), +impl<'a, C: Clock, P: Props, F: FnOnce(Option, SpanEventProps<'a, P>)> Span<'a, C, P, F> { + pub fn filtered_new( + timer: Timer, + name: impl Into>, + ctxt: SpanCtxtProps, + props: P, + filter: impl FnOnce(Option, &SpanEventProps<'a, P>) -> bool, + default_complete: F, + ) -> Self { + let props = SpanEventProps { + name: name.into(), + ctxt, + props, + }; + + if filter(timer.start_timestamp().map(Extent::point), &props) { + Span { + value: Some((timer, props)), + on_drop: Some(default_complete), + } + } else { + Self::disabled() } } + pub fn new( + timer: Timer, + name: impl Into>, + ctxt: SpanCtxtProps, + props: P, + default_complete: F, + ) -> Self { + Self::filtered_new(timer, name, ctxt, props, |_, _| true, default_complete) + } + pub fn disabled() -> Self { Span { value: None, @@ -466,27 +507,26 @@ impl, SpanProps

)> Span self.value.as_ref().map(|(timer, _)| timer) } - pub fn span_props(&self) -> Option<&SpanProps

> { - self.value.as_ref().map(|(_, props)| props) + pub fn ctxt(&self) -> Option<&SpanCtxtProps> { + self.value.as_ref().map(|(_, props)| props.ctxt()) } - pub fn props(&self) -> Option<&P> { - self.span_props().map(|props| props.props()) - } - - pub fn trace_id(&self) -> Option<&TraceId> { - self.span_props().map(|props| props.trace_id()) + pub fn name(&self) -> Option<&Str<'a>> { + self.value.as_ref().map(|(_, props)| props.name()) } - pub fn span_parent(&self) -> Option<&SpanId> { - self.span_props().and_then(|props| props.span_parent()) + pub fn props(&self) -> Option<&P> { + self.value.as_ref().map(|(_, props)| props.props()) } - pub fn span_id(&self) -> Option<&SpanId> { - self.span_props().map(|props| props.span_id()) + pub fn complete(self) { + drop(self); } - pub fn complete(mut self, complete: impl FnOnce(Option, SpanProps

)) -> bool { + pub fn complete_with( + mut self, + complete: impl FnOnce(Option, SpanEventProps<'a, P>), + ) -> bool { if let Some((timer, props)) = self.value.take() { complete(timer.extent(), props); true @@ -496,6 +536,14 @@ impl, SpanProps

)> Span } } +impl<'a, C: Clock, P: Props, F: FnOnce(Option, SpanEventProps<'a, P>)> ToExtent + for Span<'a, C, P, F> +{ + fn to_extent(&self) -> Option { + self.timer().to_extent() + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/timer.rs b/src/timer.rs index a4a413c..7d16e13 100644 --- a/src/timer.rs +++ b/src/timer.rs @@ -16,6 +16,10 @@ impl Timer { } } + pub fn start_timestamp(&self) -> Option { + self.start + } + pub fn extent(&self) -> Option { let end = self.clock.now(); diff --git a/targets/file/src/lib.rs b/targets/file/src/lib.rs index 4ff40d6..914e3f6 100644 --- a/targets/file/src/lib.rs +++ b/targets/file/src/lib.rs @@ -397,7 +397,7 @@ impl Worker { if file.is_none() { if let Err(err) = fs::create_dir_all(&self.dir) { - span.complete(|extent, props| { + span.complete_with(|extent, props| { emit::warn!( rt: emit::runtime::internal(), extent, @@ -509,7 +509,7 @@ impl Worker { if let Err(err) = file.write_event(buf) { self.metrics.file_write_failed.increment(); - span.complete(|extent, props| { + span.complete_with(|extent, props| { emit::warn!( rt: emit::runtime::internal(), extent, @@ -534,7 +534,7 @@ impl Worker { .sync_all() .map_err(|e| emit_batcher::BatchError::no_retry(e))?; - span.complete(|extent, props| { + span.complete_with(|extent, props| { emit::debug!( rt: emit::runtime::internal(), extent, diff --git a/targets/opentelemetry/src/lib.rs b/targets/opentelemetry/src/lib.rs index 1f1ef1f..138dff9 100644 --- a/targets/opentelemetry/src/lib.rs +++ b/targets/opentelemetry/src/lib.rs @@ -28,8 +28,8 @@ pub struct OpenTelemetryContextFrame { } pub struct OpenTelemetryContextProps { - trace_id: Option, - span_id: Option, + trace_id: Option, + span_id: Option, } impl emit::Props for OpenTelemetryContextProps { @@ -61,8 +61,8 @@ impl emit::Ctxt for OpenTelemetryContext { type Frame = OpenTelemetryContextFrame; fn open_root(&self, props: P) -> Self::Frame { - let trace_id = props.pull::(emit::well_known::KEY_TRACE_ID); - let span_id = props.pull::(emit::well_known::KEY_SPAN_ID); + let trace_id = props.pull::(emit::well_known::KEY_TRACE_ID); + let span_id = props.pull::(emit::well_known::KEY_SPAN_ID); if let Some(span_id) = span_id { let mut span = self @@ -104,8 +104,8 @@ impl emit::Ctxt for OpenTelemetryContext { let span_id = span.span_context().span_id().to_bytes(); let props = OpenTelemetryContextProps { - trace_id: emit::trace::TraceId::from_bytes(trace_id), - span_id: emit::trace::SpanId::from_bytes(span_id), + trace_id: emit::span::TraceId::from_bytes(trace_id), + span_id: emit::span::SpanId::from_bytes(span_id), }; with(&props) diff --git a/targets/otlp/src/client.rs b/targets/otlp/src/client.rs index dd506b6..de5ef8b 100644 --- a/targets/otlp/src/client.rs +++ b/targets/otlp/src/client.rs @@ -871,7 +871,7 @@ impl OtlpTransport { .await { Ok(res) => { - span.complete(|extent, props| { + span.complete_with(|extent, props| { emit::debug!( rt: emit::runtime::internal(), props, @@ -884,7 +884,7 @@ impl OtlpTransport { res } Err(err) => { - span.complete(|extent, props| { + span.complete_with(|extent, props| { emit::warn!( rt: emit::runtime::internal(), props, diff --git a/targets/otlp/src/client/http.rs b/targets/otlp/src/client/http.rs index 590f7ca..d016a08 100644 --- a/targets/otlp/src/client/http.rs +++ b/targets/otlp/src/client/http.rs @@ -178,8 +178,8 @@ async fn send_request( // Propagate traceparent for the batch let (trace_id, span_id) = rt.ctxt().with_current(|props| { ( - props.pull::(KEY_TRACE_ID), - props.pull::(KEY_SPAN_ID), + props.pull::(KEY_TRACE_ID), + props.pull::(KEY_SPAN_ID), ) }); diff --git a/targets/otlp/src/data.rs b/targets/otlp/src/data.rs index 08acdc6..8841bc4 100644 --- a/targets/otlp/src/data.rs +++ b/targets/otlp/src/data.rs @@ -36,18 +36,18 @@ pub(crate) trait RequestEncoder { } pub(crate) trait RawEncoder { - type TraceId: From + sval::Value; - type SpanId: From + sval::Value; + type TraceId: From + sval::Value; + type SpanId: From + sval::Value; fn encode(value: V) -> PreEncoded; } pub(crate) struct Proto; -pub(crate) struct BinaryTraceId(emit::trace::TraceId); +pub(crate) struct BinaryTraceId(emit::span::TraceId); -impl From for BinaryTraceId { - fn from(id: emit::trace::TraceId) -> BinaryTraceId { +impl From for BinaryTraceId { + fn from(id: emit::span::TraceId) -> BinaryTraceId { BinaryTraceId(id) } } @@ -58,10 +58,10 @@ impl sval::Value for BinaryTraceId { } } -pub(crate) struct BinarySpanId(emit::trace::SpanId); +pub(crate) struct BinarySpanId(emit::span::SpanId); -impl From for BinarySpanId { - fn from(id: emit::trace::SpanId) -> BinarySpanId { +impl From for BinarySpanId { + fn from(id: emit::span::SpanId) -> BinarySpanId { BinarySpanId(id) } } @@ -83,10 +83,10 @@ impl RawEncoder for Proto { pub(crate) struct Json; -pub(crate) struct TextTraceId(emit::trace::TraceId); +pub(crate) struct TextTraceId(emit::span::TraceId); -impl From for TextTraceId { - fn from(id: emit::trace::TraceId) -> TextTraceId { +impl From for TextTraceId { + fn from(id: emit::span::TraceId) -> TextTraceId { TextTraceId(id) } } @@ -97,10 +97,10 @@ impl sval::Value for TextTraceId { } } -pub(crate) struct TextSpanId(emit::trace::SpanId); +pub(crate) struct TextSpanId(emit::span::SpanId); -impl From for TextSpanId { - fn from(id: emit::trace::SpanId) -> TextSpanId { +impl From for TextSpanId { + fn from(id: emit::span::SpanId) -> TextSpanId { TextSpanId(id) } } diff --git a/targets/otlp/src/data/logs/log_record.rs b/targets/otlp/src/data/logs/log_record.rs index 71521fc..7e1e1ed 100644 --- a/targets/otlp/src/data/logs/log_record.rs +++ b/targets/otlp/src/data/logs/log_record.rs @@ -86,8 +86,8 @@ impl PropsLogRecordAttributes { } impl< - TR: From + sval::Value, - SP: From + sval::Value, + TR: From + sval::Value, + SP: From + sval::Value, P: emit::props::Props, > sval::Value for PropsLogRecordAttributes { @@ -111,14 +111,14 @@ impl< emit::well_known::KEY_SPAN_ID => { span_id = v .by_ref() - .cast::() + .cast::() .map(|span_id| SP::from(span_id)); true } emit::well_known::KEY_TRACE_ID => { trace_id = v .by_ref() - .cast::() + .cast::() .map(|trace_id| TR::from(trace_id)); true } diff --git a/targets/otlp/src/data/traces/span.rs b/targets/otlp/src/data/traces/span.rs index fa04ea0..8fdca2c 100644 --- a/targets/otlp/src/data/traces/span.rs +++ b/targets/otlp/src/data/traces/span.rs @@ -108,8 +108,8 @@ impl PropsSpanAttributes { } impl< - TR: From + sval::Value, - SP: From + sval::Value, + TR: From + sval::Value, + SP: From + sval::Value, P: emit::props::Props, > sval::Value for PropsSpanAttributes { @@ -135,21 +135,21 @@ impl< emit::well_known::KEY_SPAN_ID => { span_id = v .by_ref() - .cast::() + .cast::() .map(|span_id| SP::from(span_id)); true } emit::well_known::KEY_SPAN_PARENT => { parent_span_id = v .by_ref() - .cast::() + .cast::() .map(|parent_span_id| SP::from(parent_span_id)); true } emit::well_known::KEY_TRACE_ID => { trace_id = v .by_ref() - .cast::() + .cast::() .map(|trace_id| TR::from(trace_id)); true } diff --git a/targets/term/src/lib.rs b/targets/term/src/lib.rs index d1f8eb4..a5f298a 100644 --- a/targets/term/src/lib.rs +++ b/targets/term/src/lib.rs @@ -62,7 +62,7 @@ impl emit::emitter::Emitter for Stdout { impl emit::runtime::InternalEmitter for Stdout {} -fn trace_id_color(trace_id: &emit::trace::TraceId) -> u8 { +fn trace_id_color(trace_id: &emit::span::TraceId) -> u8 { let mut hash = 0; for b in trace_id.to_u128().to_le_bytes() { @@ -72,7 +72,7 @@ fn trace_id_color(trace_id: &emit::trace::TraceId) -> u8 { hash } -fn span_id_color(span_id: &emit::trace::SpanId) -> u8 { +fn span_id_color(span_id: &emit::span::SpanId) -> u8 { let mut hash = 0; for b in span_id.to_u64().to_le_bytes() { @@ -192,8 +192,8 @@ fn print_event( buf: &mut Buffer, evt: &emit::event::Event, ) { - if let Some(span_id) = evt.props().pull::(KEY_SPAN_ID) { - if let Some(trace_id) = evt.props().pull::(KEY_TRACE_ID) { + if let Some(span_id) = evt.props().pull::(KEY_SPAN_ID) { + if let Some(trace_id) = evt.props().pull::(KEY_TRACE_ID) { let trace_id_color = trace_id_color(&trace_id); write_fg(buf, "▓", Color::Ansi256(trace_id_color)); diff --git a/tests/smoke-test/main.rs b/tests/smoke-test/main.rs index 103f13a..e2ab28c 100644 --- a/tests/smoke-test/main.rs +++ b/tests/smoke-test/main.rs @@ -49,7 +49,7 @@ async fn main() { ) .and_emit_to(emit_term::stdout()) .and_emit_to( - emit::level::min_level(emit::Level::Warn).wrap_emitter( + emit::level::min_level_filter(emit::Level::Warn).wrap_emitter( emit_file::set("./target/logs/log.txt") .reuse_files(true) .roll_by_minute() @@ -137,21 +137,50 @@ async fn in_ctxt(a: i32) -> Result<(), io::Error> { .await; if let Err(ref err) = r { - span.complete(|extent, props| emit::warn!(extent, props, "in_ctxt failed with {err}")); + span.complete_with(|extent, props| emit::warn!(extent, props, "in_ctxt failed with {err}")); } r } -#[emit::span("in_ctxt2", b, bx: 90)] async fn in_ctxt2(b: i32) { - emit::warn!( - "something went wrong at {#[emit::as_debug] id: 42} with {x} and {y: true}!", - #[emit::fmt(">08")] - x: 15, - #[emit::optional] - z: None::, + let rt = emit::runtime::shared(); + + let span = emit::Span::filtered_new( + emit::Timer::start(rt.clock()), + "in_ctxt2", + emit::span::SpanCtxtProps::current(rt.ctxt()).new_child(rt.rng()), + emit::empty::Empty, + |extent, props| { + rt.filter() + .matches(&emit::event!(extent, props, "in_ctxt2", b)) + }, + |extent, props| { + emit::emit!( + rt, + extent, + props, + "in_ctxt2", + b, + bx: 90, + ) + }, ); + + emit::Frame::push(rt.ctxt(), span.ctxt()) + .in_future(async { + tokio::time::sleep(Duration::from_millis(17)).await; + + emit::warn!( + rt, + "something went wrong at {#[emit::as_debug] id: 42} with {x} and {y: true}!", + #[emit::fmt(">08")] + x: 15, + #[emit::optional] + z: None::, + ); + }) + .await } static COUNT: AtomicUsize = AtomicUsize::new(0);