Skip to content

Commit 0bfffd5

Browse files
committed
prometheus: add prometheus_declare_stat and prometheus_push_stat funcs
1 parent 452dc41 commit 0bfffd5

File tree

2 files changed

+234
-3
lines changed

2 files changed

+234
-3
lines changed

modules/prometheus/doc/prometheus_admin.xml

Lines changed: 102 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,7 @@ route[my_custom_prometheus_route] {
283283
# the values field is an array itself, of name/value objects
284284
# used for individual stats publishing
285285
return (1, '[{
286-
"header": "# TYPE opensips_cps gauge",
286+
"header": "# TYPE opensips_total_cps gauge",
287287
"values": [
288288
{
289289
"name": "opensips_total_cps",
@@ -309,9 +309,109 @@ route[my_custom_prometheus_route] {
309309

310310
<section id="exported_functions" xreflabel="exported_functions">
311311
<title>Exported Functions</title>
312+
<section id="func_prometheus_declare_stat" xreflabel="prometheus_declare_stat">
313+
<title>
314+
<function moreinfo="none">prometheus_declare_stat(name, [type], [help])</function>
315+
</title>
312316
<para>
313-
No function exported to be used from configuration file.
317+
<emphasis>NOTE:</emphasis> this function can only be used in the
318+
route declared in the <xref linkend="param_script_route"/> parameter.
314319
</para>
320+
<para>
321+
Declares a custom statistic exported to Prometheus server. It specifies
322+
its type and optionally a help string.
323+
</para>
324+
<para>Parameters</para>
325+
<itemizedlist>
326+
<listitem>
327+
<para><emphasis>name</emphasis> (string) - the name of the statistic
328+
</para>
329+
<para><emphasis>type</emphasis> (string, optional) - the type of the
330+
statistic (i.e. <emphasis>counter</emphasis> or <emphasis>gauge</emphasis>).
331+
If missing the statistic is declared as <emphasis>gauge</emphasis>.
332+
</para>
333+
<para><emphasis>help</emphasis> (string, optional) - an optional value
334+
used to describe the statistic meaning. If missing, it is not used.
335+
</para>
336+
</listitem>
337+
</itemizedlist>
338+
<para>
339+
This function can only be used in the request
340+
route declared in the <xref linkend="param_script_route"/> parameter.
341+
</para>
342+
<example>
343+
<title><function moreinfo="none">prometheus_declare_stat</function> usage</title>
344+
...
345+
modparam("prometheus", "script_route", "my_custom_prometheus_route")
346+
...
347+
route[my_custom_prometheus_route] {
348+
...
349+
prometheus_declare_stat("opensips_cps");
350+
prometheus_push_stat(3);
351+
...
352+
}
353+
<programlisting format="linespecific">
354+
</programlisting>
355+
</example>
356+
</section>
357+
<section id="func_prometheus_push_stat" xreflabel="prometheus_push_stat">
358+
<title>
359+
<function moreinfo="none">prometheus_push_stat(value, [label_name], [label_value])</function>
360+
</title>
361+
<para>
362+
<emphasis>NOTE:</emphasis> this function can only be used in the
363+
route declared in the <xref linkend="param_script_route"/> parameter.
364+
</para>
365+
<para>
366+
Pushes a custom statistic value and optionally a set of labels
367+
to the Prometheus server.
368+
</para>
369+
<para>
370+
<emphasis>NOTE:</emphasis> a statistic's value should only be pushed
371+
after it had been declared using the
372+
<xref linkend="func_prometheus_declare_stat"/> function.
373+
</para>
374+
<para>Parameters</para>
375+
<itemizedlist>
376+
<listitem>
377+
<para><emphasis>value</emphasis> (integer) - the value of the statistic
378+
</para>
379+
<para><emphasis>label_name</emphasis> (string, optional) - used to define
380+
labels for the pushed statistic. If the <emphasis>label_value</emphasis>
381+
parameter is missing, this parameter is appended to the name of the
382+
statisic - this means that it should contain the whole set of labels
383+
for the value (including curly brackets). If the
384+
<emphasis>label_value</emphasis> is provided as well, then the parameter
385+
should only contain one label's name.
386+
</para>
387+
<para><emphasis>label_value</emphasis> (string, optional) - the value that
388+
should be used for the <emphasis>label_name</emphasis> parameter label.
389+
</para>
390+
</listitem>
391+
</itemizedlist>
392+
<para>
393+
This function can only be used in the request
394+
route declared in the <xref linkend="param_script_route"/> parameter.
395+
</para>
396+
<example>
397+
<title><function moreinfo="none">prometheus_push_stat</function> usage</title>
398+
...
399+
modparam("prometheus", "script_route", "my_custom_prometheus_route")
400+
...
401+
route[my_custom_prometheus_route] {
402+
...
403+
prometheus_declare_stat("opensips_cps");
404+
prometheus_push_stat(3); # no label is being used
405+
prometheus_declare_stat("opensips_cc");
406+
# the next two are equivalent
407+
prometheus_push_stat(10, "{gateway=\"gw1\"}"); # no label is being used
408+
prometheus_push_stat(10, "gateway", "gw1"); # same as the above
409+
...
410+
}
411+
<programlisting format="linespecific">
412+
</programlisting>
413+
</example>
414+
</section>
315415
</section>
316416

317417
<section>

modules/prometheus/prometheus.c

Lines changed: 132 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,14 @@ str prom_grp_label = str_init("group");
5757
httpd_api_t prom_httpd_api;
5858
str prometheus_script_route = {NULL, 0};
5959
struct script_route_ref *prometheus_route_ref = NULL;
60+
static str *prometheus_route_page = NULL;
61+
static str prometheus_route_page_stat = {0, 0};
62+
static int prometheus_route_page_max = 0;
6063

6164
static int prom_stats_param( modparam_t type, void* val);
6265
static int prom_labels_param( modparam_t type, void* val);
66+
static int w_prom_declare_stat(struct sip_msg *msg, str *name, str *type, str *help);
67+
static int w_prom_push_stat(struct sip_msg *msg, int *label, str *labeln, str *labelv);
6368

6469
/* module parameters */
6570
static const param_export_t mi_params[] = {
@@ -85,6 +90,22 @@ static const dep_export_t deps = {
8590
},
8691
};
8792

93+
static const cmd_export_t cmds[]={
94+
{"prometheus_declare_stat", (cmd_function)w_prom_declare_stat, {
95+
{CMD_PARAM_STR, 0, 0},
96+
{CMD_PARAM_STR|CMD_PARAM_OPT, 0, 0},
97+
{CMD_PARAM_STR|CMD_PARAM_OPT, 0, 0},
98+
{0,0,0}},
99+
REQUEST_ROUTE},
100+
{"prometheus_push_stat", (cmd_function)w_prom_push_stat, {
101+
{CMD_PARAM_INT, 0, 0},
102+
{CMD_PARAM_STR|CMD_PARAM_OPT, 0, 0},
103+
{CMD_PARAM_STR|CMD_PARAM_OPT, 0, 0},
104+
{0,0,0}},
105+
REQUEST_ROUTE},
106+
{0,0,{{0,0,0}},0}
107+
};
108+
88109
/* module exports */
89110
struct module_exports exports = {
90111
"prometheus", /* module name */
@@ -93,7 +114,7 @@ struct module_exports exports = {
93114
DEFAULT_DLFLAGS, /* dlopen flags */
94115
0, /* load function */
95116
&deps, /* OpenSIPS module dependencies */
96-
NULL, /* exported functions */
117+
cmds, /* exported functions */
97118
NULL, /* exported async functions */
98119
mi_params, /* exported parameters */
99120
NULL, /* exported statistics */
@@ -850,9 +871,17 @@ int prom_answer_to_connection (void *cls, void *connection,
850871
/* set request route type */
851872
set_route_type( REQUEST_ROUTE );
852873

874+
prometheus_route_page = page;
875+
prometheus_route_page_max = buffer->len;
876+
prometheus_route_page_stat.s = NULL;
877+
853878
/* run given hep route */
854879
run_top_route( sroutes->request[prometheus_route_ref->idx], route_msg);
855880

881+
prometheus_route_page = NULL;
882+
prometheus_route_page_max = 0;
883+
prometheus_route_page_stat.s = NULL;
884+
856885
memset(&val, 0, sizeof(int_str));
857886
if ((script_return_get(&val, 0) >= 1) && (val.flags & PV_VAL_STR)) {
858887
if (process_extra_prometheus(val.rs.s,val.rs.len,page,buffer->len) < 0)
@@ -976,3 +1005,105 @@ static int prom_labels_param(modparam_t type, void* val)
9761005

9771006
return 0;
9781007
}
1008+
1009+
static int w_prom_declare_stat(struct sip_msg *msg, str *name, str *type, str *help)
1010+
{
1011+
int len = 0;
1012+
str _type = str_init("gauge");
1013+
1014+
if (!prometheus_route_ref || !prometheus_route_page) {
1015+
LM_ERR("this function should only be called inside '%s' route\n",
1016+
(!prometheus_route_ref?"script_route":prometheus_script_route.s));
1017+
return -2;
1018+
}
1019+
if (!type)
1020+
type = &_type;
1021+
if (help && help->len)
1022+
len = 7 /* '# HELP ' */ + name->len + 1 /* ' ' */ + help->len + 1 /* '\n' */;
1023+
len += 7 /* '# TYPE ' */ + name->len + 1 /* ' ' */ + type->len + 1 /* '\n' */;
1024+
if (prometheus_route_page->len + len >= prometheus_route_page_max) {
1025+
LM_ERR("declaring statistic overflows\n");
1026+
return -1;
1027+
}
1028+
if (help && help->len) {
1029+
memcpy(prometheus_route_page->s + prometheus_route_page->len, "# HELP ", 7);
1030+
prometheus_route_page->len += 7;
1031+
memcpy(prometheus_route_page->s + prometheus_route_page->len, name->s, name->len);
1032+
prometheus_route_page->len += name->len;
1033+
prometheus_route_page->s[prometheus_route_page->len++] = ' ';
1034+
memcpy(prometheus_route_page->s + prometheus_route_page->len, help->s, help->len);
1035+
prometheus_route_page->len += help->len;
1036+
prometheus_route_page->s[prometheus_route_page->len++] = '\n';
1037+
}
1038+
memcpy(prometheus_route_page->s + prometheus_route_page->len, "# TYPE ", 7);
1039+
prometheus_route_page->len += 7;
1040+
memcpy(prometheus_route_page->s + prometheus_route_page->len, name->s, name->len);
1041+
prometheus_route_page_stat.s = prometheus_route_page->s + prometheus_route_page->len;
1042+
prometheus_route_page_stat.len = name->len;
1043+
prometheus_route_page->len += name->len;
1044+
prometheus_route_page->s[prometheus_route_page->len++] = ' ';
1045+
memcpy(prometheus_route_page->s + prometheus_route_page->len, type->s, type->len);
1046+
prometheus_route_page->len += type->len;
1047+
prometheus_route_page->s[prometheus_route_page->len++] = '\n';
1048+
return 1;
1049+
}
1050+
1051+
static int w_prom_push_stat(struct sip_msg *msg, int *value, str *labeln, str *labelv)
1052+
{
1053+
int len, ret = -1;
1054+
str labels = str_init("");
1055+
str val;
1056+
1057+
if (!prometheus_route_page_stat.s) {
1058+
LM_ERR("can not push stat if not previously declared! "
1059+
"use prometheus_declare_stat() first\n");
1060+
return -2;
1061+
}
1062+
if (labeln) {
1063+
if (labelv) {
1064+
/* we have both labels and value - build the string */
1065+
labels.s = pkg_malloc(5 /* '{="}' */ + labeln->len + labelv->len);
1066+
if (!labels.s) {
1067+
LM_ERR("oom for building labels\n");
1068+
return -1;
1069+
}
1070+
labels.s[0] = '{';
1071+
labels.len = 1;
1072+
memcpy(labels.s + labels.len, labeln->s, labeln->len);
1073+
labels.len += labeln->len;
1074+
labels.s[labels.len++] = '=';
1075+
labels.s[labels.len++] = '"';
1076+
memcpy(labels.s + labels.len, labelv->s, labelv->len);
1077+
labels.len += labelv->len;
1078+
labels.s[labels.len++] = '"';
1079+
labels.s[labels.len++] = '}';
1080+
} else {
1081+
labels = *labeln;
1082+
}
1083+
}
1084+
len = prometheus_route_page_stat.len + labels.len + 1 /* ' ' */ +
1085+
INT2STR_MAX_LEN + 1 /* \n' */;
1086+
if (prometheus_route_page->len + len >= prometheus_route_page_max) {
1087+
LM_ERR("pushing statistic overflows\n");
1088+
goto end;
1089+
}
1090+
1091+
memcpy(prometheus_route_page->s + prometheus_route_page->len,
1092+
prometheus_route_page_stat.s, prometheus_route_page_stat.len);
1093+
prometheus_route_page->len += prometheus_route_page_stat.len;
1094+
if (labels.len) {
1095+
memcpy(prometheus_route_page->s + prometheus_route_page->len,
1096+
labels.s, labels.len);
1097+
prometheus_route_page->len += labels.len;
1098+
}
1099+
val.s = int2str(*value, &val.len);
1100+
prometheus_route_page->s[prometheus_route_page->len++] = ' ';
1101+
memcpy(prometheus_route_page->s + prometheus_route_page->len, val.s, val.len);
1102+
prometheus_route_page->len += val.len;
1103+
prometheus_route_page->s[prometheus_route_page->len++] = '\n';
1104+
ret = 1;
1105+
end:
1106+
if (labeln && labelv)
1107+
pkg_free(labels.s);
1108+
return ret;
1109+
}

0 commit comments

Comments
 (0)