metrics: Add support for histograms.

This will enable us to add e.g. circuit build metrics (#40717).

Signed-off-by: Gabriela Moldovan <gabi@torproject.org>
This commit is contained in:
Gabriela Moldovan 2023-03-08 17:51:58 +00:00 committed by David Goulet
parent 3fa08dc9a7
commit d1264d11c3
12 changed files with 504 additions and 103 deletions

3
changes/ticket40757 Normal file
View File

@ -0,0 +1,3 @@
o Minor features (metrics):
- Add support for histograms.
Part of ticket 40757.

View File

@ -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));

View File

@ -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;

View File

@ -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);

View File

@ -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();
}

View File

@ -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. */

View File

@ -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;

View File

@ -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. */

View File

@ -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;
}

View File

@ -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) */

View File

@ -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();
}
}

View File

@ -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