[tor-commits] [tor] 02/04: metrics: Add support for histograms.
gitolite role
git at cupani.torproject.org
Mon Mar 13 15:23:52 UTC 2023
This is an automated email from the git hooks/post-receive script.
dgoulet pushed a commit to branch main
in repository tor.
commit d1264d11c3320b8a28c3715acca8a8ace1d70569
Author: Gabriela Moldovan <gabi at torproject.org>
AuthorDate: Wed Mar 8 17:51:58 2023 +0000
metrics: Add support for histograms.
This will enable us to add e.g. circuit build metrics (#40717).
Signed-off-by: Gabriela Moldovan <gabi at torproject.org>
---
changes/ticket40757 | 3 +
src/feature/hs/hs_metrics.c | 7 +-
src/feature/hs/hs_metrics_entry.h | 4 +
src/feature/relay/relay_metrics.c | 170 +++++++++++++++++-----------------
src/lib/metrics/metrics_common.c | 2 +
src/lib/metrics/metrics_common.h | 23 +++++
src/lib/metrics/metrics_store.c | 6 +-
src/lib/metrics/metrics_store.h | 6 +-
src/lib/metrics/metrics_store_entry.c | 157 ++++++++++++++++++++++++++++++-
src/lib/metrics/metrics_store_entry.h | 13 ++-
src/lib/metrics/prometheus.c | 70 +++++++++++++-
src/test/test_metrics.c | 146 ++++++++++++++++++++++++++++-
12 files changed, 504 insertions(+), 103 deletions(-)
diff --git a/changes/ticket40757 b/changes/ticket40757
new file mode 100644
index 0000000000..e2d8c1ed47
--- /dev/null
+++ b/changes/ticket40757
@@ -0,0 +1,3 @@
+ o Minor features (metrics):
+ - Add support for histograms.
+ Part of ticket 40757.
diff --git a/src/feature/hs/hs_metrics.c b/src/feature/hs/hs_metrics.c
index 4c6d957cf8..38cdb49e40 100644
--- a/src/feature/hs/hs_metrics.c
+++ b/src/feature/hs/hs_metrics.c
@@ -76,7 +76,8 @@ add_metric_with_labels(hs_service_t *service, hs_metrics_key_t metric,
if (!num_error_reasons) {
metrics_store_entry_t *entry = metrics_store_add(
store, base_metrics[metric].type, base_metrics[metric].name,
- base_metrics[metric].help);
+ base_metrics[metric].help, base_metrics[metric].bucket_count,
+ base_metrics[metric].buckets);
metrics_store_entry_add_label(entry,
metrics_format_label("onion", service->onion_address));
@@ -97,7 +98,9 @@ add_metric_with_labels(hs_service_t *service, hs_metrics_key_t metric,
metrics_store_entry_t *entry =
metrics_store_add(store, base_metrics[metric].type,
base_metrics[metric].name,
- base_metrics[metric].help);
+ base_metrics[metric].help,
+ base_metrics[metric].bucket_count,
+ base_metrics[metric].buckets);
/* Add labels to the entry. */
metrics_store_entry_add_label(entry,
metrics_format_label("onion", service->onion_address));
diff --git a/src/feature/hs/hs_metrics_entry.h b/src/feature/hs/hs_metrics_entry.h
index 07693972c0..b966f0c226 100644
--- a/src/feature/hs/hs_metrics_entry.h
+++ b/src/feature/hs/hs_metrics_entry.h
@@ -70,6 +70,10 @@ typedef struct hs_metrics_entry_t {
const char *name;
/* Metrics output help comment. */
const char *help;
+ /* The buckets, if the metric type is METRICS_TYPE_HISTOGRAM. */
+ const int64_t *buckets;
+ /* The number of buckets, if the metric type is METRICS_TYPE_HISTOGRAM. */
+ size_t bucket_count;
/* True iff a port label should be added to the metrics entry. */
bool port_as_label;
} hs_metrics_entry_t;
diff --git a/src/feature/relay/relay_metrics.c b/src/feature/relay/relay_metrics.c
index cdf34a3404..99204931fc 100644
--- a/src/feature/relay/relay_metrics.c
+++ b/src/feature/relay/relay_metrics.c
@@ -222,8 +222,8 @@ fill_circuits_values(void)
{
const relay_metrics_entry_t *rentry =
&base_metrics[RELAY_METRICS_NUM_CIRCUITS];
- metrics_store_entry_t *sentry =
- metrics_store_add(the_store, rentry->type, rentry->name, rentry->help);
+ metrics_store_entry_t *sentry = metrics_store_add(
+ the_store, rentry->type, rentry->name, rentry->help, 0, NULL);
metrics_store_entry_add_label(sentry,
metrics_format_label("state", "opened"));
@@ -255,57 +255,57 @@ fill_relay_flags(void)
const relay_metrics_entry_t *rentry =
&base_metrics[RELAY_METRICS_RELAY_FLAGS];
- metrics_store_entry_t *sentry =
- metrics_store_add(the_store, rentry->type, rentry->name, rentry->help);
+ metrics_store_entry_t *sentry = metrics_store_add(
+ the_store, rentry->type, rentry->name, rentry->help, 0, NULL);
metrics_store_entry_add_label(sentry,
metrics_format_label("type", "Fast"));
metrics_store_entry_update(sentry, is_fast);
sentry = metrics_store_add(the_store, rentry->type, rentry->name,
- rentry->help);
+ rentry->help, 0, NULL);
metrics_store_entry_add_label(sentry,
metrics_format_label("type", "Exit"));
metrics_store_entry_update(sentry, is_exit);
sentry = metrics_store_add(the_store, rentry->type, rentry->name,
- rentry->help);
+ rentry->help, 0, NULL);
metrics_store_entry_add_label(sentry,
metrics_format_label("type", "Authority"));
metrics_store_entry_update(sentry, is_authority);
sentry = metrics_store_add(the_store, rentry->type, rentry->name,
- rentry->help);
+ rentry->help, 0, NULL);
metrics_store_entry_add_label(sentry,
metrics_format_label("type", "Stable"));
metrics_store_entry_update(sentry, is_stable);
sentry = metrics_store_add(the_store, rentry->type, rentry->name,
- rentry->help);
+ rentry->help, 0, NULL);
metrics_store_entry_add_label(sentry,
metrics_format_label("type", "HSDir"));
metrics_store_entry_update(sentry, is_hs_dir);
sentry = metrics_store_add(the_store, rentry->type, rentry->name,
- rentry->help);
+ rentry->help, 0, NULL);
metrics_store_entry_add_label(sentry,
metrics_format_label("type", "Running"));
metrics_store_entry_update(sentry, is_running);
sentry = metrics_store_add(the_store, rentry->type, rentry->name,
- rentry->help);
+ rentry->help, 0, NULL);
metrics_store_entry_add_label(sentry,
metrics_format_label("type", "V2Dir"));
metrics_store_entry_update(sentry, is_v2_dir);
sentry = metrics_store_add(the_store, rentry->type, rentry->name,
- rentry->help);
+ rentry->help, 0, NULL);
metrics_store_entry_add_label(sentry,
metrics_format_label("type", "Sybil"));
metrics_store_entry_update(sentry, is_sybil);
sentry = metrics_store_add(the_store, rentry->type, rentry->name,
- rentry->help);
+ rentry->help, 0, NULL);
metrics_store_entry_add_label(sentry,
metrics_format_label("type", "Guard"));
metrics_store_entry_update(sentry, is_guard);
@@ -317,15 +317,15 @@ fill_traffic_values(void)
{
const relay_metrics_entry_t *rentry =
&base_metrics[RELAY_METRICS_NUM_TRAFFIC];
- metrics_store_entry_t *sentry =
- metrics_store_add(the_store, rentry->type, rentry->name, rentry->help);
+ metrics_store_entry_t *sentry = metrics_store_add(
+ the_store, rentry->type, rentry->name, rentry->help, 0, NULL);
metrics_store_entry_add_label(sentry,
metrics_format_label("direction", "read"));
metrics_store_entry_update(sentry, get_bytes_read());
sentry = metrics_store_add(the_store, rentry->type, rentry->name,
- rentry->help);
+ rentry->help, 0, NULL);
metrics_store_entry_add_label(sentry,
metrics_format_label("direction", "written"));
metrics_store_entry_update(sentry, get_bytes_written());
@@ -336,57 +336,57 @@ static void
fill_dos_values(void)
{
const relay_metrics_entry_t *rentry = &base_metrics[RELAY_METRICS_NUM_DOS];
- metrics_store_entry_t *sentry =
- metrics_store_add(the_store, rentry->type, rentry->name, rentry->help);
+ metrics_store_entry_t *sentry = metrics_store_add(
+ the_store, rentry->type, rentry->name, rentry->help, 0, NULL);
metrics_store_entry_add_label(sentry,
metrics_format_label("type", "circuit_rejected"));
metrics_store_entry_update(sentry, dos_get_num_cc_rejected());
sentry = metrics_store_add(the_store, rentry->type, rentry->name,
- rentry->help);
+ rentry->help, 0, NULL);
metrics_store_entry_add_label(sentry,
metrics_format_label("type", "circuit_killed_max_cell"));
metrics_store_entry_update(sentry, stats_n_circ_max_cell_reached);
sentry = metrics_store_add(the_store, rentry->type, rentry->name,
- rentry->help);
+ rentry->help, 0, NULL);
metrics_store_entry_add_label(sentry,
metrics_format_label("type", "circuit_killed_max_cell_outq"));
metrics_store_entry_update(sentry, stats_n_circ_max_cell_outq_reached);
sentry = metrics_store_add(the_store, rentry->type, rentry->name,
- rentry->help);
+ rentry->help, 0, NULL);
metrics_store_entry_add_label(sentry,
metrics_format_label("type", "marked_address"));
metrics_store_entry_update(sentry, dos_get_num_cc_marked_addr());
sentry = metrics_store_add(the_store, rentry->type, rentry->name,
- rentry->help);
+ rentry->help, 0, NULL);
metrics_store_entry_add_label(sentry,
metrics_format_label("type", "marked_address_maxq"));
metrics_store_entry_update(sentry, dos_get_num_cc_marked_addr_maxq());
sentry = metrics_store_add(the_store, rentry->type, rentry->name,
- rentry->help);
+ rentry->help, 0, NULL);
metrics_store_entry_add_label(sentry,
metrics_format_label("type", "conn_rejected"));
metrics_store_entry_update(sentry, dos_get_num_conn_addr_connect_rejected());
sentry = metrics_store_add(the_store, rentry->type, rentry->name,
- rentry->help);
+ rentry->help, 0, NULL);
metrics_store_entry_add_label(sentry,
metrics_format_label("type", "concurrent_conn_rejected"));
metrics_store_entry_update(sentry, dos_get_num_conn_addr_rejected());
sentry = metrics_store_add(the_store, rentry->type, rentry->name,
- rentry->help);
+ rentry->help, 0, NULL);
metrics_store_entry_add_label(sentry,
metrics_format_label("type", "single_hop_refused"));
metrics_store_entry_update(sentry, dos_get_num_single_hop_refused());
sentry = metrics_store_add(the_store, rentry->type, rentry->name,
- rentry->help);
+ rentry->help, 0, NULL);
metrics_store_entry_add_label(sentry,
metrics_format_label("type", "introduce2_rejected"));
metrics_store_entry_update(sentry, hs_dos_get_intro2_rejected_count());
@@ -399,8 +399,8 @@ fill_cc_counters_values(void)
const relay_metrics_entry_t *rentry =
&base_metrics[RELAY_METRICS_CC_COUNTERS];
- metrics_store_entry_t *sentry =
- metrics_store_add(the_store, rentry->type, rentry->name, rentry->help);
+ metrics_store_entry_t *sentry = metrics_store_add(
+ the_store, rentry->type, rentry->name, rentry->help, 0, NULL);
metrics_store_entry_add_label(sentry,
metrics_format_label("state", "starvation"));
metrics_store_entry_add_label(sentry,
@@ -408,7 +408,7 @@ fill_cc_counters_values(void)
metrics_store_entry_update(sentry, congestion_control_get_num_rtt_reset());
sentry = metrics_store_add(the_store, rentry->type, rentry->name,
- rentry->help);
+ rentry->help, 0, NULL);
metrics_store_entry_add_label(sentry,
metrics_format_label("state", "clock_stalls"));
metrics_store_entry_add_label(sentry,
@@ -417,7 +417,7 @@ fill_cc_counters_values(void)
congestion_control_get_num_clock_stalls());
sentry = metrics_store_add(the_store, rentry->type, rentry->name,
- rentry->help);
+ rentry->help, 0, NULL);
metrics_store_entry_add_label(sentry,
metrics_format_label("state", "flow_control"));
metrics_store_entry_add_label(sentry,
@@ -426,7 +426,7 @@ fill_cc_counters_values(void)
cc_stats_flow_num_xoff_sent);
sentry = metrics_store_add(the_store, rentry->type, rentry->name,
- rentry->help);
+ rentry->help, 0, NULL);
metrics_store_entry_add_label(sentry,
metrics_format_label("state", "flow_control"));
metrics_store_entry_add_label(sentry,
@@ -435,7 +435,7 @@ fill_cc_counters_values(void)
cc_stats_flow_num_xon_sent);
sentry = metrics_store_add(the_store, rentry->type, rentry->name,
- rentry->help);
+ rentry->help, 0, NULL);
metrics_store_entry_add_label(sentry,
metrics_format_label("state", "cc_limits"));
metrics_store_entry_add_label(sentry,
@@ -443,7 +443,7 @@ fill_cc_counters_values(void)
metrics_store_entry_update(sentry, cc_stats_vegas_above_delta);
sentry = metrics_store_add(the_store, rentry->type, rentry->name,
- rentry->help);
+ rentry->help, 0, NULL);
metrics_store_entry_add_label(sentry,
metrics_format_label("state", "cc_limits"));
metrics_store_entry_add_label(sentry,
@@ -451,7 +451,7 @@ fill_cc_counters_values(void)
metrics_store_entry_update(sentry, cc_stats_vegas_above_ss_cwnd_max);
sentry = metrics_store_add(the_store, rentry->type, rentry->name,
- rentry->help);
+ rentry->help, 0, NULL);
metrics_store_entry_add_label(sentry,
metrics_format_label("state", "cc_limits"));
metrics_store_entry_add_label(sentry,
@@ -459,7 +459,7 @@ fill_cc_counters_values(void)
metrics_store_entry_update(sentry, cc_stats_vegas_below_ss_inc_floor);
sentry = metrics_store_add(the_store, rentry->type, rentry->name,
- rentry->help);
+ rentry->help, 0, NULL);
metrics_store_entry_add_label(sentry,
metrics_format_label("state", "cc_circuits"));
metrics_store_entry_add_label(sentry,
@@ -467,7 +467,7 @@ fill_cc_counters_values(void)
metrics_store_entry_update(sentry, cc_stats_circs_created);
sentry = metrics_store_add(the_store, rentry->type, rentry->name,
- rentry->help);
+ rentry->help, 0, NULL);
metrics_store_entry_add_label(sentry,
metrics_format_label("state", "cc_circuits"));
metrics_store_entry_add_label(sentry,
@@ -475,7 +475,7 @@ fill_cc_counters_values(void)
metrics_store_entry_update(sentry, cc_stats_circs_closed);
sentry = metrics_store_add(the_store, rentry->type, rentry->name,
- rentry->help);
+ rentry->help, 0, NULL);
metrics_store_entry_add_label(sentry,
metrics_format_label("state", "cc_circuits"));
metrics_store_entry_add_label(sentry,
@@ -490,8 +490,8 @@ fill_cc_gauges_values(void)
const relay_metrics_entry_t *rentry =
&base_metrics[RELAY_METRICS_CC_GAUGES];
- metrics_store_entry_t *sentry =
- metrics_store_add(the_store, rentry->type, rentry->name, rentry->help);
+ metrics_store_entry_t *sentry = metrics_store_add(
+ the_store, rentry->type, rentry->name, rentry->help, 0, NULL);
metrics_store_entry_add_label(sentry,
metrics_format_label("state", "slow_start_exit"));
metrics_store_entry_add_label(sentry,
@@ -500,7 +500,7 @@ fill_cc_gauges_values(void)
tor_llround(cc_stats_vegas_exit_ss_cwnd_ma));
sentry = metrics_store_add(the_store, rentry->type, rentry->name,
- rentry->help);
+ rentry->help, 0, NULL);
metrics_store_entry_add_label(sentry,
metrics_format_label("state", "slow_start_exit"));
metrics_store_entry_add_label(sentry,
@@ -509,7 +509,7 @@ fill_cc_gauges_values(void)
tor_llround(cc_stats_vegas_exit_ss_bdp_ma));
sentry = metrics_store_add(the_store, rentry->type, rentry->name,
- rentry->help);
+ rentry->help, 0, NULL);
metrics_store_entry_add_label(sentry,
metrics_format_label("state", "slow_start_exit"));
metrics_store_entry_add_label(sentry,
@@ -518,7 +518,7 @@ fill_cc_gauges_values(void)
tor_llround(cc_stats_vegas_exit_ss_inc_ma));
sentry = metrics_store_add(the_store, rentry->type, rentry->name,
- rentry->help);
+ rentry->help, 0, NULL);
metrics_store_entry_add_label(sentry,
metrics_format_label("state", "on_circ_close"));
metrics_store_entry_add_label(sentry,
@@ -527,7 +527,7 @@ fill_cc_gauges_values(void)
tor_llround(cc_stats_circ_close_cwnd_ma));
sentry = metrics_store_add(the_store, rentry->type, rentry->name,
- rentry->help);
+ rentry->help, 0, NULL);
metrics_store_entry_add_label(sentry,
metrics_format_label("state", "on_circ_close"));
metrics_store_entry_add_label(sentry,
@@ -536,7 +536,7 @@ fill_cc_gauges_values(void)
tor_llround(cc_stats_circ_close_ss_cwnd_ma));
sentry = metrics_store_add(the_store, rentry->type, rentry->name,
- rentry->help);
+ rentry->help, 0, NULL);
metrics_store_entry_add_label(sentry,
metrics_format_label("state", "buffers"));
metrics_store_entry_add_label(sentry,
@@ -545,7 +545,7 @@ fill_cc_gauges_values(void)
tor_llround(cc_stats_flow_xon_outbuf_ma));
sentry = metrics_store_add(the_store, rentry->type, rentry->name,
- rentry->help);
+ rentry->help, 0, NULL);
metrics_store_entry_add_label(sentry,
metrics_format_label("state", "buffers"));
metrics_store_entry_add_label(sentry,
@@ -554,7 +554,7 @@ fill_cc_gauges_values(void)
tor_llround(cc_stats_flow_xoff_outbuf_ma));
sentry = metrics_store_add(the_store, rentry->type, rentry->name,
- rentry->help);
+ rentry->help, 0, NULL);
metrics_store_entry_add_label(sentry,
metrics_format_label("state", "cc_backoff"));
metrics_store_entry_add_label(sentry,
@@ -563,7 +563,7 @@ fill_cc_gauges_values(void)
tor_llround(cc_stats_vegas_csig_blocked_ma));
sentry = metrics_store_add(the_store, rentry->type, rentry->name,
- rentry->help);
+ rentry->help, 0, NULL);
metrics_store_entry_add_label(sentry,
metrics_format_label("state", "cc_backoff"));
metrics_store_entry_add_label(sentry,
@@ -572,7 +572,7 @@ fill_cc_gauges_values(void)
tor_llround(cc_stats_vegas_gamma_drop_ma));
sentry = metrics_store_add(the_store, rentry->type, rentry->name,
- rentry->help);
+ rentry->help, 0, NULL);
metrics_store_entry_add_label(sentry,
metrics_format_label("state", "cc_backoff"));
metrics_store_entry_add_label(sentry,
@@ -581,7 +581,7 @@ fill_cc_gauges_values(void)
tor_llround(cc_stats_vegas_delta_drop_ma));
sentry = metrics_store_add(the_store, rentry->type, rentry->name,
- rentry->help);
+ rentry->help, 0, NULL);
metrics_store_entry_add_label(sentry,
metrics_format_label("state", "cc_backoff"));
metrics_store_entry_add_label(sentry,
@@ -590,7 +590,7 @@ fill_cc_gauges_values(void)
tor_llround(cc_stats_vegas_ss_csig_blocked_ma));
sentry = metrics_store_add(the_store, rentry->type, rentry->name,
- rentry->help);
+ rentry->help, 0, NULL);
metrics_store_entry_add_label(sentry,
metrics_format_label("state", "cc_cwnd_update"));
metrics_store_entry_add_label(sentry,
@@ -599,7 +599,7 @@ fill_cc_gauges_values(void)
tor_llround(cc_stats_vegas_csig_alpha_ma));
sentry = metrics_store_add(the_store, rentry->type, rentry->name,
- rentry->help);
+ rentry->help, 0, NULL);
metrics_store_entry_add_label(sentry,
metrics_format_label("state", "cc_cwnd_update"));
metrics_store_entry_add_label(sentry,
@@ -608,7 +608,7 @@ fill_cc_gauges_values(void)
tor_llround(cc_stats_vegas_csig_beta_ma));
sentry = metrics_store_add(the_store, rentry->type, rentry->name,
- rentry->help);
+ rentry->help, 0, NULL);
metrics_store_entry_add_label(sentry,
metrics_format_label("state", "cc_cwnd_update"));
metrics_store_entry_add_label(sentry,
@@ -617,7 +617,7 @@ fill_cc_gauges_values(void)
tor_llround(cc_stats_vegas_csig_delta_ma));
sentry = metrics_store_add(the_store, rentry->type, rentry->name,
- rentry->help);
+ rentry->help, 0, NULL);
metrics_store_entry_add_label(sentry,
metrics_format_label("state", "cc_estimates"));
metrics_store_entry_add_label(sentry,
@@ -626,7 +626,7 @@ fill_cc_gauges_values(void)
tor_llround(cc_stats_vegas_ss_queue_ma));
sentry = metrics_store_add(the_store, rentry->type, rentry->name,
- rentry->help);
+ rentry->help, 0, NULL);
metrics_store_entry_add_label(sentry,
metrics_format_label("state", "cc_estimates"));
metrics_store_entry_add_label(sentry,
@@ -635,7 +635,7 @@ fill_cc_gauges_values(void)
tor_llround(cc_stats_vegas_queue_ma));
sentry = metrics_store_add(the_store, rentry->type, rentry->name,
- rentry->help);
+ rentry->help, 0, NULL);
metrics_store_entry_add_label(sentry,
metrics_format_label("state", "cc_estimates"));
metrics_store_entry_add_label(sentry,
@@ -659,16 +659,16 @@ fill_streams_values(void)
{
const relay_metrics_entry_t *rentry =
&base_metrics[RELAY_METRICS_NUM_STREAMS];
- metrics_store_entry_t *sentry =
- metrics_store_add(the_store, rentry->type, rentry->name, rentry->help);
+ metrics_store_entry_t *sentry = metrics_store_add(
+ the_store, rentry->type, rentry->name, rentry->help, 0, NULL);
fill_single_stream_value(sentry, RELAY_COMMAND_BEGIN);
sentry = metrics_store_add(the_store, rentry->type, rentry->name,
- rentry->help);
+ rentry->help, 0, NULL);
fill_single_stream_value(sentry, RELAY_COMMAND_BEGIN_DIR);
sentry = metrics_store_add(the_store, rentry->type, rentry->name,
- rentry->help);
+ rentry->help, 0, NULL);
fill_single_stream_value(sentry, RELAY_COMMAND_RESOLVE);
}
@@ -704,31 +704,31 @@ fill_conn_counter_values(void)
if (i == 10) {
continue;
}
- metrics_store_entry_t *sentry =
- metrics_store_add(the_store, rentry->type, rentry->name, rentry->help);
+ metrics_store_entry_t *sentry = metrics_store_add(
+ the_store, rentry->type, rentry->name, rentry->help, 0, NULL);
fill_single_connection_value(sentry, i, "initiated", "created", AF_INET,
rep_hist_get_conn_created(false, i, AF_INET));
sentry = metrics_store_add(the_store, rentry->type, rentry->name,
- rentry->help);
+ rentry->help, 0, NULL);
fill_single_connection_value(sentry, i, "initiated", "created", AF_INET6,
rep_hist_get_conn_created(false, i,
AF_INET6));
sentry = metrics_store_add(the_store, rentry->type, rentry->name,
- rentry->help);
+ rentry->help, 0, NULL);
fill_single_connection_value(sentry, i, "received", "created", AF_INET,
rep_hist_get_conn_created(true, i, AF_INET));
sentry = metrics_store_add(the_store, rentry->type, rentry->name,
- rentry->help);
+ rentry->help, 0, NULL);
fill_single_connection_value(sentry, i, "received", "created", AF_INET6,
rep_hist_get_conn_created(true, i, AF_INET6));
sentry = metrics_store_add(the_store, rentry->type, rentry->name,
- rentry->help);
+ rentry->help, 0, NULL);
fill_single_connection_value(sentry, i, "received", "rejected", AF_INET,
rep_hist_get_conn_rejected(i, AF_INET));
sentry = metrics_store_add(the_store, rentry->type, rentry->name,
- rentry->help);
+ rentry->help, 0, NULL);
fill_single_connection_value(sentry, i, "received", "rejected", AF_INET6,
rep_hist_get_conn_rejected(i, AF_INET6));
@@ -748,21 +748,21 @@ fill_conn_gauge_values(void)
if (i == 10) {
continue;
}
- metrics_store_entry_t *sentry =
- metrics_store_add(the_store, rentry->type, rentry->name, rentry->help);
+ metrics_store_entry_t *sentry = metrics_store_add(
+ the_store, rentry->type, rentry->name, rentry->help, 0, NULL);
fill_single_connection_value(sentry, i, "initiated", "opened", AF_INET,
rep_hist_get_conn_opened(false, i, AF_INET));
sentry = metrics_store_add(the_store, rentry->type, rentry->name,
- rentry->help);
+ rentry->help, 0, NULL);
fill_single_connection_value(sentry, i, "initiated", "opened", AF_INET6,
rep_hist_get_conn_opened(false, i, AF_INET6));
sentry = metrics_store_add(the_store, rentry->type, rentry->name,
- rentry->help);
+ rentry->help, 0, NULL);
fill_single_connection_value(sentry, i, "received", "opened", AF_INET,
rep_hist_get_conn_opened(true, i, AF_INET));
sentry = metrics_store_add(the_store, rentry->type, rentry->name,
- rentry->help);
+ rentry->help, 0, NULL);
fill_single_connection_value(sentry, i, "received", "opened", AF_INET6,
rep_hist_get_conn_opened(true, i, AF_INET6));
}
@@ -777,7 +777,7 @@ fill_tcp_exhaustion_values(void)
&base_metrics[RELAY_METRICS_NUM_TCP_EXHAUSTION];
sentry = metrics_store_add(the_store, rentry->type, rentry->name,
- rentry->help);
+ rentry->help, 0, NULL);
metrics_store_entry_update(sentry, rep_hist_get_n_tcp_exhaustion());
}
@@ -835,7 +835,7 @@ fill_dns_error_values(void)
for (size_t j = 0; j < num_errors; j++) {
sentry = metrics_store_add(the_store, rentry->type, rentry->name,
- rentry->help);
+ rentry->help, 0, NULL);
metrics_store_entry_add_label(sentry, record_label);
metrics_store_entry_add_label(sentry,
metrics_format_label("reason", errors[j].name));
@@ -849,7 +849,7 @@ fill_dns_error_values(void)
/* Put in the DNS errors, unfortunately not per-type for now. */
for (size_t j = 0; j < num_errors; j++) {
sentry = metrics_store_add(the_store, rentry->type, rentry->name,
- rentry->help);
+ rentry->help, 0, NULL);
metrics_store_entry_add_label(sentry,
metrics_format_label("reason", errors[j].name));
metrics_store_entry_update(sentry,
@@ -873,7 +873,7 @@ fill_dns_query_values(void)
char *record_label =
tor_strdup(metrics_format_label("record", dns_types[i].name));
sentry = metrics_store_add(the_store, rentry->type, rentry->name,
- rentry->help);
+ rentry->help, 0, NULL);
metrics_store_entry_add_label(sentry, record_label);
metrics_store_entry_update(sentry,
rep_hist_get_n_dns_request(dns_types[i].type));
@@ -882,7 +882,7 @@ fill_dns_query_values(void)
#endif
sentry = metrics_store_add(the_store, rentry->type, rentry->name,
- rentry->help);
+ rentry->help, 0, NULL);
metrics_store_entry_update(sentry, rep_hist_get_n_dns_request(0));
}
@@ -895,13 +895,13 @@ fill_global_bw_limit_values(void)
&base_metrics[RELAY_METRICS_NUM_GLOBAL_RW_LIMIT];
sentry = metrics_store_add(the_store, rentry->type, rentry->name,
- rentry->help);
+ rentry->help, 0, NULL);
metrics_store_entry_add_label(sentry,
metrics_format_label("side", "read"));
metrics_store_entry_update(sentry, rep_hist_get_n_read_limit_reached());
sentry = metrics_store_add(the_store, rentry->type, rentry->name,
- rentry->help);
+ rentry->help, 0, NULL);
metrics_store_entry_add_label(sentry,
metrics_format_label("side", "write"));
metrics_store_entry_update(sentry, rep_hist_get_n_write_limit_reached());
@@ -916,13 +916,13 @@ fill_socket_values(void)
&base_metrics[RELAY_METRICS_NUM_SOCKETS];
sentry = metrics_store_add(the_store, rentry->type, rentry->name,
- rentry->help);
+ rentry->help, 0, NULL);
metrics_store_entry_add_label(sentry,
metrics_format_label("state", "opened"));
metrics_store_entry_update(sentry, get_n_open_sockets());
sentry = metrics_store_add(the_store, rentry->type, rentry->name,
- rentry->help);
+ rentry->help, 0, NULL);
metrics_store_entry_update(sentry, get_max_sockets());
}
@@ -940,7 +940,7 @@ fill_onionskins_values(void)
char *type_label =
tor_strdup(metrics_format_label("type", handshake_type_to_str(t)));
sentry = metrics_store_add(the_store, rentry->type, rentry->name,
- rentry->help);
+ rentry->help, 0, NULL);
metrics_store_entry_add_label(sentry, type_label);
metrics_store_entry_add_label(sentry,
metrics_format_label("action", "processed"));
@@ -948,7 +948,7 @@ fill_onionskins_values(void)
rep_hist_get_circuit_n_handshake_assigned(t));
sentry = metrics_store_add(the_store, rentry->type, rentry->name,
- rentry->help);
+ rentry->help, 0, NULL);
metrics_store_entry_add_label(sentry, type_label);
metrics_store_entry_add_label(sentry,
metrics_format_label("action", "dropped"));
@@ -967,25 +967,25 @@ fill_oom_values(void)
&base_metrics[RELAY_METRICS_NUM_OOM_BYTES];
sentry = metrics_store_add(the_store, rentry->type, rentry->name,
- rentry->help);
+ rentry->help, 0, NULL);
metrics_store_entry_add_label(sentry,
metrics_format_label("subsys", "cell"));
metrics_store_entry_update(sentry, oom_stats_n_bytes_removed_cell);
sentry = metrics_store_add(the_store, rentry->type, rentry->name,
- rentry->help);
+ rentry->help, 0, NULL);
metrics_store_entry_add_label(sentry,
metrics_format_label("subsys", "dns"));
metrics_store_entry_update(sentry, oom_stats_n_bytes_removed_dns);
sentry = metrics_store_add(the_store, rentry->type, rentry->name,
- rentry->help);
+ rentry->help, 0, NULL);
metrics_store_entry_add_label(sentry,
metrics_format_label("subsys", "geoip"));
metrics_store_entry_update(sentry, oom_stats_n_bytes_removed_geoip);
sentry = metrics_store_add(the_store, rentry->type, rentry->name,
- rentry->help);
+ rentry->help, 0, NULL);
metrics_store_entry_add_label(sentry,
metrics_format_label("subsys", "hsdir"));
metrics_store_entry_update(sentry, oom_stats_n_bytes_removed_hsdir);
diff --git a/src/lib/metrics/metrics_common.c b/src/lib/metrics/metrics_common.c
index f3f7e22d88..5836a581f1 100644
--- a/src/lib/metrics/metrics_common.c
+++ b/src/lib/metrics/metrics_common.c
@@ -24,6 +24,8 @@ metrics_type_to_str(const metrics_type_t type)
return "counter";
case METRICS_TYPE_GAUGE:
return "gauge";
+ case METRICS_TYPE_HISTOGRAM:
+ return "histogram";
default:
tor_assert_unreached();
}
diff --git a/src/lib/metrics/metrics_common.h b/src/lib/metrics/metrics_common.h
index 3644ad3d50..6c8e8bdbdc 100644
--- a/src/lib/metrics/metrics_common.h
+++ b/src/lib/metrics/metrics_common.h
@@ -10,6 +10,7 @@
#define TOR_LIB_METRICS_METRICS_COMMON_H
#include "lib/cc/torint.h"
+#include "lib/container/smartlist.h"
/** Helper macro that must be used to construct the right namespaced metrics
* name. A name is a string so stringify the result. */
@@ -28,8 +29,18 @@ typedef enum {
METRICS_TYPE_COUNTER,
/* Can go up or down. */
METRICS_TYPE_GAUGE,
+ /* Cumulative counters for multiple observation buckets. */
+ METRICS_TYPE_HISTOGRAM,
} metrics_type_t;
+typedef struct metrics_histogram_bucket_t {
+ /* The value of the counter of this bucket. */
+ uint64_t value;
+ /* Technically, this should be a floating point value, but in practice, we
+ * can make do with integer buckets. */
+ int64_t bucket;
+} metrics_histogram_bucket_t;
+
/** Metric counter object (METRICS_TYPE_COUNTER). */
typedef struct metrics_counter_t {
uint64_t value;
@@ -40,6 +51,18 @@ typedef struct metrics_gauge_t {
int64_t value;
} metrics_gauge_t;
+/** Metric histogram object (METRICS_TYPE_HISTOGRAM). */
+typedef struct metrics_histogram_t {
+ /* The observation buckets. */
+ metrics_histogram_bucket_t *buckets;
+ /* The number of observation buckets. */
+ size_t bucket_count;
+ /* The sum of all observations */
+ int64_t sum;
+ /* The total number of observations */
+ uint64_t count;
+} metrics_histogram_t;
+
const char *metrics_type_to_str(const metrics_type_t type);
/* Helpers. */
diff --git a/src/lib/metrics/metrics_store.c b/src/lib/metrics/metrics_store.c
index b017e97688..db80ca029b 100644
--- a/src/lib/metrics/metrics_store.c
+++ b/src/lib/metrics/metrics_store.c
@@ -107,7 +107,9 @@ metrics_store_get_all(const metrics_store_t *store, const char *name)
* unique identifier. The help string can be omitted. */
metrics_store_entry_t *
metrics_store_add(metrics_store_t *store, metrics_type_t type,
- const char *name, const char *help)
+ const char *name, const char *help, size_t bucket_count,
+ const int64_t *buckets)
+
{
smartlist_t *entries;
metrics_store_entry_t *entry;
@@ -120,7 +122,7 @@ metrics_store_add(metrics_store_t *store, metrics_type_t type,
entries = smartlist_new();
strmap_set(store->entries, name, entries);
}
- entry = metrics_store_entry_new(type, name, help);
+ entry = metrics_store_entry_new(type, name, help, bucket_count, buckets);
smartlist_add(entries, entry);
return entry;
diff --git a/src/lib/metrics/metrics_store.h b/src/lib/metrics/metrics_store.h
index d85f484bd6..d06c87303c 100644
--- a/src/lib/metrics/metrics_store.h
+++ b/src/lib/metrics/metrics_store.h
@@ -26,8 +26,10 @@ metrics_store_t *metrics_store_new(void);
/* Modifiers. */
metrics_store_entry_t *metrics_store_add(metrics_store_t *store,
- metrics_type_t type,
- const char *name, const char *help);
+ metrics_type_t type, const char *name,
+ const char *help, size_t bucket_count,
+ const int64_t *buckets);
+
void metrics_store_reset(metrics_store_t *store);
/* Accessors. */
diff --git a/src/lib/metrics/metrics_store_entry.c b/src/lib/metrics/metrics_store_entry.c
index 971d9379bd..649fd660c3 100644
--- a/src/lib/metrics/metrics_store_entry.c
+++ b/src/lib/metrics/metrics_store_entry.c
@@ -6,6 +6,7 @@
* @brief Metrics store entry which contains the gathered data.
**/
+#include "metrics_common.h"
#define METRICS_STORE_ENTRY_PRIVATE
#include <string.h>
@@ -22,10 +23,11 @@
* Public API.
*/
-/** Return newly allocated store entry of type COUNTER. */
+/** Return newly allocated store entry of the specified type. */
metrics_store_entry_t *
metrics_store_entry_new(const metrics_type_t type, const char *name,
- const char *help)
+ const char *help, size_t bucket_count,
+ const int64_t *buckets)
{
metrics_store_entry_t *entry = tor_malloc_zero(sizeof(*entry));
@@ -38,6 +40,18 @@ metrics_store_entry_new(const metrics_type_t type, const char *name,
entry->help = tor_strdup(help);
}
+ if (type == METRICS_TYPE_HISTOGRAM && bucket_count > 0) {
+ tor_assert(buckets);
+
+ entry->u.histogram.bucket_count = bucket_count;
+ entry->u.histogram.buckets =
+ tor_malloc_zero(sizeof(metrics_histogram_bucket_t) * bucket_count);
+
+ for (size_t i = 0; i < bucket_count; ++i) {
+ entry->u.histogram.buckets[i].bucket = buckets[i];
+ }
+ }
+
return entry;
}
@@ -52,6 +66,11 @@ metrics_store_entry_free_(metrics_store_entry_t *entry)
smartlist_free(entry->labels);
tor_free(entry->name);
tor_free(entry->help);
+
+ if (entry->type == METRICS_TYPE_HISTOGRAM) {
+ tor_free(entry->u.histogram.buckets);
+ }
+
tor_free(entry);
}
@@ -61,6 +80,11 @@ metrics_store_entry_update(metrics_store_entry_t *entry, const int64_t value)
{
tor_assert(entry);
+ /* Histogram values are updated using metrics_store_hist_entry_update */
+ if (BUG(entry->type == METRICS_TYPE_HISTOGRAM)) {
+ return;
+ }
+
switch (entry->type) {
case METRICS_TYPE_COUNTER:
/* Counter can ONLY be positive. */
@@ -73,6 +97,43 @@ metrics_store_entry_update(metrics_store_entry_t *entry, const int64_t value)
/* Gauge can increment or decrement. And can be positive or negative. */
entry->u.gauge.value += value;
break;
+ case METRICS_TYPE_HISTOGRAM:
+ tor_assert_unreached();
+ }
+}
+
+/** Update a store entry with value for the specified observation obs.
+ *
+ * Note: entry **must** be a histogram. */
+void
+metrics_store_hist_entry_update(metrics_store_entry_t *entry,
+ const int64_t value, const int64_t obs)
+{
+ if (BUG(entry->type != METRICS_TYPE_HISTOGRAM)) {
+ return;
+ }
+
+ /* Counter can ONLY be positive for histograms. */
+ if (BUG(value < 0)) {
+ return;
+ }
+
+ /* If we're about to overflow or underflow the sum, reset all counters back
+ * to 0 before recording the observation. */
+ if (PREDICT_UNLIKELY(
+ (obs > 0 && entry->u.histogram.sum > INT64_MAX - obs) ||
+ (obs < 0 && entry->u.histogram.sum < INT64_MIN - obs))) {
+ metrics_store_entry_reset(entry);
+ }
+
+ entry->u.histogram.count += value;
+ entry->u.histogram.sum += obs;
+
+ for (size_t i = 0; i < entry->u.histogram.bucket_count; ++i) {
+ metrics_histogram_bucket_t *hb = &entry->u.histogram.buckets[i];
+ if (obs <= hb->bucket) {
+ hb->value += value;
+ }
}
}
@@ -81,8 +142,22 @@ void
metrics_store_entry_reset(metrics_store_entry_t *entry)
{
tor_assert(entry);
- /* Everything back to 0. */
- memset(&entry->u, 0, sizeof(entry->u));
+
+ switch (entry->type) {
+ case METRICS_TYPE_COUNTER: FALLTHROUGH;
+ case METRICS_TYPE_GAUGE:
+ /* Everything back to 0. */
+ memset(&entry->u, 0, sizeof(entry->u));
+ break;
+ case METRICS_TYPE_HISTOGRAM:
+ for (size_t i = 0; i < entry->u.histogram.bucket_count; ++i) {
+ metrics_histogram_bucket_t *hb = &entry->u.histogram.buckets[i];
+ hb->value = 0;
+ }
+ entry->u.histogram.sum = 0;
+ entry->u.histogram.count = 0;
+ break;
+ }
}
/** Return store entry value. */
@@ -91,6 +166,11 @@ metrics_store_entry_get_value(const metrics_store_entry_t *entry)
{
tor_assert(entry);
+ /* Histogram values are accessed using metrics_store_hist_entry_get_value. */
+ if (BUG(entry->type == METRICS_TYPE_HISTOGRAM)) {
+ return 0;
+ }
+
switch (entry->type) {
case METRICS_TYPE_COUNTER:
if (entry->u.counter.value > INT64_MAX) {
@@ -99,6 +179,9 @@ metrics_store_entry_get_value(const metrics_store_entry_t *entry)
return entry->u.counter.value;
case METRICS_TYPE_GAUGE:
return entry->u.gauge.value;
+ case METRICS_TYPE_HISTOGRAM:
+ tor_assert_unreached();
+ return 0;
}
// LCOV_EXCL_START
@@ -106,6 +189,35 @@ metrics_store_entry_get_value(const metrics_store_entry_t *entry)
// LCOV_EXCL_STOP
}
+/** Return store entry value for the specified bucket.
+ *
+ * Note: entry **must** be a histogram. */
+uint64_t
+metrics_store_hist_entry_get_value(const metrics_store_entry_t *entry,
+ const int64_t bucket)
+{
+ tor_assert(entry);
+
+ if (BUG(entry->type != METRICS_TYPE_HISTOGRAM)) {
+ return 0;
+ }
+
+ for (size_t i = 0; i <= entry->u.histogram.bucket_count; ++i) {
+ metrics_histogram_bucket_t hb = entry->u.histogram.buckets[i];
+ if (bucket == hb.bucket) {
+ if (hb.value > INT64_MAX) {
+ return INT64_MAX;
+ } else {
+ return hb.value;
+ }
+ }
+ }
+
+ tor_assertf_nonfatal(false, "attempted to get the value of non-existent "
+ "bucket %" PRId64, bucket);
+ return 0;
+}
+
/** Add a label into the given entry.*/
void
metrics_store_entry_add_label(metrics_store_entry_t *entry,
@@ -147,3 +259,40 @@ metrics_store_find_entry_with_label(const smartlist_t *entries,
return NULL;
}
+
+/** Return true iff the specified entry is a histogram. */
+bool
+metrics_store_entry_is_histogram(const metrics_store_entry_t *entry)
+{
+ if (entry->type == METRICS_TYPE_HISTOGRAM) {
+ return true;
+ }
+
+ return false;
+}
+
+/** Return the total number of observations for the specified histogram. */
+uint64_t
+metrics_store_hist_entry_get_count(const metrics_store_entry_t *entry)
+{
+ tor_assert(entry);
+
+ if (BUG(entry->type != METRICS_TYPE_HISTOGRAM)) {
+ return 0;
+ }
+
+ return entry->u.histogram.count;
+}
+
+/** Return the sum of all observations for the specified histogram. */
+int64_t
+metrics_store_hist_entry_get_sum(const metrics_store_entry_t *entry)
+{
+ tor_assert(entry);
+
+ if (BUG(entry->type != METRICS_TYPE_HISTOGRAM)) {
+ return 0;
+ }
+
+ return entry->u.histogram.sum;
+}
diff --git a/src/lib/metrics/metrics_store_entry.h b/src/lib/metrics/metrics_store_entry.h
index 0e09e099fe..53fa437406 100644
--- a/src/lib/metrics/metrics_store_entry.h
+++ b/src/lib/metrics/metrics_store_entry.h
@@ -38,6 +38,7 @@ struct metrics_store_entry_t {
union {
metrics_counter_t counter;
metrics_gauge_t gauge;
+ metrics_histogram_t histogram;
} u;
};
@@ -48,7 +49,9 @@ typedef struct metrics_store_entry_t metrics_store_entry_t;
/* Allocators. */
metrics_store_entry_t *metrics_store_entry_new(const metrics_type_t type,
const char *name,
- const char *help);
+ const char *help,
+ size_t bucket_count,
+ const int64_t *buckets);
void metrics_store_entry_free_(metrics_store_entry_t *entry);
#define metrics_store_entry_free(entry) \
@@ -56,10 +59,16 @@ void metrics_store_entry_free_(metrics_store_entry_t *entry);
/* Accessors. */
int64_t metrics_store_entry_get_value(const metrics_store_entry_t *entry);
+uint64_t metrics_store_hist_entry_get_value(const metrics_store_entry_t *entry,
+ const int64_t bucket);
bool metrics_store_entry_has_label(const metrics_store_entry_t *entry,
const char *label);
metrics_store_entry_t *metrics_store_find_entry_with_label(
const smartlist_t *entries, const char *label);
+bool metrics_store_entry_is_histogram(const metrics_store_entry_t *entry);
+uint64_t metrics_store_hist_entry_get_count(
+ const metrics_store_entry_t *entry);
+int64_t metrics_store_hist_entry_get_sum(const metrics_store_entry_t *entry);
/* Modifiers. */
void metrics_store_entry_add_label(metrics_store_entry_t *entry,
@@ -67,5 +76,7 @@ void metrics_store_entry_add_label(metrics_store_entry_t *entry,
void metrics_store_entry_reset(metrics_store_entry_t *entry);
void metrics_store_entry_update(metrics_store_entry_t *entry,
const int64_t value);
+void metrics_store_hist_entry_update(metrics_store_entry_t *entry,
+ const int64_t value, const int64_t obs);
#endif /* !defined(TOR_LIB_METRICS_METRICS_STORE_ENTRY_H) */
diff --git a/src/lib/metrics/prometheus.c b/src/lib/metrics/prometheus.c
index aac23ac92e..2f98f8ebb6 100644
--- a/src/lib/metrics/prometheus.c
+++ b/src/lib/metrics/prometheus.c
@@ -17,6 +17,8 @@
#include "lib/metrics/prometheus.h"
+#include <string.h>
+
/** Return a static buffer containing all the labels properly formatted
* for the output as a string.
*
@@ -33,13 +35,54 @@ format_labels(smartlist_t *labels)
}
line = smartlist_join_strings(labels, ",", 0, NULL);
- tor_snprintf(buf, sizeof(buf), "{%s}", line);
+ tor_snprintf(buf, sizeof(buf), "%s", line);
end:
tor_free(line);
return buf;
}
+/** Write the string representation of the histogram entry to the specified
+ * buffer.
+ *
+ * Note: entry **must** be a histogram.
+ */
+static void
+format_histogram(const metrics_store_entry_t *entry, buf_t *data)
+{
+ tor_assert(entry->type == METRICS_TYPE_HISTOGRAM);
+
+ const char *labels = format_labels(entry->labels);
+
+ for (size_t i = 0; i < entry->u.histogram.bucket_count; ++i) {
+ metrics_histogram_bucket_t hb = entry->u.histogram.buckets[i];
+ if (strlen(labels) > 0) {
+ buf_add_printf(data, "%s_bucket{%s,le=\"%.2f\"} %" PRIi64 "\n",
+ entry->name, labels, (double)hb.bucket, hb.value);
+ } else {
+ buf_add_printf(data, "%s_bucket{le=\"%.2f\"} %" PRIi64 "\n",
+ entry->name, (double)hb.bucket, hb.value);
+ }
+ }
+
+ if (strlen(labels) > 0) {
+ buf_add_printf(data, "%s_bucket{%s,le=\"+Inf\"} %" PRIi64 "\n",
+ entry->name, labels,
+ metrics_store_hist_entry_get_count(entry));
+ buf_add_printf(data, "%s_sum{%s} %" PRIi64 "\n", entry->name, labels,
+ metrics_store_hist_entry_get_sum(entry));
+ buf_add_printf(data, "%s_count{%s} %" PRIi64 "\n", entry->name, labels,
+ metrics_store_hist_entry_get_count(entry));
+ } else {
+ buf_add_printf(data, "%s_bucket{le=\"+Inf\"} %" PRIi64 "\n", entry->name,
+ metrics_store_hist_entry_get_count(entry));
+ buf_add_printf(data, "%s_sum %" PRIi64 "\n", entry->name,
+ metrics_store_hist_entry_get_sum(entry));
+ buf_add_printf(data, "%s_count %" PRIi64 "\n", entry->name,
+ metrics_store_hist_entry_get_count(entry));
+ }
+}
+
/** Format the given entry in to the buffer data. */
void
prometheus_format_store_entry(const metrics_store_entry_t *entry, buf_t *data,
@@ -53,7 +96,26 @@ prometheus_format_store_entry(const metrics_store_entry_t *entry, buf_t *data,
buf_add_printf(data, "# TYPE %s %s\n", entry->name,
metrics_type_to_str(entry->type));
}
- buf_add_printf(data, "%s%s %" PRIi64 "\n", entry->name,
- format_labels(entry->labels),
- metrics_store_entry_get_value(entry));
+
+ switch (entry->type) {
+ case METRICS_TYPE_COUNTER: FALLTHROUGH;
+ case METRICS_TYPE_GAUGE:
+ {
+ const char *labels = format_labels(entry->labels);
+ if (strlen(labels) > 0) {
+ buf_add_printf(data, "%s{%s} %" PRIi64 "\n", entry->name,
+ labels,
+ metrics_store_entry_get_value(entry));
+ } else {
+ buf_add_printf(data, "%s %" PRIi64 "\n", entry->name,
+ metrics_store_entry_get_value(entry));
+ }
+ break;
+ }
+ case METRICS_TYPE_HISTOGRAM:
+ format_histogram(entry, data);
+ break;
+ default:
+ tor_assert_unreached();
+ }
}
diff --git a/src/test/test_metrics.c b/src/test/test_metrics.c
index ba1a763f0c..0bf072dbfc 100644
--- a/src/test/test_metrics.c
+++ b/src/test/test_metrics.c
@@ -28,11 +28,16 @@
#include "lib/encoding/confline.h"
#include "lib/metrics/metrics_store.h"
+#include <limits.h>
+
#define TEST_METRICS_ENTRY_NAME "entryA"
#define TEST_METRICS_ENTRY_HELP "Description of entryA"
#define TEST_METRICS_ENTRY_LABEL_1 "label=\"farfadet\""
#define TEST_METRICS_ENTRY_LABEL_2 "label=\"ponki\""
+#define TEST_METRICS_HIST_ENTRY_NAME "test_hist_entry"
+#define TEST_METRICS_HIST_ENTRY_HELP "Description of test_hist_entry"
+
static void
set_metrics_port(or_options_t *options)
{
@@ -189,7 +194,8 @@ test_prometheus(void *arg)
/* Add entry and validate its content. */
entry = metrics_store_add(store, METRICS_TYPE_COUNTER,
TEST_METRICS_ENTRY_NAME,
- TEST_METRICS_ENTRY_HELP);
+ TEST_METRICS_ENTRY_HELP,
+ 0, NULL);
tt_assert(entry);
metrics_store_entry_add_label(entry, TEST_METRICS_ENTRY_LABEL_1);
@@ -208,11 +214,61 @@ test_prometheus(void *arg)
metrics_store_free(store);
}
+static void
+test_prometheus_histogram(void *arg)
+{
+ metrics_store_t *store = NULL;
+ metrics_store_entry_t *entry = NULL;
+ buf_t *buf = buf_new();
+ char *output = NULL;
+ const int64_t buckets[] = { 10, 20, 3000 };
+
+ (void) arg;
+
+ /* Fresh new store. No entries. */
+ store = metrics_store_new();
+ tt_assert(store);
+
+ /* Add a histogram entry and validate its content. */
+ entry = metrics_store_add(store, METRICS_TYPE_HISTOGRAM,
+ TEST_METRICS_HIST_ENTRY_NAME,
+ TEST_METRICS_HIST_ENTRY_HELP,
+ ARRAY_LENGTH(buckets), buckets);
+ tt_assert(entry);
+ metrics_store_entry_add_label(entry, TEST_METRICS_ENTRY_LABEL_1);
+
+ static const char *expected =
+ "# HELP " TEST_METRICS_HIST_ENTRY_NAME " "
+ TEST_METRICS_HIST_ENTRY_HELP "\n"
+ "# TYPE " TEST_METRICS_HIST_ENTRY_NAME " histogram\n"
+ TEST_METRICS_HIST_ENTRY_NAME "_bucket{"
+ TEST_METRICS_ENTRY_LABEL_1 ",le=\"10.00\"} 0\n"
+ TEST_METRICS_HIST_ENTRY_NAME "_bucket{"
+ TEST_METRICS_ENTRY_LABEL_1 ",le=\"20.00\"} 0\n"
+ TEST_METRICS_HIST_ENTRY_NAME "_bucket{"
+ TEST_METRICS_ENTRY_LABEL_1 ",le=\"3000.00\"} 0\n"
+ TEST_METRICS_HIST_ENTRY_NAME "_bucket{"
+ TEST_METRICS_ENTRY_LABEL_1 ",le=\"+Inf\"} 0\n"
+ TEST_METRICS_HIST_ENTRY_NAME "_sum{" TEST_METRICS_ENTRY_LABEL_1 "} 0\n"
+ TEST_METRICS_HIST_ENTRY_NAME "_count{" TEST_METRICS_ENTRY_LABEL_1 "} 0\n";
+
+ metrics_store_get_output(METRICS_FORMAT_PROMETHEUS, store, buf);
+ output = buf_extract(buf, NULL);
+ tt_str_op(expected, OP_EQ, output);
+
+ done:
+ buf_free(buf);
+ tor_free(output);
+ metrics_store_free(store);
+}
+
static void
test_store(void *arg)
{
metrics_store_t *store = NULL;
metrics_store_entry_t *entry = NULL;
+ const int64_t buckets[] = { 10, 20, 3000 };
+ const size_t bucket_count = ARRAY_LENGTH(buckets);
(void) arg;
@@ -224,7 +280,7 @@ test_store(void *arg)
/* Add entry and validate its content. */
entry = metrics_store_add(store, METRICS_TYPE_COUNTER,
TEST_METRICS_ENTRY_NAME,
- TEST_METRICS_ENTRY_HELP);
+ TEST_METRICS_ENTRY_HELP, 0, NULL);
tt_assert(entry);
tt_int_op(entry->type, OP_EQ, METRICS_TYPE_COUNTER);
tt_str_op(entry->name, OP_EQ, TEST_METRICS_ENTRY_NAME);
@@ -251,7 +307,7 @@ test_store(void *arg)
/* Add entry and validate its content. */
entry = metrics_store_add(store, METRICS_TYPE_COUNTER,
TEST_METRICS_ENTRY_NAME,
- TEST_METRICS_ENTRY_HELP);
+ TEST_METRICS_ENTRY_HELP, 0, NULL);
tt_assert(entry);
metrics_store_entry_add_label(entry, TEST_METRICS_ENTRY_LABEL_2);
@@ -261,6 +317,89 @@ test_store(void *arg)
tt_assert(entries);
tt_int_op(smartlist_len(entries), OP_EQ, 2);
+ /* Add a histogram entry and validate its content. */
+ entry = metrics_store_add(store, METRICS_TYPE_HISTOGRAM,
+ TEST_METRICS_HIST_ENTRY_NAME,
+ TEST_METRICS_HIST_ENTRY_HELP,
+ bucket_count, buckets);
+
+ tt_assert(entry);
+ tt_int_op(entry->type, OP_EQ, METRICS_TYPE_HISTOGRAM);
+ tt_str_op(entry->name, OP_EQ, TEST_METRICS_HIST_ENTRY_NAME);
+ tt_str_op(entry->help, OP_EQ, TEST_METRICS_HIST_ENTRY_HELP);
+ tt_uint_op(entry->u.histogram.bucket_count, OP_EQ, bucket_count);
+
+ for (size_t i = 0; i < bucket_count; ++i) {
+ tt_uint_op(entry->u.histogram.buckets[i].bucket, OP_EQ, buckets[i]);
+ tt_uint_op(entry->u.histogram.buckets[i].value, OP_EQ, 0);
+ }
+
+ /* Access the entry. */
+ tt_assert(metrics_store_get_all(store, TEST_METRICS_HIST_ENTRY_NAME));
+
+ /* Record various observations. */
+ metrics_store_hist_entry_update(entry, 3, 11);
+ tt_int_op(metrics_store_hist_entry_get_value(entry, 10), OP_EQ, 0);
+ tt_int_op(metrics_store_hist_entry_get_value(entry, 20), OP_EQ, 3);
+ tt_int_op(metrics_store_hist_entry_get_value(entry, 3000), OP_EQ, 3);
+ tt_int_op(metrics_store_hist_entry_get_value(entry, 3000), OP_EQ, 3);
+ tt_int_op(metrics_store_hist_entry_get_count(entry), OP_EQ, 3);
+ tt_int_op(metrics_store_hist_entry_get_sum(entry), OP_EQ, 11);
+
+ metrics_store_hist_entry_update(entry, 1, 42);
+ tt_int_op(metrics_store_hist_entry_get_value(entry, 10), OP_EQ, 0);
+ tt_int_op(metrics_store_hist_entry_get_value(entry, 20), OP_EQ, 3);
+ tt_int_op(metrics_store_hist_entry_get_value(entry, 3000), OP_EQ, 4);
+ tt_int_op(metrics_store_hist_entry_get_value(entry, 3000), OP_EQ, 4);
+ tt_int_op(metrics_store_hist_entry_get_count(entry), OP_EQ, 4);
+ tt_int_op(metrics_store_hist_entry_get_sum(entry), OP_EQ, 53);
+
+ /* Ensure this resets all buckets back to 0. */
+ metrics_store_entry_reset(entry);
+ for (size_t i = 0; i < bucket_count; ++i) {
+ tt_uint_op(entry->u.histogram.buckets[i].bucket, OP_EQ, buckets[i]);
+ tt_uint_op(entry->u.histogram.buckets[i].value, OP_EQ, 0);
+ }
+
+ /* tt_int_op assigns the third argument to a variable of type long, which
+ * overflows on some platforms (e.g. on some 32-bit systems). We disable
+ * these checks for those platforms. */
+#if LONG_MAX >= INT64_MAX
+ metrics_store_hist_entry_update(entry, 1, INT64_MAX - 13);
+ tt_int_op(metrics_store_hist_entry_get_sum(entry), OP_EQ, INT64_MAX - 13);
+ metrics_store_hist_entry_update(entry, 1, 13);
+ tt_int_op(metrics_store_hist_entry_get_sum(entry), OP_EQ, INT64_MAX);
+ /* Uh-oh, the sum of all observations is now greater than INT64_MAX. Make
+ * sure we reset the entry instead of overflowing the sum. */
+ metrics_store_hist_entry_update(entry, 1, 1);
+ tt_int_op(metrics_store_hist_entry_get_value(entry, 10), OP_EQ, 1);
+ tt_int_op(metrics_store_hist_entry_get_value(entry, 20), OP_EQ, 1);
+ tt_int_op(metrics_store_hist_entry_get_value(entry, 3000), OP_EQ, 1);
+ tt_int_op(metrics_store_hist_entry_get_value(entry, 3000), OP_EQ, 1);
+ tt_int_op(metrics_store_hist_entry_get_count(entry), OP_EQ, 1);
+ tt_int_op(metrics_store_hist_entry_get_sum(entry), OP_EQ, 1);
+#endif
+
+#if LONG_MIN <= INT64_MIN
+ metrics_store_entry_reset(entry);
+ /* In practice, we're not going to have negative observations (as we only use
+ * histograms for timings, which are always positive), but technically
+ * prometheus _does_ support negative observations. */
+ metrics_store_hist_entry_update(entry, 1, INT64_MIN + 13);
+ tt_int_op(metrics_store_hist_entry_get_sum(entry), OP_EQ, INT64_MIN + 13);
+ metrics_store_hist_entry_update(entry, 1, -13);
+ tt_int_op(metrics_store_hist_entry_get_sum(entry), OP_EQ, INT64_MIN);
+ /* Uh-oh, the sum of all observations is now less than INT64_MIN. Make
+ * sure we reset the entry instead of underflowing the sum. */
+ metrics_store_hist_entry_update(entry, 1, -1);
+ tt_int_op(metrics_store_hist_entry_get_value(entry, 10), OP_EQ, 1);
+ tt_int_op(metrics_store_hist_entry_get_value(entry, 20), OP_EQ, 1);
+ tt_int_op(metrics_store_hist_entry_get_value(entry, 3000), OP_EQ, 1);
+ tt_int_op(metrics_store_hist_entry_get_value(entry, 3000), OP_EQ, 1);
+ tt_int_op(metrics_store_hist_entry_get_count(entry), OP_EQ, 1);
+ tt_int_op(metrics_store_hist_entry_get_sum(entry), OP_EQ, -1);
+#endif
+
done:
metrics_store_free(store);
}
@@ -270,6 +409,7 @@ struct testcase_t metrics_tests[] = {
{ "config", test_config, TT_FORK, NULL, NULL },
{ "connection", test_connection, TT_FORK, NULL, NULL },
{ "prometheus", test_prometheus, TT_FORK, NULL, NULL },
+ { "prometheus_histogram", test_prometheus_histogram, TT_FORK, NULL, NULL },
{ "store", test_store, TT_FORK, NULL, NULL },
END_OF_TESTCASES
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the tor-commits
mailing list