Skip to content

Commit 6224db7

Browse files
authored
fix(opentelemetry_oban): update error handling in plugin to use formatted exception messages (#563)
Signed-off-by: Yordis Prieto <[email protected]>
1 parent b22c6cc commit 6224db7

File tree

2 files changed

+62
-9
lines changed

2 files changed

+62
-9
lines changed

instrumentation/opentelemetry_oban/lib/opentelemetry_oban/plugin_handler.ex

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,14 +54,21 @@ defmodule OpentelemetryOban.PluginHandler do
5454
def handle_plugin_exception(
5555
_event,
5656
_measurements,
57-
%{kind: :error, reason: exception, stacktrace: stacktrace} = metadata,
57+
%{kind: kind, reason: reason, stacktrace: stacktrace} = metadata,
5858
_config
5959
) do
6060
ctx = OpentelemetryTelemetry.set_current_telemetry_span(@tracer_id, metadata)
6161

6262
# Record exception and mark the span as errored
63-
Span.record_exception(ctx, exception, stacktrace)
64-
Span.set_status(ctx, OpenTelemetry.status(:error, Exception.message(exception)))
63+
# We use :otel_span.record_exception/5 (Erlang) instead of Span.record_exception/3 (Elixir)
64+
# because the Elixir version only works with exception structs (e.g., %RuntimeError{}),
65+
# while the Erlang version handles ANY error reason (atoms like :badarg, tuples, etc.)
66+
:otel_span.record_exception(ctx, kind, reason, stacktrace, [])
67+
68+
Span.set_status(
69+
ctx,
70+
OpenTelemetry.status(:error, Exception.format_banner(kind, reason, stacktrace))
71+
)
6572

6673
OpentelemetryTelemetry.end_telemetry_span(@tracer_id, metadata)
6774
end

instrumentation/opentelemetry_oban/test/opentelemetry_oban/plugin_handler_test.exs

Lines changed: 52 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -74,25 +74,70 @@ defmodule OpentelemetryOban.PluginHandlerTest do
7474
exception = %UndefinedFunctionError{
7575
arity: 0,
7676
function: :error,
77-
message: nil,
77+
message: "function Some.error/0 is undefined (module Some is not available)",
7878
module: Some,
7979
reason: nil
8080
}
8181

82+
stacktrace = [{Some, :error, [], []}]
83+
8284
:telemetry.execute(
8385
[:oban, :plugin, :exception],
8486
%{duration: 444},
8587
%{
8688
plugin: Elixir.Oban.Plugins.Stager,
8789
kind: :error,
88-
stacktrace: [
89-
{Some, :error, [], []}
90-
],
90+
stacktrace: stacktrace,
9191
reason: exception
9292
}
9393
)
9494

95-
expected_status = OpenTelemetry.status(:error, Exception.message(exception))
95+
expected_status =
96+
OpenTelemetry.status(:error, Exception.format_banner(:error, exception, stacktrace))
97+
98+
assert_receive {:span,
99+
span(
100+
name: "Elixir.Oban.Plugins.Stager process",
101+
events: events,
102+
status: ^expected_status
103+
)}
104+
105+
[
106+
event(
107+
name: :exception,
108+
attributes: event_attributes
109+
)
110+
] = :otel_events.list(events)
111+
112+
# When using :otel_span.record_exception/5 with kind/reason/stacktrace,
113+
# it doesn't extract the message field even from exception structs
114+
assert [:"exception.stacktrace", :"exception.type"] ==
115+
Enum.sort(Map.keys(:otel_attributes.map(event_attributes)))
116+
end
117+
118+
test "records span on plugin error with non-exception reason" do
119+
:telemetry.execute(
120+
[:oban, :plugin, :start],
121+
%{system_time: System.system_time()},
122+
%{plugin: Elixir.Oban.Plugins.Stager}
123+
)
124+
125+
# Test with an atom error reason like :badarg
126+
stacktrace = [{Some, :error, [], []}]
127+
128+
:telemetry.execute(
129+
[:oban, :plugin, :exception],
130+
%{duration: 444},
131+
%{
132+
plugin: Elixir.Oban.Plugins.Stager,
133+
kind: :error,
134+
stacktrace: stacktrace,
135+
reason: :badarg
136+
}
137+
)
138+
139+
expected_status =
140+
OpenTelemetry.status(:error, Exception.format_banner(:error, :badarg, stacktrace))
96141

97142
assert_receive {:span,
98143
span(
@@ -108,7 +153,8 @@ defmodule OpentelemetryOban.PluginHandlerTest do
108153
)
109154
] = :otel_events.list(events)
110155

111-
assert [:"exception.message", :"exception.stacktrace", :"exception.type"] ==
156+
# Non-exception error reasons don't have exception.message
157+
assert [:"exception.stacktrace", :"exception.type"] ==
112158
Enum.sort(Map.keys(:otel_attributes.map(event_attributes)))
113159
end
114160

0 commit comments

Comments
 (0)