From aba0af5d66bfddab9dfef99ff25a5d4b3ece6210 Mon Sep 17 00:00:00 2001 From: Matthew Shapiro Date: Tue, 6 Feb 2024 17:00:48 -0500 Subject: [PATCH] Revert "Allow precreation of AttributeSets for metrics (#1421)" (#1512) --- examples/metrics-basic/src/main.rs | 16 +- .../examples/basic-otlp-http/src/main.rs | 9 +- .../examples/basic-otlp/src/main.rs | 11 +- opentelemetry-prometheus/examples/hyper.rs | 10 +- opentelemetry-prometheus/src/lib.rs | 9 +- .../tests/integration_test.rs | 131 +++--- opentelemetry-sdk/benches/attribute_set.rs | 6 +- opentelemetry-sdk/benches/metric.rs | 21 +- opentelemetry-sdk/benches/metric_counter.rs | 14 - opentelemetry-sdk/src/attributes/mod.rs | 3 + opentelemetry-sdk/src/attributes/set.rs | 181 ++++++++ opentelemetry-sdk/src/lib.rs | 10 +- opentelemetry-sdk/src/metrics/data/mod.rs | 3 +- opentelemetry-sdk/src/metrics/instrument.rs | 23 +- .../src/metrics/internal/aggregate.rs | 49 ++- .../metrics/internal/exponential_histogram.rs | 11 +- .../src/metrics/internal/histogram.rs | 4 +- .../src/metrics/internal/last_value.rs | 4 +- opentelemetry-sdk/src/metrics/internal/sum.rs | 3 +- opentelemetry-sdk/src/metrics/meter.rs | 8 +- opentelemetry-sdk/src/metrics/mod.rs | 21 +- opentelemetry-sdk/src/resource/mod.rs | 31 -- .../src/testing/metrics/in_memory_exporter.rs | 4 +- opentelemetry-stdout/src/common.rs | 4 +- opentelemetry-stdout/src/logs/transform.rs | 4 +- opentelemetry-stdout/src/trace/transform.rs | 4 +- opentelemetry/CHANGELOG.md | 10 - opentelemetry/Cargo.toml | 1 - opentelemetry/src/attributes/mod.rs | 5 - opentelemetry/src/attributes/set.rs | 406 ------------------ opentelemetry/src/global/mod.rs | 10 +- opentelemetry/src/lib.rs | 10 +- .../src/metrics/instruments/counter.rs | 16 +- .../src/metrics/instruments/gauge.rs | 12 +- .../src/metrics/instruments/histogram.rs | 12 +- opentelemetry/src/metrics/instruments/mod.rs | 4 +- .../metrics/instruments/up_down_counter.rs | 16 +- opentelemetry/src/metrics/meter.rs | 133 ++++-- opentelemetry/src/metrics/noop.rs | 11 +- stress/Cargo.toml | 5 - stress/src/metrics_cached_attrs.rs | 53 --- 41 files changed, 503 insertions(+), 795 deletions(-) create mode 100644 opentelemetry-sdk/src/attributes/mod.rs create mode 100644 opentelemetry-sdk/src/attributes/set.rs delete mode 100644 opentelemetry/src/attributes/mod.rs delete mode 100644 opentelemetry/src/attributes/set.rs delete mode 100644 stress/src/metrics_cached_attrs.rs diff --git a/examples/metrics-basic/src/main.rs b/examples/metrics-basic/src/main.rs index a923234273..78dda47cad 100644 --- a/examples/metrics-basic/src/main.rs +++ b/examples/metrics-basic/src/main.rs @@ -1,5 +1,4 @@ use opentelemetry::metrics::Unit; -use opentelemetry::AttributeSet; use opentelemetry::{metrics::MeterProvider as _, KeyValue}; use opentelemetry_sdk::metrics::{PeriodicReader, SdkMeterProvider}; use opentelemetry_sdk::{runtime, Resource}; @@ -53,10 +52,11 @@ async fn main() -> Result<(), Box> { observer.observe_u64( &observable_counter, 100, - AttributeSet::from(&[ + [ KeyValue::new("mykey1", "myvalue1"), KeyValue::new("mykey2", "myvalue2"), - ]), + ] + .as_ref(), ) })?; @@ -84,10 +84,11 @@ async fn main() -> Result<(), Box> { observer.observe_i64( &observable_up_down_counter, 100, - AttributeSet::from(&[ + [ KeyValue::new("mykey1", "myvalue1"), KeyValue::new("mykey2", "myvalue2"), - ]), + ] + .as_ref(), ) })?; @@ -141,10 +142,11 @@ async fn main() -> Result<(), Box> { observer.observe_f64( &observable_gauge, 1.0, - AttributeSet::from(&[ + [ KeyValue::new("mykey1", "myvalue1"), KeyValue::new("mykey2", "myvalue2"), - ]), + ] + .as_ref(), ) })?; diff --git a/opentelemetry-otlp/examples/basic-otlp-http/src/main.rs b/opentelemetry-otlp/examples/basic-otlp-http/src/main.rs index aa028b920b..0f320dcb8d 100644 --- a/opentelemetry-otlp/examples/basic-otlp-http/src/main.rs +++ b/opentelemetry-otlp/examples/basic-otlp-http/src/main.rs @@ -11,7 +11,6 @@ use opentelemetry_sdk::metrics as sdkmetrics; use opentelemetry_sdk::resource; use opentelemetry_sdk::trace as sdktrace; -use opentelemetry::AttributeSet; use std::error::Error; use tracing::info; use tracing_subscriber::prelude::*; @@ -63,13 +62,13 @@ fn init_metrics() -> metrics::Result { const LEMONS_KEY: Key = Key::from_static_str("ex.com/lemons"); const ANOTHER_KEY: Key = Key::from_static_str("ex.com/another"); -static COMMON_ATTRIBUTES: Lazy = Lazy::new(|| { - AttributeSet::from(&[ +static COMMON_ATTRIBUTES: Lazy<[KeyValue; 4]> = Lazy::new(|| { + [ LEMONS_KEY.i64(10), KeyValue::new("A", "1"), KeyValue::new("B", "2"), KeyValue::new("C", "3"), - ]) + ] }); #[tokio::main] @@ -105,7 +104,7 @@ async fn main() -> Result<(), Box> { info!(target: "my-target", "hello from {}. My price is {}", "apple", 1.99); let histogram = meter.f64_histogram("ex.com.two").init(); - histogram.record(5.5, COMMON_ATTRIBUTES.clone()); + histogram.record(5.5, COMMON_ATTRIBUTES.as_ref()); global::shutdown_tracer_provider(); global::shutdown_logger_provider(); diff --git a/opentelemetry-otlp/examples/basic-otlp/src/main.rs b/opentelemetry-otlp/examples/basic-otlp/src/main.rs index 1c252906f9..1bf5f7e421 100644 --- a/opentelemetry-otlp/examples/basic-otlp/src/main.rs +++ b/opentelemetry-otlp/examples/basic-otlp/src/main.rs @@ -4,7 +4,6 @@ use opentelemetry::global; use opentelemetry::global::{logger_provider, shutdown_logger_provider, shutdown_tracer_provider}; use opentelemetry::logs::LogError; use opentelemetry::trace::TraceError; -use opentelemetry::AttributeSet; use opentelemetry::{ metrics, trace::{TraceContextExt, Tracer}, @@ -73,13 +72,13 @@ fn init_logs() -> Result { const LEMONS_KEY: Key = Key::from_static_str("lemons"); const ANOTHER_KEY: Key = Key::from_static_str("ex.com/another"); -static COMMON_ATTRIBUTES: Lazy = Lazy::new(|| { - AttributeSet::from(&[ +static COMMON_ATTRIBUTES: Lazy<[KeyValue; 4]> = Lazy::new(|| { + [ LEMONS_KEY.i64(10), KeyValue::new("A", "1"), KeyValue::new("B", "2"), KeyValue::new("C", "3"), - ]) + ] }); #[tokio::main] @@ -110,11 +109,11 @@ async fn main() -> Result<(), Box> { .init(); meter.register_callback(&[gauge.as_any()], move |observer| { - observer.observe_f64(&gauge, 1.0, COMMON_ATTRIBUTES.clone()) + observer.observe_f64(&gauge, 1.0, COMMON_ATTRIBUTES.as_ref()) })?; let histogram = meter.f64_histogram("ex.com.two").init(); - histogram.record(5.5, COMMON_ATTRIBUTES.clone()); + histogram.record(5.5, COMMON_ATTRIBUTES.as_ref()); tracer.in_span("operation", |cx| { let span = cx.span(); diff --git a/opentelemetry-prometheus/examples/hyper.rs b/opentelemetry-prometheus/examples/hyper.rs index a97bb4320e..943ba617b6 100644 --- a/opentelemetry-prometheus/examples/hyper.rs +++ b/opentelemetry-prometheus/examples/hyper.rs @@ -4,7 +4,6 @@ use hyper::{ Body, Method, Request, Response, Server, }; use once_cell::sync::Lazy; -use opentelemetry::AttributeSet; use opentelemetry::{ metrics::{Counter, Histogram, MeterProvider as _, Unit}, KeyValue, @@ -15,8 +14,7 @@ use std::convert::Infallible; use std::sync::Arc; use std::time::SystemTime; -static HANDLER_ALL: Lazy = - Lazy::new(|| AttributeSet::from(&[KeyValue::new("handler", "all")])); +static HANDLER_ALL: Lazy<[KeyValue; 1]> = Lazy::new(|| [KeyValue::new("handler", "all")]); async fn serve_req( req: Request, @@ -25,7 +23,7 @@ async fn serve_req( println!("Receiving request at path {}", req.uri()); let request_start = SystemTime::now(); - state.http_counter.add(1, HANDLER_ALL.clone()); + state.http_counter.add(1, HANDLER_ALL.as_ref()); let response = match (req.method(), req.uri().path()) { (&Method::GET, "/metrics") => { @@ -35,7 +33,7 @@ async fn serve_req( encoder.encode(&metric_families, &mut buffer).unwrap(); state .http_body_gauge - .record(buffer.len() as u64, HANDLER_ALL.clone()); + .record(buffer.len() as u64, HANDLER_ALL.as_ref()); Response::builder() .status(200) @@ -55,7 +53,7 @@ async fn serve_req( state.http_req_histogram.record( request_start.elapsed().map_or(0.0, |d| d.as_secs_f64()), - AttributeSet::default(), + &[], ); Ok(response) } diff --git a/opentelemetry-prometheus/src/lib.rs b/opentelemetry-prometheus/src/lib.rs index a28061e513..6ff87d3ed0 100644 --- a/opentelemetry-prometheus/src/lib.rs +++ b/opentelemetry-prometheus/src/lib.rs @@ -3,14 +3,13 @@ //! [Prometheus]: https://prometheus.io //! //! ``` -//! use opentelemetry::{AttributeSet, metrics::MeterProvider, KeyValue}; +//! use opentelemetry::{metrics::MeterProvider, KeyValue}; //! use opentelemetry_sdk::metrics::SdkMeterProvider; //! use prometheus::{Encoder, TextEncoder}; //! //! # fn main() -> Result<(), Box> { //! //! // create a new prometheus registry -//! use opentelemetry::AttributeSet; //! let registry = prometheus::Registry::new(); //! //! // configure OpenTelemetry to use this registry @@ -32,10 +31,8 @@ //! .with_description("Records values") //! .init(); //! -//! let attributes = AttributeSet::from(&[KeyValue::new("key", "value")]); -//! -//! counter.add(100, attributes.clone()); -//! histogram.record(100, attributes); +//! counter.add(100, &[KeyValue::new("key", "value")]); +//! histogram.record(100, &[KeyValue::new("key", "value")]); //! //! // Encode data as text or protobuf //! let encoder = TextEncoder::new(); diff --git a/opentelemetry-prometheus/tests/integration_test.rs b/opentelemetry-prometheus/tests/integration_test.rs index b81e4ec241..0786b1a4c0 100644 --- a/opentelemetry-prometheus/tests/integration_test.rs +++ b/opentelemetry-prometheus/tests/integration_test.rs @@ -3,7 +3,6 @@ use std::path::Path; use std::time::Duration; use opentelemetry::metrics::{Meter, MeterProvider as _, Unit}; -use opentelemetry::AttributeSet; use opentelemetry::Key; use opentelemetry::KeyValue; use opentelemetry_prometheus::ExporterBuilder; @@ -45,29 +44,27 @@ fn prometheus_exporter_integration() { name: "counter", expected_file: "counter.txt", record_metrics: Box::new(|meter| { - let attrs = AttributeSet::from(&[ + let attrs = vec![ Key::new("A").string("B"), Key::new("C").string("D"), Key::new("E").bool(true), Key::new("F").i64(42), - ]); - + ]; let counter = meter .f64_counter("foo") .with_description("a simple counter") .with_unit(Unit::new("ms")) .init(); - counter.add(5.0, attrs.clone()); - counter.add(10.3, attrs.clone()); - counter.add(9.0, attrs); - - let attrs2 = AttributeSet::from(&[ + counter.add(5.0, &attrs); + counter.add(10.3, &attrs); + counter.add(9.0, &attrs); + let attrs2 = vec![ Key::new("A").string("D"), Key::new("C").string("B"), Key::new("E").bool(true), Key::new("F").i64(42), - ]); - counter.add(5.0, attrs2); + ]; + counter.add(5.0, &attrs2); }), ..Default::default() }, @@ -76,28 +73,27 @@ fn prometheus_exporter_integration() { expected_file: "counter_disabled_suffix.txt", builder: ExporterBuilder::default().without_counter_suffixes(), record_metrics: Box::new(|meter| { - let attrs = AttributeSet::from(&[ + let attrs = vec![ Key::new("A").string("B"), Key::new("C").string("D"), Key::new("E").bool(true), Key::new("F").i64(42), - ]); - + ]; let counter = meter .f64_counter("foo") .with_description("a simple counter without a total suffix") .with_unit(Unit::new("ms")) .init(); - counter.add(5.0, attrs.clone()); - counter.add(10.3, attrs.clone()); - counter.add(9.0, attrs); - let attrs2 = AttributeSet::from(&[ + counter.add(5.0, &attrs); + counter.add(10.3, &attrs); + counter.add(9.0, &attrs); + let attrs2 = vec![ Key::new("A").string("D"), Key::new("C").string("B"), Key::new("E").bool(true), Key::new("F").i64(42), - ]); - counter.add(5.0, attrs2); + ]; + counter.add(5.0, &attrs2); }), ..Default::default() }, @@ -105,15 +101,14 @@ fn prometheus_exporter_integration() { name: "gauge", expected_file: "gauge.txt", record_metrics: Box::new(|meter| { - let attrs = - AttributeSet::from(&[Key::new("A").string("B"), Key::new("C").string("D")]); + let attrs = vec![Key::new("A").string("B"), Key::new("C").string("D")]; let gauge = meter .f64_up_down_counter("bar") .with_description("a fun little gauge") .with_unit(Unit::new("1")) .init(); - gauge.add(1.0, attrs.clone()); - gauge.add(-0.25, attrs); + gauge.add(1.0, &attrs); + gauge.add(-0.25, &attrs); }), ..Default::default() }, @@ -121,17 +116,16 @@ fn prometheus_exporter_integration() { name: "histogram", expected_file: "histogram.txt", record_metrics: Box::new(|meter| { - let attrs = - AttributeSet::from(&[Key::new("A").string("B"), Key::new("C").string("D")]); + let attrs = vec![Key::new("A").string("B"), Key::new("C").string("D")]; let histogram = meter .f64_histogram("histogram_baz") .with_description("a very nice histogram") .with_unit(Unit::new("By")) .init(); - histogram.record(23.0, attrs.clone()); - histogram.record(7.0, attrs.clone()); - histogram.record(101.0, attrs.clone()); - histogram.record(105.0, attrs); + histogram.record(23.0, &attrs); + histogram.record(7.0, &attrs); + histogram.record(101.0, &attrs); + histogram.record(105.0, &attrs); }), ..Default::default() }, @@ -140,23 +134,23 @@ fn prometheus_exporter_integration() { expected_file: "sanitized_labels.txt", builder: ExporterBuilder::default().without_units(), record_metrics: Box::new(|meter| { - let attrs = AttributeSet::from(&[ + let attrs = vec![ // exact match, value should be overwritten Key::new("A.B").string("X"), Key::new("A.B").string("Q"), // unintended match due to sanitization, values should be concatenated Key::new("C.D").string("Y"), Key::new("C/D").string("Z"), - ]); + ]; let counter = meter .f64_counter("foo") .with_description("a sanitary counter") // This unit is not added to .with_unit(Unit::new("By")) .init(); - counter.add(5.0, attrs.clone()); - counter.add(10.3, attrs.clone()); - counter.add(9.0, attrs); + counter.add(5.0, &attrs); + counter.add(10.3, &attrs); + counter.add(9.0, &attrs); }), ..Default::default() }, @@ -164,34 +158,33 @@ fn prometheus_exporter_integration() { name: "invalid instruments are renamed", expected_file: "sanitized_names.txt", record_metrics: Box::new(|meter| { - let attrs = - AttributeSet::from(&[Key::new("A").string("B"), Key::new("C").string("D")]); + let attrs = vec![Key::new("A").string("B"), Key::new("C").string("D")]; // Valid. let mut gauge = meter .f64_up_down_counter("bar") .with_description("a fun little gauge") .init(); - gauge.add(100., attrs.clone()); - gauge.add(-25.0, attrs.clone()); + gauge.add(100., &attrs); + gauge.add(-25.0, &attrs); // Invalid, will be renamed. gauge = meter .f64_up_down_counter("invalid.gauge.name") .with_description("a gauge with an invalid name") .init(); - gauge.add(100.0, attrs.clone()); + gauge.add(100.0, &attrs); let counter = meter .f64_counter("0invalid.counter.name") .with_description("a counter with an invalid name") .init(); - counter.add(100.0, attrs.clone()); + counter.add(100.0, &attrs); let histogram = meter .f64_histogram("invalid.hist.name") .with_description("a histogram with an invalid name") .init(); - histogram.record(23.0, attrs); + histogram.record(23.0, &attrs); }), ..Default::default() }, @@ -200,19 +193,19 @@ fn prometheus_exporter_integration() { empty_resource: true, expected_file: "empty_resource.txt", record_metrics: Box::new(|meter| { - let attrs = AttributeSet::from(&[ + let attrs = vec![ Key::new("A").string("B"), Key::new("C").string("D"), Key::new("E").bool(true), Key::new("F").i64(42), - ]); + ]; let counter = meter .f64_counter("foo") .with_description("a simple counter") .init(); - counter.add(5.0, attrs.clone()); - counter.add(10.3, attrs.clone()); - counter.add(9.0, attrs.clone()); + counter.add(5.0, &attrs); + counter.add(10.3, &attrs); + counter.add(9.0, &attrs); }), ..Default::default() }, @@ -221,19 +214,19 @@ fn prometheus_exporter_integration() { custom_resource_attrs: vec![Key::new("A").string("B"), Key::new("C").string("D")], expected_file: "custom_resource.txt", record_metrics: Box::new(|meter| { - let attrs = AttributeSet::from(&[ + let attrs = vec![ Key::new("A").string("B"), Key::new("C").string("D"), Key::new("E").bool(true), Key::new("F").i64(42), - ]); + ]; let counter = meter .f64_counter("foo") .with_description("a simple counter") .init(); - counter.add(5., attrs.clone()); - counter.add(10.3, attrs.clone()); - counter.add(9.0, attrs); + counter.add(5., &attrs); + counter.add(10.3, &attrs); + counter.add(9.0, &attrs); }), ..Default::default() }, @@ -242,19 +235,19 @@ fn prometheus_exporter_integration() { builder: ExporterBuilder::default().without_target_info(), expected_file: "without_target_info.txt", record_metrics: Box::new(|meter| { - let attrs = AttributeSet::from(&[ + let attrs = vec![ Key::new("A").string("B"), Key::new("C").string("D"), Key::new("E").bool(true), Key::new("F").i64(42), - ]); + ]; let counter = meter .f64_counter("foo") .with_description("a simple counter") .init(); - counter.add(5.0, attrs.clone()); - counter.add(10.3, attrs.clone()); - counter.add(9.0, attrs); + counter.add(5.0, &attrs); + counter.add(10.3, &attrs); + counter.add(9.0, &attrs); }), ..Default::default() }, @@ -263,15 +256,14 @@ fn prometheus_exporter_integration() { builder: ExporterBuilder::default().without_scope_info(), expected_file: "without_scope_info.txt", record_metrics: Box::new(|meter| { - let attrs = - AttributeSet::from(&[Key::new("A").string("B"), Key::new("C").string("D")]); + let attrs = vec![Key::new("A").string("B"), Key::new("C").string("D")]; let gauge = meter .i64_up_down_counter("bar") .with_description("a fun little gauge") .with_unit(Unit::new("1")) .init(); - gauge.add(2, attrs.clone()); - gauge.add(-1, attrs); + gauge.add(2, &attrs); + gauge.add(-1, &attrs); }), ..Default::default() }, @@ -282,15 +274,14 @@ fn prometheus_exporter_integration() { .without_target_info(), expected_file: "without_scope_and_target_info.txt", record_metrics: Box::new(|meter| { - let attrs = - AttributeSet::from(&[Key::new("A").string("B"), Key::new("C").string("D")]); + let attrs = vec![Key::new("A").string("B"), Key::new("C").string("D")]; let counter = meter .u64_counter("bar") .with_description("a fun little counter") .with_unit(Unit::new("By")) .init(); - counter.add(2, attrs.clone()); - counter.add(1, attrs); + counter.add(2, &attrs); + counter.add(1, &attrs); }), ..Default::default() }, @@ -299,20 +290,20 @@ fn prometheus_exporter_integration() { builder: ExporterBuilder::default().with_namespace("test"), expected_file: "with_namespace.txt", record_metrics: Box::new(|meter| { - let attrs = AttributeSet::from(&[ + let attrs = vec![ Key::new("A").string("B"), Key::new("C").string("D"), Key::new("E").bool(true), Key::new("F").i64(42), - ]); + ]; let counter = meter .f64_counter("foo") .with_description("a simple counter") .init(); - counter.add(5.0, attrs.clone()); - counter.add(10.3, attrs.clone()); - counter.add(9.0, attrs); + counter.add(5.0, &attrs); + counter.add(10.3, &attrs); + counter.add(9.0, &attrs); }), ..Default::default() }, diff --git a/opentelemetry-sdk/benches/attribute_set.rs b/opentelemetry-sdk/benches/attribute_set.rs index 1fb6dccc1e..6f3360b9cf 100644 --- a/opentelemetry-sdk/benches/attribute_set.rs +++ b/opentelemetry-sdk/benches/attribute_set.rs @@ -1,6 +1,6 @@ use criterion::{criterion_group, criterion_main, Criterion}; -use opentelemetry::AttributeSet; use opentelemetry::KeyValue; +use opentelemetry_sdk::AttributeSet; // Run this benchmark with: // cargo bench --bench metric_counter @@ -12,7 +12,7 @@ fn criterion_benchmark(c: &mut Criterion) { fn attribute_set(c: &mut Criterion) { c.bench_function("AttributeSet_without_duplicates", |b| { b.iter(|| { - let attributes = &[ + let attributes: &[KeyValue] = &[ KeyValue::new("attribute1", "value1"), KeyValue::new("attribute2", "value2"), KeyValue::new("attribute3", "value3"), @@ -24,7 +24,7 @@ fn attribute_set(c: &mut Criterion) { c.bench_function("AttributeSet_with_duplicates", |b| { b.iter(|| { - let attributes = &[ + let attributes: &[KeyValue] = &[ KeyValue::new("attribute1", "value1"), KeyValue::new("attribute3", "value3"), KeyValue::new("attribute3", "value3"), diff --git a/opentelemetry-sdk/benches/metric.rs b/opentelemetry-sdk/benches/metric.rs index 3c668d58a8..d018634e04 100644 --- a/opentelemetry-sdk/benches/metric.rs +++ b/opentelemetry-sdk/benches/metric.rs @@ -2,7 +2,6 @@ use rand::Rng; use std::sync::{Arc, Weak}; use criterion::{criterion_group, criterion_main, Bencher, Criterion}; -use opentelemetry::AttributeSet; use opentelemetry::{ metrics::{Counter, Histogram, MeterProvider as _, Result}, Key, KeyValue, @@ -167,12 +166,8 @@ fn counters(c: &mut Criterion) { let (_, cntr3) = bench_counter(None, "cumulative"); let mut group = c.benchmark_group("Counter"); - group.bench_function("AddNoAttrs", |b| { - b.iter(|| cntr.add(1, AttributeSet::default())) - }); - group.bench_function("AddNoAttrsDelta", |b| { - b.iter(|| cntr2.add(1, AttributeSet::default())) - }); + group.bench_function("AddNoAttrs", |b| b.iter(|| cntr.add(1, &[]))); + group.bench_function("AddNoAttrsDelta", |b| b.iter(|| cntr2.add(1, &[]))); group.bench_function("AddOneAttr", |b| { b.iter(|| cntr.add(1, &[KeyValue::new("K", "V")])) @@ -279,16 +274,14 @@ fn counters(c: &mut Criterion) { } group.bench_function("AddOneTillMaxAttr", |b| { - b.iter(|| cntr3.add(1, max_attributes.as_slice())) + b.iter(|| cntr3.add(1, &max_attributes)) }); for i in MAX_DATA_POINTS..MAX_DATA_POINTS * 2 { max_attributes.push(KeyValue::new(i.to_string(), i)) } - group.bench_function("AddMaxAttr", |b| { - b.iter(|| cntr3.add(1, max_attributes.as_slice())) - }); + group.bench_function("AddMaxAttr", |b| b.iter(|| cntr3.add(1, &max_attributes))); group.bench_function("AddInvalidAttr", |b| { b.iter(|| cntr.add(1, &[KeyValue::new("", "V"), KeyValue::new("K", "V")])) @@ -400,12 +393,10 @@ fn histograms(c: &mut Criterion) { format!("V,{},{},{}", bound_size, attr_size, i), )) } - - let attributes = AttributeSet::from(&attributes); let value: u64 = rng.gen_range(0..MAX_BOUND).try_into().unwrap(); group.bench_function( format!("Record{}Attrs{}bounds", attr_size, bound_size), - |b| b.iter(|| hist.record(value, attributes.clone())), + |b| b.iter(|| hist.record(value, &attributes)), ); } } @@ -424,7 +415,7 @@ fn benchmark_collect_histogram(b: &mut Bencher, n: usize) { for i in 0..n { let h = mtr.u64_histogram(format!("fake_data_{i}")).init(); - h.record(1, AttributeSet::default()); + h.record(1, &[]); } let mut rm = ResourceMetrics { diff --git a/opentelemetry-sdk/benches/metric_counter.rs b/opentelemetry-sdk/benches/metric_counter.rs index 9e12243fdf..4bb4c84e6a 100644 --- a/opentelemetry-sdk/benches/metric_counter.rs +++ b/opentelemetry-sdk/benches/metric_counter.rs @@ -1,5 +1,4 @@ use criterion::{criterion_group, criterion_main, Criterion}; -use opentelemetry::AttributeSet; use opentelemetry::{ metrics::{Counter, MeterProvider as _}, KeyValue, @@ -68,19 +67,6 @@ fn counter_add(c: &mut Criterion) { ); }); }); - - c.bench_function("Counter_Add_Cached_Attributes", |b| { - let attributes = AttributeSet::from(&[ - KeyValue::new("attribute2", attribute_values[0]), - KeyValue::new("attribute3", attribute_values[1]), - KeyValue::new("attribute1", attribute_values[2]), - KeyValue::new("attribute4", attribute_values[3]), - ]); - - b.iter(|| { - counter.add(1, attributes.clone()); - }); - }); } criterion_group!(benches, criterion_benchmark); diff --git a/opentelemetry-sdk/src/attributes/mod.rs b/opentelemetry-sdk/src/attributes/mod.rs new file mode 100644 index 0000000000..1182e996fb --- /dev/null +++ b/opentelemetry-sdk/src/attributes/mod.rs @@ -0,0 +1,3 @@ +mod set; + +pub use set::AttributeSet; diff --git a/opentelemetry-sdk/src/attributes/set.rs b/opentelemetry-sdk/src/attributes/set.rs new file mode 100644 index 0000000000..ae5d5a4a73 --- /dev/null +++ b/opentelemetry-sdk/src/attributes/set.rs @@ -0,0 +1,181 @@ +use std::collections::hash_map::DefaultHasher; +use std::collections::HashSet; +use std::{ + cmp::Ordering, + hash::{Hash, Hasher}, +}; + +use opentelemetry::{Array, Key, KeyValue, Value}; +use ordered_float::OrderedFloat; + +use crate::Resource; + +#[derive(Clone, Debug)] +struct HashKeyValue(KeyValue); + +impl Hash for HashKeyValue { + fn hash(&self, state: &mut H) { + self.0.key.hash(state); + match &self.0.value { + Value::F64(f) => OrderedFloat(*f).hash(state), + Value::Array(a) => match a { + Array::Bool(b) => b.hash(state), + Array::I64(i) => i.hash(state), + Array::F64(f) => f.iter().for_each(|f| OrderedFloat(*f).hash(state)), + Array::String(s) => s.hash(state), + }, + Value::Bool(b) => b.hash(state), + Value::I64(i) => i.hash(state), + Value::String(s) => s.hash(state), + }; + } +} + +impl PartialOrd for HashKeyValue { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for HashKeyValue { + fn cmp(&self, other: &Self) -> Ordering { + match self.0.key.cmp(&other.0.key) { + Ordering::Equal => match type_order(&self.0.value).cmp(&type_order(&other.0.value)) { + Ordering::Equal => match (&self.0.value, &other.0.value) { + (Value::F64(f), Value::F64(of)) => OrderedFloat(*f).cmp(&OrderedFloat(*of)), + (Value::Array(Array::Bool(b)), Value::Array(Array::Bool(ob))) => b.cmp(ob), + (Value::Array(Array::I64(i)), Value::Array(Array::I64(oi))) => i.cmp(oi), + (Value::Array(Array::String(s)), Value::Array(Array::String(os))) => s.cmp(os), + (Value::Array(Array::F64(f)), Value::Array(Array::F64(of))) => { + match f.len().cmp(&of.len()) { + Ordering::Equal => f + .iter() + .map(|x| OrderedFloat(*x)) + .collect::>() + .cmp(&of.iter().map(|x| OrderedFloat(*x)).collect()), + other => other, + } + } + (Value::Bool(b), Value::Bool(ob)) => b.cmp(ob), + (Value::I64(i), Value::I64(oi)) => i.cmp(oi), + (Value::String(s), Value::String(os)) => s.cmp(os), + _ => Ordering::Equal, + }, + other => other, // 2nd order by value types + }, + other => other, // 1st order by key + } + } +} + +fn type_order(v: &Value) -> u8 { + match v { + Value::Bool(_) => 1, + Value::I64(_) => 2, + Value::F64(_) => 3, + Value::String(_) => 4, + Value::Array(a) => match a { + Array::Bool(_) => 5, + Array::I64(_) => 6, + Array::F64(_) => 7, + Array::String(_) => 8, + }, + } +} + +impl PartialEq for HashKeyValue { + fn eq(&self, other: &Self) -> bool { + self.0.key == other.0.key + && match (&self.0.value, &other.0.value) { + (Value::F64(f), Value::F64(of)) => OrderedFloat(*f).eq(&OrderedFloat(*of)), + (Value::Array(Array::F64(f)), Value::Array(Array::F64(of))) => { + f.len() == of.len() + && f.iter() + .zip(of.iter()) + .all(|(f, of)| OrderedFloat(*f).eq(&OrderedFloat(*of))) + } + (non_float, other_non_float) => non_float.eq(other_non_float), + } + } +} + +impl Eq for HashKeyValue {} + +/// A unique set of attributes that can be used as instrument identifiers. +/// +/// This must implement [Hash], [PartialEq], and [Eq] so it may be used as +/// HashMap keys and other de-duplication methods. +#[derive(Clone, Default, Debug, PartialEq, Eq)] +pub struct AttributeSet(Vec, u64); + +impl From<&[KeyValue]> for AttributeSet { + fn from(values: &[KeyValue]) -> Self { + let mut seen_keys = HashSet::with_capacity(values.len()); + let vec = values + .iter() + .rev() + .filter_map(|kv| { + if seen_keys.insert(kv.key.clone()) { + Some(HashKeyValue(kv.clone())) + } else { + None + } + }) + .collect::>(); + + AttributeSet::new(vec) + } +} + +impl From<&Resource> for AttributeSet { + fn from(values: &Resource) -> Self { + let vec = values + .iter() + .map(|(key, value)| HashKeyValue(KeyValue::new(key.clone(), value.clone()))) + .collect::>(); + + AttributeSet::new(vec) + } +} + +impl AttributeSet { + fn new(mut values: Vec) -> Self { + values.sort_unstable(); + let mut hasher = DefaultHasher::new(); + values.iter().fold(&mut hasher, |mut hasher, item| { + item.hash(&mut hasher); + hasher + }); + + AttributeSet(values, hasher.finish()) + } + + /// Returns the number of elements in the set. + pub fn len(&self) -> usize { + self.0.len() + } + + /// Returns `true` if the set contains no elements. + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + + /// Retains only the attributes specified by the predicate. + pub fn retain(&mut self, f: F) + where + F: Fn(&KeyValue) -> bool, + { + self.0.retain(|kv| f(&kv.0)) + } + + /// Iterate over key value pairs in the set + pub fn iter(&self) -> impl Iterator { + self.0.iter().map(|kv| (&kv.0.key, &kv.0.value)) + } +} + +impl Hash for AttributeSet { + fn hash(&self, state: &mut H) { + state.write_u64(self.1) + } +} diff --git a/opentelemetry-sdk/src/lib.rs b/opentelemetry-sdk/src/lib.rs index 0c27b1685b..947a217197 100644 --- a/opentelemetry-sdk/src/lib.rs +++ b/opentelemetry-sdk/src/lib.rs @@ -52,10 +52,9 @@ //! ### Creating instruments and recording measurements //! //! ``` -//! # use opentelemetry::AttributeSet; -//! #[cfg(feature = "metrics")] +//! # #[cfg(feature = "metrics")] //! # { -//! use opentelemetry::{AttributeSet, global, KeyValue}; +//! use opentelemetry::{global, KeyValue}; //! //! // get a meter from a provider //! let meter = global::meter("my_service"); @@ -64,8 +63,7 @@ //! let counter = meter.u64_counter("my_counter").init(); //! //! // record a measurement -//! let attributes = AttributeSet::from(&[KeyValue::new("http.client_ip", "83.164.160.102")]); -//! counter.add(1, attributes); +//! counter.add(1, &[KeyValue::new("http.client_ip", "83.164.160.102")]); //! # } //! ``` //! @@ -122,6 +120,7 @@ )] #![cfg_attr(test, deny(warnings))] +pub(crate) mod attributes; pub mod export; mod instrumentation; #[cfg(feature = "logs")] @@ -145,6 +144,7 @@ pub mod trace; #[doc(hidden)] pub mod util; +pub use attributes::*; pub use instrumentation::{InstrumentationLibrary, Scope}; #[doc(inline)] pub use resource::Resource; diff --git a/opentelemetry-sdk/src/metrics/data/mod.rs b/opentelemetry-sdk/src/metrics/data/mod.rs index ec429ba1f8..e827006c95 100644 --- a/opentelemetry-sdk/src/metrics/data/mod.rs +++ b/opentelemetry-sdk/src/metrics/data/mod.rs @@ -4,8 +4,7 @@ use std::{any, borrow::Cow, fmt, time::SystemTime}; use opentelemetry::{metrics::Unit, KeyValue}; -use crate::{instrumentation::Scope, Resource}; -use opentelemetry::AttributeSet; +use crate::{attributes::AttributeSet, instrumentation::Scope, Resource}; pub use self::temporality::Temporality; diff --git a/opentelemetry-sdk/src/metrics/instrument.rs b/opentelemetry-sdk/src/metrics/instrument.rs index 5b342fd3ff..3ecae355b5 100644 --- a/opentelemetry-sdk/src/metrics/instrument.rs +++ b/opentelemetry-sdk/src/metrics/instrument.rs @@ -5,10 +5,11 @@ use opentelemetry::{ AsyncInstrument, MetricsError, Result, SyncCounter, SyncGauge, SyncHistogram, SyncUpDownCounter, Unit, }, - AttributeSet, Key, + Key, KeyValue, }; use crate::{ + attributes::AttributeSet, instrumentation::Scope, metrics::{aggregation::Aggregation, internal::Measure}, }; @@ -258,33 +259,33 @@ pub(crate) struct ResolvedMeasures { } impl SyncCounter for ResolvedMeasures { - fn add(&self, val: T, attrs: AttributeSet) { + fn add(&self, val: T, attrs: &[KeyValue]) { for measure in &self.measures { - measure.call(val, attrs.clone()) + measure.call(val, AttributeSet::from(attrs)) } } } impl SyncUpDownCounter for ResolvedMeasures { - fn add(&self, val: T, attrs: AttributeSet) { + fn add(&self, val: T, attrs: &[KeyValue]) { for measure in &self.measures { - measure.call(val, attrs.clone()) + measure.call(val, AttributeSet::from(attrs)) } } } impl SyncGauge for ResolvedMeasures { - fn record(&self, val: T, attrs: AttributeSet) { + fn record(&self, val: T, attrs: &[KeyValue]) { for measure in &self.measures { - measure.call(val, attrs.clone()) + measure.call(val, AttributeSet::from(attrs)) } } } impl SyncHistogram for ResolvedMeasures { - fn record(&self, val: T, attrs: AttributeSet) { + fn record(&self, val: T, attrs: &[KeyValue]) { for measure in &self.measures { - measure.call(val, attrs.clone()) + measure.call(val, AttributeSet::from(attrs)) } } } @@ -376,9 +377,9 @@ impl Observable { } impl AsyncInstrument for Observable { - fn observe(&self, measurement: T, attrs: AttributeSet) { + fn observe(&self, measurement: T, attrs: &[KeyValue]) { for measure in &self.measures { - measure.call(measurement, attrs.clone()) + measure.call(measurement, AttributeSet::from(attrs)) } } diff --git a/opentelemetry-sdk/src/metrics/internal/aggregate.rs b/opentelemetry-sdk/src/metrics/internal/aggregate.rs index 8486892701..08d6feec04 100644 --- a/opentelemetry-sdk/src/metrics/internal/aggregate.rs +++ b/opentelemetry-sdk/src/metrics/internal/aggregate.rs @@ -1,9 +1,12 @@ use std::{marker, sync::Arc}; use once_cell::sync::Lazy; -use opentelemetry::{AttributeSet, KeyValue}; +use opentelemetry::KeyValue; -use crate::metrics::data::{Aggregation, Gauge, Temporality}; +use crate::{ + metrics::data::{Aggregation, Gauge, Temporality}, + AttributeSet, +}; use super::{ exponential_histogram::ExpoHistogram, @@ -14,8 +17,10 @@ use super::{ }; const STREAM_CARDINALITY_LIMIT: u32 = 2000; -pub(crate) static STREAM_OVERFLOW_ATTRIBUTE_SET: Lazy = - Lazy::new(|| AttributeSet::from(&[KeyValue::new("otel.metric.overflow", "true")])); +pub(crate) static STREAM_OVERFLOW_ATTRIBUTE_SET: Lazy = Lazy::new(|| { + let key_values: [KeyValue; 1] = [KeyValue::new("otel.metric.overflow", "true")]; + AttributeSet::from(&key_values[..]) +}); /// Checks whether aggregator has hit cardinality limit for metric streams pub(crate) fn is_under_cardinality_limit(size: usize) -> bool { @@ -91,7 +96,7 @@ impl> AggregateBuilder { let filter = self.filter.as_ref().map(Arc::clone); move |n, mut attrs: AttributeSet| { if let Some(filter) = &filter { - attrs = attrs.clone_with(filter.as_ref()); + attrs.retain(filter.as_ref()); } f.call(n, attrs) } @@ -221,7 +226,7 @@ mod tests { let (measure, agg) = AggregateBuilder::::new(None, None).last_value(); let mut a = Gauge { data_points: vec![DataPoint { - attributes: AttributeSet::from(&[KeyValue::new("a", 1)]), + attributes: AttributeSet::from(&[KeyValue::new("a", 1)][..]), start_time: Some(SystemTime::now()), time: Some(SystemTime::now()), value: 1u64, @@ -229,7 +234,7 @@ mod tests { }], }; let new_attributes = [KeyValue::new("b", 2)]; - measure.call(2, AttributeSet::from(&new_attributes)); + measure.call(2, AttributeSet::from(&new_attributes[..])); let (count, new_agg) = agg.call(Some(&mut a)); @@ -238,7 +243,7 @@ mod tests { assert_eq!(a.data_points.len(), 1); assert_eq!( a.data_points[0].attributes, - AttributeSet::from(&new_attributes) + AttributeSet::from(&new_attributes[..]) ); assert_eq!(a.data_points[0].value, 2); } @@ -251,14 +256,14 @@ mod tests { let mut a = Sum { data_points: vec![ DataPoint { - attributes: AttributeSet::from(&[KeyValue::new("a1", 1)]), + attributes: AttributeSet::from(&[KeyValue::new("a1", 1)][..]), start_time: Some(SystemTime::now()), time: Some(SystemTime::now()), value: 1u64, exemplars: vec![], }, DataPoint { - attributes: AttributeSet::from(&[KeyValue::new("a2", 2)]), + attributes: AttributeSet::from(&[KeyValue::new("a2", 2)][..]), start_time: Some(SystemTime::now()), time: Some(SystemTime::now()), value: 2u64, @@ -273,7 +278,7 @@ mod tests { is_monotonic: false, }; let new_attributes = [KeyValue::new("b", 2)]; - measure.call(3, AttributeSet::from(&new_attributes)); + measure.call(3, AttributeSet::from(&new_attributes[..])); let (count, new_agg) = agg.call(Some(&mut a)); @@ -284,7 +289,7 @@ mod tests { assert_eq!(a.data_points.len(), 1); assert_eq!( a.data_points[0].attributes, - AttributeSet::from(&new_attributes) + AttributeSet::from(&new_attributes[..]) ); assert_eq!(a.data_points[0].value, 3); } @@ -297,14 +302,14 @@ mod tests { let mut a = Sum { data_points: vec![ DataPoint { - attributes: AttributeSet::from(&[KeyValue::new("a1", 1)]), + attributes: AttributeSet::from(&[KeyValue::new("a1", 1)][..]), start_time: Some(SystemTime::now()), time: Some(SystemTime::now()), value: 1u64, exemplars: vec![], }, DataPoint { - attributes: AttributeSet::from(&[KeyValue::new("a2", 2)]), + attributes: AttributeSet::from(&[KeyValue::new("a2", 2)][..]), start_time: Some(SystemTime::now()), time: Some(SystemTime::now()), value: 2u64, @@ -319,7 +324,7 @@ mod tests { is_monotonic: false, }; let new_attributes = [KeyValue::new("b", 2)]; - measure.call(3, AttributeSet::from(&new_attributes)); + measure.call(3, AttributeSet::from(&new_attributes[..])); let (count, new_agg) = agg.call(Some(&mut a)); @@ -330,7 +335,7 @@ mod tests { assert_eq!(a.data_points.len(), 1); assert_eq!( a.data_points[0].attributes, - AttributeSet::from(&new_attributes) + AttributeSet::from(&new_attributes[..]) ); assert_eq!(a.data_points[0].value, 3); } @@ -343,7 +348,7 @@ mod tests { .explicit_bucket_histogram(vec![1.0], true, true); let mut a = Histogram { data_points: vec![HistogramDataPoint { - attributes: AttributeSet::from(&[KeyValue::new("a2", 2)]), + attributes: AttributeSet::from(&[KeyValue::new("a2", 2)][..]), start_time: SystemTime::now(), time: SystemTime::now(), count: 2, @@ -361,7 +366,7 @@ mod tests { }, }; let new_attributes = [KeyValue::new("b", 2)]; - measure.call(3, AttributeSet::from(&new_attributes)); + measure.call(3, AttributeSet::from(&new_attributes[..])); let (count, new_agg) = agg.call(Some(&mut a)); @@ -371,7 +376,7 @@ mod tests { assert_eq!(a.data_points.len(), 1); assert_eq!( a.data_points[0].attributes, - AttributeSet::from(&new_attributes) + AttributeSet::from(&new_attributes[..]) ); assert_eq!(a.data_points[0].count, 1); assert_eq!(a.data_points[0].bounds, vec![1.0]); @@ -389,7 +394,7 @@ mod tests { .exponential_bucket_histogram(4, 20, true, true); let mut a = ExponentialHistogram { data_points: vec![ExponentialHistogramDataPoint { - attributes: AttributeSet::from(&[KeyValue::new("a2", 2)]), + attributes: AttributeSet::from(&[KeyValue::new("a2", 2)][..]), start_time: SystemTime::now(), time: SystemTime::now(), count: 2, @@ -416,7 +421,7 @@ mod tests { }, }; let new_attributes = [KeyValue::new("b", 2)]; - measure.call(3, AttributeSet::from(&new_attributes.clone())); + measure.call(3, AttributeSet::from(&new_attributes[..])); let (count, new_agg) = agg.call(Some(&mut a)); @@ -426,7 +431,7 @@ mod tests { assert_eq!(a.data_points.len(), 1); assert_eq!( a.data_points[0].attributes, - AttributeSet::from(&new_attributes) + AttributeSet::from(&new_attributes[..]) ); assert_eq!(a.data_points[0].count, 1); assert_eq!(a.data_points[0].min, Some(3)); diff --git a/opentelemetry-sdk/src/metrics/internal/exponential_histogram.rs b/opentelemetry-sdk/src/metrics/internal/exponential_histogram.rs index 5411637e05..189b61c553 100644 --- a/opentelemetry-sdk/src/metrics/internal/exponential_histogram.rs +++ b/opentelemetry-sdk/src/metrics/internal/exponential_histogram.rs @@ -1,9 +1,12 @@ use std::{collections::HashMap, f64::consts::LOG2_E, sync::Mutex, time::SystemTime}; use once_cell::sync::Lazy; -use opentelemetry::{metrics::MetricsError, AttributeSet}; +use opentelemetry::metrics::MetricsError; -use crate::metrics::data::{self, Aggregation, Temporality}; +use crate::{ + metrics::data::{self, Aggregation, Temporality}, + AttributeSet, +}; use super::Number; @@ -624,7 +627,7 @@ mod tests { } fn run_min_max_sum_f64() { - let alice = AttributeSet::from(&[KeyValue::new("user", "alice")]); + let alice = AttributeSet::from(&[KeyValue::new("user", "alice")][..]); struct Expected { min: f64, max: f64, @@ -685,7 +688,7 @@ mod tests { } fn run_min_max_sum + From>() { - let alice = AttributeSet::from(&[KeyValue::new("user", "alice")]); + let alice = AttributeSet::from(&[KeyValue::new("user", "alice")][..]); struct Expected { min: T, max: T, diff --git a/opentelemetry-sdk/src/metrics/internal/histogram.rs b/opentelemetry-sdk/src/metrics/internal/histogram.rs index 75acfe3f9e..45ac569e2b 100644 --- a/opentelemetry-sdk/src/metrics/internal/histogram.rs +++ b/opentelemetry-sdk/src/metrics/internal/histogram.rs @@ -1,8 +1,8 @@ use std::{collections::HashMap, sync::Mutex, time::SystemTime}; -use crate::metrics::data::HistogramDataPoint; use crate::metrics::data::{self, Aggregation, Temporality}; -use opentelemetry::{global, metrics::MetricsError, AttributeSet}; +use crate::{attributes::AttributeSet, metrics::data::HistogramDataPoint}; +use opentelemetry::{global, metrics::MetricsError}; use super::{ aggregate::{is_under_cardinality_limit, STREAM_OVERFLOW_ATTRIBUTE_SET}, diff --git a/opentelemetry-sdk/src/metrics/internal/last_value.rs b/opentelemetry-sdk/src/metrics/internal/last_value.rs index f5d9f8d997..e5b2364b5b 100644 --- a/opentelemetry-sdk/src/metrics/internal/last_value.rs +++ b/opentelemetry-sdk/src/metrics/internal/last_value.rs @@ -4,8 +4,8 @@ use std::{ time::SystemTime, }; -use crate::metrics::data::DataPoint; -use opentelemetry::{global, metrics::MetricsError, AttributeSet}; +use crate::{attributes::AttributeSet, metrics::data::DataPoint}; +use opentelemetry::{global, metrics::MetricsError}; use super::{ aggregate::{is_under_cardinality_limit, STREAM_OVERFLOW_ATTRIBUTE_SET}, diff --git a/opentelemetry-sdk/src/metrics/internal/sum.rs b/opentelemetry-sdk/src/metrics/internal/sum.rs index b6d3233217..3fac77c459 100644 --- a/opentelemetry-sdk/src/metrics/internal/sum.rs +++ b/opentelemetry-sdk/src/metrics/internal/sum.rs @@ -4,8 +4,9 @@ use std::{ time::SystemTime, }; +use crate::attributes::AttributeSet; use crate::metrics::data::{self, Aggregation, DataPoint, Temporality}; -use opentelemetry::{global, metrics::MetricsError, AttributeSet}; +use opentelemetry::{global, metrics::MetricsError}; use super::{ aggregate::{is_under_cardinality_limit, STREAM_OVERFLOW_ATTRIBUTE_SET}, diff --git a/opentelemetry-sdk/src/metrics/meter.rs b/opentelemetry-sdk/src/metrics/meter.rs index 26501f85d7..c801adcb0a 100644 --- a/opentelemetry-sdk/src/metrics/meter.rs +++ b/opentelemetry-sdk/src/metrics/meter.rs @@ -1,7 +1,6 @@ use core::fmt; use std::{any::Any, borrow::Cow, collections::HashSet, sync::Arc}; -use opentelemetry::AttributeSet; use opentelemetry::{ global, metrics::{ @@ -10,6 +9,7 @@ use opentelemetry::{ InstrumentProvider, MetricsError, ObservableCounter, ObservableGauge, ObservableUpDownCounter, Observer as ApiObserver, Result, Unit, UpDownCounter, }, + KeyValue, }; use crate::instrumentation::Scope; @@ -647,7 +647,7 @@ impl Observer { } impl ApiObserver for Observer { - fn observe_f64(&self, inst: &dyn AsyncInstrument, measurement: f64, attrs: AttributeSet) { + fn observe_f64(&self, inst: &dyn AsyncInstrument, measurement: f64, attrs: &[KeyValue]) { if let Some(f64_obs) = inst.as_any().downcast_ref::>() { if self.f64s.contains(&f64_obs.id) { f64_obs.observe(measurement, attrs) @@ -666,7 +666,7 @@ impl ApiObserver for Observer { } } - fn observe_u64(&self, inst: &dyn AsyncInstrument, measurement: u64, attrs: AttributeSet) { + fn observe_u64(&self, inst: &dyn AsyncInstrument, measurement: u64, attrs: &[KeyValue]) { if let Some(u64_obs) = inst.as_any().downcast_ref::>() { if self.u64s.contains(&u64_obs.id) { u64_obs.observe(measurement, attrs) @@ -685,7 +685,7 @@ impl ApiObserver for Observer { } } - fn observe_i64(&self, inst: &dyn AsyncInstrument, measurement: i64, attrs: AttributeSet) { + fn observe_i64(&self, inst: &dyn AsyncInstrument, measurement: i64, attrs: &[KeyValue]) { if let Some(i64_obs) = inst.as_any().downcast_ref::>() { if self.i64s.contains(&i64_obs.id) { i64_obs.observe(measurement, attrs) diff --git a/opentelemetry-sdk/src/metrics/mod.rs b/opentelemetry-sdk/src/metrics/mod.rs index 622133aa70..021ee3d469 100644 --- a/opentelemetry-sdk/src/metrics/mod.rs +++ b/opentelemetry-sdk/src/metrics/mod.rs @@ -13,7 +13,6 @@ //! metrics::{MeterProvider, Unit}, //! KeyValue, //! }; -//! use opentelemetry::AttributeSet; //! use opentelemetry_sdk::{metrics::SdkMeterProvider, Resource}; //! //! // Generate SDK configuration, resource, views, etc @@ -32,8 +31,7 @@ //! .init(); //! //! // use instruments to record measurements -//! let attributes = AttributeSet::from(&[KeyValue::new("rate", "standard")]); -//! counter.add(10, attributes); +//! counter.add(10, &[KeyValue::new("rate", "standard")]); //! ``` //! //! [Resource]: crate::Resource @@ -64,7 +62,6 @@ pub use view::*; mod tests { use super::*; use crate::{runtime, testing::metrics::InMemoryMetricsExporter}; - use opentelemetry::AttributeSet; use opentelemetry::{ metrics::{MeterProvider as _, Unit}, KeyValue, @@ -183,9 +180,9 @@ mod tests { .with_description("my_description") .init(); - let attribute = AttributeSet::from(&[KeyValue::new("key1", "value1")]); - counter.add(10, attribute.clone()); - counter_duplicated.add(5, attribute); + let attribute = vec![KeyValue::new("key1", "value1")]; + counter.add(10, &attribute); + counter_duplicated.add(5, &attribute); meter_provider.force_flush().unwrap(); @@ -269,6 +266,7 @@ mod tests { // "multi_thread" tokio flavor must be used else flush won't // be able to make progress! #[tokio::test(flavor = "multi_thread", worker_threads = 1)] + #[ignore = "Spatial aggregation is not yet implemented."] async fn spatial_aggregation_when_view_drops_attributes_observable_counter() { // cargo test spatial_aggregation_when_view_drops_attributes_observable_counter --features=metrics,testing @@ -301,8 +299,7 @@ mod tests { KeyValue::new("statusCode", "200"), KeyValue::new("verb", "get"), ] - .as_slice() - .into(), + .as_ref(), ); observer.observe_u64( @@ -312,8 +309,7 @@ mod tests { KeyValue::new("statusCode", "200"), KeyValue::new("verb", "post"), ] - .as_slice() - .into(), + .as_ref(), ); observer.observe_u64( @@ -323,8 +319,7 @@ mod tests { KeyValue::new("statusCode", "500"), KeyValue::new("verb", "get"), ] - .as_slice() - .into(), + .as_ref(), ); }) .expect("Expected to register callback"); diff --git a/opentelemetry-sdk/src/resource/mod.rs b/opentelemetry-sdk/src/resource/mod.rs index cf6f18440a..79ce0122eb 100644 --- a/opentelemetry-sdk/src/resource/mod.rs +++ b/opentelemetry-sdk/src/resource/mod.rs @@ -30,7 +30,6 @@ pub use os::OsResourceDetector; pub use process::ProcessResourceDetector; pub use telemetry::TelemetryResourceDetector; -use opentelemetry::AttributeSet; use opentelemetry::{Key, KeyValue, Value}; use std::borrow::Cow; use std::collections::{hash_map, HashMap}; @@ -191,16 +190,6 @@ impl Resource { pub fn get(&self, key: Key) -> Option { self.attrs.get(&key).cloned() } - - /// Creates a new attribute set from the resource's current attributes - pub fn to_attribute_set(&self) -> AttributeSet { - let key_values = self - .iter() - .map(|(key, value)| KeyValue::new(key.clone(), value.clone())) - .collect::>(); - - AttributeSet::from(&key_values) - } } /// An owned iterator over the entries of a `Resource`. @@ -376,24 +365,4 @@ mod tests { }, ) } - - #[test] - fn can_create_attribute_set_from_resource() { - let resource = Resource::new([KeyValue::new("key1", "value1"), KeyValue::new("key2", 3)]); - - let set = resource.to_attribute_set(); - let mut kvs = set.iter().collect::>(); - - assert_eq!(kvs.len(), 2, "Incorrect number of attributes"); - - kvs.sort_by(|kv1, kv2| kv1.0.cmp(kv2.0)); - assert_eq!(kvs[0].0, &Key::from("key1"), "Unexpected first key"); - assert_eq!( - kvs[0].1, - &Value::String("value1".into()), - "Unexpected first value" - ); - assert_eq!(kvs[1].0, &Key::from("key2"), "Unexpected second key"); - assert_eq!(kvs[1].1, &Value::I64(3), "Unexpected second value"); - } } diff --git a/opentelemetry-sdk/src/testing/metrics/in_memory_exporter.rs b/opentelemetry-sdk/src/testing/metrics/in_memory_exporter.rs index 3edff3d2dd..d28cd4062f 100644 --- a/opentelemetry-sdk/src/testing/metrics/in_memory_exporter.rs +++ b/opentelemetry-sdk/src/testing/metrics/in_memory_exporter.rs @@ -29,7 +29,6 @@ use std::sync::{Arc, Mutex}; /// ``` ///# use opentelemetry_sdk::{metrics, runtime}; ///# use opentelemetry::{KeyValue}; -///# use opentelemetry::AttributeSet; ///# use opentelemetry::metrics::MeterProvider; ///# use opentelemetry_sdk::testing::metrics::InMemoryMetricsExporter; ///# use opentelemetry_sdk::metrics::PeriodicReader; @@ -47,8 +46,7 @@ use std::sync::{Arc, Mutex}; /// // Create and record metrics using the MeterProvider /// let meter = meter_provider.meter(std::borrow::Cow::Borrowed("example")); /// let counter = meter.u64_counter("my_counter").init(); -/// let attributes = AttributeSet::from(&[KeyValue::new("key", "value")]); -/// counter.add(1, attributes); +/// counter.add(1, &[KeyValue::new("key", "value")]); /// /// meter_provider.force_flush().unwrap(); /// diff --git a/opentelemetry-stdout/src/common.rs b/opentelemetry-stdout/src/common.rs index 0897fbb22a..0098b1a6b3 100644 --- a/opentelemetry-stdout/src/common.rs +++ b/opentelemetry-stdout/src/common.rs @@ -12,8 +12,8 @@ use serde::{Serialize, Serializer}; #[derive(Debug, Serialize, Clone, Hash, Eq, PartialEq)] pub(crate) struct AttributeSet(pub BTreeMap); -impl From<&opentelemetry::AttributeSet> for AttributeSet { - fn from(value: &opentelemetry::AttributeSet) -> Self { +impl From<&opentelemetry_sdk::AttributeSet> for AttributeSet { + fn from(value: &opentelemetry_sdk::AttributeSet) -> Self { AttributeSet( value .iter() diff --git a/opentelemetry-stdout/src/logs/transform.rs b/opentelemetry-stdout/src/logs/transform.rs index dba3b830ca..9612cf1ff6 100644 --- a/opentelemetry-stdout/src/logs/transform.rs +++ b/opentelemetry-stdout/src/logs/transform.rs @@ -4,7 +4,7 @@ use crate::common::{ as_human_readable, as_opt_human_readable, as_opt_unix_nano, as_unix_nano, KeyValue, Resource, Scope, Value, }; -use opentelemetry::AttributeSet; +use opentelemetry_sdk::AttributeSet; use serde::Serialize; /// Transformed logs data that can be serialized. @@ -26,7 +26,7 @@ impl From> for LogData { let resource: Resource = sdk_log.resource.as_ref().into(); let rl = resource_logs - .entry(sdk_log.resource.as_ref().to_attribute_set()) + .entry(sdk_log.resource.as_ref().into()) .or_insert_with(move || ResourceLogs { resource, scope_logs: Vec::with_capacity(1), diff --git a/opentelemetry-stdout/src/trace/transform.rs b/opentelemetry-stdout/src/trace/transform.rs index 335ddc1285..73729606df 100644 --- a/opentelemetry-stdout/src/trace/transform.rs +++ b/opentelemetry-stdout/src/trace/transform.rs @@ -1,5 +1,5 @@ use crate::common::{as_human_readable, as_unix_nano, KeyValue, Resource, Scope}; -use opentelemetry::AttributeSet; +use opentelemetry_sdk::AttributeSet; use serde::{Serialize, Serializer}; use std::{borrow::Cow, collections::HashMap, time::SystemTime}; @@ -20,7 +20,7 @@ impl From> for SpanData { let resource = sdk_span.resource.as_ref().into(); let rs = resource_spans - .entry(sdk_span.resource.as_ref().to_attribute_set()) + .entry(sdk_span.resource.as_ref().into()) .or_insert_with(move || ResourceSpans { resource, scope_spans: Vec::with_capacity(1), diff --git a/opentelemetry/CHANGELOG.md b/opentelemetry/CHANGELOG.md index db83c515ae..b57647e5ab 100644 --- a/opentelemetry/CHANGELOG.md +++ b/opentelemetry/CHANGELOG.md @@ -17,16 +17,6 @@ gains, and avoids `IndexMap` dependency. This affects `body` and `attributes` of `LogRecord`. [#1353](https://github.com/open-telemetry/opentelemetry-rust/pull/1353) - Add `TextMapCompositePropagator` [#1373](https://github.com/open-telemetry/opentelemetry-rust/pull/1373) -- `Counters`, `UpDownCounters`, `Gauges`, and `Histograms` now take an - `Into` as the parameter type for recording metric values. This - allows passing in a precreated `AttributeSet` for better performance when the - same set of attributes are used across instruments. This is backward - compatible with previous calls passing in `&[KeyValue]`. - [#1421](https://github.com/open-telemetry/opentelemetry-rust/pull/1421) -- Observable instruments no longer accept `&[KeyValue]` parameters for - `observe()` calls, and only accept a precreated `AttributeSet` - value. - [#1421](https://github.com/open-telemetry/opentelemetry-rust/pull/1421) ### Removed diff --git a/opentelemetry/Cargo.toml b/opentelemetry/Cargo.toml index 8a4ad4b6c0..388cc24cce 100644 --- a/opentelemetry/Cargo.toml +++ b/opentelemetry/Cargo.toml @@ -24,7 +24,6 @@ rustdoc-args = ["--cfg", "docsrs"] futures-core = { workspace = true } futures-sink = "0.3" once_cell = "1.13.0" -ordered-float = "4.0" pin-project-lite = { workspace = true, optional = true } thiserror = { workspace = true } urlencoding = "2.1.2" diff --git a/opentelemetry/src/attributes/mod.rs b/opentelemetry/src/attributes/mod.rs deleted file mode 100644 index ca3029549b..0000000000 --- a/opentelemetry/src/attributes/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -//! Utilities for managing attributes for metrics - -mod set; - -pub use set::{AttributeSet, ToKeyValue}; diff --git a/opentelemetry/src/attributes/set.rs b/opentelemetry/src/attributes/set.rs deleted file mode 100644 index 4f20c8fdcb..0000000000 --- a/opentelemetry/src/attributes/set.rs +++ /dev/null @@ -1,406 +0,0 @@ -use once_cell::sync::Lazy; -use std::collections::hash_map::DefaultHasher; -use std::collections::HashSet; -use std::sync::Arc; -use std::{ - cmp::Ordering, - hash::{Hash, Hasher}, -}; - -use crate::{Array, Key, KeyValue, Value}; -use ordered_float::OrderedFloat; - -#[derive(Clone, Debug)] -struct HashKeyValue(KeyValue); - -impl Hash for HashKeyValue { - fn hash(&self, state: &mut H) { - self.0.key.hash(state); - match &self.0.value { - Value::F64(f) => OrderedFloat(*f).hash(state), - Value::Array(a) => match a { - Array::Bool(b) => b.hash(state), - Array::I64(i) => i.hash(state), - Array::F64(f) => f.iter().for_each(|f| OrderedFloat(*f).hash(state)), - Array::String(s) => s.hash(state), - }, - Value::Bool(b) => b.hash(state), - Value::I64(i) => i.hash(state), - Value::String(s) => s.hash(state), - }; - } -} - -impl PartialOrd for HashKeyValue { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for HashKeyValue { - fn cmp(&self, other: &Self) -> Ordering { - match self.0.key.cmp(&other.0.key) { - Ordering::Equal => match type_order(&self.0.value).cmp(&type_order(&other.0.value)) { - Ordering::Equal => match (&self.0.value, &other.0.value) { - (Value::F64(f), Value::F64(of)) => OrderedFloat(*f).cmp(&OrderedFloat(*of)), - (Value::Array(Array::Bool(b)), Value::Array(Array::Bool(ob))) => b.cmp(ob), - (Value::Array(Array::I64(i)), Value::Array(Array::I64(oi))) => i.cmp(oi), - (Value::Array(Array::String(s)), Value::Array(Array::String(os))) => s.cmp(os), - (Value::Array(Array::F64(f)), Value::Array(Array::F64(of))) => { - match f.len().cmp(&of.len()) { - Ordering::Equal => f - .iter() - .map(|x| OrderedFloat(*x)) - .collect::>() - .cmp(&of.iter().map(|x| OrderedFloat(*x)).collect()), - other => other, - } - } - (Value::Bool(b), Value::Bool(ob)) => b.cmp(ob), - (Value::I64(i), Value::I64(oi)) => i.cmp(oi), - (Value::String(s), Value::String(os)) => s.cmp(os), - _ => Ordering::Equal, - }, - other => other, // 2nd order by value types - }, - other => other, // 1st order by key - } - } -} - -fn type_order(v: &Value) -> u8 { - match v { - Value::Bool(_) => 1, - Value::I64(_) => 2, - Value::F64(_) => 3, - Value::String(_) => 4, - Value::Array(a) => match a { - Array::Bool(_) => 5, - Array::I64(_) => 6, - Array::F64(_) => 7, - Array::String(_) => 8, - }, - } -} - -impl PartialEq for HashKeyValue { - fn eq(&self, other: &Self) -> bool { - self.0.key == other.0.key - && match (&self.0.value, &other.0.value) { - (Value::F64(f), Value::F64(of)) => OrderedFloat(*f).eq(&OrderedFloat(*of)), - (Value::Array(Array::F64(f)), Value::Array(Array::F64(of))) => { - f.len() == of.len() - && f.iter() - .zip(of.iter()) - .all(|(f, of)| OrderedFloat(*f).eq(&OrderedFloat(*of))) - } - (non_float, other_non_float) => non_float.eq(other_non_float), - } - } -} - -impl Eq for HashKeyValue {} - -static EMPTY_SET: Lazy> = - Lazy::new(|| Arc::new(InternalAttributeSet::new(Vec::with_capacity(0)))); - -#[derive(Eq, PartialEq, Debug)] -struct InternalAttributeSet { - key_values: Vec, - hash: u64, -} - -impl InternalAttributeSet { - fn new(mut values: Vec) -> Self { - values.sort_unstable(); - let mut hasher = DefaultHasher::new(); - values.iter().fold(&mut hasher, |mut hasher, item| { - item.hash(&mut hasher); - hasher - }); - - InternalAttributeSet { - key_values: values, - hash: hasher.finish(), - } - } -} - -impl> From for InternalAttributeSet -where - ::IntoIter: DoubleEndedIterator + ExactSizeIterator, -{ - fn from(value: I) -> Self { - let iter = value.into_iter(); - let mut seen_keys = HashSet::with_capacity(iter.len()); - let vec = iter - .into_iter() - .rev() - .filter_map(|kv| { - if seen_keys.contains(&kv.key) { - None - } else { - seen_keys.insert(kv.key.clone()); - Some(HashKeyValue(kv)) - } - }) - .collect::>(); - - InternalAttributeSet::new(vec) - } -} - -impl Hash for InternalAttributeSet { - fn hash(&self, state: &mut H) { - state.write_u64(self.hash) - } -} - -/// Trait declaring that a type can be converted into a `KeyValue` -pub trait ToKeyValue { - /// Create a `KeyValue` from the current instance. - fn to_key_value(self) -> KeyValue; -} - -impl ToKeyValue for KeyValue { - fn to_key_value(self) -> KeyValue { - self - } -} - -impl ToKeyValue for &KeyValue { - fn to_key_value(self) -> KeyValue { - self.clone() - } -} - -/// A unique set of attributes that can be used as instrument identifiers. -/// -/// Cloning of an attribute set is cheap, as all clones share a reference to the underlying -/// attribute data. -/// -/// This must implement [Hash], [PartialEq], and [Eq] so it may be used as -/// HashMap keys and other de-duplication methods. -#[derive(Clone, Debug, Hash, PartialEq, Eq)] -pub struct AttributeSet(Arc); - -impl From for AttributeSet -where - KV: ToKeyValue, - I: IntoIterator, - ::IntoIter: DoubleEndedIterator + ExactSizeIterator, -{ - fn from(values: I) -> Self { - AttributeSet(Arc::new(InternalAttributeSet::from( - values.into_iter().map(ToKeyValue::to_key_value), - ))) - } -} - -impl AttributeSet { - /// Returns the number of elements in the set. - pub fn len(&self) -> usize { - self.0.key_values.len() - } - - /// Returns `true` if the set contains no elements. - pub fn is_empty(&self) -> bool { - self.0.key_values.is_empty() - } - - /// Creates a new attribute set that retains only the attributes specified by the predicate. - pub fn clone_with(&self, f: F) -> AttributeSet - where - F: Fn(&KeyValue) -> bool, - { - let key_values = self - .0 - .key_values - .iter() - .filter(|kv| f(&kv.0)) - .cloned() - .collect::>(); - - AttributeSet(Arc::new(InternalAttributeSet::new(key_values))) - } - - /// Iterate over key value pairs in the set - pub fn iter(&self) -> impl Iterator { - self.0.key_values.iter().map(|kv| (&kv.0.key, &kv.0.value)) - } -} - -impl Default for AttributeSet { - fn default() -> Self { - AttributeSet(EMPTY_SET.clone()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::StringValue; - - #[test] - fn can_create_attribute_set_from_array() { - let array = [KeyValue::new("key1", "value1"), KeyValue::new("key2", 3)]; - - let set = AttributeSet::from(&array); - let mut kvs = set.iter().collect::>(); - - assert_eq!(kvs.len(), 2, "Incorrect number of attributes"); - - kvs.sort_by(|kv1, kv2| kv1.0.cmp(kv2.0)); - assert_eq!(kvs[0].0, &Key::from("key1"), "Unexpected first key"); - assert_eq!( - kvs[0].1, - &Value::String("value1".into()), - "Unexpected first value" - ); - assert_eq!(kvs[1].0, &Key::from("key2"), "Unexpected second key"); - assert_eq!(kvs[1].1, &Value::I64(3), "Unexpected second value"); - } - - #[test] - fn can_create_attribute_set_from_owned_vec() { - let vec = vec![KeyValue::new("key1", "value1"), KeyValue::new("key2", 3)]; - - let set = AttributeSet::from(vec); - let mut kvs = set.iter().collect::>(); - - assert_eq!(kvs.len(), 2, "Incorrect number of attributes"); - - kvs.sort_by(|kv1, kv2| kv1.0.cmp(kv2.0)); - assert_eq!(kvs[0].0, &Key::from("key1"), "Unexpected first key"); - assert_eq!( - kvs[0].1, - &Value::String("value1".into()), - "Unexpected first value" - ); - assert_eq!(kvs[1].0, &Key::from("key2"), "Unexpected second key"); - assert_eq!(kvs[1].1, &Value::I64(3), "Unexpected second value"); - } - - #[test] - fn two_sets_with_same_key_values_in_different_orders_are_equal() { - let array1 = [ - KeyValue::new("key1", "value1"), - KeyValue::new("key2", 3), - KeyValue::new("key3", Value::Array(Array::Bool(vec![true]))), - KeyValue::new("key4", Value::Array(Array::F64(vec![1.5]))), - KeyValue::new("key5", Value::Array(Array::I64(vec![15]))), - KeyValue::new( - "key6", - Value::Array(Array::String(vec![StringValue::from("test")])), - ), - ]; - - let array2 = [ - KeyValue::new( - "key6", - Value::Array(Array::String(vec![StringValue::from("test")])), - ), - KeyValue::new("key1", "value1"), - KeyValue::new("key3", Value::Array(Array::Bool(vec![true]))), - KeyValue::new("key4", Value::Array(Array::F64(vec![1.5]))), - KeyValue::new("key5", Value::Array(Array::I64(vec![15]))), - KeyValue::new("key2", 3), - ]; - - let set1 = AttributeSet::from(&array1); - let set2 = AttributeSet::from(&array2); - - assert_eq!(set1, set2); - } - - #[test] - fn two_sets_with_same_key_values_in_different_orders_have_same_hash() { - let array1 = [ - KeyValue::new("key1", "value1"), - KeyValue::new("key2", 3), - KeyValue::new("key3", Value::Array(Array::Bool(vec![true]))), - KeyValue::new("key4", Value::Array(Array::F64(vec![1.5]))), - KeyValue::new("key5", Value::Array(Array::I64(vec![15]))), - KeyValue::new( - "key6", - Value::Array(Array::String(vec![StringValue::from("test")])), - ), - ]; - - let array2 = [ - KeyValue::new( - "key6", - Value::Array(Array::String(vec![StringValue::from("test")])), - ), - KeyValue::new("key1", "value1"), - KeyValue::new("key3", Value::Array(Array::Bool(vec![true]))), - KeyValue::new("key4", Value::Array(Array::F64(vec![1.5]))), - KeyValue::new("key5", Value::Array(Array::I64(vec![15]))), - KeyValue::new("key2", 3), - ]; - - let set1 = AttributeSet::from(&array1); - let set2 = AttributeSet::from(&array2); - - let mut hasher1 = DefaultHasher::new(); - let mut hasher2 = DefaultHasher::new(); - set1.hash(&mut hasher1); - set2.hash(&mut hasher2); - - assert_eq!(hasher1.finish(), hasher2.finish()); - } - - #[test] - fn clone_with_removes_unspecified_key_values() { - let array = [ - KeyValue::new("key1", "value1"), - KeyValue::new("key2", 3), - KeyValue::new("key3", 4), - ]; - - let set = AttributeSet::from(&array); - let set2 = set.clone_with(|kv| kv.key == Key::new("key2")); - - assert_ne!(set, set2, "Both sets were unexpectedly equal"); - assert_eq!(set2.len(), 1, "Expected only one attribute in new set"); - - let kvs = set2.iter().collect::>(); - assert_eq!(kvs[0].0, &Key::from("key2"), "Unexpected key"); - assert_eq!(kvs[0].1, &Value::I64(3), "Unexpected value"); - } - - #[test] - fn len_returns_accurate_value() { - let array = [KeyValue::new("key1", "value1"), KeyValue::new("key2", 3)]; - - let set = AttributeSet::from(&array); - let kvs = set.iter().collect::>(); - - assert_eq!(set.len(), kvs.len()); - } - - #[test] - fn empty_when_no_attributes_provided() { - let set = AttributeSet::from(&[]); - assert!(set.is_empty()); - } - - #[test] - fn default_set_has_no_attributes() { - let set = AttributeSet::default(); - assert!(set.is_empty()); - assert_eq!(set.len(), 0); - } - - #[test] - fn last_key_wins_for_deduplication() { - let array = [KeyValue::new("key1", "value1"), KeyValue::new("key1", 3)]; - - let set = AttributeSet::from(&array); - let kvs = set.iter().collect::>(); - - assert_eq!(set.len(), 1, "Expected only a single key value pair"); - assert_eq!(kvs[0].0, &Key::new("key1"), "Unexpected key"); - assert_eq!(kvs[0].1, &Value::I64(3), "Unexpected value"); - } -} diff --git a/opentelemetry/src/global/mod.rs b/opentelemetry/src/global/mod.rs index 3af9145dc5..790343968c 100644 --- a/opentelemetry/src/global/mod.rs +++ b/opentelemetry/src/global/mod.rs @@ -92,7 +92,7 @@ //! # #[cfg(feature="metrics")] //! # { //! use opentelemetry::metrics::{Meter, noop::NoopMeterProvider}; -//! use opentelemetry::{AttributeSet, global, KeyValue}; +//! use opentelemetry::{global, KeyValue}; //! //! fn init_meter() { //! let provider = NoopMeterProvider::new(); @@ -108,8 +108,7 @@ //! let counter = meter.u64_counter("my_counter").init(); //! //! // record metrics -//! let attributes = AttributeSet::from(&[KeyValue::new("mykey", "myvalue")]); -//! counter.add(1, attributes); +//! counter.add(1, &[KeyValue::new("mykey", "myvalue")]); //! } //! //! // in main or other app start @@ -123,7 +122,7 @@ //! ``` //! # #[cfg(feature="metrics")] //! # { -//! use opentelemetry::{AttributeSet, global, KeyValue}; +//! use opentelemetry::{global, KeyValue}; //! //! pub fn my_traced_library_function() { //! // End users of your library will configure their global meter provider @@ -132,8 +131,7 @@ //! let counter = tracer.u64_counter("my_counter").init(); //! //! // record metrics -//! let attributes = AttributeSet::from(&[KeyValue::new("mykey", "myvalue")]); -//! counter.add(1, attributes); +//! counter.add(1, &[KeyValue::new("mykey", "myvalue")]); //! } //! # } //! ``` diff --git a/opentelemetry/src/lib.rs b/opentelemetry/src/lib.rs index 2e125817ab..09c8b58770 100644 --- a/opentelemetry/src/lib.rs +++ b/opentelemetry/src/lib.rs @@ -72,7 +72,7 @@ //! ``` //! # #[cfg(feature = "metrics")] //! # { -//! use opentelemetry::{AttributeSet, global, KeyValue}; +//! use opentelemetry::{global, KeyValue}; //! //! // get a meter from a provider //! let meter = global::meter("my_service"); @@ -80,11 +80,8 @@ //! // create an instrument //! let counter = meter.u64_counter("my_counter").init(); //! -//! // Form the attributes -//! let attributes = AttributeSet::from(&[KeyValue::new("http.client_ip", "83.164.160.102")]); -//! //! // record a measurement -//! counter.add(1, attributes); +//! counter.add(1, &[KeyValue::new("http.client_ip", "83.164.160.102")]); //! # } //! ``` //! @@ -215,9 +212,6 @@ pub mod global; pub mod baggage; -mod attributes; -pub use attributes::{AttributeSet, ToKeyValue}; - mod context; pub use context::{Context, ContextGuard}; diff --git a/opentelemetry/src/metrics/instruments/counter.rs b/opentelemetry/src/metrics/instruments/counter.rs index f437e08099..171b6a49ea 100644 --- a/opentelemetry/src/metrics/instruments/counter.rs +++ b/opentelemetry/src/metrics/instruments/counter.rs @@ -1,5 +1,7 @@ -use crate::attributes::AttributeSet; -use crate::metrics::{AsyncInstrument, AsyncInstrumentBuilder, InstrumentBuilder, MetricsError}; +use crate::{ + metrics::{AsyncInstrument, AsyncInstrumentBuilder, InstrumentBuilder, MetricsError}, + KeyValue, +}; use core::fmt; use std::sync::Arc; use std::{any::Any, convert::TryFrom}; @@ -7,7 +9,7 @@ use std::{any::Any, convert::TryFrom}; /// An SDK implemented instrument that records increasing values. pub trait SyncCounter { /// Records an increment to the counter. - fn add(&self, value: T, attributes: AttributeSet); + fn add(&self, value: T, attributes: &[KeyValue]); } /// An instrument that records increasing values. @@ -30,8 +32,8 @@ impl Counter { } /// Records an increment to the counter. - pub fn add(&self, value: T, attributes: impl Into) { - self.0.add(value, attributes.into()) + pub fn add(&self, value: T, attributes: &[KeyValue]) { + self.0.add(value, attributes) } } @@ -85,7 +87,7 @@ impl ObservableCounter { /// It is only valid to call this within a callback. If called outside of the /// registered callback it should have no effect on the instrument, and an /// error will be reported via the error handler. - pub fn observe(&self, value: T, attributes: AttributeSet) { + pub fn observe(&self, value: T, attributes: &[KeyValue]) { self.0.observe(value, attributes) } @@ -96,7 +98,7 @@ impl ObservableCounter { } impl AsyncInstrument for ObservableCounter { - fn observe(&self, measurement: T, attributes: AttributeSet) { + fn observe(&self, measurement: T, attributes: &[KeyValue]) { self.0.observe(measurement, attributes) } diff --git a/opentelemetry/src/metrics/instruments/gauge.rs b/opentelemetry/src/metrics/instruments/gauge.rs index 0f64c01018..ab9fb2e05d 100644 --- a/opentelemetry/src/metrics/instruments/gauge.rs +++ b/opentelemetry/src/metrics/instruments/gauge.rs @@ -1,6 +1,6 @@ use crate::{ - attributes::AttributeSet, metrics::{AsyncInstrument, AsyncInstrumentBuilder, InstrumentBuilder, MetricsError}, + KeyValue, }; use core::fmt; use std::sync::Arc; @@ -9,7 +9,7 @@ use std::{any::Any, convert::TryFrom}; /// An SDK implemented instrument that records independent values pub trait SyncGauge { /// Records an independent value. - fn record(&self, value: T, attributes: AttributeSet); + fn record(&self, value: T, attributes: &[KeyValue]); } /// An instrument that records independent values @@ -32,8 +32,8 @@ impl Gauge { } /// Records an independent value. - pub fn record(&self, value: T, attributes: impl Into) { - self.0.record(value, attributes.into()) + pub fn record(&self, value: T, attributes: &[KeyValue]) { + self.0.record(value, attributes) } } @@ -92,7 +92,7 @@ impl ObservableGauge { /// It is only valid to call this within a callback. If called outside of the /// registered callback it should have no effect on the instrument, and an /// error will be reported via the error handler. - pub fn observe(&self, measurement: T, attributes: AttributeSet) { + pub fn observe(&self, measurement: T, attributes: &[KeyValue]) { self.0.observe(measurement, attributes) } @@ -103,7 +103,7 @@ impl ObservableGauge { } impl AsyncInstrument for ObservableGauge { - fn observe(&self, measurement: M, attributes: AttributeSet) { + fn observe(&self, measurement: M, attributes: &[KeyValue]) { self.observe(measurement, attributes) } diff --git a/opentelemetry/src/metrics/instruments/histogram.rs b/opentelemetry/src/metrics/instruments/histogram.rs index d4ec5cad61..c6246ebee2 100644 --- a/opentelemetry/src/metrics/instruments/histogram.rs +++ b/opentelemetry/src/metrics/instruments/histogram.rs @@ -1,5 +1,7 @@ -use crate::attributes::AttributeSet; -use crate::metrics::{InstrumentBuilder, MetricsError}; +use crate::{ + metrics::{InstrumentBuilder, MetricsError}, + KeyValue, +}; use core::fmt; use std::convert::TryFrom; use std::sync::Arc; @@ -7,7 +9,7 @@ use std::sync::Arc; /// An SDK implemented instrument that records a distribution of values. pub trait SyncHistogram { /// Adds an additional value to the distribution. - fn record(&self, value: T, attributes: AttributeSet); + fn record(&self, value: T, attributes: &[KeyValue]); } /// An instrument that records a distribution of values. @@ -30,8 +32,8 @@ impl Histogram { } /// Adds an additional value to the distribution. - pub fn record(&self, value: T, attributes: impl Into) { - self.0.record(value, attributes.into()) + pub fn record(&self, value: T, attributes: &[KeyValue]) { + self.0.record(value, attributes) } } diff --git a/opentelemetry/src/metrics/instruments/mod.rs b/opentelemetry/src/metrics/instruments/mod.rs index 446e60c705..137712d735 100644 --- a/opentelemetry/src/metrics/instruments/mod.rs +++ b/opentelemetry/src/metrics/instruments/mod.rs @@ -1,5 +1,5 @@ -use crate::attributes::AttributeSet; use crate::metrics::{Meter, MetricsError, Result, Unit}; +use crate::KeyValue; use core::fmt; use std::any::Any; use std::borrow::Cow; @@ -17,7 +17,7 @@ pub trait AsyncInstrument: Send + Sync { /// Observes the state of the instrument. /// /// It is only valid to call this within a callback. - fn observe(&self, measurement: T, attributes: AttributeSet); + fn observe(&self, measurement: T, attributes: &[KeyValue]); /// Used for SDKs to downcast instruments in callbacks. fn as_any(&self) -> Arc; diff --git a/opentelemetry/src/metrics/instruments/up_down_counter.rs b/opentelemetry/src/metrics/instruments/up_down_counter.rs index 0ac1833d55..1134ecedad 100644 --- a/opentelemetry/src/metrics/instruments/up_down_counter.rs +++ b/opentelemetry/src/metrics/instruments/up_down_counter.rs @@ -1,5 +1,7 @@ -use crate::attributes::AttributeSet; -use crate::metrics::{InstrumentBuilder, MetricsError}; +use crate::{ + metrics::{InstrumentBuilder, MetricsError}, + KeyValue, +}; use core::fmt; use std::sync::Arc; use std::{any::Any, convert::TryFrom}; @@ -9,7 +11,7 @@ use super::{AsyncInstrument, AsyncInstrumentBuilder}; /// An SDK implemented instrument that records increasing or decreasing values. pub trait SyncUpDownCounter { /// Records an increment or decrement to the counter. - fn add(&self, value: T, attributes: AttributeSet); + fn add(&self, value: T, attributes: &[KeyValue]); } /// An instrument that records increasing or decreasing values. @@ -35,8 +37,8 @@ impl UpDownCounter { } /// Records an increment or decrement to the counter. - pub fn add(&self, value: T, attributes: impl Into) { - self.0.add(value, attributes.into()) + pub fn add(&self, value: T, attributes: &[KeyValue]) { + self.0.add(value, attributes) } } @@ -91,7 +93,7 @@ impl ObservableUpDownCounter { /// It is only valid to call this within a callback. If called outside of the /// registered callback it should have no effect on the instrument, and an /// error will be reported via the error handler. - pub fn observe(&self, value: T, attributes: AttributeSet) { + pub fn observe(&self, value: T, attributes: &[KeyValue]) { self.0.observe(value, attributes) } @@ -102,7 +104,7 @@ impl ObservableUpDownCounter { } impl AsyncInstrument for ObservableUpDownCounter { - fn observe(&self, measurement: T, attributes: AttributeSet) { + fn observe(&self, measurement: T, attributes: &[KeyValue]) { self.0.observe(measurement, attributes) } diff --git a/opentelemetry/src/metrics/meter.rs b/opentelemetry/src/metrics/meter.rs index 999c0e9b1f..f64fae6976 100644 --- a/opentelemetry/src/metrics/meter.rs +++ b/opentelemetry/src/metrics/meter.rs @@ -1,4 +1,3 @@ -use crate::attributes::AttributeSet; use core::fmt; use std::any::Any; use std::borrow::Cow; @@ -72,7 +71,7 @@ pub trait MeterProvider { /// Provides access to instrument instances for recording measurements. /// /// ``` -/// use opentelemetry::{AttributeSet, global, KeyValue}; +/// use opentelemetry::{global, KeyValue}; /// /// let meter = global::meter("my-meter"); /// @@ -81,107 +80,181 @@ pub trait MeterProvider { /// // u64 Counter /// let u64_counter = meter.u64_counter("my_u64_counter").init(); /// -/// // Define the attributes the counters will use -/// let attributes = AttributeSet::from(&[ +/// // Record measurements using the counter instrument add() +/// u64_counter.add( +/// 10, +/// [ /// KeyValue::new("mykey1", "myvalue1"), /// KeyValue::new("mykey2", "myvalue2"), -/// ]); -/// -/// // Record measurements using the counter instrument add() -/// u64_counter.add(10, attributes.clone()); +/// ].as_ref() +/// ); /// /// // f64 Counter /// let f64_counter = meter.f64_counter("my_f64_counter").init(); /// /// // Record measurements using the counter instrument add() -/// f64_counter.add(3.15, attributes.clone()); +/// f64_counter.add( +/// 3.15, +/// [ +/// KeyValue::new("mykey1", "myvalue1"), +/// KeyValue::new("mykey2", "myvalue2"), +/// ].as_ref() +/// ); /// /// // u6 observable counter /// let observable_u4_counter = meter.u64_observable_counter("my_observable_u64_counter").init(); /// /// // Register a callback to this meter for an asynchronous instrument to record measurements -/// let observer_attributes = attributes.clone(); /// meter.register_callback(&[observable_u4_counter.as_any()], move |observer| { -/// observer.observe_u64(&observable_u4_counter, 1, observer_attributes.clone()) +/// observer.observe_u64( +/// &observable_u4_counter, +/// 1, +/// [ +/// KeyValue::new("mykey1", "myvalue1"), +/// KeyValue::new("mykey2", "myvalue2"), +/// ].as_ref(), +/// ) /// }); /// /// // f64 observable counter /// let observable_f64_counter = meter.f64_observable_counter("my_observable_f64_counter").init(); /// /// // Register a callback to this meter for an asynchronous instrument to record measurements -/// let observer_attributes = attributes.clone(); /// meter.register_callback(&[observable_f64_counter.as_any()], move |observer| { -/// observer.observe_f64(&observable_f64_counter, 1.55, observer_attributes.clone()) +/// observer.observe_f64( +/// &observable_f64_counter, +/// 1.55, +/// [ +/// KeyValue::new("mykey1", "myvalue1"), +/// KeyValue::new("mykey2", "myvalue2"), +/// ].as_ref(), +/// ) /// }); /// /// // i64 updown counter /// let updown_i64_counter = meter.i64_up_down_counter("my_updown_i64_counter").init(); /// /// // Record measurements using the updown counter instrument add() -/// updown_i64_counter.add(-10, attributes.clone()); +/// updown_i64_counter.add( +/// -10, +/// [ +/// KeyValue::new("mykey1", "myvalue1"), +/// KeyValue::new("mykey2", "myvalue2"), +/// ].as_ref(), +/// ); /// /// // f64 updown counter /// let updown_f64_counter = meter.f64_up_down_counter("my_updown_f64_counter").init(); /// /// // Record measurements using the updown counter instrument add() -/// updown_f64_counter.add(-10.67, attributes.clone()); +/// updown_f64_counter.add( +/// -10.67, +/// [ +/// KeyValue::new("mykey1", "myvalue1"), +/// KeyValue::new("mykey2", "myvalue2"), +/// ].as_ref(), +/// ); /// /// // i64 observable updown counter /// let observable_i64_up_down_counter = meter.i64_observable_up_down_counter("my_observable_i64_updown_counter").init(); /// /// // Register a callback to this meter for an asynchronous instrument to record measurements -/// let observer_attributes = attributes.clone(); /// meter.register_callback(&[observable_i64_up_down_counter.as_any()], move |observer| { -/// observer.observe_i64(&observable_i64_up_down_counter, 1, observer_attributes.clone()) +/// observer.observe_i64( +/// &observable_i64_up_down_counter, +/// 1, +/// [ +/// KeyValue::new("mykey1", "myvalue1"), +/// KeyValue::new("mykey2", "myvalue2"), +/// ].as_ref(), +/// ) /// }); /// /// // f64 observable updown counter /// let observable_f64_up_down_counter = meter.f64_observable_up_down_counter("my_observable_f64_updown_counter").init(); /// /// // Register a callback to this meter for an asynchronous instrument to record measurements -/// let observer_attributes = attributes.clone(); /// meter.register_callback(&[observable_f64_up_down_counter.as_any()], move |observer| { -/// observer.observe_f64(&observable_f64_up_down_counter, 1.16, observer_attributes.clone()) +/// observer.observe_f64( +/// &observable_f64_up_down_counter, +/// 1.16, +/// [ +/// KeyValue::new("mykey1", "myvalue1"), +/// KeyValue::new("mykey2", "myvalue2"), +/// ].as_ref(), +/// ) /// }); /// /// // Observable f64 gauge /// let f64_gauge = meter.f64_observable_gauge("my_f64_gauge").init(); /// /// // Register a callback to this meter for an asynchronous instrument to record measurements -/// let observer_attributes = attributes.clone(); /// meter.register_callback(&[f64_gauge.as_any()], move |observer| { -/// observer.observe_f64(&f64_gauge, 2.32, observer_attributes.clone()) +/// observer.observe_f64( +/// &f64_gauge, +/// 2.32, +/// [ +/// KeyValue::new("mykey1", "myvalue1"), +/// KeyValue::new("mykey2", "myvalue2"), +/// ].as_ref(), +/// ) /// }); /// /// // Observable i64 gauge /// let i64_gauge = meter.i64_observable_gauge("my_i64_gauge").init(); /// /// // Register a callback to this meter for an asynchronous instrument to record measurements -/// let observer_attributes = attributes.clone(); /// meter.register_callback(&[i64_gauge.as_any()], move |observer| { -/// observer.observe_i64(&i64_gauge, 12, observer_attributes.clone()) +/// observer.observe_i64( +/// &i64_gauge, +/// 12, +/// [ +/// KeyValue::new("mykey1", "myvalue1"), +/// KeyValue::new("mykey2", "myvalue2"), +/// ].as_ref(), +/// ) /// }); /// /// // Observable u64 gauge /// let u64_gauge = meter.u64_observable_gauge("my_u64_gauge").init(); /// /// // Register a callback to this meter for an asynchronous instrument to record measurements -/// let observer_attributes = attributes.clone(); /// meter.register_callback(&[u64_gauge.as_any()], move |observer| { -/// observer.observe_u64(&u64_gauge, 1, observer_attributes.clone()) +/// observer.observe_u64( +/// &u64_gauge, +/// 1, +/// [ +/// KeyValue::new("mykey1", "myvalue1"), +/// KeyValue::new("mykey2", "myvalue2"), +/// ].as_ref(), +/// ) /// }); /// /// // f64 histogram /// let f64_histogram = meter.f64_histogram("my_f64_histogram").init(); /// /// // Record measurements using the histogram instrument record() -/// f64_histogram.record(10.5, attributes.clone()); +/// f64_histogram.record( +/// 10.5, +/// [ +/// KeyValue::new("mykey1", "myvalue1"), +/// KeyValue::new("mykey2", "myvalue2"), +/// ] +/// .as_ref(), +/// ); /// /// // u64 histogram /// let u64_histogram = meter.u64_histogram("my_u64_histogram").init(); /// /// // Record measurements using the histogram instrument record() -/// u64_histogram.record(12, attributes); +/// u64_histogram.record( +/// 12, +/// [ +/// KeyValue::new("mykey1", "myvalue1"), +/// KeyValue::new("mykey2", "myvalue2"), +/// ] +/// .as_ref(), +/// ); /// /// ``` #[derive(Clone)] @@ -365,13 +438,13 @@ pub trait CallbackRegistration: Send + Sync { /// Records measurements for multiple instruments in a callback. pub trait Observer { /// Records the f64 value with attributes for the observable. - fn observe_f64(&self, inst: &dyn AsyncInstrument, measurement: f64, attrs: AttributeSet); + fn observe_f64(&self, inst: &dyn AsyncInstrument, measurement: f64, attrs: &[KeyValue]); /// Records the u64 value with attributes for the observable. - fn observe_u64(&self, inst: &dyn AsyncInstrument, measurement: u64, attrs: AttributeSet); + fn observe_u64(&self, inst: &dyn AsyncInstrument, measurement: u64, attrs: &[KeyValue]); /// Records the i64 value with attributes for the observable. - fn observe_i64(&self, inst: &dyn AsyncInstrument, measurement: i64, attrs: AttributeSet); + fn observe_i64(&self, inst: &dyn AsyncInstrument, measurement: i64, attrs: &[KeyValue]); } impl fmt::Debug for Meter { diff --git a/opentelemetry/src/metrics/noop.rs b/opentelemetry/src/metrics/noop.rs index 3f675ed462..adf4b03da3 100644 --- a/opentelemetry/src/metrics/noop.rs +++ b/opentelemetry/src/metrics/noop.rs @@ -3,7 +3,6 @@ //! This implementation is returned as the global Meter if no `Meter` //! has been set. It is also useful for testing purposes as it is intended //! to have minimal resource utilization and runtime impact. -use crate::attributes::AttributeSet; use crate::{ metrics::{ AsyncInstrument, CallbackRegistration, InstrumentProvider, Meter, MeterProvider, Observer, @@ -94,25 +93,25 @@ impl NoopSyncInstrument { } impl SyncCounter for NoopSyncInstrument { - fn add(&self, _value: T, _attributes: AttributeSet) { + fn add(&self, _value: T, _attributes: &[KeyValue]) { // Ignored } } impl SyncUpDownCounter for NoopSyncInstrument { - fn add(&self, _value: T, _attributes: AttributeSet) { + fn add(&self, _value: T, _attributes: &[KeyValue]) { // Ignored } } impl SyncHistogram for NoopSyncInstrument { - fn record(&self, _value: T, _attributes: AttributeSet) { + fn record(&self, _value: T, _attributes: &[KeyValue]) { // Ignored } } impl SyncGauge for NoopSyncInstrument { - fn record(&self, _value: T, _attributes: AttributeSet) { + fn record(&self, _value: T, _attributes: &[KeyValue]) { // Ignored } } @@ -131,7 +130,7 @@ impl NoopAsyncInstrument { } impl AsyncInstrument for NoopAsyncInstrument { - fn observe(&self, _value: T, _attributes: AttributeSet) { + fn observe(&self, _value: T, _attributes: &[KeyValue]) { // Ignored } diff --git a/stress/Cargo.toml b/stress/Cargo.toml index bfeef7e938..026a75a72e 100644 --- a/stress/Cargo.toml +++ b/stress/Cargo.toml @@ -9,11 +9,6 @@ name = "metrics" path = "src/metrics.rs" doc = false -[[bin]] # Bin to run the metrics cached attributes stress tests -name = "metrics_cached_attrs" -path = "src/metrics_cached_attrs.rs" -doc = false - [[bin]] # Bin to run the logs stress tests name = "logs" path = "src/logs.rs" diff --git a/stress/src/metrics_cached_attrs.rs b/stress/src/metrics_cached_attrs.rs deleted file mode 100644 index 867ffa2e3e..0000000000 --- a/stress/src/metrics_cached_attrs.rs +++ /dev/null @@ -1,53 +0,0 @@ -use lazy_static::lazy_static; -use opentelemetry::AttributeSet; -use opentelemetry::{ - metrics::{Counter, MeterProvider as _}, - KeyValue, -}; -use opentelemetry_sdk::metrics::{ManualReader, SdkMeterProvider}; -use rand::{rngs::SmallRng, Rng, SeedableRng}; -use std::borrow::Cow; - -mod throughput; - -lazy_static! { - static ref PROVIDER: SdkMeterProvider = SdkMeterProvider::builder() - .with_reader(ManualReader::builder().build()) - .build(); - static ref ATTRIBUTE_VALUES: [&'static str; 10] = [ - "value1", "value2", "value3", "value4", "value5", "value6", "value7", "value8", "value9", - "value10" - ]; - static ref COUNTER: Counter = PROVIDER - .meter(<&str as Into>>::into("test")) - .u64_counter("hello") - .init(); - static ref ATTRIBUTE_SETS: Vec = { - let mut vec = Vec::new(); - for i0 in 0..ATTRIBUTE_VALUES.len() { - for i1 in 0..ATTRIBUTE_VALUES.len() { - for i2 in 0..ATTRIBUTE_VALUES.len() { - vec.push(AttributeSet::from(&[ - KeyValue::new("attribute1", ATTRIBUTE_VALUES[i0]), - KeyValue::new("attribute2", ATTRIBUTE_VALUES[i1]), - KeyValue::new("attribute3", ATTRIBUTE_VALUES[i2]), - ])) - } - } - } - - vec - }; -} - -fn main() { - throughput::test_throughput(test_counter); -} - -fn test_counter() { - let mut rng = SmallRng::from_entropy(); - let len = ATTRIBUTE_SETS.len(); - let index = rng.gen_range(0..len); - - COUNTER.add(1, ATTRIBUTE_SETS[index].clone()); -}