./ct_report/coverage/mongoose_instrument.COVER.html

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