polkadot_node_metrics/runtime/
mod.rs1#![cfg(feature = "runtime-metrics")]
29
30use codec::Decode;
31use polkadot_primitives::{
32 metric_definitions::{CounterDefinition, CounterVecDefinition, HistogramDefinition},
33 RuntimeMetricLabelValues, RuntimeMetricOp, RuntimeMetricUpdate,
34};
35use prometheus_endpoint::{
36 register, Counter, CounterVec, Histogram, HistogramOpts, Opts, PrometheusError, Registry, U64,
37};
38use std::{
39 collections::hash_map::HashMap,
40 sync::{Arc, Mutex, MutexGuard},
41};
42mod parachain;
43
44#[derive(Clone, Default)]
46pub struct Metrics {
47 counter_vecs: Arc<Mutex<HashMap<String, CounterVec<U64>>>>,
48 counters: Arc<Mutex<HashMap<String, Counter<U64>>>>,
49 histograms: Arc<Mutex<HashMap<String, Histogram>>>,
50}
51
52#[derive(Clone)]
54pub struct RuntimeMetricsProvider(Registry, Metrics);
55
56impl RuntimeMetricsProvider {
57 pub fn new(metrics_registry: Registry) -> Self {
59 Self(metrics_registry, Metrics::default())
60 }
61
62 pub fn register_countervec(&self, countervec: CounterVecDefinition) {
64 self.with_counter_vecs_lock_held(|mut hashmap| {
65 hashmap.entry(countervec.name.to_owned()).or_insert(register(
66 CounterVec::new(
67 Opts::new(countervec.name, countervec.description),
68 countervec.labels,
69 )?,
70 &self.0,
71 )?);
72 Ok(())
73 })
74 }
75
76 pub fn register_counter(&self, counter: CounterDefinition) {
78 self.with_counters_lock_held(|mut hashmap| {
79 hashmap
80 .entry(counter.name.to_owned())
81 .or_insert(register(Counter::new(counter.name, counter.description)?, &self.0)?);
82 return Ok(())
83 })
84 }
85
86 pub fn register_histogram(&self, hist: HistogramDefinition) {
88 self.with_histograms_lock_held(|mut hashmap| {
89 hashmap.entry(hist.name.to_owned()).or_insert(register(
90 Histogram::with_opts(
91 HistogramOpts::new(hist.name, hist.description).buckets(hist.buckets.to_vec()),
92 )?,
93 &self.0,
94 )?);
95 return Ok(())
96 })
97 }
98
99 pub fn inc_counter_vec_by(&self, name: &str, value: u64, labels: &RuntimeMetricLabelValues) {
101 self.with_counter_vecs_lock_held(|mut hashmap| {
102 hashmap.entry(name.to_owned()).and_modify(|counter_vec| {
103 counter_vec.with_label_values(&labels.as_str_vec()).inc_by(value)
104 });
105
106 Ok(())
107 });
108 }
109
110 pub fn inc_counter_by(&self, name: &str, value: u64) {
112 self.with_counters_lock_held(|mut hashmap| {
113 hashmap
114 .entry(name.to_owned())
115 .and_modify(|counter_vec| counter_vec.inc_by(value));
116 Ok(())
117 })
118 }
119
120 pub fn observe_histogram(&self, name: &str, value: u128) {
122 self.with_histograms_lock_held(|mut hashmap| {
123 hashmap
124 .entry(name.to_owned())
125 .and_modify(|histogram| histogram.observe(value as f64 / 1_000_000_000.0)); Ok(())
127 })
128 }
129
130 fn with_counters_lock_held<F>(&self, do_something: F)
131 where
132 F: FnOnce(MutexGuard<'_, HashMap<String, Counter<U64>>>) -> Result<(), PrometheusError>,
133 {
134 let _ = self.1.counters.lock().map(do_something).or_else(|error| Err(error));
135 }
136
137 fn with_counter_vecs_lock_held<F>(&self, do_something: F)
138 where
139 F: FnOnce(MutexGuard<'_, HashMap<String, CounterVec<U64>>>) -> Result<(), PrometheusError>,
140 {
141 let _ = self.1.counter_vecs.lock().map(do_something).or_else(|error| Err(error));
142 }
143
144 fn with_histograms_lock_held<F>(&self, do_something: F)
145 where
146 F: FnOnce(MutexGuard<'_, HashMap<String, Histogram>>) -> Result<(), PrometheusError>,
147 {
148 let _ = self.1.histograms.lock().map(do_something).or_else(|error| Err(error));
149 }
150}
151
152impl sc_tracing::TraceHandler for RuntimeMetricsProvider {
153 fn handle_span(&self, _span: &sc_tracing::SpanDatum) {}
154 fn handle_event(&self, event: &sc_tracing::TraceEvent) {
155 if event
156 .values
157 .string_values
158 .get("target")
159 .unwrap_or(&String::default())
160 .ne("metrics")
161 {
162 return
163 }
164
165 if let Some(update_op_bs58) = event.values.string_values.get("params") {
166 match RuntimeMetricUpdate::decode(
168 &mut RuntimeMetricsProvider::parse_event_params(&update_op_bs58)
169 .unwrap_or_default()
170 .as_slice(),
171 ) {
172 Ok(update_op) => {
173 self.parse_metric_update(update_op);
174 },
175 Err(_) => {
176 },
178 }
179 }
180 }
181}
182
183impl RuntimeMetricsProvider {
184 fn parse_metric_update(&self, update: RuntimeMetricUpdate) {
186 match update.op {
187 RuntimeMetricOp::IncrementCounterVec(value, ref labels) =>
188 self.inc_counter_vec_by(update.metric_name(), value, labels),
189 RuntimeMetricOp::IncrementCounter(value) =>
190 self.inc_counter_by(update.metric_name(), value),
191 RuntimeMetricOp::ObserveHistogram(value) =>
192 self.observe_histogram(update.metric_name(), value),
193 }
194 }
195
196 fn parse_event_params(event_params: &str) -> Option<Vec<u8>> {
198 let new_len = event_params.len().saturating_sub(2);
200 let event_params = &event_params[..new_len];
201
202 const SKIP_CHARS: &'static str = " { update_op: ";
204 if SKIP_CHARS.len() < event_params.len() {
205 if SKIP_CHARS.eq_ignore_ascii_case(&event_params[..SKIP_CHARS.len()]) {
206 return bs58::decode(&event_params[SKIP_CHARS.len()..].as_bytes()).into_vec().ok()
207 }
208 }
209
210 None
212 }
213}
214
215pub fn logger_hook() -> impl FnOnce(&mut sc_cli::LoggerBuilder, &sc_service::Configuration) -> () {
217 |logger_builder, config| {
218 if config.prometheus_registry().is_none() {
219 return
220 }
221 let registry = config.prometheus_registry().cloned().unwrap();
222 let metrics_provider = RuntimeMetricsProvider::new(registry);
223 parachain::register_metrics(&metrics_provider);
224 logger_builder.with_custom_profiling(Box::new(metrics_provider));
225 }
226}