Skip to content

Commit 942845e

Browse files
committed
Define HTTP metrics when starting ejabberd_cowboy
1 parent bdbf593 commit 942845e

File tree

1 file changed

+73
-5
lines changed

1 file changed

+73
-5
lines changed

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].

0 commit comments

Comments
 (0)