Skip to content

Commit a8e5950

Browse files
committed
Add a counted procedural macro to simplify handling count metrics
1 parent cd59346 commit a8e5950

File tree

3 files changed

+68
-1
lines changed

3 files changed

+68
-1
lines changed

Cargo.toml

+2
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,5 @@ tracing = { version = "0.1", default-features = false }
4848
tracing-core = { version = "0.1", default-features = false }
4949
tracing-subscriber = { version = "0.3", default-features = false }
5050
url = { version = "2.5", default-features = false }
51+
syn = { version = "2.0.72", fedefault-features = false, features = ["full"] }
52+

opentelemetry/Cargo.toml

+8-1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ futures-sink = "0.3"
2626
once_cell = { workspace = true }
2727
pin-project-lite = { workspace = true, optional = true }
2828
thiserror = { workspace = true }
29+
syn = { workspace = true }
30+
2931

3032
[target.'cfg(all(target_arch = "wasm32", not(target_os = "wasi")))'.dependencies]
3133
js-sys = "0.3.63"
@@ -40,10 +42,15 @@ logs_level_enabled = ["logs"]
4042
otel_unstable = []
4143

4244
[dev-dependencies]
43-
opentelemetry_sdk = { path = "../opentelemetry-sdk", features = ["logs_level_enabled"]} # for documentation tests
45+
opentelemetry_sdk = { path = "../opentelemetry-sdk", features = ["logs_level_enabled"] } # for documentation tests
4446
criterion = { workspace = true }
4547
rand = { workspace = true }
4648

49+
[lib]
50+
name = "macros"
51+
proc-macro = true
52+
path = "src/macros.rs"
53+
4754
[[bench]]
4855
name = "metrics"
4956
harness = false

opentelemetry/src/macros.rs

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
use proc_macro::TokenStream;
2+
use syn::{Block, ItemFn, LitStr, parse_macro_input};
3+
use syn::__private::ToTokens;
4+
5+
// Default Values
6+
const DEFAULT_METER_PROVIDER_NAME: &'static str = "default_meter_provider";
7+
const DEFAULT_DESCRIPTION: &'static str = "Empty description!";
8+
9+
// Attributes
10+
const METER_PROVIDER_NAME_ATTR_NAME: &'static str = "meter_provider";
11+
const NAME_ATTR_NAME: &'static str = "name";
12+
const DESCRIPTION_ATTR_NAME: &'static str = "description";
13+
14+
// Messages
15+
const ATTR_ERROR_MESSAGE: &'static str = "unsupported attribute for counted macro!";
16+
17+
18+
#[proc_macro_attribute]
19+
pub fn counted(attr: TokenStream, item: TokenStream) -> TokenStream {
20+
let mut input = parse_macro_input!(item as ItemFn);
21+
22+
let mut name = format!("fn_{}_count", input.sig.ident.to_string());
23+
let mut meter_provider = DEFAULT_METER_PROVIDER_NAME.to_string();
24+
let mut description = DEFAULT_DESCRIPTION.to_string();
25+
let parser = syn::meta::parser(|meta| {
26+
if meta.path.is_ident(NAME_ATTR_NAME) {
27+
let value = meta.value()?.parse::<LitStr>()?;
28+
name = value.value();
29+
return Ok(());
30+
} else if meta.path.is_ident(METER_PROVIDER_NAME_ATTR_NAME) {
31+
let value = meta.value()?.parse::<LitStr>()?;
32+
meter_provider = value.value();
33+
return Ok(());
34+
} else if meta.path.is_ident(DESCRIPTION_ATTR_NAME) {
35+
let value = meta.value()?.parse::<LitStr>()?;
36+
description = value.value();
37+
return Ok(());
38+
}
39+
40+
Err(meta.error(ATTR_ERROR_MESSAGE))
41+
});
42+
43+
parse_macro_input!(attr with parser);
44+
45+
let mut counted_code_block = syn::parse_str::<Block>(&format!(r#"
46+
{{
47+
static COUNTER: std::sync::LazyLock<opentelemetry::metrics::Counter<u64>> = std::sync::LazyLock::new(|| {{
48+
let meter = opentelemetry::global::meter("{}");
49+
meter.u64_counter("{}").with_description("{}").init()
50+
}});
51+
COUNTER.add(1, &[]);
52+
}}
53+
"#, meter_provider, name, description)).unwrap();
54+
55+
counted_code_block.stmts.extend_from_slice(&*input.block.stmts);
56+
input.block.stmts = counted_code_block.stmts;
57+
TokenStream::from(input.into_token_stream())
58+
}

0 commit comments

Comments
 (0)