Skip to content

Commit

Permalink
support completing spans as results with ok_lvl and err_lvl
Browse files Browse the repository at this point in the history
  • Loading branch information
KodrAus committed Jun 12, 2024
1 parent 4aa2a1b commit 841c528
Show file tree
Hide file tree
Showing 9 changed files with 313 additions and 9 deletions.
3 changes: 0 additions & 3 deletions emitter/term/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,8 +174,6 @@ fn print_event(
write_plain(buf, " ");
}

write_fg(buf, format_args!("{} ", evt.module().root()), MODULE);

let mut lvl = None;
if let Some(level) = evt.props().pull::<emit::Level, _>(KEY_LVL) {
lvl = level_color(&level).map(Color::Ansi256);
Expand Down Expand Up @@ -429,7 +427,6 @@ impl<'a> emit::template::Write for Writer<'a> {
}
}

const MODULE: Color = Color::Ansi256(244);
const KIND: Color = Color::Ansi256(174);

const TEXT: Color = Color::Ansi256(69);
Expand Down
4 changes: 4 additions & 0 deletions examples/common_patterns/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ path = "filter_by_level.rs"
name = "filter_per_emitter"
path = "filter_per_emitter.rs"

[[example]]
name = "span_auto_result_completion"
path = "span_auto_result_completion.rs"

[[example]]
name = "span_manual_completion"
path = "span_manual_completion.rs"
Expand Down
37 changes: 37 additions & 0 deletions examples/common_patterns/span_auto_result_completion.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
use std::time::Duration;

#[derive(thiserror::Error, Debug)]
#[error("invalid number {n}")]
struct Error {
n: i32,
}

// The `guard` control parameter binds an `emit::Span` that you can use
// to manually complete your span, adding extra properties if needed.
//
// If you don't complete the span manually then it will complete on its
// own when it falls out of scope.
#[emit::span(
ok_lvl: emit::Level::Info,
err_lvl: emit::Level::Error,
"Running an example",
i,
)]
fn example(i: i32) -> Result<(), Error> {
let r = i + 1;

if r == 4 {
Err(Error { n: r })
} else {
Ok(())
}
}

fn main() {
let rt = emit::setup().emit_to(emit_term::stdout()).init();

let _ = example(1);
let _ = example(3);

rt.blocking_flush(Duration::from_secs(5));
}
30 changes: 28 additions & 2 deletions macros/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,13 @@ impl Arg<TokenStream> {
Arg::new(key, to_tokens)
}

