Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions sentry-ruby/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

Individual gem's changelog has been deprecated. Please check the [project changelog](https://github.com/getsentry/sentry-ruby/blob/master/CHANGELOG.md).

## Unreleased
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This changelog has been deprecated. Can you update the top-level one instead?


### Bug Fixes

- Fix: Handle exception with large stacktrace without dropping entire item [#1806](https://github.com/getsentry/sentry-ruby/pull/1806)

## 4.4.2

- Fix NoMethodError when SDK's dsn is nil [#1433](https://github.com/getsentry/sentry-ruby/pull/1433)
Expand Down
20 changes: 20 additions & 0 deletions sentry-ruby/lib/sentry/transport.rb
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,26 @@ def serialize_envelope(envelope)
result = item.to_s
end

if result.bytesize > Event::MAX_SERIALIZED_PAYLOAD_SIZE
if single_exceptions = item.payload.dig(:exception, :values)
single_exceptions.each do |single_exception|
traces = single_exception.dig(:stacktrace, :frames)
if traces && traces.size > 0
single_exception.delete(:stacktrace)
end
end
elsif single_exceptions = item.payload.dig("exception", "values")
single_exceptions.each do |single_exception|
traces = single_exception.dig("stacktrace", "frames")
if traces && traces.size > 0
single_exception.delete("stacktrace")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also don't have an answer for this yet, but I'd like to avoid duplicating all the payload truncation logic because of key types.

Copy link
Member

@sl0thentr0py sl0thentr0py May 3, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

python has some overly general serialization logic that drops fields based on nested hash breadth/depth/byte length logic, but I don't like that so much either because it's really hard to read.

end
end
end

result = item.to_s
end

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't comment lower so I just leave it here: we also need to update the message form:

is still oversized without breadcrumbs

to

is still oversized without breadcrumbs and stracktrace

if result.bytesize > Event::MAX_SERIALIZED_PAYLOAD_SIZE
size_breakdown = item.payload.map do |key, value|
"#{key}: #{JSON.generate(value).bytesize}"
Expand Down
102 changes: 79 additions & 23 deletions sentry-ruby/spec/sentry/transport_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -100,40 +100,96 @@
end

context "oversized event" do
let(:event) { client.event_from_message("foo") }
let(:envelope) { subject.envelope_from_event(event) }
context "due to breadcrumb" do
let(:event) { client.event_from_message("foo") }
let(:envelope) { subject.envelope_from_event(event) }

before do
event.breadcrumbs = Sentry::BreadcrumbBuffer.new(100)
100.times do |i|
event.breadcrumbs.record Sentry::Breadcrumb.new(category: i.to_s, message: "x" * Sentry::Event::MAX_MESSAGE_SIZE_IN_BYTES)
before do
event.breadcrumbs = Sentry::BreadcrumbBuffer.new(100)
100.times do |i|
event.breadcrumbs.record Sentry::Breadcrumb.new(category: i.to_s, message: "x" * Sentry::Event::MAX_MESSAGE_SIZE_IN_BYTES)
end
serialized_result = JSON.generate(event.to_hash)
expect(serialized_result.bytesize).to be > Sentry::Event::MAX_SERIALIZED_PAYLOAD_SIZE
end
serialized_result = JSON.generate(event.to_hash)
expect(serialized_result.bytesize).to be > Sentry::Event::MAX_SERIALIZED_PAYLOAD_SIZE
end

it "removes breadcrumbs and carry on" do
data, _ = subject.serialize_envelope(envelope)
expect(data.bytesize).to be < Sentry::Event::MAX_SERIALIZED_PAYLOAD_SIZE
it "removes breadcrumbs and carry on" do
data, _ = subject.serialize_envelope(envelope)
expect(data.bytesize).to be < Sentry::Event::MAX_SERIALIZED_PAYLOAD_SIZE

expect(envelope.items.count).to eq(1)

event_item = envelope.items.first
expect(event_item.payload[:breadcrumbs]).to be_nil
end

expect(envelope.items.count).to eq(1)
context "if it's still oversized" do
before do
100.times do |i|
event.contexts["context_#{i}"] = "s" * Sentry::Event::MAX_MESSAGE_SIZE_IN_BYTES
end
end

event_item = envelope.items.first
expect(event_item.payload[:breadcrumbs]).to be_nil
it "rejects the item and logs attributes size breakdown" do
data, _ = subject.serialize_envelope(envelope)
expect(data).to be_nil
expect(io.string).not_to match(/Sending envelope with items \[event\]/)
expect(io.string).to match(/tags: 2, contexts: 820791, extra: 2/)
end
end
end

context "if it's still oversized" do
context "due to stacktrace frames" do
let(:event) { client.event_from_exception(SystemStackError.new("stack level too deep")) }
let(:envelope) { subject.envelope_from_event(event) }

let(:unparsed_app_line) do
"app.rb:12:in `/'"
end
let(:in_app_pattern) do
project_root = "/fake/project_root"
Regexp.new("^(#{project_root}/)?#{Sentry::Backtrace::APP_DIRS_PATTERN}")
end

before do
100.times do |i|
event.contexts["context_#{i}"] = "s" * Sentry::Event::MAX_MESSAGE_SIZE_IN_BYTES
end
single_exception = event.exception.instance_variable_get(:@values)[0]
new_stacktrace = Sentry::StacktraceInterface.new(
frames: [
Sentry::StacktraceInterface::Frame.new(
"/fake/path/#{ "x" * Sentry::Event::MAX_SERIALIZED_PAYLOAD_SIZE }",
Sentry::Backtrace::Line.parse(unparsed_app_line, in_app_pattern),
),
],
)
single_exception.instance_variable_set(:@stacktrace, new_stacktrace)

serialized_result = JSON.generate(event.to_hash)
expect(serialized_result.bytesize).to be > Sentry::Event::MAX_SERIALIZED_PAYLOAD_SIZE
end

it "rejects the item and logs attributes size breakdown" do
it "removes stacktrace frames and carry on" do
data, _ = subject.serialize_envelope(envelope)
expect(data).to be_nil
expect(io.string).not_to match(/Sending envelope with items \[event\]/)
expect(io.string).to match(/tags: 2, contexts: 820791, extra: 2/)
expect(data.bytesize).to be < Sentry::Event::MAX_SERIALIZED_PAYLOAD_SIZE

expect(envelope.items.count).to eq(1)

event_item = envelope.items.first
expect(event_item.payload[:exception][:values][0][:stacktrace]).to be_nil
end

context "if it's still oversized" do
before do
100.times do |i|
event.contexts["context_#{i}"] = "s" * Sentry::Event::MAX_MESSAGE_SIZE_IN_BYTES
end
end

it "rejects the item and logs attributes size breakdown" do
data, _ = subject.serialize_envelope(envelope)
expect(data).to be_nil
expect(io.string).not_to match(/Sending envelope with items \[event\]/)
expect(io.string).to match(/tags: 2, contexts: 820791, extra: 2/)
end
end
end
end
Expand Down