Skip to content

Commit 078e87f

Browse files
committed
update syntax for sentry instrument + Appsignal transaction name convension
1 parent fe996d4 commit 078e87f

2 files changed

Lines changed: 55 additions & 94 deletions

File tree

lib/idempotency.rb

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ def use_cache(request, request_identifiers, lock_duration: nil, action: nil)
7171
duration_start = Process.clock_gettime(::Process::CLOCK_MONOTONIC)
7272
action_name = action || "#{request.request_method}:#{request.path}"
7373

74-
with_apm_instrumentation('idempotency.use_cache', action: action_name) do
74+
with_apm_instrumentation('idempotency.use_cache', action_name) do
7575
return yield unless cache_request?(request)
7676

7777
request_headers = request.env
@@ -150,30 +150,25 @@ def unquote(str)
150150
end
151151

152152
# rubocop:disable Metrics/AbcSize
153-
def with_apm_instrumentation(name, tags = {}, &block)
153+
def with_apm_instrumentation(name, action, &block)
154154
# Build nested instrumentation layers from innermost to outermost
155155
instrumented_block = block
156156

157157
# Wrap with Sentry if enabled
158158
if config.observability.sentry_enabled && defined?(Sentry)
159159
instrumented_block = lambda do
160-
transaction = Sentry.start_transaction(name: name, op: 'idempotency', **tags)
161-
Sentry.get_current_scope.set_span(transaction)
162-
163-
begin
160+
Sentry.with_child_span(op: name, description: action) do |_span|
164161
block.call
165-
rescue StandardError => e
166-
Sentry.capture_exception(e)
167-
raise
168-
ensure
169-
transaction.finish
170162
end
163+
rescue StandardError => e
164+
Sentry.capture_exception(e)
165+
raise
171166
end
172167
end
173168

174169
# Wrap with AppSignal if enabled (outermost layer)
175170
if config.observability.appsignal_enabled && defined?(Appsignal)
176-
Appsignal.monitor_transaction(name, tags) do
171+
Appsignal.instrument(name, action) do
177172
instrumented_block.call
178173
rescue StandardError => e
179174
Appsignal.set_error(e)

spec/idempotency/apm_instrumentation_spec.rb

Lines changed: 48 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
end
3232

3333
it 'executes the block without instrumentation' do
34-
result = idempotency.send(:with_apm_instrumentation, 'test.operation', action: 'test') do
34+
result = idempotency.send(:with_apm_instrumentation, 'test.operation', 'test') do
3535
test_block.call
3636
end
3737

@@ -42,6 +42,7 @@
4242
context 'when AppSignal is enabled' do
4343
before do
4444
stub_const('Appsignal', double('Appsignal'))
45+
allow(Appsignal).to receive(:instrument).and_yield
4546

4647
Idempotency.configure do |config|
4748
config.redis_pool = redis_pool
@@ -51,13 +52,12 @@
5152
end
5253
end
5354

