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