./ct_report/coverage/mongoose_instrument.COVER.html

1 -module(mongoose_instrument).
2
3 %% API
4 -export([start_link/0, reload/0,
5 set_up/1, set_up/3,
6 tear_down/1, tear_down/2,
7 span/4, span/5,
8 execute/3]).
9
10 %% gen_server callbacks
11 -export([init/1,
12 handle_call/3,
13 handle_cast/2,
14 code_change/3,
15 handle_info/2,
16 terminate/2]).
17
18 -ignore_xref([set_up/3, tear_down/2, span/4, span/5, execute/3]).
19
20 -include("mongoose.hrl").
21
22 -type event_name() :: atom().
23 -type labels() :: #{label_key() => binary() | label_value()}. % to be extended
24 -type label_key() :: atom().
25 -type label_value() :: atom() | binary().
26 -type metrics() :: #{atom() => spiral | histogram}. % to be extended
27 -type measurements() :: #{atom() => integer() | atom() | binary()}.
28 -type spec() :: {event_name(), labels(), config()}.
29 -type config() :: #{metrics => metrics()}. % to be extended
30 -type handler_fun() :: fun((event_name(), labels(), config(), measurements()) -> any()).
31 -type handlers() :: {[handler_fun()], config()}.
32 -type execution_time() :: integer().
33 -type measure_fun(Result) :: fun((execution_time(), Result) -> measurements()).
34
35 -callback set_up(event_name(), labels(), config()) -> boolean().
36 -callback handle_event(event_name(), labels(), config(), measurements()) -> any().
37
38 -export_type([event_name/0, labels/0, config/0, measurements/0, spec/0, handlers/0]).
39
40 -spec start_link() -> gen_server:start_ret().
41 start_link() ->
42 104 gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
43
44 -spec reload() -> ok.
45 reload() ->
46 104 gen_server:call(?MODULE, reload).
47
48 -spec set_up([spec()]) -> ok.
49 set_up(Specs) ->
50
:-(
lists:foreach(fun({EventName, Labels, Config}) -> set_up(EventName, Labels, Config) end, Specs).
51
52 -spec tear_down([spec()]) -> ok.
53 tear_down(Specs) ->
54
:-(
lists:foreach(fun({EventName, Labels, _Config}) -> tear_down(EventName, Labels) end, Specs).
55
56 -spec set_up(event_name(), labels(), config()) -> ok.
57 set_up(EventName, Labels, Config) ->
58
:-(
case gen_server:call(?MODULE, {set_up, EventName, Labels, Config}) of
59
:-(
ok -> ok;
60
:-(
{error, Error} -> error(Error)
61 end.
62
63 -spec tear_down(event_name(), labels()) -> ok.
64 tear_down(EventName, Labels) ->
65
:-(
gen_server:call(?MODULE, {tear_down, EventName, Labels}).
66
67 -spec span(event_name(), labels(), fun(() -> Result), measure_fun(Result)) -> Result.
68 span(Event, Labels, F, MeasureF) ->
69
:-(
span(Event, Labels, F, [], MeasureF).
70
71 -spec span(event_name(), labels(), fun((...) -> Result), list(), measure_fun(Result)) -> Result.
72 span(Event, Labels, F, Args, MeasureF) ->
73
:-(
Handlers = get_handlers(Event, Labels),
74
:-(
{Time, Result} = timer:tc(F, Args),
75
:-(
handle_event(Event, Labels, MeasureF(Time, Result), Handlers),
76
:-(
Result.
77
78 -spec execute(event_name(), labels(), measurements()) -> ok.
79 execute(Event, Labels, Measurements) ->
80
:-(
Handlers = get_handlers(Event, Labels),
81
:-(
handle_event(Event, Labels, Measurements, Handlers).
82
83 %% gen_server callbacks
84
85 -type state() :: #{event_name() => {[label_key()], handlers_by_labels()}}.
86 -type handlers_by_labels() :: #{labels() => handlers()}.
87
88 -spec init([]) -> {ok, state()}.
89 init([]) ->
90 104 erlang:process_flag(trap_exit, true), %% We need to make sure that terminate is called in tests
91 104 {ok, #{}}.
92
93 -spec handle_call(any(), pid(), state()) -> {reply, ok | {error, map()}, state()}.
94 handle_call({set_up, EventName, Labels, Config}, _From, State) ->
95
:-(
case set_up_and_register(EventName, Labels, Config, State) of
96 {error, _} = Error ->
97
:-(
{reply, Error, State};
98 NewState = #{} ->
99
:-(
update_if_persisted(State, NewState),
100
:-(
{reply, ok, NewState}
101 end;
102 handle_call({tear_down, EventName, Labels}, _From, State) ->
103
:-(
NewState = deregister(EventName, Labels, State),
104
:-(
update_if_persisted(State, NewState),
105
:-(
{reply, ok, NewState};
106 handle_call(reload, _From, State) ->
107 104 persistent_term:put(?MODULE, State),
108 104 {reply, ok, State};
109 handle_call(Request, From, State) ->
110
:-(
?UNEXPECTED_CALL(Request, From),
111
:-(
{reply, {error, #{what => unexpected_call, request => Request}}, State}.
112
113 -spec handle_cast(any(), state()) -> {noreply, state()}.
114 handle_cast(Msg, State) ->
115
:-(
?UNEXPECTED_CAST(Msg),
116
:-(
{noreply, State}.
117
118 -spec handle_info(any(), state()) -> {noreply, state()}.
119 handle_info(Info, State) ->
120
:-(
?UNEXPECTED_INFO(Info),
121
:-(
{noreply, State}.
122
123 -spec terminate(any(), state()) -> ok.
124 terminate(_Reason, _State) ->
125 105 persistent_term:erase(?MODULE),
126 105 ok.
127
128 -spec code_change(any(), state(), any()) -> {ok, state()}.
129 code_change(_OldVsn, State, _Extra) ->
130
:-(
{ok, State}.
131
132 %% Internal functions
133
134 -spec update_if_persisted(state(), state()) -> ok.
135 update_if_persisted(State, NewState) ->
136
:-(
try persistent_term:get(?MODULE) of
137
:-(
State -> persistent_term:put(?MODULE, NewState)
138 catch
139
:-(
error:badarg -> ok
140 end.
141
142 -spec set_up_and_register(event_name(), labels(), config(), state()) -> state() | {error, map()}.
143 set_up_and_register(EventName, Labels, Config, State) ->
144
:-(
LabelKeys = label_keys(Labels),
145
:-(
case State of
146 #{EventName := {LabelKeys, #{Labels := _}}} ->
147
:-(
{error, #{what => event_already_registered,
148 event_name => EventName, labels => Labels}};
149 #{EventName := {LabelKeys, M}} ->
150
:-(
Handlers = do_set_up(EventName, Labels, Config),
151
:-(
State#{EventName := {LabelKeys, M#{Labels => Handlers}}};
152 #{EventName := {OtherKeys, _}} ->
153
:-(
{error, #{what => inconsistent_labels,
154 event_name => EventName, labels => Labels, existing_label_keys => OtherKeys}};
155 #{} ->
156
:-(
Handlers = do_set_up(EventName, Labels, Config),
157
:-(
State#{EventName => {LabelKeys, #{Labels => Handlers}}}
158 end.
159
160 -spec do_set_up(event_name(), labels(), config()) -> handlers().
161 do_set_up(EventName, Labels, Config) ->
162
:-(
AllModules = handler_modules(),
163
:-(
UsedModules = lists:filter(fun(Mod) -> Mod:set_up(EventName, Labels, Config) end, AllModules),
164
:-(
{[fun Mod:handle_event/4 || Mod <- UsedModules], Config}.
165
166 -spec deregister(event_name(), labels(), state()) -> state().
167 deregister(EventName, Labels, State) ->
168
:-(
case State of
169 #{EventName := {LabelKeys, M}} ->
170
:-(
case maps:remove(Labels, M) of
171 Empty when Empty =:= #{} ->
172
:-(
maps:remove(EventName, State);
173 NewM ->
174
:-(
State#{EventName := {LabelKeys, NewM}}
175 end;
176 #{} ->
177
:-(
State
178 end.
179
180 -spec lookup(event_name(), labels()) -> {ok, handlers()} | {error, not_found}.
181 lookup(EventName, Labels) ->
182
:-(
try persistent_term:get(?MODULE) of
183 #{EventName := {_, #{Labels := Handlers}}} ->
184
:-(
{ok, Handlers};
185 _ ->
186
:-(
{error, not_found}
187 catch
188 error:badarg ->
189
:-(
{error, not_found}
190 end.
191
192 -spec label_keys(labels()) -> [label_key()].
193 label_keys(Labels) ->
194
:-(
lists:sort(maps:keys(Labels)).
195
196 -spec get_handlers(event_name(), labels()) -> handlers().
197 get_handlers(EventName, Labels) ->
198
:-(
case lookup(EventName, Labels) of
199 {ok, Handlers} ->
200
:-(
Handlers;
201 {error, not_found} ->
202
:-(
error(#{what => event_not_registered, event_name => EventName, labels => Labels})
203 end.
204
205 -spec handle_event(event_name(), labels(), measurements(), handlers()) -> ok.
206 handle_event(Event, Labels, Measurements, {EventHandlers, Config}) ->
207
:-(
lists:foreach(fun(Handler) -> Handler(Event, Labels, Config, Measurements) end, EventHandlers).
208
209 -spec handler_modules() -> [module()].
210 handler_modules() ->
211
:-(
[list_to_existing_atom("mongoose_instrument_" ++ atom_to_list(Key))
212
:-(
|| Key <- maps:keys(mongoose_config:get_opt(instrumentation))].
Line Hits Source