Asterisk - The Open Source Telephony Project  18.5.0
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 <[email protected]>
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 res_prometheus
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  * \details
60  * While the config file should generally provide the configuration
61  * for this module, it is useful for testing purposes to allow the
62  * configuration to be injected into the module. This struct is
63  * public to allow this to occur.
64  *
65  * \note
66  * Modifying the configuration outside of testing purposes is not
67  * encouraged.
68  */
70  /*! \brief Whether or not the module is enabled */
71  unsigned int enabled;
72  /*! \brief Whether or not core metrics are enabled */
73  unsigned int core_metrics_enabled;
75  /*! \brief The HTTP URI we register ourselves to */
77  /*! \brief Auth username for Basic Auth */
79  /*! \brief Auth password for Basic Auth */
81  /*! \brief Auth realm */
83  );
84 };
85 
86 /*!
87  * \brief A function table for a metrics provider
88  *
89  * \details
90  * It's generally nice to separate out things that provide metrics
91  * from the core of this module. For those that want to be notified
92  * when things happen in the core module, they can provide an instance
93  * of this function table using \c prometheus_metrics_provider_register
94  * and be notified when module affecting changes occur.
95  */
97  /*!
98  * \brief Handy name of the provider for debugging purposes
99  */
100  const char *name;
101  /*!
102  * \brief Reload callback
103  *
104  * \param config The reloaded config
105  *
106  * \retval 0 success
107  * \retval -1 error
108  */
109  int (* const reload_cb)(struct prometheus_general_config *config);
110  /*!
111  * \brief Unload callback.
112  */
113  void (* const unload_cb)(void);
114 };
115 
116 /*!
117  * \brief Prometheus metric type
118  *
119  * \note
120  * Clearly, at some point, we should support summaries and histograms.
121  * As an initial implementation, counters / gauges give us quite a
122  * bit of functionality.
123  */
125  /*!
126  * \brief A metric whose value always goes up
127  */
129  /*
130  * \brief A metric whose value can bounce around like a jackrabbit
131  */
133 };
134 
135 /*!
136  * \brief How the metric was allocated.
137  *
138  * \note Clearly, you don't want to get this wrong.
139  */
141  /*!
142  * \brief The metric was allocated on the stack
143  */
145  /*!
146  * \brief The metric was allocated on the heap
147  */
149 };
150 
151 /*!
152  * \brief A label that further defines a metric
153  */
155  /*!
156  * \brief The name of the label
157  */
159  /*!
160  * \brief The value of the label
161  */
163 };
164 
165 /*!
166  * \brief An actual, honest to god, metric.
167  *
168  * \details
169  * A bit of effort has gone into making this structure as efficient as we
170  * possibly can. Given that a *lot* of metrics can theoretically be dumped out,
171  * and that Asterisk attempts to be a "real-time" system, we want this process
172  * to be as efficient as possible. Countering that is the ridiculous flexibility
173  * that Prometheus allows for (and, to an extent, wants) - namely the notion of
174  * families of metrics delineated by their labels.
175  *
176  * In order to balance this, metrics have arrays of labels. While this makes for
177  * a very large struct (such that loading one of these into memory is probably
178  * going to blow your cache), you will at least get the whole thing, since
179  * you're going to need those labels to figure out what you're looking like.
180  *
181  * A hierarchy of metrics occurs when all metrics have the same \c name, but
182  * different labels.
183  *
184  * We manage the hierarchy by allowing a metric to maintain their own list of
185  * related metrics. When metrics are registered (/c prometheus_metric_register),
186  * the function will automatically determine the hierarchy and place them into
187  * the appropriate lists. When you are creating metrics on the fly in a callback
188  * (\c prometheus_callback_register), you have to manage this hierarchy
189  * yourself, and only print out the first metric in a chain.
190  *
191  * Note that **EVERYTHING** in a metric is immutable once registered, save for
192  * its value. Modifying the hierarchy, labels, name, help, whatever is going to
193  * result in a "bad time", and is also expressly against Prometheus law. (Don't
194  * get your liver eaten.)
195  */
197  /*!
198  * \brief What type of metric we are
199  */
201  /*!
202  * \brief How this metric was allocated
203  */
204  enum prometheus_metric_allocation_strategy allocation_strategy;
205  /*!
206  * \brief A lock protecting the metric \c value
207  *
208  * \note The metric must be locked prior to updating its value!
209  */
211  /*!
212  * \brief Pointer to a static string defining this metric's help text.
213  */
214  const char *help;
215  /*!
216  * \brief Our metric name
217  */
219  /*!
220  * \brief The metric's labels
221  */
223  /*!
224  * \brief The current value.
225  *
226  * \details
227  * If \c get_metric_value is set, this value is ignored until the callback
228  * happens
229  */
231  /*
232  * \brief Callback function to obtain the metric value
233  * \details
234  * If updates need to happen when the metric is gathered, provide the
235  * callback function. Otherwise, leave it \c NULL.
236  */
237  void (* get_metric_value)(struct prometheus_metric *metric);
238  /*!
239  * \brief A list of children metrics
240  * \details
241  * Children metrics have the same name but different label.
242  *
243  * Registration of a metric will automatically nest the metrics; otherwise
244  * they are treated independently.
245  *
246  * The help of the first metric in a chain of related metrics is the only
247  * one that will be printed.
248  *
249  * For metrics output during a callback, the handler is responsible for
250  * managing the children. For metrics that are registered, the registration
251  * automatically nests the metrics.
252  */
255 };
256 
257 /**
258  * \brief Convenience macro for initializing a metric on the stack
259  *
260  * \param mtype The metric type. See \c prometheus_metric_type
261  * \param n Name of the metric
262  * \param h Help text for the metric
263  * \param cb Callback function. Optional; may be \c NULL
264  *
265  * \details
266  * When initializing a metric on the stack, various fields have to be provided
267  * to initialize the metric correctly. This macro can be used to simplify the
268  * process.
269  *
270  * Example Usage:
271  * \code
272  * struct prometheus_metric test_counter_one =
273  * PROMETHEUS_METRIC_STATIC_INITIALIZATION(
274  * PROMETHEUS_METRIC_COUNTER,
275  * "test_counter_one",
276  * "A test counter",
277  * NULL);
278  * struct prometheus_metric test_counter_two =
279  * PROMETHEUS_METRIC_STATIC_INITIALIZATION(
280  * PROMETHEUS_METRIC_COUNTER,
281  * "test_counter_two",
282  * "A test counter",
283  * metric_values_get_counter_value_cb);
284  * \endcode
285  *
286  */
287 #define PROMETHEUS_METRIC_STATIC_INITIALIZATION(mtype, n, h, cb) { \
288  .type = (mtype), \
289  .allocation_strategy = PROMETHEUS_METRIC_ALLOCD, \
290  .lock = AST_MUTEX_INIT_VALUE, \
291  .name = (n), \
292  .help = (h), \
293  .children = AST_LIST_HEAD_NOLOCK_INIT_VALUE, \
294  .get_metric_value = (cb), \
295 }
296 
297 /**
298  * \brief Convenience macro for setting a label / value in a metric
299  *
300  * \param metric The metric to set the label on
301  * \param label Position of the label to set
302  * \param n Name of the label
303  * \param v Value of the label
304  *
305  * \details
306  * When creating nested metrics, it's helpful to set their label after they have
307  * been declared but before they have been registered. This macro acts as a
308  * convenience function to set the labels properly on a declared metric.
309  *
310  * \note Setting labels *after* registration will lead to a "bad time"
311  *
312  * Example Usage:
313  * \code
314  * PROMETHEUS_METRIC_SET_LABEL(
315  * test_gauge_child_two, 0, "key_one", "value_two");
316  * PROMETHEUS_METRIC_SET_LABEL(
317  * test_gauge_child_two, 1, "key_two", "value_two");
318  * \endcode
319  *
320  */
321 #define PROMETHEUS_METRIC_SET_LABEL(metric, label, n, v) do { \
322  ast_assert((label) < PROMETHEUS_MAX_LABELS); \
323  ast_copy_string((metric)->labels[(label)].name, (n), sizeof((metric)->labels[(label)].name)); \
324  ast_copy_string((metric)->labels[(label)].value, (v), sizeof((metric)->labels[(label)].value)); \
325 } while (0)
326 
327 /*!
328  * \brief Destroy a metric and all its children
329  *
330  * \note If you still want the children, make sure you remove the head of the
331  * \c children list first.
332  *
333  * \param metric The metric to destroy
334  */
335 void prometheus_metric_free(struct prometheus_metric *metric);
336 
337 /*!
338  * \brief Create a malloc'd counter metric
339  *
340  * \note The metric must be registered after creation
341  *
342  * \param name The name of the metric
343  * \param help Help text for the metric
344  *
345  * \retval prometheus_metric on success
346  * \retval NULL on error
347  */
349  const char *help);
350 
351 /*!
352  * \brief Create a malloc'd gauge metric
353  *
354  * \note The metric must be registered after creation
355  *
356  * \param name The name of the metric
357  * \param help Help text for the metric
358  *
359  * \retval prometheus_metric on success
360  * \retval NULL on error
361  */
363  const char *help);
364 
365 /**
366  * \brief Convert a metric (and its children) into Prometheus compatible text
367  *
368  * \param metric The metric to convert to a string
369  * \param [out] output The \c ast_str string to populate with the metric(s)
370  */
372  struct ast_str **output);
373 
374 /*!
375  * \brief Defines a callback that will be invoked when the HTTP route is called
376  *
377  * \details
378  * This callback presents the second way of passing metrics to a Prometheus
379  * server. For metrics that are generated often or whose value needs to be
380  * stored, metrics can be created and registered. For metrics that can be
381  * obtained "on-the-fly", this mechanism is preferred. When the HTTP route is
382  * queried by promtheus, the registered callbacks are invoked. The string passed
383  * to the callback should be populated with stack-allocated metrics using
384  * \c prometheus_metric_to_string.
385  *
386  * Example Usage:
387  * \code
388  * static void prometheus_metric_callback(struct ast_str **output)
389  * {
390  * struct prometheus_metric test_counter =
391  * PROMETHEUS_METRIC_STATIC_INITIALIZATION(
392  * PROMETHEUS_METRIC_COUNTER,
393  * "test_counter",
394  * "A test counter",
395  * NULL);
396  *
397  * prometheus_metric_to_string(&test_counter, output);
398  * }
399  *
400  * static void load_module(void)
401  * {
402  * struct prometheus_callback callback = {
403  * .name = "test_callback",
404  * .callback_fn = &prometheus_metric_callback,
405  * };
406  *
407  * prometheus_callback_register(&callback);
408  * }
409  *
410  * \endcode
411  *
412  */
414  /*!
415  * \brief The name of our callback (always useful for debugging)
416  */
417  const char *name;
418  /*!
419  * \brief The callback function to invoke
420  */
421  void (* callback_fn)(struct ast_str **output);
422 };
423 
424 /*!
425  * Register a metric for collection
426  *
427  * \param metric The metric to register
428  *
429  * \retval 0 success
430  * \retval -1 error
431  */
433 
434 /*!
435  * \brief Remove a registered metric
436  *
437  * \param metric The metric to unregister
438  *
439  * \note Unregistering also destroys the metric, if found
440  *
441  * \retval 0 The metric was found, unregistered, and disposed of
442  * \retval -1 The metric was not found
443  */
445 
446 /*!
447  * The current number of registered metrics
448  *
449  * \retval The current number of registered metrics
450  */
452 
453 /*!
454  * Register a metric callback
455  *
456  * \param callback The callback to register
457  *
458  * \retval 0 success
459  * \retval -1 error
460  */
462 
463 /*!
464  * \brief Remove a registered callback
465  *
466  * \param callback The callback to unregister
467  */
469 
470 /*!
471  * \brief Register a metrics provider
472  *
473  * \param provider The provider function table to register
474  */
476 
477 /*!
478  * \brief Retrieve the current configuration of the module
479  *
480  * \note
481  * This should primarily be done for testing purposes.
482  *
483  * \details
484  * config is an AO2 ref counted object
485  *
486  * \retval NULL on error
487  * \retval config on success
488  */
490 
491 /*!
492  * \brief Set the configuration for the module
493  *
494  * \note
495  * This should primarily be done for testing purposes
496  *
497  * \details
498  * This is not a ref-stealing function. The reference count to \c config
499  * will be incremented as a result of calling this method.
500  *
501  */
503 
504 /*!
505  * \brief Allocate a new configuration object
506  *
507  * \details
508  * The returned object is an AO2 ref counted object
509  *
510  * \retval NULL on error
511  * \retval config on success
512  */
514 
515 #endif /* #ifndef RES_PROMETHEUS_H__ */
static const char type[]
Definition: chan_ooh323.c:109
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)
char * config
Definition: conf2ael.c:66
struct prometheus_metric::@313 entry
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)
#define AST_DECLARE_STRING_FIELDS(field_list)
Declare the fields needed in a structure.
Definition: stringfields.h:337
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.
int value
Definition: syslog.c:37
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.
#define AST_STRING_FIELD(name)
Declare a string field.
Definition: stringfields.h:299
const ast_string_field auth_password
Auth password for Basic Auth.
A set of macros to manage forward-linked lists.
AST_LIST_HEAD_NOLOCK(contactliststruct, contact)
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&#39;d gauge metric.
struct prometheus_metric::@312 children
A list of children metrics.
The descriptor of a dynamic string XXX storage will be optimized later if needed We use the ts field ...
Definition: strings.h:584
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
Definition: linkedlists.h:409
const char * help
Pointer to a static string defining this metric&#39;s help text.
void * prometheus_general_config_alloc(void)
Allocate a new configuration object.
static const char name[]
Definition: cdr_mysql.c:74
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.
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&#39;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.
The metric was allocated on the heap.
struct prometheus_general_config * prometheus_general_config_get(void)
Retrieve the current configuration of the module.
static struct prometheus_metrics_provider provider
Definition: bridges.c:178
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.