Browse code

xhttp_prom: module to generate Prometheus metrics.

Vicente Hernando authored on 07/06/2019 13:40:55
Showing 10 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,9 @@
1
+#
2
+# WARNING: do not run this directly, it should be run by the master Makefile
3
+
4
+include ../../Makefile.defs
5
+auto_gen=
6
+NAME=xhttp_prom.so
7
+LIBS=
8
+
9
+include ../../Makefile.modules
0 10
new file mode 100644
... ...
@@ -0,0 +1,4 @@
1
+docs = xhttp_prom.xml
2
+
3
+docbook_dir = ../../../../doc/docbook
4
+include $(docbook_dir)/Makefile.module
0 5
new file mode 100644
... ...
@@ -0,0 +1,41 @@
1
+<?xml version="1.0" encoding='ISO-8859-1'?>
2
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
3
+"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [
4
+
5
+<!-- Include general documentation entities -->
6
+<!ENTITY % docentities SYSTEM "../../../../doc/docbook/entities.xml">
7
+%docentities;
8
+
9
+]>
10
+
11
+<book xmlns:xi="http://www.w3.org/2001/XInclude">
12
+	<bookinfo>
13
+		<title>xHTTP_PROM Module</title>
14
+		<productname class="trade">&kamailioname;</productname>
15
+		<authorgroup>
16
+			<author>
17
+				<firstname>Vicente</firstname>
18
+				<surname>Hernando</surname>
19
+				<email>vhernando@sonoc.io</email>
20
+			</author>
21
+			<editor>
22
+				<firstname>Vicente</firstname>
23
+				<surname>Hernando</surname>
24
+				<email>vhernando@sonoc.io</email>
25
+			</editor>
26
+			<author>
27
+			    <firstname>Javier</firstname>
28
+				<surname>Gallart</surname>
29
+				<email>jgallart@sonoc.io</email>
30
+			</author>
31
+		</authorgroup>
32
+		<copyright>
33
+			<year>2019</year>
34
+			<holder>www.sonoc.io</holder>
35
+		</copyright>
36
+	</bookinfo>
37
+	<toc></toc>
38
+
39
+	<xi:include href="xhttp_prom_admin.xml"/>
40
+
41
+</book>
0 42
new file mode 100644
... ...
@@ -0,0 +1,728 @@
1
+<?xml version="1.0" encoding='ISO-8859-1'?>
2
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
3
+"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [
4
+
5
+<!-- Include general documentation entities -->
6
+<!ENTITY % docentities SYSTEM "../../../../doc/docbook/entities.xml">
7
+%docentities;
8
+
9
+]>
10
+<!-- Module User's Guide -->
11
+
12
+<chapter>
13
+
14
+  <title>&adminguide;</title>
15
+  
16
+  <section>
17
+	<title>Overview</title>
18
+	<para>
19
+	  This module generates suitable metrics for a Prometheus monitoring platform.
20
+	</para>
21
+	<para>
22
+	  It answers Prometheus pull requests (HTTP requests to /metrics URL).
23
+	</para>
24
+	<para>
25
+	  The module generates metrics based on &kamailio; statistics, and also the user
26
+	  can create his own metrics (currently counters and gauges).
27
+	</para>
28
+	<para>
29
+	  The xHTTP_PROM module uses the xHTTP module to handle HTTP requests.
30
+	  Read the documentation of the xHTTP module for more details.
31
+	</para>
32
+	<para>
33
+	  NOTE: This module is based on xHTTP_RPC one.
34
+	</para>
35
+	<para>
36
+	  <emphasis>IMPORTANT</emphasis>: This module uses private memory to generate HTTP
37
+	  responses, and shared memory to store all the metrics.
38
+	  Remember to increase size of private and shared memory if you use a huge amount
39
+	  of metrics.
40
+	</para>
41
+	<para>
42
+	  Prometheus URLs:
43
+	  <itemizedlist>
44
+		<listitem>
45
+		  <ulink url="https://prometheus.io/">https://prometheus.io/</ulink>
46
+		</listitem>
47
+		<listitem>
48
+		  <ulink url="https://prometheus.io/docs/concepts/data_model/#metric-names-and-labels">https://prometheus.io/docs/concepts/data_model/#metric-names-and-labels</ulink>
49
+		</listitem>
50
+		<listitem>
51
+		<ulink url="https://prometheus.io/docs/instrumenting/exposition_formats/">https://prometheus.io/docs/instrumenting/exposition_formats/</ulink>
52
+		</listitem>
53
+	  </itemizedlist>
54
+	</para>
55
+  </section>
56
+  <section>
57
+	<title>Dependencies</title>
58
+	<section>
59
+	  <title>&kamailio; Modules</title>
60
+	  <para>
61
+		The following modules must be loaded before this module:
62
+		<itemizedlist>
63
+		  <listitem>
64
+			<para>
65
+			  <emphasis>xhttp</emphasis> -- xHTTP.
66
+			</para>
67
+		  </listitem>
68
+		</itemizedlist>
69
+	  </para>
70
+	</section>
71
+	<section>
72
+	  <title>External Libraries or Applications</title>
73
+	  <para>
74
+		The following libraries or applications must be installed before running
75
+		&kamailio; with this module loaded:
76
+		<itemizedlist>
77
+		  <listitem>
78
+			<para>
79
+			  <emphasis>None</emphasis>
80
+			</para>
81
+		  </listitem>
82
+		</itemizedlist>
83
+	  </para>
84
+	</section>
85
+  </section>
86
+  <section>
87
+	<title>Parameters</title>
88
+	<section id="xhttp_prom.p.xhttp_prom_buf_size">
89
+	  <title><varname>xhttp_prom_buf_size</varname> (integer)</title>
90
+	  <para>
91
+		Specifies the maximum length of the buffer (in bytes) used
92
+		to write the metric reply information in order to
93
+		build the HTML response.
94
+	  </para>
95
+	  <para>
96
+		<emphasis>
97
+		  Default value is 0 (auto set to 1/3 of the size of the configured pkg mem).
98
+		</emphasis>
99
+	  </para>
100
+	  <example>
101
+		<title>Set <varname>xhttp_prom_buf_size</varname> parameter</title>
102
+		<programlisting format="linespecific">
103
+...
104
+modparam("xhttp", "xhttp_prom_buf_size", 1024)
105
+...
106
+		</programlisting>
107
+	  </example>
108
+	</section>
109
+	<section id="xhttp_prom.p.xhttp_prom_timeout">
110
+	  <title><varname>xhttp_prom_timeout</varname> (integer)</title>
111
+	  <para>
112
+		Specifies a timeout in minutes. A metric not used during this timeout is
113
+		automatically deleted. Listing metrics does not count as using them.
114
+	  </para>
115
+	  <para>
116
+		<emphasis>
117
+		  Default value is 60 minutes.
118
+		</emphasis>
119
+	  </para>
120
+	  <example>
121
+		<title>Set <varname>xhttp_prom_timeout</varname> parameter</title>
122
+		<programlisting format="linespecific">
123
+...
124
+# Set timeout to 10 hours		  
125
+modparam("xhttp", "xhttp_prom_timeout", 600)
126
+...
127
+		</programlisting>
128
+	  </example>
129
+	</section>
130
+	<section id="xhttp_prom.p.xhttp_prom_stats">
131
+	  <title><varname>xhttp_prom_stats</varname> (str)</title>
132
+	  <para>
133
+		Specifies which internal statistics from &kamailio; to show.
134
+		
135
+		Possible values:
136
+		<itemizedlist>
137
+		  <listitem>
138
+			<para><emphasis>all</emphasis> - Show whole &kamailio; statistics</para>
139
+		  </listitem>
140
+		  <listitem>
141
+			<para><emphasis>group_name:</emphasis> - Show all statistics for a group</para>
142
+		  </listitem>
143
+		  <listitem>
144
+			<para><emphasis>statistic_name</emphasis> - Show a specific statistic.
145
+			It automatically finds the group.</para>
146
+		  </listitem>
147
+		</itemizedlist>
148
+	  </para>
149
+	  <para>
150
+		<emphasis>
151
+		  Default value is "", meaning do not display any &kamailio; statistics.
152
+		</emphasis>
153
+	  </para>
154
+	  <example>
155
+		<title>Set <varname>xhttp_prom_stats</varname> parameter</title>
156
+		<programlisting format="linespecific">
157
+...
158
+# show all kamailio statistics.
159
+modparam("xhttp_prom", "xhttp_prom_stats", "all")
160
+
161
+# show statistics for sl group.
162
+modparam("xhttp_prom", "xhttp_prom_stats", "sl:")
163
+
164
+# Show statistic for 200_replies in sl group.
165
+modparam("xhttp_prom", "xhttp_prom_stats", "200_replies")
166
+
167
+# Do not display internal &kamailio; statistics. This is the default option.
168
+modparam("xhttp_prom", "xhttp_prom_stats", "")
169
+...
170
+		</programlisting>
171
+	  </example>
172
+	</section>
173
+	<section id="xhttp_prom.p.prom_counter">
174
+	  <title><varname>prom_counter</varname> (str)</title>
175
+	  <para>
176
+		Create a counter metric.
177
+	  </para>
178
+	  <para>
179
+		This function declares a counter but the actual counter is only created
180
+		when using it (by adding to or resetting it)
181
+	  </para>
182
+	  <para>
183
+		It takes a list of attribute=value separated by semicolon, the attributes can
184
+		be name and label.
185
+	  </para>
186
+	  <itemizedlist>
187
+		<listitem>
188
+		  <para>
189
+			<emphasis>name</emphasis> - name of the counter. This attribute is mandatory.
190
+			It is used to generate the metric name. Each name is unique, no metric shall
191
+			repeat a name.
192
+		  </para>
193
+		</listitem>
194
+		<listitem>
195
+		  <para>
196
+			<emphasis>label</emphasis> - names of labels in the counter. Optional.
197
+			Only one label parameter at most allowed in counters.
198
+			Each label name is separated by <emphasis>:</emphasis> without spaces.
199
+			At most only three label names allowed in each label parameter.
200
+			<example><title><varname>prom_counter</varname> label example</title>
201
+			<programlisting format="linespecific">
202
+# Create two labels called method and handler
203
+label = method:handler
204
+This would generate  {method="whatever", handler="whatever2"} when building
205
+the metric.
206
+			</programlisting>
207
+			</example>
208
+		  </para>
209
+		</listitem>
210
+	  </itemizedlist>
211
+	  <example>
212
+		<title>Set <varname>prom_counter</varname> parameter</title>
213
+		<programlisting format="linespecific">
214
+...
215
+		  
216
+# Create cnt_first counter with no labels.
217
+modparam("xhttp_prom", "prom_counter", "name=cnt_first;");
218
+
219
+# Create cnt_second counter with no labels.
220
+modparam("xhttp_prom", "prom_counter", "name=cnt_second;");
221
+
222
+
223
+# Create cnt_third counter with label method
224
+modparam("xhttp_prom", "prom_counter", "name=cnt_third; label=method");
225
+
226
+These lines declare the counter but the actual metric will be created when
227
+using it by prom_counter_inc or prom_counter_reset functions.
228
+
229
+...
230
+		</programlisting>
231
+	  </example>
232
+	</section>
233
+	<section id="xhttp_prom.p.prom_gauge">
234
+	  <title><varname>prom_gauge</varname> (str)</title>
235
+	  <para>
236
+		Create a gauge metric.
237
+	  </para>
238
+	  <para>
239
+		This function declares the gauge but the actual gauge is only created
240
+		when using it (by setting or resetting it)
241
+	  </para>
242
+	  <para>
243
+		It takes a list of attribute=value separated by semicolon, the attributes can
244
+		be name and value.
245
+	  </para>
246
+	  <itemizedlist>
247
+		<listitem>
248
+		  <para>
249
+			<emphasis>name</emphasis> - name of the gauge. This attribute is mandatory.
250
+			It is used to generate the metric name. Each name is unique, no metric shall
251
+			repeat a name.
252
+		  </para>
253
+		</listitem>
254
+		<listitem>
255
+		  <para>
256
+			<emphasis>label</emphasis> - names of labels in the gauge. Optional.
257
+			Only one label parameter at most allowed in gauges.
258
+			Each label name is separated by <emphasis>:</emphasis> without spaces.
259
+			At most only three label names allowed inside each label parameter.
260
+			<example><title><varname>prom_gauge</varname> label example</title>
261
+			<programlisting format="linespecific">
262
+# Create two labels called method and handler
263
+label = method:handler
264
+This would generate  {method="whatever", handler="whatever2"} when building
265
+the metric.
266
+			</programlisting>
267
+			</example>
268
+		  </para>
269
+		</listitem>
270
+	  </itemizedlist>
271
+	  <example>
272
+		<title>Set <varname>prom_gauge</varname> parameter</title>
273
+		<programlisting format="linespecific">
274
+...
275
+
276
+# Create gg_first gauge with no labels
277
+modparam("xhttp_prom", "prom_gauge", "name=gg_first;");
278
+
279
+# Create gg_second gauge with no labels
280
+modparam("xhttp_prom", "prom_gauge", "name=gg_second;");
281
+
282
+
283
+# Create gg_third gauge with two labels method and handler:
284
+modparam("xhttp_prom", "prom_gauge", "name=gg_third; label=method:handler;");
285
+
286
+...
287
+		</programlisting>
288
+	  </example>
289
+	</section>
290
+  </section>
291
+  <section>
292
+	<title>Functions</title>
293
+	<section id="xhttp_prom.f.prom_counter_reset">
294
+	  <title>
295
+		<function moreinfo="none">prom_counter_reset(name, l0, l1, l2)</function>
296
+	  </title>
297
+	  <para>
298
+		Get a counter based on its name and labels and reset its value to 0.
299
+		Name parameter is mandatory. Values of labels are optional (from none up to three).
300
+		Name in prom_counter_reset has to match same name in prom_counter parameter.
301
+		Number of labels in prom_counter_reset has to match number of labels in prom_counter parameter.
302
+		First time a counter is used with this reset function the counter is created if it does not exist already.
303
+	  </para>
304
+	  <para>
305
+		This function accepts pseudovariables on its parameters.
306
+	  </para>
307
+	  <para>
308
+		Available via KEMI framework as <emphasis>counter_reset_l0</emphasis>,
309
+		<emphasis>counter_reset_l1</emphasis>,
310
+		<emphasis>counter_reset_l2</emphasis> and
311
+		<emphasis>counter_reset_l3</emphasis>.
312
+	  </para>
313
+	  <example>
314
+		<title><function>prom_counter_reset</function> usage</title>
315
+		<programlisting format="linespecific">
316
+...
317
+# Definition of counter with prom_counter with labels method and IP
318
+modparam("xhttp_prom", "prom_counter", "name=cnt01; label=method:IP;");
319
+...
320
+# Reset cnt01 counter with two values "push" and "192.168.0.1" in labels to zero.
321
+# First time we execute this function the counter will be created.
322
+prom_counter_reset("cnt01", "push", "192.168.0.1");
323
+...
324
+# A metric like this will appear when listing this counter:
325
+kamailio_cnt01 {method="push", IP="192.168.0.1"} 0 1234567890
326
+...
327
+		</programlisting>
328
+	  </example>
329
+	</section>
330
+	<section id="xhttp_prom.f.prom_gauge_reset">
331
+	  <title>
332
+		<function moreinfo="none">prom_gauge_reset(name, l0, l1, l2)</function>
333
+	  </title>
334
+	  <para>
335
+		Get a gauge based on its name and labels and reset its value to 0.
336
+		Name parameter is mandatory. Values of labels are optional (from none up to three).
337
+		Name in prom_gauge_reset has to match same name in prom_gauge parameter.
338
+		Number of labels in prom_gauge_reset has to match number of labels in prom_gauge parameter.
339
+		First time a gauge is used with this reset function the gauge is created if it does not exist already.
340
+	  </para>
341
+	  <para>
342
+		This function accepts pseudovariables on its parameters.
343
+	  </para>
344
+	  <para>
345
+		Available via KEMI framework as <emphasis>gauge_reset_l0</emphasis>,
346
+		<emphasis>gauge_reset_l1</emphasis>,
347
+		<emphasis>gauge_reset_l2</emphasis> and
348
+		<emphasis>gauge_reset_l3</emphasis>.
349
+	  </para>
350
+	  <example>
351
+		<title><function>prom_gauge_reset</function> usage</title>
352
+		<programlisting format="linespecific">
353
+...
354
+# Definition of gauge with prom_gauge with labels method and IP
355
+modparam("xhttp_prom", "prom_gauge", "name=cnt01; label=method:IP;");
356
+...
357
+# Reset cnt01 gauge with two values "push" and "192.168.0.1" in labels to zero.
358
+# First time we execute this function the gauge will be created.
359
+prom_gauge_reset("cnt01", "push", "192.168.0.1");
360
+...
361
+# A metric like this will appear when listing this gauge:
362
+kamailio_cnt01 {method="push", IP="192.168.0.1"} 0 1234567890
363
+...
364
+		</programlisting>
365
+	  </example>
366
+	</section>
367
+	<section id="xhttp_prom.f.prom_counter_inc">
368
+	  <title>
369
+		<function moreinfo="none">prom_counter_inc(name, number, l0, l1, l2)</function>
370
+	  </title>
371
+	  <para>
372
+		Get a counter identified by its name and labels and increase its value by a number.
373
+		If counter does not exist it creates the counter, initializes it to zero and adds the number.
374
+	  </para>
375
+	  <para>
376
+		Name is mandatory, number is mandatory.
377
+		Number has to be positive or zero (integer).
378
+		l0, l1, l2 are values of labels and are optional.
379
+	  </para>
380
+	  <para>
381
+		name value and number of labels have to match a previous counter definition with prom_counter.
382
+	  </para>
383
+	  <para>
384
+		This function accepts pseudovariables on its parameters.
385
+	  </para>
386
+	  <para>
387
+		Available via KEMI framework as <emphasis>counter_inc_l0</emphasis>,
388
+		<emphasis>counter_inc_l1</emphasis>,
389
+		<emphasis>counter_inc_l2</emphasis> and
390
+		<emphasis>counter_inc_l3</emphasis>.
391
+	  </para>
392
+	  <example>
393
+		<title><function>prom_counter_inc</function> usage</title>
394
+		<programlisting format="linespecific">
395
+...
396
+# Definition of cnt01 counter with no labels.
397
+modparam("xhttp_prom", "prom_counter", "name=cnt01;");
398
+...
399
+# Add 10 to value of cnt01 counter (with no labels) If counter does not exist it gets created.
400
+prom_counter_inc("cnt01", "10");
401
+...
402
+
403
+# Definition of cnt02 counter with two labels method and IP
404
+modparam("xhttp_prom", "prom_counter", "name=cnt02; label=method:IP;");
405
+...
406
+# Add 15 to value of cnt02 counter with labels method and IP. It creates the counter if it does not exist.
407
+prom_counter_inc("cnt02", "15", "push", "192.168.0.1");
408
+# When listed the metric it will show a line like this:
409
+kamailio_cnt02 {method="push", IP="192.168.0.1"} 15 1234567890
410
+...
411
+		</programlisting>
412
+	  </example>
413
+	</section>
414
+	<section id="xhttp_prom.f.prom_gauge_set">
415
+	  <title>
416
+		<function moreinfo="none">prom_gauge_set(name, number, l0, l1, l2)</function>
417
+	  </title>
418
+	  <para>
419
+		Get a gauge identified by its name and labels and set its value to a number.
420
+		If gauge does not exist it creates the gauge and assigns the value to it.
421
+	  </para>
422
+	  <para>
423
+		Name is mandatory, number is mandatory.
424
+		Number is a string that will be parsed as a float.
425
+		l0, l1, l2 are values of labels and are optional.
426
+	  </para>
427
+	  <para>
428
+		name value and number of labels have to match a previous gauge definition with prom_gauge.
429
+	  </para>
430
+	  <para>
431
+		This function accepts pseudovariables on its parameters.
432
+	  </para>
433
+	  <para>
434
+		Available via KEMI framework as <emphasis>gauge_set_l0</emphasis>,
435
+		<emphasis>gauge_set_l1</emphasis>,
436
+		<emphasis>gauge_set_l2</emphasis> and
437
+		<emphasis>gauge_set_l3</emphasis>.
438
+	  </para>
439
+	  <example>
440
+		<title><function>prom_gauge_set</function> usage</title>
441
+		<programlisting format="linespecific">
442
+...
443
+# Definition of gg01 gauge with no labels.
444
+modparam("xhttp_prom", "prom_gauge", "name=gg01;");
445
+...
446
+# Assign -12.5 to value of gg01 gauge (with no labels) If gauge does not exist it gets created
447
+prom_gauge_set("gg01", "-12.5");
448
+...
449
+
450
+# Definition of gg02 gauge with two labels method and IP
451
+modparam("xhttp_prom", "prom_gauge", "name=cnt02; label=method:IP;");
452
+...
453
+# Assign 2.8 to value of gg02 gauge with labels method and IP. It creates the gauge if it does not exist.
454
+prom_gauge_set("gg02", "2.8", "push", "192.168.0.1");
455
+# When listed the metric it will show a line like this:
456
+kamailio_gg02 {method="push", IP="192.168.0.1"} 2.8 1234567890
457
+...
458
+		</programlisting>
459
+	  </example>
460
+	</section>
461
+	<section id="xhttp_prom.f.prom_dispatch">
462
+	  <title>
463
+		<function moreinfo="none">prom_dispatch()</function>
464
+	  </title>
465
+	  <para>
466
+		Handle the HTTP request and generate a response.
467
+	  </para>
468
+	  <para>
469
+		Available via KEMI framework as <emphasis>xhttp_prom.dispatch</emphasis>
470
+	  </para>
471
+	  <example>
472
+		<title><function>prom_dispatch</function> usage</title>
473
+		<programlisting format="linespecific">
474
+...
475
+# Needed to use SIP frames as HTTP ones.
476
+tcp_accept_no_cl=yes
477
+...
478
+# xhttp module depends on sl one.
479
+loadmodule "sl.so"
480
+loadmodule "xhttp.so"
481
+loadmodule "xhttp_prom.so"
482
+...
483
+# show all kamailio statistics.
484
+modparam("xhttp_prom", "xhttp_prom_stats", "all")
485
+...
486
+event_route[xhttp:request] {
487
+	$var(xhttp_prom_root) = $(hu{s.substr,0,8});
488
+	if ($var(xhttp_prom_root) == "/metrics")
489
+		prom_dispatch();
490
+	else
491
+		xhttp_reply("200", "OK", "text/html",
492
+        		"&lt;html&gt;&lt;body&gt;Wrong URL $hu&lt;/body&gt;&lt;/html&gt;");
493
+}
494
+...
495
+		</programlisting>
496
+	  </example>
497
+	  <example>
498
+		<title><function>prom_dispatch</function> usage (more complete)</title>
499
+		<para>Another example with counters and gauge:</para>
500
+		<programlisting format="linespecific">
501
+...
502
+# Needed to use SIP frames as HTTP ones.
503
+tcp_accept_no_cl=yes
504
+
505
+# xhttp module depends on sl one.
506
+loadmodule "sl.so"
507
+loadmodule "xhttp.so"
508
+loadmodule "xhttp_prom.so"
509
+
510
+# show &kamailio; statistics for sl group
511
+modparam("xhttp_prom", "xhttp_prom_stats", "sl:")
512
+
513
+# Define two counters and a gauge
514
+modparam("xhttp_prom", "prom_counter", "name=cnt_first;");
515
+modparam("xhttp_prom", "prom_counter", "name=cnt_second; label=method:IP");
516
+modparam("xhttp_prom", "prom_gauge", "name=gg_first; label=handler");
517
+
518
+event_route[xhttp:request] {
519
+	$var(xhttp_prom_root) = $(hu{s.substr,0,8});
520
+	if ($var(xhttp_prom_root) == "/metrics") {
521
+	    prom_counter_reset("cnt_first");
522
+		prom_counter_inc("cnt_second", "10", "push", "192.168.0.1");
523
+		prom_gauge_set("gg_first", "5.2", "my_handler");
524
+		prom_dispatch();
525
+	} else
526
+		xhttp_reply("200", "OK", "text/html",
527
+        		"&lt;html&gt;&lt;body&gt;Wrong URL $hu&lt;/body&gt;&lt;/html&gt;");
528
+}
529
+...
530
+
531
+We can manually check the result with a web browser:
532
+We assume &kamailio; runs in localhost and port is set to default (same as SIP: 5060)
533
+http://localhost:5060
534
+...
535
+
536
+# User defined metrics
537
+kamailio_cnt_first 0 1554839325427
538
+kamailio_cnt_second {method="push", IP="192.168.0.1"} 10 1554839325427
539
+kamailio_gg_first{handler="my_handler"} 5.2 1554839325427
540
+
541
+# Kamailio internal statistics
542
+kamailio_sl_1xx_replies 0 1554839325427
543
+kamailio_sl_200_replies 15 1554839325427
544
+kamailio_sl_202_replies 0 1554839325427
545
+kamailio_sl_2xx_replies 0 1554839325427
546
+kamailio_sl_300_replies 0 1554839325427
547
+kamailio_sl_301_replies 0 1554839325427
548
+kamailio_sl_302_replies 0 1554839325427
549
+kamailio_sl_3xx_replies 0 1554839325427
550
+kamailio_sl_400_replies 0 1554839325427
551
+kamailio_sl_401_replies 0 1554839325427
552
+kamailio_sl_403_replies 0 1554839325427
553
+kamailio_sl_404_replies 0 1554839325427
554
+kamailio_sl_407_replies 0 1554839325427
555
+kamailio_sl_408_replies 0 1554839325427
556
+kamailio_sl_483_replies 0 1554839325427
557
+kamailio_sl_4xx_replies 0 1554839325427
558
+kamailio_sl_500_replies 0 1554839325427
559
+kamailio_sl_5xx_replies 0 1554839325427
560
+kamailio_sl_6xx_replies 0 1554839325427
561
+kamailio_sl_failures 0 1554839325427
562
+kamailio_sl_received_ACKs 0 1554839325427
563
+kamailio_sl_sent_err_replies 0 1554839325427
564
+kamailio_sl_sent_replies 15 1554839325427
565
+kamailio_sl_xxx_replies 0 1554839325461
566
+...
567
+		</programlisting>
568
+	  </example>
569
+	</section>
570
+	<section id="xhttp_prom.f.prom_check_uri">
571
+	  <title>
572
+		<function moreinfo="none">prom_check_uri()</function>
573
+	  </title>
574
+	  <para>
575
+		Check if path of HTTP URL equals /metrics. This avoids us to check hu variable
576
+		from xHTTP module.
577
+	  </para>
578
+	  <para>NOTE: Remember not to block /metrics URL in xHTTP module</para>
579
+	  <para>
580
+		Available via KEMI framework as <emphasis>xhttp_prom.check_uri</emphasis>
581
+	  </para>
582
+	  <example>
583
+		<title><function>prom_check_uri</function> usage</title>
584
+		<programlisting format="linespecific">
585
+...
586
+# Needed to use SIP frames as HTTP ones.
587
+tcp_accept_no_cl=yes
588
+...
589
+# xhttp module depends on sl one.
590
+loadmodule "sl.so"
591
+loadmodule "xhttp.so"
592
+loadmodule "xhttp_prom.so"
593
+...
594
+# show all kamailio statistics.
595
+modparam("xhttp_prom", "xhttp_prom_stats", "all")
596
+...
597
+event_route[xhttp:request] {
598
+	if (prom_check_uri())
599
+		prom_dispatch();
600
+	else
601
+		xhttp_reply("200", "OK", "text/html",
602
+        		"&lt;html&gt;&lt;body&gt;Wrong URL $hu&lt;/body&gt;&lt;/html&gt;");
603
+}
604
+...
605
+		</programlisting>
606
+	  </example>
607
+	</section>
608
+  </section>
609
+  <section>
610
+	<title><acronym>RPC</acronym> Commands</title>
611
+	<section  id="xhttp_prom.rpc.counter_reset">
612
+	  <title><function moreinfo="none">xhttp_prom.counter_reset</function></title>
613
+	  <para>
614
+		Set a counter to zero.
615
+	  </para>
616
+      <para>
617
+        Name: <emphasis>xhttp_prom.counter_reset</emphasis>
618
+      </para>
619
+      <para>Parameters:</para>
620
+	  <itemizedlist>
621
+		<listitem><para><emphasis>name</emphasis>: name of the counter (mandatory)</para></listitem>
622
+		<listitem><para><emphasis>l0</emphasis>: value of the first label (optional)</para></listitem>
623
+		<listitem><para><emphasis>l1</emphasis>: value of second label (optional)</para></listitem>
624
+		<listitem><para><emphasis>l2</emphasis>: value of the third label (optional)</para></listitem>
625
+	  </itemizedlist>
626
+	  <example>
627
+		<title><function>xhttp_prom.counter_reset</function> usage</title>
628
+		<programlisting format="linespecific">
629
+		  ...
630
+		  &kamcmd; xhttp_prom.counter_reset "cnt01" "push" "192.168.0.1"
631
+		  ...
632
+		</programlisting>
633
+	  </example>
634
+    </section>
635
+	<section  id="xhttp_prom.rpc.counter_inc">
636
+	  <title><function moreinfo="none">xhttp_prom.counter_inc</function></title>
637
+	  <para>
638
+		Add a number to a counter based on its name and labels.
639
+	  </para>
640
+      <para>
641
+        Name: <emphasis>xhttp_prom.counter_inc</emphasis>
642
+      </para>
643
+      <para>Parameters:</para>
644
+	  <itemizedlist>
645
+		<listitem><para><emphasis>name</emphasis>: name of the counter (mandatory)</para></listitem>
646
+		<listitem><para><emphasis>number</emphasis>: integer to add to counter value. Negative values not allowed.</para></listitem>
647
+		<listitem><para><emphasis>l0</emphasis>: value of the first label (optional)</para></listitem>
648
+		<listitem><para><emphasis>l1</emphasis>: value of second label (optional)</para></listitem>
649
+		<listitem><para><emphasis>l2</emphasis>: value of the third label (optional)</para></listitem>
650
+	  </itemizedlist>
651
+	  <example>
652
+		<title><function>xhttp_prom.counter_inc</function> usage</title>
653
+		<programlisting format="linespecific">
654
+		  ...
655
+		  &kamcmd; xhttp_prom.counter_inc "cnt01" 15 "push" "192.168.0.1"
656
+		  ...
657
+		</programlisting>
658
+	  </example>
659
+    </section>
660
+	<section  id="xhttp_prom.rpc.gauge_reset">
661
+	  <title><function moreinfo="none">xhttp_prom.gauge_reset</function></title>
662
+	  <para>
663
+		Set gauge value to zero. Select gauge based on its name and labels.
664
+	  </para>
665
+      <para>
666
+        Name: <emphasis>xhttp_prom.gauge_reset</emphasis>
667
+      </para>
668
+      <para>Parameters:</para>
669
+	  <itemizedlist>
670
+		<listitem><para><emphasis>name</emphasis>: name of the gauge (mandatory)</para></listitem>
671
+		<listitem><para><emphasis>l0</emphasis>: value of the first label (optional)</para></listitem>
672
+		<listitem><para><emphasis>l1</emphasis>: value of second label (optional)</para></listitem>
673
+		<listitem><para><emphasis>l2</emphasis>: value of the third label (optional)</para></listitem>
674
+	  </itemizedlist>
675
+	  <example>
676
+		<title><function>xhttp_prom.gauge_reset</function> usage</title>
677
+		<programlisting format="linespecific">
678
+		  ...
679
+		  &kamcmd; xhttp_prom.gauge_reset "gg01" "push" "192.168.0.1"
680
+		  ...
681
+		</programlisting>
682
+	  </example>
683
+    </section>
684
+	<section  id="xhttp_prom.rpc.gauge_set">
685
+	  <title><function moreinfo="none">xhttp_prom.gauge_set</function></title>
686
+	  <para>
687
+		Set a gauge to a number. Select the gauge by its name and labels.
688
+	  </para>
689
+      <para>
690
+        Name: <emphasis>xhttp_prom.gauge_set</emphasis>
691
+      </para>
692
+      <para>Parameters:</para>
693
+	  <itemizedlist>
694
+				<listitem><para><emphasis>name</emphasis>: name of the gauge (mandatory)</para></listitem>
695
+		<listitem><para><emphasis>number</emphasis>: float value to set the gauge to (mandatory)</para></listitem>
696
+		<listitem><para><emphasis>l0</emphasis>: value of the first label (optional)</para></listitem>
697
+		<listitem><para><emphasis>l1</emphasis>: value of second label (optional)</para></listitem>
698
+		<listitem><para><emphasis>l2</emphasis>: value of the third label (optional)</para></listitem>
699
+	  </itemizedlist>
700
+	  <example>
701
+		<title><function>xhttp_prom.gauge_set</function> usage</title>
702
+		<programlisting format="linespecific">
703
+		  ...
704
+		  &kamcmd; xhttp_prom.gauge_set "gg01" -- -5.2
705
+		  ...
706
+		</programlisting>
707
+	  </example>
708
+    </section>
709
+	<section  id="xhttp_prom.rpc.metric_list_print">
710
+	  <title><function moreinfo="none">xhttp_prom.metric_list_print</function></title>
711
+	  <para>
712
+		List of all user defined metrics.
713
+	  </para>
714
+      <para>
715
+        Name: <emphasis>xhttp_prom.metric_list_print</emphasis>
716
+      </para>
717
+      <para>Parameters:<emphasis>none</emphasis></para>
718
+	  <example>
719
+		<title><function>xhttp_prom.metric_list_print</function> usage</title>
720
+		<programlisting format="linespecific">
721
+		  ...
722
+		  &kamcmd; xhttp_prom.metric_list_print
723
+		  ...
724
+		</programlisting>
725
+	  </example>
726
+	</section>
727
+  </section>
728
+</chapter>	
0 729
new file mode 100644
... ...
@@ -0,0 +1,280 @@
1
+/*
2
+ * Copyright (C) 2012 VoIP Embedded, Inc.
3
+ *
4
+ * Copyright (C) 2019 Vicente Hernando (Sonoc)
5
+ *
6
+ * This file is part of Kamailio, a free SIP server.
7
+ *
8
+ * Kamailio is free software; you can redistribute it and/or modify
9
+ * it under the terms of the GNU General Public License as published by
10
+ * the Free Software Foundation; either version 2 of the License, or
11
+ * (at your option) any later version
12
+ *
13
+ * Kamailio is distributed in the hope that it will be useful,
14
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
+ * GNU General Public License for more details.
17
+ *
18
+ * You should have received a copy of the GNU General Public License
19
+ * along with this program; if not, write to the Free Software
20
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
21
+ *
22
+ */
23
+
24
+/**
25
+ * Functionality of prometheus module.
26
+ */
27
+
28
+#include <string.h>
29
+#include <time.h>
30
+#include <inttypes.h>
31
+#include <stdarg.h>
32
+
33
+#include "../../core/counters.h"
34
+#include "../../core/ut.h"
35
+
36
+#include "prom.h"
37
+#include "prom_metric.h"
38
+
39
+/**
40
+ * Delete current buffer data.
41
+ */
42
+void prom_body_delete(prom_ctx_t *ctx)
43
+{
44
+	ctx->reply.body.len = 0;
45
+}
46
+
47
+/**
48
+ * Write some data in prom_body buffer.
49
+ *
50
+ * /return number of bytes written.
51
+ * /return -1 on error.
52
+ */
53
+int prom_body_printf(prom_ctx_t *ctx, char *fmt, ...)
54
+{
55
+	struct xhttp_prom_reply *reply = &ctx->reply;
56
+	
57
+	va_list ap;
58
+	
59
+	va_start(ap, fmt);
60
+
61
+	LM_DBG("Body current length: %d\n", reply->body.len);
62
+
63
+	char *p = reply->buf.s + reply->body.len;
64
+	int remaining_len = reply->buf.len - reply->body.len;
65
+	LM_DBG("Remaining length: %d\n", remaining_len);
66
+
67
+	/* int vsnprintf(char *str, size_t size, const char *format, va_list ap); */
68
+	int len = vsnprintf(p, remaining_len, fmt, ap);
69
+	if (len < 0) {
70
+		LM_ERR("Error printing body buffer\n");
71
+		goto error;
72
+	} else if (len >= remaining_len) {
73
+		LM_ERR("Error body buffer overflow: %d (%d)\n", len, remaining_len);
74
+		goto error;
75
+	} else {
76
+		/* Buffer printed OK. */
77
+		reply->body.len += len;
78
+		LM_DBG("Body new length: %d\n", reply->body.len);
79
+	}
80
+
81
+	va_end(ap);
82
+	return len;
83
+
84
+error:
85
+	va_end(ap);
86
+	return -1;
87
+}
88
+
89
+/**
90
+ * Get current timestamp in milliseconds.
91
+ *
92
+ * /param ts pointer to timestamp integer.
93
+ * /return 0 on success.
94
+ */
95
+int get_timestamp(uint64_t *ts)
96
+{
97
+	assert(ts);
98
+	
99
+	struct timeval current_time;
100
+	if (gettimeofday(&current_time, NULL) < 0) {
101
+		LM_ERR("failed to get current time!\n");
102
+		return -1;
103
+	}
104
+
105
+	*ts = (uint64_t)current_time.tv_sec*1000 +
106
+		(uint64_t)current_time.tv_usec/1000;
107
+	
108
+	return 0;
109
+}
110
+
111
+/**
112
+ * Generate a string suitable for a Prometheus metric.
113
+ *
114
+ * /return 0 on success.
115
+ */
116
+static int metric_generate(prom_ctx_t *ctx, str *group, str *name, counter_handle_t *h)
117
+{
118
+	long counter_val = counter_get_val(*h);
119
+
120
+	/* Calculate timestamp. */
121
+	uint64_t ts;
122
+	if (get_timestamp(&ts)) {
123
+		LM_ERR("Error getting current timestamp\n");
124
+		return -1;
125
+	}
126
+	LM_DBG("Timestamp: %" PRIu64 "\n", ts);
127
+
128
+	
129
+	/* LM_DBG("%.*s:%.*s = %lu\n",
130
+	   group->len, group->s, name->len, name->s, counter_val); */
131
+	LM_DBG("kamailio_%.*s_%.*s %lu %" PRIu64 "\n",
132
+		   group->len, group->s, name->len, name->s,
133
+		   counter_val, (uint64_t)ts);
134
+
135
+	if (prom_body_printf(ctx, "kamailio_%.*s_%.*s %lu %" PRIu64 "\n",
136
+						 group->len, group->s, name->len, name->s,
137
+						 counter_val, (uint64_t)ts) == -1) {
138
+		LM_ERR("Fail to print\n");
139
+		return -1;
140
+	}
141
+
142
+	return 0;
143
+}
144
+
145
+/**
146
+ * Statistic getter callback.
147
+ */
148
+static void prom_get_grp_vars_cbk(void* p, str* g, str* n, counter_handle_t h)
149
+{
150
+	metric_generate(p, g, n, &h);
151
+}
152
+
153
+/**
154
+ * Group statistic getter callback.
155
+ */
156
+static void prom_get_all_grps_cbk(void* p, str* g)
157
+{
158
+	counter_iterate_grp_vars(g->s, prom_get_grp_vars_cbk, p);
159
+}
160
+
161
+#define STATS_MAX_LEN 1024
162
+
163
+/**
164
+ * Get statistics (based on stats_get_all)
165
+ *
166
+ * /return 0 on success
167
+ */
168
+int prom_stats_get(prom_ctx_t *ctx, str *stat)
169
+{
170
+	if (stat == NULL) {
171
+		LM_ERR("No stats set\n");
172
+		return -1;
173
+	}
174
+
175
+	prom_body_delete(ctx);
176
+	
177
+	LM_DBG("User defined statistics\n");
178
+	if (prom_metric_list_print(ctx)) {
179
+		LM_ERR("Fail to print user defined metrics\n");
180
+		return -1;
181
+	}
182
+
183
+	LM_DBG("Statistics for: %.*s\n", stat->len, stat->s);
184
+
185
+	int len = stat->len;
186
+
187
+	stat_var *s_stat;
188
+
189
+	if (len == 0) {
190
+		LM_DBG("Do not show Kamailio statistics\n");
191
+
192
+	}
193
+	else if (len==3 && strncmp("all", stat->s, 3)==0) {
194
+		LM_DBG("Showing all statistics\n");
195
+		if (prom_body_printf(
196
+				ctx, "\n# Kamailio whole internal statistics\n") == -1) {
197
+			LM_ERR("Fail to print\n");
198
+			return -1;
199
+		}	
200
+
201
+		counter_iterate_grp_names(prom_get_all_grps_cbk, ctx);
202
+	}
203
+	else if (stat->s[len-1]==':') {
204
+		LM_DBG("Showing statistics for group: %.*s\n", stat->len, stat->s);
205
+
206
+		if (len == 1) {
207
+			LM_ERR("Void group for statistics: %.*s\n", stat->len, stat->s);
208
+			return -1;
209
+			
210
+		} else {
211
+			if (prom_body_printf(
212
+					ctx, "\n# Kamailio statistics for group: %.*s\n",
213
+					stat->len, stat->s) == -1) {
214
+				LM_ERR("Fail to print\n");
215
+				return -1;
216
+			}
217
+
218
+			/* Temporary stat_tmp string. */
219
+			char stat_tmp[STATS_MAX_LEN];
220
+			memcpy(stat_tmp, stat->s, len);
221
+			stat_tmp[len-1] = '\0';
222
+			counter_iterate_grp_vars(stat_tmp, prom_get_grp_vars_cbk, ctx);
223
+			stat_tmp[len-1] = ':';
224
+		}
225
+	}
226
+	else {
227
+		LM_DBG("Showing statistic for: %.*s\n", stat->len, stat->s);
228
+
229
+		s_stat = get_stat(stat);
230
+		if (s_stat) {
231
+			str group_str, name_str;
232
+			group_str.s = get_stat_module(s_stat);
233
+			if (group_str.s) {
234
+				group_str.len = strlen(group_str.s);
235
+			} else {
236
+				group_str.len = 0;
237
+			}
238
+			
239
+			name_str.s = get_stat_name(s_stat);
240
+			if (name_str.s) {
241
+				name_str.len = strlen(name_str.s);
242
+			} else {
243
+				name_str.len = 0;
244
+			}
245
+			
246
+			LM_DBG("%s:%s = %lu\n",
247
+				   ZSW(get_stat_module(s_stat)), ZSW(get_stat_name(s_stat)),
248
+				   get_stat_val(s_stat));
249
+
250
+			if (group_str.len && name_str.len && s_stat) {
251
+				if (prom_body_printf(
252
+						ctx, "\n# Kamailio statistics for: %.*s\n",
253
+						stat->len, stat->s) == -1) {
254
+					LM_ERR("Fail to print\n");
255
+					return -1;
256
+				}
257
+
258
+				counter_handle_t stat_handle;
259
+				stat_handle.id = (unsigned short)(unsigned long)s_stat;
260
+				if (metric_generate(ctx, &group_str, &name_str, &stat_handle)) {
261
+					LM_ERR("Failed to generate metric: %.*s - %.*s\n",
262
+						   group_str.len, group_str.s,
263
+						   name_str.len, name_str.s);
264
+					return -1;
265
+				}
266
+			} else {
267
+				LM_ERR("Not enough length for group (%d) or name (%d)\n",
268
+					   group_str.len, name_str.len);
269
+				return -1;
270
+			}
271
+		} /* if s_stat */
272
+		else {
273
+			LM_ERR("stat not found: %.*s\n", stat->len, stat->s);
274
+			return -1;
275
+		}
276
+	} /* if len == 0 */
277
+
278
+	return 0;
279
+} /* prom_stats_get */
280
+
0 281
new file mode 100644
... ...
@@ -0,0 +1,56 @@
1
+/*
2
+ * Copyright (C) 2012 VoIP Embedded, Inc.
3
+ *
4
+ * Copyright (C) 2019 Vicente Hernando (Sonoc)
5
+ *
6
+ * This file is part of Kamailio, a free SIP server.
7
+ *
8
+ * Kamailio is free software; you can redistribute it and/or modify
9
+ * it under the terms of the GNU General Public License as published by
10
+ * the Free Software Foundation; either version 2 of the License, or
11
+ * (at your option) any later version
12
+ *
13
+ * Kamailio is distributed in the hope that it will be useful,
14
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
+ * GNU General Public License for more details.
17
+ *
18
+ * You should have received a copy of the GNU General Public License
19
+ * along with this program; if not, write to the Free Software
20
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
21
+ *
22
+ */
23
+
24
+/**
25
+ * Header for functionality of prometheus module.
26
+ */
27
+
28
+#ifndef _PROM_H_
29
+#define _PROM_H_
30
+
31
+#include "xhttp_prom.h"
32
+
33
+/**
34
+ * Get current timestamp in milliseconds.
35
+ *
36
+ * /param ts pointer to timestamp integer.
37
+ * /return 0 on success.
38
+ */
39
+int get_timestamp(uint64_t *ts);
40
+
41
+/**
42
+ * Write some data in prom_body buffer.
43
+ *
44
+ * /return number of bytes written.
45
+ * /return -1 on error.
46
+ */
47
+int prom_body_printf(prom_ctx_t *ctx, char *fmt, ...);
48
+
49
+/**
50
+ * Get statistics (based on stats_get_all)
51
+ *
52
+ * /return 0 on success
53
+ */
54
+int prom_stats_get(prom_ctx_t *ctx, str *stat);
55
+
56
+#endif // _PROM_H_
0 57
new file mode 100644
... ...
@@ -0,0 +1,1361 @@
1
+/*
2
+ * Copyright (C) 2012 VoIP Embedded, Inc.
3
+ *
4
+ * Copyright (C) 2019 Vicente Hernando (Sonoc)
5
+ *
6
+ * This file is part of Kamailio, a free SIP server.
7
+ *
8
+ * Kamailio is free software; you can redistribute it and/or modify
9
+ * it under the terms of the GNU General Public License as published by
10
+ * the Free Software Foundation; either version 2 of the License, or
11
+ * (at your option) any later version
12
+ *
13
+ * Kamailio is distributed in the hope that it will be useful,
14
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
+ * GNU General Public License for more details.
17
+ *
18
+ * You should have received a copy of the GNU General Public License
19
+ * along with this program; if not, write to the Free Software
20
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
21
+ *
22
+ */
23
+
24
+#include <string.h>
25
+#include <stdint.h>
26
+#include <inttypes.h>
27
+#include <string.h>
28
+
29
+#include "../../core/mem/shm_mem.h"
30
+#include "../../core/locking.h"
31
+#include "../../core/ut.h"
32
+#include "../../core/parser/parse_param.h"
33
+
34
+#include "prom_metric.h"
35
+#include "prom.h"
36
+
37
+/* TODO: Every internal function locks and unlocks the metric system. */
38
+
39
+typedef enum metric_type {
40
+	M_UNSET = 0,
41
+	M_COUNTER = 1,
42
+	M_GAUGE = 2
43
+	/* TODO: Add more types. */
44
+} metric_type_t;
45
+
46
+/**
47
+ * Struct to store a string (node of a list)
48
+ */
49
+typedef struct prom_lb_node_s {
50
+	str n;
51
+	struct prom_lb_node_s *next;
52
+} prom_lb_node_t;
53
+
54
+/**
55
+ * Struct to store a list of strings (labels)
56
+ */
57
+typedef struct prom_lb_s {
58
+	int n_elem; /* Number of strings. */
59
+	struct prom_lb_node_s *lb;
60
+	/* TODO: Hashes? */
61
+} prom_lb_t;
62
+
63
+/**
64
+ * Struct to store a value of a label.
65
+ */
66
+typedef struct prom_lvalue_s {
67
+	prom_lb_t lval;
68
+	uint64_t ts; /* timespan. Last time metric was modified. */
69
+	union {
70
+		uint64_t cval;
71
+		double gval;
72
+	} m;
73
+	struct prom_lvalue_s *next;
74
+} prom_lvalue_t;
75
+
76
+/**
77
+ * Struct to store a metric.
78
+ */
79
+typedef struct prom_metric_s {
80
+	metric_type_t type;
81
+	str name;
82
+	struct prom_lb_s *lb_name; /* Names of labels. */
83
+	struct prom_lvalue_s *lval_list;
84
+	struct prom_metric_s *next;
85
+} prom_metric_t;
86
+
87
+/**
88
+ * Data related to Prometheus metrics.
89
+ */
90
+static prom_metric_t *prom_metric_list = NULL;
91
+static gen_lock_t *prom_lock = NULL; /* Lock to protect Prometheus metrics. */
92
+static uint64_t lvalue_timeout = 120000; /* Timeout in milliseconds for old lvalue struct. */
93
+
94
+static void prom_counter_free(prom_metric_t *m_cnt);
95
+static void prom_gauge_free(prom_metric_t *m_gg);
96
+static void prom_metric_free(prom_metric_t *metric);
97
+static void prom_lb_free(prom_lb_t *prom_lb, int shared_mem);
98
+static void prom_lb_node_free(prom_lb_node_t *lb_node, int shared_mem);
99
+static int prom_lb_node_add(prom_lb_t *m_lb, char *s, int len, int shared_mem);
100
+static void prom_lvalue_free(prom_lvalue_t *plv);
101
+static void prom_lvalue_list_free(prom_lvalue_t *plv);
102
+
103
+/**
104
+ * Free list of Prometheus metrics.
105
+ */
106
+static void prom_metric_list_free()
107
+{
108
+	prom_metric_t *p, *next;
109
+
110
+	p = prom_metric_list;
111
+	while (p) {
112
+		next = p->next;
113
+		prom_metric_free(p);
114
+		p = next;
115
+	}
116
+
117
+	prom_metric_list = NULL;
118
+}
119
+
120
+/**
121
+ * Initialize user defined metrics.
122
+ */
123
+int prom_metric_init(int timeout_minutes)
124
+{
125
+	/* Initialize timeout. minutes to milliseconds. */
126
+	if (timeout_minutes < 1) {
127
+		LM_ERR("Invalid timeout: %d\n", timeout_minutes);
128
+		return -1;
129
+	}
130
+	lvalue_timeout = ((uint64_t)timeout_minutes) * 60000;
131
+	LM_DBG("lvalue_timeout set to %" PRIu64 "\n", lvalue_timeout);
132
+	
133
+	/* Initialize lock. */
134
+	prom_lock = lock_alloc();
135
+	if (!prom_lock) {
136
+		LM_ERR("Cannot allocate lock\n");
137
+		return -1;
138
+	}
139
+
140
+	if (lock_init(prom_lock) == NULL) {
141
+		LM_ERR("Cannot initialize the lock\n");
142
+		lock_dealloc(prom_lock);
143
+		prom_lock = NULL;
144
+		return -1;
145
+	}
146
+
147
+	/* Everything went fine. */
148
+	return 0;
149
+}
150
+
151
+/**
152
+ * Close user defined metrics.
153
+ */
154
+void prom_metric_close()
155
+{
156
+	/* Free lock */
157
+	if (prom_lock) {
158
+		LM_DBG("Freeing lock\n");
159
+		lock_destroy(prom_lock);
160
+		lock_dealloc(prom_lock);
161
+		prom_lock = NULL;
162
+	}
163
+
164
+	/* Free metric list. */
165
+	if (prom_metric_list) {
166
+		LM_DBG("Freeing list of Prometheus metrics\n");
167
+		prom_metric_list_free();
168
+	}
169
+}
170
+
171
+/**
172
+ * Free a metric.
173
+ */
174
+static void prom_metric_free(prom_metric_t *metric)
175
+{
176
+	assert(metric);
177
+
178
+	if (metric->type == M_COUNTER) {
179
+		prom_counter_free(metric);
180
+	} else if (metric->type == M_GAUGE) {
181
+		prom_gauge_free(metric);
182
+	} else {
183
+		LM_ERR("Unknown metric: %d\n", metric->type);
184
+		return;
185
+	}
186
+}
187
+
188
+/**
189
+ * Free a counter.
190
+ */
191
+static void prom_counter_free(prom_metric_t *m_cnt)
192
+{
193
+	assert(m_cnt);
194
+
195
+	assert(m_cnt->type == M_COUNTER);
196
+
197
+	if (m_cnt->name.s) {
198
+		shm_free(m_cnt->name.s);
199
+	}
200
+
201
+	prom_lb_free(m_cnt->lb_name, 1);
202
+
203
+	prom_lvalue_list_free(m_cnt->lval_list);
204
+	
205
+	shm_free(m_cnt);
206
+}
207
+
208
+/**
209
+ * Get a metric based on its name.
210
+ *
211
+ * /return pointer to metric on success.
212
+ * /return NULL on error.
213
+ */
214
+static prom_metric_t* prom_metric_get(str *s_name)
215
+{
216
+	prom_metric_t *p = prom_metric_list;
217
+
218
+	while (p) {
219
+		if (s_name->len == p->name.len && strncmp(s_name->s, p->name.s, s_name->len) == 0) {
220
+			LM_DBG("Metric found: %.*s\n", p->name.len, p->name.s);
221
+			break;
222
+		}
223
+		p = p->next;
224
+	}
225
+
226
+	return p;
227
+}
228
+
229
+/**
230
+ * Compare prom_lb_t structure using some strings.
231
+ *
232
+ * /return 0 if prom_lb_t matches the strings.
233
+ */
234
+static int prom_lb_compare(prom_lb_t *plb, str *l1, str *l2, str *l3)
235
+{
236
+	if (plb == NULL) {
237
+		if (l1 != NULL) {
238
+			return -1;
239
+		}
240
+		return 0;
241
+	}
242
+
243
+	if (l1 == NULL) {
244
+		if (plb->n_elem != 0) {
245
+			return -1;
246
+		}
247
+		return 0;
248
+	}
249
+
250
+	/* At least one label. */
251
+	prom_lb_node_t *p = plb->lb;	
252
+	if (p == NULL) {
253
+		return -1;
254
+	}
255
+	if (l1->len != p->n.len || strncmp(l1->s, p->n.s, l1->len)) {
256
+		return -1;
257
+	}
258
+
259
+	p = p->next;
260
+	if (l2 == NULL) {
261
+		if (plb->n_elem != 1) {
262
+			return -1;
263
+		}
264
+		return 0;
265
+	}
266
+
267
+	/* At least two labels. */
268
+	if (p == NULL) {
269
+		return -1;
270
+	}
271
+	if (l2->len != p->n.len || strncmp(l2->s, p->n.s, l2->len)) {
272
+		return -1;
273