Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit b7bffef

Browse files
authoredJul 9, 2018
Merge 9b5a91f into 521a142
2 parents 521a142 + 9b5a91f commit b7bffef

7 files changed

+501
-6
lines changed
 

Diff for: ‎rebar.config

+2-1
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,8 @@
126126
{fed1, [{relx, [ {overlay_vars, ["rel/vars.config", "rel/fed1.vars.config"]},
127127
{overlay, [{template, "rel/files/ejabberd.cfg", "etc/ejabberd.cfg"}]} ]}]},
128128
{reg1, [{relx, [ {overlay_vars, ["rel/vars.config", "rel/reg1.vars.config"]},
129-
{overlay, [{template, "rel/files/ejabberd.cfg", "etc/ejabberd.cfg"}]} ]}]}
129+
{overlay, [{template, "rel/files/ejabberd.cfg", "etc/ejabberd.cfg"}]} ]}]},
130+
{test, [{deps, [ {gun, "1.0.0-pre.5"}]}]}
130131
]}.
131132

132133
{plugins,

Diff for: ‎src/ejabberd_cowboy.erl

+73-5
Original file line numberDiff line numberDiff line change
@@ -130,8 +130,9 @@ do_start_cowboy(Ref, Opts) ->
130130
TransportOpts = gen_mod:get_opt(transport_options, Opts, []),
131131
Modules = gen_mod:get_opt(modules, Opts),
132132
Dispatch = cowboy_router:compile(get_routes(Modules)),
133-
ProtocolOpts = [{env, [{dispatch, Dispatch}]} |
134-
gen_mod:get_opt(protocol_options, Opts, [])],
133+
{MetricsEnv, MetricsProtoOpts} = maybe_init_metrics(Opts),
134+
ProtocolOpts = [{env, [{dispatch, Dispatch} | MetricsEnv]} |
135+
gen_mod:get_opt(protocol_options, Opts, [])] ++ MetricsProtoOpts,
135136
case catch start_http_or_https(SSLOpts, Ref, NumAcceptors, TransportOpts, ProtocolOpts) of
136137
{error, {{shutdown,
137138
{failed_to_start_child, ranch_acceptors_sup,
@@ -180,7 +181,9 @@ get_routes([], Routes) ->
180181
Routes;
181182
get_routes([{Host, BasePath, Module} | Tail], Routes) ->
182183
get_routes([{Host, BasePath, Module, []} | Tail], Routes);
183-
get_routes([{Host, BasePath, Module, Opts} | Tail], Routes) ->
184+
get_routes([{Host, BasePath, Module, HandlerOpts} | Tail], Routes) ->
185+
get_routes([{Host, BasePath, Module, HandlerOpts, []} | Tail], Routes);
186+
get_routes([{Host, BasePath, Module, HandlerOpts, _Opts} | Tail], Routes) ->
184187
%% ejabberd_config tries to expand the atom '_' as a Macro, which fails.
185188
%% To work around that, use "_" instead and translate it to '_' here.
186189
CowboyHost = case Host of
@@ -190,8 +193,8 @@ get_routes([{Host, BasePath, Module, Opts} | Tail], Routes) ->
190193
{module, Module} = code:ensure_loaded(Module),
191194
Paths = proplists:get_value(CowboyHost, Routes, []) ++
192195
case erlang:function_exported(Module, cowboy_router_paths, 2) of
193-
true -> Module:cowboy_router_paths(BasePath, Opts);
194-
_ -> [{BasePath, Module, Opts}]
196+
true -> Module:cowboy_router_paths(BasePath, HandlerOpts);
197+
_ -> [{BasePath, Module, HandlerOpts}]
195198
end,
196199
get_routes(Tail, lists:keystore(CowboyHost, 1, Routes, {CowboyHost, Paths})).
197200

@@ -224,3 +227,68 @@ maybe_insert_max_connections(TransportOpts, Opts) ->
224227
NewTuple = {Key, Value},
225228
lists:keystore(Key, 1, TransportOpts, NewTuple)
226229
end.
230+
231+
-spec measured_methods() -> [mongoose_cowboy_metrics:method()].
232+
measured_methods() ->
233+
[<<"GET">>,
234+
<<"HEAD">>,
235+
<<"POST">>,
236+
<<"PUT">>,
237+
<<"DELETE">>,
238+
<<"OPTIONS">>,
239+
<<"PATCH">>].
240+
241+
-spec measured_classes() -> [mongoose_cowboy_metrics:status_class()].
242+
measured_classes() ->
243+
[<<"1XX">>, <<"2XX">>, <<"3XX">>, <<"4XX">>, <<"5XX">>].
244+
245+
base_metrics_prefix() ->
246+
[http].
247+
248+
middlewares_with_metrics() ->
249+
[mongoose_cowboy_metrics_mw_before,
250+
cowboy_router,
251+
mongoose_cowboy_metrics_mw_after,
252+
cowboy_handler].
253+
254+
-spec maybe_init_metrics(list()) -> {MetricsEnv :: list(), MetricsProtocolOpts :: list()}.
255+
maybe_init_metrics(Opts) ->
256+
case proplists:get_value(metrics, Opts, false) of
257+
true ->
258+
BasePrefix = base_metrics_prefix(),
259+
HandlerToPrefixMappings = build_metric_prefixes(
260+
BasePrefix, proplists:get_value(modules, Opts, []), #{}),
261+
[create_metrics(Prefix) || Prefix <- maps:values(HandlerToPrefixMappings)],
262+
{[{record_metrics, true}, {handler_to_metric_prefix, HandlerToPrefixMappings}],
263+
[{middlewares, middlewares_with_metrics()}]};
264+
false ->
265+
{[], []}
266+
end.
267+
268+
-spec build_metric_prefixes(BasePrefix :: list(), Modules :: [tuple()], Acc) -> Acc
269+
when Acc :: #{module() => mongoose_cowboy_metrics:prefix()}.
270+
build_metric_prefixes(_BasePrefix, [], Acc) ->
271+
Acc;
272+
build_metric_prefixes(BasePrefix, [{_Host, _Path, Handler, _HandlerOpts, Opts} | Tail], Acc) ->
273+
case proplists:get_value(metrics, Opts, []) of
274+
MetricsOpts when is_list(MetricsOpts) ->
275+
HandlerPrefix = proplists:get_value(prefix, MetricsOpts, Handler),
276+
Prefix = BasePrefix ++ lists:flatten([HandlerPrefix]),
277+
build_metric_prefixes(BasePrefix, Tail, maps:put(Handler, Prefix, Acc));
278+
false ->
279+
build_metric_prefixes(BasePrefix, Tail, Acc)
280+
end;
281+
build_metric_prefixes(BasePrefix, [{Host, Path, Handler, HandlerOpts} | Tail], Acc) ->
282+
build_metric_prefixes(BasePrefix, [{Host, Path, Handler, HandlerOpts, []} | Tail], Acc);
283+
build_metric_prefixes(BasePrefix, [{Host, Path, Handler} | Tail], Acc) ->
284+
build_metric_prefixes(BasePrefix, [{Host, Path, Handler, [], []} | Tail], Acc).
285+
286+
-spec create_metrics(mongoose_cowboy_metrics:prefix()) -> any().
287+
create_metrics(Prefix) ->
288+
CountMetrics = [mongoose_cowboy_metrics:request_count_metric(Prefix, M) || M <- measured_methods()] ++
289+
[mongoose_cowboy_metrics:response_count_metric(Prefix, M, C)
290+
|| M <- measured_methods(), C <- measured_classes()],
291+
LatencyMetrics = [mongoose_cowboy_metrics:response_latency_metric(Prefix, M, C)
292+
|| M <- measured_methods(), C <- measured_classes()],
293+
[mongoose_metrics:ensure_metric(global, M, spiral) || M <- CountMetrics],
294+
[mongoose_metrics:ensure_metric(global, M, histogram) || M <- LatencyMetrics].

Diff for: ‎src/mongoose_cowboy_metrics.erl

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
%%==============================================================================
2+
%% Copyright 2016 Erlang Solutions Ltd.
3+
%%
4+
%% Licensed under the Apache License, Version 2.0 (the "License");
5+
%% you may not use this file except in compliance with the License.
6+
%% You may obtain a copy of the License at
7+
%%
8+
%% http://www.apache.org/licenses/LICENSE-2.0
9+
%%
10+
%% Unless required by applicable law or agreed to in writing, software
11+
%% distributed under the License is distributed on an "AS IS" BASIS,
12+
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
%% See the License for the specific language governing permissions and
14+
%% limitations under the License.
15+
%%
16+
%% @doc Functions for generating names of metric's updated by `mongoose_cowboy_metrics_mw_after'
17+
%%
18+
%% To generate metric names use `request_count_metric/2', `response_latency_metric/2' and
19+
%% `response_count_metric/3'. See `mongoose_cowboy_metric_mw_after' module to check what values
20+
%% `Prefix', `Method' and `Class' may take.
21+
%%
22+
%%==============================================================================
23+
24+
-module(mongoose_cowboy_metrics).
25+
26+
%% API
27+
-export([request_count_metric/2]).
28+
-export([response_count_metric/3]).
29+
-export([response_latency_metric/3]).
30+
31+
-type prefix() :: list().
32+
-type method() :: binary(). %% <<"GET">>, <<"POST">>, etc.
33+
-type status_class() :: binary(). %% <<"2XX">>, <<"4XX">>, etc.
34+
-type metric_name() :: list().
35+
36+
-export_type([prefix/0]).
37+
-export_type([method/0]).
38+
-export_type([status_class/0]).
39+
-export_type([metric_name/0]).
40+
41+
%%-------------------------------------------------------------------
42+
%% API
43+
%%-------------------------------------------------------------------
44+
45+
-spec request_count_metric(prefix(), method()) -> metric_name().
46+
request_count_metric(Prefix, Method) ->
47+
Prefix ++ [Method, request, count].
48+
49+
-spec response_count_metric(prefix(), method(), status_class()) -> metric_name().
50+
response_count_metric(Prefix, Method, Class) ->
51+
Prefix ++ [Method, response, Class, count].
52+
53+
-spec response_latency_metric(prefix(), method(), status_class()) -> metric_name().
54+
response_latency_metric(Prefix, Method, Class) ->
55+
Prefix ++ [Method, response, Class, latency].

Diff for: ‎src/mongoose_cowboy_metrics_mw_after.erl

+121
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
%%==============================================================================
2+
%% Copyright 2016 Erlang Solutions Ltd.
3+
%%
4+
%% Licensed under the Apache License, Version 2.0 (the "License");
5+
%% you may not use this file except in compliance with the License.
6+
%% You may obtain a copy of the License at
7+
%%
8+
%% http://www.apache.org/licenses/LICENSE-2.0
9+
%%
10+
%% Unless required by applicable law or agreed to in writing, software
11+
%% distributed under the License is distributed on an "AS IS" BASIS,
12+
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
%% See the License for the specific language governing permissions and
14+
%% limitations under the License.
15+
%%
16+
%% @doc Cowboy middleware updating metrics related to request handling
17+
%%
18+
%% This middleware needs to run after `mongoose_cowboy_metrics_mw_before' and `cowboy_router'
19+
%% middleware. However, it does not need to run after `cowboy_hander' middleware because metrics are
20+
%% updated in `onresponse' callback which is called whenever the request is sent.
21+
%%
22+
%% It is executed only if listener's env variable `record_metrics' is set to `true'.
23+
%%
24+
%% This middleware does not create any metrics, they need to be created earlier using
25+
%% `mongoose_metrics' module. Metric names used by the middleware are contructed as follows:
26+
%% - take some `Prefix', a list
27+
%% - take a request method, `Method', one of:
28+
%% - `<<"GET">>'
29+
%% - `<<"HEAD">>'
30+
%% - `<<"POST">>'
31+
%% - `<<"PUT">>'
32+
%% - `<<"DELETE">>'
33+
%% - `<<"OPTIONS">>'
34+
%% - `<<"PATCH">>'
35+
%% - take a request status class, `Class', and create a string like e.g. `<<"2XX">>' for success
36+
%% status codes
37+
%% - for each `Method' and `Class' define following metric names
38+
%% - `Prefix ++ [Method, request, count]' - updated by `1' whenever a request with method `Method'
39+
%% is about to be handled
40+
%% - `Prefix ++ [Method, response, Class, count]' - updated by `1' whenever a response of status
41+
%% class `Class' to a request with method `Method' is sent
42+
%% - `Prefix ++ [Method, response, Class, latency]' - updated by number of microseconds which
43+
%% passed since request timestamp was recorded by `mongoose_cowboy_metrics_mw_before' whenever
44+
%% a response of status class `Class' to a request with method `Method' is sent
45+
%%
46+
%% As you might have already guessed it makes sense to define `count' metrics as spirals, and
47+
%% `latency' metrics as histograms. The middleware will always try to update the metric regardless
48+
%% of whether it was created. Note that it's run after `cowboy_router' middleware, which means that
49+
%% error responses returned by the router (such as 404 for no matching handler) won't be recorded.
50+
%%
51+
%% And what about `Prefix'? By default prefix is the name of the handler handling the
52+
%% request wrapped in a list. However, you might provide `handler_to_metric_prefix' map as Cowboy
53+
%% listener environment value, where keys are handler names and values are corresponding prefixes.
54+
%%
55+
%% You can use functions from `mongoose_cowboy_metrics' module to generate names of metrics recorded
56+
%% by this module.
57+
%%
58+
%%==============================================================================
59+
60+
-module(mongoose_cowboy_metrics_mw_after).
61+
62+
-behaviour(cowboy_middleware).
63+
64+
%% cowboy_middleware callbacks
65+
-export([execute/2]).
66+
67+
%%-------------------------------------------------------------------
68+
%% cowboy_middleware callbacks
69+
%%-------------------------------------------------------------------
70+
71+
execute(Req, Env) ->
72+
case proplists:get_value(record_metrics, Env, false) of
73+
true ->
74+
{req_timestamp, StartTs} = proplists:lookup(req_timestamp, Env),
75+
{handler, Handler} = proplists:lookup(handler, Env),
76+
Method = get_req_method(Req),
77+
HandlerToPrefixMappings = proplists:get_value(handler_to_metric_prefix, Env, #{}),
78+
Prefix = maps:get(Handler, HandlerToPrefixMappings, [Handler]),
79+
mongoose_metrics:update(global, mongoose_cowboy_metrics:request_count_metric(Prefix, Method), 1),
80+
OnResponse = on_response_fun(StartTs, Method, Prefix),
81+
{ok, cowboy_req:set([{onresponse, OnResponse}], Req), Env};
82+
false ->
83+
{ok, Req, Env}
84+
end.
85+
86+
%%-------------------------------------------------------------------
87+
%% Internals
88+
%%-------------------------------------------------------------------
89+
90+
-spec on_response_fun(erlang:timestamp(), mongoose_cowboy_metrics:method(),
91+
mongoose_cowboy_metrics:prefix()) -> cowboy:onresponse_fun().
92+
on_response_fun(StartTs, Method, Prefix) ->
93+
fun(Status, _Headers, _Body, RespReq) ->
94+
EndTs = erlang:timestamp(),
95+
Latency = calculate_latency(StartTs, EndTs),
96+
Class = calculate_status_class(Status),
97+
mongoose_metrics:update(global, mongoose_cowboy_metrics:response_count_metric(Prefix, Method, Class), 1),
98+
mongoose_metrics:update(global, mongoose_cowboy_metrics:response_latency_metric(Prefix, Method, Class), Latency),
99+
RespReq
100+
end.
101+
102+
-spec calculate_latency(erlang:timestamp(), erlang:timestamp()) -> Microsecs :: non_neg_integer().
103+
calculate_latency(StartTs, EndTs) ->
104+
timestamp_to_microsecs(EndTs) - timestamp_to_microsecs(StartTs).
105+
106+
-spec timestamp_to_microsecs(erlang:timestamp()) -> Microsecs :: non_neg_integer().
107+
timestamp_to_microsecs({MegaSecs, Secs, MicroSecs}) ->
108+
(MegaSecs * 1000000 + Secs) * 1000000 + MicroSecs.
109+
110+
-spec get_req_method(cowboy_req:req()) -> mongoose_cowboy_metrics:method().
111+
get_req_method(Req) ->
112+
{Method, _} = cowboy_req:method(Req),
113+
Method.
114+
115+
-spec calculate_status_class(100..599) -> mongoose_cowboy_metrics:status_class().
116+
calculate_status_class(S) when S >= 100, S < 200 -> <<"1XX">>;
117+
calculate_status_class(S) when S >= 200, S < 300 -> <<"2XX">>;
118+
calculate_status_class(S) when S >= 300, S < 400 -> <<"3XX">>;
119+
calculate_status_class(S) when S >= 400, S < 500 -> <<"4XX">>;
120+
calculate_status_class(S) when S >= 500, S < 600 -> <<"5XX">>.
121+

Diff for: ‎src/mongoose_cowboy_metrics_mw_before.erl

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
%%==============================================================================
2+
%% Copyright 2016 Erlang Solutions Ltd.
3+
%%
4+
%% Licensed under the Apache License, Version 2.0 (the "License");
5+
%% you may not use this file except in compliance with the License.
6+
%% You may obtain a copy of the License at
7+
%%
8+
%% http://www.apache.org/licenses/LICENSE-2.0
9+
%%
10+
%% Unless required by applicable law or agreed to in writing, software
11+
%% distributed under the License is distributed on an "AS IS" BASIS,
12+
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
%% See the License for the specific language governing permissions and
14+
%% limitations under the License.
15+
%%
16+
%% @doc Cowboy middleware, one of the two responsible for recoding HTTP API metrics
17+
%%
18+
%% The job of this middleware is to record the timestamp of when the request comes in and to set.
19+
%%
20+
%% It's executed only if listener's env variable `record_metrics' is set to `true'.
21+
%%
22+
%% This middleware should be placed as soon as possible in the middleware chain, so that request
23+
%% timestamp of the request will be captured as quickly as possible.
24+
%%
25+
%%==============================================================================
26+
27+
-module(mongoose_cowboy_metrics_mw_before).
28+
29+
-behaviour(cowboy_middleware).
30+
31+
%% cowboy_middleware callbacks
32+
-export([execute/2]).
33+
34+
%%-------------------------------------------------------------------
35+
%% cowboy_middleware callbacks
36+
%%-------------------------------------------------------------------
37+
38+
execute(Req, Env) ->
39+
case proplists:get_value(record_metrics, Env, false) of
40+
true ->
41+
Ts = erlang:timestamp(),
42+
{ok, Req, [{req_timestamp, Ts} | Env]};
43+
false ->
44+
{ok, Req, Env}
45+
end.
46+

Diff for: ‎test/mongoose_cowboy_metrics_SUITE.erl

+201
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
-module(mongoose_cowboy_metrics_SUITE).
2+
-compile(export_all).
3+
4+
-include_lib("eunit/include/eunit.hrl").
5+
6+
-import(mongoose_cowboy_metrics, [request_count_metric/2,
7+
response_count_metric/2,
8+
response_count_metric/3,
9+
response_latency_metric/3]).
10+
11+
-define(LISTENER, mongoose_cowboy_metrics_SUITE_listener).
12+
-define(HANDLER, ?MODULE).
13+
-define(METHODS, [<<"GET">>,
14+
<<"HEAD">>,
15+
<<"POST">>,
16+
<<"PUT">>,
17+
<<"DELETE">>,
18+
<<"OPTIONS">>,
19+
<<"PATCH">>]).
20+
-define(DEFAULT_PREFIX, [?HANDLER]).
21+
-define(CLASSES, [<<"1XX">>, <<"2XX">>, <<"3XX">>, <<"4XX">>, <<"5XX">>]).
22+
23+
24+
25+
%%-------------------------------------------------------------------
26+
%% Suite definition
27+
%%-------------------------------------------------------------------
28+
29+
all() ->
30+
[metrics_are_not_recorded_by_default,
31+
metrics_are_not_recorded_when_record_metrics_is_false,
32+
metrics_are_recorded,
33+
metrics_are_recorded_with_configured_prefix,
34+
unsent_responses_generate_metrics
35+
].
36+
37+
%%-------------------------------------------------------------------
38+
%% Init & teardown
39+
%%-------------------------------------------------------------------
40+
41+
init_per_suite(Config) ->
42+
application:ensure_all_started(gun),
43+
ejabberd_helper:start_ejabberd_with_config(Config, "ejabberd.cfg"),
44+
Config.
45+
46+
end_per_suite(_Config) ->
47+
application:stop(gun),
48+
ejabberd_helper:stop_ejabberd(),
49+
ok.
50+
51+
%%-------------------------------------------------------------------
52+
%% Test cases
53+
%%-------------------------------------------------------------------
54+
55+
metrics_are_not_recorded_by_default(_Config) ->
56+
create_metrics(),
57+
start_listener([{'_', ?HANDLER, []}], []),
58+
59+
metrics_are_not_recorded(),
60+
61+
stop_listener(),
62+
destroy_metrics().
63+
64+
metrics_are_not_recorded_when_record_metrics_is_false(_Config) ->
65+
create_metrics(),
66+
start_listener([{'_', ?HANDLER, []}], [{record_metrics, false}]),
67+
68+
metrics_are_not_recorded(),
69+
70+
stop_listener(),
71+
destroy_metrics().
72+
73+
metrics_are_not_recorded() ->
74+
run_requests([get, head, post, put, delete, options, patch],
75+
[<<"100">>, <<"200">>, <<"300">>, <<"400">>, <<"500">>]),
76+
[ensure_metric_value(M, 0)
77+
|| M <- count_metrics(?DEFAULT_PREFIX) ++ latency_metrics(?DEFAULT_PREFIX)].
78+
79+
metrics_are_recorded(_Config) ->
80+
create_metrics(),
81+
start_listener([{'_', ?HANDLER, []}], [{record_metrics, true}]),
82+
83+
timer:sleep(1000),
84+
run_requests([get, head, post, put, delete, options, patch],
85+
[<<"100">>, <<"200">>, <<"300">>, <<"400">>, <<"500">>]),
86+
87+
[ensure_metric_value(request_count_metric(?DEFAULT_PREFIX, M), 5) || M <- ?METHODS],
88+
[ensure_metric_value(response_count_metric(?DEFAULT_PREFIX, M, C), 1) || M <- ?METHODS, C <- ?CLASSES],
89+
[ensure_metric_bumped(response_latency_metric(?DEFAULT_PREFIX, M, C)) || M <- ?METHODS, C <- ?CLASSES],
90+
91+
stop_listener(),
92+
destroy_metrics().
93+
94+
95+
metrics_are_recorded_with_configured_prefix(_Config) ->
96+
Prefix = [http, api, my_endpoint],
97+
create_metrics(Prefix),
98+
start_listener([{'_', ?HANDLER, []}], [{record_metrics, true},
99+
{handler_to_metric_prefix, #{?HANDLER => Prefix}}]),
100+
101+
run_requests([get, head, post, put, delete, options, patch],
102+
[<<"100">>, <<"200">>, <<"300">>, <<"400">>, <<"500">>]),
103+
104+
[ensure_metric_value(request_count_metric(Prefix, M), 5) || M <- ?METHODS],
105+
[ensure_metric_value(response_count_metric(Prefix, M, C), 1) || M <- ?METHODS, C <- ?CLASSES],
106+
[ensure_metric_bumped(response_latency_metric(Prefix, M, C)) || M <- ?METHODS, C <- ?CLASSES],
107+
108+
stop_listener(),
109+
destroy_metrics(Prefix).
110+
111+
unsent_responses_generate_metrics(_Config) ->
112+
create_metrics(),
113+
start_listener([{'_', ?HANDLER, [{action, none}]}], [{record_metrics, true}]),
114+
115+
run_requests([get, head, post, put, delete, options, patch],
116+
[<<"100">>, <<"200">>, <<"300">>, <<"400">>, <<"500">>]),
117+
118+
%% when response is not sent Cowboy returns 204
119+
[ensure_metric_value(request_count_metric(?DEFAULT_PREFIX, M), 5) || M <- ?METHODS],
120+
[ensure_metric_value(response_count_metric(?DEFAULT_PREFIX, M, <<"2XX">>), 5) || M <- ?METHODS],
121+
[ensure_metric_value(response_count_metric(?DEFAULT_PREFIX, M, C), 0) || M <- ?METHODS, C <- ?CLASSES -- [<<"2XX">>]],
122+
[ensure_metric_bumped(response_latency_metric(?DEFAULT_PREFIX, M, <<"2XX">>)) || M <- ?METHODS],
123+
[ensure_metric_value(response_latency_metric(?DEFAULT_PREFIX, M, C), 0) || M <- ?METHODS, C <- ?CLASSES -- [<<"2XX">>]],
124+
125+
stop_listener(),
126+
destroy_metrics().
127+
128+
%%-------------------------------------------------------------------
129+
%% cowboy handler callbacks
130+
%%-------------------------------------------------------------------
131+
132+
init(_Type, Req, Opts) ->
133+
{ok, Req, proplists:get_value(action, Opts, echo_status)}.
134+
135+
handle(Req, echo_status = State) ->
136+
{BinStatus, Req1} = cowboy_req:qs_val(<<"status">>, Req),
137+
Status = binary_to_integer(BinStatus),
138+
{ok, RespReq} = cowboy_req:reply(Status, [], <<>>, Req1),
139+
{ok, RespReq, State};
140+
handle(Req, none = State) ->
141+
{ok, Req, State}.
142+
143+
terminate(_Reason, _Req, _State) ->
144+
ok.
145+
146+
%%-------------------------------------------------------------------
147+
%% Helpers
148+
%%-------------------------------------------------------------------
149+
150+
start_listener(Paths, ExtraEnv) ->
151+
Dispatch = cowboy_router:compile([{'_', Paths}]),
152+
Middleware = [mongoose_cowboy_metrics_mw_before,
153+
cowboy_router,
154+
mongoose_cowboy_metrics_mw_after,
155+
cowboy_handler],
156+
{ok, _} = cowboy:start_http(?LISTENER, 1, [{port, 0}],
157+
[{env, [{dispatch, Dispatch} | ExtraEnv]},
158+
{middlewares, Middleware}]).
159+
160+
stop_listener() ->
161+
ok = cowboy:stop_listener(?LISTENER).
162+
163+
create_metrics() ->
164+
create_metrics(?DEFAULT_PREFIX).
165+
166+
create_metrics(Prefix) ->
167+
%% in case of request count we're interested in the exact values - let's use counter
168+
[ok = mongoose_metrics:ensure_metric(global, M, counter) || M <- count_metrics(Prefix)],
169+
%% in case of latencies it doesn't make sense to check if values are correct - we
170+
%% only need to know if the value has been reported, so gauge is enough
171+
[ok = mongoose_metrics:ensure_metric(global, M, gauge) || M <- latency_metrics(Prefix)].
172+
173+
destroy_metrics() ->
174+
destroy_metrics(?DEFAULT_PREFIX).
175+
176+
destroy_metrics(Prefix) ->
177+
[exometer:delete([global | M]) || M <- count_metrics(Prefix) ++ latency_metrics(Prefix)].
178+
179+
count_metrics(Prefix) ->
180+
[request_count_metric(Prefix, M) || M <- ?METHODS] ++
181+
[response_count_metric(Prefix, M, C) || M <- ?METHODS, C <- ?CLASSES].
182+
183+
latency_metrics(Prefix) ->
184+
[response_latency_metric(Prefix, M, C) || M <- ?METHODS, C <- ?CLASSES].
185+
186+
ensure_metric_value(Metric, Value) ->
187+
{ok, DataPoints} = mongoose_metrics:get_metric_value([global | Metric]),
188+
?assertEqual(Value, proplists:get_value(value, DataPoints)).
189+
190+
ensure_metric_bumped(Metric) ->
191+
{ok, DataPoints} = mongoose_metrics:get_metric_value([global | Metric]),
192+
?assertNotEqual(0, proplists:get_value(value, DataPoints)).
193+
194+
run_requests(Methods, Statuses) ->
195+
{ok, Conn} = gun:open("localhost", ranch:get_port(?LISTENER)),
196+
{ok, _} = gun:await_up(Conn),
197+
[begin
198+
StreamRef = gun:M(Conn, <<"/?status=", S/binary>>, []),
199+
{response, fin, _, _} = gun:await(Conn, StreamRef)
200+
end || M <- Methods, S <- Statuses].
201+

Diff for: ‎test/mongoose_cowboy_metrics_SUITE_data/ejabberd.cfg

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{hosts, ["localhost"]}.
2+
3+
{sm_backend, {mnesia, []}}.

0 commit comments

Comments
 (0)
Please sign in to comment.