Skip to content

Commit 6c2d80c

Browse files
danschultzersolnic
authored andcommitted
Refactor OTel (#835)
* Add environment to transaction * Make root span the transaction * Generic transactions spans parsing
1 parent 1856faa commit 6c2d80c

File tree

16 files changed

+232
-433
lines changed

16 files changed

+232
-433
lines changed

lib/sentry/opentelemetry/span_processor.ex

Lines changed: 70 additions & 312 deletions
Large diffs are not rendered by default.

lib/sentry/test.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -361,7 +361,7 @@ defmodule Sentry.Test do
361361
362362
iex> Sentry.Test.start_collecting_sentry_reports()
363363
:ok
364-
iex> Sentry.send_transaction(%Sentry.Transaction{})
364+
iex> Sentry.send_transaction(Sentry.Transaction.new(%{span_id: "123", spans: []}))
365365
{:ok, ""}
366366
iex> [%Sentry.Transaction{}] = Sentry.Test.pop_sentry_transactions()
367367

lib/sentry/transaction.ex

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,60 @@
11
defmodule Sentry.Transaction do
22
@type t() :: %__MODULE__{}
33

4-
alias Sentry.{UUID}
4+
alias Sentry.{Config, UUID}
5+
6+
@enforce_keys ~w(event_id span_id spans)a
57

68
defstruct [
79
:event_id,
8-
:start_timestamp,
9-
:timestamp,
10+
:environment,
11+
:span_id,
1012
:transaction,
1113
:transaction_info,
12-
:status,
1314
:contexts,
14-
:request,
1515
:measurements,
16-
spans: [],
16+
:spans,
1717
type: "transaction"
1818
]
1919

2020
def new(attrs) do
21-
struct(__MODULE__, Map.put(attrs, :event_id, UUID.uuid4_hex()))
21+
struct!(
22+
__MODULE__,
23+
attrs
24+
|> Map.put(:event_id, UUID.uuid4_hex())
25+
|> Map.put(:environment, Config.environment_name())
26+
)
2227
end
2328

2429
# Used to then encode the returned map to JSON.
2530
@doc false
2631
def to_map(%__MODULE__{} = transaction) do
27-
Map.put(
28-
Map.from_struct(transaction),
29-
:spans,
30-
Enum.map(transaction.spans, &Sentry.Span.to_map(&1))
31-
)
32+
transaction_attrs = Map.take(transaction, [:event_id, :environment, :transaction, :transaction_info, :contexts, :measurements, :type])
33+
{[root_span], child_spans} = Enum.split_with(transaction.spans, &is_nil(&1.parent_span_id))
34+
35+
root_span
36+
|> Sentry.Span.to_map()
37+
|> Map.put(:spans, Enum.map(child_spans, &Sentry.Span.to_map/1))
38+
|> Map.drop([:description])
39+
|> Map.merge(transaction_attrs)
3240
end
3341
end
3442

3543
defmodule Sentry.Span do
44+
@enforce_keys ~w(span_id trace_id start_timestamp timestamp)a
45+
3646
defstruct [
37-
:op,
47+
:trace_id,
48+
:span_id,
49+
:parent_span_id,
3850
:start_timestamp,
3951
:timestamp,
4052
:description,
41-
:span_id,
42-
:parent_span_id,
43-
:trace_id,
53+
:op,
54+
:status,
4455
:tags,
4556
:data,
46-
:origin,
47-
:status
57+
:origin
4858
]
4959

5060
# Used to then encode the returned map to JSON.

mix.exs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,8 @@ defmodule Sentry.Mixfile do
115115

116116
# Required by Tracing
117117
{:opentelemetry, "~> 1.5"},
118-
{:opentelemetry_api, "~> 1.3"}
118+
{:opentelemetry_api, "~> 1.3"},
119+
{:opentelemetry_semantic_conventions, "~> 1.0"}
119120
]
120121
end
121122