pub fn take_when(self) -> TokenStream {
/**
Returns `Some(#tokens)` if the arg was present, or `None::<emit::Empty>` if it wasn't.
*/
pub fn take_some_or_empty(self) -> TokenStream {
self.take()
.map(|tokens| quote!(Some(#tokens)))
.unwrap_or_else(|| quote!(None::<emit::empty::Empty>))
.unwrap_or_else(|| quote!(None::<emit::Empty>))
}

pub fn take_rt(self) -> Result<TokenStream, syn::Error> {
Expand All @@ -100,6 +103,29 @@ impl Arg<TokenStream> {
self.take().ok_or_else(|| syn::Error::new(Span::call_site(), "a runtime must be specified by the `rt` parameter unless the `implicit_rt` feature of `emit` is enabled"))
}
}

pub fn take_if_std(self) -> Result<Option<TokenStream>, syn::Error> {
#[cfg(feature = "std")]
{
Ok(self.take())
}
#[cfg(not(feature = "std"))]
{
use syn::spanned::Spanned;

if let Some(value) = self.take() {
Err(syn::Error::new(
value.span(),
format!(
"capturing `{}` is only possible when the `std` Cargo feature is enabled",
self.key
),
))
} else {
Ok(None)
}
}
}
}

impl<T> Arg<T> {
Expand Down
2 changes: 1 addition & 1 deletion macros/src/emit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ impl Parse for Args {
props: props.take().unwrap_or_else(|| quote!(emit::empty::Empty)),
event: event.take(),
rt: rt.take_rt()?,
when: when.take_when(),
when: when.take_some_or_empty(),
})
}
}
Expand Down
2 changes: 2 additions & 0 deletions macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,8 @@ This macro accepts the following optional control parameters:
- `module: impl Into<emit::Path>`: The module the event belongs to. If unspecified the current module path is used.
- `when: impl emit::Filter`: A filter to use instead of the one configured on the runtime.
- `arg`: An identifier to bind an `emit::Span` to in the body of the span for manual completion.
- `ok_lvl`: Assume the instrumented block returns a `Result`. Assign the event the given level when the result is `Ok`.
- `err_lvl`. Assume the instrumented block returns a `Result`. Assign the event the given level when the result is `Err` and attach the error as the `err` property.
# Template
Expand Down
132 changes: 129 additions & 3 deletions macros/src/span.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ struct Args {
module: TokenStream,
when: TokenStream,
guard: Option<Ident>,
ok_lvl: Option<TokenStream>,
err_lvl: Option<TokenStream>,
}

impl Parse for Args {
Expand All @@ -42,17 +44,36 @@ impl Parse for Args {

Ok(quote_spanned!(expr.span()=> #expr))
});
let mut ok_lvl = Arg::token_stream("ok_lvl", |fv| {
let expr = &fv.expr;

Ok(quote_spanned!(expr.span()=> #expr))
});
let mut err_lvl = Arg::token_stream("err_lvl", |fv| {
let expr = &fv.expr;

Ok(quote_spanned!(expr.span()=> #expr))
});
let mut guard = Arg::ident("guard");

args::set_from_field_values(
input.parse_terminated(FieldValue::parse, Token![,])?.iter(),
[&mut module, &mut guard, &mut rt, &mut when],
[
&mut module,
&mut guard,
&mut rt,
&mut when,
&mut ok_lvl,
&mut err_lvl,
],
)?;

Ok(Args {
rt: rt.take_rt()?,
module: module.take().unwrap_or_else(|| module_tokens()),
when: when.take_when(),
when: when.take_some_or_empty(),
ok_lvl: ok_lvl.take_if_std()?,
err_lvl: err_lvl.take_if_std()?,
guard: guard.take(),
})
}
Expand All @@ -75,6 +96,9 @@ pub fn expand_tokens(opts: ExpandTokens) -> Result<TokenStream, syn::Error> {

let module_tokens = args.module;

let ok_lvl = args.ok_lvl;
let err_lvl = args.err_lvl;

let mut item = syn::parse2::<Stmt>(opts.item)?;
match &mut item {
// A synchronous function
Expand All @@ -94,6 +118,8 @@ pub fn expand_tokens(opts: ExpandTokens) -> Result<TokenStream, syn::Error> {
&evt_props,
&span_guard,
quote!(#block),
ok_lvl,
err_lvl,
))?;
}
// A synchronous block
Expand All @@ -107,6 +133,8 @@ pub fn expand_tokens(opts: ExpandTokens) -> Result<TokenStream, syn::Error> {
&evt_props,
&span_guard,
quote!(#block),
ok_lvl,
err_lvl,
))?;
}
// An asynchronous function
Expand All @@ -126,6 +154,8 @@ pub fn expand_tokens(opts: ExpandTokens) -> Result<TokenStream, syn::Error> {
&evt_props,
&span_guard,
quote!(#block),
ok_lvl,
err_lvl,
))?;
}
// An asynchronous block
Expand All @@ -139,6 +169,8 @@ pub fn expand_tokens(opts: ExpandTokens) -> Result<TokenStream, syn::Error> {
&evt_props,
&span_guard,
quote!(#block),
ok_lvl,
err_lvl,
))?;
}
_ => return Err(syn::Error::new(item.span(), "unrecognized item type")),
Expand All @@ -156,12 +188,24 @@ fn inject_sync(
evt_props: &Props,
span_guard: &Ident,
body: TokenStream,
ok_lvl: Option<TokenStream>,
err_lvl: Option<TokenStream>,
) -> TokenStream {
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();

let body = completion(
quote!((move || #body)()),
ok_lvl,
err_lvl,
span_guard,
rt_tokens,
&template_tokens,
&evt_props_tokens,
);

quote!({
let (mut __ctxt, __span_guard) = emit::__private::__private_begin_span(
#rt_tokens,
Expand Down Expand Up @@ -197,12 +241,24 @@ fn inject_async(
evt_props: &Props,
span_guard: &Ident,
body: TokenStream,
ok_lvl: Option<TokenStream>,
err_lvl: Option<TokenStream>,
) -> TokenStream {
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();

let body = completion(
quote!(async #body.await),
ok_lvl,
err_lvl,
span_guard,
rt_tokens,
&template_tokens,
&evt_props_tokens,
);

quote!({
let (__ctxt, __span_guard) = emit::__private::__private_begin_span(
#rt_tokens,
Expand All @@ -225,7 +281,77 @@ fn inject_async(
__ctxt.in_future(async move {
let #span_guard = __span_guard;

async #body.await
#body
}).await
})
}

fn completion(
body: TokenStream,
ok_lvl: Option<TokenStream>,
err_lvl: Option<TokenStream>,
span_guard: &Ident,
rt_tokens: &TokenStream,
template_tokens: &TokenStream,
evt_props_tokens: &TokenStream,
) -> TokenStream {
if ok_lvl.is_some() || err_lvl.is_some() {
let ok_branch = ok_lvl
.map(|lvl| {
quote!(
Ok(ok) => {
#span_guard.complete_with(|span| {
emit::__private::__private_complete_span_ok(
#rt_tokens,
span,
#template_tokens,
#evt_props_tokens,
&#lvl,
)
});

Ok(ok)
}
)
})
.unwrap_or_else(|| {
quote!(
Ok(ok) => Ok(ok)
)
});

let err_branch = err_lvl
.map(|lvl| {
quote!(
Err(err) => {
#span_guard.complete_with(|span| {
emit::__private::__private_complete_span_err(
#rt_tokens,
span,
#template_tokens,
#evt_props_tokens,
&#lvl,
&err,
)
});

Err(err)
}
)
})
.unwrap_or_else(|| {
quote!(
Err(err) => Err(err)
)
});

quote!(
match #body {
#ok_branch,
#err_branch,
}
)
} else {
body
}
}
Loading

0 comments on commit 841c528

Please sign in to comment.