/* Copyright (c) 2020-2021, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** * @file metrics_store.c * @brief Metrics interface to store them based on specific store type and get * their MetricsPort output. **/ #define METRICS_STORE_ENTRY_PRIVATE #include "orconfig.h" #include "lib/container/map.h" #include "lib/log/util_bug.h" #include "lib/malloc/malloc.h" #include "lib/metrics/metrics_store.h" #include "lib/metrics/metrics_store_entry.h" /* Format Drivers. */ #include "lib/metrics/prometheus.h" /** A metric store which contains a map of entries. */ struct metrics_store_t { /** Indexed by metrics entry name. An entry is a smartlist_t of one or more * metrics_store_entry_t allowing for multiple metrics of the same name. * * The reason we allow multiple entries is because there are cases where one * metrics can be used twice by the same entity but with different labels. * One example is an onion service with multiple ports, the port specific * metrics will have a port value as a label. */ strmap_t *entries; }; /** Function pointer to the format function of a specific driver. */ typedef void (fmt_driver_fn_t)(const metrics_store_entry_t *, buf_t *, bool no_comment); /** Helper: Free a single entry in a metrics_store_t taking a void pointer * parameter. */ static void metrics_store_free_void(void *p) { smartlist_t *list = p; SMARTLIST_FOREACH(list, metrics_store_entry_t *, entry, metrics_store_entry_free(entry)); smartlist_free(list); } #include /** Put the given store output in the buffer data and use the format function * given in fmt to get it for each entry. */ static void get_output(const metrics_store_t *store, buf_t *data, fmt_driver_fn_t fmt) { tor_assert(store); tor_assert(data); tor_assert(fmt); STRMAP_FOREACH(store->entries, key, const smartlist_t *, entries) { /* Indicate that we've formatted the coment already for the entries. */ bool comment_formatted = false; SMARTLIST_FOREACH_BEGIN(entries, const metrics_store_entry_t *, entry) { fmt(entry, data, comment_formatted); comment_formatted = true; } SMARTLIST_FOREACH_END(entry); } STRMAP_FOREACH_END; } /** Return a newly allocated and initialized store of the given type. */ metrics_store_t * metrics_store_new(void) { metrics_store_t *store = tor_malloc_zero(sizeof(*store)); store->entries = strmap_new(); return store; } /** Free the given store including all its entries. */ void metrics_store_free_(metrics_store_t *store) { if (store == NULL) { return; } strmap_free(store->entries, metrics_store_free_void); tor_free(store); } /** Find all metrics entry in the given store identified by name. If not found, * NULL is returned. */ smartlist_t * metrics_store_get_all(const metrics_store_t *store, const char *name) { tor_assert(store); tor_assert(name); return strmap_get(store->entries, name); } /** Add a new metrics entry to the given store and type. The name MUST be the * 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) { smartlist_t *entries; metrics_store_entry_t *entry; tor_assert(store); tor_assert(name); entries = metrics_store_get_all(store, name); if (!entries) { entries = smartlist_new(); strmap_set(store->entries, name, entries); } entry = metrics_store_entry_new(type, name, help); smartlist_add(entries, entry); return entry; } /** Set the output of the given store of the format fmt into the given buffer * data. */ void metrics_store_get_output(const metrics_format_t fmt, const metrics_store_t *store, buf_t *data) { tor_assert(store); switch (fmt) { case METRICS_FORMAT_PROMETHEUS: get_output(store, data, prometheus_format_store_entry); break; default: // LCOV_EXCL_START tor_assert_unreached(); // LCOV_EXCL_STOP } } /** Reset a store as in free its content. */ void metrics_store_reset(metrics_store_t *store) { if (store == NULL) { return; } strmap_free(store->entries, metrics_store_free_void); store->entries = strmap_new(); }