aboutsummaryrefslogtreecommitdiff
path: root/src/lib/metrics/metrics_store.c
blob: 33b17804384478a3c8bf4d37aa6980358598f6ed (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
/* 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 <stdio.h>

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