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
-
-
-
- | Name |
- Type |
- Default |
- Description |
-
-
- | 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
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/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"]},
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).