diff --git a/lib/tesla/middleware/telemetry.ex b/lib/tesla/middleware/telemetry.ex index 02e446d7..42a54984 100644 --- a/lib/tesla/middleware/telemetry.ex +++ b/lib/tesla/middleware/telemetry.ex @@ -18,6 +18,28 @@ if Code.ensure_loaded?(:telemetry) do end) ``` + ## Options + - `:prefix` - replaces default `[:tesla]` with desired Telemetry event prefix (see below) + + ## Custom Prefix + + All events will use a `:prefix` which defaults to `[:tesla]`. + + You can customize events by providing your own `:prefix` locally: + + ``` + defmodule MyClient do + use Tesla + + plug Tesla.Middleware.Telemetry, prefix: [:custom, :prefix] + + end + + :telemetry.attach("my-tesla-telemetry", [:custom, :prefix, :request, :stop], fn event, measurements, meta, config -> + # Do something with the event + end) + ``` + ## Telemetry Events * `[:tesla, :request, :start]` - emitted at the beginning of the request. @@ -46,11 +68,14 @@ if Code.ensure_loaded?(:telemetry) do @behaviour Tesla.Middleware + @default_prefix [:tesla] + @impl Tesla.Middleware - def call(env, next, _opts) do + def call(env, next, opts) do start_time = System.monotonic_time() + prefix = Keyword.get(opts, :prefix, @default_prefix) - emit_start(%{env: env}) + emit_start(%{env: env}, prefix) try do Tesla.run(env, next) @@ -59,62 +84,49 @@ if Code.ensure_loaded?(:telemetry) do stacktrace = System.stacktrace() duration = System.monotonic_time() - start_time - emit_exception(duration, %{kind: kind, reason: reason, stacktrace: stacktrace}) + emit_exception(duration, %{kind: kind, reason: reason, stacktrace: stacktrace}, prefix) :erlang.raise(kind, reason, stacktrace) else {:ok, env} = result -> duration = System.monotonic_time() - start_time - emit_stop(duration, %{env: env}) - emit_legacy_event(duration, result) + emit_stop(duration, %{env: env}, prefix) + emit_legacy_event(duration, result, prefix) result {:error, reason} = result -> duration = System.monotonic_time() - start_time - emit_stop(duration, %{env: env, error: reason}) - emit_legacy_event(duration, result) + emit_stop(duration, %{env: env, error: reason}, prefix) + emit_legacy_event(duration, result, prefix) result end end - defp emit_start(metadata) do - :telemetry.execute( - [:tesla, :request, :start], - %{system_time: System.system_time()}, - metadata - ) + defp emit_start(metadata, prefix) do + event = prefix ++ [:request, :start] + :telemetry.execute(event, %{system_time: System.system_time()}, metadata) end - defp emit_stop(duration, metadata) do - :telemetry.execute( - [:tesla, :request, :stop], - %{duration: duration}, - metadata - ) + defp emit_stop(duration, metadata, prefix) do + event = prefix ++ [:request, :stop] + :telemetry.execute(event, %{duration: duration}, metadata) end - defp emit_legacy_event(duration, result) do + defp emit_legacy_event(duration, result, prefix) do if !@disable_legacy_event do + event = prefix ++ [:request] duration_µs = System.convert_time_unit(duration, :native, :microsecond) - - :telemetry.execute( - [:tesla, :request], - %{request_time: duration_µs}, - %{result: result} - ) + :telemetry.execute(event, %{request_time: duration_µs}, %{result: result}) end end - defp emit_exception(duration, metadata) do - :telemetry.execute( - [:tesla, :request, :exception], - %{duration: duration}, - metadata - ) + defp emit_exception(duration, metadata, prefix) do + event = prefix ++ [:request, :exception] + :telemetry.execute(event, %{duration: duration}, metadata) end end end diff --git a/test/tesla/middleware/telemetry_test.exs b/test/tesla/middleware/telemetry_test.exs index 6986333a..26e8ada3 100644 --- a/test/tesla/middleware/telemetry_test.exs +++ b/test/tesla/middleware/telemetry_test.exs @@ -1,6 +1,8 @@ defmodule Tesla.Middleware.TelemetryTest do use ExUnit.Case, async: true + @moduletag capture_log: true + defmodule Client do use Tesla @@ -26,47 +28,102 @@ defmodule Tesla.Middleware.TelemetryTest do :ok end - test "events are all emitted properly" do - Enum.each(["/telemetry", "/telemetry_error"], fn path -> - :telemetry.attach("start event", [:tesla, :request, :start], &echo_event/4, %{ - caller: self() - }) + describe "Telemetry" do + test "events are all emitted properly" do + Enum.each(["/telemetry", "/telemetry_error"], fn path -> + :telemetry.attach("start event", [:tesla, :request, :start], &echo_event/4, %{ + caller: self() + }) - :telemetry.attach("stop event", [:tesla, :request, :stop], &echo_event/4, %{ - caller: self() - }) + :telemetry.attach("stop event", [:tesla, :request, :stop], &echo_event/4, %{ + caller: self() + }) + + :telemetry.attach("legacy event", [:tesla, :request], &echo_event/4, %{caller: self()}) + + Client.get(path) - :telemetry.attach("legacy event", [:tesla, :request], &echo_event/4, %{ + assert_receive {:event, [:tesla, :request, :start], %{system_time: time}, + %{env: %Tesla.Env{url: path, method: :get}}} + + assert_receive {:event, [:tesla, :request, :stop], %{duration: time}, + %{env: %Tesla.Env{url: path, method: :get}}} + + assert_receive {:event, [:tesla, :request], %{request_time: time}, %{result: result}} + end) + end + + test "with an exception raised" do + :telemetry.attach("with_exception", [:tesla, :request, :exception], &echo_event/4, %{ caller: self() }) - Client.get(path) + assert_raise RuntimeError, fn -> + Client.get("/telemetry_exception") + end + + assert_receive {:event, [:tesla, :request, :exception], %{duration: time}, + %{kind: kind, reason: reason, stacktrace: stacktrace}} + end + end + + describe "with :prefix" do + defmodule ClientWithPrefix do + use Tesla - assert_receive {:event, [:tesla, :request, :start], %{system_time: time}, - %{env: %Tesla.Env{url: path, method: :get}}} + plug Tesla.Middleware.Telemetry, prefix: [:custom, :prefix] - assert_receive {:event, [:tesla, :request, :stop], %{duration: time}, - %{env: %Tesla.Env{url: path, method: :get}}} + adapter fn env -> + case env.url do + "/telemetry" -> {:ok, env} + "/telemetry_error" -> {:error, :econnrefused} + "/telemetry_exception" -> raise "some exception" + end + end + end - assert_receive {:event, [:tesla, :request], %{request_time: time}, %{result: result}} - end) - end + test "events are all emitted properly" do + Enum.each(["/telemetry", "/telemetry_error"], fn path -> + :telemetry.attach("start event", [:custom, :prefix, :request, :start], &echo_event/4, %{ + caller: self() + }) + + :telemetry.attach("stop event", [:custom, :prefix, :request, :stop], &echo_event/4, %{ + caller: self() + }) - test "with an exception raised" do - :telemetry.attach("with_exception", [:tesla, :request, :exception], &echo_event/4, %{ - caller: self() - }) + :telemetry.attach("legacy event", [:custom, :prefix, :request], &echo_event/4, %{ + caller: self() + }) - assert_raise RuntimeError, fn -> - Client.get("/telemetry_exception") + ClientWithPrefix.get(path) + + assert_receive {:event, [:custom, :prefix, :request, :start], %{system_time: time}, + %{env: %Tesla.Env{url: path, method: :get}}} + + assert_receive {:event, [:custom, :prefix, :request, :stop], %{duration: time}, + %{env: %Tesla.Env{url: path, method: :get}}} + + assert_receive {:event, [:custom, :prefix, :request], %{request_time: time}, + %{result: result}} + end) end - assert_receive {:event, [:tesla, :request, :exception], %{duration: time}, - %{ - kind: kind, - reason: reason, - stacktrace: stacktrace - }} + test "with an exception raised" do + :telemetry.attach( + "with_exception", + [:custom, :prefix, :request, :exception], + &echo_event/4, + %{caller: self()} + ) + + assert_raise RuntimeError, fn -> + ClientWithPrefix.get("/telemetry_exception") + end + + assert_receive {:event, [:custom, :prefix, :request, :exception], %{duration: time}, + %{kind: kind, reason: reason, stacktrace: stacktrace}} + end end def echo_event(event, measurements, metadata, config) do