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). |