Page MenuHomePhorge

No OneTemporary

Size
7 KB
Referenced Files
None
Subscribers
None
diff --git a/lib/tesla/middleware/telemetry.ex b/lib/tesla/middleware/telemetry.ex
index 9ffac7a..bc12982 100644
--- a/lib/tesla/middleware/telemetry.ex
+++ b/lib/tesla/middleware/telemetry.ex
@@ -1,32 +1,100 @@
if Code.ensure_loaded?(:telemetry) do
defmodule Tesla.Middleware.Telemetry do
@behaviour Tesla.Middleware
@moduledoc """
- Send the request time and meta-information through telemetry.
+ Emits events using the `:telemetry` library to expose instrumentation.
### Example usage
```
defmodule MyClient do
use Tesla
- plug Tesla.Middleware.Telemetry
+ plug Tesla.Middleware.Telemetry, prefix: [:my_client]
end
- :telemetry.attach("my-tesla-telemetry", [:tesla, :request], fn event, time, meta, config ->
+ :telemetry.attach("my-tesla-telemetry", [:my_client, :tesla, :request, stop], fn event, measurements, meta, config ->
# Do something with the event
end)
```
+ ### Options
+
+ * `:event_prefix` - a list of atoms to prefix to the telemetry event. Defaults to `[]`
+
+ ## Telemetry Events
+
+ * `[:tesla, :request, :start]` - emitted at the beginning of the request.
+ * Measurement: `%{time: System.monotonic_time}`
+ * Metadata: `%{env: Tesla.Env.t}`
+
+ * `[:tesla, :request, :stop]` - emitted at the end of the request.
+ * Measurement: `%{duration: native_time}`
+ * Metadata: `%{env: Tesla.Env.t}`
+
+ * `[:tesla, :request, :error]` - emitted at the end of the request when there is an error.
+ * Measurement: `%{duration: native_time}`
+ * Metadata: `%{env: Tesla.Env.t, reason: term}`
+
+ * `[:tesla, :request, :exception]` - emitted at the end of the request when an exception is raised.
+ * Measurement: `%{duration: native_time}`
+ * Metadata: `%{env: Tesla.Env.t, exception: Exception.t, stacktrace: Exception.stacktrace}`
+
Please check the [telemetry](https://hexdocs.pm/telemetry/) for the further usage.
"""
@doc false
- def call(env, next, _opts) do
- {time, res} = :timer.tc(Tesla, :run, [env, next])
- :telemetry.execute([:tesla, :request], %{request_time: time}, %{result: res})
- res
+ def call(req_env, next, opts) do
+ prefix = Keyword.get(opts, :event_prefix, [])
+ start_time = System.monotonic_time()
+
+ try do
+ emit_start(req_env, start_time, prefix)
+
+ Tesla.run(req_env, next)
+ |> emit_result(start_time, prefix, req_env)
+ rescue
+ e ->
+ stacktrace = System.stacktrace()
+ metadata = %{env: req_env, exception: e, stacktrace: stacktrace}
+
+ :telemetry.execute(
+ prefix ++ [:tesla, :request, :exception],
+ %{duration: System.monotonic_time() - start_time},
+ metadata
+ )
+
+ reraise e, stacktrace
+ end
+ end
+
+ defp emit_start(req_env, start_time, prefix) do
+ :telemetry.execute(prefix ++ [:tesla, :request, :start], %{time: start_time}, %{
+ env: req_env
+ })
+ end
+
+ defp emit_result(result, start_time, prefix, req_env) do
+ try do
+ result
+ after
+ case result do
+ {:ok, env} ->
+ :telemetry.execute(
+ prefix ++ [:tesla, :request, :stop],
+ %{duration: System.monotonic_time() - start_time},
+ %{env: env}
+ )
+
+ {:error, error} ->
+ :telemetry.execute(
+ prefix ++ [:tesla, :request, :error],
+ %{duration: System.monotonic_time() - start_time},
+ %{env: req_env, reason: error}
+ )
+ end
+ end
end
end
end
diff --git a/test/tesla/middleware/telemetry_test.exs b/test/tesla/middleware/telemetry_test.exs
index a4417dd..2c2f339 100644
--- a/test/tesla/middleware/telemetry_test.exs
+++ b/test/tesla/middleware/telemetry_test.exs
@@ -1,52 +1,102 @@
defmodule Tesla.Middleware.TelemetryTest do
use ExUnit.Case, async: true
defmodule Client do
use Tesla
plug Tesla.Middleware.Telemetry
adapter fn env ->
case env.url do
"/telemetry" -> {:ok, env}
"/telemetry_error" -> {:error, :econnrefused}
+ "/telemetry_exception" -> raise "some exception"
+ end
+ end
+ end
+
+ defmodule ClientWithOptions do
+ use Tesla
+
+ plug Tesla.Middleware.Telemetry, event_prefix: [:my_client]
+
+ adapter fn env ->
+ case env.url do
+ "/telemetry" -> {:ok, env}
end
end
end
setup do
Application.ensure_all_started(:telemetry)
+
+ on_exit(fn ->
+ :telemetry.list_handlers([])
+ |> Enum.each(&:telemetry.detach(&1.id))
+ end)
+
:ok
end
- test "Get the info from telemetry" do
- :telemetry.attach(
- "telemetry_test",
- [:tesla, :request],
- fn [:tesla, :request], %{request_time: time}, meta, _config ->
- send(self(), {:ok_called, is_integer(time), meta})
- end,
- nil
- )
+ test "accepts options" do
+ :telemetry.attach("with_opts", [:my_client, :tesla, :request, :stop], &echo_event/4, %{
+ caller: self()
+ })
+
+ ClientWithOptions.get("/telemetry")
+
+ assert_receive {:event, [:my_client, :tesla, :request, :stop], %{duration: time},
+ %{env: %Tesla.Env{url: "/telemetry", method: :get}}}
+ end
+
+ test "with default options" do
+ :telemetry.attach("with_default_opts_start", [:tesla, :request, :start], &echo_event/4, %{
+ caller: self()
+ })
+
+ :telemetry.attach("with_default_opts_stop", [:tesla, :request, :stop], &echo_event/4, %{
+ caller: self()
+ })
Client.get("/telemetry")
- assert_receive {:ok_called, true,
- %{result: {:ok, %Tesla.Env{url: "/telemetry", method: :get}}}},
- 1000
+ assert_receive {:event, [:tesla, :request, :start], %{time: time},
+ %{env: %Tesla.Env{url: "/telemetry", method: :get}}}
+
+ assert_receive {:event, [:tesla, :request, :stop], %{duration: time},
+ %{env: %Tesla.Env{url: "/telemetry", method: :get}}}
end
- test "Get the error from telemetry" do
- :telemetry.attach(
- "telemetry_test_error",
- [:tesla, :request],
- fn [:tesla, :request], %{request_time: time}, meta, _config ->
- send(self(), {:error_called, is_integer(time), meta})
- end,
- nil
- )
+ test "with an error returned" do
+ :telemetry.attach("with_error", [:tesla, :request, :error], &echo_event/4, %{caller: self()})
Client.get("/telemetry_error")
- assert_receive {:error_called, true, %{result: {:error, :econnrefused}}}, 1000
+
+ assert_receive {:event, [:tesla, :request, :error], %{duration: time},
+ %{
+ env: %Tesla.Env{url: "/telemetry_error", method: :get},
+ reason: :econnrefused
+ }}
+ end
+
+ test "with an exception raised" do
+ :telemetry.attach("with_exception", [:tesla, :request, :exception], &echo_event/4, %{
+ caller: self()
+ })
+
+ assert_raise RuntimeError, fn ->
+ Client.get("/telemetry_exception")
+ end
+
+ assert_receive {:event, [:tesla, :request, :exception], %{duration: time},
+ %{
+ env: %Tesla.Env{url: "/telemetry_exception", method: :get},
+ exception: kind,
+ stacktrace: stacktrace
+ }}
+ end
+
+ def echo_event(event, measurements, metadata, config) do
+ send(config.caller, {:event, event, measurements, metadata})
end
end

File Metadata

Mime Type
text/x-diff
Expires
Mon, Nov 25, 3:48 AM (1 d, 12 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
39565
Default Alt Text
(7 KB)

Event Timeline