Compare commits
26 Commits
metrics-ex
...
main
Author | SHA1 | Date |
---|---|---|
Deirdre Connolly | bde1a50315 | |
Deirdre Connolly | d10286ff1e | |
Henry de Valence | 971133128e | |
Toby Lawrence | 7ef47304ed | |
Toby Lawrence | 8b57975110 | |
Toby Lawrence | 507493a59a | |
Toby Lawrence | 027cde096a | |
Toby Lawrence | ec1faac74c | |
Toby Lawrence | 576a7e42b6 | |
dependabot[bot] | 1cdae103b9 | |
Toby Lawrence | ba0530e2cd | |
Toby Lawrence | ee379362f5 | |
Toby Lawrence | 2cd4e9e100 | |
Toby Lawrence | db02cd80da | |
Toby Lawrence | 55f289261c | |
Toby Lawrence | 2d807af5b0 | |
Toby Lawrence | 30c7ff1a98 | |
Toby Lawrence | 7a8f3da859 | |
Toby Lawrence | 8ebe921ef7 | |
Toby Lawrence | 4312b9f205 | |
str4d | 01fcc020c7 | |
Floris Bruynooghe | 4fafc34869 | |
Floris Bruynooghe | 1b1c271531 | |
Toby Lawrence | d72a744a76 | |
Toby Lawrence | e926a6b6c6 | |
Toby Lawrence | 5fa7e8fd1d |
|
@ -24,7 +24,7 @@ jobs:
|
|||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
rust_version: ['1.43.0', 'stable', 'nightly']
|
||||
rust_version: ['1.46.0', 'stable', 'nightly']
|
||||
os: [ubuntu-latest, windows-latest, macOS-latest]
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
|
@ -35,6 +35,23 @@ jobs:
|
|||
override: true
|
||||
- name: Run Tests
|
||||
run: cargo test
|
||||
docs:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
RUSTDOCFLAGS: -Dwarnings
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install Rust Nightly
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: nightly
|
||||
override: true
|
||||
components: rust-docs
|
||||
- name: Check docs
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: doc
|
||||
args: --workspace --no-deps
|
||||
bench:
|
||||
name: Bench ${{ matrix.os }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
@ -49,4 +66,7 @@ jobs:
|
|||
toolchain: 'stable'
|
||||
override: true
|
||||
- name: Run Benchmarks
|
||||
run: cargo bench
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: bench
|
||||
args: --all-features
|
||||
|
|
146
COPYRIGHT
146
COPYRIGHT
|
@ -1,102 +1,94 @@
|
|||
Short version for non-lawyers:
|
||||
|
||||
metrics is MIT licensed.
|
||||
`metrics` is MIT licensed.
|
||||
|
||||
Longer version:
|
||||
|
||||
Copyrights in the metrics project are retained by their contributors. No
|
||||
copyright assignment is required to contribute to the metrics project.
|
||||
Copyrights in the `metrics` project are retained by their contributors. No copyright assignment is
|
||||
required to contribute to the `metrics` project.
|
||||
|
||||
Some files include explicit copyright notices and/or license notices.
|
||||
For full authorship information, see the version control history.
|
||||
Some files include explicit copyright notices and/or license notices. For full authorship
|
||||
information, see the version control history.
|
||||
|
||||
Except as otherwise noted (below and/or in individual files), metrics
|
||||
is licensed under the MIT license <LICENSE> or
|
||||
<http://opensource.org/licenses/MIT>.
|
||||
Except as otherwise noted (below and/or in individual files), `metrics` is licensed under the MIT
|
||||
license <LICENSE> or <http://opensource.org/licenses/MIT>.
|
||||
|
||||
|
||||
metrics includes packages written by third parties.
|
||||
The following third party packages are included, and carry
|
||||
their own copyright notices and license terms:
|
||||
`metrics` includes packages written by third parties. The following third party packages are
|
||||
included, and carry their own copyright notices and license terms:
|
||||
|
||||
* Portions of the API design are derived from tic
|
||||
<https://github.com/brayniac/tic>, which carries the following
|
||||
license:
|
||||
* Portions of the API design are derived from the `tic` crate which carries the following license:
|
||||
|
||||
Copyright (c) 2016 Brian Martin
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without restriction,
|
||||
including without limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
|
||||
and associated documentation files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
The above copyright notice and this permission notice shall be included in all copies or
|
||||
substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
|
||||
BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
* metrics is a fork of rust-lang-nursery/log which carries the following
|
||||
license:
|
||||
* metrics is a fork of `rust-lang-nursery/log` which carries the following license:
|
||||
|
||||
Copyright (c) 2014 The Rust Project Developers
|
||||
|
||||
Permission is hereby granted, free of charge, to any
|
||||
person obtaining a copy of this software and associated
|
||||
documentation files (the "Software"), to deal in the
|
||||
Software without restriction, including without
|
||||
limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software
|
||||
is furnished to do so, subject to the following
|
||||
conditions:
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
|
||||
and associated documentation files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice
|
||||
shall be included in all copies or substantial portions
|
||||
of the Software.
|
||||
The above copyright notice and this permission notice shall be included in all copies or
|
||||
substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||||
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
|
||||
BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
* metrics-observer reuses code from `std::time::Duration` which carries
|
||||
the following license:
|
||||
* metrics-observer reuses code from `std::time::Duration` which carries the following license:
|
||||
|
||||
Permission is hereby granted, free of charge, to any
|
||||
person obtaining a copy of this software and associated
|
||||
documentation files (the "Software"), to deal in the
|
||||
Software without restriction, including without
|
||||
limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software
|
||||
is furnished to do so, subject to the following
|
||||
conditions:
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
|
||||
and associated documentation files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice
|
||||
shall be included in all copies or substantial portions
|
||||
of the Software.
|
||||
The above copyright notice and this permission notice shall be included in all copies or
|
||||
substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||||
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
|
||||
BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
* metrics includes code from the `beef` crate which carries the following license:
|
||||
|
||||
Copyright (c) 2020 Maciej Hirsz <hello@maciej.codes>
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
|
||||
and associated documentation files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or
|
||||
substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
|
||||
BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@ -9,3 +9,7 @@ members = [
|
|||
"metrics-tracing-context",
|
||||
]
|
||||
exclude = ["metrics-observer"]
|
||||
|
||||
[patch.crates-io]
|
||||
hyper = { git = "https://github.com/hyperium/hyper/", rev = "ed2b22a7f66899d338691552fbcb6c0f2f4e06b9" }
|
||||
|
||||
|
|
|
@ -80,7 +80,7 @@ impl Generator {
|
|||
impl Drop for Generator {
|
||||
fn drop(&mut self) {
|
||||
info!(
|
||||
" sender latency: min: {:9} p50: {:9} p95: {:9} p99: {:9} p999: {:9} max: {:9}",
|
||||
" sender latency: min: {:8} p50: {:8} p95: {:8} p99: {:8} p999: {:8} max: {:8}",
|
||||
nanos_to_readable(self.hist.min()),
|
||||
nanos_to_readable(self.hist.value_at_percentile(50.0)),
|
||||
nanos_to_readable(self.hist.value_at_percentile(95.0)),
|
||||
|
@ -196,7 +196,7 @@ fn main() {
|
|||
info!("--------------------------------------------------------------------------------");
|
||||
info!(" ingested samples total: {}", total);
|
||||
info!(
|
||||
"snapshot retrieval: min: {:9} p50: {:9} p95: {:9} p99: {:9} p999: {:9} max: {:9}",
|
||||
"snapshot retrieval: min: {:8} p50: {:8} p95: {:8} p99: {:8} p999: {:8} max: {:8}",
|
||||
nanos_to_readable(snapshot_hist.min()),
|
||||
nanos_to_readable(snapshot_hist.value_at_percentile(50.0)),
|
||||
nanos_to_readable(snapshot_hist.value_at_percentile(95.0)),
|
||||
|
|
|
@ -27,8 +27,8 @@ parking_lot = "0.11"
|
|||
thiserror = "1.0"
|
||||
|
||||
# Optional
|
||||
hyper = { version = "0.13", default-features = false, features = ["tcp"], optional = true }
|
||||
tokio = { version = "0.2", features = ["rt-core", "tcp", "time", "macros"], optional = true }
|
||||
hyper = { version = "0.14.0-dev", default-features = false, features = ["tcp", "server", "http1", "http2"], optional = true }
|
||||
tokio = { version = "0.3", features = ["rt", "net", "time", "macros"], optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
quanta = "0.6"
|
||||
|
|
|
@ -13,13 +13,14 @@ use metrics_util::{
|
|||
parse_quantiles, CompositeKey, Handle, Histogram, MetricKind, Quantile, Registry,
|
||||
};
|
||||
use parking_lot::RwLock;
|
||||
use std::collections::HashMap;
|
||||
use std::io;
|
||||
use std::iter::FromIterator;
|
||||
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
|
||||
use std::sync::Arc;
|
||||
#[cfg(feature = "tokio-exporter")]
|
||||
use std::thread;
|
||||
use std::{collections::HashMap, time::SystemTime};
|
||||
use std::time::SystemTime;
|
||||
use thiserror::Error as ThisError;
|
||||
#[cfg(feature = "tokio-exporter")]
|
||||
use tokio::{pin, runtime, select};
|
||||
|
@ -211,7 +212,6 @@ impl Inner {
|
|||
output.push_str(value.to_string().as_str());
|
||||
output.push_str("\n");
|
||||
}
|
||||
output.push_str("\n");
|
||||
}
|
||||
|
||||
for (name, mut by_labels) in gauges.drain() {
|
||||
|
@ -233,7 +233,6 @@ impl Inner {
|
|||
output.push_str(value.to_string().as_str());
|
||||
output.push_str("\n");
|
||||
}
|
||||
output.push_str("\n");
|
||||
}
|
||||
|
||||
let mut sorted_overrides = self
|
||||
|
@ -316,8 +315,6 @@ impl Inner {
|
|||
output.push_str(count.to_string().as_str());
|
||||
output.push_str("\n");
|
||||
}
|
||||
|
||||
output.push_str("\n");
|
||||
}
|
||||
|
||||
output
|
||||
|
@ -345,7 +342,7 @@ impl PrometheusRecorder {
|
|||
fn add_description_if_missing(&self, key: &Key, description: Option<&'static str>) {
|
||||
if let Some(description) = description {
|
||||
let mut descriptions = self.inner.descriptions.write();
|
||||
if !descriptions.contains_key(key.name().as_ref()) {
|
||||
if !descriptions.contains_key(key.name().to_string().as_str()) {
|
||||
descriptions.insert(key.name().to_string(), description);
|
||||
}
|
||||
}
|
||||
|
@ -443,14 +440,16 @@ impl PrometheusBuilder {
|
|||
/// installing the recorder as the global recorder.
|
||||
#[cfg(feature = "tokio-exporter")]
|
||||
pub fn install(self) -> Result<(), Error> {
|
||||
let (recorder, exporter) = self.build_with_exporter()?;
|
||||
metrics::set_boxed_recorder(Box::new(recorder))?;
|
||||
|
||||
let mut runtime = runtime::Builder::new()
|
||||
.basic_scheduler()
|
||||
let runtime = runtime::Builder::new_current_thread()
|
||||
.enable_all()
|
||||
.build()?;
|
||||
|
||||
let (recorder, exporter) = {
|
||||
let _guard = runtime.enter();
|
||||
self.build_with_exporter()
|
||||
}?;
|
||||
metrics::set_boxed_recorder(Box::new(recorder))?;
|
||||
|
||||
thread::Builder::new()
|
||||
.name("metrics-exporter-prometheus-http".to_string())
|
||||
.spawn(move || {
|
||||
|
@ -596,7 +595,11 @@ fn key_to_parts(key: Key) -> (String, Vec<String>) {
|
|||
let name = key.name();
|
||||
let labels = key.labels();
|
||||
let sanitize = |c| c == '.' || c == '=' || c == '{' || c == '}' || c == '+' || c == '-';
|
||||
let name = name.replace(sanitize, "_");
|
||||
let name = name
|
||||
.parts()
|
||||
.map(|s| s.replace(sanitize, "_"))
|
||||
.collect::<Vec<_>>()
|
||||
.join("_");
|
||||
let labels = labels
|
||||
.into_iter()
|
||||
.map(|label| {
|
||||
|
|
|
@ -224,13 +224,13 @@ pub fn histogram(input: TokenStream) -> TokenStream {
|
|||
|
||||
fn get_expanded_registration(
|
||||
metric_type: &str,
|
||||
key: LitStr,
|
||||
name: LitStr,
|
||||
unit: Option<Expr>,
|
||||
description: Option<LitStr>,
|
||||
labels: Option<Labels>,
|
||||
) -> proc_macro2::TokenStream {
|
||||
let register_ident = format_ident!("register_{}", metric_type);
|
||||
let key = key_to_quoted(key, labels);
|
||||
let key = key_to_quoted(labels);
|
||||
|
||||
let unit = match unit {
|
||||
Some(e) => quote! { Some(#e) },
|
||||
|
@ -244,6 +244,7 @@ fn get_expanded_registration(
|
|||
|
||||
quote! {
|
||||
{
|
||||
static METRIC_NAME: [metrics::SharedString; 1] = [metrics::SharedString::const_str(#name)];
|
||||
// Only do this work if there's a recorder installed.
|
||||
if let Some(recorder) = metrics::try_recorder() {
|
||||
// Registrations are fairly rare, don't attempt to cache here
|
||||
|
@ -257,7 +258,7 @@ fn get_expanded_registration(
|
|||
fn get_expanded_callsite<V>(
|
||||
metric_type: &str,
|
||||
op_type: &str,
|
||||
key: LitStr,
|
||||
name: LitStr,
|
||||
labels: Option<Labels>,
|
||||
op_values: V,
|
||||
) -> proc_macro2::TokenStream
|
||||
|
@ -288,15 +289,17 @@ where
|
|||
let labels_len = quote! { #labels_len };
|
||||
|
||||
quote! {
|
||||
static METRIC_NAME: [metrics::SharedString; 1] = [metrics::SharedString::const_str(#name)];
|
||||
static METRIC_LABELS: [metrics::Label; #labels_len] = [#(#labels),*];
|
||||
static METRIC_KEY: metrics::KeyData =
|
||||
metrics::KeyData::from_static_parts(#key, &METRIC_LABELS);
|
||||
metrics::KeyData::from_static_parts(&METRIC_NAME, &METRIC_LABELS);
|
||||
}
|
||||
}
|
||||
None => {
|
||||
quote! {
|
||||
static METRIC_NAME: [metrics::SharedString; 1] = [metrics::SharedString::const_str(#name)];
|
||||
static METRIC_KEY: metrics::KeyData =
|
||||
metrics::KeyData::from_static_name(#key);
|
||||
metrics::KeyData::from_static_name(&METRIC_NAME);
|
||||
}
|
||||
}
|
||||
_ => unreachable!("use_fast_path == true, but found expression-based labels"),
|
||||
|
@ -314,9 +317,11 @@ where
|
|||
}
|
||||
} else {
|
||||
// We're on the slow path, so we allocate, womp.
|
||||
let key = key_to_quoted(key, labels);
|
||||
let key = key_to_quoted(labels);
|
||||
quote! {
|
||||
{
|
||||
static METRIC_NAME: [metrics::SharedString; 1] = [metrics::SharedString::const_str(#name)];
|
||||
|
||||
// Only do this work if there's a recorder installed.
|
||||
if let Some(recorder) = metrics::try_recorder() {
|
||||
recorder.#op_ident(metrics::Key::Owned(#key), #op_values);
|
||||
|
@ -353,17 +358,19 @@ fn read_key(input: &mut ParseStream) -> Result<LitStr> {
|
|||
Ok(key)
|
||||
}
|
||||
|
||||
fn key_to_quoted(name: LitStr, labels: Option<Labels>) -> proc_macro2::TokenStream {
|
||||
fn key_to_quoted(labels: Option<Labels>) -> proc_macro2::TokenStream {
|
||||
match labels {
|
||||
None => quote! { metrics::KeyData::from_name(#name) },
|
||||
None => quote! { metrics::KeyData::from_static_name(&METRIC_NAME) },
|
||||
Some(labels) => match labels {
|
||||
Labels::Inline(pairs) => {
|
||||
let labels = pairs
|
||||
.into_iter()
|
||||
.map(|(key, val)| quote! { metrics::Label::new(#key, #val) });
|
||||
quote! { metrics::KeyData::from_parts(#name, vec![#(#labels),*]) }
|
||||
quote! {
|
||||
metrics::KeyData::from_parts(&METRIC_NAME[..], vec![#(#labels),*])
|
||||
}
|
||||
}
|
||||
Labels::Existing(e) => quote! { metrics::KeyData::from_parts(#name, #e) },
|
||||
Labels::Existing(e) => quote! { metrics::KeyData::from_parts(&METRIC_NAME[..], #e) },
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,9 +10,11 @@ fn test_get_expanded_registration() {
|
|||
get_expanded_registration("mytype", parse_quote! { "mykeyname" }, None, None, None);
|
||||
|
||||
let expected = concat!(
|
||||
"{ if let Some (recorder) = metrics :: try_recorder () { ",
|
||||
"{ ",
|
||||
"static METRIC_NAME : [metrics :: SharedString ; 1] = [metrics :: SharedString :: const_str (\"mykeyname\")] ; ",
|
||||
"if let Some (recorder) = metrics :: try_recorder () { ",
|
||||
"recorder . register_mytype (",
|
||||
"metrics :: Key :: Owned (metrics :: KeyData :: from_name (\"mykeyname\")) , ",
|
||||
"metrics :: Key :: Owned (metrics :: KeyData :: from_static_name (& METRIC_NAME)) , ",
|
||||
"None , ",
|
||||
"None",
|
||||
") ; ",
|
||||
|
@ -35,9 +37,11 @@ fn test_get_expanded_registration_with_unit() {
|
|||
);
|
||||
|
||||
let expected = concat!(
|
||||
"{ if let Some (recorder) = metrics :: try_recorder () { ",
|
||||
"{ ",
|
||||
"static METRIC_NAME : [metrics :: SharedString ; 1] = [metrics :: SharedString :: const_str (\"mykeyname\")] ; ",
|
||||
"if let Some (recorder) = metrics :: try_recorder () { ",
|
||||
"recorder . register_mytype (",
|
||||
"metrics :: Key :: Owned (metrics :: KeyData :: from_name (\"mykeyname\")) , ",
|
||||
"metrics :: Key :: Owned (metrics :: KeyData :: from_static_name (& METRIC_NAME)) , ",
|
||||
"Some (metrics :: Unit :: Nanoseconds) , ",
|
||||
"None",
|
||||
") ; ",
|
||||
|
@ -59,9 +63,11 @@ fn test_get_expanded_registration_with_description() {
|
|||
);
|
||||
|
||||
let expected = concat!(
|
||||
"{ if let Some (recorder) = metrics :: try_recorder () { ",
|
||||
"{ ",
|
||||
"static METRIC_NAME : [metrics :: SharedString ; 1] = [metrics :: SharedString :: const_str (\"mykeyname\")] ; ",
|
||||
"if let Some (recorder) = metrics :: try_recorder () { ",
|
||||
"recorder . register_mytype (",
|
||||
"metrics :: Key :: Owned (metrics :: KeyData :: from_name (\"mykeyname\")) , ",
|
||||
"metrics :: Key :: Owned (metrics :: KeyData :: from_static_name (& METRIC_NAME)) , ",
|
||||
"None , ",
|
||||
"Some (\"flerkin\")",
|
||||
") ; ",
|
||||
|
@ -84,9 +90,11 @@ fn test_get_expanded_registration_with_unit_and_description() {
|
|||
);
|
||||
|
||||
let expected = concat!(
|
||||
"{ if let Some (recorder) = metrics :: try_recorder () { ",
|
||||
"{ ",
|
||||
"static METRIC_NAME : [metrics :: SharedString ; 1] = [metrics :: SharedString :: const_str (\"mykeyname\")] ; ",
|
||||
"if let Some (recorder) = metrics :: try_recorder () { ",
|
||||
"recorder . register_mytype (",
|
||||
"metrics :: Key :: Owned (metrics :: KeyData :: from_name (\"mykeyname\")) , ",
|
||||
"metrics :: Key :: Owned (metrics :: KeyData :: from_static_name (& METRIC_NAME)) , ",
|
||||
"Some (metrics :: Unit :: Nanoseconds) , ",
|
||||
"Some (\"flerkin\")",
|
||||
") ; ",
|
||||
|
@ -108,7 +116,8 @@ fn test_get_expanded_callsite_fast_path_no_labels() {
|
|||
|
||||
let expected = concat!(
|
||||
"{ ",
|
||||
"static METRIC_KEY : metrics :: KeyData = metrics :: KeyData :: from_static_name (\"mykeyname\") ; ",
|
||||
"static METRIC_NAME : [metrics :: SharedString ; 1] = [metrics :: SharedString :: const_str (\"mykeyname\")] ; ",
|
||||
"static METRIC_KEY : metrics :: KeyData = metrics :: KeyData :: from_static_name (& METRIC_NAME) ; ",
|
||||
"if let Some (recorder) = metrics :: try_recorder () { ",
|
||||
"recorder . myop_mytype (metrics :: Key :: Borrowed (& METRIC_KEY) , 1) ; ",
|
||||
"} }",
|
||||
|
@ -130,8 +139,9 @@ fn test_get_expanded_callsite_fast_path_static_labels() {
|
|||
|
||||
let expected = concat!(
|
||||
"{ ",
|
||||
"static METRIC_NAME : [metrics :: SharedString ; 1] = [metrics :: SharedString :: const_str (\"mykeyname\")] ; ",
|
||||
"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) ; ",
|
||||
"static METRIC_KEY : metrics :: KeyData = metrics :: KeyData :: from_static_parts (& METRIC_NAME , & METRIC_LABELS) ; ",
|
||||
"if let Some (recorder) = metrics :: try_recorder () { ",
|
||||
"recorder . myop_mytype (metrics :: Key :: Borrowed (& METRIC_KEY) , 1) ; ",
|
||||
"} ",
|
||||
|
@ -154,9 +164,10 @@ fn test_get_expanded_callsite_fast_path_dynamic_labels() {
|
|||
|
||||
let expected = concat!(
|
||||
"{ ",
|
||||
"static METRIC_NAME : [metrics :: SharedString ; 1] = [metrics :: SharedString :: const_str (\"mykeyname\")] ; ",
|
||||
"if let Some (recorder) = metrics :: try_recorder () { ",
|
||||
"recorder . myop_mytype (metrics :: Key :: Owned (",
|
||||
"metrics :: KeyData :: from_parts (\"mykeyname\" , vec ! [metrics :: Label :: new (\"key1\" , & value1)])",
|
||||
"metrics :: KeyData :: from_parts (& METRIC_NAME [..] , vec ! [metrics :: Label :: new (\"key1\" , & value1)])",
|
||||
") , 1) ; ",
|
||||
"} ",
|
||||
"}",
|
||||
|
@ -178,9 +189,10 @@ fn test_get_expanded_callsite_regular_path() {
|
|||
|
||||
let expected = concat!(
|
||||
"{ ",
|
||||
"static METRIC_NAME : [metrics :: SharedString ; 1] = [metrics :: SharedString :: const_str (\"mykeyname\")] ; ",
|
||||
"if let Some (recorder) = metrics :: try_recorder () { ",
|
||||
"recorder . myop_mytype (",
|
||||
"metrics :: Key :: Owned (metrics :: KeyData :: from_parts (\"mykeyname\" , mylabels)) , ",
|
||||
"metrics :: Key :: Owned (metrics :: KeyData :: from_parts (& METRIC_NAME [..] , mylabels)) , ",
|
||||
"1",
|
||||
") ; ",
|
||||
"} }",
|
||||
|
@ -191,18 +203,17 @@ fn test_get_expanded_callsite_regular_path() {
|
|||
|
||||
#[test]
|
||||
fn test_key_to_quoted_no_labels() {
|
||||
let stream = key_to_quoted(parse_quote! {"mykeyname"}, None);
|
||||
let expected = "metrics :: KeyData :: from_name (\"mykeyname\")";
|
||||
let stream = key_to_quoted(None);
|
||||
let expected = "metrics :: KeyData :: from_static_name (& METRIC_NAME)";
|
||||
assert_eq!(stream.to_string(), expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_key_to_quoted_existing_labels() {
|
||||
let stream = key_to_quoted(
|
||||
parse_quote! {"mykeyname"},
|
||||
Some(Labels::Existing(Expr::Path(parse_quote! { mylabels }))),
|
||||
);
|
||||
let expected = "metrics :: KeyData :: from_parts (\"mykeyname\" , mylabels)";
|
||||
let stream = key_to_quoted(Some(Labels::Existing(Expr::Path(
|
||||
parse_quote! { mylabels },
|
||||
))));
|
||||
let expected = "metrics :: KeyData :: from_parts (& METRIC_NAME [..] , mylabels)";
|
||||
assert_eq!(stream.to_string(), expected);
|
||||
}
|
||||
|
||||
|
@ -210,15 +221,12 @@ fn test_key_to_quoted_existing_labels() {
|
|||
/// Key).
|
||||
#[test]
|
||||
fn test_key_to_quoted_inline_labels() {
|
||||
let stream = key_to_quoted(
|
||||
parse_quote! {"mykeyname"},
|
||||
Some(Labels::Inline(vec![
|
||||
(parse_quote! {"mylabel1"}, parse_quote! { mylabel1 }),
|
||||
(parse_quote! {"mylabel2"}, parse_quote! { "mylabel2" }),
|
||||
])),
|
||||
);
|
||||
let stream = key_to_quoted(Some(Labels::Inline(vec![
|
||||
(parse_quote! {"mylabel1"}, parse_quote! { mylabel1 }),
|
||||
(parse_quote! {"mylabel2"}, parse_quote! { "mylabel2" }),
|
||||
])));
|
||||
let expected = concat!(
|
||||
"metrics :: KeyData :: from_parts (\"mykeyname\" , vec ! [",
|
||||
"metrics :: KeyData :: from_parts (& METRIC_NAME [..] , vec ! [",
|
||||
"metrics :: Label :: new (\"mylabel1\" , mylabel1) , ",
|
||||
"metrics :: Label :: new (\"mylabel2\" , \"mylabel2\")",
|
||||
"])"
|
||||
|
@ -228,10 +236,7 @@ fn test_key_to_quoted_inline_labels() {
|
|||
|
||||
#[test]
|
||||
fn test_key_to_quoted_inline_labels_empty() {
|
||||
let stream = key_to_quoted(parse_quote! {"mykeyname"}, Some(Labels::Inline(vec![])));
|
||||
let expected = concat!(
|
||||
"metrics :: KeyData :: from_parts (\"mykeyname\" , vec ! [",
|
||||
"])"
|
||||
);
|
||||
let stream = key_to_quoted(Some(Labels::Inline(vec![])));
|
||||
let expected = concat!("metrics :: KeyData :: from_parts (& METRIC_NAME [..] , vec ! [])");
|
||||
assert_eq!(stream.to_string(), expected);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use criterion::{criterion_group, criterion_main, Benchmark, Criterion};
|
||||
use metrics::{Key, KeyData, Label, NoopRecorder, Recorder};
|
||||
use metrics::{Key, KeyData, Label, NoopRecorder, Recorder, SharedString};
|
||||
use metrics_tracing_context::{MetricsLayer, TracingContextLayer};
|
||||
use metrics_util::layers::Layer;
|
||||
use tracing::{
|
||||
|
@ -22,8 +22,9 @@ fn layer_benchmark(c: &mut Criterion) {
|
|||
|
||||
let tracing_layer = TracingContextLayer::all();
|
||||
let recorder = tracing_layer.layer(NoopRecorder);
|
||||
static LABELS: [Label; 1] = [Label::from_static_parts("foo", "bar")];
|
||||
static KEY_DATA: KeyData = KeyData::from_static_parts("key", &LABELS);
|
||||
static KEY_NAME: [SharedString; 1] = [SharedString::const_str("key")];
|
||||
static KEY_LABELS: [Label; 1] = [Label::from_static_parts("foo", "bar")];
|
||||
static KEY_DATA: KeyData = KeyData::from_static_parts(&KEY_NAME, &KEY_LABELS);
|
||||
|
||||
b.iter(|| {
|
||||
recorder.increment_counter(Key::Borrowed(&KEY_DATA), 1);
|
||||
|
@ -32,8 +33,9 @@ fn layer_benchmark(c: &mut Criterion) {
|
|||
})
|
||||
.with_function("noop recorder overhead (increment_counter)", |b| {
|
||||
let recorder = NoopRecorder;
|
||||
static LABELS: [Label; 1] = [Label::from_static_parts("foo", "bar")];
|
||||
static KEY_DATA: KeyData = KeyData::from_static_parts("key", &LABELS);
|
||||
static KEY_NAME: [SharedString; 1] = [SharedString::const_str("key")];
|
||||
static KEY_LABELS: [Label; 1] = [Label::from_static_parts("foo", "bar")];
|
||||
static KEY_DATA: KeyData = KeyData::from_static_parts(&KEY_NAME, &KEY_LABELS);
|
||||
|
||||
b.iter(|| {
|
||||
recorder.increment_counter(Key::Borrowed(&KEY_DATA), 1);
|
||||
|
|
|
@ -101,7 +101,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
/// [`TracingContext`] is a [`metrics::Recorder`] that injects labels from [`tracing::Span`]s.
|
||||
/// [`TracingContext`] is a [`metrics::Recorder`] that injects labels from[`tracing::Span`]s.
|
||||
pub struct TracingContext<R, F> {
|
||||
inner: R,
|
||||
label_filter: F,
|
||||
|
|
|
@ -20,7 +20,7 @@ impl Visit for Labels {
|
|||
}
|
||||
|
||||
fn record_bool(&mut self, field: &Field, value: bool) {
|
||||
let label = Label::new(field.name(), if value { "true" } else { "false" });
|
||||
let label = Label::from_static_parts(field.name(), if value { "true" } else { "false" });
|
||||
self.0.push(label);
|
||||
}
|
||||
|
||||
|
|
|
@ -26,11 +26,19 @@ harness = false
|
|||
name = "registry"
|
||||
harness = false
|
||||
|
||||
[[bench]]
|
||||
name = "prefix"
|
||||
harness = false
|
||||
|
||||
[[bench]]
|
||||
name = "filter"
|
||||
harness = false
|
||||
|
||||
[dependencies]
|
||||
metrics = { version = "0.13.0-alpha.1", path = "../metrics", features = ["std"] }
|
||||
crossbeam-epoch = { version = "0.9", optional = true }
|
||||
crossbeam-utils = { version = "0.8", default-features = false }
|
||||
arc-swap = { version = "0.4", optional = true }
|
||||
arc-swap = { version = "1.0", optional = true }
|
||||
atomic-shim = { version = "0.1", optional = true }
|
||||
aho-corasick = { version = "0.7", optional = true }
|
||||
dashmap = { version = "3", optional = true }
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
use criterion::{criterion_group, criterion_main, Benchmark, Criterion};
|
||||
use metrics::{Key, KeyData, Label, NoopRecorder, Recorder, SharedString};
|
||||
use metrics_util::layers::{FilterLayer, Layer};
|
||||
|
||||
fn layer_benchmark(c: &mut Criterion) {
|
||||
c.bench(
|
||||
"filter",
|
||||
Benchmark::new("match", |b| {
|
||||
let patterns = vec!["tokio"];
|
||||
let filter_layer = FilterLayer::from_patterns(patterns);
|
||||
let recorder = filter_layer.layer(NoopRecorder);
|
||||
static KEY_NAME: [SharedString; 1] = [SharedString::const_str("tokio.foo")];
|
||||
static KEY_LABELS: [Label; 1] = [Label::from_static_parts("foo", "bar")];
|
||||
static KEY_DATA: KeyData = KeyData::from_static_parts(&KEY_NAME, &KEY_LABELS);
|
||||
|
||||
b.iter(|| {
|
||||
recorder.increment_counter(Key::Borrowed(&KEY_DATA), 1);
|
||||
})
|
||||
})
|
||||
.with_function("no match", |b| {
|
||||
let patterns = vec!["tokio"];
|
||||
let filter_layer = FilterLayer::from_patterns(patterns);
|
||||
let recorder = filter_layer.layer(NoopRecorder);
|
||||
static KEY_NAME: [SharedString; 1] = [SharedString::const_str("hyper.foo")];
|
||||
static KEY_LABELS: [Label; 1] = [Label::from_static_parts("foo", "bar")];
|
||||
static KEY_DATA: KeyData = KeyData::from_static_parts(&KEY_NAME, &KEY_LABELS);
|
||||
|
||||
b.iter(|| {
|
||||
recorder.increment_counter(Key::Borrowed(&KEY_DATA), 1);
|
||||
})
|
||||
})
|
||||
.with_function("deep match", |b| {
|
||||
let patterns = vec!["tokio"];
|
||||
let filter_layer = FilterLayer::from_patterns(patterns);
|
||||
let recorder = filter_layer.layer(NoopRecorder);
|
||||
static KEY_NAME: [SharedString; 2] = [
|
||||
SharedString::const_str("prefix"),
|
||||
SharedString::const_str("tokio.foo"),
|
||||
];
|
||||
static KEY_LABELS: [Label; 1] = [Label::from_static_parts("foo", "bar")];
|
||||
static KEY_DATA: KeyData = KeyData::from_static_parts(&KEY_NAME, &KEY_LABELS);
|
||||
|
||||
b.iter(|| {
|
||||
recorder.increment_counter(Key::Borrowed(&KEY_DATA), 1);
|
||||
})
|
||||
})
|
||||
.with_function("noop recorder overhead (increment_counter)", |b| {
|
||||
let recorder = NoopRecorder;
|
||||
static KEY_NAME: [SharedString; 1] = [SharedString::const_str("tokio.foo")];
|
||||
static KEY_LABELS: [Label; 1] = [Label::from_static_parts("foo", "bar")];
|
||||
static KEY_DATA: KeyData = KeyData::from_static_parts(&KEY_NAME, &KEY_LABELS);
|
||||
|
||||
b.iter(|| {
|
||||
recorder.increment_counter(Key::Borrowed(&KEY_DATA), 1);
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
criterion_group!(benches, layer_benchmark);
|
||||
criterion_main!(benches);
|
|
@ -0,0 +1,33 @@
|
|||
use criterion::{criterion_group, criterion_main, Benchmark, Criterion};
|
||||
use metrics::{Key, KeyData, Label, NoopRecorder, Recorder, SharedString};
|
||||
use metrics_util::layers::{Layer, PrefixLayer};
|
||||
|
||||
fn layer_benchmark(c: &mut Criterion) {
|
||||
c.bench(
|
||||
"prefix",
|
||||
Benchmark::new("basic", |b| {
|
||||
let prefix_layer = PrefixLayer::new("prefix");
|
||||
let recorder = prefix_layer.layer(NoopRecorder);
|
||||
static KEY_NAME: [SharedString; 1] = [SharedString::const_str("simple_key")];
|
||||
static KEY_LABELS: [Label; 1] = [Label::from_static_parts("foo", "bar")];
|
||||
static KEY_DATA: KeyData = KeyData::from_static_parts(&KEY_NAME, &KEY_LABELS);
|
||||
|
||||
b.iter(|| {
|
||||
recorder.increment_counter(Key::Borrowed(&KEY_DATA), 1);
|
||||
})
|
||||
})
|
||||
.with_function("noop recorder overhead (increment_counter)", |b| {
|
||||
let recorder = NoopRecorder;
|
||||
static KEY_NAME: [SharedString; 1] = [SharedString::const_str("simple_key")];
|
||||
static KEY_LABELS: [Label; 1] = [Label::from_static_parts("foo", "bar")];
|
||||
static KEY_DATA: KeyData = KeyData::from_static_parts(&KEY_NAME, &KEY_LABELS);
|
||||
|
||||
b.iter(|| {
|
||||
recorder.increment_counter(Key::Borrowed(&KEY_DATA), 1);
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
criterion_group!(benches, layer_benchmark);
|
||||
criterion_main!(benches);
|
|
@ -1,5 +1,5 @@
|
|||
use criterion::{criterion_group, criterion_main, BatchSize, Benchmark, Criterion};
|
||||
use metrics::{Key, KeyData, Label};
|
||||
use metrics::{Key, KeyData, Label, SharedString};
|
||||
use metrics_util::Registry;
|
||||
|
||||
fn registry_benchmark(c: &mut Criterion) {
|
||||
|
@ -7,7 +7,8 @@ fn registry_benchmark(c: &mut Criterion) {
|
|||
"registry",
|
||||
Benchmark::new("cached op (basic)", |b| {
|
||||
let registry: Registry<Key, ()> = Registry::new();
|
||||
static KEY_DATA: KeyData = KeyData::from_static_name("simple_key");
|
||||
static KEY_NAME: [SharedString; 1] = [SharedString::const_str("simple_key")];
|
||||
static KEY_DATA: KeyData = KeyData::from_static_name(&KEY_NAME);
|
||||
|
||||
b.iter(|| {
|
||||
let key = Key::Borrowed(&KEY_DATA);
|
||||
|
@ -16,8 +17,9 @@ fn registry_benchmark(c: &mut Criterion) {
|
|||
})
|
||||
.with_function("cached op (labels)", |b| {
|
||||
let registry: Registry<Key, ()> = Registry::new();
|
||||
static KEY_NAME: [SharedString; 1] = [SharedString::const_str("simple_key")];
|
||||
static KEY_LABELS: [Label; 1] = [Label::from_static_parts("type", "http")];
|
||||
static KEY_DATA: KeyData = KeyData::from_static_parts("simple_key", &KEY_LABELS);
|
||||
static KEY_DATA: KeyData = KeyData::from_static_parts(&KEY_NAME, &KEY_LABELS);
|
||||
|
||||
b.iter(|| {
|
||||
let key = Key::Borrowed(&KEY_DATA);
|
||||
|
@ -67,15 +69,15 @@ fn registry_benchmark(c: &mut Criterion) {
|
|||
})
|
||||
.with_function("const key data overhead (basic)", |b| {
|
||||
b.iter(|| {
|
||||
let key = "simple_key";
|
||||
KeyData::from_static_name(key)
|
||||
static KEY_NAME: [SharedString; 1] = [SharedString::const_str("simple_key")];
|
||||
KeyData::from_static_name(&KEY_NAME)
|
||||
})
|
||||
})
|
||||
.with_function("const key data overhead (labels)", |b| {
|
||||
b.iter(|| {
|
||||
let key = "simple_key";
|
||||
static KEY_NAME: [SharedString; 1] = [SharedString::const_str("simple_key")];
|
||||
static LABELS: [Label; 1] = [Label::from_static_parts("type", "http")];
|
||||
KeyData::from_static_parts(key, &LABELS)
|
||||
KeyData::from_static_parts(&KEY_NAME, &LABELS)
|
||||
})
|
||||
})
|
||||
.with_function("owned key overhead (basic)", |b| {
|
||||
|
@ -92,12 +94,14 @@ fn registry_benchmark(c: &mut Criterion) {
|
|||
})
|
||||
})
|
||||
.with_function("cached key overhead (basic)", |b| {
|
||||
static KEY_DATA: KeyData = KeyData::from_static_name("simple_key");
|
||||
static KEY_NAME: [SharedString; 1] = [SharedString::const_str("simple_key")];
|
||||
static KEY_DATA: KeyData = KeyData::from_static_name(&KEY_NAME);
|
||||
b.iter(|| Key::Borrowed(&KEY_DATA))
|
||||
})
|
||||
.with_function("cached key overhead (labels)", |b| {
|
||||
static KEY_NAME: [SharedString; 1] = [SharedString::const_str("simple_key")];
|
||||
static KEY_LABELS: [Label; 1] = [Label::from_static_parts("type", "http")];
|
||||
static KEY_DATA: KeyData = KeyData::from_static_parts("simple_key", &KEY_LABELS);
|
||||
static KEY_DATA: KeyData = KeyData::from_static_parts(&KEY_NAME, &KEY_LABELS);
|
||||
b.iter(|| Key::Borrowed(&KEY_DATA))
|
||||
}),
|
||||
);
|
||||
|
|
|
@ -5,14 +5,19 @@ use std::{
|
|||
sync::atomic::{AtomicUsize, Ordering},
|
||||
};
|
||||
|
||||
const BLOCK_SIZE: usize = 512;
|
||||
#[cfg(target_pointer_width = "16")]
|
||||
const BLOCK_SIZE: usize = 16;
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
const BLOCK_SIZE: usize = 32;
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
const BLOCK_SIZE: usize = 64;
|
||||
|
||||
/// Discrete chunk of values with atomic read/write access.
|
||||
struct Block<T> {
|
||||
// Write index.
|
||||
write: AtomicUsize,
|
||||
|
||||
// Read index.
|
||||
// Read bitmap.
|
||||
read: AtomicUsize,
|
||||
|
||||
// The individual slots.
|
||||
|
@ -35,7 +40,7 @@ impl<T> Block<T> {
|
|||
|
||||
/// Gets the current length of this block.
|
||||
pub fn len(&self) -> usize {
|
||||
self.read.load(Ordering::Acquire)
|
||||
self.read.load(Ordering::Acquire).trailing_ones() as usize
|
||||
}
|
||||
|
||||
/// Gets a slice of the data written to this block.
|
||||
|
@ -71,7 +76,7 @@ impl<T> Block<T> {
|
|||
}
|
||||
|
||||
// Scoot our read index forward.
|
||||
self.read.fetch_add(1, Ordering::AcqRel);
|
||||
self.read.fetch_or(1 << index, Ordering::AcqRel);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -324,6 +329,7 @@ mod tests {
|
|||
|
||||
let result = block.push(42);
|
||||
assert!(result.is_ok());
|
||||
assert_eq!(block.len(), 1);
|
||||
|
||||
let data = block.data();
|
||||
assert_eq!(data.len(), 1);
|
||||
|
|
|
@ -13,7 +13,9 @@ pub struct Filter<R> {
|
|||
|
||||
impl<R> Filter<R> {
|
||||
fn should_filter(&self, key: &Key) -> bool {
|
||||
self.automaton.is_match(key.name().as_ref())
|
||||
key.name()
|
||||
.parts()
|
||||
.any(|s| self.automaton.is_match(s.as_ref()))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -75,11 +77,14 @@ impl FilterLayer {
|
|||
/// Creates a `FilterLayer` from an existing set of patterns.
|
||||
pub fn from_patterns<P, I>(patterns: P) -> Self
|
||||
where
|
||||
P: Iterator<Item = I>,
|
||||
P: IntoIterator<Item = I>,
|
||||
I: AsRef<str>,
|
||||
{
|
||||
FilterLayer {
|
||||
patterns: patterns.map(|s| s.as_ref().to_string()).collect(),
|
||||
patterns: patterns
|
||||
.into_iter()
|
||||
.map(|s| s.as_ref().to_string())
|
||||
.collect(),
|
||||
case_insensitive: false,
|
||||
use_dfa: true,
|
||||
}
|
||||
|
@ -142,7 +147,7 @@ mod tests {
|
|||
let patterns = &["tokio", "bb8"];
|
||||
let recorder = DebuggingRecorder::new();
|
||||
let snapshotter = recorder.snapshotter();
|
||||
let filter = FilterLayer::from_patterns(patterns.iter());
|
||||
let filter = FilterLayer::from_patterns(patterns);
|
||||
let layered = filter.layer(recorder);
|
||||
|
||||
let before = snapshotter.snapshot();
|
||||
|
@ -186,7 +191,10 @@ mod tests {
|
|||
assert_eq!(after.len(), 2);
|
||||
|
||||
for (_kind, key, unit, desc, _value) in after {
|
||||
assert!(!key.name().contains("tokio") && !key.name().contains("bb8"));
|
||||
assert!(
|
||||
!key.name().to_string().contains("tokio")
|
||||
&& !key.name().to_string().contains("bb8")
|
||||
);
|
||||
// We cheat here since we're not comparing one-to-one with the source data,
|
||||
// but we know which metrics are going to make it through so we can hard code.
|
||||
assert_eq!(Some(Unit::Bytes), unit);
|
||||
|
@ -217,8 +225,8 @@ mod tests {
|
|||
|
||||
for (_kind, key, _unit, _desc, _value) in &after {
|
||||
assert!(
|
||||
!key.name().to_lowercase().contains("tokio")
|
||||
&& !key.name().to_lowercase().contains("bb8")
|
||||
!key.name().to_string().to_lowercase().contains("tokio")
|
||||
&& !key.name().to_string().to_lowercase().contains("bb8")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,12 @@
|
|||
//!
|
||||
//! impl<R> StairwayDeny<R> {
|
||||
//! fn is_invalid_key(&self, key: &Key) -> bool {
|
||||
//! key.name().contains("stairway") || key.name().contains("heaven")
|
||||
//! for part in key.name().parts() {
|
||||
//! if part.contains("stairway") || part.contains("heaven") {
|
||||
//! return true
|
||||
//! }
|
||||
//! }
|
||||
//! false
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
|
|
|
@ -1,19 +1,17 @@
|
|||
use crate::layers::Layer;
|
||||
use metrics::{Key, Recorder, Unit};
|
||||
use metrics::{Key, Recorder, SharedString, Unit};
|
||||
|
||||
/// Applies a prefix to every metric key.
|
||||
///
|
||||
/// Keys will be prefixed in the format of `<prefix>.<remaining>`.
|
||||
pub struct Prefix<R> {
|
||||
prefix: String,
|
||||
prefix: SharedString,
|
||||
inner: R,
|
||||
}
|
||||
|
||||
impl<R> Prefix<R> {
|
||||
fn prefix_key(&self, key: Key) -> Key {
|
||||
key.into_owned()
|
||||
.map_name(|old| format!("{}.{}", self.prefix, old))
|
||||
.into()
|
||||
key.into_owned().prepend_name(self.prefix.clone()).into()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -52,12 +50,12 @@ impl<R: Recorder> Recorder for Prefix<R> {
|
|||
/// A layer for applying a prefix to every metric key.
|
||||
///
|
||||
/// More information on the behavior of the layer can be found in [`Prefix`].
|
||||
pub struct PrefixLayer(String);
|
||||
pub struct PrefixLayer(&'static str);
|
||||
|
||||
impl PrefixLayer {
|
||||
/// Creates a new `PrefixLayer` based on the given prefix.
|
||||
pub fn new<S: Into<String>>(prefix: S) -> PrefixLayer {
|
||||
PrefixLayer(prefix.into())
|
||||
PrefixLayer(Box::leak(prefix.into().into_boxed_str()))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -66,7 +64,7 @@ impl<R> Layer<R> for PrefixLayer {
|
|||
|
||||
fn layer(&self, inner: R) -> Self::Output {
|
||||
Prefix {
|
||||
prefix: self.0.clone(),
|
||||
prefix: self.0.into(),
|
||||
inner,
|
||||
}
|
||||
}
|
||||
|
@ -115,7 +113,7 @@ mod tests {
|
|||
assert_eq!(after.len(), 3);
|
||||
|
||||
for (i, (_kind, key, unit, desc, _value)) in after.iter().enumerate() {
|
||||
assert!(key.name().starts_with("testing"));
|
||||
assert!(key.name().to_string().starts_with("testing"));
|
||||
assert_eq!(&Some(ud[i].0.clone()), unit);
|
||||
assert_eq!(&Some(ud[i].1), desc);
|
||||
}
|
||||
|
|
|
@ -27,10 +27,15 @@ bench = false
|
|||
name = "macros"
|
||||
harness = false
|
||||
|
||||
[[bench]]
|
||||
name = "key"
|
||||
harness = false
|
||||
|
||||
[dependencies]
|
||||
beef = "0.4"
|
||||
metrics-macros = { version = "0.1.0-alpha.1", path = "../metrics-macros" }
|
||||
proc-macro-hack = "0.5"
|
||||
sharded-slab = "0.1"
|
||||
|
||||
[dev-dependencies]
|
||||
log = "0.4"
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
use criterion::{criterion_group, criterion_main, Benchmark, Criterion};
|
||||
|
||||
use metrics::{NameParts, SharedString};
|
||||
|
||||
fn key_benchmark(c: &mut Criterion) {
|
||||
c.bench(
|
||||
"key",
|
||||
Benchmark::new("name_parts/to_string", |b| {
|
||||
static NAME_PARTS: [SharedString; 2] = [
|
||||
SharedString::const_str("part1"),
|
||||
SharedString::const_str("part2"),
|
||||
];
|
||||
let name = NameParts::from_static_names(&NAME_PARTS);
|
||||
b.iter(|| name.to_string())
|
||||
})
|
||||
.with_function("name_parts/Display::to_string", |b| {
|
||||
static NAME_PARTS: [SharedString; 2] = [
|
||||
SharedString::const_str("part1"),
|
||||
SharedString::const_str("part2"),
|
||||
];
|
||||
let name = NameParts::from_static_names(&NAME_PARTS);
|
||||
b.iter(|| std::fmt::Display::to_string(&name))
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
criterion_group!(benches, key_benchmark);
|
||||
criterion_main!(benches);
|
|
@ -0,0 +1,22 @@
|
|||
//! This example is purely for development.
|
||||
use metrics::{Key, KeyData, Label, NameParts, SharedString};
|
||||
use std::borrow::Cow;
|
||||
|
||||
fn main() {
|
||||
println!("KeyData: {} bytes", std::mem::size_of::<KeyData>());
|
||||
println!("Key: {} bytes", std::mem::size_of::<Key>());
|
||||
println!("NameParts: {} bytes", std::mem::size_of::<NameParts>());
|
||||
println!("Label: {} bytes", std::mem::size_of::<Label>());
|
||||
println!(
|
||||
"Cow<'static, [Label]>: {} bytes",
|
||||
std::mem::size_of::<Cow<'static, [Label]>>()
|
||||
);
|
||||
println!(
|
||||
"Vec<SharedString>: {} bytes",
|
||||
std::mem::size_of::<Vec<SharedString>>()
|
||||
);
|
||||
println!(
|
||||
"[Option<SharedString>; 2]: {} bytes",
|
||||
std::mem::size_of::<[Option<SharedString>; 2]>()
|
||||
);
|
||||
}
|
|
@ -1,13 +1,13 @@
|
|||
#[cfg(target_pointer_width = "64")]
|
||||
use beef::lean::Cow;
|
||||
#[cfg(not(target_pointer_width = "64"))]
|
||||
use beef::Cow;
|
||||
use crate::cow::Cow;
|
||||
|
||||
/// An allocation-optimized string.
|
||||
///
|
||||
/// 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.
|
||||
///
|
||||
/// `SharedString` can be converted to from either `&'static str` or `String`, with a method,
|
||||
/// `const_str`, from constructing `SharedString` from `&'static str` in a `const` fashion.
|
||||
pub type SharedString = Cow<'static, str>;
|
||||
|
||||
/// Units for a given metric.
|
||||
|
@ -16,61 +16,57 @@ pub type SharedString = Cow<'static, str>;
|
|||
/// downstream systems natively support defining units and so they can be specified during registration.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Unit {
|
||||
// Dimensionless measurements.
|
||||
/// Count.
|
||||
Count,
|
||||
/// Percentage.
|
||||
Percent,
|
||||
// Time measurements.
|
||||
/// Seconds.
|
||||
///
|
||||
/// One second is equal to 1000 milliseconds.
|
||||
Seconds,
|
||||
/// Milliseconds.
|
||||
///
|
||||
/// One millisecond is equal to 1000 microseconds.
|
||||
Milliseconds,
|
||||
/// Microseconds.
|
||||
///
|
||||
/// One microsecond is equal to 1000 nanoseconds.
|
||||
Microseconds,
|
||||
/// Nanoseconds.
|
||||
Nanoseconds,
|
||||
|
||||
// Data measurement.
|
||||
/// Terabytes.
|
||||
Terabytes,
|
||||
/// Gigabytes.
|
||||
Gigabytes,
|
||||
/// Megabytes.
|
||||
Megabytes,
|
||||
/// Kilobytes.
|
||||
Kilobytes,
|
||||
/// Tebibytes.
|
||||
///
|
||||
/// One tebibyte is equal to 1024 gigibytes.
|
||||
Tebibytes,
|
||||
/// Gigibytes.
|
||||
///
|
||||
/// One gigibyte is equal to 1024 mebibytes.
|
||||
Gigibytes,
|
||||
/// Mebibytes.
|
||||
///
|
||||
/// One mebibyte is equal to 1024 kibibytes.
|
||||
Mebibytes,
|
||||
/// Kibibytes.
|
||||
///
|
||||
/// One kibibyte is equal to 1024 bytes.
|
||||
Kibibytes,
|
||||
/// Bytes.
|
||||
Bytes,
|
||||
/// Terabits.
|
||||
Terabits,
|
||||
/// Gigabits.
|
||||
Gigabits,
|
||||
/// Megabits.
|
||||
Megabits,
|
||||
/// Kilobits.
|
||||
Kilobits,
|
||||
/// Bits.
|
||||
Bits,
|
||||
|
||||
// Rate measurements.
|
||||
/// Terabytes per second.
|
||||
TerabytesPerSecond,
|
||||
/// Gigabytes per second.
|
||||
GigabytesPerSecond,
|
||||
/// Megabytes per second.
|
||||
MegabytesPerSecond,
|
||||
/// Kilobytes per second.
|
||||
KilobytesPerSecond,
|
||||
/// Bytes per second.
|
||||
BytesPerSecond,
|
||||
/// Terabits per second.
|
||||
///
|
||||
/// One terabit is equal to 1000 gigabits.
|
||||
TerabitsPerSecond,
|
||||
/// Gigabits per second.
|
||||
///
|
||||
/// One gigabit is equal to 1000 megabits.
|
||||
GigabitsPerSecond,
|
||||
/// Megabits per second.
|
||||
///
|
||||
/// One megabit is equal to 1000 kilobits.
|
||||
MegabitsPerSecond,
|
||||
/// Kilobits per second.
|
||||
///
|
||||
/// One kilobit is equal to 1000 bits.
|
||||
KilobitsPerSecond,
|
||||
/// Bits per second.
|
||||
BitsPerSecond,
|
||||
|
@ -88,21 +84,11 @@ impl Unit {
|
|||
Unit::Milliseconds => "milliseconds",
|
||||
Unit::Microseconds => "microseconds",
|
||||
Unit::Nanoseconds => "nanoseconds",
|
||||
Unit::Terabytes => "terabytes",
|
||||
Unit::Gigabytes => "gigabytes",
|
||||
Unit::Megabytes => "megabytes",
|
||||
Unit::Kilobytes => "kilobytes",
|
||||
Unit::Tebibytes => "tebibytes",
|
||||
Unit::Gigibytes => "gigibytes",
|
||||
Unit::Mebibytes => "mebibytes",
|
||||
Unit::Kibibytes => "kibibytes",
|
||||
Unit::Bytes => "bytes",
|
||||
Unit::Terabits => "terabits",
|
||||
Unit::Gigabits => "gigabits",
|
||||
Unit::Megabits => "megabits",
|
||||
Unit::Kilobits => "kilobits",
|
||||
Unit::Bits => "bits",
|
||||
Unit::TerabytesPerSecond => "terabytes_per_second",
|
||||
Unit::GigabytesPerSecond => "gigabytes_per_second",
|
||||
Unit::MegabytesPerSecond => "megabytes_per_second",
|
||||
Unit::KilobytesPerSecond => "kilobytes_per_second",
|
||||
Unit::BytesPerSecond => "bytes_per_second",
|
||||
Unit::TerabitsPerSecond => "terabits_per_second",
|
||||
Unit::GigabitsPerSecond => "gigabits_per_second",
|
||||
Unit::MegabitsPerSecond => "megabits_per_second",
|
||||
|
@ -126,25 +112,15 @@ impl Unit {
|
|||
Unit::Milliseconds => "ms",
|
||||
Unit::Microseconds => "us",
|
||||
Unit::Nanoseconds => "ns",
|
||||
Unit::Terabytes => "TB",
|
||||
Unit::Gigabytes => "Gb",
|
||||
Unit::Megabytes => "MB",
|
||||
Unit::Kilobytes => "KB",
|
||||
Unit::Tebibytes => "TiB",
|
||||
Unit::Gigibytes => "GiB",
|
||||
Unit::Mebibytes => "MiB",
|
||||
Unit::Kibibytes => "KiB",
|
||||
Unit::Bytes => "B",
|
||||
Unit::Terabits => "Tb",
|
||||
Unit::Gigabits => "Gb",
|
||||
Unit::Megabits => "Mb",
|
||||
Unit::Kilobits => "Kb",
|
||||
Unit::Bits => "b",
|
||||
Unit::TerabytesPerSecond => "TBps",
|
||||
Unit::GigabytesPerSecond => "GBps",
|
||||
Unit::MegabytesPerSecond => "MBps",
|
||||
Unit::KilobytesPerSecond => "KBps",
|
||||
Unit::BytesPerSecond => "Bps",
|
||||
Unit::TerabitsPerSecond => "Tbps",
|
||||
Unit::GigabitsPerSecond => "Gbps",
|
||||
Unit::MegabitsPerSecond => "Mbps",
|
||||
Unit::KilobitsPerSecond => "Kbps",
|
||||
Unit::KilobitsPerSecond => "kbps",
|
||||
Unit::BitsPerSecond => "bps",
|
||||
Unit::CountPerSecond => "/s",
|
||||
}
|
||||
|
@ -161,21 +137,11 @@ impl Unit {
|
|||
"milliseconds" => Some(Unit::Milliseconds),
|
||||
"microseconds" => Some(Unit::Microseconds),
|
||||
"nanoseconds" => Some(Unit::Nanoseconds),
|
||||
"terabytes" => Some(Unit::Terabytes),
|
||||
"gigabytes" => Some(Unit::Gigabytes),
|
||||
"megabytes" => Some(Unit::Megabytes),
|
||||
"kilobytes" => Some(Unit::Kilobytes),
|
||||
"tebibytes" => Some(Unit::Tebibytes),
|
||||
"gigibytes" => Some(Unit::Gigibytes),
|
||||
"mebibytes" => Some(Unit::Mebibytes),
|
||||
"kibibytes" => Some(Unit::Kibibytes),
|
||||
"bytes" => Some(Unit::Bytes),
|
||||
"terabits" => Some(Unit::Terabits),
|
||||
"gigabits" => Some(Unit::Gigabits),
|
||||
"megabits" => Some(Unit::Megabits),
|
||||
"kilobits" => Some(Unit::Kilobits),
|
||||
"bits" => Some(Unit::Bits),
|
||||
"terabytes_per_second" => Some(Unit::TerabytesPerSecond),
|
||||
"gigabytes_per_second" => Some(Unit::GigabytesPerSecond),
|
||||
"megabytes_per_second" => Some(Unit::MegabytesPerSecond),
|
||||
"kilobytes_per_second" => Some(Unit::KilobytesPerSecond),
|
||||
"bytes_per_second" => Some(Unit::BytesPerSecond),
|
||||
"terabits_per_second" => Some(Unit::TerabitsPerSecond),
|
||||
"gigabits_per_second" => Some(Unit::GigabitsPerSecond),
|
||||
"megabits_per_second" => Some(Unit::MegabitsPerSecond),
|
||||
|
@ -197,21 +163,11 @@ impl Unit {
|
|||
/// Whether or not this unit relates to the measurement of data.
|
||||
pub fn is_data_based(&self) -> bool {
|
||||
match self {
|
||||
Unit::Terabytes
|
||||
| Unit::Gigabytes
|
||||
| Unit::Megabytes
|
||||
| Unit::Kilobytes
|
||||
Unit::Tebibytes
|
||||
| Unit::Gigibytes
|
||||
| Unit::Mebibytes
|
||||
| Unit::Kibibytes
|
||||
| Unit::Bytes
|
||||
| Unit::Terabits
|
||||
| Unit::Gigabits
|
||||
| Unit::Megabits
|
||||
| Unit::Kilobits
|
||||
| Unit::Bits
|
||||
| Unit::TerabytesPerSecond
|
||||
| Unit::GigabytesPerSecond
|
||||
| Unit::MegabytesPerSecond
|
||||
| Unit::KilobytesPerSecond
|
||||
| Unit::BytesPerSecond
|
||||
| Unit::TerabitsPerSecond
|
||||
| Unit::GigabitsPerSecond
|
||||
| Unit::MegabitsPerSecond
|
||||
|
@ -224,13 +180,8 @@ impl Unit {
|
|||
/// Whether or not this unit relates to the measurement of data rates.
|
||||
pub fn is_data_rate_based(&self) -> bool {
|
||||
match self {
|
||||
Unit::TerabytesPerSecond
|
||||
| Unit::GigabytesPerSecond
|
||||
| Unit::MegabytesPerSecond
|
||||
| Unit::KilobytesPerSecond
|
||||
| Unit::BytesPerSecond
|
||||
| Unit::TerabitsPerSecond
|
||||
| Unit::Gigabits
|
||||
Unit::TerabitsPerSecond
|
||||
| Unit::GigabitsPerSecond
|
||||
| Unit::MegabitsPerSecond
|
||||
| Unit::KilobitsPerSecond
|
||||
| Unit::BitsPerSecond => true,
|
||||
|
@ -265,3 +216,37 @@ impl IntoU64 for core::time::Duration {
|
|||
pub fn __into_u64<V: IntoU64>(value: V) -> u64 {
|
||||
value.into_u64()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Unit;
|
||||
|
||||
#[test]
|
||||
fn test_unit_conversions() {
|
||||
let all_variants = vec![
|
||||
Unit::Count,
|
||||
Unit::Percent,
|
||||
Unit::Seconds,
|
||||
Unit::Milliseconds,
|
||||
Unit::Microseconds,
|
||||
Unit::Nanoseconds,
|
||||
Unit::Tebibytes,
|
||||
Unit::Gigibytes,
|
||||
Unit::Mebibytes,
|
||||
Unit::Kibibytes,
|
||||
Unit::Bytes,
|
||||
Unit::TerabitsPerSecond,
|
||||
Unit::GigabitsPerSecond,
|
||||
Unit::MegabitsPerSecond,
|
||||
Unit::KilobitsPerSecond,
|
||||
Unit::BitsPerSecond,
|
||||
Unit::CountPerSecond,
|
||||
];
|
||||
|
||||
for variant in all_variants {
|
||||
let s = variant.as_str();
|
||||
let parsed = Unit::from_str(s);
|
||||
assert_eq!(Some(variant), parsed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,515 @@
|
|||
use crate::label::Label;
|
||||
use alloc::borrow::Borrow;
|
||||
use alloc::string::String;
|
||||
use alloc::vec::Vec;
|
||||
use core::cmp::Ordering;
|
||||
use core::fmt;
|
||||
use core::hash::{Hash, Hasher};
|
||||
use core::marker::PhantomData;
|
||||
use core::mem::ManuallyDrop;
|
||||
use core::ptr::{slice_from_raw_parts, NonNull};
|
||||
|
||||
/// A clone-on-write smart pointer with an optimized memory layout.
|
||||
pub struct Cow<'a, T: Cowable + ?Sized + 'a> {
|
||||
/// Pointer to data.
|
||||
ptr: NonNull<T::Pointer>,
|
||||
|
||||
/// Pointer metadata: length and capacity.
|
||||
meta: Metadata,
|
||||
|
||||
/// Lifetime marker.
|
||||
marker: PhantomData<&'a T>,
|
||||
}
|
||||
|
||||
impl<T> Cow<'_, T>
|
||||
where
|
||||
T: Cowable + ?Sized,
|
||||
{
|
||||
#[inline]
|
||||
pub fn owned(val: T::Owned) -> Self {
|
||||
let (ptr, meta) = T::owned_into_parts(val);
|
||||
|
||||
Cow {
|
||||
ptr,
|
||||
meta,
|
||||
marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Cow<'a, T>
|
||||
where
|
||||
T: Cowable + ?Sized,
|
||||
{
|
||||
#[inline]
|
||||
pub fn borrowed(val: &'a T) -> Self {
|
||||
let (ptr, meta) = T::ref_into_parts(val);
|
||||
|
||||
Cow {
|
||||
ptr,
|
||||
meta,
|
||||
marker: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn into_owned(self) -> T::Owned {
|
||||
let cow = ManuallyDrop::new(self);
|
||||
|
||||
if cow.is_borrowed() {
|
||||
unsafe { T::clone_from_parts(cow.ptr, &cow.meta) }
|
||||
} else {
|
||||
unsafe { T::owned_from_parts(cow.ptr, &cow.meta) }
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_borrowed(&self) -> bool {
|
||||
self.meta.capacity() == 0
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_owned(&self) -> bool {
|
||||
self.meta.capacity() != 0
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn borrow(&self) -> &T {
|
||||
unsafe { &*T::ref_from_parts(self.ptr, &self.meta) }
|
||||
}
|
||||
}
|
||||
|
||||
// Implementations of constant functions for creating `Cow` via static strings, static string
|
||||
// slices, and static label slices.
|
||||
impl<'a> Cow<'a, str> {
|
||||
pub const fn const_str(val: &'a str) -> Self {
|
||||
Cow {
|
||||
// We are casting *const T to *mut T, however for all borrowed values
|
||||
// this raw pointer is only ever dereferenced back to &T.
|
||||
ptr: unsafe { NonNull::new_unchecked(val.as_ptr() as *mut u8) },
|
||||
meta: Metadata::from_ref(val.len()),
|
||||
marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Cow<'a, [Cow<'static, str>]> {
|
||||
pub const fn const_slice(val: &'a [Cow<'static, str>]) -> Self {
|
||||
Cow {
|
||||
ptr: unsafe { NonNull::new_unchecked(val.as_ptr() as *mut Cow<'static, str>) },
|
||||
meta: Metadata::from_ref(val.len()),
|
||||
marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Cow<'a, [Label]> {
|
||||
pub const fn const_slice(val: &'a [Label]) -> Self {
|
||||
Cow {
|
||||
ptr: unsafe { NonNull::new_unchecked(val.as_ptr() as *mut Label) },
|
||||
meta: Metadata::from_ref(val.len()),
|
||||
marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Hash for Cow<'_, T>
|
||||
where
|
||||
T: Hash + Cowable + ?Sized,
|
||||
{
|
||||
#[inline]
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.borrow().hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Default for Cow<'a, T>
|
||||
where
|
||||
T: Cowable + ?Sized,
|
||||
&'a T: Default,
|
||||
{
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
Cow::borrowed(Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Eq for Cow<'_, T> where T: Eq + Cowable + ?Sized {}
|
||||
|
||||
impl<A, B> PartialOrd<Cow<'_, B>> for Cow<'_, A>
|
||||
where
|
||||
A: Cowable + ?Sized + PartialOrd<B>,
|
||||
B: Cowable + ?Sized,
|
||||
{
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &Cow<'_, B>) -> Option<Ordering> {
|
||||
PartialOrd::partial_cmp(self.borrow(), other.borrow())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Ord for Cow<'_, T>
|
||||
where
|
||||
T: Ord + Cowable + ?Sized,
|
||||
{
|
||||
#[inline]
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
Ord::cmp(self.borrow(), other.borrow())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> From<&'a T> for Cow<'a, T>
|
||||
where
|
||||
T: Cowable + ?Sized,
|
||||
{
|
||||
#[inline]
|
||||
fn from(val: &'a T) -> Self {
|
||||
Cow::borrowed(val)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for Cow<'_, str> {
|
||||
#[inline]
|
||||
fn from(s: String) -> Self {
|
||||
Cow::owned(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<Label>> for Cow<'_, [Label]> {
|
||||
#[inline]
|
||||
fn from(v: Vec<Label>) -> Self {
|
||||
Cow::owned(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<Cow<'static, str>>> for Cow<'_, [Cow<'static, str>]> {
|
||||
#[inline]
|
||||
fn from(v: Vec<Cow<'static, str>>) -> Self {
|
||||
Cow::owned(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drop for Cow<'_, T>
|
||||
where
|
||||
T: Cowable + ?Sized,
|
||||
{
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
if self.is_owned() {
|
||||
unsafe { T::owned_from_parts(self.ptr, &self.meta) };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Clone for Cow<'a, T>
|
||||
where
|
||||
T: Cowable + ?Sized,
|
||||
{
|
||||
#[inline]
|
||||
fn clone(&self) -> Self {
|
||||
if self.is_owned() {
|
||||
// Gotta clone the actual inner value.
|
||||
Cow::owned(unsafe { T::clone_from_parts(self.ptr, &self.meta) })
|
||||
} else {
|
||||
Cow { ..*self }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> core::ops::Deref for Cow<'_, T>
|
||||
where
|
||||
T: Cowable + ?Sized,
|
||||
{
|
||||
type Target = T;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &T {
|
||||
self.borrow()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AsRef<T> for Cow<'_, T>
|
||||
where
|
||||
T: Cowable + ?Sized,
|
||||
{
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &T {
|
||||
self.borrow()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Borrow<T> for Cow<'_, T>
|
||||
where
|
||||
T: Cowable + ?Sized,
|
||||
{
|
||||
#[inline]
|
||||
fn borrow(&self) -> &T {
|
||||
self.borrow()
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, B> PartialEq<Cow<'_, B>> for Cow<'_, A>
|
||||
where
|
||||
A: Cowable + ?Sized,
|
||||
B: Cowable + ?Sized,
|
||||
A: PartialEq<B>,
|
||||
{
|
||||
fn eq(&self, other: &Cow<B>) -> bool {
|
||||
self.borrow() == other.borrow()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> fmt::Debug for Cow<'_, T>
|
||||
where
|
||||
T: Cowable + fmt::Debug + ?Sized,
|
||||
{
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.borrow().fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> fmt::Display for Cow<'_, T>
|
||||
where
|
||||
T: Cowable + fmt::Display + ?Sized,
|
||||
{
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.borrow().fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T: Cowable + Sync + ?Sized> Sync for Cow<'_, T> {}
|
||||
unsafe impl<T: Cowable + Send + ?Sized> Send for Cow<'_, T> {}
|
||||
|
||||
/// Helper trait required by `Cow<T>` to extract capacity of owned
|
||||
/// variant of `T`, and manage conversions.
|
||||
///
|
||||
/// This can be only implemented on types that match requirements:
|
||||
///
|
||||
/// + `T::Owned` has a `capacity`, which is an extra word that is absent in `T`.
|
||||
/// + `T::Owned` with `capacity` of `0` does not allocate memory.
|
||||
/// + `T::Owned` can be reconstructed from `*mut T` borrowed out of it, plus capacity.
|
||||
pub unsafe trait Cowable {
|
||||
type Pointer;
|
||||
type Owned;
|
||||
|
||||
fn ref_into_parts(&self) -> (NonNull<Self::Pointer>, Metadata);
|
||||
fn owned_into_parts(owned: Self::Owned) -> (NonNull<Self::Pointer>, Metadata);
|
||||
|
||||
unsafe fn ref_from_parts(ptr: NonNull<Self::Pointer>, metadata: &Metadata) -> *const Self;
|
||||
unsafe fn owned_from_parts(ptr: NonNull<Self::Pointer>, metadata: &Metadata) -> Self::Owned;
|
||||
unsafe fn clone_from_parts(ptr: NonNull<Self::Pointer>, metadata: &Metadata) -> Self::Owned;
|
||||
}
|
||||
|
||||
unsafe impl Cowable for str {
|
||||
type Pointer = u8;
|
||||
type Owned = String;
|
||||
|
||||
#[inline]
|
||||
fn ref_into_parts(&self) -> (NonNull<u8>, Metadata) {
|
||||
// A note on soundness:
|
||||
//
|
||||
// We are casting *const T to *mut T, however for all borrowed values
|
||||
// this raw pointer is only ever dereferenced back to &T.
|
||||
let ptr = unsafe { NonNull::new_unchecked(self.as_ptr() as *mut _) };
|
||||
let metadata = Metadata::from_ref(self.len());
|
||||
(ptr, metadata)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn ref_from_parts(ptr: NonNull<u8>, metadata: &Metadata) -> *const str {
|
||||
slice_from_raw_parts(ptr.as_ptr(), metadata.length()) as *const _
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn owned_into_parts(owned: String) -> (NonNull<u8>, Metadata) {
|
||||
let mut owned = ManuallyDrop::new(owned);
|
||||
let ptr = unsafe { NonNull::new_unchecked(owned.as_mut_ptr()) };
|
||||
let metadata = Metadata::from_owned(owned.len(), owned.capacity());
|
||||
(ptr, metadata)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn owned_from_parts(ptr: NonNull<u8>, metadata: &Metadata) -> String {
|
||||
String::from_utf8_unchecked(Vec::from_raw_parts(
|
||||
ptr.as_ptr(),
|
||||
metadata.length(),
|
||||
metadata.capacity(),
|
||||
))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn clone_from_parts(ptr: NonNull<u8>, metadata: &Metadata) -> Self::Owned {
|
||||
let str = Self::ref_from_parts(ptr, metadata);
|
||||
str.as_ref().unwrap().to_string()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a> Cowable for [Cow<'a, str>] {
|
||||
type Pointer = Cow<'a, str>;
|
||||
type Owned = Vec<Cow<'a, str>>;
|
||||
|
||||
#[inline]
|
||||
fn ref_into_parts(&self) -> (NonNull<Cow<'a, str>>, Metadata) {
|
||||
// A note on soundness:
|
||||
//
|
||||
// We are casting *const T to *mut T, however for all borrowed values
|
||||
// this raw pointer is only ever dereferenced back to &T.
|
||||
let ptr = unsafe { NonNull::new_unchecked(self.as_ptr() as *mut _) };
|
||||
let metadata = Metadata::from_ref(self.len());
|
||||
(ptr, metadata)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn ref_from_parts(
|
||||
ptr: NonNull<Cow<'a, str>>,
|
||||
metadata: &Metadata,
|
||||
) -> *const [Cow<'a, str>] {
|
||||
slice_from_raw_parts(ptr.as_ptr(), metadata.length())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn owned_into_parts(owned: Vec<Cow<'a, str>>) -> (NonNull<Cow<'a, str>>, Metadata) {
|
||||
let mut owned = ManuallyDrop::new(owned);
|
||||
let ptr = unsafe { NonNull::new_unchecked(owned.as_mut_ptr()) };
|
||||
let metadata = Metadata::from_owned(owned.len(), owned.capacity());
|
||||
(ptr, metadata)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn owned_from_parts(
|
||||
ptr: NonNull<Cow<'a, str>>,
|
||||
metadata: &Metadata,
|
||||
) -> Vec<Cow<'a, str>> {
|
||||
Vec::from_raw_parts(ptr.as_ptr(), metadata.length(), metadata.capacity())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn clone_from_parts(ptr: NonNull<Cow<'a, str>>, metadata: &Metadata) -> Self::Owned {
|
||||
let ptr = Self::ref_from_parts(ptr, metadata);
|
||||
let xs = ptr.as_ref().unwrap();
|
||||
let mut owned = Vec::with_capacity(xs.len() + 1);
|
||||
owned.extend_from_slice(xs);
|
||||
owned
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Cowable for [Label] {
|
||||
type Pointer = Label;
|
||||
type Owned = Vec<Label>;
|
||||
|
||||
#[inline]
|
||||
fn ref_into_parts(&self) -> (NonNull<Label>, Metadata) {
|
||||
// A note on soundness:
|
||||
//
|
||||
// We are casting *const T to *mut T, however for all borrowed values
|
||||
// this raw pointer is only ever dereferenced back to &T.
|
||||
let ptr = unsafe { NonNull::new_unchecked(self.as_ptr() as *mut _) };
|
||||
let metadata = Metadata::from_ref(self.len());
|
||||
(ptr, metadata)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn ref_from_parts(ptr: NonNull<Label>, metadata: &Metadata) -> *const [Label] {
|
||||
slice_from_raw_parts(ptr.as_ptr(), metadata.length())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn owned_into_parts(owned: Vec<Label>) -> (NonNull<Label>, Metadata) {
|
||||
let mut owned = ManuallyDrop::new(owned);
|
||||
let ptr = unsafe { NonNull::new_unchecked(owned.as_mut_ptr()) };
|
||||
let metadata = Metadata::from_owned(owned.len(), owned.capacity());
|
||||
(ptr, metadata)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn owned_from_parts(ptr: NonNull<Label>, metadata: &Metadata) -> Vec<Label> {
|
||||
Vec::from_raw_parts(ptr.as_ptr(), metadata.length(), metadata.capacity())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn clone_from_parts(ptr: NonNull<Label>, metadata: &Metadata) -> Self::Owned {
|
||||
let xs = Self::ref_from_parts(ptr, metadata);
|
||||
xs.as_ref().unwrap().to_vec()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
pub struct Metadata(usize, usize);
|
||||
|
||||
impl Metadata {
|
||||
#[inline]
|
||||
fn length(&self) -> usize {
|
||||
self.0
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn capacity(&self) -> usize {
|
||||
self.1
|
||||
}
|
||||
|
||||
pub const fn from_ref(len: usize) -> Metadata {
|
||||
Metadata(len, 0)
|
||||
}
|
||||
|
||||
pub const fn from_owned(len: usize, capacity: usize) -> Metadata {
|
||||
Metadata(len, capacity)
|
||||
}
|
||||
|
||||
pub const fn borrowed() -> Metadata {
|
||||
Metadata(0, 0)
|
||||
}
|
||||
|
||||
pub const fn owned() -> Metadata {
|
||||
Metadata(0, 1)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
This can be enabled again when we have a way to do panics/asserts in stable Rust,
|
||||
since const panicking is behind a feature flag at the moment.
|
||||
|
||||
const MASK_LO: usize = u32::MAX as usize;
|
||||
const MASK_HI: usize = !MASK_LO;
|
||||
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
impl Metadata {
|
||||
#[inline]
|
||||
fn length(&self) -> usize {
|
||||
self.0 & MASK_LO
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn capacity(&self) -> usize {
|
||||
self.0 & MASK_HI
|
||||
}
|
||||
|
||||
pub const fn from_ref(len: usize) -> Metadata {
|
||||
if len & MASK_HI != 0 {
|
||||
panic!("Cow: length out of bounds for referenced value");
|
||||
}
|
||||
|
||||
Metadata(len)
|
||||
}
|
||||
|
||||
pub const fn from_owned(len: usize, capacity: usize) -> Metadata {
|
||||
if len & MASK_HI != 0 {
|
||||
panic!("Cow: length out of bounds for owned value");
|
||||
}
|
||||
|
||||
if capacity & MASK_HI != 0 {
|
||||
panic!("Cow: capacity out of bounds for owned value");
|
||||
}
|
||||
|
||||
Metadata((capacity & MASK_LO) << 32 | len & MASK_LO)
|
||||
}
|
||||
|
||||
pub const fn borrowed() -> Metadata {
|
||||
Metadata(0)
|
||||
}
|
||||
|
||||
pub const fn owned() -> Metadata {
|
||||
Metadata(1 << 32)
|
||||
}
|
||||
}*/
|
|
@ -1,5 +1,5 @@
|
|||
use crate::{IntoLabels, Label, SharedString};
|
||||
use alloc::{borrow::Cow, string::String, vec::Vec};
|
||||
use crate::{cow::Cow, IntoLabels, Label, SharedString};
|
||||
use alloc::{string::String, vec::Vec};
|
||||
use core::{
|
||||
fmt,
|
||||
hash::{Hash, Hasher},
|
||||
|
@ -7,13 +7,95 @@ use core::{
|
|||
slice::Iter,
|
||||
};
|
||||
|
||||
const NO_LABELS: [Label; 0] = [];
|
||||
|
||||
/// Parts compromising a metric name.
|
||||
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
|
||||
pub struct NameParts(Cow<'static, [SharedString]>);
|
||||
|
||||
impl NameParts {
|
||||
/// Creates a [`NameParts`] from the given name.
|
||||
pub fn from_name<N: Into<SharedString>>(name: N) -> Self {
|
||||
NameParts(Cow::owned(vec![name.into()]))
|
||||
}
|
||||
|
||||
/// Creates a [`NameParts`] from the given static name.
|
||||
pub const fn from_static_names(names: &'static [SharedString]) -> Self {
|
||||
NameParts(Cow::<'static, [SharedString]>::const_slice(names))
|
||||
}
|
||||
|
||||
/// Appends a name part.
|
||||
pub fn append<S: Into<SharedString>>(self, part: S) -> Self {
|
||||
let mut parts = self.0.into_owned();
|
||||
parts.push(part.into());
|
||||
NameParts(Cow::owned(parts))
|
||||
}
|
||||
|
||||
/// Prepends a name part.
|
||||
pub fn prepend<S: Into<SharedString>>(self, part: S) -> Self {
|
||||
let mut parts = self.0.into_owned();
|
||||
parts.insert(0, part.into());
|
||||
NameParts(Cow::owned(parts))
|
||||
}
|
||||
|
||||
/// Gets a reference to the parts for this name.
|
||||
pub fn parts(&self) -> Iter<'_, SharedString> {
|
||||
self.0.iter()
|
||||
}
|
||||
|
||||
/// Renders the name parts as a dot-delimited string.
|
||||
pub fn to_string(&self) -> String {
|
||||
// It's faster to allocate the string by hand instead of collecting the parts and joining
|
||||
// them, or deferring to Dsiplay::to_string, or anything else. This may change in the
|
||||
// future, or benefit from some sort of string pooling, but otherwise, this seemingly
|
||||
// suboptimal approach -- oh no, a single allocation! :P -- works pretty well overall.
|
||||
let mut first = false;
|
||||
let mut s = String::with_capacity(16);
|
||||
for p in self.0.iter() {
|
||||
if first {
|
||||
s.push_str(".");
|
||||
first = false;
|
||||
}
|
||||
s.push_str(p.as_ref());
|
||||
}
|
||||
s
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for NameParts {
|
||||
fn from(name: String) -> NameParts {
|
||||
NameParts::from_name(name)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&'static str> for NameParts {
|
||||
fn from(name: &'static str) -> NameParts {
|
||||
NameParts::from_name(name)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&'static [SharedString]> for NameParts {
|
||||
fn from(names: &'static [SharedString]) -> NameParts {
|
||||
NameParts::from_static_names(names)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for NameParts {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let s = self.to_string();
|
||||
f.write_str(s.as_str())?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Inner representation of [`Key`].
|
||||
///
|
||||
/// While [`Key`] is the type that users will interact with via [`Recorder`][crate::Recorder`,
|
||||
/// While [`Key`] is the type that users will interact with via [`Recorder`][crate::Recorder],
|
||||
/// [`KeyData`] is responsible for the actual storage of the name and label data.
|
||||
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
|
||||
pub struct KeyData {
|
||||
name: SharedString,
|
||||
// TODO: once const slicing is possible on stable, we could likely use `beef` for both of these
|
||||
name_parts: NameParts,
|
||||
labels: Cow<'static, [Label]>,
|
||||
}
|
||||
|
||||
|
@ -23,44 +105,47 @@ impl KeyData {
|
|||
where
|
||||
N: Into<SharedString>,
|
||||
{
|
||||
Self::from_parts(name, Vec::new())
|
||||
Self {
|
||||
name_parts: NameParts::from_name(name),
|
||||
labels: Cow::owned(Vec::new()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a [`KeyData`] from a name and vector of [`Label`]s.
|
||||
/// Creates a [`KeyData`] from a name and set of labels.
|
||||
pub fn from_parts<N, L>(name: N, labels: L) -> Self
|
||||
where
|
||||
N: Into<SharedString>,
|
||||
N: Into<NameParts>,
|
||||
L: IntoLabels,
|
||||
{
|
||||
Self {
|
||||
name: name.into(),
|
||||
labels: labels.into_labels().into(),
|
||||
name_parts: name.into(),
|
||||
labels: Cow::owned(labels.into_labels()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a [`KeyData`] from a static name.
|
||||
///
|
||||
/// 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: SharedString::const_str(name),
|
||||
labels: Cow::Owned(Vec::new()),
|
||||
}
|
||||
pub const fn from_static_name(name_parts: &'static [SharedString]) -> Self {
|
||||
Self::from_static_parts(name_parts, &NO_LABELS)
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
pub const fn from_static_parts(
|
||||
name_parts: &'static [SharedString],
|
||||
labels: &'static [Label],
|
||||
) -> Self {
|
||||
Self {
|
||||
name: SharedString::const_str(name),
|
||||
labels: Cow::Borrowed(labels),
|
||||
name_parts: NameParts::from_static_names(name_parts),
|
||||
labels: Cow::<[Label]>::const_slice(labels),
|
||||
}
|
||||
}
|
||||
|
||||
/// Name of this key.
|
||||
pub fn name(&self) -> &SharedString {
|
||||
&self.name
|
||||
/// Name parts of this key.
|
||||
pub fn name(&self) -> &NameParts {
|
||||
&self.name_parts
|
||||
}
|
||||
|
||||
/// Labels of this key, if they exist.
|
||||
|
@ -68,21 +153,27 @@ impl KeyData {
|
|||
self.labels.iter()
|
||||
}
|
||||
|
||||
/// Map the name of this key to a new name, based on `f`.
|
||||
///
|
||||
/// 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(SharedString) -> String,
|
||||
{
|
||||
let new_name = f(self.name);
|
||||
self.name = new_name.into();
|
||||
self
|
||||
/// Appends a part to the name,
|
||||
pub fn append_name<S: Into<SharedString>>(self, part: S) -> Self {
|
||||
let name_parts = self.name_parts.append(part);
|
||||
Self {
|
||||
name_parts,
|
||||
labels: self.labels,
|
||||
}
|
||||
}
|
||||
|
||||
/// Consumes this [`Key`], returning the name and any labels.
|
||||
pub fn into_parts(self) -> (SharedString, Vec<Label>) {
|
||||
(self.name, self.labels.into_owned())
|
||||
/// Prepends a part to the name.
|
||||
pub fn prepend_name<S: Into<SharedString>>(self, part: S) -> Self {
|
||||
let name_parts = self.name_parts.prepend(part);
|
||||
Self {
|
||||
name_parts,
|
||||
labels: self.labels,
|
||||
}
|
||||
}
|
||||
|
||||
/// Consumes this [`Key`], returning the name parts and any labels.
|
||||
pub fn into_parts(self) -> (NameParts, Vec<Label>) {
|
||||
(self.name_parts.clone(), self.labels.into_owned())
|
||||
}
|
||||
|
||||
/// Clones this [`Key`], and expands the existing set of labels.
|
||||
|
@ -91,12 +182,12 @@ impl KeyData {
|
|||
return self.clone();
|
||||
}
|
||||
|
||||
let name = self.name.clone();
|
||||
let name_parts = self.name_parts.clone();
|
||||
let mut labels = self.labels.clone().into_owned();
|
||||
labels.extend(extra_labels);
|
||||
|
||||
Self {
|
||||
name,
|
||||
name_parts,
|
||||
labels: labels.into(),
|
||||
}
|
||||
}
|
||||
|
@ -105,9 +196,9 @@ impl KeyData {
|
|||
impl fmt::Display for KeyData {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
if self.labels.is_empty() {
|
||||
write!(f, "KeyData({})", self.name)
|
||||
write!(f, "KeyData({})", self.name_parts)
|
||||
} else {
|
||||
write!(f, "KeyData({}, [", self.name)?;
|
||||
write!(f, "KeyData({}, [", self.name_parts)?;
|
||||
let mut first = true;
|
||||
for label in self.labels.as_ref() {
|
||||
if first {
|
||||
|
@ -140,7 +231,10 @@ where
|
|||
L: IntoLabels,
|
||||
{
|
||||
fn from(parts: (N, L)) -> Self {
|
||||
Self::from_parts(parts.0, parts.1)
|
||||
Self {
|
||||
name_parts: NameParts::from_name(parts.0),
|
||||
labels: Cow::owned(parts.1.into_labels()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -241,12 +335,14 @@ impl From<&'static KeyData> for Key {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{Key, KeyData};
|
||||
use crate::Label;
|
||||
use crate::{Label, SharedString};
|
||||
use std::collections::HashMap;
|
||||
|
||||
static BORROWED_BASIC: KeyData = KeyData::from_static_name("name");
|
||||
static BORROWED_NAME: [SharedString; 1] = [SharedString::const_str("name")];
|
||||
static FOOBAR_NAME: [SharedString; 1] = [SharedString::const_str("foobar")];
|
||||
static BORROWED_BASIC: KeyData = KeyData::from_static_name(&BORROWED_NAME);
|
||||
static LABELS: [Label; 1] = [Label::from_static_parts("key", "value")];
|
||||
static BORROWED_LABELS: KeyData = KeyData::from_static_parts("name", &LABELS);
|
||||
static BORROWED_LABELS: KeyData = KeyData::from_static_parts(&BORROWED_NAME, &LABELS);
|
||||
|
||||
#[test]
|
||||
fn test_keydata_eq_and_hash() {
|
||||
|
@ -262,7 +358,7 @@ mod tests {
|
|||
assert_eq!(previous, Some(&42));
|
||||
|
||||
let labels = LABELS.to_vec();
|
||||
let owned_labels = KeyData::from_parts("name", labels);
|
||||
let owned_labels = KeyData::from_parts(&BORROWED_NAME[..], labels);
|
||||
assert_eq!(&owned_labels, &BORROWED_LABELS);
|
||||
|
||||
let previous = keys.insert(owned_labels, 43);
|
||||
|
@ -287,7 +383,7 @@ mod tests {
|
|||
assert_eq!(previous, Some(&42));
|
||||
|
||||
let labels = LABELS.to_vec();
|
||||
let owned_labels = Key::from(KeyData::from_parts("name", labels));
|
||||
let owned_labels = Key::from(KeyData::from_parts(&BORROWED_NAME[..], labels));
|
||||
let borrowed_labels = Key::from(&BORROWED_LABELS);
|
||||
assert_eq!(owned_labels, borrowed_labels);
|
||||
|
||||
|
@ -304,19 +400,19 @@ mod tests {
|
|||
let result1 = key1.to_string();
|
||||
assert_eq!(result1, "KeyData(foobar)");
|
||||
|
||||
let key2 = KeyData::from_parts("foobar", vec![Label::new("system", "http")]);
|
||||
let key2 = KeyData::from_parts(&FOOBAR_NAME[..], vec![Label::new("system", "http")]);
|
||||
let result2 = key2.to_string();
|
||||
assert_eq!(result2, "KeyData(foobar, [system = http])");
|
||||
|
||||
let key3 = KeyData::from_parts(
|
||||
"foobar",
|
||||
&FOOBAR_NAME[..],
|
||||
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_parts(
|
||||
"foobar",
|
||||
&FOOBAR_NAME[..],
|
||||
vec![
|
||||
Label::new("black", "black"),
|
||||
Label::new("lives", "lives"),
|
||||
|
@ -335,8 +431,10 @@ mod tests {
|
|||
let owned_a = KeyData::from_name("a");
|
||||
let owned_b = KeyData::from_name("b");
|
||||
|
||||
static STATIC_A: KeyData = KeyData::from_static_name("a");
|
||||
static STATIC_B: KeyData = KeyData::from_static_name("b");
|
||||
static A_NAME: [SharedString; 1] = [SharedString::const_str("a")];
|
||||
static STATIC_A: KeyData = KeyData::from_static_name(&A_NAME);
|
||||
static B_NAME: [SharedString; 1] = [SharedString::const_str("b")];
|
||||
static STATIC_B: KeyData = KeyData::from_static_name(&B_NAME);
|
||||
|
||||
assert_eq!(Key::Owned(owned_a.clone()), Key::Owned(owned_a.clone()));
|
||||
assert_eq!(Key::Owned(owned_b.clone()), Key::Owned(owned_b.clone()));
|
||||
|
|
|
@ -223,6 +223,8 @@ use proc_macro_hack::proc_macro_hack;
|
|||
mod common;
|
||||
pub use self::common::*;
|
||||
|
||||
mod cow;
|
||||
|
||||
mod key;
|
||||
pub use self::key::*;
|
||||
|
||||
|
|
Loading…
Reference in New Issue