|
| 1 | +%% @doc Exports all collected metrics as a flat list of Erlang terms. |
| 2 | +%% |
| 3 | +%% Each metric sample is represented as a 4-tuple: |
| 4 | +%% `{MetricName, Type, Labels, Value}' |
| 5 | +%% |
| 6 | +%% Where: |
| 7 | +%% <ul> |
| 8 | +%% <li>`MetricName' is a binary (histograms/summaries get `_bucket', `_sum', |
| 9 | +%% `_count' suffixes)</li> |
| 10 | +%% <li>`Type' is a lowercase atom: `counter', `gauge', `histogram', |
| 11 | +%% `summary', or `untyped'</li> |
| 12 | +%% <li>`Labels' is a proplist `[{BinaryName, BinaryValue}]'</li> |
| 13 | +%% <li>`Value' is an integer or float</li> |
| 14 | +%% </ul> |
| 15 | +%% |
| 16 | +%% Example: |
| 17 | +%% ``` |
| 18 | +%% prometheus_array_format:format(). |
| 19 | +%% [{<<"http_requests_total">>, counter, [{<<"method">>, <<"GET">>}], 42}, |
| 20 | +%% {<<"latency_bucket">>, histogram, [{<<"le">>, <<"1.0">>}], 5}, |
| 21 | +%% {<<"latency_sum">>, histogram, [], 2.3}, |
| 22 | +%% {<<"latency_count">>, histogram, [], 10}] |
| 23 | +%% ''' |
| 24 | +-module(prometheus_array_format). |
| 25 | + |
| 26 | +-include("prometheus_model.hrl"). |
| 27 | + |
| 28 | +-export([format/0, format/1]). |
| 29 | + |
| 30 | +%% @doc Exports metrics from the default registry. |
| 31 | +-spec format() -> [{binary(), atom(), [{binary(), binary()}], number()}]. |
| 32 | +format() -> |
| 33 | + format(default). |
| 34 | + |
| 35 | +%% @doc Exports metrics from the given registry. |
| 36 | +-spec format(Registry :: atom()) -> [{binary(), atom(), [{binary(), binary()}], number()}]. |
| 37 | +format(Registry) -> |
| 38 | + MFs = collect_mfs(Registry), |
| 39 | + lists:flatmap(fun mf_to_tuples/1, MFs). |
| 40 | + |
| 41 | +%%==================================================================== |
| 42 | +%% Internal functions |
| 43 | +%%==================================================================== |
| 44 | + |
| 45 | +collect_mfs(Registry) -> |
| 46 | + put(?MODULE, []), |
| 47 | + Callback = fun(Reg, Collector) -> |
| 48 | + MFCallback = fun(MF) -> put(?MODULE, [MF | get(?MODULE)]) end, |
| 49 | + prometheus_collector:collect_mf(Reg, Collector, MFCallback) |
| 50 | + end, |
| 51 | + prometheus_registry:collect(Registry, Callback), |
| 52 | + Result = lists:reverse(get(?MODULE)), |
| 53 | + erase(?MODULE), |
| 54 | + Result. |
| 55 | + |
| 56 | +mf_to_tuples(#'MetricFamily'{name = Name, type = Type, metric = Metrics}) -> |
| 57 | + T = atom_type(Type), |
| 58 | + NameBin = iolist_to_binary(Name), |
| 59 | + lists:flatmap(fun(M) -> metric_to_tuples(NameBin, T, M) end, Metrics). |
| 60 | + |
| 61 | +atom_type('COUNTER') -> counter; |
| 62 | +atom_type('GAUGE') -> gauge; |
| 63 | +atom_type('HISTOGRAM') -> histogram; |
| 64 | +atom_type('SUMMARY') -> summary; |
| 65 | +atom_type(_) -> untyped. |
| 66 | + |
| 67 | +metric_to_tuples(Name, T, #'Metric'{label = Labels, counter = #'Counter'{value = V}}) -> |
| 68 | + [{Name, T, extract_labels(Labels), V}]; |
| 69 | +metric_to_tuples(Name, T, #'Metric'{label = Labels, gauge = #'Gauge'{value = V}}) -> |
| 70 | + [{Name, T, extract_labels(Labels), V}]; |
| 71 | +metric_to_tuples(Name, T, #'Metric'{label = Labels, untyped = #'Untyped'{value = V}}) -> |
| 72 | + [{Name, T, extract_labels(Labels), V}]; |
| 73 | +metric_to_tuples(Name, T, #'Metric'{label = Labels, |
| 74 | + summary = #'Summary'{sample_count = C, sample_sum = S, quantile = Qs}}) -> |
| 75 | + Base = extract_labels(Labels), |
| 76 | + QTerms = [{Name, T, Base ++ [{<<"quantile">>, format_float(Q)}], V} |
| 77 | + || #'Quantile'{quantile = Q, value = V} <- Qs], |
| 78 | + QTerms ++ [ |
| 79 | + {<<Name/binary, "_sum">>, T, Base, S}, |
| 80 | + {<<Name/binary, "_count">>, T, Base, C} |
| 81 | + ]; |
| 82 | +metric_to_tuples(Name, T, #'Metric'{label = Labels, |
| 83 | + histogram = #'Histogram'{sample_count = C, sample_sum = S, bucket = Bs}}) -> |
| 84 | + Base = extract_labels(Labels), |
| 85 | + BTerms = [{<<Name/binary, "_bucket">>, T, |
| 86 | + Base ++ [{<<"le">>, format_bound(UB)}], CC} |
| 87 | + || #'Bucket'{upper_bound = UB, cumulative_count = CC} <- Bs], |
| 88 | + BTerms ++ [ |
| 89 | + {<<Name/binary, "_sum">>, T, Base, S}, |
| 90 | + {<<Name/binary, "_count">>, T, Base, C} |
| 91 | + ]. |
| 92 | + |
| 93 | +extract_labels(Labels) when is_list(Labels) -> |
| 94 | + [{iolist_to_binary(N), iolist_to_binary(V)} |
| 95 | + || #'LabelPair'{name = N, value = V} <- Labels]; |
| 96 | +extract_labels(_) -> |
| 97 | + []. |
| 98 | + |
| 99 | +format_bound(infinity) -> |
| 100 | + <<"+Inf">>; |
| 101 | +format_bound(N) when is_integer(N) -> |
| 102 | + integer_to_binary(N); |
| 103 | +format_bound(N) -> |
| 104 | + float_to_binary(N, [{decimals, 10}, compact]). |
| 105 | + |
| 106 | +format_float(N) when is_integer(N) -> |
| 107 | + integer_to_binary(N); |
| 108 | +format_float(N) -> |
| 109 | + float_to_binary(N, [{decimals, 10}, compact]). |
0 commit comments