Skip to content

Commit 024edf0

Browse files
committed
Improve ecto span/transaction
1 parent 3f8d960 commit 024edf0

File tree

7 files changed

+73
-53
lines changed

7 files changed

+73
-53
lines changed

lib/sentry/client.ex

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,7 @@ defmodule Sentry.Client do
271271

272272
@spec render_transaction(%Transaction{}) :: map()
273273
def render_transaction(%Transaction{} = transaction) do
274+
# IO.inspect(transaction, label: "transaction")
274275
transaction
275276
|> Transaction.to_map()
276277
|> Map.merge(%{

lib/sentry/opentelemetry/span_processor.ex

Lines changed: 34 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,7 @@ defmodule Sentry.Opentelemetry.SpanProcessor do
4747
})
4848
end
4949

50-
defp build_transaction(
51-
%SpanRecord{attributes: attributes, origin: "opentelemetry_ecto"} = root_span,
52-
child_spans
53-
) do
50+
defp build_transaction(%SpanRecord{origin: "opentelemetry_ecto"} = root_span, child_spans) do
5451
Transaction.new(%{
5552
transaction: root_span.name,
5653
start_timestamp: root_span.start_time,
@@ -61,18 +58,7 @@ defmodule Sentry.Opentelemetry.SpanProcessor do
6158
contexts: %{
6259
trace: build_trace_context(root_span)
6360
},
64-
data: %{
65-
"db.system" => attributes[:"db.system"],
66-
"db.name" => attributes[:"db.name"],
67-
"db.instance" => attributes[:"db.instance"],
68-
"db.type" => attributes[:"db.type"],
69-
"db.url" => attributes[:"db.url"],
70-
"total_time_microseconds" => attributes[:total_time_microseconds],
71-
"idle_time_microseconds" => attributes[:idle_time_microseconds],
72-
"decode_time_microseconds" => attributes[:decode_time_microseconds],
73-
"queue_time_microseconds" => attributes[:queue_time_microseconds],
74-
"query_time_microseconds" => attributes[:query_time_microseconds]
75-
},
61+
data: root_span.attributes,
7662
measurements: %{},
7763
spans: Enum.map(child_spans, &build_span(&1))
7864
})
@@ -83,7 +69,7 @@ defmodule Sentry.Opentelemetry.SpanProcessor do
8369
child_spans
8470
) do
8571
Transaction.new(%{
86-
transaction: "#{attributes[:"phoenix.plug"]}##{attributes[:"phoenix.action"]}",
72+
transaction: "#{attributes["phoenix.plug"]}##{attributes["phoenix.action"]}",
8773
start_timestamp: root_span.start_time,
8874
timestamp: root_span.end_time,
8975
transaction_info: %{
@@ -93,23 +79,23 @@ defmodule Sentry.Opentelemetry.SpanProcessor do
9379
trace: build_trace_context(root_span)
9480
},
9581
request: %{
96-
url: attributes[:"http.target"],
97-
method: attributes[:"http.method"],
82+
url: attributes["http.target"],
83+
method: attributes["http.method"],
9884
headers: %{
99-
"User-Agent" => attributes[:"http.user_agent"]
85+
"User-Agent" => attributes["http.user_agent"]
10086
},
10187
env: %{
102-
"SERVER_NAME" => attributes[:"net.host.name"],
103-
"SERVER_PORT" => attributes[:"net.host.port"]
88+
"SERVER_NAME" => attributes["net.host.name"],
89+
"SERVER_PORT" => attributes["net.host.port"]
10490
}
10591
},
10692
data: %{
107-
"http.response.status_code" => attributes[:"http.status_code"],
108-
"method" => attributes[:"http.method"],
109-
"path" => attributes[:"http.target"],
93+
"http.response.status_code" => attributes["http.status_code"],
94+
"method" => attributes["http.method"],
95+
"path" => attributes["http.target"],
11096
"params" => %{
111-
"controller" => attributes[:"phoenix.plug"],
112-
"action" => attributes[:"phoenix.action"]
97+
"controller" => attributes["phoenix.plug"],
98+
"action" => attributes["phoenix.action"]
11399
}
114100
},
115101
measurements: %{},
@@ -121,30 +107,30 @@ defmodule Sentry.Opentelemetry.SpanProcessor do
121107
%SpanRecord{attributes: attributes, origin: "opentelemetry_bandit"} = root_span,
122108
child_spans
123109
) do
124-
%Sentry.Transaction{
110+
Transaction.new(%{
125111
start_timestamp: root_span.start_time,
126112
timestamp: root_span.end_time,
127-
transaction: attributes[:"http.target"],
113+
transaction: attributes["http.target"],
128114
transaction_info: %{
129115
source: "url"
130116
},
131117
contexts: %{
132118
trace: build_trace_context(root_span)
133119
},
134120
request: %{
135-
url: attributes[:"http.url"],
136-
method: attributes[:"http.method"],
121+
url: attributes["http.url"],
122+
method: attributes["http.method"],
137123
headers: %{
138-
"User-Agent" => attributes[:"http.user_agent"]
124+
"User-Agent" => attributes["http.user_agent"]
139125
},
140126
env: %{
141-
"SERVER_NAME" => attributes[:"net.peer.name"],
142-
"SERVER_PORT" => attributes[:"net.peer.port"]
127+
"SERVER_NAME" => attributes["net.peer.name"],
128+
"SERVER_PORT" => attributes["net.peer.port"]
143129
}
144130
},
145131
measurements: %{},
146132
spans: Enum.map(child_spans, &build_span(&1))
147-
}
133+
})
148134
end
149135

150136
defp build_trace_context(
@@ -157,19 +143,21 @@ defmodule Sentry.Opentelemetry.SpanProcessor do
157143
op: "http.server",
158144
origin: root_span.origin,
159145
data: %{
160-
"http.response.status_code" => attributes[:"http.status_code"]
146+
"http.response.status_code" => attributes["http.status_code"]
161147
}
162148
}
163149
end
164150

165-
defp build_trace_context(%SpanRecord{origin: "opentelemetry_ecto"} = root_span) do
151+
defp build_trace_context(
152+
%SpanRecord{origin: "opentelemetry_ecto", attributes: attributes} = root_span
153+
) do
166154
%{
167155
trace_id: root_span.trace_id,
168156
span_id: root_span.span_id,
169157
parent_span_id: root_span.parent_span_id,
170-
op: "db",
158+
op: "db.#{attributes["db.type"]}.ecto",
171159
origin: root_span.origin,
172-
data: root_span.attributes
160+
data: attributes
173161
}
174162
end
175163

@@ -187,7 +175,7 @@ defmodule Sentry.Opentelemetry.SpanProcessor do
187175
defp build_span(
188176
%SpanRecord{origin: "opentelemetry_phoenix", attributes: attributes} = span_record
189177
) do
190-
op = "#{attributes[:"phoenix.plug"]}##{attributes[:"phoenix.action"]}"
178+
op = "#{attributes["phoenix.plug"]}##{attributes["phoenix.action"]}"
191179

192180
%Span{
193181
op: op,
@@ -196,7 +184,7 @@ defmodule Sentry.Opentelemetry.SpanProcessor do
196184
trace_id: span_record.trace_id,
197185
span_id: span_record.span_id,
198186
parent_span_id: span_record.parent_span_id,
199-
description: attributes[:"http.route"],
187+
description: attributes["http.route"],
200188
origin: span_record.origin
201189
}
202190
end
@@ -228,16 +216,14 @@ defmodule Sentry.Opentelemetry.SpanProcessor do
228216
defp build_span(%SpanRecord{origin: "opentelemetry_ecto", attributes: attributes} = span_record) do
229217
%Span{
230218
trace_id: span_record.trace_id,
231-
op: span_record.name,
232-
start_timestamp: span_record.start_time,
233-
timestamp: span_record.end_time,
234219
span_id: span_record.span_id,
235220
parent_span_id: span_record.parent_span_id,
221+
op: "db.#{attributes["db.type"]}.ecto",
222+
description: attributes["db.statement"] || span_record.name,
236223
origin: span_record.origin,
237-
data: %{
238-
"db.system" => attributes[:"db.system"],
239-
"db.name" => attributes[:"db.name"]
240-
}
224+
start_timestamp: span_record.start_time,
225+
timestamp: span_record.end_time,
226+
data: attributes
241227
}
242228
end
243229

lib/sentry/opentelemetry/span_record.ex

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,20 @@ defmodule Sentry.Opentelemetry.SpanRecord do
3030
origin: origin,
3131
start_time: cast_timestamp(otel_attrs[:start_time]),
3232
end_time: cast_timestamp(otel_attrs[:end_time]),
33-
attributes: attributes
33+
attributes: normalize_attributes(attributes)
3434
)
3535
|> Map.new()
3636

