Skip to content

Commit 687f379

Browse files
authored
feat(opentelemetry_oban): add missing OTEL messaging semconv attributes (#598)
1 parent b6becf8 commit 687f379

File tree

5 files changed

+57
-12
lines changed

5 files changed

+57
-12
lines changed

instrumentation/opentelemetry_oban/lib/opentelemetry_oban.ex

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ defmodule OpentelemetryOban do
1818

1919
alias Ecto.Changeset
2020
alias OpenTelemetry.Span
21+
alias OpenTelemetry.SemConv.ErrorAttributes
2122
alias OpenTelemetry.SemConv.Incubating.MessagingAttributes
2223

2324
require Logger
@@ -122,6 +123,7 @@ defmodule OpentelemetryOban do
122123
ctx = OpenTelemetry.Tracer.current_span_ctx()
123124
Span.record_exception(ctx, exception, __STACKTRACE__)
124125
Span.set_status(ctx, OpenTelemetry.status(:error, ""))
126+
Span.set_attribute(ctx, ErrorAttributes.error_type(), inspect(exception.__struct__))
125127
reraise exception, __STACKTRACE__
126128
end
127129
end
@@ -134,10 +136,15 @@ defmodule OpentelemetryOban do
134136
end
135137

136138
def insert_all(name, changesets) when is_list(changesets) do
137-
# changesets in insert_all can include different workers and different
138-
# queues. This means we cannot provide much information here, but we can
139-
# still record the insert and propagate the context information.
140-
OpenTelemetry.Tracer.with_span "send", kind: :producer do
139+
attrs = %{
140+
MessagingAttributes.messaging_system() => :oban,
141+
MessagingAttributes.messaging_operation_name() => "send",
142+
MessagingAttributes.messaging_operation_type() =>
143+
MessagingAttributes.messaging_operation_type_values().create,
144+
MessagingAttributes.messaging_batch_message_count() => length(changesets)
145+
}
146+
147+
OpenTelemetry.Tracer.with_span "send", attributes: attrs, kind: :producer do
141148
changesets = Enum.map(changesets, &add_tracing_information_to_meta/1)
142149
Oban.insert_all(name, changesets)
143150
end

instrumentation/opentelemetry_oban/lib/opentelemetry_oban/job_handler.ex

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ defmodule OpentelemetryOban.JobHandler do
44
alias OpenTelemetry.Ctx
55
alias OpenTelemetry.Span
66
alias OpenTelemetry.Tracer
7+
alias OpenTelemetry.SemConv.ErrorAttributes
78
alias OpenTelemetry.SemConv.Incubating.MessagingAttributes
89

910
@tracer_id __MODULE__
@@ -123,10 +124,16 @@ defmodule OpentelemetryOban.JobHandler do
123124

124125
Span.record_exception(ctx, error, stacktrace)
125126
Span.set_status(ctx, OpenTelemetry.status(:error, ""))
127+
set_error_type(error)
126128

127129
OpentelemetryTelemetry.end_telemetry_span(@tracer_id, metadata)
128130
end
129131

132+
defp set_error_type(%struct_name{} = error) when is_exception(error),
133+
do: Tracer.set_attribute(ErrorAttributes.error_type(), inspect(struct_name))
134+
135+
defp set_error_type(_error), do: :ok
136+
130137
defp put_links(span_opts, []), do: span_opts
131138
defp put_links(span_opts, links), do: Map.put(span_opts, :links, links)
132139

instrumentation/opentelemetry_oban/lib/opentelemetry_oban/plugin_handler.ex

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ defmodule OpentelemetryOban.PluginHandler do
33

44
alias OpenTelemetry.Tracer
55
alias OpenTelemetry.Span
6+
alias OpenTelemetry.SemConv.ErrorAttributes
67

78
@tracer_id __MODULE__
89

@@ -72,9 +73,16 @@ defmodule OpentelemetryOban.PluginHandler do
7273
OpenTelemetry.status(:error, Exception.format_banner(kind, reason, stacktrace))
7374
)
7475

76+
set_error_type(reason)
77+
7578
OpentelemetryTelemetry.end_telemetry_span(@tracer_id, metadata)
7679
end
7780

81+
defp set_error_type(%struct_name{} = error) when is_exception(error),
82+
do: Tracer.set_attribute(ErrorAttributes.error_type(), inspect(struct_name))
83+
84+
defp set_error_type(_error), do: :ok
85+
7886
defp end_span_plugin_attrs(%{plugin: Oban.Plugins.Cron} = metadata) do
7987
%{"oban.plugins.cron.jobs_count": length(metadata[:jobs])}
8088
end

