./ct_report/coverage/mongoose_instrument_prometheus.COVER.html

1 -module(mongoose_instrument_prometheus).
2
3 -behaviour(mongoose_instrument).
4
5 -export([set_up/3, handle_event/4]).
6
7 %% Define Prometheus metric-related types, because the library has no type specs
8 -type spec() :: proplists:proplist().
9 -type name() :: string().
10 -type help() :: string().
11
12 -spec set_up(mongoose_instrument:event_name(), mongoose_instrument:labels(),
13 mongoose_instrument:config()) -> boolean().
14 set_up(EventName, Labels, #{metrics := Metrics}) ->
15 1607 maps:foreach(fun(MetricName, MetricType) ->
16 2454 set_up_metric(EventName, Labels, MetricName, MetricType)
17 end, Metrics),
18 1607 true;
19 set_up(_EventName, _Labels, #{}) ->
20
:-(
false.
21
22 -spec handle_event(mongoose_instrument:event_name(), mongoose_instrument:labels(),
23 mongoose_instrument:config(), mongoose_instrument:measurements()) -> ok.
24 handle_event(EventName, Labels, #{metrics := Metrics}, Measurements) ->
25 15578 LabelValues = labels_to_values(Labels),
26 15578 maps:foreach(fun(MetricName, MetricType) ->
27 33482 handle_metric_event(EventName, LabelValues, MetricName, MetricType, Measurements)
28 end, Metrics).
29
30 -spec set_up_metric(mongoose_instrument:event_name(), mongoose_instrument:labels(),
31 mongoose_instrument:metric_name(), mongoose_instrument:metric_type()) ->
32 ok.
33 set_up_metric(EventName, Labels, MetricName, MetricType) ->
34 2454 LabelKeys = labels_to_keys(Labels),
35 2454 LabelValues = labels_to_values(Labels),
36 2454 MetricSpec = metric_spec(EventName, LabelKeys, MetricName),
37 2454 FullName = proplists:get_value(name, MetricSpec),
38 2454 case declare_metric(MetricSpec, MetricType) of
39 true ->
40 32 ok; %% Metric does not exist - no need to reset
41 false ->
42 2422 reset_metric(FullName, LabelValues, MetricType)
43 end,
44 2454 initialize_metric(FullName, LabelValues, MetricType).
45
46 -spec declare_metric(proplists:proplist(), mongoose_instrument:metric_type()) -> boolean().
47 declare_metric(MetricSpec, gauge) ->
48 58 prometheus_gauge:declare(MetricSpec);
49 declare_metric(MetricSpec, spiral) ->
50 1683 prometheus_counter:declare(MetricSpec);
51 declare_metric(MetricSpec, histogram) ->
52 713 prometheus_histogram:declare([{buckets, histogram_buckets()} | MetricSpec]).
53
54 -spec reset_metric(name(), [mongoose_instrument:label_value()],
55 mongoose_instrument:metric_type()) -> boolean().
56 reset_metric(Name, LabelValues, gauge) ->
57 56 prometheus_gauge:remove(Name, LabelValues);
58 reset_metric(Name, LabelValues, spiral) ->
59 1663 prometheus_counter:remove(Name, LabelValues);
60 reset_metric(Name, LabelValues, histogram) ->
61 703 prometheus_histogram:remove(Name, LabelValues).
62
63 -spec initialize_metric(name(), [mongoose_instrument:label_value()],
64 mongoose_instrument:metric_type()) -> ok.
65 initialize_metric(Name, LabelValues, spiral) ->
66 %% Initialize counter, because it has a meaningful initial value of zero
67 %% Additionally, leaving it undefined would delay rate calculation in Prometheus
68 1683 prometheus_counter:inc(Name, LabelValues, 0);
69 initialize_metric(_Name, _LabelValues, _) ->
70 %% Don't initialize, because no meaningful value could be provided
71 771 ok.
72
73 -spec metric_spec(mongoose_instrument:event_name(), [mongoose_instrument:label_key()],
74 mongoose_instrument:metric_name()) -> spec().
75 metric_spec(EventName, LabelKeys, MetricName) ->
76 2454 [{name, full_metric_name(EventName, MetricName)},
77 {help, metric_help(EventName, MetricName)},
78 {labels, LabelKeys}].
79
80 -spec histogram_buckets() -> [integer()].
81 histogram_buckets() ->
82 713 histogram_buckets([], 1 bsl 30). % ~1.07 * 10^9
83
84 histogram_buckets(AccBuckets, Val) when Val > 0 ->
85 22103 histogram_buckets([Val | AccBuckets], Val bsr 1);
86 histogram_buckets(AccBuckets, _Val) ->
87 713 AccBuckets.
88
89 -spec handle_metric_event(mongoose_instrument:event_name(), [mongoose_instrument:label_value()],
90 mongoose_instrument:metric_name(), mongoose_instrument:metric_type(),
91 mongoose_instrument:measurements()) -> ok.
92 handle_metric_event(EventName, LabelValues, MetricName, MetricType, Measurements) ->
93 33482 case Measurements of
94 #{MetricName := MetricValue} ->
95 32470 FullName = full_metric_name(EventName, MetricName),
96 32470 update_metric(FullName, LabelValues, MetricType, MetricValue);
97 #{} ->
98 1012 ok
99 end.
100
101 -spec metric_help(mongoose_instrument:event_name(), mongoose_instrument:metric_name()) -> help().
102 metric_help(EventName, MetricName) ->
103 2454 lists:flatten(io_lib:format("Event: ~p, Metric: ~p", [EventName, MetricName])).
104
105 -spec full_metric_name(mongoose_instrument:event_name(), mongoose_instrument:metric_name()) ->
106 name().
107 full_metric_name(EventName, MetricName) ->
108 34924 atom_to_list(EventName) ++ "_" ++ atom_to_list(MetricName).
109
110 -spec labels_to_keys(mongoose_instrument:labels()) -> [mongoose_instrument:label_key()].
111 labels_to_keys(Labels) ->
112 2454 lists:sort(maps:keys(Labels)).
113
114 -spec labels_to_values(mongoose_instrument:labels()) -> [mongoose_instrument:label_value()].
115 labels_to_values(Labels) ->
116 18032 [V || {_K, V} <- lists:keysort(1, maps:to_list(Labels))].
117
118 -spec update_metric(name(), [mongoose_instrument:label_value()],
119 mongoose_instrument:metric_type(), integer()) -> ok.
120 update_metric(Name, Labels, gauge, Value) when is_integer(Value) ->
121 160 ok = prometheus_gauge:set(Name, Labels, Value);
122 update_metric(Name, Labels, spiral, Value) when is_integer(Value), Value >= 0 ->
123 15567 ok = prometheus_counter:inc(Name, Labels, Value);
124 update_metric(Name, Labels, histogram, Value) when is_integer(Value) ->
125 16743 ok = prometheus_histogram:observe(Name, Labels, Value).
Line Hits Source