Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F114165
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Size
7 KB
Referenced Files
None
Subscribers
None
View Options
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
Details
Attached
Mime Type
text/x-diff
Expires
Mon, Nov 25, 9:42 PM (1 d, 8 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
39565
Default Alt Text
(7 KB)
Attached To
Mode
R28 tesla
Attached
Detach File
Event Timeline
Log In to Comment