Skip to content

Commit 2ca83e4

Browse files
committed
Bump oversized stacktrace trunction to 500 on both sides
Revert "Remove stacktrace trimming from the SDK (#2714)" This reverts commit ed7a2db.
1 parent b92b953 commit 2ca83e4

3 files changed

Lines changed: 96 additions & 1 deletion

File tree

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@
2626
- Migrate from to_hash to to_h ([#2351](https://github.com/getsentry/sentry-ruby/pull/2351))
2727
- Add `before_send_check_in` for applying to `CheckInEvent` ([#2703](https://github.com/getsentry/sentry-ruby/pull/2703))
2828
- Returning a hash from `before_send` and `before_send_transaction` is no longer supported and will drop the event.
29-
- Remove stacktrace trimming ([#2714](https://github.com/getsentry/sentry-ruby/pull/2714))
3029
- `config.enabled_environments` now defaults to `nil` instead of `[]` for sending to all environments ([#2716](https://github.com/getsentry/sentry-ruby/pull/2716))
3130
- Requests which have response status codes in the inclusive ranges `[(301..303), (305..399), (401..404)]` will no longer create transactions by default. See `config.trace_ignore_status_codes` below to control what gets traced.
31+
- Stacktrace truncation for oversized events now takes 500 frames on each side instead of 250.
3232

3333
### Features
3434

sentry-ruby/lib/sentry/envelope/item.rb

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
module Sentry
44
# @api private
55
class Envelope::Item
6+
STACKTRACE_FRAME_LIMIT_ON_OVERSIZED_PAYLOAD = 1000
67
MAX_SERIALIZED_PAYLOAD_SIZE = 1024 * 1000
78

89
SIZE_LIMITS = Hash.new(MAX_SERIALIZED_PAYLOAD_SIZE).update(
@@ -43,6 +44,11 @@ def serialize
4344
result = to_s
4445
end
4546

47+
if result.bytesize > size_limit
48+
reduce_stacktrace!
49+
result = to_s
50+
end
51+
4652
[result, result.bytesize > size_limit]
4753
end
4854

@@ -61,5 +67,21 @@ def remove_breadcrumbs!
6167
payload.delete("breadcrumbs")
6268
end
6369
end
70+
71+
def reduce_stacktrace!
72+
if exceptions = payload.dig(:exception, :values) || payload.dig("exception", "values")
73+
exceptions.each do |exception|
74+
# in most cases there is only one exception (2 or 3 when have multiple causes), so we won't loop through this double condition much
75+
traces = exception.dig(:stacktrace, :frames) || exception.dig("stacktrace", "frames")
76+
77+
if traces && traces.size > STACKTRACE_FRAME_LIMIT_ON_OVERSIZED_PAYLOAD
78+
size_on_both_ends = STACKTRACE_FRAME_LIMIT_ON_OVERSIZED_PAYLOAD / 2
79+
traces.replace(
80+
traces[0..(size_on_both_ends - 1)] + traces[-size_on_both_ends..-1],
81+
)
82+
end
83+
end
84+
end
85+
end
6486
end
6587
end

sentry-ruby/spec/sentry/transport_spec.rb

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,79 @@
326326
end
327327
end
328328
end
329+
330+
context "due to stacktrace frames" do
331+
let(:event) { client.event_from_exception(SystemStackError.new("stack level too deep")) }
332+
let(:envelope) { subject.envelope_from_event(event) }
333+
334+
let(:in_app_pattern) do
335+
project_root = "/fake/project_root"
336+
Regexp.new("^(#{project_root}/)?#{Sentry::Configuration::APP_DIRS_PATTERN}")
337+
end
338+
let(:frame_list_limit) { 1000 }
339+
let(:frame_list_size) { frame_list_limit * 20 }
340+
341+
before do
342+
single_exception = event.exception.values[0]
343+
new_stacktrace = Sentry::StacktraceInterface.new(
344+
frames: frame_list_size.times.map do |zero_based_index|
345+
Sentry::StacktraceInterface::Frame.new(
346+
"/fake/path",
347+
Sentry::Backtrace::Line.parse("app.rb:#{zero_based_index + 1}:in `/'", in_app_pattern)
348+
)
349+
end,
350+
)
351+
single_exception.instance_variable_set(:@stacktrace, new_stacktrace)
352+
353+
serialized_result = JSON.generate(event.to_h)
354+
expect(serialized_result.bytesize).to be > Sentry::Envelope::Item::MAX_SERIALIZED_PAYLOAD_SIZE
355+
end
356+
357+
it "keeps some stacktrace frames and carry on" do
358+
data, _ = subject.serialize_envelope(envelope)
359+
expect(data.bytesize).to be < Sentry::Envelope::Item::MAX_SERIALIZED_PAYLOAD_SIZE
360+
361+
expect(envelope.items.count).to eq(1)
362+
363+
event_item = envelope.items.first
364+
frames = event_item.payload[:exception][:values][0][:stacktrace][:frames]
365+
expect(frames.length).to eq(frame_list_limit)
366+
367+
# Last N lines kept
368+
# N = Frame limit / 2
369+
expect(frames[-1][:lineno]).to eq(frame_list_size)
370+
expect(frames[-1][:filename]).to eq('app.rb')
371+
expect(frames[-1][:function]).to eq('/')
372+
#
373+
expect(frames[-(frame_list_limit / 2)][:lineno]).to eq(frame_list_size - ((frame_list_limit / 2) - 1))
374+
expect(frames[-(frame_list_limit / 2)][:filename]).to eq('app.rb')
375+
expect(frames[-(frame_list_limit / 2)][:function]).to eq('/')
376+
377+
# First N lines kept
378+
# N = Frame limit / 2
379+
expect(frames[0][:lineno]).to eq(1)
380+
expect(frames[0][:filename]).to eq('app.rb')
381+
expect(frames[0][:function]).to eq('/')
382+
expect(frames[(frame_list_limit / 2) - 1][:lineno]).to eq(frame_list_limit / 2)
383+
expect(frames[(frame_list_limit / 2) - 1][:filename]).to eq('app.rb')
384+
expect(frames[(frame_list_limit / 2) - 1][:function]).to eq('/')
385+
end
386+
387+
context "if it's still oversized" do
388+
before do
389+
1000.times do |i|
390+
event.contexts["context_#{i}"] = "s" * Sentry::Event::MAX_MESSAGE_SIZE_IN_BYTES
391+
end
392+
end
393+
394+
it "rejects the item and logs attributes size breakdown" do
395+
data, _ = subject.serialize_envelope(envelope)
396+
expect(data).to be_nil
397+
expect(io.string).not_to match(/Sending envelope with items \[event\]/)
398+
expect(io.string).to match(/tags: 2, contexts: 8208891, extra: 2/)
399+
end
400+
end
401+
end
329402
end
330403
end
331404

0 commit comments

Comments
 (0)