Skip to content

Commit f0d53ce

Browse files
authored
Merge pull request rmosolgo#4853 from porter77/master
Add support for transaction names in Sentry tracing
2 parents fdd8d04 + a7be59e commit f0d53ce

File tree

3 files changed

+90
-18
lines changed

3 files changed

+90
-18
lines changed

lib/graphql/tracing/sentry_trace.rb

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,31 @@ module Tracing
55
module SentryTrace
66
include PlatformTrace
77

8+
# @param set_transaction_name [Boolean] If true, the GraphQL operation name will be used as the transaction name.
9+
# This is not advised if you run more than one query per HTTP request, for example, with `graphql-client` or multiplexing.
10+
# It can also be specified per-query with `context[:set_sentry_transaction_name]`.
11+
def initialize(set_transaction_name: false, **_rest)
12+
@set_transaction_name = set_transaction_name
13+
super
14+
end
15+
16+
def execute_query(**data)
17+
set_this_txn_name = data[:query].context[:set_sentry_transaction_name]
18+
if set_this_txn_name == true || (set_this_txn_name.nil? && @set_transaction_name)
19+
Sentry.configure_scope do |scope|
20+
scope.set_transaction_name(transaction_name(data[:query]))
21+
end
22+
end
23+
instrument_execution("graphql.execute", "execute_query", data) { super }
24+
end
25+
826
{
927
"lex" => "graphql.lex",
1028
"parse" => "graphql.parse",
1129
"validate" => "graphql.validate",
1230
"analyze_query" => "graphql.analyze",
1331
"analyze_multiplex" => "graphql.analyze_multiplex",
1432
"execute_multiplex" => "graphql.execute_multiplex",
15-
"execute_query" => "graphql.execute",
1633
"execute_query_lazy" => "graphql.execute"
1734
}.each do |trace_method, platform_key|
1835
module_eval <<-RUBY, __FILE__, __LINE__

spec/graphql/tracing/sentry_trace_spec.rb

Lines changed: 60 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
# frozen_string_literal: true
2-
32
require "spec_helper"
43

54
describe GraphQL::Tracing::SentryTrace do
6-
class SentryTraceTestSchema < GraphQL::Schema
5+
module SentryTraceTest
76
class Thing < GraphQL::Schema::Object
87
field :str, String
98
def str; "blah"; end
@@ -20,31 +19,38 @@ def int
2019
def thing; :thing; end
2120
end
2221

23-
query(Query)
22+
class SchemaWithoutTransactionName < GraphQL::Schema
23+
query(Query)
2424

25-
module OtherTrace
26-
def execute_query(query:)
27-
query.context[:other_trace_ran] = true
28-
super
25+
module OtherTrace
26+
def execute_query(query:)
27+
query.context[:other_trace_ran] = true
28+
super
29+
end
2930
end
31+
trace_with OtherTrace
32+
trace_with GraphQL::Tracing::SentryTrace
33+
end
34+
35+
class SchemaWithTransactionName < GraphQL::Schema
36+
query(Query)
37+
trace_with(GraphQL::Tracing::SentryTrace, set_transaction_name: true)
3038
end
31-
trace_with OtherTrace
32-
trace_with GraphQL::Tracing::SentryTrace
3339
end
3440

3541
before do
3642
Sentry.clear_all
3743
end
3844

3945
it "works with other trace modules" do
40-
res = SentryTraceTestSchema.execute("{ int }")
46+
res = SentryTraceTest::SchemaWithoutTransactionName.execute("{ int }")
4147
assert res.context[:other_trace_ran]
4248
end
4349

4450
describe "When Sentry is not configured" do
4551
it "does not initialize any spans" do
4652
Sentry.stub(:initialized?, false) do
47-
SentryTraceTestSchema.execute("{ int thing { str } }")
53+
SentryTraceTest::SchemaWithoutTransactionName.execute("{ int thing { str } }")
4854
assert_equal [], Sentry::SPAN_DATA
4955
assert_equal [], Sentry::SPAN_DESCRIPTIONS
5056
assert_equal [], Sentry::SPAN_OPS
@@ -55,7 +61,7 @@ def execute_query(query:)
5561
describe "When Sentry.with_child_span returns nil" do
5662
it "does not initialize any spans" do
5763
Sentry.stub(:with_child_span, nil) do
58-
SentryTraceTestSchema.execute("{ int thing { str } }")
64+
SentryTraceTest::SchemaWithoutTransactionName.execute("{ int thing { str } }")
5965
assert_equal [], Sentry::SPAN_DATA
6066
assert_equal [], Sentry::SPAN_DESCRIPTIONS
6167
assert_equal [], Sentry::SPAN_OPS
@@ -64,7 +70,7 @@ def execute_query(query:)
6470
end
6571

