From de6c20437b931e1ceddf8a2070987080084d94b3 Mon Sep 17 00:00:00 2001 From: Richard Kallos Date: Tue, 23 May 2023 16:04:43 -0400 Subject: [PATCH 1/3] Add telemetry dep --- rebar.config | 3 ++- rebar.lock | 9 ++++++--- src/shackle.app.src | 2 +- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/rebar.config b/rebar.config index a3037df..d616bf8 100644 --- a/rebar.config +++ b/rebar.config @@ -7,7 +7,8 @@ {deps, [ {foil, "0.1.3"}, {granderl, {git, "https://github.com/tokenrove/granderl.git", {ref, "baafd1bc825cb1fc022760eae913f774fa6af91b"}}}, - {metal, "0.1.1"} + {metal, "0.1.1"}, + {telemetry, "1.2.1"} ]}. {dialyzer, [{plt_extra_apps, [public_key]}]}. diff --git a/rebar.lock b/rebar.lock index 63b703a..a106a08 100644 --- a/rebar.lock +++ b/rebar.lock @@ -4,12 +4,15 @@ {git,"https://github.com/tokenrove/granderl.git", {ref,"baafd1bc825cb1fc022760eae913f774fa6af91b"}}, 0}, - {<<"metal">>,{pkg,<<"metal">>,<<"0.1.1">>},0}]}. + {<<"metal">>,{pkg,<<"metal">>,<<"0.1.1">>},0}, + {<<"telemetry">>,{pkg,<<"telemetry">>,<<"1.2.1">>},0}]}. [ {pkg_hash,[ {<<"foil">>, <<"415835CA94A8D0A55AB3D334FE2D1A1DCF36E7A0F69789050765770B6BF5E6E9">>}, - {<<"metal">>, <<"5D3D1322DA7BCD34B94FED5486F577973685298883954F7A3E517EF5EF6953F5">>}]}, + {<<"metal">>, <<"5D3D1322DA7BCD34B94FED5486F577973685298883954F7A3E517EF5EF6953F5">>}, + {<<"telemetry">>, <<"68FDFE8D8F05A8428483A97D7AAB2F268AAFF24B49E0F599FAA091F1D4E7F61C">>}]}, {pkg_hash_ext,[ {<<"foil">>, <<"3A1CC0939075D7E26F8ED9983839E2E9CE4B2AE301110F7DF5A6EDA544FCD125">>}, - {<<"metal">>, <<"88B82B634998A1A768DEDCD372C2F7E657B19445325C0AF5CCBAC62C77210F1D">>}]} + {<<"metal">>, <<"88B82B634998A1A768DEDCD372C2F7E657B19445325C0AF5CCBAC62C77210F1D">>}, + {<<"telemetry">>, <<"DAD9CE9D8EFFC621708F99EAC538EF1CBE05D6A874DD741DE2E689C47FEAFED5">>}]} ]. diff --git a/src/shackle.app.src b/src/shackle.app.src index d34d446..3eaf998 100644 --- a/src/shackle.app.src +++ b/src/shackle.app.src @@ -1,5 +1,5 @@ {application, shackle, [ - {applications, [kernel, stdlib, granderl, metal, foil, ssl]}, + {applications, [kernel, stdlib, granderl, metal, foil, ssl, telemetry]}, {description, "High-Performance Erlang Network Client Framework"}, {env, []}, {licenses, ["MIT"]}, From 3dcf6b4bd16a680faa54a0fb38bbba3fec249eef Mon Sep 17 00:00:00 2001 From: Richard Kallos Date: Tue, 30 May 2023 11:14:46 -0400 Subject: [PATCH 2/3] Replace ?METRICS with calls to shackle_telemetry --- include/shackle_internal.hrl | 2 - src/shackle_hooks.erl | 44 ------------------- src/shackle_pool.erl | 6 +-- src/shackle_server.erl | 16 +++---- src/shackle_sup.erl | 1 - src/shackle_telemetry.erl | 85 ++++++++++++++++++++++++++++++++++++ 6 files changed, 96 insertions(+), 58 deletions(-) delete mode 100644 src/shackle_hooks.erl create mode 100644 src/shackle_telemetry.erl diff --git a/include/shackle_internal.hrl b/include/shackle_internal.hrl index ca2cac4..cc0731d 100644 --- a/include/shackle_internal.hrl +++ b/include/shackle_internal.hrl @@ -7,8 +7,6 @@ -define(GET_ENV(Key, Default), application:get_env(?APP, Key, Default)). -define(LOOKUP(Key, List), ?LOOKUP(Key, List, undefined)). -define(LOOKUP(Key, List, Default), shackle_utils:lookup(Key, List, Default)). --define(METRICS(Client, Type, Key), shackle_hooks:metrics(Client, Type, Key, 1)). --define(METRICS(Client, Type, Key, Value), shackle_hooks:metrics(Client, Type, Key, Value)). -define(MSG_CONNECT, connect). -define(SERVER, shackle_server). -define(SUPERVISOR, shackle_sup). diff --git a/src/shackle_hooks.erl b/src/shackle_hooks.erl deleted file mode 100644 index 703abc5..0000000 --- a/src/shackle_hooks.erl +++ /dev/null @@ -1,44 +0,0 @@ --module(shackle_hooks). --include("shackle_internal.hrl"). - --dialyzer({nowarn_function, metrics/4}). --ignore_xref([ - {shackle_hooks_foil, lookup, 1} -]). - --compile(inline). --compile({inline_size, 512}). - --export([ - init/0, - metrics/4 -]). - -%% callbacks --optional_callbacks([metrics/4]). - --callback metrics(client(), metric_type(), metric_key(), metric_value()) -> - ok. - -%% public --spec init() -> - ok. - -init() -> - Hooks = ?GET_ENV(hooks, []), - Metrics = ?LOOKUP(metrics, Hooks, undefined), - foil:new(?MODULE), - foil:insert(?MODULE, metrics, Metrics), - foil:load(?MODULE), - ok. - --spec metrics(client(), metric_type(), metric_key(), metric_value()) -> - ok. - -metrics(Client, Type, Key, Value) -> - case shackle_hooks_foil:lookup(metrics) of - {ok, {M, F}} -> - M:F(Client, Type, Key, Value); - _ -> - ok - end. diff --git a/src/shackle_pool.erl b/src/shackle_pool.erl index 94a4a5a..9af972f 100644 --- a/src/shackle_pool.erl +++ b/src/shackle_pool.erl @@ -139,7 +139,7 @@ server(_Name, #pool_options { client = Client }, 0) -> - ?METRICS(Client, counter, <<"no_server">>), + shackle_telemetry:no_server(Client), {error, no_server}; server(Name, #pool_options { backlog_size = BacklogSize, @@ -157,11 +157,11 @@ server(Name, #pool_options { true -> {ok, Client, ServerName}; false -> - ?METRICS(Client, counter, <<"backlog_full">>), + shackle_telemetry:backlog_full(Client), server(Name, Options, N - 1) end; false -> - ?METRICS(Client, counter, <<"disabled">>), + shackle_telemetry:disabled(Client), server(Name, Options, N - 1) end. diff --git a/src/shackle_server.erl b/src/shackle_server.erl index 0300137..08da97a 100644 --- a/src/shackle_server.erl +++ b/src/shackle_server.erl @@ -101,7 +101,7 @@ handle_msg({Request, #cast { {ok, ExtRequestId, Data, ClientState2} -> case Protocol:send(Socket, Data) of ok -> - ?METRICS(Client, counter, <<"send">>), + shackle_telemetry:send(Client, size(Data)), case ExtRequestId of undefined -> reply(ok, Cast, State); @@ -184,7 +184,7 @@ handle_msg({timeout, ExtRequestId}, {#state { true -> try Client:handle_timeout(ExtRequestId, ClientState) of {ok, Reply, ClientState2} -> - ?METRICS(Client, counter, <<"handle_timeout">>), + shackle_telemetry:handle_timeout(Client), process_responses([Reply], State), {ok, {State, ClientState2}}; {error, Reason, ClientState2} -> @@ -201,7 +201,7 @@ handle_msg({timeout, ExtRequestId}, {#state { false -> case shackle_queue:remove(Queue, Id, ExtRequestId) of {ok, Cast, _TimerRef} -> - ?METRICS(Client, counter, <<"timeout">>), + shackle_telemetry:timeout(Client), reply({error, timeout}, Cast, State); {error, not_found} -> ok @@ -325,7 +325,7 @@ handle_msg_data(Socket, Data, #state { socket = Socket } = State, ClientState) -> - ?METRICS(Client, counter, <<"recv">>), + shackle_telemetry:recv(Client, size(Data)), try Client:handle_data(Data, ClientState) of {ok, Replies, ClientState2} -> process_responses(Replies, State), @@ -364,16 +364,16 @@ process_responses([{ExtRequestId, Reply} | T], #state { queue = Queue } = State) -> - ?METRICS(Client, counter, <<"replies">>), + shackle_telemetry:replies(Client), case shackle_queue:remove(Queue, Id, ExtRequestId) of {ok, #cast {timestamp = Timestamp} = Cast, TimerRef} -> - ?METRICS(Client, counter, <<"found">>), + shackle_telemetry:found(Client), Diff = timer:now_diff(os:timestamp(), Timestamp), - ?METRICS(Client, timing, <<"reply">>, Diff), + shackle_telemetry:reply(Client, Diff), erlang:cancel_timer(TimerRef), reply(Reply, Cast, State); {error, not_found} -> - ?METRICS(Client, counter, <<"not_found">>, 1), + shackle_telemetry:not_found(Client), ok end, process_responses(T, State). diff --git a/src/shackle_sup.erl b/src/shackle_sup.erl index ac20384..ab75057 100644 --- a/src/shackle_sup.erl +++ b/src/shackle_sup.erl @@ -23,7 +23,6 @@ start_link() -> {ok, {{one_for_one, 5, 10}, [supervisor:child_spec()]}}. init([]) -> - shackle_hooks:init(), shackle_pool:init(), shackle_status:init(), diff --git a/src/shackle_telemetry.erl b/src/shackle_telemetry.erl new file mode 100644 index 0000000..232918b --- /dev/null +++ b/src/shackle_telemetry.erl @@ -0,0 +1,85 @@ +-module(shackle_telemetry). +-include("shackle_internal.hrl"). + +-compile(inline). +-compile({inline_size, 512}). + +-export([ + backlog_full/1, + disabled/1, + found/1, + handle_timeout/1, + no_server/1, + not_found/1, + recv/2, + replies/1, + reply/2, + send/2, + timeout/1 +]). + +-spec backlog_full(client()) -> ok. +backlog_full(Client) -> + Measurements = #{count => 1}, + Metadata = #{client => Client}, + telemetry:execute([shackle, backlog_full], Measurements, Metadata). + +-spec disabled(client()) -> ok. +disabled(Client) -> + Measurements = #{count => 1}, + Metadata = #{client => Client}, + telemetry:execute([shackle, disabled], Measurements, Metadata). + +-spec found(client()) -> ok. +found(Client) -> + Measurements = #{count => 1}, + Metadata = #{client => Client}, + telemetry:execute([shackle, found], Measurements, Metadata). + +-spec handle_timeout(client()) -> ok. +handle_timeout(Client) -> + Measurements = #{count => 1}, + Metadata = #{client => Client}, + telemetry:execute([shackle, handle_timeout], Measurements, Metadata). + +-spec no_server(client()) -> ok. +no_server(Client) -> + Measurements = #{count => 1}, + Metadata = #{client => Client}, + telemetry:execute([shackle, no_server], Measurements, Metadata). + +-spec not_found(client()) -> ok. +not_found(Client) -> + Measurements = #{count => 1}, + Metadata = #{client => Client}, + telemetry:execute([shackle, not_found], Measurements, Metadata). + +-spec recv(client(), non_neg_integer()) -> ok. +recv(Client, NBytes) -> + Measurements = #{count => 1, bytes => NBytes}, + Metadata = #{client => Client}, + telemetry:execute([shackle, recv], Measurements, Metadata). + +-spec replies(client()) -> ok. +replies(Client) -> + Measurements = #{count => 1}, + Metadata = #{client => Client}, + telemetry:execute([shackle, replies], Measurements, Metadata). + +-spec reply(client(), non_neg_integer()) -> ok. +reply(Client, Microseconds) -> + Measurements = #{duration => Microseconds}, + Metadata = #{client => Client}, + telemetry:execute([shackle, reply], Measurements, Metadata). + +-spec send(client(), non_neg_integer()) -> ok. +send(Client, NBytes) -> + Measurements = #{count => 1, bytes => NBytes}, + Metadata = #{client => Client}, + telemetry:execute([shackle, send], Measurements, Metadata). + +-spec timeout(client()) -> ok. +timeout(Client) -> + Measurements = #{count => 1}, + Metadata = #{client => Client}, + telemetry:execute([shackle, timeout], Measurements, Metadata). From 6fdbe402a2680051b0d8be4fd5042bcde77755a4 Mon Sep 17 00:00:00 2001 From: Richard Kallos Date: Tue, 30 May 2023 13:30:22 -0400 Subject: [PATCH 3/3] Update README.md --- README.md | 46 ++-------------------------------------------- 1 file changed, 2 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index 0baa9a5..f403f40 100644 --- a/README.md +++ b/README.md @@ -212,50 +212,8 @@ shackle_pool:start(pool_name(), client(), client_options(), pool_options()) {ok, <<"bar">>} ``` -## Environment variables - - - - - - - - - - - - - - -
NameTypeDefaultDescription
hooks[{atom(), {module(), atom()}}][]used to receive events/metrics about your client
- -## Hooks -Hooks allow you to receive events and metrics about your client. To do so you need to implement the `shackle_hooks` behaviour and then use the `hooks` environment variable. - -```erlang --module(my_client_hooks). - --behaviour(shackle_hooks). --export([ - metrics/4 -]). - -metrics(Client, counter, Key, Value) -> - statsderl:increment(key(Client, Key), Value, 0.005); -metrics(Client, timing, Key, Value) -> - statsderl:timing(key(Client, Key), Value, 0.005). - -key(Client, Key) -> - [<<"shackle.">>, atom_to_binary(Client, latin1), <<".">>, Key]. -``` - -```erlang -{shackle, [ - {hooks, [ - {metrics {my_client_hooks, metrics}} - ]} -]} -``` +## Telemetry +Shackle integrates with the backend-agnostic [telemetry](https://hexdocs.pm/telemetry/) library. See `shackle_telemetry` for the list of telemetry events that shackle can emit. ## Tests