./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 100 gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
45
46 -spec reload() -> ok.
47 reload() ->
48 100 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() => #{labels() => handlers()}}.
88
89 -spec init([]) -> {ok, state()}.
90 init([]) ->
91 100 erlang:process_flag(trap_exit, true), %% Make sure that terminate is called
92 100 {ok, #{}}.
93
94 -spec handle_call(any(), gen_server:from(), state()) -> {reply, ok | {error, map()}, state()}.
95 handle_call({set_up, EventName, Labels, Config}, _From, State) ->
96
:-(
case set_up_and_register(EventName, Labels, Config, State) of
97 {error, _} = Error ->
98
:-(
{reply, Error, State};
99 NewState = #{} ->
100
:-(
update_if_persisted(State, NewState),
101
:-(
{reply, ok, NewState}
102 end;
103 handle_call({tear_down, EventName, Labels}, _From, State) ->
104
:-(
NewState = deregister(EventName, Labels, State),
105
:-(
update_if_persisted(State, NewState),
106
:-(
{reply, ok, NewState};
107 handle_call(reload, _From, State) ->
108 100 persistent_term:put(?MODULE, State),
109 100 {reply, ok, State};
110 handle_call(Request, From, State) ->
111
:-(
?UNEXPECTED_CALL(Request, From),
112
:-(
{reply, {error, #{what => unexpected_call, request => Request}}, State}.
113
114 -spec handle_cast(any(), state()) -> {noreply, state()}.
115 handle_cast(Msg, State) ->
116
:-(
?UNEXPECTED_CAST(Msg),
117
:-(
{noreply, State}.
118
119 -spec handle_info(any(), state()) -> {noreply, state()}.
120 handle_info(Info, State) ->
121
:-(
?UNEXPECTED_INFO(Info),
122
:-(
{noreply, State}.
123
124 -spec terminate(any(), state()) -> ok.
125 terminate(_Reason, _State) ->
126 100 persistent_term:erase(?MODULE),
127 100 ok.
128
129 -spec code_change(any(), state(), any()) -> {ok, state()}.
130 code_change(_OldVsn, State, _Extra) ->
131
:-(
{ok, State}.
132
133 %% Internal functions
134
135 -spec update_if_persisted(state(), state()) -> ok.
136 update_if_persisted(State, NewState) ->
137
:-(
try persistent_term:get(?MODULE) of
138
:-(
State -> persistent_term:put(?MODULE, NewState)
139 catch
140
:-(
error:badarg -> ok
141 end.
142
143 -spec set_up_and_register(event_name(), labels(), config(), state()) -> state() | {error, map()}.
144 set_up_and_register(EventName, Labels, Config, State) ->
145
:-(
LabelKeys = label_keys(Labels),
146
:-(
case State of
147 #{EventName := #{Labels := _}} ->
148
:-(
{error, #{what => event_already_registered,
149 event_name => EventName, labels => Labels}};
150 #{EventName := HandlerMap} ->
151
:-(
{ExistingLabels, _, _} = maps:next(maps:iterator(HandlerMap)),
152
:-(
case label_keys(ExistingLabels) of
153 LabelKeys ->
154
:-(
Handlers = do_set_up(EventName, Labels, Config),
155
:-(
State#{EventName := HandlerMap#{Labels => Handlers}};
156 ExistingKeys ->
157
:-(
{error, #{what => inconsistent_labels,
158 event_name => EventName, labels => Labels,
159 existing_label_keys => ExistingKeys}}
160 end;
161 #{} ->
162
:-(
Handlers = do_set_up(EventName, Labels, Config),
163
:-(
State#{EventName => #{Labels => Handlers}}
164 end.
165
166 -spec do_set_up(event_name(), labels(), config()) -> handlers().
167 do_set_up(EventName, Labels, Config) ->
168
:-(
AllModules = handler_modules(),
169
:-(
UsedModules = lists:filter(fun(Mod) -> Mod:set_up(EventName, Labels, Config) end, AllModules),
170
:-(
{[fun Mod:handle_event/4 || Mod <- UsedModules], Config}.
171
172 -spec deregister(event_name(), labels(), state()) -> state().
173 deregister(EventName, Labels, State) ->
174
:-(
case State of
175 #{EventName := HandlerMap} ->
176
:-(
case maps:remove(Labels, HandlerMap) of
177 Empty when Empty =:= #{} ->
178
:-(
maps:remove(EventName, State);
179 NewHandlerMap ->
180
:-(
State#{EventName := NewHandlerMap}
181 end;
182 #{} ->
183
:-(
State
184 end.
185
186 -spec lookup(event_name(), labels()) -> {ok, handlers()} | {error, not_found}.
187 lookup(EventName, Labels) ->
188
:-(
try persistent_term:get(?MODULE) of
189 #{EventName := #{Labels := Handlers}} ->
190
:-(
{ok, Handlers};
191 _ ->
192
:-(
{error, not_found}
193 catch
194 error:badarg ->
195 %% reload/0 not called yet - behave as if there were no registered events
196
:-(
{error, not_found}
197 end.
198
199 -spec label_keys(labels()) -> [label_key()].
200 label_keys(Labels) ->
201
:-(
lists:sort(maps:keys(Labels)).
202
203 -spec get_handlers(event_name(), labels()) -> handlers().
204 get_handlers(EventName, Labels) ->
205
:-(
case lookup(EventName, Labels) of
206 {ok, Handlers} ->
207
:-(
Handlers;
208 {error, not_found} ->
209
:-(
error(#{what => event_not_registered, event_name => EventName, labels => Labels})
210 end.
211
212 -spec handle_event(event_name(), labels(), measurements(), handlers()) -> ok.
213 handle_event(Event, Labels, Measurements, {EventHandlers, Config}) ->
214
:-(
lists:foreach(fun(Handler) -> Handler(Event, Labels, Config, Measurements) end, EventHandlers).
215
216 -spec handler_modules() -> [module()].
217 handler_modules() ->
218
:-(
[list_to_existing_atom("mongoose_instrument_" ++ atom_to_list(Key))
219
:-(
|| Key <- maps:keys(mongoose_config:get_opt(instrumentation))].
Line Hits Source