remove scoping and switch to pure statics for fast path
This commit is contained in:
parent
cec241dec9
commit
cb54c40d13
|
@ -25,3 +25,6 @@ proc-macro2 = "1.0"
|
|||
proc-macro-hack = "0.5"
|
||||
lazy_static = "1.4"
|
||||
regex = "1.3"
|
||||
|
||||
[dev-dependencies]
|
||||
syn = { version = "1.0", features = ["full"] }
|
|
@ -3,7 +3,6 @@ extern crate proc_macro;
|
|||
use self::proc_macro::TokenStream;
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
use proc_macro2::Span;
|
||||
use proc_macro_hack::proc_macro_hack;
|
||||
use quote::{format_ident, quote, ToTokens};
|
||||
use regex::Regex;
|
||||
|
@ -14,38 +13,24 @@ use syn::{parse_macro_input, Expr, LitStr, Token};
|
|||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
enum Key {
|
||||
NotScoped(LitStr),
|
||||
Scoped(LitStr),
|
||||
}
|
||||
|
||||
impl Key {
|
||||
pub fn span(&self) -> Span {
|
||||
match self {
|
||||
Key::Scoped(s) => s.span(),
|
||||
Key::NotScoped(s) => s.span(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum Labels {
|
||||
Existing(Expr),
|
||||
Inline(Vec<(LitStr, Expr)>),
|
||||
}
|
||||
|
||||
struct WithoutExpression {
|
||||
key: Key,
|
||||
key: LitStr,
|
||||
labels: Option<Labels>,
|
||||
}
|
||||
|
||||
struct WithExpression {
|
||||
key: Key,
|
||||
key: LitStr,
|
||||
op_value: Expr,
|
||||
labels: Option<Labels>,
|
||||
}
|
||||
|
||||
struct Registration {
|
||||
key: Key,
|
||||
key: LitStr,
|
||||
unit: Option<Expr>,
|
||||
description: Option<LitStr>,
|
||||
labels: Option<Labels>,
|
||||
|
@ -239,7 +224,7 @@ pub fn histogram(input: TokenStream) -> TokenStream {
|
|||
|
||||
fn get_expanded_registration(
|
||||
metric_type: &str,
|
||||
key: Key,
|
||||
key: LitStr,
|
||||
unit: Option<Expr>,
|
||||
description: Option<LitStr>,
|
||||
labels: Option<Labels>,
|
||||
|
@ -272,45 +257,64 @@ fn get_expanded_registration(
|
|||
fn get_expanded_callsite<V>(
|
||||
metric_type: &str,
|
||||
op_type: &str,
|
||||
key: Key,
|
||||
key: LitStr,
|
||||
labels: Option<Labels>,
|
||||
op_values: V,
|
||||
) -> proc_macro2::TokenStream
|
||||
where
|
||||
V: ToTokens,
|
||||
{
|
||||
let use_fast_path = can_use_fast_path(&labels);
|
||||
let key = key_to_quoted(key, labels);
|
||||
|
||||
// We use a helper method for histogram values to coerce into u64, but otherwise,
|
||||
// just pass through whatever the caller gave us.
|
||||
let op_values = if metric_type == "histogram" {
|
||||
quote! {
|
||||
metrics::__into_u64(#op_values)
|
||||
}
|
||||
quote! { metrics::__into_u64(#op_values) }
|
||||
} else {
|
||||
quote! { #op_values }
|
||||
};
|
||||
|
||||
let op_ident = format_ident!("{}_{}", op_type, metric_type);
|
||||
|
||||
let use_fast_path = can_use_fast_path(&labels);
|
||||
if use_fast_path {
|
||||
// We're on the fast path here, so we'll build our key, statically cache it,
|
||||
// and use a borrowed reference to it for this and future operations.
|
||||
let statics = match labels {
|
||||
Some(Labels::Inline(pairs)) => {
|
||||
let labels = pairs
|
||||
.into_iter()
|
||||
.map(|(key, val)| quote! { metrics::Label::from_static_parts(#key, #val) })
|
||||
.collect::<Vec<_>>();
|
||||
let labels_len = labels.len();
|
||||
let labels_len = quote! { #labels_len };
|
||||
|
||||
quote! {
|
||||
static METRIC_LABELS: [metrics::Label; #labels_len] = [#(#labels),*];
|
||||
static METRIC_KEY: metrics::KeyData =
|
||||
metrics::KeyData::from_static_parts(#key, &METRIC_LABELS);
|
||||
}
|
||||
}
|
||||
None => {
|
||||
quote! {
|
||||
static METRIC_KEY: metrics::KeyData =
|
||||
metrics::KeyData::from_static_name(#key);
|
||||
}
|
||||
}
|
||||
_ => unreachable!("use_fast_path == true, but found expression-based labels"),
|
||||
};
|
||||
|
||||
quote! {
|
||||
{
|
||||
static CACHED_KEY: metrics::OnceKeyData = metrics::OnceKeyData::new();
|
||||
#statics
|
||||
|
||||
// Only do this work if there's a recorder installed.
|
||||
if let Some(recorder) = metrics::try_recorder() {
|
||||
// Initialize our fast path.
|
||||
let key = CACHED_KEY.get_or_init(|| { #key });
|
||||
recorder.#op_ident(metrics::Key::Borrowed(&key), #op_values);
|
||||
recorder.#op_ident(metrics::Key::Borrowed(&METRIC_KEY), #op_values);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// We're on the slow path, so basically we register every single time.
|
||||
//
|
||||
// Recorders are expected to deduplicate any duplicate registrations.
|
||||
// We're on the slow path, so we allocate, womp.
|
||||
let key = key_to_quoted(key, labels);
|
||||
quote! {
|
||||
{
|
||||
// Only do this work if there's a recorder installed.
|
||||
|
@ -332,20 +336,9 @@ fn can_use_fast_path(labels: &Option<Labels>) -> bool {
|
|||
}
|
||||
}
|
||||
|
||||
fn read_key(input: &mut ParseStream) -> Result<Key> {
|
||||
let key = if let Ok(_) = input.parse::<Token![<]>() {
|
||||
let s = input.parse::<LitStr>()?;
|
||||
input.parse::<Token![>]>()?;
|
||||
Key::Scoped(s)
|
||||
} else {
|
||||
let s = input.parse::<LitStr>()?;
|
||||
Key::NotScoped(s)
|
||||
};
|
||||
|
||||
let inner = match key {
|
||||
Key::Scoped(ref s) => s.value(),
|
||||
Key::NotScoped(ref s) => s.value(),
|
||||
};
|
||||
fn read_key(input: &mut ParseStream) -> Result<LitStr> {
|
||||
let key = input.parse::<LitStr>()?;
|
||||
let inner = key.value();
|
||||
|
||||
lazy_static! {
|
||||
static ref RE: Regex = Regex::new("^[a-zA-Z][a-zA-Z0-9_:\\.]*$").unwrap();
|
||||
|
@ -360,22 +353,7 @@ fn read_key(input: &mut ParseStream) -> Result<Key> {
|
|||
Ok(key)
|
||||
}
|
||||
|
||||
fn quote_key_name(key: Key) -> proc_macro2::TokenStream {
|
||||
match key {
|
||||
Key::NotScoped(s) => {
|
||||
quote! { #s }
|
||||
}
|
||||
Key::Scoped(s) => {
|
||||
quote! {
|
||||
format!("{}.{}", std::module_path!().replace("::", "."), #s)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn key_to_quoted(key: Key, labels: Option<Labels>) -> proc_macro2::TokenStream {
|
||||
let name = quote_key_name(key);
|
||||
|
||||
fn key_to_quoted(name: LitStr, labels: Option<Labels>) -> proc_macro2::TokenStream {
|
||||
match labels {
|
||||
None => quote! { metrics::KeyData::from_name(#name) },
|
||||
Some(labels) => match labels {
|
||||
|
@ -383,9 +361,9 @@ fn key_to_quoted(key: Key, labels: Option<Labels>) -> proc_macro2::TokenStream {
|
|||
let labels = pairs
|
||||
.into_iter()
|
||||
.map(|(key, val)| quote! { metrics::Label::new(#key, #val) });
|
||||
quote! { metrics::KeyData::from_name_and_labels(#name, vec![#(#labels),*]) }
|
||||
quote! { metrics::KeyData::from_parts(#name, vec![#(#labels),*]) }
|
||||
}
|
||||
Labels::Existing(e) => quote! { metrics::KeyData::from_name_and_labels(#name, #e) },
|
||||
Labels::Existing(e) => quote! { metrics::KeyData::from_parts(#name, #e) },
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,31 +3,11 @@ use syn::{Expr, ExprPath};
|
|||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_quote_key_name_scoped() {
|
||||
let stream = quote_key_name(Key::Scoped(parse_quote! { "qwerty" }));
|
||||
let expected =
|
||||
"format ! (\"{}.{}\" , std :: module_path ! () . replace (\"::\" , \".\") , \"qwerty\")";
|
||||
assert_eq!(stream.to_string(), expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_quote_key_name_not_scoped() {
|
||||
let stream = quote_key_name(Key::NotScoped(parse_quote! { "qwerty" }));
|
||||
let expected = "\"qwerty\"";
|
||||
assert_eq!(stream.to_string(), expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_expanded_registration() {
|
||||
// Basic registration.
|
||||
let stream = get_expanded_registration(
|
||||
"mytype",
|
||||
Key::NotScoped(parse_quote! { "mykeyname" }),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
let stream =
|
||||
get_expanded_registration("mytype", parse_quote! { "mykeyname" }, None, None, None);
|
||||
|
||||
let expected = concat!(
|
||||
"{ if let Some (recorder) = metrics :: try_recorder () { ",
|
||||
|
@ -48,7 +28,7 @@ fn test_get_expanded_registration_with_unit() {
|
|||
let units: ExprPath = parse_quote! { metrics::Unit::Nanoseconds };
|
||||
let stream = get_expanded_registration(
|
||||
"mytype",
|
||||
Key::NotScoped(parse_quote! { "mykeyname" }),
|
||||
parse_quote! { "mykeyname" },
|
||||
Some(Expr::Path(units)),
|
||||
None,
|
||||
None,
|
||||
|
@ -72,7 +52,7 @@ fn test_get_expanded_registration_with_description() {
|
|||
// And with description.
|
||||
let stream = get_expanded_registration(
|
||||
"mytype",
|
||||
Key::NotScoped(parse_quote! { "mykeyname" }),
|
||||
parse_quote! { "mykeyname" },
|
||||
None,
|
||||
Some(parse_quote! { "flerkin" }),
|
||||
None,
|
||||
|
@ -97,7 +77,7 @@ fn test_get_expanded_registration_with_unit_and_description() {
|
|||
let units: ExprPath = parse_quote! { metrics::Unit::Nanoseconds };
|
||||
let stream = get_expanded_registration(
|
||||
"mytype",
|
||||
Key::NotScoped(parse_quote! { "mykeyname" }),
|
||||
parse_quote! { "mykeyname" },
|
||||
Some(Expr::Path(units)),
|
||||
Some(parse_quote! { "flerkin" }),
|
||||
None,
|
||||
|
@ -116,38 +96,82 @@ fn test_get_expanded_registration_with_unit_and_description() {
|
|||
assert_eq!(stream.to_string(), expected);
|
||||
}
|
||||
|
||||
/// If there are no dynamic labels - generate an invocation with caching.
|
||||
#[test]
|
||||
fn test_get_expanded_callsite_fast_path() {
|
||||
fn test_get_expanded_callsite_fast_path_no_labels() {
|
||||
let stream = get_expanded_callsite(
|
||||
"mytype",
|
||||
"myop",
|
||||
Key::NotScoped(parse_quote! {"mykeyname"}),
|
||||
parse_quote! {"mykeyname"},
|
||||
None,
|
||||
quote! { 1 },
|
||||
);
|
||||
|
||||
let expected = concat!(
|
||||
"{ ",
|
||||
"static CACHED_KEY : metrics :: OnceKeyData = metrics :: OnceKeyData :: new () ; ",
|
||||
"static METRIC_KEY : metrics :: KeyData = metrics :: KeyData :: from_static_name (\"mykeyname\") ; ",
|
||||
"if let Some (recorder) = metrics :: try_recorder () { ",
|
||||
"let key = CACHED_KEY . get_or_init (|| { ",
|
||||
"metrics :: KeyData :: from_name (\"mykeyname\") ",
|
||||
"}) ; ",
|
||||
"recorder . myop_mytype (metrics :: Key :: Borrowed (& key) , 1) ; ",
|
||||
"recorder . myop_mytype (metrics :: Key :: Borrowed (& METRIC_KEY) , 1) ; ",
|
||||
"} }",
|
||||
);
|
||||
|
||||
assert_eq!(stream.to_string(), expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_expanded_callsite_fast_path_static_labels() {
|
||||
let labels = Labels::Inline(vec![(parse_quote! { "key1" }, parse_quote! { "value1" })]);
|
||||
let stream = get_expanded_callsite(
|
||||
"mytype",
|
||||
"myop",
|
||||
parse_quote! {"mykeyname"},
|
||||
Some(labels),
|
||||
quote! { 1 },
|
||||
);
|
||||
|
||||
let expected = concat!(
|
||||
"{ ",
|
||||
"static METRIC_LABELS : [metrics :: Label ; 1usize] = [metrics :: Label :: from_static_parts (\"key1\" , \"value1\")] ; ",
|
||||
"static METRIC_KEY : metrics :: KeyData = metrics :: KeyData :: from_static_parts (\"mykeyname\" , & METRIC_LABELS) ; ",
|
||||
"if let Some (recorder) = metrics :: try_recorder () { ",
|
||||
"recorder . myop_mytype (metrics :: Key :: Borrowed (& METRIC_KEY) , 1) ; ",
|
||||
"} ",
|
||||
"}",
|
||||
);
|
||||
|
||||
assert_eq!(stream.to_string(), expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_expanded_callsite_fast_path_dynamic_labels() {
|
||||
let labels = Labels::Inline(vec![(parse_quote! { "key1" }, parse_quote! { &value1 })]);
|
||||
let stream = get_expanded_callsite(
|
||||
"mytype",
|
||||
"myop",
|
||||
parse_quote! {"mykeyname"},
|
||||
Some(labels),
|
||||
quote! { 1 },
|
||||
);
|
||||
|
||||
let expected = concat!(
|
||||
"{ ",
|
||||
"if let Some (recorder) = metrics :: try_recorder () { ",
|
||||
"recorder . myop_mytype (metrics :: Key :: Owned (",
|
||||
"metrics :: KeyData :: from_parts (\"mykeyname\" , vec ! [metrics :: Label :: new (\"key1\" , & value1)])",
|
||||
") , 1) ; ",
|
||||
"} ",
|
||||
"}",
|
||||
);
|
||||
|
||||
assert_eq!(stream.to_string(), expected);
|
||||
}
|
||||
|
||||
/// If there are dynamic labels - generate a direct invocation.
|
||||
#[test]
|
||||
fn test_get_expanded_callsite_regular_path() {
|
||||
let stream = get_expanded_callsite(
|
||||
"mytype",
|
||||
"myop",
|
||||
Key::NotScoped(parse_quote! {"mykeyname"}),
|
||||
parse_quote! {"mykeyname"},
|
||||
Some(Labels::Existing(parse_quote! { mylabels })),
|
||||
quote! { 1 },
|
||||
);
|
||||
|
@ -156,7 +180,7 @@ fn test_get_expanded_callsite_regular_path() {
|
|||
"{ ",
|
||||
"if let Some (recorder) = metrics :: try_recorder () { ",
|
||||
"recorder . myop_mytype (",
|
||||
"metrics :: Key :: Owned (metrics :: KeyData :: from_name_and_labels (\"mykeyname\" , mylabels)) , ",
|
||||
"metrics :: Key :: Owned (metrics :: KeyData :: from_parts (\"mykeyname\" , mylabels)) , ",
|
||||
"1",
|
||||
") ; ",
|
||||
"} }",
|
||||
|
@ -167,7 +191,7 @@ fn test_get_expanded_callsite_regular_path() {
|
|||
|
||||
#[test]
|
||||
fn test_key_to_quoted_no_labels() {
|
||||
let stream = key_to_quoted(Key::NotScoped(parse_quote! {"mykeyname"}), None);
|
||||
let stream = key_to_quoted(parse_quote! {"mykeyname"}, None);
|
||||
let expected = "metrics :: KeyData :: from_name (\"mykeyname\")";
|
||||
assert_eq!(stream.to_string(), expected);
|
||||
}
|
||||
|
@ -175,10 +199,10 @@ fn test_key_to_quoted_no_labels() {
|
|||
#[test]
|
||||
fn test_key_to_quoted_existing_labels() {
|
||||
let stream = key_to_quoted(
|
||||
Key::NotScoped(parse_quote! {"mykeyname"}),
|
||||
parse_quote! {"mykeyname"},
|
||||
Some(Labels::Existing(Expr::Path(parse_quote! { mylabels }))),
|
||||
);
|
||||
let expected = "metrics :: KeyData :: from_name_and_labels (\"mykeyname\" , mylabels)";
|
||||
let expected = "metrics :: KeyData :: from_parts (\"mykeyname\" , mylabels)";
|
||||
assert_eq!(stream.to_string(), expected);
|
||||
}
|
||||
|
||||
|
@ -187,14 +211,14 @@ fn test_key_to_quoted_existing_labels() {
|
|||
#[test]
|
||||
fn test_key_to_quoted_inline_labels() {
|
||||
let stream = key_to_quoted(
|
||||
Key::NotScoped(parse_quote! {"mykeyname"}),
|
||||
parse_quote! {"mykeyname"},
|
||||
Some(Labels::Inline(vec![
|
||||
(parse_quote! {"mylabel1"}, parse_quote! { mylabel1 }),
|
||||
(parse_quote! {"mylabel2"}, parse_quote! { "mylabel2" }),
|
||||
])),
|
||||
);
|
||||
let expected = concat!(
|
||||
"metrics :: KeyData :: from_name_and_labels (\"mykeyname\" , vec ! [",
|
||||
"metrics :: KeyData :: from_parts (\"mykeyname\" , vec ! [",
|
||||
"metrics :: Label :: new (\"mylabel1\" , mylabel1) , ",
|
||||
"metrics :: Label :: new (\"mylabel2\" , \"mylabel2\")",
|
||||
"])"
|
||||
|
@ -204,12 +228,9 @@ fn test_key_to_quoted_inline_labels() {
|
|||
|
||||
#[test]
|
||||
fn test_key_to_quoted_inline_labels_empty() {
|
||||
let stream = key_to_quoted(
|
||||
Key::NotScoped(parse_quote! {"mykeyname"}),
|
||||
Some(Labels::Inline(vec![])),
|
||||
);
|
||||
let stream = key_to_quoted(parse_quote! {"mykeyname"}, Some(Labels::Inline(vec![])));
|
||||
let expected = concat!(
|
||||
"metrics :: KeyData :: from_name_and_labels (\"mykeyname\" , vec ! [",
|
||||
"metrics :: KeyData :: from_parts (\"mykeyname\" , vec ! [",
|
||||
"])"
|
||||
);
|
||||
assert_eq!(stream.to_string(), expected);
|
||||
|
|
|
@ -127,7 +127,7 @@ where
|
|||
fn enhance_key(&self, key: Key) -> Key {
|
||||
let (name, mut labels) = key.into_owned().into_parts();
|
||||
self.enhance_labels(&mut labels);
|
||||
KeyData::from_name_and_labels(name, labels).into()
|
||||
KeyData::from_parts(name, labels).into()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@ fn test_basic_functionality() {
|
|||
snapshot,
|
||||
vec![(
|
||||
MetricKind::Counter,
|
||||
KeyData::from_name_and_labels(
|
||||
KeyData::from_parts(
|
||||
"login_attempts",
|
||||
vec![
|
||||
Label::new("service", "login_service"),
|
||||
|
@ -95,7 +95,7 @@ fn test_macro_forms() {
|
|||
vec![
|
||||
(
|
||||
MetricKind::Counter,
|
||||
KeyData::from_name_and_labels(
|
||||
KeyData::from_parts(
|
||||
"login_attempts_no_labels",
|
||||
vec![
|
||||
Label::new("user", "ferris"),
|
||||
|
@ -109,7 +109,7 @@ fn test_macro_forms() {
|
|||
),
|
||||
(
|
||||
MetricKind::Counter,
|
||||
KeyData::from_name_and_labels(
|
||||
KeyData::from_parts(
|
||||
"login_attempts_static_labels",
|
||||
vec![
|
||||
Label::new("service", "login_service"),
|
||||
|
@ -124,7 +124,7 @@ fn test_macro_forms() {
|
|||
),
|
||||
(
|
||||
MetricKind::Counter,
|
||||
KeyData::from_name_and_labels(
|
||||
KeyData::from_parts(
|
||||
"login_attempts_dynamic_labels",
|
||||
vec![
|
||||
Label::new("node_name", "localhost"),
|
||||
|
@ -139,7 +139,7 @@ fn test_macro_forms() {
|
|||
),
|
||||
(
|
||||
MetricKind::Counter,
|
||||
KeyData::from_name_and_labels(
|
||||
KeyData::from_parts(
|
||||
"login_attempts_static_and_dynamic_labels",
|
||||
vec![
|
||||
Label::new("service", "login_service"),
|
||||
|
@ -224,7 +224,7 @@ fn test_multiple_paths_to_the_same_callsite() {
|
|||
vec![
|
||||
(
|
||||
MetricKind::Counter,
|
||||
KeyData::from_name_and_labels(
|
||||
KeyData::from_parts(
|
||||
"my_counter",
|
||||
vec![
|
||||
Label::new("shared_field", "path1"),
|
||||
|
@ -239,7 +239,7 @@ fn test_multiple_paths_to_the_same_callsite() {
|
|||
),
|
||||
(
|
||||
MetricKind::Counter,
|
||||
KeyData::from_name_and_labels(
|
||||
KeyData::from_parts(
|
||||
"my_counter",
|
||||
vec![
|
||||
Label::new("shared_field", "path2"),
|
||||
|
@ -295,7 +295,7 @@ fn test_nested_spans() {
|
|||
snapshot,
|
||||
vec![(
|
||||
MetricKind::Counter,
|
||||
KeyData::from_name_and_labels(
|
||||
KeyData::from_parts(
|
||||
"my_counter",
|
||||
vec![
|
||||
Label::new("shared_field", "inner"),
|
||||
|
@ -340,7 +340,7 @@ fn test_label_filtering() {
|
|||
snapshot,
|
||||
vec![(
|
||||
MetricKind::Counter,
|
||||
KeyData::from_name_and_labels(
|
||||
KeyData::from_parts(
|
||||
"login_attempts",
|
||||
vec![
|
||||
Label::new("service", "login_service"),
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use criterion::{criterion_group, criterion_main, BatchSize, Benchmark, Criterion};
|
||||
use metrics::{Key, KeyData, Label, OnceKeyData};
|
||||
use metrics::{Key, KeyData, Label};
|
||||
use metrics_util::Registry;
|
||||
|
||||
fn registry_benchmark(c: &mut Criterion) {
|
||||
|
@ -7,22 +7,20 @@ fn registry_benchmark(c: &mut Criterion) {
|
|||
"registry",
|
||||
Benchmark::new("cached op (basic)", |b| {
|
||||
let registry: Registry<Key, ()> = Registry::new();
|
||||
static KEY_DATA: OnceKeyData = OnceKeyData::new();
|
||||
static KEY_DATA: KeyData = KeyData::from_static_name("simple_key");
|
||||
|
||||
b.iter(|| {
|
||||
let key = Key::Borrowed(KEY_DATA.get_or_init(|| KeyData::from_name("simple_key")));
|
||||
let key = Key::Borrowed(&KEY_DATA);
|
||||
registry.op(key, |_| (), || ())
|
||||
})
|
||||
})
|
||||
.with_function("cached op (labels)", |b| {
|
||||
let registry: Registry<Key, ()> = Registry::new();
|
||||
static KEY_DATA: OnceKeyData = OnceKeyData::new();
|
||||
static KEY_LABELS: [Label; 1] = [Label::from_static_parts("type", "http")];
|
||||
static KEY_DATA: KeyData = KeyData::from_static_parts("simple_key", &KEY_LABELS);
|
||||
|
||||
b.iter(|| {
|
||||
let key = Key::Borrowed(KEY_DATA.get_or_init(|| {
|
||||
let labels = vec![Label::new("type", "http")];
|
||||
KeyData::from_name_and_labels("simple_key", labels)
|
||||
}));
|
||||
let key = Key::Borrowed(&KEY_DATA);
|
||||
registry.op(key, |_| (), || ())
|
||||
})
|
||||
})
|
||||
|
@ -64,7 +62,20 @@ fn registry_benchmark(c: &mut Criterion) {
|
|||
b.iter(|| {
|
||||
let key = "simple_key";
|
||||
let labels = vec![Label::new("type", "http")];
|
||||
KeyData::from_name_and_labels(key, labels)
|
||||
KeyData::from_parts(key, labels)
|
||||
})
|
||||
})
|
||||
.with_function("const key data overhead (basic)", |b| {
|
||||
b.iter(|| {
|
||||
let key = "simple_key";
|
||||
KeyData::from_static_name(key)
|
||||
})
|
||||
})
|
||||
.with_function("const key data overhead (labels)", |b| {
|
||||
b.iter(|| {
|
||||
let key = "simple_key";
|
||||
static LABELS: [Label; 1] = [Label::from_static_parts("type", "http")];
|
||||
KeyData::from_static_parts(key, &LABELS)
|
||||
})
|
||||
})
|
||||
.with_function("owned key overhead (basic)", |b| {
|
||||
|
@ -77,29 +88,17 @@ fn registry_benchmark(c: &mut Criterion) {
|
|||
b.iter(|| {
|
||||
let key = "simple_key";
|
||||
let labels = vec![Label::new("type", "http")];
|
||||
Key::Owned(KeyData::from_name_and_labels(key, labels))
|
||||
Key::Owned(KeyData::from_parts(key, labels))
|
||||
})
|
||||
})
|
||||
.with_function("cached key overhead (basic)", |b| {
|
||||
static KEY_DATA: OnceKeyData = OnceKeyData::new();
|
||||
b.iter(|| {
|
||||
let key_data = KEY_DATA.get_or_init(|| {
|
||||
let key = "simple_key";
|
||||
KeyData::from_name(key)
|
||||
});
|
||||
Key::Borrowed(key_data)
|
||||
})
|
||||
static KEY_DATA: KeyData = KeyData::from_static_name("simple_key");
|
||||
b.iter(|| Key::Borrowed(&KEY_DATA))
|
||||
})
|
||||
.with_function("cached key overhead (labels)", |b| {
|
||||
static KEY_DATA: OnceKeyData = OnceKeyData::new();
|
||||
b.iter(|| {
|
||||
let key_data = KEY_DATA.get_or_init(|| {
|
||||
let key = "simple_key";
|
||||
let labels = vec![Label::new("type", "http")];
|
||||
KeyData::from_name_and_labels(key, labels)
|
||||
});
|
||||
Key::Borrowed(key_data)
|
||||
})
|
||||
static KEY_LABELS: [Label; 1] = [Label::from_static_parts("type", "http")];
|
||||
static KEY_DATA: KeyData = KeyData::from_static_parts("simple_key", &KEY_LABELS);
|
||||
b.iter(|| Key::Borrowed(&KEY_DATA))
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -31,7 +31,6 @@ harness = false
|
|||
beef = "0.4"
|
||||
metrics-macros = { version = "0.1.0-alpha.1", path = "../metrics-macros" }
|
||||
proc-macro-hack = "0.5"
|
||||
once_cell = "1"
|
||||
|
||||
[dev-dependencies]
|
||||
log = "0.4"
|
||||
|
|
|
@ -33,11 +33,13 @@ fn macro_benchmark(c: &mut Criterion) {
|
|||
c.bench(
|
||||
"macros",
|
||||
Benchmark::new("uninitialized/no_labels", |b| {
|
||||
metrics::clear_recorder();
|
||||
b.iter(|| {
|
||||
counter!("counter_bench", 42);
|
||||
})
|
||||
})
|
||||
.with_function("uninitialized/with_static_labels", |b| {
|
||||
metrics::clear_recorder();
|
||||
b.iter(|| {
|
||||
counter!("counter_bench", 42, "request" => "http", "svc" => "admin");
|
||||
})
|
||||
|
|
|
@ -86,38 +86,22 @@ fn main() {
|
|||
increment!("requests_processed", "request_type" => "admin");
|
||||
increment!("requests_processed", "request_type" => "admin", "server" => server_name.clone());
|
||||
increment!("requests_processed", common_labels);
|
||||
increment!(<"requests_processed">);
|
||||
increment!(<"requests_processed">, "request_type" => "admin");
|
||||
increment!(<"requests_processed">, "request_type" => "admin", "server" => server_name.clone());
|
||||
increment!(<"requests_processed">, common_labels);
|
||||
|
||||
// All the supported permutations of `counter!`:
|
||||
counter!("bytes_sent", 64);
|
||||
counter!("bytes_sent", 64, "listener" => "frontend");
|
||||
counter!("bytes_sent", 64, "listener" => "frontend", "server" => server_name.clone());
|
||||
counter!("bytes_sent", 64, common_labels);
|
||||
counter!(<"bytes_sent">, 64);
|
||||
counter!(<"bytes_sent">, 64, "listener" => "frontend");
|
||||
counter!(<"bytes_sent">, 64, "listener" => "frontend", "server" => server_name.clone());
|
||||
counter!(<"bytes_sent">, 64, common_labels);
|
||||
|
||||
// All the supported permutations of `gauge!`:
|
||||
gauge!("connection_count", 300.0);
|
||||
gauge!("connection_count", 300.0, "listener" => "frontend");
|
||||
gauge!("connection_count", 300.0, "listener" => "frontend", "server" => server_name.clone());
|
||||
gauge!("connection_count", 300.0, common_labels);
|
||||
gauge!(<"connection_count">, 300.0);
|
||||
gauge!(<"connection_count">, 300.0, "listener" => "frontend");
|
||||
gauge!(<"connection_count">, 300.0, "listener" => "frontend", "server" => server_name.clone());
|
||||
gauge!(<"connection_count">, 300.0, common_labels);
|
||||
|
||||
// All the supported permutations of `histogram!`:
|
||||
histogram!("svc.execution_time", 70);
|
||||
histogram!("svc.execution_time", 70, "type" => "users");
|
||||
histogram!("svc.execution_time", 70, "type" => "users", "server" => server_name.clone());
|
||||
histogram!("svc.execution_time", 70, common_labels);
|
||||
histogram!(<"svc.execution_time">, 70);
|
||||
histogram!(<"svc.execution_time">, 70, "type" => "users");
|
||||
histogram!(<"svc.execution_time">, 70, "type" => "users", "server" => server_name.clone());
|
||||
histogram!(<"svc.execution_time">, 70, common_labels);
|
||||
}
|
||||
|
|
|
@ -5,10 +5,10 @@ use beef::Cow;
|
|||
|
||||
/// An allocation-optimized string.
|
||||
///
|
||||
/// We specify `ScopedString` to attempt to get the best of both worlds: flexibility to provide a
|
||||
/// We specify `SharedString` to attempt to get the best of both worlds: flexibility to provide a
|
||||
/// static or dynamic (owned) string, while retaining the performance benefits of being able to
|
||||
/// take ownership of owned strings and borrows of completely static strings.
|
||||
pub type ScopedString = Cow<'static, str>;
|
||||
pub type SharedString = Cow<'static, str>;
|
||||
|
||||
/// Units for a given metric.
|
||||
///
|
||||
|
@ -254,7 +254,7 @@ impl IntoU64 for u64 {
|
|||
}
|
||||
}
|
||||
|
||||
impl IntoU64 for std::time::Duration {
|
||||
impl IntoU64 for core::time::Duration {
|
||||
fn into_u64(self) -> u64 {
|
||||
self.as_nanos() as u64
|
||||
}
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
use crate::{IntoLabels, Label, ScopedString};
|
||||
use crate::{IntoLabels, Label, SharedString};
|
||||
use alloc::{borrow::Cow, string::String, vec::Vec};
|
||||
use core::{
|
||||
fmt,
|
||||
hash::{Hash, Hasher},
|
||||
ops,
|
||||
slice::Iter,
|
||||
};
|
||||
|
||||
|
@ -11,28 +13,28 @@ use core::{
|
|||
/// responsible for the actual storage of the name and label data.
|
||||
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
|
||||
pub struct KeyData {
|
||||
name: ScopedString,
|
||||
labels: Vec<Label>,
|
||||
name: SharedString,
|
||||
labels: Cow<'static, [Label]>,
|
||||
}
|
||||
|
||||
impl KeyData {
|
||||
/// Creates a [`KeyData`] from a name.
|
||||
pub fn from_name<N>(name: N) -> Self
|
||||
where
|
||||
N: Into<ScopedString>,
|
||||
N: Into<SharedString>,
|
||||
{
|
||||
Self::from_name_and_labels(name, Vec::new())
|
||||
Self::from_parts(name, Vec::new())
|
||||
}
|
||||
|
||||
/// Creates a [`KeyData`] from a name and vector of [`Label`]s.
|
||||
pub fn from_name_and_labels<N, L>(name: N, labels: L) -> Self
|
||||
pub fn from_parts<N, L>(name: N, labels: L) -> Self
|
||||
where
|
||||
N: Into<ScopedString>,
|
||||
N: Into<SharedString>,
|
||||
L: IntoLabels,
|
||||
{
|
||||
Self {
|
||||
name: name.into(),
|
||||
labels: labels.into_labels(),
|
||||
labels: labels.into_labels().into(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -41,13 +43,23 @@ impl KeyData {
|
|||
/// This function is `const`, so it can be used in a static context.
|
||||
pub const fn from_static_name(name: &'static str) -> Self {
|
||||
Self {
|
||||
name: ScopedString::const_str(name),
|
||||
labels: Vec::new(),
|
||||
name: SharedString::const_str(name),
|
||||
labels: Cow::Owned(Vec::new()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a [`KeyData`] from a static name and static set of labels.
|
||||
///
|
||||
/// This function is `const`, so it can be used in a static context.
|
||||
pub const fn from_static_parts(name: &'static str, labels: &'static [Label]) -> Self {
|
||||
Self {
|
||||
name: SharedString::const_str(name),
|
||||
labels: Cow::Borrowed(labels),
|
||||
}
|
||||
}
|
||||
|
||||
/// Name of this key.
|
||||
pub fn name(&self) -> &ScopedString {
|
||||
pub fn name(&self) -> &SharedString {
|
||||
&self.name
|
||||
}
|
||||
|
||||
|
@ -61,7 +73,7 @@ impl KeyData {
|
|||
/// The value returned by `f` becomes the new name of the key.
|
||||
pub fn map_name<F>(mut self, f: F) -> Self
|
||||
where
|
||||
F: Fn(ScopedString) -> String,
|
||||
F: Fn(SharedString) -> String,
|
||||
{
|
||||
let new_name = f(self.name);
|
||||
self.name = new_name.into();
|
||||
|
@ -69,8 +81,8 @@ impl KeyData {
|
|||
}
|
||||
|
||||
/// Consumes this [`Key`], returning the name and any labels.
|
||||
pub fn into_parts(self) -> (ScopedString, Vec<Label>) {
|
||||
(self.name, self.labels)
|
||||
pub fn into_parts(self) -> (SharedString, Vec<Label>) {
|
||||
(self.name, self.labels.into_owned())
|
||||
}
|
||||
|
||||
/// Clones this [`Key`], and expands the existing set of labels.
|
||||
|
@ -80,10 +92,13 @@ impl KeyData {
|
|||
}
|
||||
|
||||
let name = self.name.clone();
|
||||
let mut labels = self.labels.clone();
|
||||
let mut labels = self.labels.clone().into_owned();
|
||||
labels.extend(extra_labels);
|
||||
|
||||
Self { name, labels }
|
||||
Self {
|
||||
name,
|
||||
labels: labels.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -94,7 +109,7 @@ impl fmt::Display for KeyData {
|
|||
} else {
|
||||
write!(f, "KeyData({}, [", self.name)?;
|
||||
let mut first = true;
|
||||
for label in &self.labels {
|
||||
for label in self.labels.as_ref() {
|
||||
if first {
|
||||
write!(f, "{} = {}", label.0, label.1)?;
|
||||
first = false;
|
||||
|
@ -121,11 +136,11 @@ impl From<&'static str> for KeyData {
|
|||
|
||||
impl<N, L> From<(N, L)> for KeyData
|
||||
where
|
||||
N: Into<ScopedString>,
|
||||
N: Into<SharedString>,
|
||||
L: IntoLabels,
|
||||
{
|
||||
fn from(parts: (N, L)) -> Self {
|
||||
Self::from_name_and_labels(parts.0, parts.1)
|
||||
Self::from_parts(parts.0, parts.1)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -180,7 +195,7 @@ impl Key {
|
|||
}
|
||||
}
|
||||
|
||||
impl std::ops::Deref for Key {
|
||||
impl ops::Deref for Key {
|
||||
type Target = KeyData;
|
||||
|
||||
#[must_use]
|
||||
|
@ -223,44 +238,37 @@ impl From<&'static KeyData> for Key {
|
|||
}
|
||||
}
|
||||
|
||||
/// A thread-safe cell which can only be written to once, for key data.
|
||||
///
|
||||
/// Allows for efficient caching of static [`KeyData`] at metric callsites.
|
||||
pub type OnceKeyData = once_cell::sync::OnceCell<KeyData>;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{Key, KeyData, OnceKeyData};
|
||||
use super::{Key, KeyData};
|
||||
use crate::Label;
|
||||
use std::collections::HashMap;
|
||||
|
||||
static BORROWED_BASIC: OnceKeyData = OnceKeyData::new();
|
||||
static BORROWED_LABELS: OnceKeyData = OnceKeyData::new();
|
||||
static BORROWED_BASIC: KeyData = KeyData::from_static_name("name");
|
||||
static LABELS: [Label; 1] = [Label::from_static_parts("key", "value")];
|
||||
static BORROWED_LABELS: KeyData = KeyData::from_static_parts("name", &LABELS);
|
||||
|
||||
#[test]
|
||||
fn test_keydata_eq_and_hash() {
|
||||
let mut keys = HashMap::new();
|
||||
|
||||
let owned_basic = KeyData::from_name("name");
|
||||
let borrowed_basic = BORROWED_BASIC.get_or_init(|| KeyData::from_name("name"));
|
||||
assert_eq!(&owned_basic, borrowed_basic);
|
||||
assert_eq!(&owned_basic, &BORROWED_BASIC);
|
||||
|
||||
let previous = keys.insert(owned_basic, 42);
|
||||
assert!(previous.is_none());
|
||||
|
||||
let previous = keys.get(&borrowed_basic);
|
||||
let previous = keys.get(&BORROWED_BASIC);
|
||||
assert_eq!(previous, Some(&42));
|
||||
|
||||
let labels = vec![Label::new("key", "value")];
|
||||
let owned_labels = KeyData::from_name_and_labels("name", labels.clone());
|
||||
let borrowed_labels =
|
||||
BORROWED_LABELS.get_or_init(|| KeyData::from_name_and_labels("name", labels.clone()));
|
||||
assert_eq!(&owned_labels, borrowed_labels);
|
||||
let labels = LABELS.to_vec();
|
||||
let owned_labels = KeyData::from_parts("name", labels);
|
||||
assert_eq!(&owned_labels, &BORROWED_LABELS);
|
||||
|
||||
let previous = keys.insert(owned_labels, 43);
|
||||
assert!(previous.is_none());
|
||||
|
||||
let previous = keys.get(&borrowed_labels);
|
||||
let previous = keys.get(&BORROWED_LABELS);
|
||||
assert_eq!(previous, Some(&43));
|
||||
}
|
||||
|
||||
|
@ -269,9 +277,7 @@ mod tests {
|
|||
let mut keys = HashMap::new();
|
||||
|
||||
let owned_basic: Key = KeyData::from_name("name").into();
|
||||
let borrowed_basic: Key = BORROWED_BASIC
|
||||
.get_or_init(|| KeyData::from_name("name"))
|
||||
.into();
|
||||
let borrowed_basic: Key = Key::from(&BORROWED_BASIC);
|
||||
assert_eq!(owned_basic, borrowed_basic);
|
||||
|
||||
let previous = keys.insert(owned_basic, 42);
|
||||
|
@ -280,11 +286,9 @@ mod tests {
|
|||
let previous = keys.get(&borrowed_basic);
|
||||
assert_eq!(previous, Some(&42));
|
||||
|
||||
let labels = vec![Label::new("key", "value")];
|
||||
let owned_labels = Key::from(KeyData::from_name_and_labels("name", labels.clone()));
|
||||
let borrowed_labels = Key::from(
|
||||
BORROWED_LABELS.get_or_init(|| KeyData::from_name_and_labels("name", labels.clone())),
|
||||
);
|
||||
let labels = LABELS.to_vec();
|
||||
let owned_labels = Key::from(KeyData::from_parts("name", labels));
|
||||
let borrowed_labels = Key::from(&BORROWED_LABELS);
|
||||
assert_eq!(owned_labels, borrowed_labels);
|
||||
|
||||
let previous = keys.insert(owned_labels, 43);
|
||||
|
@ -300,18 +304,18 @@ mod tests {
|
|||
let result1 = key1.to_string();
|
||||
assert_eq!(result1, "KeyData(foobar)");
|
||||
|
||||
let key2 = KeyData::from_name_and_labels("foobar", vec![Label::new("system", "http")]);
|
||||
let key2 = KeyData::from_parts("foobar", vec![Label::new("system", "http")]);
|
||||
let result2 = key2.to_string();
|
||||
assert_eq!(result2, "KeyData(foobar, [system = http])");
|
||||
|
||||
let key3 = KeyData::from_name_and_labels(
|
||||
let key3 = KeyData::from_parts(
|
||||
"foobar",
|
||||
vec![Label::new("system", "http"), Label::new("user", "joe")],
|
||||
);
|
||||
let result3 = key3.to_string();
|
||||
assert_eq!(result3, "KeyData(foobar, [system = http, user = joe])");
|
||||
|
||||
let key4 = KeyData::from_name_and_labels(
|
||||
let key4 = KeyData::from_parts(
|
||||
"foobar",
|
||||
vec![
|
||||
Label::new("black", "black"),
|
||||
|
@ -331,27 +335,24 @@ mod tests {
|
|||
let owned_a = KeyData::from_name("a");
|
||||
let owned_b = KeyData::from_name("b");
|
||||
|
||||
static STATIC_A: OnceKeyData = OnceKeyData::new();
|
||||
static STATIC_B: OnceKeyData = OnceKeyData::new();
|
||||
|
||||
let borrowed_a = STATIC_A.get_or_init(|| owned_a.clone());
|
||||
let borrowed_b = STATIC_B.get_or_init(|| owned_b.clone());
|
||||
static STATIC_A: KeyData = KeyData::from_static_name("a");
|
||||
static STATIC_B: KeyData = KeyData::from_static_name("b");
|
||||
|
||||
assert_eq!(Key::Owned(owned_a.clone()), Key::Owned(owned_a.clone()));
|
||||
assert_eq!(Key::Owned(owned_b.clone()), Key::Owned(owned_b.clone()));
|
||||
|
||||
assert_eq!(Key::Borrowed(borrowed_a), Key::Borrowed(borrowed_a));
|
||||
assert_eq!(Key::Borrowed(borrowed_b), Key::Borrowed(borrowed_b));
|
||||
assert_eq!(Key::Borrowed(&STATIC_A), Key::Borrowed(&STATIC_A));
|
||||
assert_eq!(Key::Borrowed(&STATIC_B), Key::Borrowed(&STATIC_B));
|
||||
|
||||
assert_eq!(Key::Owned(owned_a.clone()), Key::Borrowed(borrowed_a));
|
||||
assert_eq!(Key::Owned(owned_b.clone()), Key::Borrowed(borrowed_b));
|
||||
assert_eq!(Key::Owned(owned_a.clone()), Key::Borrowed(&STATIC_A));
|
||||
assert_eq!(Key::Owned(owned_b.clone()), Key::Borrowed(&STATIC_B));
|
||||
|
||||
assert_eq!(Key::Borrowed(borrowed_a), Key::Owned(owned_a.clone()));
|
||||
assert_eq!(Key::Borrowed(borrowed_b), Key::Owned(owned_b.clone()));
|
||||
assert_eq!(Key::Borrowed(&STATIC_A), Key::Owned(owned_a.clone()));
|
||||
assert_eq!(Key::Borrowed(&STATIC_B), Key::Owned(owned_b.clone()));
|
||||
|
||||
assert_ne!(Key::Owned(owned_a.clone()), Key::Owned(owned_b.clone()),);
|
||||
assert_ne!(Key::Borrowed(borrowed_a), Key::Borrowed(borrowed_b));
|
||||
assert_ne!(Key::Owned(owned_a.clone()), Key::Borrowed(borrowed_b));
|
||||
assert_ne!(Key::Owned(owned_b.clone()), Key::Borrowed(borrowed_a));
|
||||
assert_ne!(Key::Borrowed(&STATIC_A), Key::Borrowed(&STATIC_B));
|
||||
assert_ne!(Key::Owned(owned_a.clone()), Key::Borrowed(&STATIC_B));
|
||||
assert_ne!(Key::Owned(owned_b.clone()), Key::Borrowed(&STATIC_A));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use crate::ScopedString;
|
||||
use crate::SharedString;
|
||||
use alloc::vec::Vec;
|
||||
|
||||
/// Metadata for a metric key in the for of a key/value pair.
|
||||
///
|
||||
|
@ -11,18 +12,23 @@ use crate::ScopedString;
|
|||
/// branched internally -- for example, an optimized path and a fallback path -- you may wish to
|
||||
/// add a label that tracks which codepath was taken.
|
||||
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
|
||||
pub struct Label(pub(crate) ScopedString, pub(crate) ScopedString);
|
||||
pub struct Label(pub(crate) SharedString, pub(crate) SharedString);
|
||||
|
||||
impl Label {
|
||||
/// Creates a [`Label`] from a key and value.
|
||||
pub fn new<K, V>(key: K, value: V) -> Self
|
||||
where
|
||||
K: Into<ScopedString>,
|
||||
V: Into<ScopedString>,
|
||||
K: Into<SharedString>,
|
||||
V: Into<SharedString>,
|
||||
{
|
||||
Label(key.into(), value.into())
|
||||
}
|
||||
|
||||
/// Creates a [`Label`] from a static key and value.
|
||||
pub const fn from_static_parts(key: &'static str, value: &'static str) -> Self {
|
||||
Label(SharedString::const_str(key), SharedString::const_str(value))
|
||||
}
|
||||
|
||||
/// Key of this label.
|
||||
pub fn key(&self) -> &str {
|
||||
self.0.as_ref()
|
||||
|
@ -34,15 +40,15 @@ impl Label {
|
|||
}
|
||||
|
||||
/// Consumes this [`Label`], returning the key and value.
|
||||
pub fn into_parts(self) -> (ScopedString, ScopedString) {
|
||||
pub fn into_parts(self) -> (SharedString, SharedString) {
|
||||
(self.0, self.1)
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V> From<&(K, V)> for Label
|
||||
where
|
||||
K: Into<ScopedString> + Clone,
|
||||
V: Into<ScopedString> + Clone,
|
||||
K: Into<SharedString> + Clone,
|
||||
V: Into<SharedString> + Clone,
|
||||
{
|
||||
fn from(pair: &(K, V)) -> Label {
|
||||
Label::new(pair.0.clone(), pair.1.clone())
|
||||
|
|
|
@ -215,6 +215,10 @@
|
|||
//! [metrics-util]: https://docs.rs/metrics-util
|
||||
//! [AtomicBucket]: https://docs.rs/metrics-util/0.4.0-alpha.6/metrics_util/struct.AtomicBucket.html
|
||||
#![deny(missing_docs)]
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
use proc_macro_hack::proc_macro_hack;
|
||||
|
||||
mod common;
|
||||
|
@ -238,29 +242,14 @@ pub use self::recorder::*;
|
|||
/// recorder does anything with the description is implementation defined. Labels can also be
|
||||
/// specified when registering a metric.
|
||||
///
|
||||
/// # Scoped versus unscoped
|
||||
/// Metrics can be unscoped or scoped, where the scoping is derived by the current module the call
|
||||
/// is taking place in. This scope is used as a prefix to the provided metric name. For example,
|
||||
/// take an example metric of "my_counter_metric" being emitted in a binary application "service":
|
||||
///
|
||||
/// - unscoped: `my_counter_metric`
|
||||
/// - scoped: `service.my_counter_metric`
|
||||
///
|
||||
/// If the metric was emitted in a nested module within the application, the scope would represent
|
||||
/// the full module path to where the metric is being emitted, such as
|
||||
/// `service.some_module.my_counter_metric`.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # use metrics::register_counter;
|
||||
/// # use metrics::Unit;
|
||||
/// # fn main() {
|
||||
/// // A regular, unscoped counter:
|
||||
/// // A basic counter:
|
||||
/// register_counter!("some_metric_name");
|
||||
///
|
||||
/// // A scoped counter. This inherits a scope derived by the current module:
|
||||
/// register_counter!(<"some_metric_name">);
|
||||
///
|
||||
/// // Providing a unit for a counter:
|
||||
/// register_counter!("some_metric_name", Unit::Bytes);
|
||||
///
|
||||
|
@ -297,29 +286,14 @@ pub use metrics_macros::register_counter;
|
|||
/// recorder does anything with the description is implementation defined. Labels can also be
|
||||
/// specified when registering a metric.
|
||||
///
|
||||
/// # Scoped versus unscoped
|
||||
/// Metrics can be unscoped or scoped, where the scoping is derived by the current module the call
|
||||
/// is taking place in. This scope is used as a prefix to the provided metric name. For example,
|
||||
/// take an example metric of "my_gauge_metric" being emitted in a binary application "service":
|
||||
///
|
||||
/// - unscoped: `my_gauge_metric`
|
||||
/// - scoped: `service.my_gauge_metric`
|
||||
///
|
||||
/// If the metric was emitted in a nested module within the application, the scope would represent
|
||||
/// the full module path to where the metric is being emitted, such as
|
||||
/// `service.some_module.my_gauge_metric`.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # use metrics::register_gauge;
|
||||
/// # use metrics::Unit;
|
||||
/// # fn main() {
|
||||
/// // A regular, unscoped gauge:
|
||||
/// // A basic gauge:
|
||||
/// register_gauge!("some_metric_name");
|
||||
///
|
||||
/// // A scoped gauge. This inherits a scope derived by the current module:
|
||||
/// register_gauge!(<"some_metric_name">);
|
||||
///
|
||||
/// // Providing a unit for a gauge:
|
||||
/// register_gauge!("some_metric_name", Unit::Bytes);
|
||||
///
|
||||
|
@ -356,29 +330,14 @@ pub use metrics_macros::register_gauge;
|
|||
/// recorder does anything with the description is implementation defined. Labels can also be
|
||||
/// specified when registering a metric.
|
||||
///
|
||||
/// # Scoped versus unscoped
|
||||
/// Metrics can be unscoped or scoped, where the scoping is derived by the current module the call
|
||||
/// is taking place in. This scope is used as a prefix to the provided metric name. For example,
|
||||
/// take an example metric of "my_histogram_metric" being emitted in a binary application "service":
|
||||
///
|
||||
/// - unscoped: `my_histogram_metric`
|
||||
/// - scoped: `service.my_histogram_metric`
|
||||
///
|
||||
/// If the metric was emitted in a nested module within the application, the scope would represent
|
||||
/// the full module path to where the metric is being emitted, such as
|
||||
/// `service.some_module.my_histogram_metric`.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # use metrics::register_histogram;
|
||||
/// # use metrics::Unit;
|
||||
/// # fn main() {
|
||||
/// // A regular, unscoped histogram:
|
||||
/// // A basic histogram:
|
||||
/// register_histogram!("some_metric_name");
|
||||
///
|
||||
/// // A scoped histogram. This inherits a scope derived by the current module:
|
||||
/// register_histogram!(<"some_metric_name">);
|
||||
///
|
||||
/// // Providing a unit for a histogram:
|
||||
/// register_histogram!("some_metric_name", Unit::Nanoseconds);
|
||||
///
|
||||
|
@ -411,28 +370,13 @@ pub use metrics_macros::register_histogram;
|
|||
/// Counters represent a single monotonic value, which means the value can only be incremented, not
|
||||
/// decremented, and always starts out with an initial value of zero.
|
||||
///
|
||||
/// # Scoped versus unscoped
|
||||
/// Metrics can be unscoped or scoped, where the scoping is derived by the current module the call
|
||||
/// is taking place in. This scope is used as a prefix to the provided metric name. For example,
|
||||
/// take an example metric of "my_counter_metric" being emitted in a binary application "service":
|
||||
///
|
||||
/// - unscoped: `my_counter_metric`
|
||||
/// - scoped: `service.my_counter_metric`
|
||||
///
|
||||
/// If the metric was emitted in a nested module within the application, the scope would represent
|
||||
/// the full module path to where the metric is being emitted, such as
|
||||
/// `service.some_module.my_counter_metric`.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # use metrics::increment;
|
||||
/// # fn main() {
|
||||
/// // A regular, unscoped increment:
|
||||
/// // A basic increment:
|
||||
/// increment!("some_metric_name");
|
||||
///
|
||||
/// // A scoped increment. This inherits a scope derived by the current module:
|
||||
/// increment!(<"some_metric_name">);
|
||||
///
|
||||
/// // Specifying labels:
|
||||
/// increment!("some_metric_name", "service" => "http");
|
||||
///
|
||||
|
@ -450,28 +394,13 @@ pub use metrics_macros::increment;
|
|||
/// Counters represent a single monotonic value, which means the value can only be incremented, not
|
||||
/// decremented, and always starts out with an initial value of zero.
|
||||
///
|
||||
/// # Scoped versus unscoped
|
||||
/// Metrics can be unscoped or scoped, where the scoping is derived by the current module the call
|
||||
/// is taking place in. This scope is used as a prefix to the provided metric name. For example,
|
||||
/// take an example metric of "my_counter_metric" being emitted in a binary application "service":
|
||||
///
|
||||
/// - unscoped: `my_counter_metric`
|
||||
/// - scoped: `service.my_counter_metric`
|
||||
///
|
||||
/// If the metric was emitted in a nested module within the application, the scope would represent
|
||||
/// the full module path to where the metric is being emitted, such as
|
||||
/// `service.some_module.my_counter_metric`.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # use metrics::counter;
|
||||
/// # fn main() {
|
||||
/// // A regular, unscoped counter:
|
||||
/// // A basic counter:
|
||||
/// counter!("some_metric_name", 12);
|
||||
///
|
||||
/// // A scoped counter. This inherits a scope derived by the current module:
|
||||
/// counter!(<"some_metric_name">, 12);
|
||||
///
|
||||
/// // Specifying labels:
|
||||
/// counter!("some_metric_name", 12, "service" => "http");
|
||||
///
|
||||
|
@ -489,28 +418,13 @@ pub use metrics_macros::counter;
|
|||
/// Gauges represent a single value that can go up or down over time, and always starts out with an
|
||||
/// initial value of zero.
|
||||
///
|
||||
/// # Scoped versus unscoped
|
||||
/// Metrics can be unscoped or scoped, where the scoping is derived by the current module the call
|
||||
/// is taking place in. This scope is used as a prefix to the provided metric name. For example,
|
||||
/// take an example metric of "my_gauge_metric" being emitted in a binary application "service":
|
||||
///
|
||||
/// - unscoped: `my_gauge_metric`
|
||||
/// - scoped: `service.my_gauge_metric`
|
||||
///
|
||||
/// If the metric was emitted in a nested module within the application, the scope would represent
|
||||
/// the full module path to where the metric is being emitted, such as
|
||||
/// `service.some_module.my_gauge_metric`.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # use metrics::gauge;
|
||||
/// # fn main() {
|
||||
/// // A regular, unscoped gauge:
|
||||
/// // A basic gauge:
|
||||
/// gauge!("some_metric_name", 42.2222);
|
||||
///
|
||||
/// // A scoped gauge. This inherits a scope derived by the current module:
|
||||
/// gauge!(<"some_metric_name">, 33.3333);
|
||||
///
|
||||
/// // Specifying labels:
|
||||
/// gauge!("some_metric_name", 66.6666, "service" => "http");
|
||||
///
|
||||
|
@ -528,18 +442,6 @@ pub use metrics_macros::gauge;
|
|||
/// Histograms measure the distribution of values for a given set of measurements, and start with no
|
||||
/// initial values.
|
||||
///
|
||||
/// # Scoped versus unscoped
|
||||
/// Metrics can be unscoped or scoped, where the scoping is derived by the current module the call
|
||||
/// is taking place in. This scope is used as a prefix to the provided metric name. For example,
|
||||
/// take an example metric of "my_histogram_metric" being emitted in a binary application "service":
|
||||
///
|
||||
/// - unscoped: `my_histogram_metric`
|
||||
/// - scoped: `service.my_histogram_metric`
|
||||
///
|
||||
/// If the metric was emitted in a nested module within the application, the scope would represent
|
||||
/// the full module path to where the metric is being emitted, such as
|
||||
/// `service.some_module.my_histogram_metric`.
|
||||
///
|
||||
/// # Implicit conversions
|
||||
/// Histograms are represented as `u64` values, but often come from another source, such as a time
|
||||
/// measurement. By default, `histogram!` will accept a `u64` directly or a
|
||||
|
@ -553,16 +455,13 @@ pub use metrics_macros::gauge;
|
|||
/// # use metrics::histogram;
|
||||
/// # use std::time::Duration;
|
||||
/// # fn main() {
|
||||
/// // A regular, unscoped histogram:
|
||||
/// // A basic histogram:
|
||||
/// histogram!("some_metric_name", 34);
|
||||
///
|
||||
/// // An implicit conversion from `Duration`:
|
||||
/// let d = Duration::from_millis(17);
|
||||
/// histogram!("some_metric_name", d);
|
||||
///
|
||||
/// // A scoped histogram. This inherits a scope derived by the current module:
|
||||
/// histogram!(<"some_metric_name">, 38);
|
||||
///
|
||||
/// // Specifying labels:
|
||||
/// histogram!("some_metric_name", 38, "service" => "http");
|
||||
///
|
||||
|
|
|
@ -196,7 +196,7 @@ pub fn recorder() -> &'static dyn Recorder {
|
|||
/// If a recorder has not been set, returns `None`.
|
||||
pub fn try_recorder() -> Option<&'static dyn Recorder> {
|
||||
unsafe {
|
||||
if STATE.load(Ordering::SeqCst) != INITIALIZED {
|
||||
if STATE.load(Ordering::Relaxed) != INITIALIZED {
|
||||
None
|
||||
} else {
|
||||
Some(RECORDER)
|
||||
|
|
Loading…
Reference in New Issue