instrumentation/opentelemetry_oban/test/opentelemetry_oban/plugin_handler_test.exs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,19 +98,21 @@ defmodule OpentelemetryOban.PluginHandlerTest do
9898
assert_receive {:span,
9999
span(
100100
name: "Elixir.Oban.Plugins.Stager process",
101+
attributes: span_attributes,
101102
events: events,
102103
status: ^expected_status
103104
)}
104105

106+
assert %{"error.type": "UndefinedFunctionError"} =
107+
:otel_attributes.map(span_attributes)
108+
105109
[
106110
event(
107111
name: :exception,
108112
attributes: event_attributes
109113
)
110114
] = :otel_events.list(events)
111115

112-
# When using :otel_span.record_exception/5 with kind/reason/stacktrace,
113-
# it doesn't extract the message field even from exception structs
114116
assert [:"exception.stacktrace", :"exception.type"] ==
115117
Enum.sort(Map.keys(:otel_attributes.map(event_attributes)))
116118
end
@@ -142,18 +144,20 @@ defmodule OpentelemetryOban.PluginHandlerTest do
142144
assert_receive {:span,
143145
span(
144146
name: "Elixir.Oban.Plugins.Stager process",
147+
attributes: span_attributes,
145148
events: events,
146149
status: ^expected_status
147150
)}
148151

152+
refute Map.has_key?(:otel_attributes.map(span_attributes), :"error.type")
153+
149154
[
150155
event(
151156
name: :exception,
152157
attributes: event_attributes
153158
)
154159
] = :otel_events.list(events)
155160

156-
# Non-exception error reasons don't have exception.message
157161
assert [:"exception.stacktrace", :"exception.type"] ==
158162
Enum.sort(Map.keys(:otel_attributes.map(event_attributes)))
159163
end

instrumentation/opentelemetry_oban/test/opentelemetry_oban_test.exs

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,8 @@ defmodule OpentelemetryObanTest do
179179
status: ^expected_status
180180
)}
181181

182+
attrs = :otel_attributes.map(attributes)
183+
182184
assert %{
183185
"oban.job.attempt": 1,
184186
"oban.job.inserted_at": _inserted_at,
@@ -192,8 +194,9 @@ defmodule OpentelemetryObanTest do
192194
"messaging.operation.type": :process,
193195
"messaging.system": :oban,
194196
"messaging.message.id": ^id,
195-
"messaging.client.id": "TestJobThatReturnsError"
196-
} = :otel_attributes.map(attributes)
197+
"messaging.client.id": "TestJobThatReturnsError",
198+
"error.type": "Oban.PerformError"
199+
} = attrs
197200

198201
[
199202
event(
@@ -259,6 +262,8 @@ defmodule OpentelemetryObanTest do
259262
status: ^expected_status
260263
)}
261264

265+
attrs = :otel_attributes.map(attributes)
266+
262267
assert %{
263268
"messaging.destination.name": "events",
264269
"messaging.operation.name": "process",
@@ -272,8 +277,9 @@ defmodule OpentelemetryObanTest do
272277
"messaging.operation.type": :process,
273278
"messaging.message.id": ^id,
274279
"messaging.client.id": "TestJobThatThrowsException",
275-
"messaging.system": :oban
276-
} = :otel_attributes.map(attributes)
280+
"messaging.system": :oban,
281+
"error.type": "UndefinedFunctionError"
282+
} = attrs
277283

278284
[
279285
event(
@@ -327,10 +333,14 @@ defmodule OpentelemetryObanTest do
327333
assert_receive {:span,
328334
span(
329335
name: "send events",
336+
attributes: send_attributes,
330337
events: events,
331338
status: ^expected_status
332339
)}
333340

341+
assert %{"error.type": "Ecto.InvalidChangesetError"} =
342+
:otel_attributes.map(send_attributes)
343+
334344
[
335345
event(
336346
name: :exception,
@@ -355,13 +365,22 @@ defmodule OpentelemetryObanTest do
355365
assert_receive {:span,
356366
span(
357367
name: "send",
358-
attributes: _attributes,
368+
attributes: send_attributes,
359369
trace_id: send_trace_id,
360370
span_id: send_span_id,
361371
kind: :producer,
362372
status: :undefined
363373
)}
364374

375+
send_attrs = :otel_attributes.map(send_attributes)
376+
377+
assert %{
378+
"messaging.system": :oban,
379+
"messaging.operation.name": "send",
380+
"messaging.operation.type": :create,
381+
"messaging.batch.message_count": 2
382+
} = send_attrs
383+
365384
assert_receive {:span,
366385
span(
367386
name: "process events",

0 commit comments

Comments
 (0)