3737
struct(__MODULE__, attrs)
3838
end
3939

40+
defp normalize_attributes(attributes) do
41+
Enum.map(attributes, fn {key, value} ->
42+
{to_string(key), value}
43+
end)
44+
|> Map.new()
45+
end
46+
4047
defp cast_span_id(nil), do: nil
4148
defp cast_span_id(:undefined), do: nil
4249
defp cast_span_id(span_id), do: bytes_to_hex(span_id, 16)

lib/sentry/transaction.ex

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@ defmodule Sentry.Transaction do
1010
:transaction,
1111
:transaction_info,
1212
:contexts,
13-
:platform,
14-
:sdk,
1513
:request,
1614
:measurements,
1715
spans: [],

test_integrations/phoenix_app/lib/phoenix_app/application.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ defmodule PhoenixApp.Application do
1515

1616
OpentelemetryBandit.setup()
1717
OpentelemetryPhoenix.setup()
18-
OpentelemetryEcto.setup([:phoenix_app, :repo])
18+
OpentelemetryEcto.setup([:phoenix_app, :repo], db_statement: :enabled)
1919

2020
children = [
2121
PhoenixAppWeb.Telemetry,
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
defmodule PhoenixApp.RepoTest do
2+
use PhoenixApp.DataCase
3+
4+
alias PhoenixApp.{Repo, User}
5+
6+
import Sentry.TestHelpers
7+
8+
setup do
9+
put_test_config(dsn: "http://public:secret@localhost:8080/1")
10+
11+
Sentry.Test.start_collecting_sentry_reports()
12+
end
13+
14+
test "instrumented top-level ecto transaction span" do
15+
Repo.all(User) |> Enum.map(& &1.id)
16+
17+
transactions = Sentry.Test.pop_sentry_transactions()
18+
19+
assert length(transactions) == 1
20+
21+
assert [transaction] = transactions
22+
23+
assert transaction.transaction_info == %{source: "db"}
24+
assert transaction.contexts.trace.op == "db.sql.ecto"
25+
assert transaction.contexts.trace.data["db.system"] == :sqlite
26+
end
27+
end

test_integrations/phoenix_app/test/phoenix_app_web/controllers/transaction_test.exs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,8 @@ defmodule Sentry.Integrations.Phoenix.TransactionTest do
6060

6161
assert [span] = transaction.spans
6262

63-
assert span.op == "phoenix_app.repo.query:users"
63+
assert span.op == "db.sql.ecto"
64+
assert String.starts_with?(span.description, "SELECT ")
6465
assert span.trace_id == trace.trace_id
6566
assert span.parent_span_id == trace.span_id
6667
end

0 commit comments

Comments
 (0)