metrics_store.c (4405B)
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 metrics_store_free_void(void *p) 44 { 45 smartlist_t *list = p; 46 SMARTLIST_FOREACH(list, metrics_store_entry_t *, entry, 47 metrics_store_entry_free(entry)); 48 smartlist_free(list); 49 } 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 get_output(const metrics_store_t *store, buf_t *data, fmt_driver_fn_t fmt) 57 { 58 tor_assert(store); 59 tor_assert(data); 60 tor_assert(fmt); 61 62 STRMAP_FOREACH(store->entries, key, const smartlist_t *, entries) { 63 /* Indicate that we've formatted the comment already for the entries. */ 64 bool comment_formatted = false; 65 SMARTLIST_FOREACH_BEGIN(entries, const metrics_store_entry_t *, entry) { 66 fmt(entry, data, comment_formatted); 67 comment_formatted = true; 68 } SMARTLIST_FOREACH_END(entry); 69 } STRMAP_FOREACH_END; 70 } 71 72 /** Return a newly allocated and initialized store of the given type. */ 73 metrics_store_t * 74 metrics_store_new(void) 75 { 76 metrics_store_t *store = tor_malloc_zero(sizeof(*store)); 77 78 store->entries = strmap_new(); 79 80 return store; 81 } 82 83 /** Free the given store including all its entries. */ 84 void 85 metrics_store_free_(metrics_store_t *store) 86 { 87 if (store == NULL) { 88 return; 89 } 90 91 strmap_free(store->entries, metrics_store_free_void); 92 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 metrics_store_get_all(const metrics_store_t *store, const char *name) 99 { 100 tor_assert(store); 101 tor_assert(name); 102 103 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 metrics_store_add(metrics_store_t *store, metrics_type_t type, 110 const char *name, const char *help, size_t bucket_count, 111 const int64_t *buckets) 112 113 { 114 smartlist_t *entries; 115 metrics_store_entry_t *entry; 116 117 tor_assert(store); 118 tor_assert(name); 119 120 entries = metrics_store_get_all(store, name); 121 if (!entries) { 122 entries = smartlist_new(); 123 strmap_set(store->entries, name, entries); 124 } 125 entry = metrics_store_entry_new(type, name, help, bucket_count, buckets); 126 smartlist_add(entries, entry); 127 128 return entry; 129 } 130 131 /** Set the output of the given store of the format fmt into the given buffer 132 * data. */ 133 void 134 metrics_store_get_output(const metrics_format_t fmt, 135 const metrics_store_t *store, buf_t *data) 136 { 137 tor_assert(store); 138 139 switch (fmt) { 140 case METRICS_FORMAT_PROMETHEUS: 141 get_output(store, data, prometheus_format_store_entry); 142 break; 143 default: 144 // LCOV_EXCL_START 145 tor_assert_unreached(); 146 // LCOV_EXCL_STOP 147 } 148 } 149 150 /** Reset a store as in free its content. */ 151 void 152 metrics_store_reset(metrics_store_t *store) 153 { 154 if (store == NULL) { 155 return; 156 } 157 strmap_free(store->entries, metrics_store_free_void); 158 store->entries = strmap_new(); 159 }