Skip to content

Commit

Permalink
Add support for sending telemetry events on throws and exits (#443)
Browse files Browse the repository at this point in the history
  • Loading branch information
mtrudel authored Jan 4, 2025
1 parent 32478bb commit 576d567
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 16 deletions.
15 changes: 7 additions & 8 deletions lib/bandit/telemetry.ex
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@ defmodule Bandit.Telemetry do
the conn
* `plug`: The Plug which is being used to serve this request. Specified as `{plug_module, plug_opts}`
* `kind`: The kind of unexpected condition, typically `:exit`
* `exception`: The exception which caused this unexpected termination
* `exception`: The exception which caused this unexpected termination. May be an exception
or an arbitrary value when the event was an uncaught throw or an exit
* `stacktrace`: The stacktrace of the location which caused this unexpected termination
## `[:bandit, :websocket, *]`
Expand Down Expand Up @@ -195,22 +196,20 @@ defmodule Bandit.Telemetry do

@spec span_exception(t(), Exception.kind(), Exception.t() | term(), Exception.stacktrace()) ::
:ok
def span_exception(span, :error, reason, stacktrace) when is_exception(reason) do
def span_exception(span, kind, reason, stacktrace) do
# Using :exit for backwards-compatibility with Bandit =< 1.5.7
kind = if kind == :error, do: :exit, else: kind

metadata =
Map.merge(span.start_metadata, %{
# Using :exit for backwards-compatibility with Bandit =< 1.5.7
kind: :exit,
kind: kind,
exception: reason,
stacktrace: stacktrace
})

span_event(span, :exception, %{}, metadata)
end

def span_exception(_span, _kind, _reason, _stacktrace) do
:ok
end

@doc false
@spec span_event(t(), span_name(), :telemetry.event_measurements(), :telemetry.event_metadata()) ::
:ok
Expand Down
36 changes: 32 additions & 4 deletions test/bandit/http1/request_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -2330,21 +2330,49 @@ defmodule HTTP1RequestTest do
end

@tag :capture_log
test "it should not send `exception` events for throwing requests", context do
test "it should send `exception` events for throwing requests", context do
Req.get!(context.req, url: "/uncaught_throw")

refute_receive {:telemetry, [:bandit, :request, :stop], _, _}
assert_receive {:telemetry, [:bandit, :request, :exception], measurements, metadata}, 500

assert measurements
~> %{monotonic_time: integer()}

assert metadata
~> %{
connection_telemetry_span_context: reference(),
telemetry_span_context: reference(),
conn: struct_like(Plug.Conn, []),
plug: {__MODULE__, []},
kind: :throw,
exception: "thrown",
stacktrace: list()
}
end

def uncaught_throw(_conn) do
throw("thrown")
end

@tag :capture_log
test "it should not send `exception` events for exiting requests", context do
test "it should send `exception` events for exiting requests", context do
Req.get!(context.req, url: "/uncaught_exit")

refute_receive {:telemetry, [:bandit, :request, :exception], _, _}
assert_receive {:telemetry, [:bandit, :request, :exception], measurements, metadata}, 500

assert measurements
~> %{monotonic_time: integer()}

assert metadata
~> %{
connection_telemetry_span_context: reference(),
telemetry_span_context: reference(),
conn: struct_like(Plug.Conn, []),
plug: {__MODULE__, []},
kind: :exit,
exception: "exited",
stacktrace: list()
}
end

def uncaught_exit(_conn) do
Expand Down
34 changes: 30 additions & 4 deletions test/bandit/http2/plug_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -1012,21 +1012,47 @@ defmodule HTTP2PlugTest do
end

@tag :capture_log
test "it should not send `exception` events for throwing requests", context do
test "it should send `exception` events for throwing requests", context do
Req.get!(context.req, url: "/uncaught_throw")

refute_receive {:telemetry, [:bandit, :request, :exception], _, _}
assert_receive {:telemetry, [:bandit, :request, :exception], measurements, metadata}, 500

assert measurements ~> %{monotonic_time: integer()}

assert metadata
~> %{
connection_telemetry_span_context: reference(),
telemetry_span_context: reference(),
conn: struct_like(Plug.Conn, []),
plug: {__MODULE__, []},
kind: :throw,
exception: "thrown",
stacktrace: list()
}
end

def uncaught_throw(_conn) do
throw("thrown")
end

@tag :capture_log
test "it should not send `exception` events for exiting requests", context do
test "it should send `exception` events for exiting requests", context do
Req.get!(context.req, url: "/uncaught_exit")

refute_receive {:telemetry, [:bandit, :request, :exception], _, _}
assert_receive {:telemetry, [:bandit, :request, :exception], measurements, metadata}, 500

assert measurements ~> %{monotonic_time: integer()}

assert metadata
~> %{
connection_telemetry_span_context: reference(),
telemetry_span_context: reference(),
conn: struct_like(Plug.Conn, []),
plug: {__MODULE__, []},
kind: :exit,
exception: "exited",
stacktrace: list()
}
end

def uncaught_exit(_conn) do
Expand Down

0 comments on commit 576d567

Please sign in to comment.