feat: initialize Kurdistan SDK - independent fork of Polkadot SDK
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// This file is part of Pezkuwi.
|
||||
|
||||
// Pezkuwi is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Pezkuwi is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Pezkuwi. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Metrics helpers
|
||||
//!
|
||||
//! Collects a bunch of metrics providers and related features such as
|
||||
//! `Metronome` for usage with metrics collections.
|
||||
//!
|
||||
//! This crate also reexports Prometheus metric types which are expected to be implemented by
|
||||
//! subsystems.
|
||||
|
||||
#![deny(missing_docs)]
|
||||
#![deny(unused_imports)]
|
||||
|
||||
pub use metered;
|
||||
|
||||
/// Cyclic metric collection support.
|
||||
pub mod metronome;
|
||||
pub use self::metronome::Metronome;
|
||||
|
||||
#[cfg(feature = "runtime-metrics")]
|
||||
pub mod runtime;
|
||||
#[cfg(feature = "runtime-metrics")]
|
||||
pub use self::runtime::logger_hook;
|
||||
|
||||
/// Export a dummy logger hook when the `runtime-metrics` feature is not enabled.
|
||||
#[cfg(not(feature = "runtime-metrics"))]
|
||||
pub fn logger_hook() -> impl FnOnce(&mut sc_cli::LoggerBuilder, &sc_service::Configuration) {
|
||||
|_logger_builder, _config| {}
|
||||
}
|
||||
|
||||
/// This module reexports Prometheus types and defines the [`Metrics`](metrics::Metrics) trait.
|
||||
pub mod metrics {
|
||||
/// Reexport Substrate Prometheus types.
|
||||
pub use prometheus_endpoint as prometheus;
|
||||
|
||||
/// Subsystem- or job-specific Prometheus metrics.
|
||||
///
|
||||
/// Usually implemented as a wrapper for `Option<ActualMetrics>`
|
||||
/// to ensure `Default` bounds or as a dummy type ().
|
||||
/// Prometheus metrics internally hold an `Arc` reference, so cloning them is fine.
|
||||
pub trait Metrics: Default + Clone {
|
||||
/// Try to register metrics in the Prometheus registry.
|
||||
fn try_register(
|
||||
registry: &prometheus::Registry,
|
||||
) -> Result<Self, prometheus::PrometheusError>;
|
||||
|
||||
/// Convenience method to register metrics in the optional Prometheus registry.
|
||||
///
|
||||
/// If no registry is provided, returns `Default::default()`. Otherwise, returns the same
|
||||
/// thing that `try_register` does.
|
||||
fn register(
|
||||
registry: Option<&prometheus::Registry>,
|
||||
) -> Result<Self, prometheus::PrometheusError> {
|
||||
match registry {
|
||||
None => Ok(Self::default()),
|
||||
Some(registry) => Self::try_register(registry),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// dummy impl
|
||||
impl Metrics for () {
|
||||
fn try_register(
|
||||
_registry: &prometheus::Registry,
|
||||
) -> Result<(), prometheus::PrometheusError> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "runtime-metrics", not(feature = "runtime-benchmarks"), test))]
|
||||
mod tests;
|
||||
@@ -0,0 +1,67 @@
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// This file is part of Pezkuwi.
|
||||
|
||||
// Pezkuwi is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Pezkuwi is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Pezkuwi. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use futures::prelude::*;
|
||||
use futures_timer::Delay;
|
||||
use std::{
|
||||
pin::Pin,
|
||||
task::{Context, Poll},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
enum MetronomeState {
|
||||
Snooze,
|
||||
SetAlarm,
|
||||
}
|
||||
|
||||
/// Create a stream of ticks with a defined cycle duration.
|
||||
pub struct Metronome {
|
||||
delay: Delay,
|
||||
period: Duration,
|
||||
state: MetronomeState,
|
||||
}
|
||||
|
||||
impl Metronome {
|
||||
/// Create a new metronome source with a defined cycle duration.
|
||||
pub fn new(cycle: Duration) -> Self {
|
||||
let period = cycle.into();
|
||||
Self { period, delay: Delay::new(period), state: MetronomeState::Snooze }
|
||||
}
|
||||
}
|
||||
|
||||
impl futures::Stream for Metronome {
|
||||
type Item = ();
|
||||
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
||||
loop {
|
||||
match self.state {
|
||||
MetronomeState::SetAlarm => {
|
||||
let val = self.period;
|
||||
self.delay.reset(val);
|
||||
self.state = MetronomeState::Snooze;
|
||||
},
|
||||
MetronomeState::Snooze => {
|
||||
if !Pin::new(&mut self.delay).poll(cx).is_ready() {
|
||||
break;
|
||||
}
|
||||
self.state = MetronomeState::SetAlarm;
|
||||
return Poll::Ready(Some(()));
|
||||
},
|
||||
}
|
||||
}
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,226 @@
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// This file is part of Pezkuwi.
|
||||
|
||||
// Pezkuwi is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Pezkuwi is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Pezkuwi. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Runtime Metrics helpers.
|
||||
//!
|
||||
//! A runtime metric provider implementation that builds on top of Substrate wasm
|
||||
//! tracing support. This requires that the custom profiler (`TraceHandler`) to be
|
||||
//! registered in substrate via a `logger_hook()`. Events emitted from runtime are
|
||||
//! then captured/processed by the `TraceHandler` implementation.
|
||||
//!
|
||||
//! Don't add logs in this file because it gets executed before the logger is
|
||||
//! initialized and they won't be delivered. Add println! statements if you need
|
||||
//! to debug this code.
|
||||
|
||||
#![cfg(feature = "runtime-metrics")]
|
||||
|
||||
use codec::Decode;
|
||||
use pezkuwi_primitives::{
|
||||
metric_definitions::{CounterDefinition, CounterVecDefinition, HistogramDefinition},
|
||||
RuntimeMetricLabelValues, RuntimeMetricOp, RuntimeMetricUpdate,
|
||||
};
|
||||
use prometheus_endpoint::{
|
||||
register, Counter, CounterVec, Histogram, HistogramOpts, Opts, PrometheusError, Registry, U64,
|
||||
};
|
||||
use std::{
|
||||
collections::hash_map::HashMap,
|
||||
sync::{Arc, Mutex, MutexGuard},
|
||||
};
|
||||
mod teyrchain;
|
||||
|
||||
/// Holds the registered Prometheus metric collections.
|
||||
#[derive(Clone, Default)]
|
||||
pub struct Metrics {
|
||||
counter_vecs: Arc<Mutex<HashMap<String, CounterVec<U64>>>>,
|
||||
counters: Arc<Mutex<HashMap<String, Counter<U64>>>>,
|
||||
histograms: Arc<Mutex<HashMap<String, Histogram>>>,
|
||||
}
|
||||
|
||||
/// Runtime metrics wrapper.
|
||||
#[derive(Clone)]
|
||||
pub struct RuntimeMetricsProvider(Registry, Metrics);
|
||||
|
||||
impl RuntimeMetricsProvider {
|
||||
/// Creates new instance.
|
||||
pub fn new(metrics_registry: Registry) -> Self {
|
||||
Self(metrics_registry, Metrics::default())
|
||||
}
|
||||
|
||||
/// Register a counter vec metric.
|
||||
pub fn register_countervec(&self, countervec: CounterVecDefinition) {
|
||||
self.with_counter_vecs_lock_held(|mut hashmap| {
|
||||
hashmap.entry(countervec.name.to_owned()).or_insert(register(
|
||||
CounterVec::new(
|
||||
Opts::new(countervec.name, countervec.description),
|
||||
countervec.labels,
|
||||
)?,
|
||||
&self.0,
|
||||
)?);
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
/// Register a counter metric.
|
||||
pub fn register_counter(&self, counter: CounterDefinition) {
|
||||
self.with_counters_lock_held(|mut hashmap| {
|
||||
hashmap
|
||||
.entry(counter.name.to_owned())
|
||||
.or_insert(register(Counter::new(counter.name, counter.description)?, &self.0)?);
|
||||
return Ok(());
|
||||
})
|
||||
}
|
||||
|
||||
/// Register a histogram metric
|
||||
pub fn register_histogram(&self, hist: HistogramDefinition) {
|
||||
self.with_histograms_lock_held(|mut hashmap| {
|
||||
hashmap.entry(hist.name.to_owned()).or_insert(register(
|
||||
Histogram::with_opts(
|
||||
HistogramOpts::new(hist.name, hist.description).buckets(hist.buckets.to_vec()),
|
||||
)?,
|
||||
&self.0,
|
||||
)?);
|
||||
return Ok(());
|
||||
})
|
||||
}
|
||||
|
||||
/// Increment a counter with labels by a value
|
||||
pub fn inc_counter_vec_by(&self, name: &str, value: u64, labels: &RuntimeMetricLabelValues) {
|
||||
self.with_counter_vecs_lock_held(|mut hashmap| {
|
||||
hashmap.entry(name.to_owned()).and_modify(|counter_vec| {
|
||||
counter_vec.with_label_values(&labels.as_str_vec()).inc_by(value)
|
||||
});
|
||||
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
|
||||
/// Increment a counter by a value.
|
||||
pub fn inc_counter_by(&self, name: &str, value: u64) {
|
||||
self.with_counters_lock_held(|mut hashmap| {
|
||||
hashmap
|
||||
.entry(name.to_owned())
|
||||
.and_modify(|counter_vec| counter_vec.inc_by(value));
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
/// Observe a histogram. `value` should be in `ns`.
|
||||
pub fn observe_histogram(&self, name: &str, value: u128) {
|
||||
self.with_histograms_lock_held(|mut hashmap| {
|
||||
hashmap
|
||||
.entry(name.to_owned())
|
||||
.and_modify(|histogram| histogram.observe(value as f64 / 1_000_000_000.0)); // ns to sec
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn with_counters_lock_held<F>(&self, do_something: F)
|
||||
where
|
||||
F: FnOnce(MutexGuard<'_, HashMap<String, Counter<U64>>>) -> Result<(), PrometheusError>,
|
||||
{
|
||||
let _ = self.1.counters.lock().map(do_something).or_else(|error| Err(error));
|
||||
}
|
||||
|
||||
fn with_counter_vecs_lock_held<F>(&self, do_something: F)
|
||||
where
|
||||
F: FnOnce(MutexGuard<'_, HashMap<String, CounterVec<U64>>>) -> Result<(), PrometheusError>,
|
||||
{
|
||||
let _ = self.1.counter_vecs.lock().map(do_something).or_else(|error| Err(error));
|
||||
}
|
||||
|
||||
fn with_histograms_lock_held<F>(&self, do_something: F)
|
||||
where
|
||||
F: FnOnce(MutexGuard<'_, HashMap<String, Histogram>>) -> Result<(), PrometheusError>,
|
||||
{
|
||||
let _ = self.1.histograms.lock().map(do_something).or_else(|error| Err(error));
|
||||
}
|
||||
}
|
||||
|
||||
impl sc_tracing::TraceHandler for RuntimeMetricsProvider {
|
||||
fn handle_span(&self, _span: &sc_tracing::SpanDatum) {}
|
||||
fn handle_event(&self, event: &sc_tracing::TraceEvent) {
|
||||
if event
|
||||
.values
|
||||
.string_values
|
||||
.get("target")
|
||||
.unwrap_or(&String::default())
|
||||
.ne("metrics")
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(update_op_bs58) = event.values.string_values.get("params") {
|
||||
// Deserialize the metric update struct.
|
||||
match RuntimeMetricUpdate::decode(
|
||||
&mut RuntimeMetricsProvider::parse_event_params(&update_op_bs58)
|
||||
.unwrap_or_default()
|
||||
.as_slice(),
|
||||
) {
|
||||
Ok(update_op) => {
|
||||
self.parse_metric_update(update_op);
|
||||
},
|
||||
Err(_) => {
|
||||
// do nothing
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RuntimeMetricsProvider {
|
||||
// Parse end execute the update operation.
|
||||
fn parse_metric_update(&self, update: RuntimeMetricUpdate) {
|
||||
match update.op {
|
||||
RuntimeMetricOp::IncrementCounterVec(value, ref labels) =>
|
||||
self.inc_counter_vec_by(update.metric_name(), value, labels),
|
||||
RuntimeMetricOp::IncrementCounter(value) =>
|
||||
self.inc_counter_by(update.metric_name(), value),
|
||||
RuntimeMetricOp::ObserveHistogram(value) =>
|
||||
self.observe_histogram(update.metric_name(), value),
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the `bs58` encoded metric update operation.
|
||||
fn parse_event_params(event_params: &str) -> Option<Vec<u8>> {
|
||||
// Shave " }" suffix.
|
||||
let new_len = event_params.len().saturating_sub(2);
|
||||
let event_params = &event_params[..new_len];
|
||||
|
||||
// Shave " { update_op: " prefix.
|
||||
const SKIP_CHARS: &'static str = " { update_op: ";
|
||||
if SKIP_CHARS.len() < event_params.len() {
|
||||
if SKIP_CHARS.eq_ignore_ascii_case(&event_params[..SKIP_CHARS.len()]) {
|
||||
return bs58::decode(&event_params[SKIP_CHARS.len()..].as_bytes()).into_vec().ok();
|
||||
}
|
||||
}
|
||||
|
||||
// No event was parsed
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the custom profiling closure that we'll apply to the `LoggerBuilder`.
|
||||
pub fn logger_hook() -> impl FnOnce(&mut sc_cli::LoggerBuilder, &sc_service::Configuration) -> () {
|
||||
|logger_builder, config| {
|
||||
if config.prometheus_registry().is_none() {
|
||||
return;
|
||||
}
|
||||
let registry = config.prometheus_registry().cloned().unwrap();
|
||||
let metrics_provider = RuntimeMetricsProvider::new(registry);
|
||||
teyrchain::register_metrics(&metrics_provider);
|
||||
logger_builder.with_custom_profiling(Box::new(metrics_provider));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// This file is part of Pezkuwi.
|
||||
|
||||
// Pezkuwi is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Pezkuwi is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Pezkuwi. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Client side declaration and registration of the teyrchain Prometheus metrics.
|
||||
//! All of the metrics have a correspondent runtime metric definition.
|
||||
|
||||
use crate::runtime::RuntimeMetricsProvider;
|
||||
use pezkuwi_primitives::metric_definitions::{
|
||||
TEYRCHAIN_CREATE_INHERENT_BITFIELDS_SIGNATURE_CHECKS,
|
||||
TEYRCHAIN_INHERENT_DATA_BITFIELDS_PROCESSED, TEYRCHAIN_INHERENT_DATA_CANDIDATES_PROCESSED,
|
||||
TEYRCHAIN_INHERENT_DATA_DISPUTE_SETS_PROCESSED, TEYRCHAIN_INHERENT_DATA_WEIGHT,
|
||||
TEYRCHAIN_VERIFY_DISPUTE_SIGNATURE,
|
||||
};
|
||||
|
||||
/// Register the teyrchain runtime metrics.
|
||||
pub fn register_metrics(runtime_metrics_provider: &RuntimeMetricsProvider) {
|
||||
runtime_metrics_provider.register_counter(TEYRCHAIN_INHERENT_DATA_BITFIELDS_PROCESSED);
|
||||
|
||||
runtime_metrics_provider.register_countervec(TEYRCHAIN_INHERENT_DATA_WEIGHT);
|
||||
runtime_metrics_provider.register_countervec(TEYRCHAIN_INHERENT_DATA_DISPUTE_SETS_PROCESSED);
|
||||
runtime_metrics_provider.register_countervec(TEYRCHAIN_INHERENT_DATA_CANDIDATES_PROCESSED);
|
||||
runtime_metrics_provider
|
||||
.register_countervec(TEYRCHAIN_CREATE_INHERENT_BITFIELDS_SIGNATURE_CHECKS);
|
||||
runtime_metrics_provider.register_histogram(TEYRCHAIN_VERIFY_DISPUTE_SIGNATURE);
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// This file is part of Pezkuwi.
|
||||
|
||||
// Pezkuwi is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Pezkuwi is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Pezkuwi. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Pezkuwi runtime metrics integration test.
|
||||
|
||||
use http_body_util::BodyExt;
|
||||
use hyper::Uri;
|
||||
use hyper_util::{client::legacy::Client, rt::TokioExecutor};
|
||||
use pezkuwi_primitives::metric_definitions::TEYRCHAIN_INHERENT_DATA_BITFIELDS_PROCESSED;
|
||||
use pezkuwi_test_service::{node_config, run_validator_node, test_prometheus_config};
|
||||
use sp_keyring::Sr25519Keyring::*;
|
||||
use std::collections::HashMap;
|
||||
|
||||
const DEFAULT_PROMETHEUS_PORT: u16 = 9616;
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn runtime_can_publish_metrics() {
|
||||
let mut alice_config =
|
||||
node_config(|| {}, tokio::runtime::Handle::current(), Alice, Vec::new(), true);
|
||||
|
||||
// Enable Prometheus metrics for Alice.
|
||||
alice_config.prometheus_config = Some(test_prometheus_config(DEFAULT_PROMETHEUS_PORT));
|
||||
|
||||
let mut builder = sc_cli::LoggerBuilder::new("");
|
||||
|
||||
// Enable profiling with `wasm_tracing` target.
|
||||
builder.with_profiling(Default::default(), String::from("wasm_tracing=trace"));
|
||||
|
||||
// Setup the runtime metrics provider.
|
||||
crate::logger_hook()(&mut builder, &alice_config);
|
||||
|
||||
builder.init().expect("Failed to set up the logger");
|
||||
|
||||
// Start validator Alice.
|
||||
let alice = run_validator_node(alice_config, None).await;
|
||||
|
||||
let bob_config =
|
||||
node_config(|| {}, tokio::runtime::Handle::current(), Bob, vec![alice.addr.clone()], true);
|
||||
|
||||
// Start validator Bob.
|
||||
let _bob = run_validator_node(bob_config, None).await;
|
||||
|
||||
// Wait for Alice to see two finalized blocks.
|
||||
alice.wait_for_finalized_blocks(2).await;
|
||||
|
||||
let metrics_uri = format!("http://localhost:{}/metrics", DEFAULT_PROMETHEUS_PORT);
|
||||
let metrics = scrape_prometheus_metrics(&metrics_uri).await;
|
||||
|
||||
// There should be at least 1 bitfield processed by now.
|
||||
assert!(
|
||||
*metrics
|
||||
.get(&TEYRCHAIN_INHERENT_DATA_BITFIELDS_PROCESSED.name.to_owned())
|
||||
.unwrap() > 1
|
||||
);
|
||||
}
|
||||
|
||||
async fn scrape_prometheus_metrics(metrics_uri: &str) -> HashMap<String, u64> {
|
||||
let res = Client::builder(TokioExecutor::new())
|
||||
.build_http::<http_body_util::Full<hyper::body::Bytes>>()
|
||||
.get(Uri::try_from(metrics_uri).expect("bad URI"))
|
||||
.await
|
||||
.expect("GET request failed");
|
||||
|
||||
// Retrieve the `HTTP` response body.
|
||||
let body = String::from_utf8(
|
||||
res.into_body()
|
||||
.collect()
|
||||
.await
|
||||
.expect("can't get body as bytes")
|
||||
.to_bytes()
|
||||
.to_vec(),
|
||||
)
|
||||
.expect("body is not an UTF8 string");
|
||||
|
||||
let lines: Vec<_> = body.lines().map(|s| Ok(s.to_owned())).collect();
|
||||
prometheus_parse::Scrape::parse(lines.into_iter())
|
||||
.expect("Scraper failed to parse Prometheus metrics")
|
||||
.samples
|
||||
.into_iter()
|
||||
.filter_map(|prometheus_parse::Sample { metric, value, .. }| match value {
|
||||
prometheus_parse::Value::Counter(value) => Some((metric, value as u64)),
|
||||
prometheus_parse::Value::Gauge(value) => Some((metric, value as u64)),
|
||||
prometheus_parse::Value::Untyped(value) => Some((metric, value as u64)),
|
||||
_ => None,
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
Reference in New Issue
Block a user