Asterisk - The Open Source Telephony Project GIT-master-754dea3
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros Modules Pages
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="uri" default="metrics">
87 <since>
88 <version>17.0.0</version>
89 </since>
90 <synopsis>The HTTP URI to serve metrics up on.</synopsis>
91 </configOption>
92 <configOption name="auth_username">
93 <since>
94 <version>17.0.0</version>
95 </since>
96 <synopsis>Username to use for Basic Auth.</synopsis>
97 <description>
98 <para>
99 If set, use Basic Auth to authenticate requests to the route
100 specified by <replaceable>uri</replaceable>. Note that you
101 will need to configure your Prometheus server with the
102 appropriate auth credentials.
103 </para>
104 <para>
105 If set, <replaceable>auth_password</replaceable> must also
106 be set appropriately.
107 </para>
108 <warning>
109 <para>
110 It is highly recommended to set up Basic Auth. Failure
111 to do so may result in useful information about your
112 Asterisk system being made easily scrapable by the
113 wide world. Consider yourself duly warned.
114 </para>
115 </warning>
116 </description>
117 </configOption>
118 <configOption name="auth_password">
119 <since>
120 <version>17.0.0</version>
121 </since>
122 <synopsis>Password to use for Basic Auth.</synopsis>
123 <description>
124 <para>
125 If set, this is used in conjunction with <replaceable>auth_username</replaceable>
126 to require Basic Auth for all requests to the Prometheus metrics. Note that
127 setting this without <replaceable>auth_username</replaceable> will not
128 do anything.
129 </para>
130 </description>
131 </configOption>
132 <configOption name="auth_realm" default="Asterisk Prometheus Metrics">
133 <since>
134 <version>17.0.0</version>
135 </since>
136 <synopsis>Auth realm used in challenge responses</synopsis>
137 </configOption>
138 </configObject>
139 </configFile>
140 </configInfo>
141***/
142
143#define AST_MODULE_SELF_SYM __internal_res_prometheus_self
144
145#include "asterisk.h"
146
147#include "asterisk/module.h"
148#include "asterisk/vector.h"
149#include "asterisk/http.h"
151#include "asterisk/ast_version.h"
152#include "asterisk/buildinfo.h"
154
156
157/*! \brief Lock that protects data structures during an HTTP scrape */
159
161
163
165
166static struct timeval last_scrape;
167
168/*! \brief The actual module config */
169struct module_config {
170 /*! \brief General settings */
172};
173
174static struct aco_type global_option = {
175 .type = ACO_GLOBAL,
176 .name = "general",
177 .item_offset = offsetof(struct module_config, general),
178 .category_match = ACO_WHITELIST_EXACT,
179 .category = "general",
180};
181
183
185 .filename = "prometheus.conf",
186 .types = ACO_TYPES(&global_option),
187};
188
189/*! \brief The module configuration container */
191
192static void *module_config_alloc(void);
193static int prometheus_config_pre_apply(void);
194static void prometheus_config_post_apply(void);
195/*! \brief Register information about the configs being processed by this module */
197 .files = ACO_FILES(&prometheus_conf),
198 .pre_apply_config = prometheus_config_pre_apply,
199 .post_apply_config = prometheus_config_post_apply,
200);
201
202#define CORE_PROPERTIES_HELP "Asterisk instance properties. The value of this will always be 1."
203
204#define CORE_UPTIME_HELP "Asterisk instance uptime in seconds."
205
206#define CORE_LAST_RELOAD_HELP "Time since last Asterisk reload in seconds."
207
208#define CORE_METRICS_SCRAPE_TIME_HELP "Total time taken to collect metrics, in milliseconds"
209
210static void get_core_uptime_cb(struct prometheus_metric *metric)
211{
212 struct timeval now = ast_tvnow();
213 int64_t duration = ast_tvdiff_sec(now, ast_startuptime);
214
215 snprintf(metric->value, sizeof(metric->value), "%" PRIu64, duration);
216}
217
218static void get_last_reload_cb(struct prometheus_metric *metric)
219{
220 struct timeval now = ast_tvnow();
221 int64_t duration = ast_tvdiff_sec(now, ast_lastreloadtime);
222
223 snprintf(metric->value, sizeof(metric->value), "%" PRIu64, duration);
224}
225
226/*!
227 * \brief The scrape duration metric
228 *
229 * \details
230 * This metric is special in that it should never be registered.
231 * Instead, the HTTP callback function that walks the metrics will
232 * always populate this metric explicitly if core metrics
233 * are enabled.
234 */
238 "asterisk_core_scrape_time_ms",
240 NULL);
241
242#define METRIC_CORE_PROPS_ARRAY_INDEX 0
243/*!
244 * \brief Core metrics to scrape
245 */
249 "asterisk_core_properties",
251 NULL),
254 "asterisk_core_uptime_seconds",
259 "asterisk_core_last_reload_seconds",
262};
263
264/*!
265 * \internal
266 * \brief Compare two metrics to see if their name / labels / values match
267 *
268 * \param left The first metric to compare
269 * \param right The second metric to compare
270 *
271 * \retval 0 The metrics are not the same
272 * \retval 1 The metrics are the same
273 */
275 struct prometheus_metric *right)
276{
277 int i;
278 ast_debug(5, "Comparison: Names %s == %s\n", left->name, right->name);
279 if (strcmp(left->name, right->name)) {
280 return 0;
281 }
282
283 for (i = 0; i < PROMETHEUS_MAX_LABELS; i++) {
284 ast_debug(5, "Comparison: Label %d Names %s == %s\n", i,
285 left->labels[i].name, right->labels[i].name);
286 if (strcmp(left->labels[i].name, right->labels[i].name)) {
287 return 0;
288 }
289
290 ast_debug(5, "Comparison: Label %d Values %s == %s\n", i,
291 left->labels[i].value, right->labels[i].value);
292 if (strcmp(left->labels[i].value, right->labels[i].value)) {
293 return 0;
294 }
295 }
296
297 ast_debug(5, "Copmarison: %s (%p) is equal to %s (%p)\n",
298 left->name, left, right->name, right);
299 return 1;
300}
301
303{
305
306 return AST_VECTOR_SIZE(&metrics);
307}
308
310{
312 int i;
313
314 if (!metric) {
315 return -1;
316 }
317
318 for (i = 0; i < AST_VECTOR_SIZE(&metrics); i++) {
319 struct prometheus_metric *existing = AST_VECTOR_GET(&metrics, i);
320 struct prometheus_metric *child;
321
322 if (prometheus_metric_cmp(existing, metric)) {
324 "Refusing registration of existing Prometheus metric: %s\n",
325 metric->name);
326 return -1;
327 }
328
329 AST_LIST_TRAVERSE(&existing->children, child, entry) {
330 if (prometheus_metric_cmp(child, metric)) {
332 "Refusing registration of existing Prometheus metric: %s\n",
333 metric->name);
334 return -1;
335 }
336 }
337
338 if (!strcmp(metric->name, existing->name)) {
339 ast_debug(3, "Nesting metric '%s' as child (%p) under existing (%p)\n",
340 metric->name, metric, existing);
341 AST_LIST_INSERT_TAIL(&existing->children, metric, entry);
342 return 0;
343 }
344 }
345
346 ast_debug(3, "Tracking new root metric '%s'\n", metric->name);
347 if (AST_VECTOR_APPEND(&metrics, metric)) {
348 ast_log(AST_LOG_WARNING, "Failed to grow vector to make room for Prometheus metric: %s\n",
349 metric->name);
350 return -1;
351 }
352
353 return 0;
354}
355
357{
358 if (!metric) {
359 return -1;
360 }
361
362 {
364 int i;
365
366 ast_debug(3, "Removing metric '%s'\n", metric->name);
367 for (i = 0; i < AST_VECTOR_SIZE(&metrics); i++) {
368 struct prometheus_metric *existing = AST_VECTOR_GET(&metrics, i);
369
370 /*
371 * If this is a complete match, remove the matching metric
372 * and place its children back into the list
373 */
374 if (prometheus_metric_cmp(existing, metric)) {
375 struct prometheus_metric *root;
376
378 root = AST_LIST_REMOVE_HEAD(&existing->children, entry);
379 if (root) {
380 struct prometheus_metric *child;
381 AST_LIST_TRAVERSE_SAFE_BEGIN(&existing->children, child, entry) {
383 AST_LIST_INSERT_TAIL(&root->children, child, entry);
384 }
386 AST_VECTOR_INSERT_AT(&metrics, i, root);
387 }
388 prometheus_metric_free(existing);
389 return 0;
390 }
391
392 /*
393 * Name match, but labels don't match. Find the matching entry with
394 * labels and remove it along with all of its children
395 */
396 if (!strcmp(existing->name, metric->name)) {
397 struct prometheus_metric *child;
398
399 AST_LIST_TRAVERSE_SAFE_BEGIN(&existing->children, child, entry) {
400 if (prometheus_metric_cmp(child, metric)) {
403 return 0;
404 }
405 }
407 }
408 }
409 }
410
411 return -1;
412}
413
415{
416 struct prometheus_metric *child;
417
418 if (!metric) {
419 return;
420 }
421
422 while ((child = AST_LIST_REMOVE_HEAD(&metric->children, entry))) {
424 }
425 ast_mutex_destroy(&metric->lock);
426
428 return;
429 } else if (metric->allocation_strategy == PROMETHEUS_METRIC_MALLOCD) {
430 ast_free(metric);
431 }
432}
433
434/*!
435 * \internal
436 * \brief Common code for creating a metric
437 *
438 * \param name The name of the metric
439 * \param help Help string to output when rendered. This must be static.
440 *
441 * \retval NULL on failure
442 */
443static struct prometheus_metric *prometheus_metric_create(const char *name, const char *help)
444{
445 struct prometheus_metric *metric = NULL;
446
447 metric = ast_calloc(1, sizeof(*metric));
448 if (!metric) {
449 return NULL;
450 }
452 ast_mutex_init(&metric->lock);
453
454 ast_copy_string(metric->name, name, sizeof(metric->name));
455 metric->help = help;
456
457 return metric;
458}
459
460struct prometheus_metric *prometheus_gauge_create(const char *name, const char *help)
461{
462 struct prometheus_metric *metric;
463
465 if (!metric) {
466 return NULL;
467 }
469
470 return metric;
471}
472
473struct prometheus_metric *prometheus_counter_create(const char *name, const char *help)
474{
475 struct prometheus_metric *metric;
476
478 if (!metric) {
479 return NULL;
480 }
482
483 return metric;
484}
485
487{
488 switch (type) {
490 return "counter";
492 return "gauge";
493 default:
494 ast_assert(0);
495 return "unknown";
496 }
497}
498
499/*!
500 * \internal
501 * \brief Render a metric to text
502 *
503 * \param metric The metric to render
504 * \param output The string buffer to append the text to
505 */
507 struct ast_str **output)
508{
509 int i;
510 int labels_exist = 0;
511
512 ast_str_append(output, 0, "%s", metric->name);
513
514 for (i = 0; i < PROMETHEUS_MAX_LABELS; i++) {
515 if (!ast_strlen_zero(metric->labels[i].name)) {
516 labels_exist = 1;
517 if (i == 0) {
518 ast_str_append(output, 0, "%s", "{");
519 } else {
520 ast_str_append(output, 0, "%s", ",");
521 }
522 ast_str_append(output, 0, "%s=\"%s\"",
523 metric->labels[i].name,
524 metric->labels[i].value);
525 }
526 }
527
528 if (labels_exist) {
529 ast_str_append(output, 0, "%s", "}");
530 }
531
532 /*
533 * If no value exists, put in a 0. That ensures we don't anger Prometheus.
534 */
535 if (ast_strlen_zero(metric->value)) {
536 ast_str_append(output, 0, " 0\n");
537 } else {
538 ast_str_append(output, 0, " %s\n", metric->value);
539 }
540}
541
543 struct ast_str **output)
544{
545 struct prometheus_metric *child;
546
547 ast_str_append(output, 0, "# HELP %s %s\n", metric->name, metric->help);
548 ast_str_append(output, 0, "# TYPE %s %s\n", metric->name,
550 prometheus_metric_full_to_string(metric, output);
551 AST_LIST_TRAVERSE(&metric->children, child, entry) {
553 }
554}
555
557{
559
560 if (!callback || !callback->callback_fn || ast_strlen_zero(callback->name)) {
561 return -1;
562 }
563
564 AST_VECTOR_APPEND(&callbacks, callback);
565
566 return 0;
567}
568
570{
572 int i;
573
574 for (i = 0; i < AST_VECTOR_SIZE(&callbacks); i++) {
575 struct prometheus_callback *entry = AST_VECTOR_GET(&callbacks, i);
576
577 if (!strcmp(callback->name, entry->name)) {
579 return;
580 }
581 }
582}
583
584static void scrape_metrics(struct ast_str **response)
585{
586 int i;
587
588 for (i = 0; i < AST_VECTOR_SIZE(&callbacks); i++) {
589 struct prometheus_callback *callback = AST_VECTOR_GET(&callbacks, i);
590
591 if (!callback) {
592 continue;
593 }
594
595 callback->callback_fn(response);
596 }
597
598 for (i = 0; i < AST_VECTOR_SIZE(&metrics); i++) {
599 struct prometheus_metric *metric = AST_VECTOR_GET(&metrics, i);
600
601 if (!metric) {
602 continue;
603 }
604
605 ast_mutex_lock(&metric->lock);
606 if (metric->get_metric_value) {
607 metric->get_metric_value(metric);
608 }
609 prometheus_metric_to_string(metric, response);
610 ast_mutex_unlock(&metric->lock);
611 }
612}
613
615 const struct ast_http_uri *urih, const char *uri, enum ast_http_method method,
616 struct ast_variable *get_params, struct ast_variable *headers)
617{
619 struct ast_str *response = NULL;
620 struct ast_str *content_type_header = NULL;
621 struct timeval start;
622 struct timeval end;
623
624 /* If there is no module config or we're not enabled, we can't handle requests */
625 if (!mod_cfg || !mod_cfg->general->enabled) {
626 goto err503;
627 }
628
629 if (!ast_strlen_zero(mod_cfg->general->auth_username)) {
630 struct ast_http_auth *http_auth;
631
632 http_auth = ast_http_get_auth(headers);
633 if (!http_auth) {
634 goto err401;
635 }
636
637 if (strcmp(http_auth->userid, mod_cfg->general->auth_username)) {
638 ast_debug(5, "Invalid username provided for auth request: %s\n", http_auth->userid);
639 ao2_ref(http_auth, -1);
640 goto err401;
641 }
642
643 if (strcmp(http_auth->password, mod_cfg->general->auth_password)) {
644 ast_debug(5, "Invalid password provided for auth request: %s\n", http_auth->password);
645 ao2_ref(http_auth, -1);
646 goto err401;
647 }
648
649 ao2_ref(http_auth, -1);
650 }
651
652 response = ast_str_create(512);
653 content_type_header = ast_str_create(32);
654 if (!response || !content_type_header) {
655 goto err500;
656 }
657
658 ast_str_set(&content_type_header, 0, "Content-Type: text/plain\r\n");
659
660 start = ast_tvnow();
661
663
664 last_scrape = start;
665 scrape_metrics(&response);
666
667 if (mod_cfg->general->core_metrics_enabled) {
668 int64_t duration;
669
670 end = ast_tvnow();
671 duration = ast_tvdiff_ms(end, start);
672 snprintf(core_scrape_metric.value,
674 "%" PRIu64,
675 duration);
677 }
679
680 ast_http_send(ser, method, 200, "OK", content_type_header, response, 0, 0);
681
682 return 0;
683
684err401:
685 {
686 struct ast_str *auth_challenge_headers;
687
688 auth_challenge_headers = ast_str_create(128);
689 if (!auth_challenge_headers) {
690 goto err500;
691 }
692 ast_str_append(&auth_challenge_headers, 0,
693 "WWW-Authenticate: Basic realm=\"%s\"\r\n",
694 mod_cfg->general->auth_realm);
695 /* ast_http_send takes ownership of the ast_str */
696 ast_http_send(ser, method, 401, "Unauthorized", auth_challenge_headers, NULL, 0, 1);
697 }
698 ast_free(response);
699 ast_free(content_type_header);
700 return 0;
701err503:
702 ast_http_send(ser, method, 503, "Service Unavailable", NULL, NULL, 0, 1);
703 ast_free(response);
704 ast_free(content_type_header);
705 return 0;
706err500:
707 ast_http_send(ser, method, 500, "Server Error", NULL, NULL, 0, 1);
708 ast_free(response);
709 ast_free(content_type_header);
710 return 0;
711}
712
714{
715 struct ast_str *response;
716
717 response = ast_str_create(512);
718 if (!response) {
719 return NULL;
720 }
721
723 scrape_metrics(&response);
725
726 return response;
727}
728
730{
731 int64_t duration;
732
733 if (sscanf(core_scrape_metric.value, "%" PRIu64, &duration) != 1) {
734 return -1;
735 }
736
737 return duration;
738}
739
741{
743
744 return last_scrape;
745}
746
747static void prometheus_general_config_dtor(void *obj)
748{
749 struct prometheus_general_config *config = obj;
750
752}
753
755{
757
759 if (!config || ast_string_field_init(config, 32)) {
760 return NULL;
761 }
762
763 return config;
764}
765
767{
769
770 if (!mod_cfg) {
771 return NULL;
772 }
773 ao2_bump(mod_cfg->general);
774
775 return mod_cfg->general;
776}
777
779{
781
782 if (!mod_cfg) {
783 return;
784 }
785 ao2_replace(mod_cfg->general, config);
787}
788
789
790/*! \brief Configuration object destructor */
791static void module_config_dtor(void *obj)
792{
793 struct module_config *config = obj;
794
795 if (config->general) {
796 ao2_ref(config->general, -1);
797 }
798}
799
800/*! \brief Module config constructor */
801static void *module_config_alloc(void)
802{
803 struct module_config *config;
804
806 if (!config) {
807 return NULL;
808 }
809
811 if (!config->general) {
812 ao2_ref(config, -1);
813 config = NULL;
814 }
815
816 return config;
817}
818
820 .description = "Prometheus Metrics URI",
821 .callback = http_callback,
822 .has_subtree = 1,
823 .data = NULL,
824 .key = __FILE__,
825};
826
827/*!
828 * \brief Pre-apply callback for the config framework.
829 *
830 * This validates that required fields exist and are populated.
831 */
833{
834 struct module_config *config = aco_pending_config(&cfg_info);
835
836 if (!config->general->enabled) {
837 /* If we're not enabled, we don't care about anything else */
838 return 0;
839 }
840
841 if (!ast_strlen_zero(config->general->auth_username)
842 && ast_strlen_zero(config->general->auth_password)) {
843 ast_log(AST_LOG_ERROR, "'auth_username' set without a corresponding 'auth_password'\n");
844 return -1;
845 }
846
847 return 0;
848}
849
850/*!
851 * \brief Post-apply callback for the config framework.
852 *
853 * This sets any run-time information derived from the configuration
854 */
856{
858 int i;
859
860 /* We can get away with this as the lifetime of the URI
861 * registered with the HTTP core is contained within
862 * the lifetime of the module configuration
863 */
864 prometheus_uri.uri = mod_cfg->general->uri;
865
866 /* Re-register the core metrics */
867 for (i = 0; i < ARRAY_LEN(core_metrics); i++) {
869 }
870 if (mod_cfg->general->core_metrics_enabled) {
871 char eid_str[32];
872 ast_eid_to_str(eid_str, sizeof(eid_str), &ast_eid_default);
873
875
877 1, "version", ast_get_version());
879 2, "build_options", ast_get_build_opts());
881 3, "build_date", ast_build_date);
883 4, "build_os", ast_build_os);
885 5, "build_kernel", ast_build_kernel);
887 6, "build_host", ast_build_hostname);
890 "%d", 1);
891
892 for (i = 0; i < ARRAY_LEN(core_metrics); i++) {
893 PROMETHEUS_METRIC_SET_LABEL(&core_metrics[i], 0, "eid", eid_str);
895 }
896 }
897}
898
900{
902}
903
904static int unload_module(void)
905{
907 int i;
908
910
911 for (i = 0; i < AST_VECTOR_SIZE(&providers); i++) {
913
914 if (!provider->unload_cb) {
915 continue;
916 }
917
919 }
920
921 for (i = 0; i < AST_VECTOR_SIZE(&metrics); i++) {
922 struct prometheus_metric *metric = AST_VECTOR_GET(&metrics, i);
923
925 }
927
929
931
932 aco_info_destroy(&cfg_info);
934
935 return 0;
936}
937
938static int reload_module(void) {
940 int i;
941 struct prometheus_general_config *general_config;
942
944 if (aco_process_config(&cfg_info, 1) == ACO_PROCESS_ERROR) {
945 return -1;
946 }
947
948 /* Our config should be all reloaded now */
949 general_config = prometheus_general_config_get();
950 for (i = 0; i < AST_VECTOR_SIZE(&providers); i++) {
952
953 if (!provider->reload_cb) {
954 continue;
955 }
956
957 if (provider->reload_cb(general_config)) {
958 ast_log(AST_LOG_WARNING, "Failed to reload metrics provider %s\n", provider->name);
959 ao2_ref(general_config, -1);
960 return -1;
961 }
962 }
963 ao2_ref(general_config, -1);
964
966 ast_log(AST_LOG_WARNING, "Failed to re-register Prometheus Metrics URI during reload\n");
967 return -1;
968 }
969
970 return 0;
971}
972
973static int load_module(void)
974{
976
977 if (AST_VECTOR_INIT(&metrics, 64)) {
978 goto cleanup;
979 }
980
981 if (AST_VECTOR_INIT(&callbacks, 8)) {
982 goto cleanup;
983 }
984
985 if (AST_VECTOR_INIT(&providers, 8)) {
986 goto cleanup;
987 }
988
989 if (aco_info_init(&cfg_info)) {
990 goto cleanup;
991 }
993 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));
995 aco_option_register(&cfg_info, "auth_username", ACO_EXACT, global_options, "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct prometheus_general_config, auth_username));
996 aco_option_register(&cfg_info, "auth_password", ACO_EXACT, global_options, "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct prometheus_general_config, auth_password));
997 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));
998 if (aco_process_config(&cfg_info, 0) == ACO_PROCESS_ERROR) {
999 goto cleanup;
1000 }
1001
1002 if (cli_init()
1005 || bridge_metrics_init()) {
1006 goto cleanup;
1007 }
1008
1009 if(ast_module_check("res_pjsip_outbound_registration.so")) {
1010 /* Call a local function, used in the core prometheus code only */
1012 goto cleanup;
1013 }
1014
1016 goto cleanup;
1017 }
1018
1020
1021cleanup:
1023 aco_info_destroy(&cfg_info);
1027
1029}
1030
1031
1033 .support_level = AST_MODULE_SUPPORT_EXTENDED,
1034 .load = load_module,
1035 .unload = unload_module,
1037 .load_pri = AST_MODPRI_DEFAULT,
1038#ifdef HAVE_PJPROJECT
1039 /* This module explicitly calls into res_pjsip if Asterisk is built with PJSIP support, so they are required. */
1040 .requires = "res_pjsip",
1041 .optional_modules = "res_pjsip_outbound_registration",
1042#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_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:201
int bridge_metrics_init(void)
Initialize bridge metrics.
Definition: bridges.c:206
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[]
Definition: chan_ooh323.c:109
static const char config[]
Definition: chan_ooh323.c:111
int channel_metrics_init(void)
Initialize channel metrics.
Definition: channels.c:241
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
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:472
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:721
struct ast_http_auth * ast_http_get_auth(struct ast_variable *headers)
Get HTTP authentication information from headers.
Definition: http.c:1611
int ast_http_uri_link(struct ast_http_uri *urihandler)
Register a URI handler.
Definition: http.c:689
#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.
Definition: linkedlists.h:491
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
Definition: linkedlists.h:731
#define AST_LIST_TRAVERSE_SAFE_END
Closes a safe loop traversal block.
Definition: linkedlists.h:615
#define AST_LIST_TRAVERSE_SAFE_BEGIN(head, var, field)
Loops safely over (traverses) the entries in a list.
Definition: linkedlists.h:529
#define AST_LIST_REMOVE_CURRENT(field)
Removes the current entry from a list during a traversal.
Definition: linkedlists.h:557
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:833
#define ast_mutex_init(pmutex)
Definition: lock.h:190
#define ast_mutex_unlock(a)
Definition: lock.h:194
#define SCOPED_MUTEX(varname, lock)
scoped lock specialization for mutexes
Definition: lock.h:593
#define ast_mutex_destroy(a)
Definition: lock.h:192
#define ast_mutex_lock(a)
Definition: lock.h:193
#define AST_MUTEX_DEFINE_STATIC(mutex)
Definition: lock.h:524
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:343
struct timeval ast_startuptime
Definition: asterisk.c:342
static void * cleanup(void *unused)
Definition: pbx_realtime.c:124
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:1279
#define CORE_LAST_RELOAD_HELP
int prometheus_metric_unregister(struct prometheus_metric *metric)
Remove a registered metric.
struct @472 providers
struct aco_type * global_options[]
static struct timeval last_scrape
int prometheus_metric_register(struct prometheus_metric *metric)
int prometheus_metric_registered_count(void)
CONFIG_INFO_STANDARD(cfg_info, global_config, module_config_alloc,.files=ACO_FILES(&prometheus_conf),.pre_apply_config=prometheus_config_pre_apply,.post_apply_config=prometheus_config_post_apply,)
Register information about the configs being processed by this module.
struct @471 callbacks
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.
struct @470 metrics
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.
static AO2_GLOBAL_OBJ_STATIC(global_config)
The module configuration container.
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)
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.
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.
#define NULL
Definition: resample.c:96
#define ast_string_field_init(x, size)
Initialize a field pool and fields.
Definition: stringfields.h:359
#define ast_string_field_free_memory(x)
free all memory - to be called before destroying the object
Definition: stringfields.h:374
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:318
struct prometheus_general_config * general
General settings.
Defines a callback that will be invoked when the HTTP route is called.
void(* callback_fn)(struct ast_str **output)
The callback function to invoke.
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.
struct prometheus_metric::@273 entry
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.
enum prometheus_metric_allocation_strategy allocation_strategy
How this metric was allocated.
struct prometheus_metric::@272 children
A list of children metrics.
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.
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:941
#define ast_assert(a)
Definition: utils.h:739
char * ast_eid_to_str(char *s, int maxlen, struct ast_eid *eid)
Convert an EID to a string.
Definition: utils.c:2839
#define ARRAY_LEN(a)
Definition: utils.h:666
struct ast_eid ast_eid_default
Global EID.
Definition: options.c:93
Vector container support.
#define AST_VECTOR_SIZE(vec)
Get the number of elements in a vector.
Definition: vector.h:609
#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:338
#define AST_VECTOR_FREE(vec)
Deallocates this vector.
Definition: vector.h:174
#define AST_VECTOR_REMOVE(vec, idx, preserve_ordered)
Remove an element from a vector by index.
Definition: vector.h:412
#define AST_VECTOR_INIT(vec, size)
Initialize a vector.
Definition: vector.h:113
#define AST_VECTOR_APPEND(vec, elem)
Append an element to a vector, growing the vector if needed.
Definition: vector.h:256
#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:680