Skip to content

Commit ba1b99e

Browse files
committed
runtime attributes
Signed-off-by: Yoshua Wuyts <yoshuawuyts@gmail.com>
1 parent f5f37cf commit ba1b99e

File tree

8 files changed

+225
-26
lines changed

8 files changed

+225
-26
lines changed

Cargo.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,13 @@ edition = "2018"
1414
[dependencies]
1515
log = "0.4.6"
1616
backtrace = "0.3.30"
17+
async-log-attributes = { path = "async-log-attributes" }
1718

1819
[dev-dependencies]
1920
env_logger = "0.6.1"
21+
22+
[workspace]
23+
members = [
24+
".",
25+
"async-log-attributes",
26+
]

README.md

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
[![crates.io version][1]][2] [![build status][3]][4]
33
[![downloads][5]][6] [![docs.rs docs][7]][8]
44

5-
Async tracing capabilities for the log crate.
5+
Async tracing capabilities for the standard [`log`] crate.
6+
7+
[`log`]: https://docs.rs/log
68

79
- [Documentation][8]
810
- [Crates.io][2]
@@ -11,7 +13,32 @@ Async tracing capabilities for the log crate.
1113
## Examples
1214
__Basic usage__
1315
```rust
14-
// tbi
16+
use async_log::span;
17+
use log::info;
18+
19+
fn setup_logger() {
20+
let logger = env_logger::Builder::new()
21+
.filter(None, log::LevelFilter::Trace)
22+
.build();
23+
24+
async_log::Logger::wrap(logger, || 12)
25+
.start(log::LevelFilter::Trace)
26+
.unwrap();
27+
}
28+
29+
fn main() {
30+
setup_logger();
31+
32+
span!("level I", {
33+
let x = "beep";
34+
info!("look at this value, x={}", x);
35+
36+
span!("level II", {
37+
let y = "boop";
38+
info!("another nice value, y={}", y);
39+
})
40+
})
41+
}
1542
```
1643

1744
## Installation

async-log-attributes/Cargo.toml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
[package]
2+
name = "async-log-attributes"
3+
description = "Proc Macro attributes for the async-log crate."
4+
version = "0.3.0-alpha.4"
5+
license = "MIT OR Apache-2.0"
6+
readme = "README.md"
7+
repository = "https://github.com/rustasync/runtime"
8+
homepage = "https://github.com/rustasync/runtime"
9+
documentation = "https://docs.rs/runtime-attributes"
10+
authors = ["The Rust Async Ecosystem Working Group"]
11+
keywords = ["async", "log", "trace", "span", "macro"]
12+
categories = ["asynchronous", "command-line-utilities", "development-tools", "text-processing", "web-programming"]
13+
edition = "2018"
14+
15+
[lib]
16+
proc-macro = true
17+
18+
[dependencies]
19+
syn = { version = "0.15.33", features = ["full"] }
20+
proc-macro2 = { version = "0.4.29", features = ["nightly"] }
21+
quote = "0.6.12"

