From 3e2337a818086f33f0a1ede5d204aee7744c7c36 Mon Sep 17 00:00:00 2001 From: Aleksey Kashapov Date: Fri, 12 Jan 2024 12:55:44 +0300 Subject: [PATCH] TD-788: Adds prometheus support for hackney per host metrics (#32) --- src/woody_hackney_prometheus.erl | 134 +++++++++++++++++++++++++++++++ test/woody_tests_SUITE.erl | 3 +- 2 files changed, 136 insertions(+), 1 deletion(-) create mode 100644 src/woody_hackney_prometheus.erl diff --git a/src/woody_hackney_prometheus.erl b/src/woody_hackney_prometheus.erl new file mode 100644 index 0000000..310a381 --- /dev/null +++ b/src/woody_hackney_prometheus.erl @@ -0,0 +1,134 @@ +%% Translates hackney metrics for prometheus. +%% See +%% https://github.com/benoitc/hackney/tree/1.18.0?tab=readme-ov-file#metrics +%% +%% For pool metrics see dedicated collector module +%% `woody_hackney_prometheus_collector`. +-module(woody_hackney_prometheus). + +-export([ + new/2, + delete/1, + increment_counter/1, + increment_counter/2, + decrement_counter/1, + decrement_counter/2, + update_histogram/2, + update_gauge/2, + update_meter/2 +]). + +-type name() :: any(). +-type metric() :: counter | histogram | gauge | meter. + +%% API + +-define(NB_REQUESTS, hackney_nb_requests). +-define(TOTAL_REQUESTS, hackney_total_requests). +-define(HOST_NB_REQUESTS, hackney_host_nb_requests). +-define(HOST_CONNECT_TIMEOUT, hackney_host_connect_timeout). +-define(HOST_CONNECT_ERROR, hackney_host_connect_error). +-define(HOST_NEW_CONNECTION, hackney_host_new_connection). +-define(HOST_REUSE_CONNECTION, hackney_host_reuse_connection). + +%% Ignore unsupported metric +-spec new(metric(), name()) -> ok. +%% Total counters +new(counter, [hackney, nb_requests]) -> + true = prometheus_gauge:declare([ + {name, ?NB_REQUESTS}, + {registry, registry()}, + {labels, []}, + {help, "Number of running requests."} + ]), + %% Per host counters, see + %% https://github.com/benoitc/hackney/tree/1.18.0?tab=readme-ov-file#metrics-per-hosts + %% NOTE Hackney won't call `metrics:new/3` for those counters + true = prometheus_gauge:declare([ + {name, ?HOST_NB_REQUESTS}, + {registry, registry()}, + {labels, [host]}, + {help, "Number of running requests."} + ]), + [ + true = prometheus_counter:declare([ + {name, Name}, + {registry, registry()}, + {labels, [host]}, + {help, Help} + ]) + || {Name, Help} <- [ + {?HOST_CONNECT_TIMEOUT, "Number of connect timeout."}, + {?HOST_CONNECT_ERROR, "Number of timeout errors."}, + {?HOST_NEW_CONNECTION, "Number of new pool connections per host."}, + {?HOST_REUSE_CONNECTION, "Number of reused pool connections per host."} + ] + ], + ok; +new(counter, [hackney, total_requests]) -> + true = prometheus_counter:declare([ + {name, ?TOTAL_REQUESTS}, + {registry, registry()}, + {labels, []}, + {help, "Total number of requests."} + ]), + ok; +new(_Type, _Name) -> + ok. + +-spec delete(name()) -> ok. +delete(_Name) -> + ok. + +-spec increment_counter(name()) -> ok. +increment_counter(Name) -> + increment_counter(Name, 1). + +-spec increment_counter(name(), pos_integer()) -> ok. +increment_counter([hackney, nb_requests], Value) -> + prometheus_gauge:inc(registry(), ?NB_REQUESTS, [], Value); +increment_counter([hackney, total_requests], Value) -> + prometheus_counter:inc(registry(), ?TOTAL_REQUESTS, [], Value); +increment_counter([hackney, Host, nb_requests], Value) -> + prometheus_gauge:inc(registry(), ?HOST_NB_REQUESTS, [{host, Host}], Value); +increment_counter([hackney, Host, connect_timeout], Value) -> + prometheus_counter:inc(registry(), ?HOST_CONNECT_TIMEOUT, [{host, Host}], Value); +increment_counter([hackney, Host, connect_error], Value) -> + prometheus_counter:inc(registry(), ?HOST_CONNECT_ERROR, [{host, Host}], Value); +increment_counter([hackney_pool, Host, new_connection], Value) -> + prometheus_counter:inc(registry(), ?HOST_NEW_CONNECTION, [{host, Host}], Value); +increment_counter([hackney_pool, Host, reuse_connection], Value) -> + prometheus_counter:inc(registry(), ?HOST_REUSE_CONNECTION, [{host, Host}], Value); +increment_counter(_Name, _Value) -> + ok. + +-spec decrement_counter(name()) -> ok. +decrement_counter(Name) -> + decrement_counter(Name, 1). + +-spec decrement_counter(name(), pos_integer()) -> ok. +decrement_counter([hackney, nb_requests], Value) -> + prometheus_gauge:dec(registry(), ?NB_REQUESTS, [], Value); +decrement_counter([hackney, Host, nb_requests], Value) -> + prometheus_gauge:dec(registry(), ?HOST_NB_REQUESTS, [{host, Host}], Value); +decrement_counter(_Name, _Value) -> + ok. + +-spec update_histogram(name(), fun(() -> ok) | number()) -> ok. +update_histogram(_Name, Fun) when is_function(Fun, 0) -> + Fun(); +update_histogram(_Name, _Value) -> + ok. + +-spec update_gauge(name(), number()) -> ok. +update_gauge(_Name, _Value) -> + ok. + +-spec update_meter(name(), number()) -> ok. +update_meter(_Name, _Value) -> + ok. + +%% + +registry() -> + default. diff --git a/test/woody_tests_SUITE.erl b/test/woody_tests_SUITE.erl index 79d1a5b..9de03d5 100644 --- a/test/woody_tests_SUITE.erl +++ b/test/woody_tests_SUITE.erl @@ -290,8 +290,9 @@ init_per_suite(C) -> % dbg:tracer(), dbg:p(all, c), % dbg:tpl({woody_joint_workers, '_', '_'}, x), %%Apps = genlib_app:start_application_with(woody, [{trace_http_server, true}]), - {ok, Apps} = application:ensure_all_started(woody), + ok = application:set_env(hackney, mod_metrics, woody_hackney_prometheus), {ok, MetricsApps} = application:ensure_all_started(prometheus), + {ok, Apps} = application:ensure_all_started(woody), ok = woody_ranch_prometheus_collector:setup(), ok = woody_hackney_prometheus_collector:setup(), {ok, OtelApps} = setup_opentelemetry(),