Skip to content

Commit c972743

Browse files
committed
update: add appsignal instrumentation only
1 parent 078e87f commit c972743

File tree

3 files changed

+6
-157
lines changed

3 files changed

+6
-157
lines changed

idempotency.gemspec

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,6 @@ Gem::Specification.new do |spec|
3939
spec.add_dependency 'dry-monitor'
4040
spec.add_dependency 'msgpack'
4141
spec.add_dependency 'redis'
42+
43+
spec.add_dependency 'appsignal', '>= 1.3.0'
4244
end

lib/idempotency.rb

Lines changed: 3 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ def self.notifier
3131

3232
setting :observability do
3333
setting :appsignal_enabled, default: false
34-
setting :sentry_enabled, default: false
3534
end
3635

3736
setting :default_lock_expiry, default: 300 # 5 minutes
@@ -149,36 +148,16 @@ def unquote(str)
149148
end
150149
end
151150

152-
# rubocop:disable Metrics/AbcSize
153-
def with_apm_instrumentation(name, action, &block)
154-
# Build nested instrumentation layers from innermost to outermost
155-
instrumented_block = block
156-
157-
# Wrap with Sentry if enabled
158-
if config.observability.sentry_enabled && defined?(Sentry)
159-
instrumented_block = lambda do
160-
Sentry.with_child_span(op: name, description: action) do |_span|
161-
block.call
162-
end
163-
rescue StandardError => e
164-
Sentry.capture_exception(e)
165-
raise
166-
end
167-
end
168-
169-
# Wrap with AppSignal if enabled (outermost layer)
151+
def with_apm_instrumentation(name, action, &)
170152
if config.observability.appsignal_enabled && defined?(Appsignal)
171153
Appsignal.instrument(name, action) do
172-
instrumented_block.call
154+
yield
173155
rescue StandardError => e
174156
Appsignal.set_error(e)
175157
raise
176158
end
177159
else
178-
# Execute the (potentially Sentry-wrapped) block
179-
instrumented_block.call
160+
yield
180161
end
181162
end
182-
# rubocop:enable Metrics/AbcSize
183163
end
184-
# rubocop:enable Metrics/ClassLength

spec/idempotency/apm_instrumentation_spec.rb

Lines changed: 1 addition & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,12 @@
2020
let(:block_result) { 'test_result' }
2121
let(:test_block) { -> { block_result } }
2222

23-
context 'when neither AppSignal nor Sentry is enabled' do
23+
context 'when AppSignal is not enabled' do
2424
before do
2525
Idempotency.configure do |config|
2626
config.redis_pool = redis_pool
2727
config.logger = Logger.new(nil)
2828
config.observability.appsignal_enabled = false
29-
config.observability.sentry_enabled = false
3029
end
3130
end
3231

@@ -48,7 +47,6 @@
4847
config.redis_pool = redis_pool
4948
config.logger = Logger.new(nil)
5049
config.observability.appsignal_enabled = true
51-
config.observability.sentry_enabled = false
5250
end
5351
end
5452

@@ -97,52 +95,6 @@
9795
end
9896
end
9997

100-
context 'when Sentry is enabled' do
101-
let(:mock_span) { double('Sentry::Span') }
102-
103-
before do
104-
stub_const('Sentry', double('Sentry'))
105-
allow(Sentry).to receive(:with_child_span).and_yield(mock_span)
106-
107-
Idempotency.configure do |config|
108-
config.redis_pool = redis_pool
109-
config.logger = Logger.new(nil)
110-
config.observability.appsignal_enabled = false
111-
config.observability.sentry_enabled = true
112-
end
113-
end
114-
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)
120-
121-
result = idempotency.send(:with_apm_instrumentation, 'test.operation', 'test') do
122-
test_block.call
123-
end
124-
125-
expect(result).to eq(block_result)
126-
end
127-
128-
it 'captures exceptions with Sentry and re-raises' do
129-
test_error = StandardError.new('test error')
130-
131-
expect(Sentry).to receive(:with_child_span).with(
132-
op: 'test.operation',
133-
description: 'test'
134-
).and_yield(mock_span)
135-
136-
expect(Sentry).to receive(:capture_exception).with(test_error)
137-
138-
expect do
139-
idempotency.send(:with_apm_instrumentation, 'test.operation', 'test') do
140-
raise test_error
141-
end
142-
end.to raise_error(StandardError, 'test error')
143-
end
144-
end
145-
14698
context 'when AppSignal is enabled but not defined' do
14799
before do
148100
hide_const('Appsignal')
@@ -151,28 +103,6 @@
151103
config.redis_pool = redis_pool
152104
config.logger = Logger.new(nil)
153105
config.observability.appsignal_enabled = true
154-
config.observability.sentry_enabled = false
155-
end
156-
end
157-
158-
it 'falls back to executing without instrumentation' do
159-
result = idempotency.send(:with_apm_instrumentation, 'test.operation', 'test') do
160-
test_block.call
161-
end
162-
163-
expect(result).to eq(block_result)
164-
end
165-
end
166-
167-
context 'when Sentry is enabled but not defined' do
168-
before do
169-
hide_const('Sentry')
170-
171-
Idempotency.configure do |config|
172-
config.redis_pool = redis_pool
173-
config.logger = Logger.new(nil)
174-
config.observability.appsignal_enabled = false
175-
config.observability.sentry_enabled = true
176106
end
177107
end
178108

@@ -184,67 +114,5 @@
184114
expect(result).to eq(block_result)
185115
end
186116
end
187-
188-
context 'when both AppSignal and Sentry are enabled' do
189-
let(:mock_span) { double('Sentry::Span') }
190-
191-
before do
192-
stub_const('Appsignal', double('Appsignal'))
193-
stub_const('Sentry', double('Sentry'))
194-
allow(Appsignal).to receive(:instrument).and_yield
195-
allow(Sentry).to receive(:with_child_span).and_yield(mock_span)
196-
197-
Idempotency.configure do |config|
198-
config.redis_pool = redis_pool
199-
config.logger = Logger.new(nil)
200-
config.observability.appsignal_enabled = true
201-
config.observability.sentry_enabled = true
202-
end
203-
end
204-
205-
it 'instruments in both AppSignal and Sentry (nested)' do
206-
# Expect Sentry to be set up (inner layer)
207-
expect(Sentry).to receive(:with_child_span).with(
208-
op: 'test.operation',
209-
description: 'test'
210-
).and_yield(mock_span)
211-
212-
# Expect AppSignal to wrap everything (outer layer)
213-
expect(Appsignal).to receive(:instrument).with(
214-
'test.operation',
215-
'test'
216-
).and_yield
217-
218-
result = idempotency.send(:with_apm_instrumentation, 'test.operation', 'test') do
219-
test_block.call
220-
end
221-
222-
expect(result).to eq(block_result)
223-
end
224-
225-
it 'reports errors to both AppSignal and Sentry' do
226-
test_error = StandardError.new('test error')
227-
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)
233-
expect(Sentry).to receive(:capture_exception).with(test_error)
234-
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
240-
expect(Appsignal).to receive(:set_error).with(test_error)
241-
242-
expect do
243-
idempotency.send(:with_apm_instrumentation, 'test.operation', 'test') do
244-
raise test_error
245-
end
246-
end.to raise_error(StandardError, 'test error')
247-
end
248-
end
249117
end
250118
end

0 commit comments

Comments
 (0)