async-log-attributes/README.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# runtime-attributes
2+
Proc Macro attributes for the [Runtime](https://github.com/rustasync/runtime) crate. See the
3+
[Runtime](https://docs.rs/runtime) documentation for more details.
4+
5+
__This macro was designed to be used from the Runtime crate. Using this in any other way is unlikely
6+
to work.__
7+
8+
## Installation
9+
With [cargo-edit](https://crates.io/crates/cargo-edit) do:
10+
```sh
11+
$ cargo add runtime-attributes
12+
```
13+
14+
## Safety
15+
This crate uses ``#![deny(unsafe_code)]`` to ensure everything is implemented in 100% Safe Rust.
16+
17+
## Contributing
18+
Want to join us? Check out our [The "Contributing" section of the
19+
guide][contributing] and take a look at some of these issues:
20+
21+
- [Issues labeled "good first issue"][good-first-issue]
22+
- [Issues labeled "help wanted"][help-wanted]
23+
24+
#### Conduct
25+
26+
The Runtime project adheres to the [Contributor Covenant Code of
27+
Conduct](https://github.com/rustasync/runtime/blob/master/.github/CODE_OF_CONDUCT.md). This
28+
describes the minimum behavior expected from all contributors.
29+
30+
## License
31+
Licensed under either of
32+
33+
* Apache License, Version 2.0 ([LICENSE-APACHE](../LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
34+
* MIT license ([LICENSE-MIT](../LICENSE-MIT) or http://opensource.org/licenses/MIT)
35+
36+
at your option.
37+
38+
#### Contribution
39+
40+
Unless you explicitly state otherwise, any contribution intentionally submitted
41+
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
42+
dual licensed as above, without any additional terms or conditions.
43+
44+
[releases]: https://github.com/rustasync/runtime/releases
45+
[contributing]: https://github.com/rustasync/runtime/blob/master/.github/CONTRIBUTING.md
46+
[good-first-issue]: https://github.com/rustasync/runtime/labels/good%20first%20issue
47+
[help-wanted]: https://github.com/rustasync/runtime/labels/help%20wanted

async-log-attributes/src/lib.rs

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
//! Proc Macro attributes for the [Runtime](https://github.com/rustasync/runtime) crate. See the
2+
//! [Runtime](https://docs.rs/runtime) documentation for more details.
3+
4+
#![forbid(unsafe_code, future_incompatible, rust_2018_idioms)]
5+
#![deny(missing_debug_implementations, nonstandard_style)]
6+
#![feature(async_await)]
7+
#![recursion_limit = "512"]
8+
9+
extern crate proc_macro;
10+
11+
use proc_macro::TokenStream;
12+
use quote::quote;
13+
14+
/// Defines the async main function.
15+
///
16+
/// # Examples
17+
///
18+
/// ```ignore
19+
/// #[span]
20+
/// async fn main() -> std::io::Result<()> {
21+
/// Ok(())
22+
/// }
23+
/// ```
24+
#[cfg(not(test))] // NOTE: exporting main breaks tests, we should file an issue.
25+
#[proc_macro_attribute]
26+
pub fn span_wrap(_attr: TokenStream, item: TokenStream) -> TokenStream {
27+
let input = syn::parse_macro_input!(item as syn::ItemFn);
28+
29+
let attrs = &input.attrs;
30+
let vis = &input.vis;
31+
let constness = &input.constness;
32+
let unsafety = &input.unsafety;
33+
let asyncness = &input.asyncness;
34+
let abi = &input.abi;
35+
36+
let generics = &input.decl.generics;
37+
let name = &input.ident;
38+
let inputs = &input.decl.inputs;
39+
let output = &input.decl.output;
40+
let body = &input.block.stmts;
41+
42+
let args: Vec<syn::Pat> = inputs
43+
.pairs()
44+
.filter_map(|pair| match pair.into_value() {
45+
syn::FnArg::Captured(arg) => Some(arg.pat.clone()),
46+
_ => return None,
47+
})
48+
.collect();
49+
50+
let names: String = args
51+
.iter()
52+
.enumerate()
53+
.map(|(i, _arg)| match i {
54+
0 => {
55+
let mut string = format!("arg_{}", 0);
56+
string.push_str("={}");
57+
string
58+
}
59+
n => {
60+
let mut string = format!(", arg_{}", n);
61+
string.push_str("={}");
62+
string
63+
}
64+
})
65+
.collect();
66+
67+
let result = quote! {
68+
#(#attrs)*
69+
#vis #constness #unsafety #asyncness #abi fn #generics #name(#(#inputs)*) #output {
70+
let __name = format!("{}#{}", file!(), stringify!(#name));
71+
let __args = format!("{} {}", __name, format_args!(#names, #(#args)*));
72+
async_log::span!(__args, {
73+
#(#body)*
74+
})
75+
}
76+
};
77+
78+
result.into()
79+
}

examples/trace.rs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,25 @@ fn setup_logger() {
66
.filter(None, log::LevelFilter::Trace)
77
.build();
88

9-
async_log::Logger::wrap(logger, || 12)
9+
async_log::Logger::wrap(logger, || /* get the task id here */ 0)
1010
.start(log::LevelFilter::Trace)
1111
.unwrap();
1212
}
1313

1414
fn main() {
1515
setup_logger();
1616

17-
span!("main", {
18-
let x = "foo";
19-
info!("this {}", x);
17+
span!("new level, depth={}", 1, {
18+
let x = "beep";
19+
info!("look at this value, x={}", x);
2020

21-
span!("inner, x={}", x, {
22-
info!("we must go deeper {}", x);
21+
span!("new level, depth={}", 2, {
22+
inner("boop");
2323
})
2424
})
2525
}
26+
27+
#[async_log::span_wrap]
28+
fn inner(y: &str) {
29+
info!("another nice value, y={}", y);
30+
}

src/lib.rs

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//! Async tracing capabilities for [`log`].
1+
//! Async tracing capabilities for the standard [`log`] crate.
22
//!
33
//! [`log`]: https://docs.rs/log
44
//!
@@ -51,10 +51,15 @@
5151
//!
5252
//! __example__
5353
//! ```txt
54-
//! runtime::fs::read_to_string, span_mark=start, path=/tmp/foob, task_id=7, parent_id=5, thread_id=8
55-
//! runtime::fs::read_to_string, span_mark=end, path=/tmp/foob, task_id=7, parent_id=5, thread_id=8
54+
//! runtime::fs::read_to_string, span_mark=start, path=/tmp/foob, task_id=7, thread_id=8
55+
//! runtime::fs::read_to_string, span_mark=end, path=/tmp/foob, task_id=7, thread_id=8
5656
//! ```
5757
//!
58+
//! ## Why build on the log crate?
59+
//! [`log`](https://docs.rs/log/) is Rust's standard log crate. It's incredibly flexible, and was
60+
//! built with extensibility in mind. Because it's so widely used, being able to extend it allows
61+
//! us to add tracing data to crates without needing to make any changes to their `log!` calls.
62+
//!
5863
//! ## Formatting
5964
//! Structured logging (key-value logging) is [currently in the
6065
//! process](https://github.com/rust-lang-nursery/log/issues/328) of being added to `log`.
@@ -81,21 +86,22 @@
8186
//! .filter(None, log::LevelFilter::Trace)
8287
//! .build();
8388
//!
84-
//! async_log::Logger::wrap(logger, || (12, Some(13)))
89+
//! async_log::Logger::wrap(logger, || 12)
8590
//! .start(log::LevelFilter::Trace)
8691
//! .unwrap();
8792
//! }
8893
//!
8994
//! fn main() {
9095
//! setup_logger();
9196
//!
92-
//! span!("main", {
93-
//! let x = "foo";
94-
//! info!("this {}", x);
97+
//! span!("new level, depth={}", 1, {
98+
//! let x = "beep";
99+
//! info!("look at this value, x={}", x);
95100
//!
96-
//! span!("inner, x={}", x, {
97-
//! info!("we must go deeper {}", x);
98-
//! });
101+
//! span!("new level, depth={}", 2, {
102+
//! let y = "boop";
103+
//! info!("another nice value, y={}", y);
104+
//! })
99105
//! })
100106
//! }
101107
//! ```
@@ -105,6 +111,8 @@
105111
#![warn(missing_docs, missing_doc_code_examples, unreachable_pub)]
106112
#![cfg_attr(test, deny(warnings))]
107113

114+
pub use async_log_attributes::span_wrap;
115+
108116
mod backtrace;
109117
mod logger;
110118
mod macros;

src/logger.rs

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::backtrace::async_log_capture_caller;
2-
use log::{LevelFilter, Log, Metadata, Record};
2+
use log::{LevelFilter, Log, Metadata, Record, set_boxed_logger};
33

44
use std::thread;
55

@@ -32,7 +32,7 @@ where
3232

3333
/// Start logging.
3434
pub fn start(self, filter: LevelFilter) -> Result<(), log::SetLoggerError> {
35-
let res = log::set_boxed_logger(Box::new(self));
35+
let res = set_boxed_logger(Box::new(self));
3636
if res.is_ok() {
3737
log::set_max_level(filter);
3838
}
@@ -77,7 +77,7 @@ where
7777
let thread_id = format!(", thread_id={}", thread_id());
7878
let task_id = format!(", task_id={}", curr_id);
7979

80-
let (line, filename) = if self.backtrace {
80+
let (line, filename, fn_name) = if self.backtrace {
8181
match symbol {
8282
Some(symbol) => {
8383
let line = symbol
@@ -90,24 +90,29 @@ where
9090
.map(|f| format!(", filename={}", f.to_string_lossy()))
9191
.unwrap_or_else(|| String::from(""));
9292

93-
(line, filename)
93+
let fn_name = symbol
94+
.name
95+
.map(|l| format!(", fn_name={}", l))
96+
.unwrap_or_else(|| String::from(""));
97+
98+
(line, filename, fn_name)
9499
}
95-
None => (String::from(""), String::from("")),
100+
None => (String::from(""), String::from(""), String::from("")),
96101
}
97102
} else {
98-
(String::from(""), String::from(""))
103+
(String::from(""), String::from(""), String::from(""))
99104
};
100105

101106
// This is done this way b/c `Record` + `format_args` needs to be built inline. See:
102107
// https://stackoverflow.com/q/56304313/1541707
103108
self.logger.log(
104109
&log::Record::builder()
105-
.args(record.args().clone())
106110
.args(format_args!(
107-
"{}{}{}{}{}",
111+
"{}{}{}{}{}{}",
108112
record.args(),
109113
filename,
110114
line,
115+
fn_name,
111116
task_id,
112117
thread_id
113118
))

0 commit comments

Comments
 (0)