mix.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
"oban": {:hex, :oban, "2.18.3", "1608c04f8856c108555c379f2f56bc0759149d35fa9d3b825cb8a6769f8ae926", [:mix], [{:ecto_sql, "~> 3.10", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:ecto_sqlite3, "~> 0.9", [hex: :ecto_sqlite3, repo: "hexpm", optional: true]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.16", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "36ca6ca84ef6518f9c2c759ea88efd438a3c81d667ba23b02b062a0aa785475e"},
3535
"opentelemetry": {:hex, :opentelemetry, "1.5.0", "7dda6551edfc3050ea4b0b40c0d2570423d6372b97e9c60793263ef62c53c3c2", [:rebar3], [{:opentelemetry_api, "~> 1.4", [hex: :opentelemetry_api, repo: "hexpm", optional: false]}], "hexpm", "cdf4f51d17b592fc592b9a75f86a6f808c23044ba7cf7b9534debbcc5c23b0ee"},
3636
"opentelemetry_api": {:hex, :opentelemetry_api, "1.4.0", "63ca1742f92f00059298f478048dfb826f4b20d49534493d6919a0db39b6db04", [:mix, :rebar3], [], "hexpm", "3dfbbfaa2c2ed3121c5c483162836c4f9027def469c41578af5ef32589fcfc58"},
37-
"opentelemetry_semantic_conventions": {:hex, :opentelemetry_semantic_conventions, "0.2.0", "b67fe459c2938fcab341cb0951c44860c62347c005ace1b50f8402576f241435", [:mix, :rebar3], [], "hexpm", "d61fa1f5639ee8668d74b527e6806e0503efc55a42db7b5f39939d84c07d6895"},
37+
"opentelemetry_semantic_conventions": {:hex, :opentelemetry_semantic_conventions, "1.27.0", "acd0194a94a1e57d63da982ee9f4a9f88834ae0b31b0bd850815fe9be4bbb45f", [:mix, :rebar3], [], "hexpm", "9681ccaa24fd3d810b4461581717661fd85ff7019b082c2dff89c7d5b1fc2864"},
3838
"parse_trans": {:hex, :parse_trans, "3.4.1", "6e6aa8167cb44cc8f39441d05193be6e6f4e7c2946cb2759f015f8c56b76e5ff", [:rebar3], [], "hexpm", "620a406ce75dada827b82e453c19cf06776be266f5a67cff34e1ef2cbb60e49a"},
3939
"phoenix": {:hex, :phoenix, "1.7.17", "2fcdceecc6fb90bec26fab008f96abbd0fd93bc9956ec7985e5892cf545152ca", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.7", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:websock_adapter, "~> 0.5.3", [hex: :websock_adapter, repo: "hexpm", optional: false]}], "hexpm", "50e8ad537f3f7b0efb1509b2f75b5c918f697be6a45d48e49a30d3b7c0e464c9"},
4040
"phoenix_html": {:hex, :phoenix_html, "4.1.1", "4c064fd3873d12ebb1388425a8f2a19348cef56e7289e1998e2d2fa758aa982e", [:mix], [], "hexpm", "f2f2df5a72bc9a2f510b21497fd7d2b86d932ec0598f0210fed4114adc546c6f"},

test/envelope_test.exs

Lines changed: 26 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -117,14 +117,14 @@ defmodule Sentry.EnvelopeTest do
117117
test "works with transactions" do
118118
put_test_config(environment_name: "test")
119119

120-
spans = [
120+
root_span =
121121
%Sentry.Span{
122122
start_timestamp: 1_588_601_261.481_961,
123123
timestamp: 1_588_601_261.488_901,
124124
description: "GET /sockjs-node/info",
125125
op: "http",
126126
span_id: "b01b9f6349558cd1",
127-
parent_span_id: "b0e6f15b45c36b12",
127+
parent_span_id: nil,
128128
trace_id: "1e57b752bc6e4544bbaa246cd1d05dee",
129129
tags: %{"http.status_code" => "200"},
130130
data: %{
@@ -133,23 +133,27 @@ defmodule Sentry.EnvelopeTest do
133133
"type" => "xhr",
134134
"method" => "GET"
135135
}
136-
},
137-
%Sentry.Span{
138-
start_timestamp: 1_588_601_261.535_386,
139-
timestamp: 1_588_601_261.544_196,
140-
description: "Vue <App>",
141-
op: "update",
142-
span_id: "b980d4dec78d7344",
143-
parent_span_id: "9312d0d18bf51736",
144-
trace_id: "1e57b752bc6e4544bbaa246cd1d05dee"
145136
}
146-
]
147137

148-
transaction = %Sentry.Transaction{
149-
start_timestamp: System.system_time(:second),
150-
timestamp: System.system_time(:second),
151-
spans: spans
152-
}
138+
child_spans =
139+
[
140+
%Sentry.Span{
141+
start_timestamp: 1_588_601_261.535_386,
142+
timestamp: 1_588_601_261.544_196,
143+
description: "Vue <App>",
144+
op: "update",
145+
span_id: "b980d4dec78d7344",
146+
parent_span_id: "9312d0d18bf51736",
147+
trace_id: "1e57b752bc6e4544bbaa246cd1d05dee"
148+
}
149+
]
150+
151+
transaction =
152+
Sentry.Transaction.new(%{
153+
span_id: root_span.span_id,
154+
spans: [root_span | child_spans],
155+
transaction: "test-transaction"
156+
})
153157

154158
envelope = Envelope.from_transaction(transaction)
155159

@@ -159,16 +163,13 @@ defmodule Sentry.EnvelopeTest do
159163

160164
assert {:ok, decoded_transaction} = Jason.decode(transaction_line)
161165
assert decoded_transaction["type"] == "transaction"
162-
assert decoded_transaction["start_timestamp"] == transaction.start_timestamp
163-
assert decoded_transaction["timestamp"] == transaction.timestamp
164-
165-
assert [span1, span2] = decoded_transaction["spans"]
166+
assert decoded_transaction["start_timestamp"] == root_span.start_timestamp
167+
assert decoded_transaction["timestamp"] == root_span.timestamp
166168

167-
assert span1["start_timestamp"] == List.first(spans).start_timestamp
168-
assert span1["timestamp"] == List.first(spans).timestamp
169+
assert [span] = decoded_transaction["spans"]
169170

170-
assert span2["start_timestamp"] == List.last(spans).start_timestamp
171-
assert span2["timestamp"] == List.last(spans).timestamp
171+
assert span["start_timestamp"] == List.first(child_spans).start_timestamp
172+
assert span["timestamp"] == List.first(child_spans).timestamp
172173
end
173174
end
174175

test/sentry/client_report/sender_test.exs

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ defmodule Sentry.ClientReportTest do
44
import Sentry.TestHelpers
55

66
alias Sentry.ClientReport.Sender
7-
alias Sentry.{Event, Transaction}
7+
alias Sentry.{Event, Transaction, Span}
88

99
setup do
1010
original_retries =
@@ -19,6 +19,8 @@ defmodule Sentry.ClientReportTest do
1919
%{bypass: bypass}
2020
end
2121

22+
@span_id Sentry.UUID.uuid4_hex()
23+
2224
describe "record_discarded_events/2 + flushing" do
2325
test "succefully records the discarded event to the client report", %{bypass: bypass} do
2426
start_supervised!({Sender, name: :test_client_report})
@@ -28,10 +30,16 @@ defmodule Sentry.ClientReportTest do
2830
event_id: Sentry.UUID.uuid4_hex(),
2931
timestamp: "2024-10-12T13:21:13"
3032
},
31-
%Transaction{
32-
event_id: Sentry.UUID.uuid4_hex(),
33-
timestamp: "2024-10-12T13:21:13"
34-
}
33+
Transaction.new(%{
34+
span_id: @span_id,
35+
transaction: "test-transaction",
36+
spans: [%Span{
37+
span_id: @span_id,
38+
trace_id: Sentry.UUID.uuid4_hex(),
39+
start_timestamp: "2024-10-12T13:21:13",
40+
timestamp: "2024-10-12T13:21:13"
41+
}]
42+
})
3543
]
3644

3745
assert :ok = Sender.record_discarded_events(:before_send, events, :test_client_report)

test/sentry/opentelemetry/span_processor_test.exs

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -44,16 +44,18 @@ defmodule Sentry.Opentelemetry.SpanProcessorTest do
4444

4545
assert [%Sentry.Transaction{} = transaction] = Sentry.Test.pop_sentry_transactions()
4646

47-
assert_valid_iso8601(transaction.timestamp)
48-
assert_valid_iso8601(transaction.start_timestamp)
49-
assert transaction.timestamp > transaction.start_timestamp
50-
assert length(transaction.spans) == 1
51-
52-
assert_valid_trace_id(transaction.contexts.trace.trace_id)
53-
54-
assert [span] = transaction.spans
55-
56-
assert span.op == "child_instrumented_function_one"
47+
transaction_data = Sentry.Transaction.to_map(transaction)
48+
49+
assert transaction_data.event_id
50+
assert transaction_data.environment == "test"
51+
assert transaction_data.type == "transaction"
52+
assert transaction_data.op == "child_instrumented_function_one"
53+
assert transaction_data.transaction_info == %{source: :custom}
54+
assert_valid_iso8601(transaction_data.timestamp)
55+
assert_valid_iso8601(transaction_data.start_timestamp)
56+
assert transaction_data.timestamp > transaction_data.start_timestamp
57+
assert_valid_trace_id(transaction_data.contexts.trace.trace_id)
58+
assert length(transaction_data.spans) == 0
5759
end
5860

5961
test "sends captured spans as transactions with child spans" do
@@ -64,12 +66,16 @@ defmodule Sentry.Opentelemetry.SpanProcessorTest do
6466
TestEndpoint.instrumented_function()
6567

6668
assert [%Sentry.Transaction{} = transaction] = Sentry.Test.pop_sentry_transactions()
67-
assert_valid_iso8601(transaction.timestamp)
68-
assert_valid_iso8601(transaction.start_timestamp)
69-
assert transaction.timestamp > transaction.start_timestamp
70-
assert length(transaction.spans) == 3
7169

72-
[root_span, child_span_one, child_span_two] = transaction.spans
70+
transaction_data = Sentry.Transaction.to_map(transaction)
71+
72+
assert transaction_data.op == "instrumented_function"
73+
assert_valid_iso8601(transaction_data.timestamp)
74+
assert_valid_iso8601(transaction_data.start_timestamp)
75+
assert transaction_data.timestamp > transaction_data.start_timestamp
76+
assert length(transaction_data.spans) == 2
77+
78+
[child_span_one, child_span_two] = transaction_data.spans
7379
assert child_span_one.op == "child_instrumented_function_one"
7480
assert child_span_two.op == "child_instrumented_function_two"
7581
assert child_span_one.parent_span_id == transaction.contexts.trace.span_id
@@ -82,10 +88,10 @@ defmodule Sentry.Opentelemetry.SpanProcessorTest do
8288

8389
assert child_span_one.timestamp > child_span_one.start_timestamp
8490
assert child_span_two.timestamp > child_span_two.start_timestamp
85-
assert root_span.timestamp >= child_span_one.timestamp
86-
assert root_span.timestamp >= child_span_two.timestamp
87-
assert root_span.start_timestamp <= child_span_one.start_timestamp
88-
assert root_span.start_timestamp <= child_span_two.start_timestamp
91+
assert transaction_data.timestamp >= child_span_one.timestamp
92+
assert transaction_data.timestamp >= child_span_two.timestamp
93+
assert transaction_data.start_timestamp <= child_span_one.start_timestamp
94+
assert transaction_data.start_timestamp <= child_span_two.start_timestamp
8995

9096
assert_valid_trace_id(transaction.contexts.trace.trace_id)
9197
assert_valid_trace_id(child_span_one.trace_id)

test/sentry_test.exs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -240,9 +240,16 @@ defmodule SentryTest do
240240
setup do
241241
transaction =
242242
Sentry.Transaction.new(%{
243+
span_id: "root-span",
243244
transaction: "test-transaction",
244-
start_timestamp: System.system_time(:second),
245-
timestamp: System.system_time(:second)
245+
spans: [
246+
%Sentry.Span{
247+
span_id: "root-span",
248+
trace_id: "trace-id",
249+
start_timestamp: 1_234_567_891.123_456,
250+
timestamp: 1_234_567_891.123_456
251+
}
252+
]
246253
})
247254

248255
{:ok, transaction: transaction}

test_integrations/phoenix_app/lib/phoenix_app/application.ex

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

1616
# OpentelemetryBandit.setup()
17-
OpentelemetryPhoenix.setup()
17+
OpentelemetryPhoenix.setup(adapter: :bandit)
1818
OpentelemetryOban.setup()
1919
OpentelemetryEcto.setup([:phoenix_app, :repo], db_statement: :enabled)
2020

0 commit comments

Comments
 (0)