Line data Source code
1 : /* Copyright (c) 2020-2021, The Tor Project, Inc. */ 2 : /* See LICENSE for licensing information */ 3 : 4 : /** 5 : * @file metrics_store.c 6 : * @brief Metrics interface to store them based on specific store type and get 7 : * their MetricsPort output. 8 : **/ 9 : 10 : #define METRICS_STORE_ENTRY_PRIVATE 11 : 12 : #include "orconfig.h" 13 : 14 : #include "lib/container/map.h" 15 : #include "lib/log/util_bug.h" 16 : #include "lib/malloc/malloc.h" 17 : 18 : #include "lib/metrics/metrics_store.h" 19 : #include "lib/metrics/metrics_store_entry.h" 20 : 21 : /* Format Drivers. */ 22 : #include "lib/metrics/prometheus.h" 23 : 24 : /** A metric store which contains a map of entries. */ 25 : struct metrics_store_t { 26 : /** Indexed by metrics entry name. An entry is a smartlist_t of one or more 27 : * metrics_store_entry_t allowing for multiple metrics of the same name. 28 : * 29 : * The reason we allow multiple entries is because there are cases where one 30 : * metrics can be used twice by the same entity but with different labels. 31 : * One example is an onion service with multiple ports, the port specific 32 : * metrics will have a port value as a label. */ 33 : strmap_t *entries; 34 : }; 35 : 36 : /** Function pointer to the format function of a specific driver. */ 37 : typedef void (fmt_driver_fn_t)(const metrics_store_entry_t *, buf_t *, 38 : bool no_comment); 39 : 40 : /** Helper: Free a single entry in a metrics_store_t taking a void pointer 41 : * parameter. */ 42 : static void 43 248 : metrics_store_free_void(void *p) 44 : { 45 248 : smartlist_t *list = p; 46 497 : SMARTLIST_FOREACH(list, metrics_store_entry_t *, entry, 47 : metrics_store_entry_free(entry)); 48 248 : smartlist_free(list); 49 248 : } 50 : 51 : #include <stdio.h> 52 : 53 : /** Put the given store output in the buffer data and use the format function 54 : * given in fmt to get it for each entry. */ 55 : static void 56 2 : get_output(const metrics_store_t *store, buf_t *data, fmt_driver_fn_t fmt) 57 : { 58 2 : tor_assert(store); 59 2 : tor_assert(data); 60 2 : tor_assert(fmt); 61 : 62 10 : STRMAP_FOREACH(store->entries, key, const smartlist_t *, entries) { 63 : /* Indicate that we've formatted the coment already for the entries. */ 64 8 : bool comment_formatted = false; 65 63 : SMARTLIST_FOREACH_BEGIN(entries, const metrics_store_entry_t *, entry) { 66 55 : fmt(entry, data, comment_formatted); 67 55 : comment_formatted = true; 68 55 : } SMARTLIST_FOREACH_END(entry); 69 2 : } STRMAP_FOREACH_END; 70 2 : } 71 : 72 : /** Return a newly allocated and initialized store of the given type. */ 73 : metrics_store_t * 74 288 : metrics_store_new(void) 75 : { 76 288 : metrics_store_t *store = tor_malloc_zero(sizeof(*store)); 77 : 78 288 : store->entries = strmap_new(); 79 : 80 288 : return store; 81 : } 82 : 83 : /** Free the given store including all its entries. */ 84 : void 85 315 : metrics_store_free_(metrics_store_t *store) 86 : { 87 315 : if (store == NULL) { 88 : return; 89 : } 90 : 91 278 : strmap_free(store->entries, metrics_store_free_void); 92 278 : tor_free(store); 93 : } 94 : 95 : /** Find all metrics entry in the given store identified by name. If not found, 96 : * NULL is returned. */ 97 : smartlist_t * 98 317 : metrics_store_get_all(const metrics_store_t *store, const char *name) 99 : { 100 317 : tor_assert(store); 101 317 : tor_assert(name); 102 : 103 317 : return strmap_get(store->entries, name); 104 : } 105 : 106 : /** Add a new metrics entry to the given store and type. The name MUST be the 107 : * unique identifier. The help string can be omitted. */ 108 : metrics_store_entry_t * 109 309 : metrics_store_add(metrics_store_t *store, metrics_type_t type, 110 : const char *name, const char *help) 111 : { 112 309 : smartlist_t *entries; 113 309 : metrics_store_entry_t *entry; 114 : 115 309 : tor_assert(store); 116 309 : tor_assert(name); 117 : 118 309 : entries = metrics_store_get_all(store, name); 119 309 : if (!entries) { 120 261 : entries = smartlist_new(); 121 261 : strmap_set(store->entries, name, entries); 122 : } 123 309 : entry = metrics_store_entry_new(type, name, help); 124 309 : smartlist_add(entries, entry); 125 : 126 309 : return entry; 127 : } 128 : 129 : /** Set the output of the given store of the format fmt into the given buffer 130 : * data. */ 131 : void 132 2 : metrics_store_get_output(const metrics_format_t fmt, 133 : const metrics_store_t *store, buf_t *data) 134 : { 135 2 : tor_assert(store); 136 : 137 2 : switch (fmt) { 138 2 : case METRICS_FORMAT_PROMETHEUS: 139 2 : get_output(store, data, prometheus_format_store_entry); 140 2 : break; 141 0 : default: 142 : // LCOV_EXCL_START 143 : tor_assert_unreached(); 144 : // LCOV_EXCL_STOP 145 : } 146 2 : } 147 : 148 : /** Reset a store as in free its content. */ 149 : void 150 1 : metrics_store_reset(metrics_store_t *store) 151 : { 152 1 : if (store == NULL) { 153 : return; 154 : } 155 1 : strmap_free(store->entries, metrics_store_free_void); 156 1 : store->entries = strmap_new(); 157 : }