Skip to content

Commit e123996

Browse files
authored
feat: View cleanups (#2988)
1 parent 3cdc62e commit e123996

File tree

11 files changed

+178
-366
lines changed

11 files changed

+178
-366
lines changed

examples/metrics-advanced/src/main.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use std::error::Error;
77
fn init_meter_provider() -> opentelemetry_sdk::metrics::SdkMeterProvider {
88
// for example 1
99
let my_view_rename_and_unit = |i: &Instrument| {
10-
if i.name == "my_histogram" {
10+
if i.name() == "my_histogram" {
1111
Some(
1212
Stream::builder()
1313
.with_name("my_histogram_renamed")
@@ -22,7 +22,7 @@ fn init_meter_provider() -> opentelemetry_sdk::metrics::SdkMeterProvider {
2222

2323
// for example 2
2424
let my_view_change_cardinality = |i: &Instrument| {
25-
if i.name == "my_second_histogram" {
25+
if i.name() == "my_second_histogram" {
2626
// Note: If Stream is invalid, build() will return an error. By
2727
// calling `.ok()`, any such error is ignored and treated as if the
2828
// view does not match the instrument. If this is not the desired

opentelemetry-sdk/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@ also modified to suppress telemetry before invoking exporters.
5555
- Added `Stream::builder()` method that returns a new `StreamBuilder`
5656
- `StreamBuilder::build()` returns `Result<Stream, Box<dyn Error>>` enabling
5757
proper validation
58+
- Removed `new_view()` on `View`. Views can be instead added by passing anything
59+
that implements `View` trait to `with_view` method on `MeterProviderBuilder`.
60+
`View` is implemented for `Fn(&Instrument) -> Option<Stream>`, so this can be
61+
used to add views.
5862

5963
- *Breaking* `Aggregation` enum moved behind feature flag
6064
"spec_unstable_metrics_views". This was only required when using Views.

opentelemetry-sdk/Cargo.toml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ futures-executor = { workspace = true }
1818
futures-util = { workspace = true, features = ["std", "sink", "async-await-macro"] }
1919
percent-encoding = { workspace = true, optional = true }
2020
rand = { workspace = true, features = ["std", "std_rng", "small_rng", "os_rng", "thread_rng"], optional = true }
21-
glob = { workspace = true, optional = true }
2221
serde = { workspace = true, features = ["derive", "rc"], optional = true }
2322
serde_json = { workspace = true, optional = true }
2423
thiserror = { workspace = true }
@@ -45,7 +44,7 @@ trace = ["opentelemetry/trace", "rand", "percent-encoding"]
4544
jaeger_remote_sampler = ["trace", "opentelemetry-http", "http", "serde", "serde_json", "url", "experimental_async_runtime"]
4645
logs = ["opentelemetry/logs", "serde_json"]
4746
spec_unstable_logs_enabled = ["logs", "opentelemetry/spec_unstable_logs_enabled"]
48-
metrics = ["opentelemetry/metrics", "glob"]
47+
metrics = ["opentelemetry/metrics"]
4948
testing = ["opentelemetry/testing", "trace", "metrics", "logs", "rt-tokio", "rt-tokio-current-thread", "tokio/macros", "tokio/rt-multi-thread"]
5049
experimental_async_runtime = []
5150
rt-tokio = ["tokio", "tokio-stream", "experimental_async_runtime"]

opentelemetry-sdk/benches/metric.rs

Lines changed: 24 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ use opentelemetry::{
66
use opentelemetry_sdk::{
77
error::OTelSdkResult,
88
metrics::{
9-
data::ResourceMetrics, new_view, reader::MetricReader, Aggregation, Instrument,
10-
InstrumentKind, ManualReader, Pipeline, SdkMeterProvider, Stream, Temporality, View,
9+
data::ResourceMetrics, reader::MetricReader, Aggregation, Instrument, InstrumentKind,
10+
ManualReader, Pipeline, SdkMeterProvider, Stream, Temporality, View,
1111
},
1212
};
1313
use rand::Rng;
@@ -220,16 +220,12 @@ fn counters(c: &mut Criterion) {
220220
});
221221

222222
let (_, cntr) = bench_counter(
223-
Some(
224-
new_view(
225-
Instrument::new().name("*"),
226-
Stream::builder()
227-
.with_allowed_attribute_keys([Key::new("K")])
228-
.build()
229-
.unwrap(),
230-
)
231-
.unwrap(),
232-
),
223+
Some(Box::new(|_i: &Instrument| {
224+
Stream::builder()
225+
.with_allowed_attribute_keys([Key::new("K")])
226+
.build()
227+
.ok()
228+
})),
233229
"cumulative",
234230
);
235231

@@ -273,25 +269,24 @@ fn bench_histogram(bound_count: usize) -> (SharedReader, Histogram<u64>) {
273269
for i in 0..bounds.len() {
274270
bounds[i] = i * MAX_BOUND / bound_count
275271
}
276-
let view = Some(
277-
new_view(
278-
Instrument::new().name("histogram_*"),
279-
Stream::builder()
280-
.with_aggregation(Aggregation::ExplicitBucketHistogram {
281-
boundaries: bounds.iter().map(|&x| x as f64).collect(),
282-
record_min_max: true,
283-
})
284-
.build()
285-
.unwrap(),
286-
)
287-
.unwrap(),
288-
);
289272

290273
let r = SharedReader(Arc::new(ManualReader::default()));
291-
let mut builder = SdkMeterProvider::builder().with_reader(r.clone());
292-
if let Some(view) = view {
293-
builder = builder.with_view(view);
294-
}
274+
let builder = SdkMeterProvider::builder()
275+
.with_reader(r.clone())
276+
.with_view(Box::new(move |i: &Instrument| {
277+
if i.name().starts_with("histogram_") {
278+
Stream::builder()
279+
.with_aggregation(Aggregation::ExplicitBucketHistogram {
280+
boundaries: bounds.iter().map(|&x| x as f64).collect(),
281+
record_min_max: true,
282+
})
283+
.build()
284+
.ok()
285+
} else {
286+
None
287+
}
288+
}));
289+
295290
let mtr = builder.build().meter("test_meter");
296291
let hist = mtr
297292
.u64_histogram(format!("histogram_{}", bound_count))

opentelemetry-sdk/src/metrics/error.rs

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,30 +3,9 @@ use std::sync::PoisonError;
33
use thiserror::Error;
44

55
/// A specialized `Result` type for metric operations.
6-
#[cfg(feature = "spec_unstable_metrics_views")]
7-
pub type MetricResult<T> = result::Result<T, MetricError>;
8-
#[cfg(not(feature = "spec_unstable_metrics_views"))]
96
pub(crate) type MetricResult<T> = result::Result<T, MetricError>;
107

118
/// Errors returned by the metrics API.
12-
#[cfg(feature = "spec_unstable_metrics_views")]
13-
#[derive(Error, Debug)]
14-
#[non_exhaustive]
15-
pub enum MetricError {
16-
/// Other errors not covered by specific cases.
17-
#[error("Metrics error: {0}")]
18-
Other(String),
19-
/// Invalid configuration
20-
#[error("Config error {0}")]
21-
Config(String),
22-
/// Invalid instrument configuration such invalid instrument name, invalid instrument description, invalid instrument unit, etc.
23-
/// See [spec](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/api.md#general-characteristics)
24-
/// for full list of requirements.
25-
#[error("Invalid instrument configuration: {0}")]
26-
InvalidInstrumentConfiguration(&'static str),
27-
}
28-
29-
#[cfg(not(feature = "spec_unstable_metrics_views"))]
309
#[derive(Error, Debug)]
3110
pub(crate) enum MetricError {
3211
/// Other errors not covered by specific cases.

opentelemetry-sdk/src/metrics/instrument.rs

Lines changed: 38 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -65,110 +65,62 @@ impl InstrumentKind {
6565
}
6666
}
6767

68-
/// Describes properties an instrument is created with, also used for filtering
69-
/// in [View](crate::metrics::View)s.
68+
/// Describes properties an instrument is created with, used for filtering in
69+
/// [View](crate::metrics::View)s.
70+
///
71+
/// A reference to `Instrument` is provided to the `view` to select the
72+
/// instrument(s) for which the [Stream] should be applied.
7073
///
7174
/// # Example
7275
///
73-
/// Instruments can be used as criteria for views.
76+
/// ```rust
77+
/// use opentelemetry_sdk::metrics::{Instrument, Stream};
7478
///
79+
/// let my_view_change_cardinality = |i: &Instrument| {
80+
/// if i.name() == "my_second_histogram" {
81+
/// // Note: If Stream is invalid, build() will return an error. By
82+
/// // calling `.ok()`, any such error is ignored and treated as if the
83+
/// // view does not match the instrument. If this is not the desired
84+
/// // behavior, consider handling the error explicitly.
85+
/// Stream::builder().with_cardinality_limit(2).build().ok()
86+
/// } else {
87+
/// None
88+
/// }
89+
/// };
7590
/// ```
76-
/// use opentelemetry_sdk::metrics::{new_view, Aggregation, Instrument, Stream, StreamBuilder};
77-
///
78-
/// let criteria = Instrument::new().name("counter_*");
79-
/// let mask = Stream::builder()
80-
/// .with_aggregation(Aggregation::Sum)
81-
/// .build()
82-
/// .unwrap();
83-
///
84-
/// let view = new_view(criteria, mask);
85-
/// # drop(view);
86-
/// ```
87-
#[derive(Clone, Default, Debug, PartialEq)]
88-
#[non_exhaustive]
91+
#[derive(Clone, Debug, PartialEq)]
8992
pub struct Instrument {
9093
/// The human-readable identifier of the instrument.
91-
pub name: Cow<'static, str>,
94+
pub(crate) name: Cow<'static, str>,
9295
/// describes the purpose of the instrument.
93-
pub description: Cow<'static, str>,
96+
pub(crate) description: Cow<'static, str>,
9497
/// The functional group of the instrument.
95-
pub kind: Option<InstrumentKind>,
98+
pub(crate) kind: InstrumentKind,
9699
/// Unit is the unit of measurement recorded by the instrument.
97-
pub unit: Cow<'static, str>,
100+
pub(crate) unit: Cow<'static, str>,
98101
/// The instrumentation that created the instrument.
99-
pub scope: InstrumentationScope,
102+
pub(crate) scope: InstrumentationScope,
100103
}
101104

102-
#[cfg(feature = "spec_unstable_metrics_views")]
103105
impl Instrument {
104-
/// Create a new instrument with default values
105-
pub fn new() -> Self {
106-
Instrument::default()
107-
}
108-
109-
/// Set the instrument name.
110-
pub fn name(mut self, name: impl Into<Cow<'static, str>>) -> Self {
111-
self.name = name.into();
112-
self
113-
}
114-
115-
/// Set the instrument description.
116-
pub fn description(mut self, description: impl Into<Cow<'static, str>>) -> Self {
117-
self.description = description.into();
118-
self
119-
}
120-
121-
/// Set the instrument unit.
122-
pub fn unit(mut self, unit: impl Into<Cow<'static, str>>) -> Self {
123-
self.unit = unit.into();
124-
self
125-
}
126-
127-
/// Set the instrument scope.
128-
pub fn scope(mut self, scope: InstrumentationScope) -> Self {
129-
self.scope = scope;
130-
self
131-
}
132-
133-
/// empty returns if all fields of i are their default-value.
134-
pub(crate) fn is_empty(&self) -> bool {
135-
self.name.is_empty()
136-
&& self.description.is_empty()
137-
&& self.kind.is_none()
138-
&& self.unit.is_empty()
139-
&& self.scope == InstrumentationScope::default()
140-
}
141-
142-
pub(crate) fn matches(&self, other: &Instrument) -> bool {
143-
self.matches_name(other)
144-
&& self.matches_description(other)
145-
&& self.matches_kind(other)
146-
&& self.matches_unit(other)
147-
&& self.matches_scope(other)
106+
/// Instrument name.
107+
pub fn name(&self) -> &str {
108+
self.name.as_ref()
148109
}
149110

150-
pub(crate) fn matches_name(&self, other: &Instrument) -> bool {
151-
self.name.is_empty() || self.name.as_ref() == other.name.as_ref()
111+
/// Instrument kind.
112+
pub fn kind(&self) -> InstrumentKind {
113+
self.kind
152114
}
153115

154-
pub(crate) fn matches_description(&self, other: &Instrument) -> bool {
155-
self.description.is_empty() || self.description.as_ref() == other.description.as_ref()
116+
/// Instrument unit.
117+
pub fn unit(&self) -> &str {
118+
self.unit.as_ref()
156119
}
157120

158-
pub(crate) fn matches_kind(&self, other: &Instrument) -> bool {
159-
self.kind.is_none() || self.kind == other.kind
160-
}
161-
162-
pub(crate) fn matches_unit(&self, other: &Instrument) -> bool {
163-
self.unit.is_empty() || self.unit.as_ref() == other.unit.as_ref()
164-
}
165-
166-
pub(crate) fn matches_scope(&self, other: &Instrument) -> bool {
167-
(self.scope.name().is_empty() || self.scope.name() == other.scope.name())
168-
&& (self.scope.version().is_none()
169-
|| self.scope.version().as_ref() == other.scope.version().as_ref())
170-
&& (self.scope.schema_url().is_none()
171-
|| self.scope.schema_url().as_ref() == other.scope.schema_url().as_ref())
121+
/// Instrument scope.
122+
pub fn scope(&self) -> &InstrumentationScope {
123+
&self.scope
172124
}
173125
}
174126

@@ -188,7 +140,6 @@ impl Instrument {
188140
/// .unwrap();
189141
/// ```
190142
#[derive(Default, Debug)]
191-
#[non_exhaustive]
192143
pub struct StreamBuilder {
193144
name: Option<Cow<'static, str>>,
194145
description: Option<Cow<'static, str>>,
@@ -312,27 +263,9 @@ fn validate_bucket_boundaries(boundaries: &[f64]) -> Result<(), String> {
312263
Ok(())
313264
}
314265

315-
/// Describes the stream of data an instrument produces.
316-
///
317-
/// # Example
318-
///
319-
/// Streams can be used as masks in views.
320-
///
321-
/// ```
322-
/// use opentelemetry_sdk::metrics::{new_view, Aggregation, Instrument, Stream};
323-
///
324-
/// let criteria = Instrument::new().name("counter_*");
325-
/// let mask = Stream::builder()
326-
/// .with_aggregation(Aggregation::Sum)
327-
/// .build()
328-
/// .unwrap();
329-
///
330-
/// let view = new_view(criteria, mask);
331-
/// # drop(view);
332-
/// ```
266+
/// Describes the stream of data an instrument produces. Used in
267+
/// [View](crate::metrics::View)s to customize the metric output.
333268
#[derive(Default, Debug)]
334-
#[non_exhaustive]
335-
#[allow(unreachable_pub)]
336269
pub struct Stream {
337270
/// The human-readable identifier of the stream.
338271
pub(crate) name: Option<Cow<'static, str>>,

opentelemetry-sdk/src/metrics/meter.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -683,7 +683,7 @@ where
683683
name,
684684
description: description.unwrap_or_default(),
685685
unit: unit.unwrap_or_default(),
686-
kind: Some(kind),
686+
kind,
687687
scope: self.meter.scope.clone(),
688688
};
689689

opentelemetry-sdk/src/metrics/meter_provider.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,7 @@ impl MeterProviderBuilder {
308308
/// ```
309309
/// # use opentelemetry_sdk::metrics::{Stream, Instrument};
310310
/// let view_rename = |i: &Instrument| {
311-
/// if i.name == "my_counter" {
311+
/// if i.name() == "my_counter" {
312312
/// Some(Stream::builder().with_name("my_counter_renamed").build().expect("Stream should be valid"))
313313
/// } else {
314314
/// None
@@ -324,7 +324,7 @@ impl MeterProviderBuilder {
324324
/// ```
325325
/// # use opentelemetry_sdk::metrics::{Stream, Instrument};
326326
/// let view_change_cardinality = |i: &Instrument| {
327-
/// if i.name == "my_counter" {
327+
/// if i.name() == "my_counter" {
328328
/// Some(
329329
/// Stream::builder()
330330
/// .with_cardinality_limit(100).build().expect("Stream should be valid"),
@@ -343,7 +343,7 @@ impl MeterProviderBuilder {
343343
/// ```
344344
/// # use opentelemetry_sdk::metrics::{Stream, Instrument};
345345
/// let my_view_change_cardinality = |i: &Instrument| {
346-
/// if i.name == "my_second_histogram" {
346+
/// if i.name() == "my_second_histogram" {
347347
/// // Note: If Stream is invalid, build() will return `Error` variant.
348348
/// // By calling `.ok()`, any such error is ignored and treated as if the view does not match
349349
/// // the instrument.

0 commit comments

Comments
 (0)