Asterisk - The Open Source Telephony Project  21.4.1
res_prometheus.h
Go to the documentation of this file.
1 /*
2  * res_prometheus: Asterisk Prometheus Metrics
3  *
4  * Copyright (C) 2019 Sangoma, Inc.
5  *
6  * Matt Jordan <mjordan@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18 
19 #ifndef RES_PROMETHEUS_H__
20 #define RES_PROMETHEUS_H__
21 
22 /*!
23  * \file
24  *
25  * \brief Asterisk Prometheus Metrics
26  *
27  * This module provides the base APIs and functionality for exposing a
28  * metrics route in Asterisk's HTTP server suitable for consumption by
29  * a Prometheus server. It does not provide any metrics itself.
30  */
31 
32 #include "asterisk/lock.h"
33 #include "asterisk/linkedlists.h"
34 #include "asterisk/stringfields.h"
35 
36 /*!
37  * \brief How many labels a single metric can have
38  */
39 #define PROMETHEUS_MAX_LABELS 8
40 
41 /*!
42  * \brief How long a label name can be
43  */
44 #define PROMETHEUS_MAX_NAME_LENGTH 64
45 
46 /*!
47  * \brief How long a label value can be
48  */
49 #define PROMETHEUS_MAX_LABEL_LENGTH 128
50 
51 /*!
52  * \brief How large of a value we can store
53  */
54 #define PROMETHEUS_MAX_VALUE_LENGTH 32
55 
56 /*!
57  * \brief Prometheus general configuration
58  *
59  * While the config file should generally provide the configuration
60  * for this module, it is useful for testing purposes to allow the
61  * configuration to be injected into the module. This struct is
62  * public to allow this to occur.
63  *
64  * \note
65  * Modifying the configuration outside of testing purposes is not
66  * encouraged.
67  */
69  /*! \brief Whether or not the module is enabled */
70  unsigned int enabled;
71  /*! \brief Whether or not core metrics are enabled */
72  unsigned int core_metrics_enabled;
74  /*! \brief The HTTP URI we register ourselves to */
76  /*! \brief Auth username for Basic Auth */
78  /*! \brief Auth password for Basic Auth */
80  /*! \brief Auth realm */
82  );
83 };
84 
85 /*!
86  * \brief A function table for a metrics provider
87  *
88  * It's generally nice to separate out things that provide metrics
89  * from the core of this module. For those that want to be notified
90  * when things happen in the core module, they can provide an instance
91  * of this function table using \c prometheus_metrics_provider_register
92  * and be notified when module affecting changes occur.
93  */
95  /*!
96  * \brief Handy name of the provider for debugging purposes
97  */
98  const char *name;
99  /*!
100  * \brief Reload callback
101  *
102  * \param config The reloaded config
103  *
104  * \retval 0 success
105  * \retval -1 error
106  */
107  int (* const reload_cb)(struct prometheus_general_config *config);
108  /*!
109  * \brief Unload callback.
110  */
111  void (* const unload_cb)(void);
112 };
113 
114 /*!
115  * \brief Prometheus metric type
116  *
117  * \note
118  * Clearly, at some point, we should support summaries and histograms.
119  * As an initial implementation, counters / gauges give us quite a
120  * bit of functionality.
121  */
123  /*!
124  * \brief A metric whose value always goes up
125  */
127  /*!
128  * \brief A metric whose value can bounce around like a jackrabbit
129  */
131 };
132 
133 /*!
134  * \brief How the metric was allocated.
135  *
136  * \note Clearly, you don't want to get this wrong.
137  */
139  /*!
140  * \brief The metric was allocated on the stack
141  */
143  /*!
144  * \brief The metric was allocated on the heap
145  */
147 };
148 
149 /*!
150  * \brief A label that further defines a metric
151  */
153  /*!
154  * \brief The name of the label
155  */
157  /*!
158  * \brief The value of the label
159  */
161 };
162 
163 /*!
164  * \brief An actual, honest to god, metric.
165  *
166  * A bit of effort has gone into making this structure as efficient as we
167  * possibly can. Given that a *lot* of metrics can theoretically be dumped out,
168  * and that Asterisk attempts to be a "real-time" system, we want this process
169  * to be as efficient as possible. Countering that is the ridiculous flexibility
170  * that Prometheus allows for (and, to an extent, wants) - namely the notion of
171  * families of metrics delineated by their labels.
172  *
173  * In order to balance this, metrics have arrays of labels. While this makes for
174  * a very large struct (such that loading one of these into memory is probably
175  * going to blow your cache), you will at least get the whole thing, since
176  * you're going to need those labels to figure out what you're looking like.
177  *
178  * A hierarchy of metrics occurs when all metrics have the same \c name, but
179  * different labels.
180  *
181  * We manage the hierarchy by allowing a metric to maintain their own list of
182  * related metrics. When metrics are registered (/c prometheus_metric_register),
183  * the function will automatically determine the hierarchy and place them into
184  * the appropriate lists. When you are creating metrics on the fly in a callback
185  * (\c prometheus_callback_register), you have to manage this hierarchy
186  * yourself, and only print out the first metric in a chain.
187  *
188  * Note that **EVERYTHING** in a metric is immutable once registered, save for
189  * its value. Modifying the hierarchy, labels, name, help, whatever is going to
190  * result in a "bad time", and is also expressly against Prometheus law. (Don't
191  * get your liver eaten.)
192  */
194  /*!
195  * \brief What type of metric we are
196  */
198  /*!
199  * \brief How this metric was allocated
200  */
202  /*!
203  * \brief A lock protecting the metric \c value
204  *
205  * \note The metric must be locked prior to updating its value!
206  */
208  /*!
209  * \brief Pointer to a static string defining this metric's help text.
210  */
211  const char *help;
212  /*!
213  * \brief Our metric name
214  */
216  /*!
217  * \brief The metric's labels
218  */
220  /*!
221  * \brief The current value.
222  *
223  * If \c get_metric_value is set, this value is ignored until the callback
224  * happens
225  */
227  /*!
228  * \brief Callback function to obtain the metric value
229  *
230  * If updates need to happen when the metric is gathered, provide the
231  * callback function. Otherwise, leave it \c NULL.
232  */
233  void (* get_metric_value)(struct prometheus_metric *metric);
234  /*!
235  * \brief A list of children metrics
236  *
237  * Children metrics have the same name but different label.
238  *
239  * Registration of a metric will automatically nest the metrics; otherwise
240  * they are treated independently.
241  *
242  * The help of the first metric in a chain of related metrics is the only
243  * one that will be printed.
244  *
245  * For metrics output during a callback, the handler is responsible for
246  * managing the children. For metrics that are registered, the registration
247  * automatically nests the metrics.
248  */
251 };
252 
253 /*!
254  * \brief Convenience macro for initializing a metric on the stack
255  *
256  * When initializing a metric on the stack, various fields have to be provided
257  * to initialize the metric correctly. This macro can be used to simplify the
258  * process.
259  *
260  * Example Usage:
261  * \code
262  * struct prometheus_metric test_counter_one =
263  * PROMETHEUS_METRIC_STATIC_INITIALIZATION(
264  * PROMETHEUS_METRIC_COUNTER,
265  * "test_counter_one",
266  * "A test counter",
267  * NULL);
268  * struct prometheus_metric test_counter_two =
269  * PROMETHEUS_METRIC_STATIC_INITIALIZATION(
270  * PROMETHEUS_METRIC_COUNTER,
271  * "test_counter_two",
272  * "A test counter",
273  * metric_values_get_counter_value_cb);
274  * \endcode
275  *
276  * \param mtype The metric type. See \c prometheus_metric_type
277  * \param n Name of the metric
278  * \param h Help text for the metric
279  * \param cb Callback function. Optional; may be \c NULL
280  */
281 #define PROMETHEUS_METRIC_STATIC_INITIALIZATION(mtype, n, h, cb) { \
282  .type = (mtype), \
283  .allocation_strategy = PROMETHEUS_METRIC_ALLOCD, \
284  .lock = AST_MUTEX_INIT_VALUE, \
285  .name = (n), \
286  .help = (h), \
287  .children = AST_LIST_HEAD_NOLOCK_INIT_VALUE, \
288  .get_metric_value = (cb), \
289 }
290 
291 /*!
292  * \brief Convenience macro for setting a label / value in a metric
293  *
294  * When creating nested metrics, it's helpful to set their label after they have
295  * been declared but before they have been registered. This macro acts as a
296  * convenience function to set the labels properly on a declared metric.
297  *
298  * \note Setting labels *after* registration will lead to a "bad time"
299  *
300  * Example Usage:
301  * \code
302  * PROMETHEUS_METRIC_SET_LABEL(
303  * test_gauge_child_two, 0, "key_one", "value_two");
304  * PROMETHEUS_METRIC_SET_LABEL(
305  * test_gauge_child_two, 1, "key_two", "value_two");
306  * \endcode
307  *
308  * \param metric The metric to set the label on
309  * \param label Position of the label to set
310  * \param n Name of the label
311  * \param v Value of the label
312  */
313 #define PROMETHEUS_METRIC_SET_LABEL(metric, label, n, v) do { \
314  ast_assert((label) < PROMETHEUS_MAX_LABELS); \
315  ast_copy_string((metric)->labels[(label)].name, (n), sizeof((metric)->labels[(label)].name)); \
316  ast_copy_string((metric)->labels[(label)].value, (v), sizeof((metric)->labels[(label)].value)); \
317 } while (0)
318 
319 /*!
320  * \brief Destroy a metric and all its children
321  *
322  * \note If you still want the children, make sure you remove the head of the
323  * \c children list first.
324  *
325  * \param metric The metric to destroy
326  */
327 void prometheus_metric_free(struct prometheus_metric *metric);
328 
329 /*!
330  * \brief Create a malloc'd counter metric
331  *
332  * \note The metric must be registered after creation
333  *
334  * \param name The name of the metric
335  * \param help Help text for the metric
336  *
337  * \retval prometheus_metric on success
338  * \retval NULL on error
339  */
340 struct prometheus_metric *prometheus_counter_create(const char *name,
341  const char *help);
342 
343 /*!
344  * \brief Create a malloc'd gauge metric
345  *
346  * \note The metric must be registered after creation
347  *
348  * \param name The name of the metric
349  * \param help Help text for the metric
350  *
351  * \retval prometheus_metric on success
352  * \retval NULL on error
353  */
354 struct prometheus_metric *prometheus_gauge_create(const char *name,
355  const char *help);
356 
357 /*!
358  * \brief Convert a metric (and its children) into Prometheus compatible text
359  *
360  * \param metric The metric to convert to a string
361  * \param[out] output The \c ast_str string to populate with the metric(s)
362  */
364  struct ast_str **output);
365 
366 /*!
367  * \brief Defines a callback that will be invoked when the HTTP route is called
368  *
369  * This callback presents the second way of passing metrics to a Prometheus
370  * server. For metrics that are generated often or whose value needs to be
371  * stored, metrics can be created and registered. For metrics that can be
372  * obtained "on-the-fly", this mechanism is preferred. When the HTTP route is
373  * queried by prometheus, the registered callbacks are invoked. The string passed
374  * to the callback should be populated with stack-allocated metrics using
375  * \c prometheus_metric_to_string.
376  *
377  * Example Usage:
378  * \code
379  * static void prometheus_metric_callback(struct ast_str **output)
380  * {
381  * struct prometheus_metric test_counter =
382  * PROMETHEUS_METRIC_STATIC_INITIALIZATION(
383  * PROMETHEUS_METRIC_COUNTER,
384  * "test_counter",
385  * "A test counter",
386  * NULL);
387  *
388  * prometheus_metric_to_string(&test_counter, output);
389  * }
390  *
391  * static void load_module(void)
392  * {
393  * struct prometheus_callback callback = {
394  * .name = "test_callback",
395  * .callback_fn = &prometheus_metric_callback,
396  * };
397  *
398  * prometheus_callback_register(&callback);
399  * }
400  *
401  * \endcode
402  *
403  */
405  /*!
406  * \brief The name of our callback (always useful for debugging)
407  */
408  const char *name;
409  /*!
410  * \brief The callback function to invoke
411  */
412  void (* callback_fn)(struct ast_str **output);
413 };
414 
415 /*!
416  * Register a metric for collection
417  *
418  * \param metric The metric to register
419  *
420  * \retval 0 success
421  * \retval -1 error
422  */
424 
425 /*!
426  * \brief Remove a registered metric
427  *
428  * \param metric The metric to unregister
429  *
430  * \note Unregistering also destroys the metric, if found
431  *
432  * \retval 0 The metric was found, unregistered, and disposed of
433  * \retval -1 The metric was not found
434  */
436 
437 /*!
438  * The current number of registered metrics
439  *
440  * \retval The current number of registered metrics
441  */
443 
444 /*!
445  * Register a metric callback
446  *
447  * \param callback The callback to register
448  *
449  * \retval 0 success
450  * \retval -1 error
451  */
453 
454 /*!
455  * \brief Remove a registered callback
456  *
457  * \param callback The callback to unregister
458  */
460 
461 /*!
462  * \brief Register a metrics provider
463  *
464  * \param provider The provider function table to register
465  */
467 
468 /*!
469  * \brief Retrieve the current configuration of the module
470  *
471  * config is an AO2 ref counted object
472  *
473  * \note
474  * This should primarily be done for testing purposes.
475  *
476  * \retval NULL on error
477  * \retval config on success
478  */
480 
481 /*!
482  * \brief Set the configuration for the module
483  *
484  * This is not a ref-stealing function. The reference count to \c config
485  * will be incremented as a result of calling this method.
486  *
487  * \note
488  * This should primarily be done for testing purposes
489  */
491 
492 /*!
493  * \brief Allocate a new configuration object
494  *
495  * The returned object is an AO2 ref counted object
496  *
497  * \retval NULL on error
498  * \retval config on success
499  */
501 
502 #endif /* #ifndef RES_PROMETHEUS_H__ */
unsigned int core_metrics_enabled
Whether or not core metrics are enabled.
Asterisk locking-related definitions:
An actual, honest to god, metric.
#define PROMETHEUS_MAX_VALUE_LENGTH
How large of a value we can store.
int prometheus_metric_register(struct prometheus_metric *metric)
void(*const unload_cb)(void)
Unload callback.
Prometheus general configuration.
int prometheus_callback_register(struct prometheus_callback *callback)
#define PROMETHEUS_MAX_NAME_LENGTH
How long a label name can be.
prometheus_metric_allocation_strategy
How the metric was allocated.
A metric whose value always goes up.
const char * name
The name of our callback (always useful for debugging)
enum prometheus_metric_allocation_strategy allocation_strategy
How this metric was allocated.
enum prometheus_metric_type type
What type of metric we are.
#define AST_DECLARE_STRING_FIELDS(field_list)
Declare the fields needed in a structure.
Definition: stringfields.h:341
void(* get_metric_value)(struct prometheus_metric *metric)
Callback function to obtain the metric value.
int prometheus_metric_unregister(struct prometheus_metric *metric)
Remove a registered metric.
void prometheus_metric_free(struct prometheus_metric *metric)
Destroy a metric and all its children.
void prometheus_general_config_set(struct prometheus_general_config *config)
Set the configuration for the module.
void prometheus_metric_to_string(struct prometheus_metric *metric, struct ast_str **output)
Convert a metric (and its children) into Prometheus compatible text.
The metric was allocated on the stack.
#define PROMETHEUS_MAX_LABEL_LENGTH
How long a label value can be.
#define PROMETHEUS_MAX_LABELS
How many labels a single metric can have.
struct prometheus_metric::@274 children
A list of children metrics.
#define AST_STRING_FIELD(name)
Declare a string field.
Definition: stringfields.h:303
const ast_string_field auth_password
Auth password for Basic Auth.
char name[PROMETHEUS_MAX_NAME_LENGTH]
The name of the label.
A set of macros to manage forward-linked lists.
void prometheus_metrics_provider_register(const struct prometheus_metrics_provider *provider)
Register a metrics provider.
struct prometheus_metric * prometheus_gauge_create(const char *name, const char *help)
Create a malloc'd gauge metric.
#define AST_LIST_HEAD_NOLOCK(name, type)
Defines a structure to be used to hold a list of specified type (with no lock).
Definition: linkedlists.h:225
Support for dynamic strings.
Definition: strings.h:623
struct prometheus_label labels[PROMETHEUS_MAX_LABELS]
The metric's labels.
int(*const reload_cb)(struct prometheus_general_config *config)
Reload callback.
char value[PROMETHEUS_MAX_VALUE_LENGTH]
The current value.
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
Definition: linkedlists.h:410
const char * help
Pointer to a static string defining this metric's help text.
void * prometheus_general_config_alloc(void)
Allocate a new configuration object.
char value[PROMETHEUS_MAX_LABEL_LENGTH]
The value of the label.
void prometheus_callback_unregister(struct prometheus_callback *callback)
Remove a registered callback.
const char * name
Handy name of the provider for debugging purposes.
const ast_string_field auth_realm
Auth realm.
char name[PROMETHEUS_MAX_NAME_LENGTH]
Our metric name.
const ast_string_field uri
The HTTP URI we register ourselves to.
struct prometheus_metric * prometheus_counter_create(const char *name, const char *help)
Create a malloc'd counter metric.
A function table for a metrics provider.
int prometheus_metric_registered_count(void)
ast_mutex_t lock
A lock protecting the metric value.
A label that further defines a metric.
prometheus_metric_type
Prometheus metric type.
A metric whose value can bounce around like a jackrabbit.
Definition: search.h:40
The metric was allocated on the heap.
struct prometheus_general_config * prometheus_general_config_get(void)
Retrieve the current configuration of the module.
Defines a callback that will be invoked when the HTTP route is called.
Structure for mutex and tracking information.
Definition: lock.h:135
const ast_string_field auth_username
Auth username for Basic Auth.
unsigned int enabled
Whether or not the module is enabled.