54-
it 'wraps execution in AppSignal transaction' do
55-
expect(Appsignal).to receive(:monitor_transaction).with(
56-
'test.operation',
57-
{ action: 'test' }
55+
it 'wraps execution in AppSignal instrumentation' do
56+
expect(Appsignal).to receive(:instrument).with(
57+
'test.operation', 'test'
5858
).and_yield
5959

60-
result = idempotency.send(:with_apm_instrumentation, 'test.operation', action: 'test') do
60+
result = idempotency.send(:with_apm_instrumentation, 'test.operation', 'test') do
6161
test_block.call
6262
end
6363

@@ -67,15 +67,14 @@
6767
it 'reports errors to AppSignal and re-raises' do
6868
test_error = StandardError.new('test error')
6969

70-
expect(Appsignal).to receive(:monitor_transaction).with(
71-
'test.operation',
72-
{ action: 'test' }
70+
expect(Appsignal).to receive(:instrument).with(
71+
'test.operation', 'test'
7372
).and_yield
7473

7574
expect(Appsignal).to receive(:set_error).with(test_error)
7675

7776
expect do
78-
idempotency.send(:with_apm_instrumentation, 'test.operation', action: 'test') do
77+
idempotency.send(:with_apm_instrumentation, 'test.operation', 'test') do
7978
raise test_error
8079
end
8180
end.to raise_error(StandardError, 'test error')
@@ -84,30 +83,26 @@
8483
it 'handles exceptions during error reporting gracefully' do
8584
test_error = StandardError.new('test error')
8685

87-
expect(Appsignal).to receive(:monitor_transaction).with(
88-
'test.operation',
89-
{ action: 'test' }
86+
expect(Appsignal).to receive(:instrument).with(
87+
'test.operation', 'test'
9088
).and_yield
9189

9290
expect(Appsignal).to receive(:set_error).with(test_error).and_raise('AppSignal error')
9391

9492
expect do
95-
idempotency.send(:with_apm_instrumentation, 'test.operation', action: 'test') do
93+
idempotency.send(:with_apm_instrumentation, 'test.operation', 'test') do
9694
raise test_error
9795
end
9896
end.to raise_error('AppSignal error')
9997
end
10098
end
10199

102100
context 'when Sentry is enabled' do
103-
let(:mock_transaction) { double('Sentry::Transaction', finish: true) }
104-
let(:mock_scope) { double('Sentry::Scope') }
101+
let(:mock_span) { double('Sentry::Span') }
105102

106103
before do
107104
stub_const('Sentry', double('Sentry'))
108-
allow(Sentry).to receive(:start_transaction).and_return(mock_transaction)
109-
allow(Sentry).to receive(:get_current_scope).and_return(mock_scope)
110-
allow(mock_scope).to receive(:set_span)
105+
allow(Sentry).to receive(:with_child_span).and_yield(mock_span)
111106

112107
Idempotency.configure do |config|
113108
config.redis_pool = redis_pool
@@ -117,18 +112,13 @@
117112
end
118113
end
119114

120-
it 'wraps execution in Sentry transaction' do
121-
expect(Sentry).to receive(:start_transaction).with(
122-
name: 'test.operation',
123-
op: 'idempotency',
124-
action: 'test'
125-
).and_return(mock_transaction)
115+
it 'wraps execution in Sentry child span' do
116+
expect(Sentry).to receive(:with_child_span).with(
117+
op: 'test.operation',
118+
description: 'test'
119+
).and_yield(mock_span)
126120

127-
expect(Sentry).to receive(:get_current_scope).and_return(mock_scope)
128-
expect(mock_scope).to receive(:set_span).with(mock_transaction)
129-
expect(mock_transaction).to receive(:finish)
130-
131-
result = idempotency.send(:with_apm_instrumentation, 'test.operation', action: 'test') do
121+
result = idempotency.send(:with_apm_instrumentation, 'test.operation', 'test') do
132122
test_block.call
133123
end
134124

@@ -138,35 +128,15 @@
138128
it 'captures exceptions with Sentry and re-raises' do
139129
test_error = StandardError.new('test error')
140130

141-
expect(Sentry).to receive(:start_transaction).with(
142-
name: 'test.operation',
143-
op: 'idempotency',
144-
action: 'test'
145-
).and_return(mock_transaction)
131+
expect(Sentry).to receive(:with_child_span).with(
132+
op: 'test.operation',
133+
description: 'test'
134+
).and_yield(mock_span)
146135

147-
expect(Sentry).to receive(:get_current_scope).and_return(mock_scope)
148-
expect(mock_scope).to receive(:set_span).with(mock_transaction)
149136
expect(Sentry).to receive(:capture_exception).with(test_error)
150-
expect(mock_transaction).to receive(:finish)
151137

152138
expect do
153-
idempotency.send(:with_apm_instrumentation, 'test.operation', action: 'test') do
154-
raise test_error
155-
end
156-
end.to raise_error(StandardError, 'test error')
157-
end
158-
159-
it 'ensures transaction is finished even when error occurs' do
160-
test_error = StandardError.new('test error')
161-
162-
expect(Sentry).to receive(:start_transaction).and_return(mock_transaction)
163-
expect(Sentry).to receive(:get_current_scope).and_return(mock_scope)
164-
expect(mock_scope).to receive(:set_span).with(mock_transaction)
165-
expect(Sentry).to receive(:capture_exception).with(test_error)
166-
expect(mock_transaction).to receive(:finish)
167-
168-
expect do
169-
idempotency.send(:with_apm_instrumentation, 'test.operation', action: 'test') do
139+
idempotency.send(:with_apm_instrumentation, 'test.operation', 'test') do
170140
raise test_error
171141
end
172142
end.to raise_error(StandardError, 'test error')
@@ -186,7 +156,7 @@
186156
end
187157

188158
it 'falls back to executing without instrumentation' do
189-
result = idempotency.send(:with_apm_instrumentation, 'test.operation', action: 'test') do
159+
result = idempotency.send(:with_apm_instrumentation, 'test.operation', 'test') do
190160
test_block.call
191161
end
192162

@@ -207,7 +177,7 @@
207177
end
208178

209179
it 'falls back to executing without instrumentation' do
210-
result = idempotency.send(:with_apm_instrumentation, 'test.operation', action: 'test') do
180+
result = idempotency.send(:with_apm_instrumentation, 'test.operation', 'test') do
211181
test_block.call
212182
end
213183

@@ -216,15 +186,13 @@
216186
end
217187

218188
context 'when both AppSignal and Sentry are enabled' do
219-
let(:mock_transaction) { double('Sentry::Transaction', finish: true) }
220-
let(:mock_scope) { double('Sentry::Scope') }
189+
let(:mock_span) { double('Sentry::Span') }
221190

222191
before do
223192
stub_const('Appsignal', double('Appsignal'))
224193
stub_const('Sentry', double('Sentry'))
225-
allow(Sentry).to receive(:start_transaction).and_return(mock_transaction)
226-
allow(Sentry).to receive(:get_current_scope).and_return(mock_scope)
227-
allow(mock_scope).to receive(:set_span)
194+
allow(Appsignal).to receive(:instrument).and_yield
195+
allow(Sentry).to receive(:with_child_span).and_yield(mock_span)
228196

229197
Idempotency.configure do |config|
230198
config.redis_pool = redis_pool
@@ -236,23 +204,18 @@
236204

237205
it 'instruments in both AppSignal and Sentry (nested)' do
238206
# Expect Sentry to be set up (inner layer)
239-
expect(Sentry).to receive(:start_transaction).with(
240-
name: 'test.operation',
241-
op: 'idempotency',
242-
action: 'test'
243-
).and_return(mock_transaction)
244-
245-
expect(Sentry).to receive(:get_current_scope).and_return(mock_scope)
246-
expect(mock_scope).to receive(:set_span).with(mock_transaction)
247-
expect(mock_transaction).to receive(:finish)
207+
expect(Sentry).to receive(:with_child_span).with(
208+
op: 'test.operation',
209+
description: 'test'
210+
).and_yield(mock_span)
248211

249212
# Expect AppSignal to wrap everything (outer layer)
250-
expect(Appsignal).to receive(:monitor_transaction).with(
213+
expect(Appsignal).to receive(:instrument).with(
251214
'test.operation',
252-
{ action: 'test' }
215+
'test'
253216
).and_yield
254217

255-
result = idempotency.send(:with_apm_instrumentation, 'test.operation', action: 'test') do
218+
result = idempotency.send(:with_apm_instrumentation, 'test.operation', 'test') do
256219
test_block.call
257220
end
258221

@@ -262,19 +225,22 @@
262225
it 'reports errors to both AppSignal and Sentry' do
263226
test_error = StandardError.new('test error')
264227

265-
# Expect Sentry to capture the exception
266-
expect(Sentry).to receive(:start_transaction).and_return(mock_transaction)
267-
expect(Sentry).to receive(:get_current_scope).and_return(mock_scope)
268-
expect(mock_scope).to receive(:set_span).with(mock_transaction)
228+
# Expect Sentry to capture the exception (inner layer)
229+
expect(Sentry).to receive(:with_child_span).with(
230+
op: 'test.operation',
231+
description: 'test'
232+
).and_yield(mock_span)
269233
expect(Sentry).to receive(:capture_exception).with(test_error)
270-
expect(mock_transaction).to receive(:finish)
271234

272-
# Expect AppSignal to also capture the exception
273-
expect(Appsignal).to receive(:monitor_transaction).and_yield
235+
# Expect AppSignal to also capture the exception (outer layer)
236+
expect(Appsignal).to receive(:instrument).with(
237+
'test.operation',
238+
'test'
239+
).and_yield
274240
expect(Appsignal).to receive(:set_error).with(test_error)
275241

276242
expect do
277-
idempotency.send(:with_apm_instrumentation, 'test.operation', action: 'test') do
243+
idempotency.send(:with_apm_instrumentation, 'test.operation', 'test') do
278244
raise test_error
279245
end
280246
end.to raise_error(StandardError, 'test error')

0 commit comments

Comments
 (0)