Skip to content

Commit ae7a16e

Browse files
committed
fix(metrics-exporter-prometheus): 🐛 allow usage in wasm
if you compile with cargo build --target wasm32-unknown-unknown compile will fail
1 parent 86c7501 commit ae7a16e

File tree

8 files changed

+725
-149
lines changed

8 files changed

+725
-149
lines changed

Cargo.lock

+524-45
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+4
Original file line numberDiff line numberDiff line change
@@ -73,3 +73,7 @@ tracing = { version = "0.1", default-features = false }
7373
tracing-core = { version = "0.1", default-features = false }
7474
tracing-subscriber = { version = "0.3", default-features = false }
7575
trybuild = { version = "1", default-features = false }
76+
getrandom = { version = "0.3", default-features = false, features = ["wasm_js"] }
77+
gloo-timers = { version = "0.3.0", default-features = false, features = ["futures"] }
78+
wasm-bindgen-futures = { version = "0.4.30", default-features = false }
79+
reqwest = { version = "0.12", default-features = false }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[target.wasm32-unknown-unknown]
2+
rustflags = ['--cfg', 'getrandom_backend="wasm_js"']

metrics-exporter-prometheus/Cargo.toml

+11-3
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,6 @@ http-body-util = { workspace = true, optional = true }
3737

3838
# Optional
3939
hyper = { workspace = true, optional = true }
40-
hyper-rustls = { workspace = true, optional = true }
41-
hyper-util = { workspace = true, optional = true }
4240
indexmap = { workspace = true }
4341
ipnet = { workspace = true, optional = true }
4442
metrics = { version = "^0.24", path = "../metrics" }
@@ -49,7 +47,6 @@ metrics-util = { version = "^0.19", path = "../metrics-util", default-features =
4947
] }
5048
quanta = { workspace = true }
5149
thiserror = { workspace = true }
52-
tokio = { workspace = true, optional = true }
5350
tracing = { workspace = true, optional = true }
5451

5552
[dev-dependencies]
@@ -58,6 +55,17 @@ rand = { workspace = true }
5855
tracing = { workspace = true }
5956
tracing-subscriber = { workspace = true, features = ["fmt"] }
6057

58+
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
59+
tokio = { workspace = true, optional = true }
60+
hyper-util = { workspace = true, optional = true }
61+
hyper-rustls = { workspace = true, optional = true }
62+
63+
[target.'cfg(target_arch = "wasm32")'.dependencies]
64+
getrandom = { workspace = true, features = ["wasm_js"] }
65+
gloo-timers = { workspace = true, features = ["futures"] }
66+
wasm-bindgen-futures = { workspace = true, features = ["std"] }
67+
reqwest = { workspace = true }
68+
6169
[[example]]
6270
name = "prometheus_push_gateway"
6371
required-features = ["push-gateway"]

metrics-exporter-prometheus/src/exporter/builder.rs

+68-41
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,21 @@
11
use std::collections::HashMap;
22
#[cfg(feature = "push-gateway")]
33
use std::convert::TryFrom;
4-
#[cfg(feature = "http-listener")]
4+
#[cfg(all(feature = "http-listener", not(target_arch = "wasm32")))]
55
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
66
use std::num::NonZeroU32;
77
use std::sync::RwLock;
8-
#[cfg(any(feature = "http-listener", feature = "push-gateway"))]
8+
#[cfg(all(
9+
any(feature = "http-listener", feature = "push-gateway"),
10+
not(target_arch = "wasm32")
11+
))]
912
use std::thread;
1013
use std::time::Duration;
1114

1215
#[cfg(feature = "push-gateway")]
1316
use hyper::Uri;
1417
use indexmap::IndexMap;
15-
#[cfg(feature = "http-listener")]
18+
#[cfg(all(feature = "http-listener", not(target_arch = "wasm32")))]
1619
use ipnet::IpNet;
1720
use quanta::Clock;
1821

