From eefe9435e052d028af60c191db5041edbdb98612 Mon Sep 17 00:00:00 2001 From: KodrAus Date: Mon, 24 Jul 2023 15:34:13 +1000 Subject: [PATCH] majorly refactor spans and contexts --- core/src/ambient.rs | 62 +++--- core/src/ctxt.rs | 287 +++++++++++----------------- core/src/event.rs | 122 +++++++----- core/src/filter.rs | 48 ++--- core/src/id.rs | 277 ++++++++++++--------------- core/src/level.rs | 27 ++- core/src/lib.rs | 1 - core/src/props.rs | 95 +++++++-- core/src/target.rs | 46 ++--- core/src/template.rs | 203 +++++++++++++++----- core/src/time.rs | 37 +++- core/src/value.rs | 68 ++++++- core/src/well_known.rs | 69 ++++++- macros/src/lib.rs | 10 +- macros/src/props.rs | 2 +- macros/src/{span.rs => with.rs} | 32 ++-- src/lib.rs | 102 +++++----- src/local_frame.rs | 77 ++++++++ src/macro_hooks.rs | 102 +++++++--- src/platform.rs | 30 ++- src/platform/rng.rs | 18 ++ src/platform/rng_gen_id.rs | 18 -- src/platform/thread_local_ctxt.rs | 37 ++-- src/setup.rs | 4 +- src/span.rs | 75 -------- targets/opentelemetry/src/id.rs | 8 +- targets/opentelemetry/src/logs.rs | 11 +- targets/opentelemetry/src/traces.rs | 26 ++- targets/otlp/src/logs.rs | 15 +- targets/term/src/lib.rs | 27 ++- tests/smoke-test/main.rs | 4 +- 31 files changed, 1124 insertions(+), 816 deletions(-) rename macros/src/{span.rs => with.rs} (79%) create mode 100644 src/local_frame.rs create mode 100644 src/platform/rng.rs delete mode 100644 src/platform/rng_gen_id.rs delete mode 100644 src/span.rs diff --git a/core/src/ambient.rs b/core/src/ambient.rs index 5aefb90..7ff2f54 100644 --- a/core/src/ambient.rs +++ b/core/src/ambient.rs @@ -3,7 +3,7 @@ use crate::{ empty::Empty, event::Event, filter::Filter, - id::{GenId, Id}, + id::IdSource, props::Props, target::Target, template::Template, @@ -113,8 +113,8 @@ impl Ambient Target for Ambient { - fn emit_event(&self, evt: &Event

) { - self.target.emit_event(evt) + fn event(&self, evt: &Event

) { + self.target.event(evt) } fn blocking_flush(&self, timeout: core::time::Duration) { @@ -125,35 +125,35 @@ impl Target impl Filter for Ambient { - fn matches_event(&self, evt: &Event

) -> bool { - self.filter.matches_event(evt) + fn matches(&self, evt: &Event

) -> bool { + self.filter.matches(evt) } } impl Ctxt for Ambient { - type Props = TCtxt::Props; - type Span = TCtxt::Span; + type CurrentProps = TCtxt::CurrentProps; + type LocalFrame = TCtxt::LocalFrame; - fn with_current(&self, with: F) { + fn with_current(&self, with: F) { self.ctxt.with_current(with) } - fn open(&self, ts: Option, id: Id, tpl: Template, props: P) -> Self::Span { - self.ctxt.open(ts, id, tpl, props) + fn open(&self, props: P) -> Self::LocalFrame { + self.ctxt.open(props) } - fn enter(&self, scope: &mut Self::Span) { + fn enter(&self, scope: &mut Self::LocalFrame) { self.ctxt.enter(scope) } - fn exit(&self, scope: &mut Self::Span) { + fn exit(&self, scope: &mut Self::LocalFrame) { self.ctxt.exit(scope) } - fn close(&self, ts: Option, span: Self::Span) { - self.ctxt.close(ts, span) + fn close(&self, span: Self::LocalFrame) { + self.ctxt.close(span) } } @@ -165,25 +165,21 @@ impl Clock } } -impl GenId +impl IdSource for Ambient { - fn gen(&self) -> Id { - self.gen_id.gen() + fn trace_id(&self) -> Option { + self.gen_id.trace_id() } - fn gen_trace(&self) -> Option { - self.gen_id.gen_trace() - } - - fn gen_span(&self) -> Option { - self.gen_id.gen_span() + fn span_id(&self) -> Option { + self.gen_id.span_id() } } #[cfg(not(feature = "std"))] -pub fn get() -> Option<&'static Ambient> -{ +pub fn get( +) -> Option<&'static Ambient> { None::<&'static Ambient> } @@ -193,7 +189,7 @@ mod std_support { use std::sync::OnceLock; use crate::{ - ctxt::ErasedCtxt, filter::ErasedFilter, id::ErasedGenId, target::ErasedTarget, + ctxt::ErasedCtxt, filter::ErasedFilter, id::ErasedIdSource, target::ErasedTarget, time::ErasedClock, }; @@ -259,17 +255,17 @@ mod std_support { } } - trait AmbientGenId: Any + ErasedGenId + Send + Sync + 'static { + trait AmbientGenId: Any + ErasedIdSource + Send + Sync + 'static { fn as_any(&self) -> &dyn Any; - fn as_super(&self) -> &(dyn ErasedGenId + Send + Sync + 'static); + fn as_super(&self) -> &(dyn ErasedIdSource + Send + Sync + 'static); } - impl AmbientGenId for T { + impl AmbientGenId for T { fn as_any(&self) -> &dyn Any { self } - fn as_super(&self) -> &(dyn ErasedGenId + Send + Sync + 'static) { + fn as_super(&self) -> &(dyn ErasedIdSource + Send + Sync + 'static) { self } } @@ -299,9 +295,9 @@ mod std_support { TTarget: Target + Send + Sync + 'static, TFilter: Filter + Send + Sync + 'static, TCtxt: Ctxt + Send + Sync + 'static, - TCtxt::Span: Send + 'static, + TCtxt::LocalFrame: Send + 'static, TClock: Clock + Send + Sync + 'static, - TGenId: GenId + Send + Sync + 'static, + TGenId: IdSource + Send + Sync + 'static, { AMBIENT .set(Ambient { @@ -330,7 +326,7 @@ mod std_support { impl Filter + Send + Sync + Copy, impl Ctxt + Send + Sync + Copy, impl Clock + Send + Sync + Copy, - impl GenId + Send + Sync + Copy, + impl IdSource + Send + Sync + Copy, >, > { let ambient = AMBIENT.get()?; diff --git a/core/src/ctxt.rs b/core/src/ctxt.rs index f066d50..6c01dbf 100644 --- a/core/src/ctxt.rs +++ b/core/src/ctxt.rs @@ -1,27 +1,18 @@ -use crate::{empty::Empty, id::Id, props::Props, template::Template, time::Timestamp}; +use crate::{empty::Empty, props::Props}; pub trait Ctxt { - type Props: Props + ?Sized; - type Span; + type CurrentProps: Props + ?Sized; + type LocalFrame; - fn open(&self, ts: Option, id: Id, tpl: Template, props: P) -> Self::Span; + fn open(&self, props: P) -> Self::LocalFrame; - fn enter(&self, span: &mut Self::Span); + fn enter(&self, local: &mut Self::LocalFrame); - fn with_current(&self, with: F); - fn current_id(&self) -> Id { - let mut current = Id::EMPTY; + fn with_current(&self, with: F); - self.with_current(|id, _| { - current = id; - }); + fn exit(&self, local: &mut Self::LocalFrame); - current - } - - fn exit(&self, span: &mut Self::Span); - - fn close(&self, ts: Option, span: Self::Span); + fn close(&self, frame: Self::LocalFrame); fn by_ref(&self) -> ByRef { ByRef(self) @@ -29,158 +20,137 @@ pub trait Ctxt { } impl<'a, C: Ctxt + ?Sized> Ctxt for &'a C { - type Props = C::Props; - type Span = C::Span; + type CurrentProps = C::CurrentProps; + type LocalFrame = C::LocalFrame; - fn open(&self, ts: Option, id: Id, tpl: Template, props: P) -> Self::Span { - (**self).open(ts, id, tpl, props) + fn open(&self, props: P) -> Self::LocalFrame { + (**self).open(props) } - fn enter(&self, span: &mut Self::Span) { - (**self).enter(span) + fn enter(&self, frame: &mut Self::LocalFrame) { + (**self).enter(frame) } - fn with_current(&self, with: F) { + fn with_current(&self, with: F) { (**self).with_current(with) } - fn current_id(&self) -> Id { - (**self).current_id() + fn exit(&self, frame: &mut Self::LocalFrame) { + (**self).exit(frame) } - fn exit(&self, span: &mut Self::Span) { - (**self).exit(span) - } - - fn close(&self, ts: Option, scope: Self::Span) { - (**self).close(ts, scope) + fn close(&self, frame: Self::LocalFrame) { + (**self).close(frame) } } impl Ctxt for Option { - type Props = Option>; - type Span = Option; + type CurrentProps = Option>; + type LocalFrame = Option; - fn with_current(&self, with: F) { + fn with_current(&self, with: F) { match self { - Some(ctxt) => ctxt - .with_current(|id, props| unsafe { with(id, &Some(internal::Slot::new(props))) }), - None => with(Id::EMPTY, &None), + Some(ctxt) => { + ctxt.with_current(|props| unsafe { with(&Some(internal::Slot::new(props))) }) + } + None => with(&None), } } - fn current_id(&self) -> Id { - self.as_ref() - .map(|ctxt| ctxt.current_id()) - .unwrap_or_default() + fn open(&self, props: P) -> Self::LocalFrame { + self.as_ref().map(|ctxt| ctxt.open(props)) } - fn open(&self, ts: Option, id: Id, tpl: Template, props: P) -> Self::Span { - self.as_ref().map(|ctxt| ctxt.open(ts, id, tpl, props)) - } - - fn enter(&self, span: &mut Self::Span) { - if let (Some(ctxt), Some(span)) = (self, span) { + fn enter(&self, frame: &mut Self::LocalFrame) { + if let (Some(ctxt), Some(span)) = (self, frame) { ctxt.enter(span) } } - fn exit(&self, span: &mut Self::Span) { - if let (Some(ctxt), Some(span)) = (self, span) { + fn exit(&self, frame: &mut Self::LocalFrame) { + if let (Some(ctxt), Some(span)) = (self, frame) { ctxt.exit(span) } } - fn close(&self, ts: Option, span: Self::Span) { - if let (Some(ctxt), Some(span)) = (self, span) { - ctxt.close(ts, span) + fn close(&self, frame: Self::LocalFrame) { + if let (Some(ctxt), Some(span)) = (self, frame) { + ctxt.close(span) } } } #[cfg(feature = "alloc")] impl<'a, C: Ctxt + ?Sized + 'a> Ctxt for alloc::boxed::Box { - type Props = C::Props; - type Span = C::Span; + type CurrentProps = C::CurrentProps; + type LocalFrame = C::LocalFrame; - fn with_current(&self, with: F) { + fn with_current(&self, with: F) { (**self).with_current(with) } - fn current_id(&self) -> Id { - (**self).current_id() + fn open(&self, props: P) -> Self::LocalFrame { + (**self).open(props) } - fn open(&self, ts: Option, id: Id, tpl: Template, props: P) -> Self::Span { - (**self).open(ts, id, tpl, props) + fn enter(&self, frame: &mut Self::LocalFrame) { + (**self).enter(frame) } - fn enter(&self, span: &mut Self::Span) { - (**self).enter(span) + fn exit(&self, frame: &mut Self::LocalFrame) { + (**self).exit(frame) } - fn exit(&self, span: &mut Self::Span) { - (**self).exit(span) - } - - fn close(&self, ts: Option, span: Self::Span) { - (**self).close(ts, span) + fn close(&self, frame: Self::LocalFrame) { + (**self).close(frame) } } pub struct ByRef<'a, T: ?Sized>(&'a T); impl<'a, T: Ctxt + ?Sized> Ctxt for ByRef<'a, T> { - type Props = T::Props; + type CurrentProps = T::CurrentProps; - type Span = T::Span; + type LocalFrame = T::LocalFrame; - fn open(&self, ts: Option, id: Id, tpl: Template, props: P) -> Self::Span { - self.0.open(ts, id, tpl, props) + fn open(&self, props: P) -> Self::LocalFrame { + self.0.open(props) } - fn enter(&self, span: &mut Self::Span) { - self.0.enter(span) + fn enter(&self, frame: &mut Self::LocalFrame) { + self.0.enter(frame) } - fn with_current(&self, with: F) { + fn with_current(&self, with: F) { self.0.with_current(with) } - fn exit(&self, span: &mut Self::Span) { - self.0.exit(span) + fn exit(&self, frame: &mut Self::LocalFrame) { + self.0.exit(frame) } - fn close(&self, ts: Option, span: Self::Span) { - self.0.close(ts, span) - } - - fn current_id(&self) -> Id { - self.0.current_id() + fn close(&self, frame: Self::LocalFrame) { + self.0.close(frame) } } impl Ctxt for Empty { - type Props = Empty; - type Span = Empty; - - fn with_current(&self, with: F) { - with(Id::EMPTY, &Empty) - } + type CurrentProps = Empty; + type LocalFrame = Empty; - fn current_id(&self) -> Id { - Id::EMPTY + fn with_current(&self, with: F) { + with(&Empty) } - fn open(&self, _: Option, _: Id, _: Template, _: P) -> Self::Span { + fn open(&self, _: P) -> Self::LocalFrame { Empty } - fn enter(&self, _: &mut Self::Span) {} + fn enter(&self, _: &mut Self::LocalFrame) {} - fn exit(&self, _: &mut Self::Span) {} + fn exit(&self, _: &mut Self::LocalFrame) {} - fn close(&self, _: Option, _: Self::Span) {} + fn close(&self, _: Self::LocalFrame) {} } mod internal { @@ -220,7 +190,7 @@ mod alloc_support { use core::{marker::PhantomData, mem, ops::ControlFlow}; use crate::{ - id::Id, + event::Event, key::Key, props::{ErasedProps, Props}, template::Template, @@ -228,40 +198,33 @@ mod alloc_support { value::Value, }; - use super::ErasedScope; + use super::ErasedLocalFrame; pub trait DispatchCtxt { - fn dispatch_with_current(&self, with: &mut dyn FnMut(Id, ErasedSlot)); - fn dispatch_current_id(&self) -> Id; - - fn dispatch_open( - &self, - ts: Option, - id: Id, - tpl: Template, - props: &dyn ErasedProps, - ) -> ErasedScope; - fn dispatch_enter(&self, span: &mut ErasedScope); - fn dispatch_exit(&self, span: &mut ErasedScope); - fn dispatch_close(&self, ts: Option, span: ErasedScope); + fn dispatch_with_current(&self, with: &mut dyn FnMut(ErasedCurrentProps)); + + fn dispatch_open(&self, props: &dyn ErasedProps) -> ErasedLocalFrame; + fn dispatch_enter(&self, frame: &mut ErasedLocalFrame); + fn dispatch_exit(&self, frame: &mut ErasedLocalFrame); + fn dispatch_close(&self, frame: ErasedLocalFrame); } pub trait SealedCtxt { fn erase_ctxt(&self) -> crate::internal::Erased<&dyn DispatchCtxt>; } - pub struct ErasedSlot( + pub struct ErasedCurrentProps( *const dyn ErasedProps, PhantomData, ); - impl ErasedSlot { + impl ErasedCurrentProps { pub(super) unsafe fn new<'a>(v: &'a impl Props) -> Self { let v: &'a dyn ErasedProps = v; let v: &'a (dyn ErasedProps + 'static) = mem::transmute::<&'a dyn ErasedProps, &'a (dyn ErasedProps + 'static)>(v); - ErasedSlot(v as *const dyn ErasedProps, PhantomData) + ErasedCurrentProps(v as *const dyn ErasedProps, PhantomData) } pub(super) fn get<'a>(&'a self) -> &'a (dyn ErasedProps + 'a) { @@ -269,7 +232,7 @@ mod alloc_support { } } - impl Props for ErasedSlot { + impl Props for ErasedCurrentProps { fn for_each<'a, F: FnMut(Key<'a>, Value<'a>) -> ControlFlow<()>>( &'a self, for_each: F, @@ -279,15 +242,15 @@ mod alloc_support { } } - pub struct ErasedScope(Box); + pub struct ErasedLocalFrame(Box); pub trait ErasedCtxt: internal::SealedCtxt {} - impl ErasedCtxt for C where C::Span: Send + 'static {} + impl ErasedCtxt for C where C::LocalFrame: Send + 'static {} impl internal::SealedCtxt for C where - C::Span: Send + 'static, + C::LocalFrame: Send + 'static, { fn erase_ctxt(&self) -> crate::internal::Erased<&dyn internal::DispatchCtxt> { crate::internal::Erased(self) @@ -296,118 +259,88 @@ mod alloc_support { impl internal::DispatchCtxt for C where - C::Span: Send + 'static, + C::LocalFrame: Send + 'static, { - fn dispatch_with_current(&self, with: &mut dyn FnMut(Id, internal::ErasedSlot)) { - self.with_current(move |id, props| { - with(id, unsafe { internal::ErasedSlot::new(&props) }) + fn dispatch_with_current(&self, with: &mut dyn FnMut(internal::ErasedCurrentProps)) { + self.with_current(move |props| { + with(unsafe { internal::ErasedCurrentProps::new(&props) }) }) } - fn dispatch_current_id(&self) -> Id { - self.current_id() - } - - fn dispatch_open( - &self, - ts: Option, - id: Id, - tpl: Template, - props: &dyn ErasedProps, - ) -> ErasedScope { - ErasedScope(Box::new(self.open(ts, id, tpl, props))) + fn dispatch_open(&self, props: &dyn ErasedProps) -> ErasedLocalFrame { + ErasedLocalFrame(Box::new(self.open(props))) } - fn dispatch_enter(&self, span: &mut ErasedScope) { + fn dispatch_enter(&self, span: &mut ErasedLocalFrame) { if let Some(span) = span.0.downcast_mut() { self.enter(span) } } - fn dispatch_exit(&self, span: &mut ErasedScope) { + fn dispatch_exit(&self, span: &mut ErasedLocalFrame) { if let Some(span) = span.0.downcast_mut() { self.exit(span) } } - fn dispatch_close(&self, ts: Option, span: ErasedScope) { + fn dispatch_close(&self, span: ErasedLocalFrame) { if let Ok(span) = span.0.downcast() { - self.close(ts, *span) + self.close(*span) } } } impl<'a> Ctxt for dyn ErasedCtxt + 'a { - type Props = internal::ErasedSlot; - type Span = ErasedScope; + type CurrentProps = internal::ErasedCurrentProps; + type LocalFrame = ErasedLocalFrame; - fn with_current(&self, with: F) { + fn with_current(&self, with: F) { let mut f = Some(with); - self.erase_ctxt().0.dispatch_with_current(&mut |id, props| { - f.take().expect("called multiple times")(id, &props) + self.erase_ctxt().0.dispatch_with_current(&mut |props| { + f.take().expect("called multiple times")(&props) }); } - fn current_id(&self) -> Id { - self.erase_ctxt().0.dispatch_current_id() - } - - fn open( - &self, - ts: Option, - id: Id, - tpl: Template, - props: P, - ) -> Self::Span { - self.erase_ctxt().0.dispatch_open(ts, id, tpl, &props) + fn open(&self, props: P) -> Self::LocalFrame { + self.erase_ctxt().0.dispatch_open(&props) } - fn enter(&self, span: &mut Self::Span) { + fn enter(&self, span: &mut Self::LocalFrame) { self.erase_ctxt().0.dispatch_enter(span) } - fn exit(&self, span: &mut Self::Span) { + fn exit(&self, span: &mut Self::LocalFrame) { self.erase_ctxt().0.dispatch_exit(span) } - fn close(&self, ts: Option, span: Self::Span) { - self.erase_ctxt().0.dispatch_close(ts, span) + fn close(&self, span: Self::LocalFrame) { + self.erase_ctxt().0.dispatch_close(span) } } impl<'a> Ctxt for dyn ErasedCtxt + Send + Sync + 'a { - type Props = ::Props; - type Span = ::Span; + type CurrentProps = ::CurrentProps; + type LocalFrame = ::LocalFrame; - fn with_current(&self, with: F) { + fn with_current(&self, with: F) { (self as &(dyn ErasedCtxt + 'a)).with_current(with) } - fn current_id(&self) -> Id { - (self as &(dyn ErasedCtxt + 'a)).current_id() - } - - fn open( - &self, - ts: Option, - id: Id, - tpl: Template, - props: P, - ) -> Self::Span { - (self as &(dyn ErasedCtxt + 'a)).open(ts, id, tpl, props) + fn open(&self, props: P) -> Self::LocalFrame { + (self as &(dyn ErasedCtxt + 'a)).open(props) } - fn enter(&self, span: &mut Self::Span) { + fn enter(&self, span: &mut Self::LocalFrame) { (self as &(dyn ErasedCtxt + 'a)).enter(span) } - fn exit(&self, span: &mut Self::Span) { + fn exit(&self, span: &mut Self::LocalFrame) { (self as &(dyn ErasedCtxt + 'a)).exit(span) } - fn close(&self, ts: Option, span: Self::Span) { - (self as &(dyn ErasedCtxt + 'a)).close(ts, span) + fn close(&self, span: Self::LocalFrame) { + (self as &(dyn ErasedCtxt + 'a)).close(span) } } } diff --git a/core/src/event.rs b/core/src/event.rs index faecc9b..800aea7 100644 --- a/core/src/event.rs +++ b/core/src/event.rs @@ -1,22 +1,20 @@ -use core::{borrow::Borrow, fmt, ops::ControlFlow}; +use core::{ + fmt, + ops::{ControlFlow, RangeInclusive}, +}; use crate::{ - empty::Empty, - id::Id, - key::Key, - level::Level, + key::{Key, ToKey}, props::{ByRef, Chain, ErasedProps, Props}, template::{Render, Template}, time::Timestamp, - value::Value, - well_known, + value::{ToValue, Value}, + well_known::{MESSAGE_KEY, TEMPLATE_KEY, TIMESTAMP_KEY, TIMESTAMP_START_KEY}, }; #[derive(Clone)] pub struct Event<'a, P> { - ts: Option, - id: Id, - lvl: Level, + ts: Option>, tpl: Template<'a>, props: P, } @@ -25,13 +23,7 @@ impl<'a, P: Props> fmt::Debug for Event<'a, P> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut f = f.debug_struct("Event"); - if let Some(ref ts) = self.ts { - f.field("ts", ts); - } - - f.field("lvl", &self.lvl).field("msg", &self.msg()); - - self.props.for_each(|k, v| { + self.all_props().for_each(|k, v| { f.field(k.as_str(), &v); ControlFlow::Continue(()) @@ -42,67 +34,68 @@ impl<'a, P: Props> fmt::Debug for Event<'a, P> { } impl<'a, P: Props> Event<'a, P> { - pub fn new(ts: Option, id: Id, lvl: Level, tpl: Template<'a>, props: P) -> Self { + pub fn point(ts: impl Into>, tpl: Template<'a>, props: P) -> Self { + Event::spanned(ts.into().map(|ts| ts..=ts), tpl, props) + } + + pub fn spanned( + ts: impl Into>>, + tpl: Template<'a>, + props: P, + ) -> Self { Event { - ts, - id, - lvl, + ts: ts.into(), tpl, props, } } - pub fn ts(&self) -> Option { - self.ts - } - - pub fn lvl(&self) -> Level { - self.lvl - } - - pub fn msg<'b>(&'b self) -> Render<'b, &'b P> { + pub fn message(&self) -> Render<&P> { self.tpl.render(&self.props) } - pub fn tpl<'b>(&'b self) -> Render<'b, Empty> { - self.tpl.render(Empty) - } - - pub fn err<'b>(&'b self) -> Option> { - self.props.get(well_known::ERR_KEY) + pub fn template(&self) -> Template { + self.tpl.by_ref() } - pub fn id(&self) -> Id { - self.id + pub fn timestamp(&self) -> Option { + self.ts.as_ref().map(|ts| *ts.start()) } - pub fn for_each<'b>(&'b self, for_each: impl FnMut(Key<'b>, Value<'b>) -> ControlFlow<()>) { - self.props.for_each(for_each) - } - - pub fn get<'b>(&'b self, k: impl Borrow) -> Option> { - self.props.get(k) + pub fn timespan(&self) -> Option> { + self.ts.as_ref().and_then(|ts| { + if *ts.start() != *ts.end() { + Some(ts.clone()) + } else { + None + } + }) } pub fn chain(self, other: U) -> Event<'a, Chain> { Event { ts: self.ts, - id: self.id, - lvl: self.lvl, tpl: self.tpl, props: self.props.chain(other), } } + pub fn all_props(&self) -> AllProps

{ + AllProps { + ts: self.ts.clone(), + tpl: self.tpl.by_ref(), + msg: self.message(), + props: self.props.by_ref(), + } + } + pub fn props(&self) -> &P { &self.props } pub fn by_ref<'b>(&'b self) -> Event<'b, ByRef<'b, P>> { Event { - ts: self.ts, - id: self.id, - lvl: self.lvl, + ts: self.ts.clone(), tpl: self.tpl.by_ref(), props: self.props.by_ref(), } @@ -110,11 +103,36 @@ impl<'a, P: Props> Event<'a, P> { pub fn erase<'b>(&'b self) -> Event<'b, &'b dyn ErasedProps> { Event { - ts: self.ts, - id: self.id, - lvl: self.lvl, + ts: self.ts.clone(), tpl: self.tpl.by_ref(), props: &self.props, } } } + +pub struct AllProps<'a, P> { + ts: Option>, + tpl: Template<'a>, + msg: Render<'a, &'a P>, + props: ByRef<'a, P>, +} + +impl<'a, P: Props> Props for AllProps<'a, P> { + fn for_each<'kv, F: FnMut(Key<'kv>, Value<'kv>) -> ControlFlow<()>>( + &'kv self, + mut for_each: F, + ) { + if let Some(ref ts) = self.ts { + if *ts.start() != *ts.end() { + for_each(TIMESTAMP_START_KEY.to_key(), ts.start().to_value()); + } + + for_each(TIMESTAMP_KEY.to_key(), ts.end().to_value()); + } + + for_each(TEMPLATE_KEY.to_key(), self.tpl.to_value()); + for_each(MESSAGE_KEY.to_key(), self.tpl.to_value()); + + self.props.for_each(for_each); + } +} diff --git a/core/src/filter.rs b/core/src/filter.rs index 3ccf30c..5b52e7b 100644 --- a/core/src/filter.rs +++ b/core/src/filter.rs @@ -5,7 +5,7 @@ use crate::{ }; pub trait Filter { - fn matches_event(&self, evt: &Event

) -> bool; + fn matches(&self, evt: &Event

) -> bool; fn and(self, other: U) -> And where @@ -33,35 +33,35 @@ pub trait Filter { } impl<'a, F: Filter + ?Sized> Filter for &'a F { - fn matches_event(&self, evt: &Event

) -> bool { - (**self).matches_event(evt) + fn matches(&self, evt: &Event

) -> bool { + (**self).matches(evt) } } #[cfg(feature = "std")] impl<'a, F: Filter + ?Sized + 'a> Filter for Box { - fn matches_event(&self, evt: &Event

) -> bool { - (**self).matches_event(evt) + fn matches(&self, evt: &Event

) -> bool { + (**self).matches(evt) } } impl Filter for Option { - fn matches_event(&self, evt: &Event

) -> bool { + fn matches(&self, evt: &Event

) -> bool { match self { - Some(filter) => filter.matches_event(evt), - None => Empty.matches_event(evt), + Some(filter) => filter.matches(evt), + None => Empty.matches(evt), } } } impl Filter for Empty { - fn matches_event(&self, _: &Event

) -> bool { + fn matches(&self, _: &Event

) -> bool { true } } impl Filter for fn(&Event<&dyn ErasedProps>) -> bool { - fn matches_event(&self, evt: &Event

) -> bool { + fn matches(&self, evt: &Event

) -> bool { (self)(&evt.erase()) } } @@ -69,7 +69,7 @@ impl Filter for fn(&Event<&dyn ErasedProps>) -> bool { pub struct FromFn(F); impl) -> bool> Filter for FromFn { - fn matches_event(&self, evt: &Event

) -> bool { + fn matches(&self, evt: &Event

) -> bool { (self.0)(&evt.erase()) } } @@ -84,8 +84,8 @@ pub struct And { } impl Filter for And { - fn matches_event(&self, evt: &Event

) -> bool { - self.lhs.matches_event(evt) && self.rhs.matches_event(evt) + fn matches(&self, evt: &Event

) -> bool { + self.lhs.matches(evt) && self.rhs.matches(evt) } } @@ -95,16 +95,16 @@ pub struct Or { } impl Filter for Or { - fn matches_event(&self, evt: &Event

) -> bool { - self.lhs.matches_event(evt) || self.rhs.matches_event(evt) + fn matches(&self, evt: &Event

) -> bool { + self.lhs.matches(evt) || self.rhs.matches(evt) } } pub struct ByRef<'a, T: ?Sized>(&'a T); impl<'a, T: Filter + ?Sized> Filter for ByRef<'a, T> { - fn matches_event(&self, evt: &Event

) -> bool { - self.0.matches_event(evt) + fn matches(&self, evt: &Event

) -> bool { + self.0.matches(evt) } } @@ -112,7 +112,7 @@ mod internal { use crate::{event::Event, props::ErasedProps}; pub trait DispatchFilter { - fn dispatch_emit_when(&self, evt: &Event<&dyn ErasedProps>) -> bool; + fn dispatch_matches(&self, evt: &Event<&dyn ErasedProps>) -> bool; } pub trait SealedFilter { @@ -131,19 +131,19 @@ impl internal::SealedFilter for T { } impl internal::DispatchFilter for T { - fn dispatch_emit_when(&self, evt: &Event<&dyn ErasedProps>) -> bool { - self.matches_event(evt) + fn dispatch_matches(&self, evt: &Event<&dyn ErasedProps>) -> bool { + self.matches(evt) } } impl<'a> Filter for dyn ErasedFilter + 'a { - fn matches_event(&self, evt: &Event

) -> bool { - self.erase_when().0.dispatch_emit_when(&evt.erase()) + fn matches(&self, evt: &Event

) -> bool { + self.erase_when().0.dispatch_matches(&evt.erase()) } } impl<'a> Filter for dyn ErasedFilter + Send + Sync + 'a { - fn matches_event(&self, evt: &Event

) -> bool { - (self as &(dyn ErasedFilter + 'a)).matches_event(evt) + fn matches(&self, evt: &Event

) -> bool { + (self as &(dyn ErasedFilter + 'a)).matches(evt) } } diff --git a/core/src/id.rs b/core/src/id.rs index dbcf3f6..4d46524 100644 --- a/core/src/id.rs +++ b/core/src/id.rs @@ -1,109 +1,67 @@ -use crate::empty::Empty; +use crate::{ + empty::Empty, + value::{ToValue, Value}, +}; +use core::{ + fmt, + num::{NonZeroU128, NonZeroU64}, + str, + str::FromStr, +}; #[derive(Clone, Copy)] -pub struct Id { - trace: TraceId, - span: SpanId, -} - -#[derive(Clone, Copy)] -pub struct TraceId(u128); - -#[derive(Clone, Copy)] -pub struct SpanId(u64); - -impl Id { - pub const EMPTY: Self = Id { - trace: TraceId::EMPTY, - span: SpanId::EMPTY, - }; - - pub fn new(trace: Option, span: Option) -> Self { - Id { - trace: trace.unwrap_or(TraceId::EMPTY), - span: span.unwrap_or(SpanId::EMPTY), - } - } +pub struct TraceId(NonZeroU128); - pub fn or(&self, incoming: Id) -> Self { - Id::new( - self.trace().or(incoming.trace()), - self.span().or(incoming.span()), - ) - } - - pub fn or_gen(&self, incoming: Id, gen_id: impl GenId) -> Self { - Id::new( - // Use the trace id from the incoming, then our trace id, then try generate one - // Ids are more likely to share the same trace id - self.trace() - .or(incoming.trace()) - .or_else(|| gen_id.gen_trace()), - // Use the span id from the incoming, then try generate one, then our span id - // Ids are more likely to have unique span ids - self.span() - .or_else(|| gen_id.gen_span()) - .or(incoming.span()), - ) - } - - pub fn trace(&self) -> Option { - if self.trace.is_empty() { - None - } else { - Some(self.trace) - } +impl fmt::Debug for TraceId { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str(str::from_utf8(&self.to_hex()).unwrap()) } +} - pub fn span(&self) -> Option { - if self.span.is_empty() { - None - } else { - Some(self.span) - } +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 Default for Id { - fn default() -> Self { - Self::EMPTY +impl FromStr for TraceId { + type Err = ParseIdError; + + fn from_str(s: &str) -> Result { + todo!() } } -impl From for Id { - fn from(value: SpanId) -> Self { - Id::new(None, Some(value)) +impl ToValue for TraceId { + fn to_value(&self) -> Value { + Value::capture_display(self) } } -impl From for Id { - fn from(value: TraceId) -> Self { - Id::new(Some(value), None) +impl<'v> Value<'v> { + pub fn to_trace_id(&self) -> Option { + self.downcast_ref::() + .copied() + .or_else(|| self.parse()) } } -const HEX: [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', -]; - impl TraceId { - const EMPTY: Self = TraceId(0); - - fn is_empty(&self) -> bool { - self.0 == Self::EMPTY.0 + pub fn new(v: NonZeroU128) -> Self { + TraceId(v) } - pub fn from_u128(v: u128) -> Self { - TraceId(v) + pub fn from_u128(v: u128) -> Option { + Some(TraceId(NonZeroU128::new(v)?)) } pub fn to_u128(&self) -> u128 { - self.0 + self.0.get() } pub fn to_hex(&self) -> [u8; 32] { let mut dst = [0; 32]; - let src: [u8; 16] = self.0.to_be_bytes(); + let src: [u8; 16] = self.0.get().to_be_bytes(); for i in 0..src.len() { let b = src[i]; @@ -116,24 +74,59 @@ impl TraceId { } } -impl SpanId { - const EMPTY: Self = SpanId(0); +#[derive(Clone, Copy)] +pub struct SpanId(NonZeroU64); + +impl fmt::Debug for SpanId { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str(str::from_utf8(&self.to_hex()).unwrap()) + } +} + +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 { + todo!() + } +} - fn is_empty(&self) -> bool { - self.0 == Self::EMPTY.0 +impl ToValue for SpanId { + fn to_value(&self) -> Value { + Value::capture_display(self) } +} - pub fn from_u64(v: u64) -> Self { +impl<'v> Value<'v> { + pub fn to_span_id(&self) -> Option { + self.downcast_ref::() + .copied() + .or_else(|| self.parse()) + } +} + +impl SpanId { + pub fn new(v: NonZeroU64) -> Self { SpanId(v) } + pub fn from_u64(v: u64) -> Option { + Some(SpanId(NonZeroU64::new(v)?)) + } + pub fn to_u64(&self) -> u64 { - self.0 + self.0.get() } pub fn to_hex(&self) -> [u8; 16] { let mut dst = [0; 16]; - let src: [u8; 8] = self.0.to_be_bytes(); + let src: [u8; 8] = self.0.get().to_be_bytes(); for i in 0..src.len() { let b = src[i]; @@ -146,80 +139,62 @@ impl SpanId { } } -// TODO: Better name for this; may not always generate ids. Trace ids may come from a known source etc -pub trait GenId { - fn gen(&self) -> Id { - Id::new(self.gen_trace(), self.gen_span()) - } +const HEX: [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', +]; - fn gen_trace(&self) -> Option; - fn gen_span(&self) -> Option; -} +pub struct ParseIdError {} -impl<'a, T: GenId + ?Sized> GenId for &'a T { - fn gen(&self) -> Id { - (**self).gen() - } +pub trait IdSource { + fn trace_id(&self) -> Option; + fn span_id(&self) -> Option; +} - fn gen_trace(&self) -> Option { - (**self).gen_trace() +impl<'a, T: IdSource + ?Sized> IdSource for &'a T { + fn trace_id(&self) -> Option { + (**self).trace_id() } - fn gen_span(&self) -> Option { - (**self).gen_span() + fn span_id(&self) -> Option { + (**self).span_id() } } -impl<'a, T: GenId> GenId for Option { - fn gen(&self) -> Id { - self.as_ref() - .map(|id| Id::new(id.gen_trace(), id.gen_span())) - .unwrap_or_default() - } - - fn gen_trace(&self) -> Option { - self.as_ref().and_then(|id| id.gen_trace()) +impl<'a, T: IdSource> IdSource for Option { + fn trace_id(&self) -> Option { + self.as_ref().and_then(|id| id.trace_id()) } - fn gen_span(&self) -> Option { - self.as_ref().and_then(|id| id.gen_span()) + fn span_id(&self) -> Option { + self.as_ref().and_then(|id| id.span_id()) } } #[cfg(feature = "alloc")] -impl<'a, T: GenId + ?Sized + 'a> GenId for alloc::boxed::Box { - fn gen(&self) -> Id { - (**self).gen() +impl<'a, T: IdSource + ?Sized + 'a> IdSource for alloc::boxed::Box { + fn trace_id(&self) -> Option { + (**self).trace_id() } - fn gen_trace(&self) -> Option { - (**self).gen_trace() - } - - fn gen_span(&self) -> Option { - (**self).gen_span() + fn span_id(&self) -> Option { + (**self).span_id() } } -impl GenId for Empty { - fn gen(&self) -> Id { - Default::default() - } - - fn gen_trace(&self) -> Option { +impl IdSource for Empty { + fn trace_id(&self) -> Option { None } - fn gen_span(&self) -> Option { + fn span_id(&self) -> Option { None } } mod internal { - use super::{Id, SpanId, TraceId}; + use super::{SpanId, TraceId}; pub trait DispatchGenId { - fn dispatch_gen(&self) -> Id; fn dispatch_gen_trace(&self) -> Option; fn dispatch_gen_span(&self) -> Option; } @@ -229,54 +204,42 @@ mod internal { } } -pub trait ErasedGenId: internal::SealedIdGenerator {} +pub trait ErasedIdSource: internal::SealedIdGenerator {} -impl ErasedGenId for T {} +impl ErasedIdSource for T {} -impl internal::SealedIdGenerator for T { +impl internal::SealedIdGenerator for T { fn erase_gen_id(&self) -> crate::internal::Erased<&dyn internal::DispatchGenId> { crate::internal::Erased(self) } } -impl internal::DispatchGenId for T { - fn dispatch_gen(&self) -> Id { - self.gen() - } - +impl internal::DispatchGenId for T { fn dispatch_gen_trace(&self) -> Option { - self.gen_trace() + self.trace_id() } fn dispatch_gen_span(&self) -> Option { - self.gen_span() + self.span_id() } } -impl<'a> GenId for dyn ErasedGenId + 'a { - fn gen(&self) -> Id { - self.erase_gen_id().0.dispatch_gen() - } - - fn gen_trace(&self) -> Option { +impl<'a> IdSource for dyn ErasedIdSource + 'a { + fn trace_id(&self) -> Option { self.erase_gen_id().0.dispatch_gen_trace() } - fn gen_span(&self) -> Option { + fn span_id(&self) -> Option { self.erase_gen_id().0.dispatch_gen_span() } } -impl<'a> GenId for dyn ErasedGenId + Send + Sync + 'a { - fn gen(&self) -> Id { - (self as &(dyn ErasedGenId + 'a)).gen() - } - - fn gen_trace(&self) -> Option { - (self as &(dyn ErasedGenId + 'a)).gen_trace() +impl<'a> IdSource for dyn ErasedIdSource + Send + Sync + 'a { + fn trace_id(&self) -> Option { + (self as &(dyn ErasedIdSource + 'a)).trace_id() } - fn gen_span(&self) -> Option { - (self as &(dyn ErasedGenId + 'a)).gen_span() + fn span_id(&self) -> Option { + (self as &(dyn ErasedIdSource + 'a)).span_id() } } diff --git a/core/src/level.rs b/core/src/level.rs index 264988a..f5ae2f3 100644 --- a/core/src/level.rs +++ b/core/src/level.rs @@ -1,4 +1,5 @@ -use core::fmt; +use crate::value::{ToValue, Value}; +use core::{fmt, str::FromStr}; #[derive(Clone, Copy)] pub enum Level { @@ -25,8 +26,32 @@ impl fmt::Display for Level { } } +impl FromStr for Level { + type Err = ParseLevelError; + + fn from_str(s: &str) -> Result { + todo!() + } +} + +pub struct ParseLevelError {} + impl Default for Level { fn default() -> Self { Level::Info } } + +impl ToValue for Level { + fn to_value(&self) -> Value { + Value::capture_display(self) + } +} + +impl<'v> Value<'v> { + pub fn to_level(&self) -> Option { + self.downcast_ref::() + .copied() + .or_else(|| self.parse()) + } +} diff --git a/core/src/lib.rs b/core/src/lib.rs index f570eb8..7b67da1 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -2,7 +2,6 @@ #[cfg(feature = "alloc")] extern crate alloc; -extern crate core; pub mod ambient; pub mod ctxt; diff --git a/core/src/props.rs b/core/src/props.rs index afde8e5..5a1ff2c 100644 --- a/core/src/props.rs +++ b/core/src/props.rs @@ -1,12 +1,16 @@ use core::{borrow::Borrow, ops::ControlFlow}; -use crate::{empty::Empty, key::Key, value::Value}; +use crate::{ + empty::Empty, + key::{Key, ToKey}, + value::{ToValue, Value}, +}; pub trait Props { fn for_each<'kv, F: FnMut(Key<'kv>, Value<'kv>) -> ControlFlow<()>>(&'kv self, for_each: F); - fn get<'v, K: Borrow>(&'v self, key: K) -> Option> { - let key = key.borrow(); + fn get<'v, K: ToKey>(&'v self, key: K) -> Option> { + let key = key.to_key(); let mut value = None; self.for_each(|k, v| { @@ -32,6 +36,13 @@ pub trait Props { } } + fn filter bool>(self, filter: F) -> Filter + where + Self: Sized, + { + Filter { src: self, filter } + } + fn by_ref(&self) -> ByRef { ByRef(self) } @@ -42,7 +53,7 @@ impl<'a, P: Props + ?Sized> Props for &'a P { (**self).for_each(for_each) } - fn get<'v, K: Borrow>(&'v self, key: K) -> Option> { + fn get<'v, K: ToKey>(&'v self, key: K) -> Option> { (**self).get(key) } } @@ -63,13 +74,22 @@ impl<'a, P: Props + ?Sized + 'a> Props for alloc::boxed::Box

{ } } -impl<'k, 'v> Props for [(Key<'k>, Value<'v>)] { +impl Props for (K, V) { + fn for_each<'kv, F: FnMut(Key<'kv>, Value<'kv>) -> ControlFlow<()>>( + &'kv self, + mut for_each: F, + ) { + for_each(self.0.to_key(), self.1.to_value()); + } +} + +impl Props for [(K, V)] { fn for_each<'kv, F: FnMut(Key<'kv>, Value<'kv>) -> ControlFlow<()>>( &'kv self, mut for_each: F, ) { for (k, v) in self { - match for_each(k.by_ref(), v.by_ref()) { + match for_each(k.to_key(), v.to_value()) { ControlFlow::Continue(()) => continue, ControlFlow::Break(()) => return, } @@ -77,7 +97,24 @@ impl<'k, 'v> Props for [(Key<'k>, Value<'v>)] { } } -impl<'k, 'v, const N: usize> Props for [(Key<'k>, Value<'v>); N] { +impl Props for [Option<(K, V)>] { + fn for_each<'kv, F: FnMut(Key<'kv>, Value<'kv>) -> ControlFlow<()>>( + &'kv self, + mut for_each: F, + ) { + for (k, v) in self.iter().filter_map(|kv| kv.as_ref()) { + match for_each(k.to_key(), v.to_value()) { + ControlFlow::Continue(()) => continue, + ControlFlow::Break(()) => return, + } + } + } +} + +impl Props for [T; N] +where + [T]: Props, +{ fn for_each<'kv, F: FnMut(Key<'kv>, Value<'kv>) -> ControlFlow<()>>(&'kv self, for_each: F) { (self as &[_]).for_each(for_each) } @@ -114,7 +151,7 @@ impl Props for Chain { self.second.for_each(for_each) } - fn get<'v, K: Borrow>(&'v self, key: K) -> Option> { + fn get<'v, K: ToKey>(&'v self, key: K) -> Option> { let key = key.borrow(); self.first.get(key).or_else(|| self.second.get(key)) @@ -129,6 +166,35 @@ impl<'a, P: Props + ?Sized> Props for ByRef<'a, P> { } } +pub struct Filter { + src: T, + filter: F, +} + +impl bool> Props for Filter { + fn for_each<'kv, FE: FnMut(Key<'kv>, Value<'kv>) -> ControlFlow<()>>( + &'kv self, + mut for_each: FE, + ) { + self.src.for_each(|k, v| { + if (self.filter)(k.by_ref(), v.by_ref()) { + for_each(k, v) + } else { + ControlFlow::Continue(()) + } + }) + } + + fn get<'v, K: ToKey>(&'v self, key: K) -> Option> { + let key = key.to_key(); + + match self.src.get(key.by_ref()) { + Some(value) if (self.filter)(key, value.by_ref()) => Some(value), + _ => None, + } + } +} + #[repr(transparent)] pub struct SortedSlice<'a>([(Key<'a>, Value<'a>)]); @@ -149,8 +215,8 @@ impl<'a> Props for SortedSlice<'a> { self.0.for_each(for_each) } - fn get<'v, K: Borrow>(&'v self, key: K) -> Option> { - let key = Key::new_ref(key.borrow()); + fn get<'v, K: ToKey>(&'v self, key: K) -> Option> { + let key = key.to_key(); self.0 .binary_search_by(|(k, _)| k.cmp(&key)) @@ -169,7 +235,8 @@ mod internal { &'kv self, for_each: &'f mut dyn FnMut(Key<'kv>, Value<'kv>) -> ControlFlow<()>, ); - fn dispatch_get<'v>(&'v self, key: &str) -> Option>; + + fn dispatch_get(&self, key: Key) -> Option; } pub trait SealedProps { @@ -195,7 +262,7 @@ impl internal::DispatchProps for P { self.for_each(for_each) } - fn dispatch_get<'v>(&'v self, key: &str) -> Option> { + fn dispatch_get<'v>(&'v self, key: Key) -> Option> { self.get(key) } } @@ -208,7 +275,7 @@ impl<'a> Props for dyn ErasedProps + 'a { self.erase_props().0.dispatch_for_each(&mut for_each) } - fn get<'v, K: Borrow>(&'v self, key: K) -> Option> { - self.erase_props().0.dispatch_get(key.borrow()) + fn get<'v, K: ToKey>(&'v self, key: K) -> Option> { + self.erase_props().0.dispatch_get(key.to_key()) } } diff --git a/core/src/target.rs b/core/src/target.rs index 3ba2c20..5887afa 100644 --- a/core/src/target.rs +++ b/core/src/target.rs @@ -7,7 +7,7 @@ use crate::{ }; pub trait Target { - fn emit_event(&self, evt: &Event

); + fn event(&self, evt: &Event

); fn blocking_flush(&self, timeout: Duration); @@ -27,8 +27,8 @@ pub trait Target { } impl<'a, T: Target + ?Sized> Target for &'a T { - fn emit_event(&self, evt: &Event

) { - (**self).emit_event(evt) + fn event(&self, evt: &Event

) { + (**self).event(evt) } fn blocking_flush(&self, timeout: Duration) { @@ -38,8 +38,8 @@ impl<'a, T: Target + ?Sized> Target for &'a T { #[cfg(feature = "std")] impl<'a, T: Target + ?Sized + 'a> Target for Box { - fn emit_event(&self, evt: &Event

) { - (**self).emit_event(evt) + fn event(&self, evt: &Event

) { + (**self).event(evt) } fn blocking_flush(&self, timeout: Duration) { @@ -48,10 +48,10 @@ impl<'a, T: Target + ?Sized + 'a> Target for Box { } impl Target for Option { - fn emit_event(&self, evt: &Event

) { + fn event(&self, evt: &Event

) { match self { - Some(target) => target.emit_event(evt), - None => Empty.emit_event(evt), + Some(target) => target.event(evt), + None => Empty.event(evt), } } @@ -64,12 +64,12 @@ impl Target for Option { } impl Target for Empty { - fn emit_event(&self, _: &Event

) {} + fn event(&self, _: &Event

) {} fn blocking_flush(&self, _: Duration) {} } impl Target for fn(&Event<&dyn ErasedProps>) { - fn emit_event(&self, evt: &Event

) { + fn event(&self, evt: &Event

) { (self)(&evt.erase()) } @@ -79,7 +79,7 @@ impl Target for fn(&Event<&dyn ErasedProps>) { pub struct FromFn(F); impl)> Target for FromFn { - fn emit_event(&self, evt: &Event

) { + fn event(&self, evt: &Event

) { (self.0)(&evt.erase()) } @@ -96,9 +96,9 @@ pub struct And { } impl Target for And { - fn emit_event(&self, evt: &Event

) { - self.lhs.emit_event(evt); - self.rhs.emit_event(evt); + fn event(&self, evt: &Event

) { + self.lhs.event(evt); + self.rhs.event(evt); } fn blocking_flush(&self, timeout: Duration) { @@ -112,8 +112,8 @@ impl Target for And { pub struct ByRef<'a, T: ?Sized>(&'a T); impl<'a, T: Target + ?Sized> Target for ByRef<'a, T> { - fn emit_event(&self, evt: &Event

) { - self.0.emit_event(evt) + fn event(&self, evt: &Event

) { + self.0.event(evt) } fn blocking_flush(&self, timeout: Duration) { @@ -127,7 +127,7 @@ mod internal { use crate::{event::Event, props::ErasedProps}; pub trait DispatchTarget { - fn dispatch_emit_to(&self, evt: &Event<&dyn ErasedProps>); + fn dispatch_event(&self, evt: &Event<&dyn ErasedProps>); fn dispatch_blocking_flush(&self, timeout: Duration); } @@ -147,8 +147,8 @@ impl internal::SealedTarget for T { } impl internal::DispatchTarget for T { - fn dispatch_emit_to(&self, evt: &Event<&dyn ErasedProps>) { - self.emit_event(evt) + fn dispatch_event(&self, evt: &Event<&dyn ErasedProps>) { + self.event(evt) } fn dispatch_blocking_flush(&self, timeout: Duration) { @@ -157,8 +157,8 @@ impl internal::DispatchTarget for T { } impl<'a> Target for dyn ErasedTarget + 'a { - fn emit_event(&self, evt: &Event

) { - self.erase_to().0.dispatch_emit_to(&evt.erase()) + fn event(&self, evt: &Event

) { + self.erase_to().0.dispatch_event(&evt.erase()) } fn blocking_flush(&self, timeout: Duration) { @@ -167,8 +167,8 @@ impl<'a> Target for dyn ErasedTarget + 'a { } impl<'a> Target for dyn ErasedTarget + Send + Sync + 'a { - fn emit_event(&self, evt: &Event

) { - (self as &(dyn ErasedTarget + 'a)).emit_event(evt) + fn event(&self, evt: &Event

) { + (self as &(dyn ErasedTarget + 'a)).event(evt) } fn blocking_flush(&self, timeout: Duration) { diff --git a/core/src/template.rs b/core/src/template.rs index 83110f0..16ea112 100644 --- a/core/src/template.rs +++ b/core/src/template.rs @@ -1,6 +1,10 @@ use core::{fmt, marker::PhantomData}; -use crate::{empty::Empty, props::Props, value::Value}; +use crate::{ + empty::Empty, + props::Props, + value::{ToValue, Value}, +}; #[derive(Clone)] pub struct Template<'a>(TemplateKind<'a>); @@ -58,7 +62,7 @@ impl<'a> Template<'a> { pub fn as_str(&self) -> Option<&str> { match self.0.parts() { - [Part(PartKind::Text { value, .. })] => Some(value), + [part] => part.as_str(), _ => None, } } @@ -81,6 +85,16 @@ 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) + } else { + Value::from_display(self) + } + } +} + pub struct Render<'a, P> { tpl: Template<'a>, props: P, @@ -106,33 +120,23 @@ impl<'a, P> Render<'a, P> { impl<'a, P: Props> Render<'a, P> { pub fn write(&self, mut writer: impl Write) -> fmt::Result { for part in self.tpl.0.parts() { - match part.0 { - PartKind::Text { - value, - value_static: _, - } => writer.write_text(value)?, - PartKind::Hole { - label, - ref formatter, - label_static: _, - } => { - if let Some(value) = self.props.get(label) { - if let Some(formatter) = formatter { - writer.write_hole_fmt(value, formatter.by_ref())?; - } else { - writer.write_hole_value(value)?; - } - } else { - writer.write_hole_label(label)?; - } - } - } + part.write(&mut writer, &self.props)?; } Ok(()) } } +impl<'a, P: Props> ToValue for Render<'a, P> { + fn to_value(&self) -> Value { + if let Some(tpl) = self.as_str() { + Value::from(tpl) + } else { + Value::from_display(self) + } + } +} + pub trait Write: fmt::Write { fn write_text(&mut self, text: &str) -> fmt::Result { self.write_str(text) @@ -213,96 +217,150 @@ pub struct Part<'a>(PartKind<'a>); impl<'a> Part<'a> { pub fn text(text: &'static str) -> Part<'a> { Part(PartKind::Text { - value: text, + value: text as *const str, value_static: Some(text), + #[cfg(feature = "alloc")] + value_owned: None, + _marker: PhantomData, }) } pub fn text_ref(text: &'a str) -> Part<'a> { Part(PartKind::Text { - value: text, + value: text as *const str, value_static: None, + #[cfg(feature = "alloc")] + value_owned: None, + _marker: PhantomData, }) } pub fn hole(label: &'static str) -> Part<'a> { Part(PartKind::Hole { - label, + label: label as *const str, label_static: Some(label), formatter: None, + #[cfg(feature = "alloc")] + label_owned: None, + _marker: PhantomData, }) } pub fn hole_ref(label: &'a str) -> Part<'a> { Part(PartKind::Hole { - label, + label: label as *const str, label_static: None, + #[cfg(feature = "alloc")] + label_owned: None, formatter: None, + _marker: PhantomData, }) } + pub fn as_str(&self) -> Option<&str> { + match self.0 { + PartKind::Text { value, .. } => Some(unsafe { &*value }), + _ => None, + } + } + pub fn by_ref<'b>(&'b self) -> Part<'b> { match self.0 { PartKind::Text { value, value_static, + .. } => Part(PartKind::Text { value, value_static, + #[cfg(feature = "alloc")] + value_owned: None, + _marker: PhantomData, }), PartKind::Hole { label, label_static, ref formatter, + .. } => Part(PartKind::Hole { label, label_static, - formatter: formatter.as_ref().map(|formatter| formatter.by_ref()), + #[cfg(feature = "alloc")] + label_owned: None, + formatter: formatter.clone(), + _marker: PhantomData, }), } } - pub fn with_formatter(self, formatter: Formatter<'a>) -> Self { + fn to_owned(&self) -> Part<'static> { + todo!() + } + + pub fn with_formatter(self, formatter: Formatter) -> Self { match self.0 { - PartKind::Text { - value, - value_static, - } => Part(PartKind::Text { - value, - value_static, + #[cfg(not(feature = "alloc"))] + PartKind::Hole { + label, + label_static, + formatter: _, + _marker, + } => Part(PartKind::Hole { + label, + label_static, + formatter: Some(formatter), + _marker, }), + #[cfg(feature = "alloc")] PartKind::Hole { label, label_static, + label_owned, formatter: _, + _marker, } => Part(PartKind::Hole { label, label_static, + label_owned, formatter: Some(formatter), + _marker: PhantomData, }), + part => Part(part), + } + } + + fn write(&self, mut writer: impl Write, props: impl Props) -> fmt::Result { + match self.0 { + PartKind::Text { value, .. } => writer.write_text(unsafe { &*value }), + PartKind::Hole { + label, + ref formatter, + .. + } => { + let label = unsafe { &*label }; + + if let Some(value) = props.get(label) { + if let Some(formatter) = formatter { + writer.write_hole_fmt(value, formatter.clone()) + } else { + writer.write_hole_value(value) + } + } else { + writer.write_hole_label(label) + } + } } } } #[derive(Clone)] -pub struct Formatter<'a> { +pub struct Formatter { fmt: fn(Value, &mut fmt::Formatter) -> fmt::Result, - _marker: PhantomData<&'a dyn Fn(Value, &mut fmt::Formatter) -> fmt::Result>, } -impl<'a> Formatter<'a> { +impl Formatter { pub fn new(fmt: fn(Value, &mut fmt::Formatter) -> fmt::Result) -> Self { - Formatter { - fmt, - _marker: PhantomData, - } - } - - pub fn by_ref<'b>(&'b self) -> Formatter<'b> { - Formatter { - fmt: self.fmt, - _marker: PhantomData, - } + Formatter { fmt } } pub fn fmt(&self, value: Value, f: &mut fmt::Formatter) -> fmt::Result { @@ -328,15 +386,54 @@ impl<'a> Formatter<'a> { } } -#[derive(Clone)] enum PartKind<'a> { Text { - value: &'a str, + value: *const str, value_static: Option<&'static str>, + #[cfg(feature = "alloc")] + value_owned: Option, + _marker: PhantomData<&'a str>, }, Hole { - label: &'a str, + label: *const str, label_static: Option<&'static str>, - formatter: Option>, + #[cfg(feature = "alloc")] + label_owned: Option, + formatter: Option, + _marker: PhantomData<&'a str>, }, } + +impl<'a> Clone for PartKind<'a> { + fn clone(&self) -> Self { + todo!() + } +} + +#[cfg(feature = "alloc")] +mod alloc_support { + use super::*; + + pub struct OwnedTemplate(Vec>); + + impl<'a> Template<'a> { + pub fn to_owned(&self) -> OwnedTemplate { + let mut parts = Vec::new(); + + for part in self.0.parts() { + parts.push(part.to_owned()); + } + + OwnedTemplate(parts) + } + } + + impl OwnedTemplate { + pub fn by_ref(&self) -> Template { + Template(TemplateKind::Parts(&self.0)) + } + } +} + +#[cfg(feature = "alloc")] +pub use self::alloc_support::*; diff --git a/core/src/time.rs b/core/src/time.rs index 757f99a..5e14a58 100644 --- a/core/src/time.rs +++ b/core/src/time.rs @@ -1,8 +1,11 @@ -use core::{cmp, fmt, str, time::Duration}; +use core::{cmp, fmt, str, str::FromStr, time::Duration}; -use crate::empty::Empty; +use crate::{ + empty::Empty, + value::{ToValue, Value}, +}; -#[derive(Clone, Copy)] +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Timestamp(Duration); impl Timestamp { @@ -32,6 +35,28 @@ impl fmt::Display for Timestamp { } } +impl FromStr for Timestamp { + type Err = ParseTimestampError; + + fn from_str(s: &str) -> Result { + parse_rfc3339(s) + } +} + +impl ToValue for Timestamp { + fn to_value(&self) -> Value { + Value::capture_display(self) + } +} + +impl<'v> Value<'v> { + pub fn to_timestamp(&self) -> Option { + self.downcast_ref::() + .copied() + .or_else(|| self.parse()) + } +} + pub trait Clock { fn now(&self) -> Option; } @@ -105,6 +130,12 @@ impl<'a> Clock for dyn ErasedClock + Send + Sync + 'a { } } +fn parse_rfc3339(fmt: &str) -> Result { + todo!() +} + +pub struct ParseTimestampError {} + fn fmt_rfc3339(ts: Timestamp, f: &mut fmt::Formatter) -> fmt::Result { /* Original implementation: https://github.com/tailhook/humantime diff --git a/core/src/value.rs b/core/src/value.rs index 3cf16a2..c5055b9 100644 --- a/core/src/value.rs +++ b/core/src/value.rs @@ -1,12 +1,78 @@ -use core::fmt; +use core::{fmt, str::FromStr}; #[derive(Clone)] pub struct Value<'v>(value_bag::ValueBag<'v>); impl<'v> Value<'v> { + pub fn capture_display(value: &'v (impl fmt::Display + 'static)) -> Self { + Value(value_bag::ValueBag::capture_display(value)) + } + + pub fn from_display(value: &'v impl fmt::Display) -> Self { + Value(value_bag::ValueBag::from_display(value)) + } + pub fn by_ref<'b>(&'b self) -> Value<'b> { Value(self.0.by_ref()) } + + pub fn downcast_ref(&self) -> Option<&T> { + self.0.downcast_ref() + } + + pub fn visit(&self, visitor: impl Visitor<'v>) { + struct Visit(V); + + impl<'v, V: Visitor<'v>> value_bag::visit::Visit<'v> for Visit { + fn visit_any(&mut self, value: value_bag::ValueBag) -> Result<(), value_bag::Error> { + self.0.visit_any(Value(value)); + + Ok(()) + } + + fn visit_str(&mut self, value: &str) -> Result<(), value_bag::Error> { + self.0.visit_str(value); + + Ok(()) + } + } + + let _ = self.0.visit(Visit(visitor)); + } + + pub fn parse(&self) -> Option { + struct Extract(Option); + + impl<'v, T: FromStr> Visitor<'v> for Extract { + fn visit_any(&mut self, _: Value) {} + + fn visit_str(&mut self, value: &str) { + self.0 = value.parse().ok(); + } + } + + let mut visitor = Extract(None); + self.visit(&mut visitor); + visitor.0 + } +} + +pub trait Visitor<'v> { + fn visit_any(&mut self, value: Value); + + fn visit_str(&mut self, value: &str) { + self.visit_any(Value::from(value)) + } +} + +impl<'a, 'v, V: Visitor<'v> + ?Sized> Visitor<'v> for &'a mut V { + fn visit_any(&mut self, value: Value) { + (**self).visit_any(value) + } + + fn visit_str(&mut self, value: &str) { + (**self).visit_str(value) + } } impl<'v> fmt::Debug for Value<'v> { diff --git a/core/src/well_known.rs b/core/src/well_known.rs index 31d9707..0ac3c67 100644 --- a/core/src/well_known.rs +++ b/core/src/well_known.rs @@ -1,8 +1,65 @@ -pub const ERR_KEY: &'static str = "err"; +use crate::{ + id::{SpanId, TraceId}, + level::Level, + props::Props, + value::Value, +}; -pub const TS_KEY: &'static str = "ts"; -pub const LVL_KEY: &'static str = "lvl"; -pub const MSG_KEY: &'static str = "msg"; -pub const TPL_KEY: &'static str = "tpl"; +pub const ERR_KEY: &'static str = "#err"; -pub const RESERVED: &[&'static str] = &[TS_KEY, LVL_KEY, MSG_KEY, TPL_KEY]; +pub const TIMESTAMP_KEY: &'static str = "#ts"; +pub const TIMESTAMP_START_KEY: &'static str = "#tss"; + +pub const LEVEL_KEY: &'static str = "#lvl"; +pub const MESSAGE_KEY: &'static str = "#msg"; +pub const TEMPLATE_KEY: &'static str = "#tpl"; + +pub const SPAN_KEY: &'static str = "#sp"; +pub const SPAN_PARENT_KEY: &'static str = "#spp"; +pub const TRACE_KEY: &'static str = "#tr"; + +pub const fn is_reserved(key: &str) -> bool { + let key = key.as_bytes(); + + if key.len() > 1 { + key[0] == b'#' && key[1] != b'#' + } else if key.len() == 1 { + key[0] == b'#' + } else { + false + } +} + +pub trait WellKnown { + fn level(&self) -> Option; + + fn trace_id(&self) -> Option; + + fn span_id(&self) -> Option; + + fn parent_span_id(&self) -> Option; + + fn err(&self) -> Option; +} + +impl WellKnown for P { + fn level(&self) -> Option { + self.get(LEVEL_KEY)?.to_level() + } + + fn trace_id(&self) -> Option { + self.get(TRACE_KEY)?.to_trace_id() + } + + fn span_id(&self) -> Option { + self.get(SPAN_KEY)?.to_span_id() + } + + fn parent_span_id(&self) -> Option { + self.get(SPAN_PARENT_KEY)?.to_span_id() + } + + fn err(&self) -> Option { + self.get(ERR_KEY) + } +} diff --git a/macros/src/lib.rs b/macros/src/lib.rs index b22576d..c20aff2 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -23,9 +23,9 @@ mod fmt; mod hook; mod key; mod props; -mod span; mod template; mod util; +mod with; use util::ResultToTokens; @@ -109,11 +109,13 @@ pub fn fmt( } #[proc_macro_attribute] -pub fn span( +pub fn with( args: proc_macro::TokenStream, item: proc_macro::TokenStream, ) -> proc_macro::TokenStream { - span::expand_tokens(span::ExpandTokens { + with::expand_tokens(with::ExpandTokens { + sync_receiver: quote!(__private::__with), + async_receiver: quote!(__private::__with_future), input: TokenStream::from(args), item: TokenStream::from(item), }) @@ -251,7 +253,7 @@ pub fn as_error( fn emit(level: TokenStream, item: TokenStream) -> proc_macro::TokenStream { if filter::matches_build_filter() { emit::expand_tokens(emit::ExpandTokens { - receiver: quote!(emit), + receiver: quote!(__private::__emit), level, input: item, }) diff --git a/macros/src/props.rs b/macros/src/props.rs index 9f4a080..9686768 100644 --- a/macros/src/props.rs +++ b/macros/src/props.rs @@ -164,7 +164,7 @@ impl Props { } pub fn ensure_not_reserved(kv: &KeyValue) -> Result<(), syn::Error> { - if emit_core::well_known::RESERVED.contains(&&*kv.label) { + if emit_core::well_known::is_reserved(&kv.label) { Err(syn::Error::new( kv.span(), format!("`{}` is a reserved identifier", kv.label), diff --git a/macros/src/span.rs b/macros/src/with.rs similarity index 79% rename from macros/src/span.rs rename to macros/src/with.rs index 6b84dbf..c453a55 100644 --- a/macros/src/span.rs +++ b/macros/src/with.rs @@ -4,12 +4,11 @@ use syn::{ Stmt, }; -use crate::{ - props::{self, Props}, - template, -}; +use crate::props::{self, Props}; pub struct ExpandTokens { + pub sync_receiver: TokenStream, + pub async_receiver: TokenStream, pub item: TokenStream, pub input: TokenStream, } @@ -23,15 +22,13 @@ impl Parse for Args { } pub fn expand_tokens(opts: ExpandTokens) -> Result { - let (_, template, props) = template::parse2::(opts.input)?; + let props = syn::parse2::(opts.input)?; // Ensure props don't use reserved identifiers for prop in props.iter() { props::ensure_not_reserved(prop)?; } - let template_tokens = template.template_tokens(); - let mut item = syn::parse2::(opts.item)?; match &mut item { // A synchronous function @@ -42,11 +39,12 @@ pub fn expand_tokens(opts: ExpandTokens) -> Result { }, .. })) => { - **block = syn::parse2::(inject_sync(&props, template_tokens, quote!(#block)))?; + **block = + syn::parse2::(inject_sync(&props, opts.sync_receiver, quote!(#block)))?; } // A synchronous block Stmt::Expr(Expr::Block(ExprBlock { block, .. }), _) => { - *block = syn::parse2::(inject_sync(&props, template_tokens, quote!(#block)))?; + *block = syn::parse2::(inject_sync(&props, opts.sync_receiver, quote!(#block)))?; } // An asynchronous function Stmt::Item(Item::Fn(ItemFn { @@ -56,11 +54,13 @@ pub fn expand_tokens(opts: ExpandTokens) -> Result { }, .. })) => { - **block = syn::parse2::(inject_async(&props, template_tokens, quote!(#block)))?; + **block = + syn::parse2::(inject_async(&props, opts.async_receiver, quote!(#block)))?; } // An asynchronous block Stmt::Expr(Expr::Async(ExprAsync { block, .. }), _) => { - *block = syn::parse2::(inject_async(&props, template_tokens, quote!(#block)))?; + *block = + syn::parse2::(inject_async(&props, opts.async_receiver, quote!(#block)))?; } _ => return Err(syn::Error::new(item.span(), "unrecognized item type")), } @@ -68,22 +68,22 @@ pub fn expand_tokens(opts: ExpandTokens) -> Result { Ok(quote!(#item)) } -fn inject_sync(props: &Props, template_tokens: TokenStream, body: TokenStream) -> TokenStream { +fn inject_sync(props: &Props, receiver_tokens: TokenStream, body: TokenStream) -> TokenStream { let props_tokens = props.props_tokens(); quote!({ - let mut __span = emit::span(emit::Id::EMPTY, #template_tokens, #props_tokens); - let __span_guard = __span.enter(); + let mut __ctxt = emit::#receiver_tokens(#props_tokens); + let __ctxt_guard = __ctxt.enter(); #body }) } -fn inject_async(props: &Props, template_tokens: TokenStream, body: TokenStream) -> TokenStream { +fn inject_async(props: &Props, receiver_tokens: TokenStream, body: TokenStream) -> TokenStream { let props_tokens = props.props_tokens(); quote!({ - emit::span_future(emit::Id::EMPTY, #template_tokens, #props_tokens, async #body).await + emit::#receiver_tokens(#props_tokens, async #body).await }) } diff --git a/src/lib.rs b/src/lib.rs index 8ad2ff4..eb2f546 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,59 +12,78 @@ pub use emit_macros::*; #[doc(inline)] pub use emit_core::{ - ctxt, empty, event, filter, id, key, level, props, target, template, time, value, well_known, + empty, event, filter, id, key, level, props, target, template, time, value, well_known, }; +pub mod ctxt { + pub use crate::local_frame::*; + #[doc(inline)] + pub use emit_core::ctxt::*; +} + +use emit_core::{value::ToValue, well_known::WellKnown}; + pub use self::{ - event::Event, id::Id, key::Key, level::Level, props::Props, template::Template, - time::Timestamp, value::Value, + event::Event, key::Key, level::Level, props::Props, template::Template, time::Timestamp, + value::Value, }; +use crate::ctxt::{LocalFrame, LocalFrameFuture}; + mod macro_hooks; mod platform; -pub mod span; +pub mod local_frame; #[cfg(feature = "std")] mod setup; -pub fn emit(to: impl Target, when: impl Filter, lvl: Level, tpl: Template, props: impl Props) { +#[track_caller] +pub fn emit(evt: &Event) { let ambient = emit_core::ambient::get(); - ambient.with_current(|id, current_props| { - let ts = ambient.now(); - let props = props.chain(current_props); + base_emit( + ambient, + ambient, + ambient, + ambient, + evt.template(), + evt.props(), + ); +} + +#[track_caller] +fn base_emit( + to: impl Target, + when: impl Filter, + ctxt: impl Ctxt, + clock: impl Clock, + tpl: Template, + props: impl Props, +) { + ctxt.with_current(|ctxt| { + let ts = clock.now(); - let evt = Event::new(ts, id, lvl, tpl, props); + let evt = Event::point(ts, tpl, props.chain(ctxt)); - if when.matches_event(&evt) && ambient.matches_event(&evt) { - to.emit_event(&evt); - ambient.emit_event(&evt); + if when.matches(&evt) { + to.event(&evt); } }); } -pub fn span( - id: Id, - tpl: Template, - props: impl Props, -) -> span::Span { - let ambient = emit_core::ambient::get(); - - let id = id.or_gen(ambient.current_id(), ambient); - span::Span::new(ambient, ambient, id, tpl, props) +#[track_caller] +fn base_with(ctxt: impl Ctxt, props: impl Props) -> LocalFrame { + LocalFrame::new(ctxt, props) } -pub fn span_future( - id: Id, - tpl: Template, +#[track_caller] +fn base_with_future( + ctxt: impl Ctxt + Send + Sync + 'static, props: impl Props, future: F, -) -> span::SpanFuture { - let ambient = emit_core::ambient::get(); - - let id = id.or_gen(ambient.current_id(), ambient); - span::SpanFuture::new(ambient, ambient, id, tpl, props, future) +) -> LocalFrameFuture { + LocalFrameFuture::new(ctxt, props, future) } #[cfg(feature = "std")] @@ -72,31 +91,6 @@ pub fn setup() -> setup::Setup { setup::Setup::default() } -#[cfg(feature = "std")] -pub fn target() -> impl Target { - emit_core::ambient::get() -} - -#[cfg(feature = "std")] -pub fn filter() -> impl Filter { - emit_core::ambient::get() -} - -#[cfg(feature = "std")] -pub fn ctxt() -> impl Ctxt { - emit_core::ambient::get() -} - -#[cfg(feature = "std")] -pub fn clock() -> impl Clock { - emit_core::ambient::get() -} - -#[cfg(feature = "std")] -pub fn gen_id() -> impl id::GenId { - emit_core::ambient::get() -} - #[doc(hidden)] pub mod __private { pub use crate::macro_hooks::*; diff --git a/src/local_frame.rs b/src/local_frame.rs new file mode 100644 index 0000000..871d7d0 --- /dev/null +++ b/src/local_frame.rs @@ -0,0 +1,77 @@ +use emit_core::{ctxt::Ctxt, props::Props}; +use std::{ + future::Future, + marker::PhantomData, + mem, + pin::Pin, + task::{Context, Poll}, +}; + +pub struct LocalFrame { + scope: mem::ManuallyDrop, + ctxt: C, +} + +impl LocalFrame { + #[track_caller] + pub fn new(ctxt: C, props: impl Props) -> Self { + let scope = mem::ManuallyDrop::new(ctxt.open(props)); + + LocalFrame { ctxt, scope } + } + + #[track_caller] + pub fn enter(&mut self) -> EnterGuard { + self.ctxt.enter(&mut self.scope); + + EnterGuard { + scope: self, + _marker: PhantomData, + } + } +} + +pub struct EnterGuard<'a, C: Ctxt> { + scope: &'a mut LocalFrame, + _marker: PhantomData<*mut fn()>, +} + +impl<'a, C: Ctxt> Drop for EnterGuard<'a, C> { + fn drop(&mut self) { + self.scope.ctxt.exit(&mut self.scope.scope); + } +} + +impl Drop for LocalFrame { + fn drop(&mut self) { + self.ctxt + .close(unsafe { mem::ManuallyDrop::take(&mut self.scope) }) + } +} + +pub struct LocalFrameFuture { + frame: LocalFrame, + future: F, +} + +impl LocalFrameFuture { + #[track_caller] + pub fn new(scope: C, props: impl Props, future: F) -> Self { + LocalFrameFuture { + frame: LocalFrame::new(scope, props), + future, + } + } +} + +impl Future for LocalFrameFuture { + type Output = F::Output; + + #[track_caller] + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let unpinned = unsafe { Pin::get_unchecked_mut(self) }; + + let __guard = unpinned.frame.enter(); + unsafe { Pin::new_unchecked(&mut unpinned.future) }.poll(cx) + } +} diff --git a/src/macro_hooks.rs b/src/macro_hooks.rs index 1a2ce75..8194af0 100644 --- a/src/macro_hooks.rs +++ b/src/macro_hooks.rs @@ -1,11 +1,26 @@ use core::{any::Any, fmt}; -use emit_core::value::Value; - +use emit_core::value::{ToValue, Value}; + +use emit_core::{ + ambient, + ctxt::Ctxt, + filter::Filter, + id::IdSource, + level::Level, + props::Props, + target::Target, + template::Template, + time::Clock, + well_known::{WellKnown, LEVEL_KEY}, +}; #[cfg(feature = "std")] use std::error::Error; +use std::future::Future; use crate::{ + base_emit, base_with, base_with_future, + ctxt::{LocalFrame, LocalFrameFuture}, template::{Formatter, Part}, Key, }; @@ -17,7 +32,7 @@ The parameter lets us choose the way values are captured, since many types can be captured in lots of different ways. */ pub trait Capture { - fn capture(&self) -> crate::Value; + fn capture(&self) -> Value; } /** @@ -52,7 +67,7 @@ impl CaptureSized for CaptureSerde {} impl CaptureSized for CaptureError {} impl Capture for str { - fn capture(&self) -> crate::Value { + fn capture(&self) -> Value { Value::from(self) } } @@ -61,13 +76,13 @@ impl Capture for T where T: fmt::Display + Any, { - fn capture(&self) -> crate::Value { + fn capture(&self) -> Value { value_bag::ValueBag::capture_display(self).into() } } impl Capture for dyn fmt::Display { - fn capture(&self) -> crate::Value { + fn capture(&self) -> Value { value_bag::ValueBag::from_dyn_display(self).into() } } @@ -76,7 +91,7 @@ impl Capture for T where T: fmt::Display, { - fn capture(&self) -> crate::Value { + fn capture(&self) -> Value { value_bag::ValueBag::from_display(self).into() } } @@ -85,13 +100,13 @@ impl Capture for T where T: fmt::Debug + Any, { - fn capture(&self) -> crate::Value { + fn capture(&self) -> Value { value_bag::ValueBag::capture_debug(self).into() } } impl Capture for dyn fmt::Debug { - fn capture(&self) -> crate::Value { + fn capture(&self) -> Value { value_bag::ValueBag::from_dyn_debug(self).into() } } @@ -100,7 +115,7 @@ impl Capture for T where T: fmt::Debug, { - fn capture(&self) -> crate::Value { + fn capture(&self) -> Value { value_bag::ValueBag::from_debug(self).into() } } @@ -110,7 +125,7 @@ impl Capture for T where T: sval::Value + Any, { - fn capture(&self) -> crate::Value { + fn capture(&self) -> Value { value_bag::ValueBag::capture_sval2(self).into() } } @@ -120,7 +135,7 @@ impl Capture for T where T: sval::Value, { - fn capture(&self) -> crate::Value { + fn capture(&self) -> Value { value_bag::ValueBag::from_sval2(self).into() } } @@ -130,7 +145,7 @@ impl Capture for T where T: serde::Serialize + Any, { - fn capture(&self) -> crate::Value { + fn capture(&self) -> Value { value_bag::ValueBag::capture_serde1(self).into() } } @@ -140,7 +155,7 @@ impl Capture for T where T: serde::Serialize, { - fn capture(&self) -> crate::Value { + fn capture(&self) -> Value { value_bag::ValueBag::from_serde1(self).into() } } @@ -150,14 +165,14 @@ impl Capture for T where T: Error + 'static, { - fn capture(&self) -> crate::Value { + fn capture(&self) -> Value { value_bag::ValueBag::capture_error(self).into() } } #[cfg(feature = "std")] impl<'a> Capture for (dyn Error + 'static) { - fn capture(&self) -> crate::Value { + fn capture(&self) -> Value { value_bag::ValueBag::from_dyn_error(self).into() } } @@ -176,70 +191,70 @@ so that when a bound isn't satisfied we get a more accurate type error. contexts. (We're expecting non-hygienic spans to support value interpolation). */ pub trait __PrivateCaptureHook { - fn __private_capture_as_default(&self) -> crate::Value + fn __private_capture_as_default(&self) -> Value where Self: Capture, { Capture::capture(self) } - fn __private_capture_as_display(&self) -> crate::Value + fn __private_capture_as_display(&self) -> Value where Self: Capture, { Capture::capture(self) } - fn __private_capture_anon_as_display(&self) -> crate::Value + fn __private_capture_anon_as_display(&self) -> Value where Self: Capture, { Capture::capture(self) } - fn __private_capture_as_debug(&self) -> crate::Value + fn __private_capture_as_debug(&self) -> Value where Self: Capture, { Capture::capture(self) } - fn __private_capture_anon_as_debug(&self) -> crate::Value + fn __private_capture_anon_as_debug(&self) -> Value where Self: Capture, { Capture::capture(self) } - fn __private_capture_as_sval(&self) -> crate::Value + fn __private_capture_as_sval(&self) -> Value where Self: Capture, { Capture::capture(self) } - fn __private_capture_anon_as_sval(&self) -> crate::Value + fn __private_capture_anon_as_sval(&self) -> Value where Self: Capture, { Capture::capture(self) } - fn __private_capture_as_serde(&self) -> crate::Value + fn __private_capture_as_serde(&self) -> Value where Self: Capture, { Capture::capture(self) } - fn __private_capture_anon_as_serde(&self) -> crate::Value + fn __private_capture_anon_as_serde(&self) -> Value where Self: Capture, { Capture::capture(self) } - fn __private_capture_as_error(&self) -> crate::Value + fn __private_capture_as_error(&self) -> Value where Self: Capture, { @@ -251,7 +266,7 @@ impl __PrivateCaptureHook for T {} pub trait __PrivateFmtHook<'a> { fn __private_fmt_as_default(self) -> Self; - fn __private_fmt_as(self, formatter: Formatter<'a>) -> Self; + fn __private_fmt_as(self, formatter: Formatter) -> Self; } impl<'a> __PrivateFmtHook<'a> for Part<'a> { @@ -259,7 +274,7 @@ impl<'a> __PrivateFmtHook<'a> for Part<'a> { self } - fn __private_fmt_as(self, formatter: Formatter<'a>) -> Self { + fn __private_fmt_as(self, formatter: Formatter) -> Self { self.with_formatter(formatter) } } @@ -286,6 +301,37 @@ impl<'a> __PrivateKeyHook for Key<'a> { } } +#[track_caller] +pub fn __emit(to: impl Target, when: impl Filter, lvl: Level, tpl: Template, props: impl Props) { + let ambient = ambient::get(); + + base_emit( + to.and(ambient), + when.and(ambient), + ambient, + ambient, + tpl, + (LEVEL_KEY, lvl).chain(props), + ); +} + +#[track_caller] +pub fn __with(props: impl Props) -> LocalFrame { + let ambient = ambient::get(); + + base_with(ambient, props) +} + +#[track_caller] +pub fn __with_future( + props: impl Props, + future: F, +) -> LocalFrameFuture { + let ambient = ambient::get(); + + base_with_future(ambient, props, future) +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/platform.rs b/src/platform.rs index e6391d5..ccbe802 100644 --- a/src/platform.rs +++ b/src/platform.rs @@ -1,10 +1,10 @@ -use crate::{id::GenId, time::Clock, Timestamp}; +use crate::{id::IdSource, time::Clock, Timestamp}; #[cfg(not(feature = "std"))] use emit_core::empty::Empty; #[cfg(feature = "std")] -use emit_core::{id::ErasedGenId, time::ErasedClock}; +use emit_core::{id::ErasedIdSource, time::ErasedClock}; #[cfg(feature = "std")] pub(crate) mod system_clock; @@ -13,7 +13,7 @@ pub(crate) mod system_clock; pub(crate) mod thread_local_ctxt; #[cfg(feature = "rng")] -pub(crate) mod rng_gen_id; +pub(crate) mod rng; #[cfg(feature = "std")] type DefaultTime = system_clock::SystemClock; @@ -21,7 +21,7 @@ type DefaultTime = system_clock::SystemClock; type DefaultTime = Empty; #[cfg(feature = "rng")] -type DefaultGenId = rng_gen_id::RngGenId; +type DefaultIdSource = rng::Rng; #[cfg(not(feature = "rng"))] type DefaultGenId = Empty; @@ -34,9 +34,9 @@ pub(crate) struct Platform { #[cfg(feature = "std")] pub(crate) clock: Box, #[cfg(not(feature = "std"))] - pub(crate) gen_id: DefaultGenId, + pub(crate) gen_id: DefaultIdSource, #[cfg(feature = "std")] - pub(crate) gen_id: Box, + pub(crate) id_src: Box, } impl Default for Platform { @@ -53,9 +53,9 @@ impl Platform { #[cfg(feature = "std")] clock: Box::new(DefaultTime::default()), #[cfg(not(feature = "std"))] - gen_id: DefaultGenId::default(), + id_src: DefaultIdSource::default(), #[cfg(feature = "std")] - gen_id: Box::new(DefaultGenId::default()), + id_src: Box::new(DefaultIdSource::default()), } } } @@ -66,16 +66,12 @@ impl Clock for Platform { } } -impl GenId for Platform { - fn gen(&self) -> crate::Id { - self.gen_id.gen() +impl IdSource for Platform { + fn trace_id(&self) -> Option { + self.id_src.trace_id() } - fn gen_trace(&self) -> Option { - self.gen_id.gen_trace() - } - - fn gen_span(&self) -> Option { - self.gen_id.gen_span() + fn span_id(&self) -> Option { + self.id_src.span_id() } } diff --git a/src/platform/rng.rs b/src/platform/rng.rs new file mode 100644 index 0000000..74b71cc --- /dev/null +++ b/src/platform/rng.rs @@ -0,0 +1,18 @@ +use emit_core::id::{IdSource, SpanId, TraceId}; + +#[derive(Default, Debug, Clone, Copy)] +pub struct Rng; + +impl IdSource for Rng { + fn trace_id(&self) -> Option { + use rand::Rng; + + Some(TraceId::new(rand::thread_rng().gen())) + } + + fn span_id(&self) -> Option { + use rand::Rng; + + Some(SpanId::new(rand::thread_rng().gen())) + } +} diff --git a/src/platform/rng_gen_id.rs b/src/platform/rng_gen_id.rs deleted file mode 100644 index a9f8ed5..0000000 --- a/src/platform/rng_gen_id.rs +++ /dev/null @@ -1,18 +0,0 @@ -use emit_core::id::{GenId, SpanId, TraceId}; - -#[derive(Default, Debug, Clone, Copy)] -pub struct RngGenId; - -impl GenId for RngGenId { - fn gen_trace(&self) -> Option { - use rand::Rng; - - Some(TraceId::from_u128(rand::thread_rng().gen())) - } - - fn gen_span(&self) -> Option { - use rand::Rng; - - Some(SpanId::from_u64(rand::thread_rng().gen())) - } -} diff --git a/src/platform/thread_local_ctxt.rs b/src/platform/thread_local_ctxt.rs index eb895ce..3a6f8db 100644 --- a/src/platform/thread_local_ctxt.rs +++ b/src/platform/thread_local_ctxt.rs @@ -1,23 +1,20 @@ use std::{ cell::RefCell, + collections::HashMap, ops::ControlFlow::{self, *}, }; use emit_core::{ ctxt::Ctxt, - id::Id, key::{Key, OwnedKey}, props::Props, - template::Template, - time::Timestamp, value::{OwnedValue, Value}, }; // TODO: Optimize this thread_local! { static ACTIVE: RefCell = RefCell::new(ThreadLocalSpan { - id: Id::EMPTY, - props: Vec::new(), + props: HashMap::new(), }); } @@ -26,8 +23,7 @@ pub struct ThreadLocalCtxt; #[derive(Clone)] pub struct ThreadLocalSpan { - id: Id, - props: Vec<(OwnedKey, OwnedValue)>, + props: HashMap, } impl Props for ThreadLocalSpan { @@ -41,40 +37,31 @@ impl Props for ThreadLocalSpan { } impl Ctxt for ThreadLocalCtxt { - type Props = ThreadLocalSpan; - type Span = ThreadLocalSpan; + type CurrentProps = ThreadLocalSpan; + type LocalFrame = ThreadLocalSpan; - fn with_current(&self, with: F) { - ACTIVE.with(|span| { - let span = &*span.borrow(); - - with(span.id, &span) - }) - } - - fn current_id(&self) -> Id { - ACTIVE.with(|span| span.borrow().id) + fn with_current(&self, with: F) { + ACTIVE.with(|span| with(&*span.borrow())) } - fn open(&self, _: Option, id: Id, _: Template, props: P) -> Self::Span { + fn open(&self, props: P) -> Self::LocalFrame { let mut span = ACTIVE.with(|span| span.borrow().clone()); - span.id = id; props.for_each(|k, v| { - span.props.push((k.to_owned(), v.to_owned())); + span.props.insert(k.to_owned(), v.to_owned()); Continue(()) }); span } - fn enter(&self, link: &mut Self::Span) { + fn enter(&self, link: &mut Self::LocalFrame) { ACTIVE.with(|span| std::mem::swap(link, &mut *span.borrow_mut())); } - fn exit(&self, link: &mut Self::Span) { + fn exit(&self, link: &mut Self::LocalFrame) { ACTIVE.with(|span| std::mem::swap(link, &mut *span.borrow_mut())); } - fn close(&self, _: Option, _: Self::Span) {} + fn close(&self, _: Self::LocalFrame) {} } diff --git a/src/setup.rs b/src/setup.rs index fc0fbba..3bb04da 100644 --- a/src/setup.rs +++ b/src/setup.rs @@ -75,7 +75,7 @@ impl< TCtxt: Ctxt + Send + Sync + 'static, > Setup where - TCtxt::Span: Send + 'static, + TCtxt::LocalFrame: Send + 'static, { #[must_use = "call `blocking_flush(std::time::Duration::from_secs(5))` at the end of `main` to ensure events are flushed."] pub fn init(self) -> Init<&'static TTarget> { @@ -85,7 +85,7 @@ where .with_filter(self.filter) .with_ctxt(self.ctxt) .with_clock(self.platform.clock) - .with_gen_id(self.platform.gen_id), + .with_gen_id(self.platform.id_src), ) .expect("already initialized"); diff --git a/src/span.rs b/src/span.rs deleted file mode 100644 index be89718..0000000 --- a/src/span.rs +++ /dev/null @@ -1,75 +0,0 @@ -use emit_core::{ctxt::Ctxt, empty::Empty, id::Id, props::Props, template::Template, time::Clock}; -use std::{ - future::Future, - marker::PhantomData, - mem, - pin::Pin, - task::{Context, Poll}, -}; - -pub struct Span { - scope: mem::ManuallyDrop, - ctxt: C, - clock: T, -} - -impl Span { - pub fn new(ctxt: C, clock: T, id: Id, tpl: Template, props: impl Props) -> Self { - let scope = mem::ManuallyDrop::new(ctxt.open(clock.now(), id, tpl, props)); - - Span { ctxt, scope, clock } - } - - pub fn enter(&mut self) -> SpanGuard { - self.ctxt.enter(&mut self.scope); - - SpanGuard { - scope: self, - _marker: PhantomData, - } - } -} - -pub struct SpanGuard<'a, C: Ctxt, T: Clock> { - scope: &'a mut Span, - _marker: PhantomData<*mut fn()>, -} - -impl<'a, C: Ctxt, T: Clock> Drop for SpanGuard<'a, C, T> { - fn drop(&mut self) { - self.scope.ctxt.exit(&mut self.scope.scope); - } -} - -impl Drop for Span { - fn drop(&mut self) { - self.ctxt.close(self.clock.now(), unsafe { - mem::ManuallyDrop::take(&mut self.scope) - }) - } -} - -pub struct SpanFuture { - scope: Span, - future: F, -} - -impl SpanFuture { - pub fn new(scope: C, clock: T, id: Id, tpl: Template, props: impl Props, future: F) -> Self { - SpanFuture { - scope: Span::new(scope, clock, id, tpl, props), - future, - } - } -} - -impl Future for SpanFuture { - type Output = F::Output; - - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let unpinned = unsafe { Pin::get_unchecked_mut(self) }; - - let __guard = unpinned.scope.enter(); - unsafe { Pin::new_unchecked(&mut unpinned.future) }.poll(cx) - } -} diff --git a/targets/opentelemetry/src/id.rs b/targets/opentelemetry/src/id.rs index 94bfdb9..4bff55a 100644 --- a/targets/opentelemetry/src/id.rs +++ b/targets/opentelemetry/src/id.rs @@ -1,6 +1,6 @@ use opentelemetry_api::trace::{SpanId, TraceId}; -pub(crate) fn to_trace_id(id: emit_core::id::Id) -> TraceId { +pub(crate) fn to_trace_id(id: emit_core::id::IdSource) -> TraceId { TraceId::from_bytes( id.trace() .map(|id| id.to_u128()) @@ -9,7 +9,7 @@ pub(crate) fn to_trace_id(id: emit_core::id::Id) -> TraceId { ) } -pub(crate) fn to_span_id(id: emit_core::id::Id) -> SpanId { +pub(crate) fn to_span_id(id: emit_core::id::IdSource) -> SpanId { SpanId::from_bytes( id.span() .map(|id| id.to_u64()) @@ -18,11 +18,11 @@ pub(crate) fn to_span_id(id: emit_core::id::Id) -> SpanId { ) } -pub(crate) fn from_trace_span_ids(trace: TraceId, span: SpanId) -> emit_core::id::Id { +pub(crate) fn from_trace_span_ids(trace: TraceId, span: SpanId) -> emit_core::id::IdSource { let trace_id = u128::from_be_bytes(trace.to_bytes()); let span_id = u64::from_be_bytes(span.to_bytes()); - emit_core::id::Id::new( + emit_core::id::IdSource::new( Some(emit_core::id::TraceId::from_u128(trace_id)), Some(emit_core::id::SpanId::from_u64(span_id)), ) diff --git a/targets/opentelemetry/src/logs.rs b/targets/opentelemetry/src/logs.rs index 0ed7a07..2e146d3 100644 --- a/targets/opentelemetry/src/logs.rs +++ b/targets/opentelemetry/src/logs.rs @@ -3,6 +3,7 @@ use crate::{ key::to_key, value::to_any_value, }; +use emit_core::well_known::WellKnown; use opentelemetry_api::{ logs::{AnyValue, LogRecord, Logger as _, Severity}, trace::{SpanContext, TraceFlags, TraceState}, @@ -21,7 +22,7 @@ pub fn target(logger: Logger) -> OpenTelemetryLogsTarget { pub struct OpenTelemetryLogsTarget(Logger); impl emit_core::target::Target for OpenTelemetryLogsTarget { - fn emit_event(&self, evt: &emit_core::event::Event

) { + fn event(&self, evt: &emit_core::event::Event

) { self.0.emit(to_record(evt)); } @@ -45,18 +46,18 @@ fn to_record(evt: &emit_core::event::Event) -> Log TraceState::default(), )) .with_timestamp( - evt.ts() + evt.timestamp() .map(|ts| ts.to_system_time()) .unwrap_or_else(SystemTime::now), ) - .with_severity_number(match evt.lvl() { + .with_severity_number(match evt.level().unwrap_or(emit_core::level::Level::Info) { emit_core::level::Level::Debug => Severity::Debug, emit_core::level::Level::Info => Severity::Info, emit_core::level::Level::Warn => Severity::Warn, emit_core::level::Level::Error => Severity::Error, }) - .with_severity_text(evt.lvl().to_string()) - .with_body(AnyValue::String(evt.msg().to_string().into())) + .with_severity_text(evt.level().to_string()) + .with_body(AnyValue::String(evt.message().to_string().into())) .with_attributes({ let mut attributes = OrderMap::::new(); diff --git a/targets/opentelemetry/src/traces.rs b/targets/opentelemetry/src/traces.rs index 71e990f..8820871 100644 --- a/targets/opentelemetry/src/traces.rs +++ b/targets/opentelemetry/src/traces.rs @@ -1,9 +1,10 @@ +/* use crate::{ id::{from_trace_span_ids, to_span_id, to_trace_id}, key::to_key, value::to_value, }; -use emit_core::{id::Id, props::Props, template::Template, time::Timestamp}; +use emit_core::{id::IdSource, props::Props, template::Template, time::Timestamp}; use opentelemetry_api::{ trace::{TraceContextExt, Tracer}, Context, ContextGuard, OrderMap, @@ -28,10 +29,16 @@ impl emit_core::ctxt::Ctxt for OpenTelemetryTracesCtxt where T::Span: Send + Sync + 'static, { - type Props = emit_core::empty::Empty; - type Span = OpenTelemetrySpan; - - fn open(&self, ts: Option, id: Id, tpl: Template, props: P) -> Self::Span { + type CurrentProps = emit_core::empty::Empty; + type LocalFrame = OpenTelemetrySpan; + + fn open( + &self, + ts: Option, + id: IdSource, + tpl: Template, + props: P, + ) -> Self::LocalFrame { let span = self .0 .span_builder(tpl.to_string()) @@ -62,13 +69,13 @@ where } } - fn enter(&self, span: &mut Self::Span) { + fn enter(&self, span: &mut Self::LocalFrame) { if let Some(cx) = span.cx.take() { span.active = Some(cx.attach()); } } - fn with_current(&self, with: F) { + fn with_current(&self, with: F) { let cx = Context::current(); let id = from_trace_span_ids( @@ -79,12 +86,12 @@ where with(id, &emit_core::empty::Empty) } - fn exit(&self, span: &mut Self::Span) { + fn exit(&self, span: &mut Self::LocalFrame) { span.cx = Some(Context::current()); drop(span.active.take()); } - fn close(&self, ts: Option, mut span: Self::Span) { + fn close(&self, ts: Option, mut span: Self::LocalFrame) { if let Some(cx) = span.cx.take() { cx.span().end_with_timestamp( ts.map(|ts| ts.to_system_time()) @@ -93,3 +100,4 @@ where } } } +*/ diff --git a/targets/otlp/src/logs.rs b/targets/otlp/src/logs.rs index 08c4319..1b9b172 100644 --- a/targets/otlp/src/logs.rs +++ b/targets/otlp/src/logs.rs @@ -8,6 +8,7 @@ use crate::{ value::to_value, }; use emit_batcher::BatchError; +use emit_core::well_known::WellKnown; use std::{collections::HashSet, ops::ControlFlow, time::Duration}; pub fn http(dst: impl Into) -> OtlpLogsTargetBuilder { @@ -60,32 +61,34 @@ impl OtlpLogsTargetBuilder { } impl emit_core::target::Target for OtlpLogsTarget { - fn emit_event(&self, evt: &emit_core::event::Event

) { + fn event(&self, evt: &emit_core::event::Event

) { let time_unix_nano = evt - .ts() + .timestamp() .map(|ts| ts.to_unix().as_nanos() as u64) .unwrap_or_default(); let observed_time_unix_nano = time_unix_nano; - let severity_number = match evt.lvl() { + let level = evt.props().level().unwrap_or(emit_core::level::Level::Info); + + let severity_number = match level { emit_core::level::Level::Debug => SeverityNumber::Debug as i32, emit_core::level::Level::Info => SeverityNumber::Info as i32, emit_core::level::Level::Warn => SeverityNumber::Warn as i32, emit_core::level::Level::Error => SeverityNumber::Error as i32, }; - let severity_text = evt.lvl().to_string(); + let severity_text = level.to_string(); let body = Some(AnyValue { - value: Some(Value::StringValue(evt.msg().to_string())), + value: Some(Value::StringValue(evt.message().to_string())), }); let mut attributes = Vec::new(); let mut trace_id = Vec::new(); let mut span_id = Vec::new(); - if let (Some(trace), Some(span)) = (evt.id().trace(), evt.id().span()) { + if let (Some(trace), Some(span)) = (evt.props().trace_id(), evt.props().span_id()) { trace_id = trace.to_hex().to_vec(); span_id = span.to_hex().to_vec(); } diff --git a/targets/term/src/lib.rs b/targets/term/src/lib.rs index 96bf13b..0b19e0b 100644 --- a/targets/term/src/lib.rs +++ b/targets/term/src/lib.rs @@ -1,6 +1,7 @@ use core::{fmt, time::Duration}; use std::{cell::RefCell, io::Write}; +use emit::well_known::WellKnown; use termcolor::{Buffer, BufferWriter, Color, ColorChoice, ColorSpec, WriteColor}; pub fn stdout() -> Stdout { @@ -14,7 +15,7 @@ thread_local! { } impl emit::target::Target for Stdout { - fn emit_event(&self, evt: &emit::Event

) { + fn event(&self, evt: &emit::Event

) { STDOUT.with(|buf| { match buf.try_borrow_mut() { // If there are no overlapping references then use the cached buffer @@ -50,13 +51,29 @@ impl emit::target::Target for Stdout { } fn print(out: &BufferWriter, buf: &mut Buffer, evt: &emit::Event) { - if let Some(ts) = evt.ts() { - let _ = write!(buf, "[{:.0} ", ts); + let mut header_empty = true; + + if let Some(ts) = evt.timestamp() { + let _ = write!(buf, "[{:.0}", ts); + + header_empty = false; } - let _ = write!(buf, "{}]: ", evt.lvl()); + if let Some(level) = evt.props().level() { + if !header_empty { + let _ = write!(buf, " {}", level); + } else { + let _ = write!(buf, "[{}", level); + } + + header_empty = false; + } + + if !header_empty { + let _ = write!(buf, "]: "); + } - if let Ok(_) = evt.msg().write(Writer { buf }) { + if let Ok(_) = evt.message().write(Writer { buf }) { let _ = buf.write(b"\n"); let _ = out.print(&buf); } diff --git a/tests/smoke-test/main.rs b/tests/smoke-test/main.rs index 7cb80eb..2eeb56f 100644 --- a/tests/smoke-test/main.rs +++ b/tests/smoke-test/main.rs @@ -30,7 +30,7 @@ async fn main() { emitter.blocking_flush(Duration::from_secs(5)); } -#[emit::span("Hello!", a, ax: 13)] +#[emit::with(a, ax: 13)] async fn in_ctxt(a: i32) { in_ctxt2(5).await; @@ -42,7 +42,7 @@ async fn in_ctxt(a: i32) { emit::info!("working on {#[emit::as_serde] work}"); } -#[emit::span("Hello!", b, bx: 90)] +#[emit::with(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}!",