6672
it "sets the expected spans" do
67-
SentryTraceTestSchema.execute("{ int thing { str } }")
73+
SentryTraceTest::SchemaWithoutTransactionName.execute("{ int thing { str } }")
6874
expected_span_ops = [
6975
"graphql.execute_multiplex",
7076
"graphql.analyze_multiplex",
@@ -83,13 +89,13 @@ def execute_query(query:)
8389
end
8490

8591
it "sets span descriptions for an anonymous query" do
86-
SentryTraceTestSchema.execute("{ int }")
92+
SentryTraceTest::SchemaWithoutTransactionName.execute("{ int }")
8793

8894
assert_equal ["query", "query"], Sentry::SPAN_DESCRIPTIONS
8995
end
9096

9197
it "sets span data for an anonymous query" do
92-
SentryTraceTestSchema.execute("{ int }")
98+
SentryTraceTest::SchemaWithoutTransactionName.execute("{ int }")
9399
expected_span_data = [
94100
["graphql.document", "{ int }"],
95101
["graphql.operation.type", "query"]
@@ -99,13 +105,13 @@ def execute_query(query:)
99105
end
100106

101107
it "sets span descriptions for a named query" do
102-
SentryTraceTestSchema.execute("query Ab { int }")
108+
SentryTraceTest::SchemaWithoutTransactionName.execute("query Ab { int }")
103109

104110
assert_equal ["query Ab", "query Ab"], Sentry::SPAN_DESCRIPTIONS
105111
end
106112

107113
it "sets span data for a named query" do
108-
SentryTraceTestSchema.execute("query Ab { int }")
114+
SentryTraceTest::SchemaWithoutTransactionName.execute("query Ab { int }")
109115
expected_span_data = [
110116
["graphql.document", "query Ab { int }"],
111117
["graphql.operation.name", "Ab"],
@@ -114,4 +120,41 @@ def execute_query(query:)
114120

115121
assert_equal expected_span_data.sort, Sentry::SPAN_DATA.sort
116122
end
123+
124+
it "can leave the transaction name in place" do
125+
SentryTraceTest::SchemaWithoutTransactionName.execute "query X { int }"
126+
assert_equal [], Sentry::TRANSACTION_NAMES
127+
end
128+
129+
it "can override the transaction name" do
130+
SentryTraceTest::SchemaWithTransactionName.execute "query X { int }"
131+
assert_equal ["GraphQL/query.X"], Sentry::TRANSACTION_NAMES
132+
end
133+
134+
it "can override the transaction name per query" do
135+
# Override with `false`
136+
SentryTraceTest::SchemaWithTransactionName.execute "{ int }", context: { set_sentry_transaction_name: false }
137+
assert_equal [], Sentry::TRANSACTION_NAMES
138+
# Override with `true`
139+
SentryTraceTest::SchemaWithoutTransactionName.execute "{ int }", context: { set_sentry_transaction_name: true }
140+
assert_equal ["GraphQL/query.anonymous"], Sentry::TRANSACTION_NAMES
141+
end
142+
143+
it "falls back to a :tracing_fallback_transaction_name when provided" do
144+
SentryTraceTest::SchemaWithTransactionName.execute("{ int }", context: { tracing_fallback_transaction_name: "Abcd" })
145+
assert_equal ["GraphQL/query.Abcd"], Sentry::TRANSACTION_NAMES
146+
end
147+
148+
it "does not use the :tracing_fallback_transaction_name if an operation name is present" do
149+
SentryTraceTest::SchemaWithTransactionName.execute(
150+
"query Ab { int }",
151+
context: { tracing_fallback_transaction_name: "Cd" }
152+
)
153+
assert_equal ["GraphQL/query.Ab"], Sentry::TRANSACTION_NAMES
154+
end
155+
156+
it "does not require a :tracing_fallback_transaction_name even if an operation name is not present" do
157+
SentryTraceTest::SchemaWithTransactionName.execute("{ int }")
158+
assert_equal ["GraphQL/query.anonymous"], Sentry::TRANSACTION_NAMES
159+
end
117160
end

spec/support/sentry.rb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ module Sentry
99
SPAN_OPS = []
1010
SPAN_DATA = []
1111
SPAN_DESCRIPTIONS = []
12+
TRANSACTION_NAMES = []
1213

1314
def self.initialized?
1415
true
@@ -23,10 +24,15 @@ def self.with_child_span(**args, &block)
2324
yield DummySpan.new
2425
end
2526

27+
def self.configure_scope(&block)
28+
yield DummyScope.new
29+
end
30+
2631
def self.clear_all
2732
SPAN_DATA.clear
2833
SPAN_DESCRIPTIONS.clear
2934
SPAN_OPS.clear
35+
TRANSACTION_NAMES.clear
3036
end
3137

3238
class DummySpan
@@ -42,4 +48,10 @@ def finish
4248
# no-op
4349
end
4450
end
51+
52+
class DummyScope
53+
def set_transaction_name(name)
54+
TRANSACTION_NAMES << name
55+
end
56+
end
4557
end

0 commit comments

Comments
 (0)