@@ -37,7 +40,7 @@ use super::ExporterFuture;
3740
pub struct PrometheusBuilder {
3841
#[cfg_attr(not(any(feature = "http-listener", feature = "push-gateway")), allow(dead_code))]
3942
exporter_config: ExporterConfig,
40-
#[cfg(feature = "http-listener")]
43+
#[cfg(all(feature = "http-listener", not(target_arch = "wasm32")))]
4144
allowed_addresses: Option<Vec<IpNet>>,
4245
quantiles: Vec<Quantile>,
4346
bucket_duration: Option<Duration>,
@@ -56,7 +59,7 @@ impl PrometheusBuilder {
5659
pub fn new() -> Self {
5760
let quantiles = parse_quantiles(&[0.0, 0.5, 0.9, 0.95, 0.99, 0.999, 1.0]);
5861

59-
#[cfg(feature = "http-listener")]
62+
#[cfg(all(feature = "http-listener", not(target_arch = "wasm32")))]
6063
let exporter_config = ExporterConfig::HttpListener {
6164
destination: super::ListenDestination::Tcp(SocketAddr::new(
6265
IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)),
@@ -70,7 +73,7 @@ impl PrometheusBuilder {
7073

7174
Self {
7275
exporter_config,
73-
#[cfg(feature = "http-listener")]
76+
#[cfg(all(feature = "http-listener", not(target_arch = "wasm32")))]
7477
allowed_addresses: None,
7578
quantiles,
7679
bucket_duration: None,
@@ -95,7 +98,7 @@ impl PrometheusBuilder {
9598
/// Defaults to enabled, listening at `0.0.0.0:9000`.
9699
///
97100
/// [scrape endpoint]: https://prometheus.io/docs/instrumenting/exposition_formats/#text-based-format
98-
#[cfg(feature = "http-listener")]
101+
#[cfg(all(feature = "http-listener", not(target_arch = "wasm32")))]
99102
#[cfg_attr(docsrs, doc(cfg(feature = "http-listener")))]
100103
#[must_use]
101104
pub fn with_http_listener(mut self, addr: impl Into<SocketAddr>) -> Self {
@@ -180,7 +183,7 @@ impl PrometheusBuilder {
180183
///
181184
/// If the given address cannot be parsed into an IP address or subnet, an error variant will be returned describing
182185
/// the error.
183-
#[cfg(feature = "http-listener")]
186+
#[cfg(all(feature = "http-listener", not(target_arch = "wasm32")))]
184187
#[cfg_attr(docsrs, doc(cfg(feature = "http-listener")))]
185188
pub fn add_allowed_address<A>(mut self, address: A) -> Result<Self, BuildError>
186189
where
@@ -383,42 +386,56 @@ impl PrometheusBuilder {
383386
#[cfg(any(feature = "http-listener", feature = "push-gateway"))]
384387
#[cfg_attr(docsrs, doc(cfg(any(feature = "http-listener", feature = "push-gateway"))))]
385388
pub fn install(self) -> Result<(), BuildError> {
386-
use tokio::runtime;
387-
388-
let recorder = if let Ok(handle) = runtime::Handle::try_current() {
389-
let (recorder, exporter) = {
390-
let _g = handle.enter();
391-
self.build()?
392-
};
393-
394-
handle.spawn(exporter);
395-
396-
recorder
397-
} else {
398-
let thread_name =
399-
format!("metrics-exporter-prometheus-{}", self.exporter_config.as_type_str());
400-
401-
let runtime = runtime::Builder::new_current_thread()
402-
.enable_all()
403-
.build()
404-
.map_err(|e| BuildError::FailedToCreateRuntime(e.to_string()))?;
405-
406-
let (recorder, exporter) = {
407-
let _g = runtime.enter();
408-
self.build()?
389+
#[cfg(not(target_arch = "wasm32"))]
390+
{
391+
use tokio::runtime;
392+
393+
let recorder = if let Ok(handle) = runtime::Handle::try_current() {
394+
let (recorder, exporter) = {
395+
let _g = handle.enter();
396+
self.build()?
397+
};
398+
399+
handle.spawn(exporter);
400+
401+
recorder
402+
} else {
403+
let thread_name =
404+
format!("metrics-exporter-prometheus-{}", self.exporter_config.as_type_str());
405+
406+
let runtime = runtime::Builder::new_current_thread()
407+
.enable_all()
408+
.build()
409+
.map_err(|e| BuildError::FailedToCreateRuntime(e.to_string()))?;
410+
411+
let (recorder, exporter) = {
412+
let _g = runtime.enter();
413+
self.build()?
414+
};
415+
416+
thread::Builder::new()
417+
.name(thread_name)
418+
.spawn(move || runtime.block_on(exporter))
419+
.map_err(|e| BuildError::FailedToCreateRuntime(e.to_string()))?;
420+
421+
recorder
409422
};
410423

411-
thread::Builder::new()
412-
.name(thread_name)
413-
.spawn(move || runtime.block_on(exporter))
414-
.map_err(|e| BuildError::FailedToCreateRuntime(e.to_string()))?;
424+
metrics::set_global_recorder(recorder)?;
415425

416-
recorder
417-
};
426+
Ok(())
427+
}
428+
#[cfg(target_arch = "wasm32")]
429+
{
430+
let (recorder, exporter) = self.build()?;
431+
wasm_bindgen_futures::spawn_local(async move {
432+
let _ = exporter.await;
433+
});
418434

419-
metrics::set_global_recorder(recorder)?;
435+
metrics::set_global_recorder(recorder)?;
420436

421-
Ok(())
437+
Ok(())
438+
}
422439
}
423440

424441
/// Builds the recorder and installs it globally, returning a handle to it.
@@ -461,7 +478,7 @@ impl PrometheusBuilder {
461478
#[cfg_attr(docsrs, doc(cfg(any(feature = "http-listener", feature = "push-gateway"))))]
462479
#[cfg_attr(not(feature = "http-listener"), allow(unused_mut))]
463480
pub fn build(mut self) -> Result<(PrometheusRecorder, ExporterFuture), BuildError> {
464-
#[cfg(feature = "http-listener")]
481+
#[cfg(all(feature = "http-listener", not(target_arch = "wasm32")))]
465482
let allowed_addresses = self.allowed_addresses.take();
466483
let exporter_config = self.exporter_config.clone();
467484
let upkeep_timeout = self.upkeep_timeout;
@@ -470,19 +487,29 @@ impl PrometheusBuilder {
470487
let handle = recorder.handle();
471488

472489
let recorder_handle = handle.clone();
490+
#[cfg(not(target_arch = "wasm32"))]
473491
tokio::spawn(async move {
474492
loop {
475493
tokio::time::sleep(upkeep_timeout).await;
476494
recorder_handle.run_upkeep();
477495
}
478496
});
497+
#[cfg(target_arch = "wasm32")]
498+
{
499+
wasm_bindgen_futures::spawn_local(async move {
500+
loop {
501+
gloo_timers::future::sleep(upkeep_timeout).await;
502+
recorder_handle.run_upkeep();
503+
}
504+
});
505+
}
479506

480507
Ok((
481508
recorder,
482509
match exporter_config {
483510
ExporterConfig::Unconfigured => Err(BuildError::MissingExporterConfiguration)?,
484511

485-
#[cfg(feature = "http-listener")]
512+
#[cfg(all(feature = "http-listener", not(target_arch = "wasm32")))]
486513
ExporterConfig::HttpListener { destination } => match destination {
487514
super::ListenDestination::Tcp(listen_address) => {
488515
super::http_listener::new_http_listener(

metrics-exporter-prometheus/src/exporter/http_listener.rs

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use hyper::{
88
service::service_fn,
99
Request, Response, StatusCode,
1010
};
11+
#[cfg(all(feature = "http-listener", not(target_arch = "wasm32")))]
1112
use hyper_util::rt::TokioIo;
1213
use ipnet::IpNet;
1314
#[cfg(feature = "uds-listener")]

metrics-exporter-prometheus/src/exporter/mod.rs

+15-9
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
#[cfg(feature = "http-listener")]
1+
#[cfg(all(feature = "http-listener", not(target_arch = "wasm32")))]
22
use http_listener::HttpListeningError;
33
#[cfg(any(feature = "http-listener", feature = "push-gateway"))]
44
use std::future::Future;
5-
#[cfg(feature = "http-listener")]
5+
#[cfg(all(feature = "http-listener", not(target_arch = "wasm32")))]
66
use std::net::SocketAddr;
77
#[cfg(any(feature = "http-listener", feature = "push-gateway"))]
88
use std::pin::Pin;
@@ -16,15 +16,18 @@ use hyper::Uri;
1616
#[cfg(any(feature = "http-listener", feature = "push-gateway"))]
1717
#[derive(Debug)]
1818
pub enum ExporterError {
19-
#[cfg(feature = "http-listener")]
19+
#[cfg(all(feature = "http-listener", not(target_arch = "wasm32")))]
2020
HttpListener(HttpListeningError),
2121
PushGateway(()),
2222
}
23+
#[cfg(all(any(feature = "http-listener", feature = "push-gateway"), not(target_arch = "wasm32")))]
2324
/// Convenience type for Future implementing an exporter.
24-
#[cfg(any(feature = "http-listener", feature = "push-gateway"))]
2525
pub type ExporterFuture = Pin<Box<dyn Future<Output = Result<(), ExporterError>> + Send + 'static>>;
26+
#[cfg(target_arch = "wasm32")]
27+
/// Convenience type for Future implementing an exporter.
28+
pub type ExporterFuture = Pin<Box<dyn Future<Output = Result<(), ExporterError>>>>;
2629

27-
#[cfg(feature = "http-listener")]
30+
#[cfg(all(feature = "http-listener", not(target_arch = "wasm32")))]
2831
#[derive(Clone, Debug)]
2932
enum ListenDestination {
3033
Tcp(SocketAddr),
@@ -35,7 +38,7 @@ enum ListenDestination {
3538
#[derive(Clone, Debug)]
3639
enum ExporterConfig {
3740
// Run an HTTP listener on the given `listen_address`.
38-
#[cfg(feature = "http-listener")]
41+
#[cfg(all(feature = "http-listener", not(target_arch = "wasm32")))]
3942
HttpListener { destination: ListenDestination },
4043

4144
// Run a push gateway task sending to the given `endpoint` after `interval` time has elapsed,
@@ -54,10 +57,13 @@ enum ExporterConfig {
5457
}
5558

5659
impl ExporterConfig {
57-
#[cfg_attr(not(any(feature = "http-listener", feature = "push-gateway")), allow(dead_code))]
60+
#[cfg_attr(
61+
any(not(feature = "http-listener"), not(feature = "push-gateway"), target_arch = "wasm32"),
62+
allow(dead_code)
63+
)]
5864
fn as_type_str(&self) -> &'static str {
5965
match self {
60-
#[cfg(feature = "http-listener")]
66+
#[cfg(all(feature = "http-listener", not(target_arch = "wasm32")))]
6167
Self::HttpListener { .. } => "http-listener",
6268
#[cfg(feature = "push-gateway")]
6369
Self::PushGateway { .. } => "push-gateway",
@@ -66,7 +72,7 @@ impl ExporterConfig {
6672
}
6773
}
6874

69-
#[cfg(feature = "http-listener")]
75+
#[cfg(all(feature = "http-listener", not(target_arch = "wasm32")))]
7076
mod http_listener;
7177

7278
#[cfg(feature = "push-gateway")]

0 commit comments

Comments
 (0)