|
| 1 | +%% @author Łukasz Niemier <lukasz+opensource@niemier.pl> |
| 2 | +%% |
| 3 | +%% @doc Trace reporter for DataDog ([https://datadog.com]). |
| 4 | +%% |
| 5 | +%% == Configuration == |
| 6 | +%% |
| 7 | +%% <ul> |
| 8 | +%% <li>`host' - address where DataDog Agent lives (default `"localhost"')</li> |
| 9 | +%% <li>`port' - port on which Agent listens for statsd metrics (default `8125')</li> |
| 10 | +%% </ul> |
| 11 | +-module(oc_stat_exporter_datadog). |
| 12 | + |
| 13 | +-behaviour(oc_stat_exporter). |
| 14 | + |
| 15 | +-export([export/2]). |
| 16 | + |
| 17 | +-define(DEFAULT_HOST, "localhost"). |
| 18 | +-define(DEFAULT_PORT, 8125). |
| 19 | + |
| 20 | +-include_lib("kernel/include/logger.hrl"). |
| 21 | + |
| 22 | +export(ViewData, Options) -> |
| 23 | + Host = proplists:get_value(host, Options, ?DEFAULT_HOST), |
| 24 | + Port = proplists:get_value(port, Options, ?DEFAULT_PORT), |
| 25 | + {ok, Socket} = gen_udp:open(0, [{active, false}]), |
| 26 | + Packet = build_packet(ViewData), |
| 27 | + ok = gen_udp:send(Socket, Host, Port, Packet), |
| 28 | + ok = gen_udp:close(Socket), |
| 29 | + ok. |
| 30 | + |
| 31 | +build_packet(Data) when is_list(Data) -> |
| 32 | + List = [build_packet(Entry) || Entry <- Data], |
| 33 | + lists:join($\n, List); |
| 34 | +build_packet(#{name := Name, |
| 35 | + ctags := CTags, |
| 36 | + tags := Tags, |
| 37 | + data := #{type := Type, |
| 38 | + rows := Rows}}) -> |
| 39 | + Key = to_key(Name), |
| 40 | + List = [ build_rows(Key, Type, CTags, Tags, Row) || Row <- Rows ], |
| 41 | + lists:join($\n, List). |
| 42 | + |
| 43 | +build_tags(Tags, TagsV, CTags) -> |
| 44 | + build_tags(maps:merge(CTags, maps:from_list(lists:zip(Tags, TagsV)))). |
| 45 | + |
| 46 | +build_tags(Map) when is_map(Map) -> build_tags(maps:to_list(Map)); |
| 47 | +build_tags([]) -> []; |
| 48 | +build_tags(Tags) -> |
| 49 | + List = [[to_key(Key), $:, Value] |
| 50 | + || {Key, Value} <- Tags], |
| 51 | + ["|#", lists:join($,, List)]. |
| 52 | + |
| 53 | +to_key(Atom) when is_atom(Atom) -> |
| 54 | + erlang:atom_to_binary(Atom, utf8); |
| 55 | +to_key(Value) -> Value. |
| 56 | + |
| 57 | +build_rows(Name, Type, CTags, Tags, #{tags := TagsV, value := Value}) -> |
| 58 | + TagList = build_tags(Tags, TagsV, CTags), |
| 59 | + Metrics = [[Name, Stat] || Stat <- build_row(Type, Value, TagList) ], |
| 60 | + lists:join($\n, Metrics). |
| 61 | + |
| 62 | +build_row(sum, #{count := Count, |
| 63 | + mean := Mean, |
| 64 | + sum := Sum}, Tags) -> |
| 65 | + [ |
| 66 | + [".count:", format_num(Count), "|g", Tags], |
| 67 | + [".mean:", format_num(Mean), "|g", Tags], |
| 68 | + [".sum:", format_num(Sum), "|g", Tags] |
| 69 | + ]; |
| 70 | +build_row(distribution, #{buckets := Buckets} = Data, Tags) -> |
| 71 | + BucketRows = [ bucket_row(Bucket, Tags) || Bucket <- Buckets ], |
| 72 | + build_row(sum, Data, Tags) ++ BucketRows; |
| 73 | +build_row(_Type, Value, Tags) -> |
| 74 | + [[$:, format_num(Value), "|g", Tags]]. |
| 75 | + |
| 76 | +bucket_row({Bound, Count}, Tags) when is_integer(Count) -> |
| 77 | + [$:, format_num(Count), "|g", tags_append(Tags, ["le:", format_num(Bound)])]. |
| 78 | + |
| 79 | +tags_append([], Value) -> ["|#", Value]; |
| 80 | +tags_append(List, Value) -> [List, $,, Value]. |
| 81 | + |
| 82 | +format_num(infinity) -> <<"infinity">>; |
| 83 | +format_num(Integer) when is_integer(Integer) -> |
| 84 | + erlang:integer_to_binary(Integer); |
| 85 | +format_num(Float) when is_float(Float) -> |
| 86 | + erlang:float_to_binary(Float, [{decimal, 5}, compact]). |
0 commit comments