Asterisk - The Open Source Telephony Project GIT-master-545c459
Loading...
Searching...
No Matches
res_prometheus.c
Go to the documentation of this file.
1/*
2 * Asterisk -- An open source telephony toolkit.
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/*!
20 * \file
21 * \brief Core Prometheus metrics API
22 *
23 * \author Matt Jordan <mjordan@digium.com>
24 *
25 */
26
27/*** MODULEINFO
28 <use>pjproject</use>
29 <use type="module">res_pjsip</use>
30 <use type="module">res_pjsip_outbound_registration</use>
31 <support_level>extended</support_level>
32 ***/
33
34/*** DOCUMENTATION
35 <configInfo name="res_prometheus" language="en_US">
36 <synopsis>Resource for integration with Prometheus</synopsis>
37 <configFile name="prometheus.conf">
38 <configObject name="general">
39 <since>
40 <version>17.0.0</version>
41 </since>
42 <synopsis>General settings.</synopsis>
43 <description>
44 <para>
45 The <emphasis>general</emphasis> settings section contains information
46 to configure Asterisk to serve up statistics for a Prometheus server.
47 </para>
48 <note>
49 <para>You must enable Asterisk's HTTP server in <filename>http.conf</filename>
50 for this module to function properly!
51 </para>
52 </note>
53 </description>
54 <configOption name="enabled" default="no">
55 <since>
56 <version>17.0.0</version>
57 </since>
58 <synopsis>Enable or disable Prometheus statistics.</synopsis>
59 <description>
60 <enumlist>
61 <enum name="no" />
62 <enum name="yes" />
63 </enumlist>
64 </description>
65 </configOption>
66 <configOption name="core_metrics_enabled" default="yes">
67 <since>
68 <version>17.0.0</version>
69 </since>
70 <synopsis>Enable or disable core metrics.</synopsis>
71 <description>
72 <para>
73 Core metrics show various properties of the Asterisk system, including
74 how the binary was built, the version, uptime, last reload time, etc.
75 Generally, these options are harmless and should always be enabled.
76 This option mostly exists to disable output of all options for testing
77 purposes, as well as for those foolish souls who really don't care
78 what version of Asterisk they're running.
79 </para>
80 <enumlist>
81 <enum name="no" />
82 <enum name="yes" />
83 </enumlist>
84 </description>
85 </configOption>
86 <configOption name="channels_detail_metrics_enabled" default="yes">
87 <since>
88 <version>20.22.0</version>
89 <version>22.12.0</version>
90 <version>23.6.0</version>
91 <version>24.1.0</version>
92 </since>
93 <synopsis>Enable or disable asterisk_channels_state and asterisk_channels_duration_seconds metrics.</synopsis>
94 <description>
95 <para>
96 channels_detail_metrics_enabled defaults to yes to maintain backward‑compatibility.
97 Set to no to disable the high‑cardinality channel detail metrics.
98 </para>
99 <enumlist>
100 <enum name="no" />
101 <enum name="yes" />
102 </enumlist>
103 </description>
104 </configOption>
105 <configOption name="bridges_detail_metrics_enabled" default="yes">
106 <since>
107 <version>20.22.0</version>
108 <version>22.12.0</version>
109 <version>23.6.0</version>
110 <version>24.1.0</version>
111 </since>
112 <synopsis>Enable or disable asterisk_bridges_channels_count metric.</synopsis>
113 <description>
114 <para>
115 bridges_detail_metrics_enabled defaults to yes to maintain backward‑compatibility.
116 Set to no to disable the high‑cardinality bridge detail metrics.
117 </para>
118 <enumlist>
119 <enum name="no" />
120 <enum name="yes" />
121 </enumlist>
122 </description>
123 </configOption>
124 <configOption name="uri" default="metrics">
125 <since>
126 <version>17.0.0</version>
127 </since>
128 <synopsis>The HTTP URI to serve metrics up on.</synopsis>
129 </configOption>
130 <configOption name="auth_username">
131 <since>
132 <version>17.0.0</version>
133 </since>
134 <synopsis>Username to use for Basic Auth.</synopsis>
135 <description>
136 <para>
137 If set, use Basic Auth to authenticate requests to the route
138 specified by <replaceable>uri</replaceable>. Note that you
139 will need to configure your Prometheus server with the
140 appropriate auth credentials.
141 </para>
142 <para>
143 If set, <replaceable>auth_password</replaceable> must also
144 be set appropriately.
145 </para>
146 <warning>
147 <para>
148 It is highly recommended to set up Basic Auth. Failure
149 to do so may result in useful information about your
150 Asterisk system being made easily scrapable by the
151 wide world. Consider yourself duly warned.
152 </para>
153 </warning>
154 </description>
155 </configOption>
156 <configOption name="auth_password">
157 <since>
158 <version>17.0.0</version>
159 </since>
160 <synopsis>Password to use for Basic Auth.</synopsis>
161 <description>
162 <para>
163 If set, this is used in conjunction with <replaceable>auth_username</replaceable>
164 to require Basic Auth for all requests to the Prometheus metrics. Note that
165 setting this without <replaceable>auth_username</replaceable> will not
166 do anything.
167 </para>
168 </description>
169 </configOption>
170 <configOption name="auth_realm" default="Asterisk Prometheus Metrics">
171 <since>
172 <version>17.0.0</version>
173 </since>
174 <synopsis>Auth realm used in challenge responses</synopsis>
175 </configOption>
176 </configObject>
177 </configFile>
178 </configInfo>
179***/
180
181#define AST_MODULE_SELF_SYM __internal_res_prometheus_self
182
183#include "asterisk.h"
184
185#include "asterisk/module.h"
186#include "asterisk/vector.h"
187#include "asterisk/http.h"
189#include "asterisk/ast_version.h"
190#include "asterisk/buildinfo.h"
192
194
195/*! \brief Lock that protects data structures during an HTTP scrape */
197
199
201
203
204static struct timeval last_scrape;
205
206/*! \brief The actual module config */
207struct module_config {
208 /*! \brief General settings */
210};
211
212static struct aco_type global_option = {
213 .type = ACO_GLOBAL,
214 .name = "general",
215 .item_offset = offsetof(struct module_config, general),
216 .category_match = ACO_WHITELIST_EXACT,
217 .category = "general",
218};
219
221
223 .filename = "prometheus.conf",
224 .types = ACO_TYPES(&global_option),
225};
226
227/*! \brief The module configuration container */
229
230static void *module_config_alloc(void);
231static int prometheus_config_pre_apply(void);
232static void prometheus_config_post_apply(void);
233/*! \brief Register information about the configs being processed by this module */
235 .files = ACO_FILES(&prometheus_conf),
236 .pre_apply_config = prometheus_config_pre_apply,
237 .post_apply_config = prometheus_config_post_apply,
238);
239
240#define CORE_PROPERTIES_HELP "Asterisk instance properties. The value of this will always be 1."
241
242#define CORE_UPTIME_HELP "Asterisk instance uptime in seconds."
243
244#define CORE_LAST_RELOAD_HELP "Time since last Asterisk reload in seconds."
245
246#define CORE_METRICS_SCRAPE_TIME_HELP "Total time taken to collect metrics, in milliseconds"
247
248static void get_core_uptime_cb(struct prometheus_metric *metric)
249{
250 struct timeval now = ast_tvnow();
251 int64_t duration = ast_tvdiff_sec(now, ast_startuptime);
252
253 snprintf(metric->value, sizeof(metric->value), "%" PRIu64, duration);
254}
255
256static void get_last_reload_cb(struct prometheus_metric *metric)
257{
258 struct timeval now = ast_tvnow();
259 int64_t duration = ast_tvdiff_sec(now, ast_lastreloadtime);
260
261 snprintf(metric->value, sizeof(metric->value), "%" PRIu64, duration);
262}
263
264/*!
265 * \brief The scrape duration metric
266 *
267 * \details
268 * This metric is special in that it should never be registered.
269 * Instead, the HTTP callback function that walks the metrics will
270 * always populate this metric explicitly if core metrics
271 * are enabled.
272 */
276 "asterisk_core_scrape_time_ms",
278 NULL);
279
280#define METRIC_CORE_PROPS_ARRAY_INDEX 0
281/*!
282 * \brief Core metrics to scrape
283 */
301
302/*!
303 * \internal
304 * \brief Compare two metrics to see if their name / labels / values match
305 *
306 * \param left The first metric to compare
307 * \param right The second metric to compare
308 *
309 * \retval 0 The metrics are not the same
310 * \retval 1 The metrics are the same
311 */
313 struct prometheus_metric *right)
314{
315 int i;
316 ast_debug(5, "Comparison: Names %s == %s\n", left->name, right->name);
317 if (strcmp(left->name, right->name)) {
318 return 0;
319 }
320
321 for (i = 0; i < PROMETHEUS_MAX_LABELS; i++) {
322 ast_debug(5, "Comparison: Label %d Names %s == %s\n", i,
323 left->labels[i].name, right->labels[i].name);
324 if (strcmp(left->labels[i].name, right->labels[i].name)) {
325 return 0;
326 }
327
328 ast_debug(5, "Comparison: Label %d Values %s == %s\n", i,
329 left->labels[i].value, right->labels[i].value);
330 if (strcmp(left->labels[i].value, right->labels[i].value)) {
331 return 0;
332 }
333 }
334
335 ast_debug(5, "Copmarison: %s (%p) is equal to %s (%p)\n",
336 left->name, left, right->name, right);
337 return 1;
338}
339
346
348{
350 int i;
351
352 if (!metric) {
353 return -1;
354 }
355
356 for (i = 0; i < AST_VECTOR_SIZE(&metrics); i++) {
357 struct prometheus_metric *existing = AST_VECTOR_GET(&metrics, i);
358 struct prometheus_metric *child;
359
360 if (prometheus_metric_cmp(existing, metric)) {
362 "Refusing registration of existing Prometheus metric: %s\n",
363 metric->name);
364 return -1;
365 }
366
367 AST_LIST_TRAVERSE(&existing->children, child, entry) {
368 if (prometheus_metric_cmp(child, metric)) {
370 "Refusing registration of existing Prometheus metric: %s\n",
371 metric->name);
372 return -1;
373 }
374 }
375
376 if (!strcmp(metric->name, existing->name)) {
377 ast_debug(3, "Nesting metric '%s' as child (%p) under existing (%p)\n",
378 metric->name, metric, existing);
379 AST_LIST_INSERT_TAIL(&existing->children, metric, entry);
380 return 0;
381 }
382 }
383
384 ast_debug(3, "Tracking new root metric '%s'\n", metric->name);
385 if (AST_VECTOR_APPEND(&metrics, metric)) {
386 ast_log(AST_LOG_WARNING, "Failed to grow vector to make room for Prometheus metric: %s\n",
387 metric->name);
388 return -1;
389 }
390
391 return 0;
392}
393
395{
396 if (!metric) {
397 return -1;
398 }
399
400 {
402 int i;
403
404 ast_debug(3, "Removing metric '%s'\n", metric->name);
405 for (i = 0; i < AST_VECTOR_SIZE(&metrics); i++) {
406 struct prometheus_metric *existing = AST_VECTOR_GET(&metrics, i);
407
408 /*
409 * If this is a complete match, remove the matching metric
410 * and place its children back into the list
411 */
412 if (prometheus_metric_cmp(existing, metric)) {
413 struct prometheus_metric *root;
414
416 root = AST_LIST_REMOVE_HEAD(&existing->children, entry);
417 if (root) {
418 struct prometheus_metric *child;
419 AST_LIST_TRAVERSE_SAFE_BEGIN(&existing->children, child, entry) {
421 AST_LIST_INSERT_TAIL(&root->children, child, entry);
422 }
424 AST_VECTOR_INSERT_AT(&metrics, i, root);
425 }
426 prometheus_metric_free(existing);
427 return 0;
428 }
429
430 /*
431 * Name match, but labels don't match. Find the matching entry with
432 * labels and remove it along with all of its children
433 */
434 if (!strcmp(existing->name, metric->name)) {
435 struct prometheus_metric *child;
436
437 AST_LIST_TRAVERSE_SAFE_BEGIN(&existing->children, child, entry) {
438 if (prometheus_metric_cmp(child, metric)) {
441 return 0;
442 }
443 }
445 }
446 }
447 }
448
449 return -1;
450}
451
453{
454 struct prometheus_metric *child;
455
456 if (!metric) {
457 return;
458 }
459
460 while ((child = AST_LIST_REMOVE_HEAD(&metric->children, entry))) {
462 }
463 ast_mutex_destroy(&metric->lock);
464
466 return;
467 } else if (metric->allocation_strategy == PROMETHEUS_METRIC_MALLOCD) {
468 ast_free(metric);
469 }
470}
471
472/*!
473 * \internal
474 * \brief Common code for creating a metric
475 *
476 * \param name The name of the metric
477 * \param help Help string to output when rendered. This must be static.
478 *
479 * \retval NULL on failure
480 */
481static struct prometheus_metric *prometheus_metric_create(const char *name, const char *help)
482{
483 struct prometheus_metric *metric = NULL;
484
485 metric = ast_calloc(1, sizeof(*metric));
486 if (!metric) {
487 return NULL;
488 }
490 ast_mutex_init(&metric->lock);
491
492 ast_copy_string(metric->name, name, sizeof(metric->name));
493 metric->help = help;
494
495 return metric;
496}
497
498struct prometheus_metric *prometheus_gauge_create(const char *name, const char *help)
499{
500 struct prometheus_metric *metric;
501
503 if (!metric) {
504 return NULL;
505 }
507
508 return metric;
509}
510
511struct prometheus_metric *prometheus_counter_create(const char *name, const char *help)
512{
513 struct prometheus_metric *metric;
514
516 if (!metric) {
517 return NULL;
518 }
520
521 return metric;
522}
523
525{
526 switch (type) {
528 return "counter";
530 return "gauge";
531 default:
532 ast_assert(0);
533 return "unknown";
534 }
535}
536
537/*!
538 * \internal
539 * \brief Render a metric to text
540 *
541 * \param metric The metric to render
542 * \param output The string buffer to append the text to
543 */
545 struct ast_str **output)
546{
547 int i;
548 int labels_exist = 0;
549
550 ast_str_append(output, 0, "%s", metric->name);
551
552 for (i = 0; i < PROMETHEUS_MAX_LABELS; i++) {
553 if (!ast_strlen_zero(metric->labels[i].name)) {
554 labels_exist = 1;
555 if (i == 0) {
556 ast_str_append(output, 0, "%s", "{");
557 } else {
558 ast_str_append(output, 0, "%s", ",");
559 }
560 ast_str_append(output, 0, "%s=\"%s\"",
561 metric->labels[i].name,
562 metric->labels[i].value);
563 }
564 }
565
566 if (labels_exist) {
567 ast_str_append(output, 0, "%s", "}");
568 }
569
570 /*
571 * If no value exists, put in a 0. That ensures we don't anger Prometheus.
572 */
573 if (ast_strlen_zero(metric->value)) {
574 ast_str_append(output, 0, " 0\n");
575 } else {
576 ast_str_append(output, 0, " %s\n", metric->value);
577 }
578}
579
581 struct ast_str **output)
582{
583 struct prometheus_metric *child;
584
585 ast_str_append(output, 0, "# HELP %s %s\n", metric->name, metric->help);
586 ast_str_append(output, 0, "# TYPE %s %s\n", metric->name,
588 prometheus_metric_full_to_string(metric, output);
589 AST_LIST_TRAVERSE(&metric->children, child, entry) {
591 }
592}
593
595{
597
598 if (!callback || !callback->callback_fn || ast_strlen_zero(callback->name)) {
599 return -1;
600 }
601
603
604 return 0;
605}
606
608{
610 int i;
611
612 for (i = 0; i < AST_VECTOR_SIZE(&callbacks); i++) {
613 struct prometheus_callback *entry = AST_VECTOR_GET(&callbacks, i);
614
615 if (!strcmp(callback->name, entry->name)) {
617 return;
618 }
619 }
620}
621
622static void scrape_metrics(struct ast_str **response)
623{
624 int i;
625
626 for (i = 0; i < AST_VECTOR_SIZE(&callbacks); i++) {
628
629 if (!callback) {
630 continue;
631 }
632
633 callback->callback_fn(response);
634 }
635
636 for (i = 0; i < AST_VECTOR_SIZE(&metrics); i++) {
637 struct prometheus_metric *metric = AST_VECTOR_GET(&metrics, i);
638
639 if (!metric) {
640 continue;
641 }
642
643 ast_mutex_lock(&metric->lock);
644 if (metric->get_metric_value) {
645 metric->get_metric_value(metric);
646 }
647 prometheus_metric_to_string(metric, response);
648 ast_mutex_unlock(&metric->lock);
649 }
650}
651
653 const struct ast_http_uri *urih, const char *uri, enum ast_http_method method,
654 struct ast_variable *get_params, struct ast_variable *headers)
655{
657 struct ast_str *response = NULL;
658 struct ast_str *content_type_header = NULL;
659 struct timeval start;
660 struct timeval end;
661
662 /* If there is no module config or we're not enabled, we can't handle requests */
663 if (!mod_cfg || !mod_cfg->general->enabled) {
664 goto err503;
665 }
666
667 if (!ast_strlen_zero(mod_cfg->general->auth_username)) {
668 struct ast_http_auth *http_auth;
669
670 http_auth = ast_http_get_auth(headers);
671 if (!http_auth) {
672 goto err401;
673 }
674
675 if (strcmp(http_auth->userid, mod_cfg->general->auth_username)) {
676 ast_debug(5, "Invalid username provided for auth request: %s\n", http_auth->userid);
677 ao2_ref(http_auth, -1);
678 goto err401;
679 }
680
681 if (strcmp(http_auth->password, mod_cfg->general->auth_password)) {
682 ast_debug(5, "Invalid password provided for auth request: %s\n", http_auth->password);
683 ao2_ref(http_auth, -1);
684 goto err401;
685 }
686
687 ao2_ref(http_auth, -1);
688 }
689
690 response = ast_str_create(512);
691 content_type_header = ast_str_create(32);
692 if (!response || !content_type_header) {
693 goto err500;
694 }
695
696 ast_str_set(&content_type_header, 0, "Content-Type: text/plain\r\n");
697
698 start = ast_tvnow();
699
701
702 last_scrape = start;
703 scrape_metrics(&response);
704
705 if (mod_cfg->general->core_metrics_enabled) {
706 int64_t duration;
707
708 end = ast_tvnow();
709 duration = ast_tvdiff_ms(end, start);
710 snprintf(core_scrape_metric.value,
712 "%" PRIu64,
713 duration);
715 }
717
718 ast_http_send(ser, method, 200, "OK", content_type_header, response, 0, 0);
719
720 return 0;
721
722err401:
723 {
724 struct ast_str *auth_challenge_headers;
725
726 auth_challenge_headers = ast_str_create(128);
727 if (!auth_challenge_headers) {
728 goto err500;
729 }
730 ast_str_append(&auth_challenge_headers, 0,
731 "WWW-Authenticate: Basic realm=\"%s\"\r\n",
732 mod_cfg->general->auth_realm);
733 /* ast_http_send takes ownership of the ast_str */
734 ast_http_send(ser, method, 401, "Unauthorized", auth_challenge_headers, NULL, 0, 1);
735 }
736 ast_free(response);
737 ast_free(content_type_header);
738 return 0;
739err503:
740 ast_http_send(ser, method, 503, "Service Unavailable", NULL, NULL, 0, 1);
741 ast_free(response);
742 ast_free(content_type_header);
743 return 0;
744err500:
745 ast_http_send(ser, method, 500, "Server Error", NULL, NULL, 0, 1);
746 ast_free(response);
747 ast_free(content_type_header);
748 return 0;
749}
750
752{
753 struct ast_str *response;
754
755 response = ast_str_create(512);
756 if (!response) {
757 return NULL;
758 }
759
761 scrape_metrics(&response);
763
764 return response;
765}
766
768{
769 int64_t duration;
770
771 if (sscanf(core_scrape_metric.value, "%" PRIu64, &duration) != 1) {
772 return -1;
773 }
774
775 return duration;
776}
777
779{
781
782 return last_scrape;
783}
784
785static void prometheus_general_config_dtor(void *obj)
786{
787 struct prometheus_general_config *config = obj;
788
790}
791
793{
795
797 if (!config || ast_string_field_init(config, 32)) {
798 return NULL;
799 }
800
801 return config;
802}
803
805{
807
808 if (!mod_cfg) {
809 return NULL;
810 }
811 ao2_bump(mod_cfg->general);
812
813 return mod_cfg->general;
814}
815
817{
819
820 if (!mod_cfg) {
821 return;
822 }
823 ao2_replace(mod_cfg->general, config);
825}
826
827
828/*! \brief Configuration object destructor */
829static void module_config_dtor(void *obj)
830{
831 struct module_config *config = obj;
832
833 if (config->general) {
834 ao2_ref(config->general, -1);
835 }
836}
837
838/*! \brief Module config constructor */
839static void *module_config_alloc(void)
840{
841 struct module_config *config;
842
844 if (!config) {
845 return NULL;
846 }
847
849 if (!config->general) {
850 ao2_ref(config, -1);
851 config = NULL;
852 }
853
854 return config;
855}
856
858 .description = "Prometheus Metrics URI",
859 .callback = http_callback,
860 .has_subtree = 1,
861 .data = NULL,
862 .key = __FILE__,
863};
864
865/*!
866 * \brief Pre-apply callback for the config framework.
867 *
868 * This validates that required fields exist and are populated.
869 */
871{
872 struct module_config *config = aco_pending_config(&cfg_info);
873
874 if (!config->general->enabled) {
875 /* If we're not enabled, we don't care about anything else */
876 return 0;
877 }
878
879 if (!ast_strlen_zero(config->general->auth_username)
880 && ast_strlen_zero(config->general->auth_password)) {
881 ast_log(AST_LOG_ERROR, "'auth_username' set without a corresponding 'auth_password'\n");
882 return -1;
883 }
884
885 return 0;
886}
887
888/*!
889 * \brief Post-apply callback for the config framework.
890 *
891 * This sets any run-time information derived from the configuration
892 */
894{
896 int i;
897
898 /* We can get away with this as the lifetime of the URI
899 * registered with the HTTP core is contained within
900 * the lifetime of the module configuration
901 */
902 prometheus_uri.uri = mod_cfg->general->uri;
903
904 /* Re-register the core metrics */
905 for (i = 0; i < ARRAY_LEN(core_metrics); i++) {
907 }
908 if (mod_cfg->general->core_metrics_enabled) {
909 char eid_str[32];
910 ast_eid_to_str(eid_str, sizeof(eid_str), &ast_eid_default);
911
913
915 1, "version", ast_get_version());
917 2, "build_options", ast_get_build_opts());
919 3, "build_date", ast_build_date);
921 4, "build_os", ast_build_os);
923 5, "build_kernel", ast_build_kernel);
925 6, "build_host", ast_build_hostname);
928 "%d", 1);
929
930 for (i = 0; i < ARRAY_LEN(core_metrics); i++) {
931 PROMETHEUS_METRIC_SET_LABEL(&core_metrics[i], 0, "eid", eid_str);
933 }
934 }
935}
936
941
942static int unload_module(void)
943{
945 int i;
946
948
949 for (i = 0; i < AST_VECTOR_SIZE(&providers); i++) {
951
952 if (!provider->unload_cb) {
953 continue;
954 }
955
957 }
958
959 for (i = 0; i < AST_VECTOR_SIZE(&metrics); i++) {
960 struct prometheus_metric *metric = AST_VECTOR_GET(&metrics, i);
961
963 }
965
967
969
970 aco_info_destroy(&cfg_info);
972
973 return 0;
974}
975
976static int reload_module(void) {
978 int i;
979 struct prometheus_general_config *general_config;
980
982 if (aco_process_config(&cfg_info, 1) == ACO_PROCESS_ERROR) {
983 return -1;
984 }
985
986 /* Our config should be all reloaded now */
987 general_config = prometheus_general_config_get();
988 for (i = 0; i < AST_VECTOR_SIZE(&providers); i++) {
990
991 if (!provider->reload_cb) {
992 continue;
993 }
994
995 if (provider->reload_cb(general_config)) {
996 ast_log(AST_LOG_WARNING, "Failed to reload metrics provider %s\n", provider->name);
997 ao2_ref(general_config, -1);
998 return -1;
999 }
1000 }
1001 ao2_ref(general_config, -1);
1002
1004 ast_log(AST_LOG_WARNING, "Failed to re-register Prometheus Metrics URI during reload\n");
1005 return -1;
1006 }
1007
1008 return 0;
1009}
1010
1011static int load_module(void)
1012{
1014
1015 if (AST_VECTOR_INIT(&metrics, 64)) {
1016 goto cleanup;
1017 }
1018
1019 if (AST_VECTOR_INIT(&callbacks, 8)) {
1020 goto cleanup;
1021 }
1022
1023 if (AST_VECTOR_INIT(&providers, 8)) {
1024 goto cleanup;
1025 }
1026
1027 if (aco_info_init(&cfg_info)) {
1028 goto cleanup;
1029 }
1031 aco_option_register(&cfg_info, "core_metrics_enabled", ACO_EXACT, global_options, "yes", OPT_BOOL_T, 1, FLDSET(struct prometheus_general_config, core_metrics_enabled));
1032 aco_option_register(&cfg_info, "channels_detail_metrics_enabled", ACO_EXACT, global_options, "yes", OPT_BOOL_T, 1, FLDSET(struct prometheus_general_config, channels_detail_metrics_enabled));
1033 aco_option_register(&cfg_info, "bridges_detail_metrics_enabled", ACO_EXACT, global_options, "yes", OPT_BOOL_T, 1, FLDSET(struct prometheus_general_config, bridges_detail_metrics_enabled));
1035 aco_option_register(&cfg_info, "auth_username", ACO_EXACT, global_options, "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct prometheus_general_config, auth_username));
1036 aco_option_register(&cfg_info, "auth_password", ACO_EXACT, global_options, "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct prometheus_general_config, auth_password));
1037 aco_option_register(&cfg_info, "auth_realm", ACO_EXACT, global_options, "Asterisk Prometheus Metrics", OPT_STRINGFIELD_T, 0, STRFLDSET(struct prometheus_general_config, auth_realm));
1038 if (aco_process_config(&cfg_info, 0) == ACO_PROCESS_ERROR) {
1039 goto cleanup;
1040 }
1041
1042 if (cli_init()
1045 || bridge_metrics_init()) {
1046 goto cleanup;
1047 }
1048
1049 if(ast_module_check("res_pjsip_outbound_registration.so")) {
1050 /* Call a local function, used in the core prometheus code only */
1052 goto cleanup;
1053 }
1054
1056 goto cleanup;
1057 }
1058
1060
1061cleanup:
1063 aco_info_destroy(&cfg_info);
1067
1069}
1070
1071
1073 .support_level = AST_MODULE_SUPPORT_EXTENDED,
1074 .load = load_module,
1075 .unload = unload_module,
1077 .load_pri = AST_MODPRI_DEFAULT,
1078#ifdef HAVE_PJPROJECT
1079 /* This module explicitly calls into res_pjsip if Asterisk is built with PJSIP support, so they are required. */
1080 .requires = "res_pjsip",
1081 .optional_modules = "res_pjsip_outbound_registration",
1082#endif
ast_mutex_t lock
Definition app_sla.c:337
Asterisk version information.
const char * ast_get_build_opts(void)
const char * ast_get_version(void)
Retrieve the Asterisk version string.
Asterisk main include file. File version handling, generic pbx functions.
#define ast_free(a)
Definition astmm.h:180
#define ast_calloc(num, len)
A wrapper for calloc()
Definition astmm.h:202
#define ast_log
Definition astobj2.c:42
#define AO2_GLOBAL_OBJ_STATIC(name)
Define a global object holder to be used to hold an ao2 object, statically initialized.
Definition astobj2.h:847
#define ao2_cleanup(obj)
Definition astobj2.h:1934
#define ao2_global_obj_ref(holder)
Get a reference to the object stored in the global holder.
Definition astobj2.h:918
#define ao2_replace(dst, src)
Replace one object reference with another cleaning up the original.
Definition astobj2.h:501
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition astobj2.h:459
#define ao2_global_obj_release(holder)
Release the ao2 object held in the global holder.
Definition astobj2.h:859
#define ao2_bump(obj)
Bump refcount on an AO2 object by one, returning the object.
Definition astobj2.h:480
#define ao2_alloc(data_size, destructor_fn)
Definition astobj2.h:409
static struct prometheus_metrics_provider provider
Definition bridges.c:207
int bridge_metrics_init(void)
Initialize bridge metrics.
Definition bridges.c:212
const char * ast_build_os
Definition buildinfo.c:32
const char * ast_build_hostname
Definition buildinfo.c:29
const char * ast_build_date
Definition buildinfo.c:33
const char * ast_build_kernel
Definition buildinfo.c:30
static const char type[]
static const char config[]
int channel_metrics_init(void)
Initialize channel metrics.
Definition channels.c:247
static struct ast_channel * callback(struct ast_channelstorage_instance *driver, ao2_callback_data_fn *cb_fn, void *arg, void *data, int ao2_flags, int rdlock)
Configuration option-handling.
@ ACO_EXACT
void aco_info_destroy(struct aco_info *info)
Destroy an initialized aco_info struct.
@ ACO_PROCESS_ERROR
Their was an error and no changes were applied.
#define STRFLDSET(type,...)
Convert a struct and a list of stringfield fields to an argument list of field offsets.
int aco_info_init(struct aco_info *info)
Initialize an aco_info structure.
#define FLDSET(type,...)
Convert a struct and list of fields to an argument list of field offsets.
#define aco_option_register(info, name, matchtype, types, default_val, opt_type, flags,...)
Register a config option.
#define ACO_FILES(...)
@ OPT_BOOL_T
Type for default option handler for bools (ast_true/ast_false)
@ OPT_STRINGFIELD_T
Type for default option handler for stringfields.
@ ACO_GLOBAL
@ ACO_WHITELIST_EXACT
#define CONFIG_INFO_STANDARD(name, arr, alloc,...)
Declare an aco_info struct with default module and preload values.
void * aco_pending_config(struct aco_info *info)
Get pending config changes.
enum aco_process_status aco_process_config(struct aco_info *info, int reload)
Process a config info via the options registered with an aco_info.
#define ACO_TYPES(...)
A helper macro to ensure that aco_info types always have a sentinel.
static int enabled
Definition dnsmgr.c:91
char * end
Definition eagi_proxy.c:73
static const char name[]
Definition format_mp3.c:68
Support for Private Asterisk HTTP Servers.
void ast_http_send(struct ast_tcptls_session_instance *ser, enum ast_http_method method, int status_code, const char *status_title, struct ast_str *http_header, struct ast_str *out, int fd, unsigned int static_content)
Generic function for sending HTTP/1.1 response.
Definition http.c:522
ast_http_method
HTTP Request methods known by Asterisk.
Definition http.h:58
void ast_http_uri_unlink(struct ast_http_uri *urihandler)
Unregister a URI handler.
Definition http.c:779
struct ast_http_auth * ast_http_get_auth(struct ast_variable *headers)
Get HTTP authentication information from headers.
Definition http.c:1677
int ast_http_uri_link(struct ast_http_uri *urihandler)
Register a URI handler.
Definition http.c:747
#define AST_LOG_WARNING
#define AST_LOG_ERROR
#define ast_debug(level,...)
Log a DEBUG message.
#define AST_LOG_NOTICE
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
#define AST_LIST_TRAVERSE_SAFE_END
Closes a safe loop traversal block.
#define AST_LIST_TRAVERSE_SAFE_BEGIN(head, var, field)
Loops safely over (traverses) the entries in a list.
#define AST_LIST_REMOVE_CURRENT(field)
Removes the current entry from a list during a traversal.
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
#define ast_mutex_init(pmutex)
Definition lock.h:193
#define ast_mutex_unlock(a)
Definition lock.h:197
#define SCOPED_MUTEX(varname, lock)
scoped lock specialization for mutexes
Definition lock.h:596
#define ast_mutex_destroy(a)
Definition lock.h:195
#define ast_mutex_lock(a)
Definition lock.h:196
#define AST_MUTEX_DEFINE_STATIC(mutex)
Definition lock.h:527
Asterisk module definitions.
@ AST_MODFLAG_LOAD_ORDER
Definition module.h:331
@ AST_MODFLAG_GLOBAL_SYMBOLS
Definition module.h:330
int ast_module_check(const char *name)
Check if module with the name given is loaded.
Definition loader.c:2832
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition module.h:557
@ AST_MODPRI_DEFAULT
Definition module.h:346
@ AST_MODULE_SUPPORT_EXTENDED
Definition module.h:122
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition module.h:46
@ AST_MODULE_LOAD_SUCCESS
Definition module.h:70
@ AST_MODULE_LOAD_DECLINE
Module has failed to load, may be in an inconsistent state.
Definition module.h:78
struct timeval ast_lastreloadtime
Definition asterisk.c:345
struct timeval ast_startuptime
Definition asterisk.c:344
int pjsip_outbound_registration_metrics_init(void)
Initialize PJSIP outbound registration metrics.
Prometheus Metric Internal API.
int cli_init(void)
Initialize CLI command.
int endpoint_metrics_init(void)
Initialize endpoint metrics.
static int reload(void)
const char * method
Definition res_pjsip.c:1277
#define CORE_LAST_RELOAD_HELP
int prometheus_metric_unregister(struct prometheus_metric *metric)
Remove a registered metric.
struct aco_type * global_options[]
static struct timeval last_scrape
int prometheus_metric_register(struct prometheus_metric *metric)
int prometheus_metric_registered_count(void)
void prometheus_general_config_set(struct prometheus_general_config *config)
Set the configuration for the module.
static void prometheus_metric_full_to_string(struct prometheus_metric *metric, struct ast_str **output)
static void get_core_uptime_cb(struct prometheus_metric *metric)
static int http_callback(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *get_params, struct ast_variable *headers)
static void prometheus_general_config_dtor(void *obj)
struct ast_str * prometheus_scrape_to_string(void)
Get the raw output of what a scrape would produce.
static void prometheus_config_post_apply(void)
Post-apply callback for the config framework.
static ast_mutex_t scrape_lock
Lock that protects data structures during an HTTP scrape.
static struct prometheus_metric core_metrics[]
Core metrics to scrape.
struct @509 metrics
struct timeval prometheus_last_scrape_time_get(void)
Retrieve the timestamp when the last scrape occurred.
struct prometheus_metric * prometheus_gauge_create(const char *name, const char *help)
Create a malloc'd gauge metric.
static int reload_module(void)
struct @511 providers
void prometheus_callback_unregister(struct prometheus_callback *callback)
Remove a registered callback.
static void get_last_reload_cb(struct prometheus_metric *metric)
static struct prometheus_metric core_scrape_metric
The scrape duration metric.
static int prometheus_metric_cmp(struct prometheus_metric *left, struct prometheus_metric *right)
static const char * prometheus_metric_type_to_string(enum prometheus_metric_type type)
#define CORE_UPTIME_HELP
static void module_config_dtor(void *obj)
Configuration object destructor.
struct @510 callbacks
void * prometheus_general_config_alloc(void)
Allocate a new configuration object.
int64_t prometheus_last_scrape_duration_get(void)
Retrieve the amount of time it took to perform the last scrape.
static struct ast_http_uri prometheus_uri
void prometheus_metric_free(struct prometheus_metric *metric)
Destroy a metric and all its children.
void prometheus_metric_to_string(struct prometheus_metric *metric, struct ast_str **output)
Convert a metric (and its children) into Prometheus compatible text.
static int load_module(void)
struct aco_file prometheus_conf
static int prometheus_config_pre_apply(void)
Pre-apply callback for the config framework.
void prometheus_metrics_provider_register(const struct prometheus_metrics_provider *provider)
Register a metrics provider.
static int unload_module(void)
#define CORE_PROPERTIES_HELP
static void * module_config_alloc(void)
Module config constructor.
#define CORE_METRICS_SCRAPE_TIME_HELP
int prometheus_callback_register(struct prometheus_callback *callback)
struct prometheus_general_config * prometheus_general_config_get(void)
Retrieve the current configuration of the module.
static struct aco_type global_option
static void scrape_metrics(struct ast_str **response)
#define METRIC_CORE_PROPS_ARRAY_INDEX
static struct prometheus_metric * prometheus_metric_create(const char *name, const char *help)
struct prometheus_metric * prometheus_counter_create(const char *name, const char *help)
Create a malloc'd counter metric.
Asterisk Prometheus Metrics.
#define PROMETHEUS_METRIC_SET_LABEL(metric, label, n, v)
Convenience macro for setting a label / value in a metric.
#define PROMETHEUS_METRIC_STATIC_INITIALIZATION(mtype, n, h, cb)
Convenience macro for initializing a metric on the stack.
prometheus_metric_type
Prometheus metric type.
@ PROMETHEUS_METRIC_GAUGE
A metric whose value can bounce around like a jackrabbit.
@ PROMETHEUS_METRIC_COUNTER
A metric whose value always goes up.
#define PROMETHEUS_MAX_LABELS
How many labels a single metric can have.
@ PROMETHEUS_METRIC_ALLOCD
The metric was allocated on the stack.
@ PROMETHEUS_METRIC_MALLOCD
The metric was allocated on the heap.
static void cleanup(void)
Clean up any old apps that we don't need any more.
Definition res_stasis.c:327
#define NULL
Definition resample.c:96
#define ast_string_field_init(x, size)
Initialize a field pool and fields.
#define ast_string_field_free_memory(x)
free all memory - to be called before destroying the object
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
Definition strings.h:1139
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition strings.h:65
#define ast_str_create(init_len)
Create a malloc'ed dynamic length string.
Definition strings.h:659
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
Definition strings.h:1113
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition strings.h:425
The representation of a single configuration file to be processed.
const char * filename
Type information about a category-level configurable object.
enum aco_type_t type
HTTP authentication information.
Definition http.h:125
char * password
Definition http.h:129
char * userid
Definition http.h:127
Definition of a URI handler.
Definition http.h:102
const char * description
Definition http.h:104
const char * uri
Definition http.h:105
Support for dynamic strings.
Definition strings.h:623
describes a server instance
Definition tcptls.h:151
Structure for variables, used for configurations and for channel variables.
The configuration settings for this module.
Definition cdr.c:326
struct prometheus_general_config * general
General settings.
Defines a callback that will be invoked when the HTTP route is called.
const char * name
The name of our callback (always useful for debugging)
Prometheus general configuration.
char name[PROMETHEUS_MAX_NAME_LENGTH]
The name of the label.
char value[PROMETHEUS_MAX_LABEL_LENGTH]
The value of the label.
An actual, honest to god, metric.
char name[PROMETHEUS_MAX_NAME_LENGTH]
Our metric name.
void(* get_metric_value)(struct prometheus_metric *metric)
Callback function to obtain the metric value.
struct prometheus_label labels[PROMETHEUS_MAX_LABELS]
The metric's labels.
struct prometheus_metric::@288 entry
enum prometheus_metric_allocation_strategy allocation_strategy
How this metric was allocated.
const char * help
Pointer to a static string defining this metric's help text.
ast_mutex_t lock
A lock protecting the metric value.
char value[PROMETHEUS_MAX_VALUE_LENGTH]
The current value.
enum prometheus_metric_type type
What type of metric we are.
struct prometheus_metric::@287 children
A list of children metrics.
A function table for a metrics provider.
void(*const unload_cb)(void)
Unload callback.
int(*const reload_cb)(struct prometheus_general_config *config)
Reload callback.
const char * name
Handy name of the provider for debugging purposes.
int value
Definition syslog.c:37
int64_t ast_tvdiff_sec(struct timeval end, struct timeval start)
Computes the difference (in seconds) between two struct timeval instances.
Definition time.h:73
int64_t ast_tvdiff_ms(struct timeval end, struct timeval start)
Computes the difference (in milliseconds) between two struct timeval instances.
Definition time.h:107
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition time.h:159
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition utils.h:981
#define ast_assert(a)
Definition utils.h:779
char * ast_eid_to_str(char *s, int maxlen, struct ast_eid *eid)
Convert an EID to a string.
Definition utils.c:2873
#define ARRAY_LEN(a)
Definition utils.h:706
struct ast_eid ast_eid_default
Global EID.
Definition options.c:94
Vector container support.
#define AST_VECTOR_SIZE(vec)
Get the number of elements in a vector.
Definition vector.h:637
#define AST_VECTOR_INSERT_AT(vec, idx, elem)
Insert an element at a specific position in a vector, growing the vector if needed.
Definition vector.h:349
#define AST_VECTOR_FREE(vec)
Deallocates this vector.
Definition vector.h:185
#define AST_VECTOR_REMOVE(vec, idx, preserve_ordered)
Remove an element from a vector by index.
Definition vector.h:440
#define AST_VECTOR_INIT(vec, size)
Initialize a vector.
Definition vector.h:124
#define AST_VECTOR_APPEND(vec, elem)
Append an element to a vector, growing the vector if needed.
Definition vector.h:267
#define AST_VECTOR(name, type)
Define a vector structure.
Definition vector.h:44
#define AST_VECTOR_GET(vec, idx)
Get an element from a vector.
Definition vector.h:708