Skip to content

Commit 7396e13

Browse files
authored
Remove opentelemetry as the default metrics library (#97)
* [WIP] * Update prometheus-client version * Finish support for prometheus-client * Fix doc links * Ensure that prometheus-client is actually being used * Work on fixing tests, remove const_format dep * Don't use regexes in tests (because different libraries order labels differently) * Make once_cell a required dependency * Rename concurrency tracker to gauge * Move PROMETHEUS_CLIENT_REGISTRY to integrations module * Run all the other tests first * Run the tests in the same order * The prometheus-exporter feature needs to depend on the prometheus feature (for now) * Rename export to just REGISTRY * Mention prometheus-client in readme and changelog * Remove opentelemetry as the default metrics library * Disable opentelemetry tests * continue-on-error * previously... * Remove opentelemetry memory api call * Fix error type * Remove duplicate code * Make metrics library features mutually exclusive
1 parent 4d67922 commit 7396e13

File tree

9 files changed

+173
-84
lines changed

9 files changed

+173
-84
lines changed

.github/workflows/ci.yml

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,18 @@ jobs:
2424
- run: cargo clippy --features=prometheus-client
2525
- run: cargo clippy --features=opentelemetry
2626

27-
# Build the packages
28-
- run: cargo build
29-
- run: cargo build --features=custom-objective-percentile,custom-objective-latency
30-
3127
# Run the tests with each of the different metrics libraries
32-
- run: cargo test --features=prometheus-exporter
33-
- run: cargo test --no-default-features --features=prometheus-exporter,metrics --tests
34-
- run: cargo test --no-default-features --features=prometheus-exporter,prometheus --tests
35-
- run: cargo test --no-default-features --features=prometheus-exporter,prometheus-client --tests
28+
- run: cargo test --features=prometheus-exporter,metrics
29+
- run: cargo test --features=prometheus-exporter,prometheus --tests
30+
- run: cargo test --features=prometheus-exporter,prometheus-client --tests
31+
# The tests using opentelemetry are currently failing because of this issue:
32+
# https://github.com/open-telemetry/opentelemetry-rust/issues/1070
33+
# We should remove the continue-on-error once that is fixed
34+
- run: cargo test --features=prometheus-exporter,opentelemetry
35+
continue-on-error: true
36+
37+
# Build the crate using the other optional features
38+
- run: cargo build --features=metrics,custom-objective-percentile,custom-objective-latency
3639

3740
# Compile the examples
3841
- run: cargo build --package example-axum

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1717

1818
### Changed
1919

20+
- Users must configure the metrics library they want to use autometrics with
21+
- Metrics library feature flags are now mutually exclusive (previously, `autometrics` would only
22+
produce metrics using a single metrics library if multiple feature flags were enabled, using
23+
a prioritization order defined internally)
2024
- `GetLabels` trait (publicly exported but meant for internal use) changed the signature
2125
of its function to accomodate the new `ResultLabels` macro. This change is not significant
2226
if you never imported `autometrics::__private` manually (#61)
27+
- When using the `opentelemetry` together with the `prometheus-exporter`, it will no longer
28+
use the default registry provided by the `prometheus` crate. It will instead use a new registry
29+
- The `prometheus-exporter`'s `encode_global_metrics` feature now returns an error enum
30+
defined by `autometrics` as opposed to directly returning the `prometheus::Error` type
2331
- Updated `opentelemetry` dependencies to v0.19. This means that users using autometrics
2432
with `opentelemetry` but not using the `prometheus-exporter` must update the `opentelemetry`
2533
to use v0.19.
@@ -29,6 +37,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2937

3038
### Removed
3139

40+
- `opentelemetry` is no longer used by default
3241
- `GetLabelsForResult` trait (publicly exported but meant for internal use) was removed
3342
to accomodate the new `ResultLabels` macro. This change is not significant
3443
if you never imported `autometrics::__private` manually (#61)

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ See [Why Autometrics?](https://github.com/autometrics-dev#4-why-autometrics) for
6363

6464
1. Add `autometrics` to your project:
6565
```sh
66-
cargo add autometrics --features=prometheus-exporter
66+
cargo add autometrics --features=prometheus,prometheus-exporter
6767
```
6868
2. Instrument your functions with the [`#[autometrics]`](https://docs.rs/autometrics/latest/autometrics/attr.autometrics.html) macro
6969

autometrics/Cargo.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ categories = { workspace = true }
1313
readme = "README.md"
1414

1515
[features]
16-
default = ["opentelemetry"]
1716
metrics = ["dep:metrics"]
1817
opentelemetry = ["opentelemetry_api"]
1918
prometheus = ["dep:prometheus"]
@@ -22,7 +21,8 @@ prometheus-exporter = [
2221
"metrics-exporter-prometheus",
2322
"opentelemetry-prometheus",
2423
"opentelemetry_sdk",
25-
"prometheus"
24+
"dep:prometheus",
25+
"thiserror"
2626
]
2727
custom-objective-percentile = []
2828
custom-objective-latency = []
@@ -43,6 +43,7 @@ metrics-exporter-prometheus = { version = "0.12", default-features = false, opti
4343
opentelemetry-prometheus = { version = "0.12.0", optional = true }
4444
opentelemetry_sdk = { version = "0.19", default-features = false, features = ["metrics"], optional = true }
4545
prometheus = { version = "0.13", default-features = false, optional = true }
46+
thiserror = { version = "1", optional = true }
4647

4748
# Used for prometheus-client feature
4849
prometheus-client = { version = "0.21.1", optional = true }

autometrics/README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,12 +115,13 @@ fn main() {
115115

116116
#### Metrics Libraries
117117

118-
Configure the crate that autometrics will use to produce metrics by using one of the following feature flags:
118+
**Required:** Configure the crate that autometrics will use to produce metrics by using one of the following feature flags:
119119

120120
> **Note**
121121
>
122122
> If you are **not** using the `prometheus-exporter`, you must ensure that you are using the exact same version of the metrics library as `autometrics` (and it must come from `crates.io` rather than git or another source). If not, the autometrics metrics will not appear in your exported metrics.
123123
124-
- `opentelemetry` (enabled by default) - use the [opentelemetry](https://crates.io/crates/opentelemetry) crate for producing metrics.
124+
- `opentelemetry` - use the [opentelemetry](https://crates.io/crates/opentelemetry) crate for producing metrics.
125125
- `metrics` - use the [metrics](https://crates.io/crates/metrics) crate for producing metrics
126126
- `prometheus` - use the [prometheus](https://crates.io/crates/prometheus) crate for producing metrics
127+
- `prometheus-client` - use the official [prometheus-client](https://crates.io/crates/prometheus-client) crate for producing metrics

autometrics/src/prometheus_exporter.rs

Lines changed: 66 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,94 @@
1-
use crate::HISTOGRAM_BUCKETS;
21
#[cfg(feature = "metrics")]
32
use metrics_exporter_prometheus::{PrometheusBuilder, PrometheusHandle};
43
use once_cell::sync::Lazy;
4+
#[cfg(feature = "opentelemetry")]
55
use opentelemetry_prometheus::{exporter, PrometheusExporter};
6+
#[cfg(feature = "opentelemetry")]
67
use opentelemetry_sdk::export::metrics::aggregation;
8+
#[cfg(feature = "opentelemetry")]
79
use opentelemetry_sdk::metrics::{controllers, processors, selectors};
8-
use prometheus::{default_registry, Error, TextEncoder};
10+
#[cfg(any(feature = "opentelemetry", feature = "prometheus"))]
11+
use prometheus::TextEncoder;
12+
use thiserror::Error;
913

10-
static GLOBAL_EXPORTER: Lazy<GlobalPrometheus> = Lazy::new(initialize_metrics_exporter);
14+
#[cfg(not(any(
15+
feature = "metrics",
16+
feature = "opentelemetry",
17+
feature = "prometheus",
18+
feature = "prometheus-client"
19+
)))]
20+
compile_error!("At least one of the metrics, opentelemetry, prometheus, or prometheus-client features must be enabled in order to use the prometheus-exporter");
21+
22+
#[derive(Debug, Error)]
23+
pub enum EncodingError {
24+
#[cfg(any(feature = "prometheus", feature = "opentelemetry"))]
25+
#[error(transparent)]
26+
Prometheus(#[from] prometheus::Error),
27+
#[cfg(feature = "prometheus-client")]
28+
#[error(transparent)]
29+
Format(#[from] std::fmt::Error),
30+
}
31+
32+
static GLOBAL_EXPORTER: Lazy<GlobalPrometheus> = Lazy::new(|| GlobalPrometheus {
33+
#[cfg(feature = "metrics")]
34+
metrics_exporter: PrometheusBuilder::new()
35+
.set_buckets(&crate::HISTOGRAM_BUCKETS)
36+
.expect("Failed to set histogram buckets")
37+
.install_recorder()
38+
.expect("Failed to install recorder"),
39+
40+
#[cfg(feature = "opentelemetry")]
41+
opentelemetry_exporter: exporter(
42+
controllers::basic(processors::factory(
43+
selectors::simple::histogram(crate::HISTOGRAM_BUCKETS),
44+
aggregation::cumulative_temporality_selector(),
45+
))
46+
.build(),
47+
)
48+
.init(),
49+
});
1150

1251
#[derive(Clone)]
1352
#[doc(hidden)]
1453
pub struct GlobalPrometheus {
15-
exporter: PrometheusExporter,
54+
#[cfg(feature = "opentelemetry")]
55+
opentelemetry_exporter: PrometheusExporter,
1656
#[cfg(feature = "metrics")]
17-
handle: PrometheusHandle,
57+
metrics_exporter: PrometheusHandle,
1858
}
1959

2060
impl GlobalPrometheus {
21-
fn encode_metrics(&self) -> Result<String, Error> {
22-
let metric_families = self.exporter.registry().gather();
23-
let encoder = TextEncoder::new();
24-
#[allow(unused_mut)]
25-
let mut output = encoder.encode_to_string(&metric_families)?;
61+
fn encode_metrics(&self) -> Result<String, EncodingError> {
62+
let mut output = String::new();
2663

2764
#[cfg(feature = "metrics")]
2865
{
66+
output.push_str(&self.metrics_exporter.render());
2967
output.push('\n');
30-
output.push_str(&self.handle.render());
3168
}
3269

33-
#[cfg(feature = "prometheus-client")]
70+
#[cfg(feature = "opentelemetry")]
3471
{
72+
let metric_families = self.opentelemetry_exporter.registry().gather();
73+
let encoder = TextEncoder::new();
74+
encoder.encode_utf8(&metric_families, &mut output)?;
3575
output.push('\n');
76+
}
77+
78+
#[cfg(feature = "prometheus")]
79+
{
80+
let metric_families = prometheus::default_registry().gather();
81+
let encoder = TextEncoder::new();
82+
encoder.encode_utf8(&metric_families, &mut output)?;
83+
output.push('\n');
84+
}
85+
86+
#[cfg(feature = "prometheus-client")]
87+
{
3688
prometheus_client::encoding::text::encode(
3789
&mut output,
3890
&crate::tracker::prometheus_client::REGISTRY,
39-
)
40-
.map_err(|err| {
41-
Error::Msg(format!(
42-
"Failed to encode prometheus-client metrics: {}",
43-
err
44-
))
45-
})?;
91+
)?;
4692
}
4793

4894
Ok(output)
@@ -84,30 +130,6 @@ pub fn global_metrics_exporter() -> GlobalPrometheus {
84130
/// }
85131
/// }
86132
/// ```
87-
pub fn encode_global_metrics() -> Result<String, Error> {
133+
pub fn encode_global_metrics() -> Result<String, EncodingError> {
88134
GLOBAL_EXPORTER.encode_metrics()
89135
}
90-
91-
fn initialize_metrics_exporter() -> GlobalPrometheus {
92-
let controller = controllers::basic(processors::factory(
93-
selectors::simple::histogram(HISTOGRAM_BUCKETS),
94-
aggregation::cumulative_temporality_selector(),
95-
))
96-
.build();
97-
98-
// Use the prometheus crate's default registry so it still works with custom
99-
// metrics defined through the prometheus crate
100-
let registry = default_registry().clone();
101-
let prometheus_exporter = exporter(controller).with_registry(registry).init();
102-
103-
GlobalPrometheus {
104-
exporter: prometheus_exporter,
105-
106-
#[cfg(feature = "metrics")]
107-
handle: PrometheusBuilder::new()
108-
.set_buckets(&HISTOGRAM_BUCKETS)
109-
.expect("Failed to set histogram buckets")
110-
.install_recorder()
111-
.expect("Failed to install recorder"),
112-
}
113-
}

autometrics/src/tracker/mod.rs

Lines changed: 78 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -9,36 +9,89 @@ mod prometheus;
99
#[cfg(feature = "prometheus-client")]
1010
pub(crate) mod prometheus_client;
1111

12-
// Priority if multiple features are enabled:
13-
// 1. prometheus
14-
// 2. prometheus-client
15-
// 3. metrics
16-
// 4. opentelemetry (default)
17-
18-
// By default, use the opentelemetry crate
19-
#[cfg(all(
20-
feature = "opentelemetry",
21-
not(any(
22-
feature = "metrics",
23-
feature = "prometheus",
24-
feature = "prometheus-client"
25-
))
26-
))]
27-
pub use self::opentelemetry::OpenTelemetryTracker as AutometricsTracker;
12+
#[cfg(feature = "metrics")]
13+
pub use self::metrics::MetricsTracker;
14+
#[cfg(feature = "opentelemetry")]
15+
pub use self::opentelemetry::OpenTelemetryTracker;
16+
#[cfg(feature = "prometheus")]
17+
pub use self::prometheus::PrometheusTracker;
18+
#[cfg(feature = "prometheus-client")]
19+
pub use self::prometheus_client::PrometheusClientTracker;
2820

29-
// But use one of the other crates if any of those features are enabled
30-
#[cfg(all(
31-
feature = "metrics",
32-
not(any(feature = "prometheus", feature = "prometheus-client"))
21+
#[cfg(any(
22+
all(
23+
feature = "metrics",
24+
any(
25+
feature = "opentelemetry",
26+
feature = "prometheus",
27+
feature = "prometheus-client"
28+
)
29+
),
30+
all(
31+
feature = "opentelemetry",
32+
any(feature = "prometheus", feature = "prometheus-client")
33+
),
34+
all(feature = "prometheus", feature = "prometheus-client")
3335
))]
34-
pub use self::metrics::MetricsTracker as AutometricsTracker;
35-
#[cfg(feature = "prometheus")]
36-
pub use self::prometheus::PrometheusTracker as AutometricsTracker;
37-
#[cfg(all(feature = "prometheus-client", not(feature = "prometheus")))]
38-
pub use self::prometheus_client::PrometheusClientTracker as AutometricsTracker;
36+
compile_error!("Only one of the metrics, opentelemetry, prometheus, or prometheus-client features can be enabled at a time");
3937

4038
pub trait TrackMetrics {
4139
fn set_build_info(build_info_labels: &BuildInfoLabels);
4240
fn start(gauge_labels: Option<&GaugeLabels>) -> Self;
4341
fn finish(self, counter_labels: &CounterLabels, histogram_labels: &HistogramLabels);
4442
}
43+
44+
pub struct AutometricsTracker {
45+
#[cfg(feature = "metrics")]
46+
metrics_tracker: MetricsTracker,
47+
#[cfg(feature = "opentelemetry")]
48+
opentelemetry_tracker: OpenTelemetryTracker,
49+
#[cfg(feature = "prometheus")]
50+
prometheus_tracker: PrometheusTracker,
51+
#[cfg(feature = "prometheus-client")]
52+
prometheus_client_tracker: PrometheusClientTracker,
53+
}
54+
55+
impl TrackMetrics for AutometricsTracker {
56+
#[allow(unused_variables)]
57+
fn set_build_info(build_info_labels: &BuildInfoLabels) {
58+
#[cfg(feature = "metrics")]
59+
MetricsTracker::set_build_info(build_info_labels);
60+
#[cfg(feature = "opentelemetry")]
61+
OpenTelemetryTracker::set_build_info(build_info_labels);
62+
#[cfg(feature = "prometheus")]
63+
PrometheusTracker::set_build_info(build_info_labels);
64+
#[cfg(feature = "prometheus-client")]
65+
PrometheusClientTracker::set_build_info(build_info_labels);
66+
}
67+
68+
#[allow(unused_variables)]
69+
fn start(gauge_labels: Option<&GaugeLabels>) -> Self {
70+
Self {
71+
#[cfg(feature = "metrics")]
72+
metrics_tracker: MetricsTracker::start(gauge_labels),
73+
#[cfg(feature = "opentelemetry")]
74+
opentelemetry_tracker: OpenTelemetryTracker::start(gauge_labels),
75+
#[cfg(feature = "prometheus")]
76+
prometheus_tracker: PrometheusTracker::start(gauge_labels),
77+
#[cfg(feature = "prometheus-client")]
78+
prometheus_client_tracker: PrometheusClientTracker::start(gauge_labels),
79+
}
80+
}
81+
82+
#[allow(unused_variables)]
83+
fn finish(self, counter_labels: &CounterLabels, histogram_labels: &HistogramLabels) {
84+
#[cfg(feature = "metrics")]
85+
self.metrics_tracker
86+
.finish(counter_labels, histogram_labels);
87+
#[cfg(feature = "opentelemetry")]
88+
self.opentelemetry_tracker
89+
.finish(counter_labels, histogram_labels);
90+
#[cfg(feature = "prometheus")]
91+
self.prometheus_tracker
92+
.finish(counter_labels, histogram_labels);
93+
#[cfg(feature = "prometheus-client")]
94+
self.prometheus_client_tracker
95+
.finish(counter_labels, histogram_labels);
96+
}
97+
}

examples/axum/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ publish = false
55
edition = "2021"
66

77
[dependencies]
8-
autometrics = { path = "../../autometrics", features = ["prometheus-exporter"] }
8+
autometrics = { path = "../../autometrics", features = ["prometheus", "prometheus-exporter"] }
99
autometrics-example-util = { path = "../util" }
1010
axum = { version = "0.6", features = ["json"] }
1111
rand = "0.8"

examples/full-api/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ publish = false
55
edition = "2021"
66

77
[dependencies]
8-
autometrics = { path = "../../autometrics", features = ["prometheus-exporter"] }
8+
autometrics = { path = "../../autometrics", features = ["prometheus", "prometheus-exporter"] }
99
autometrics-example-util = { path = "../util" }
1010
axum = { version = "0.6", features = ["json"] }
1111
rand = "0.8"

0 commit comments

Comments
 (0)