Skip to content

feat: gzip file attachments compressed on the background worker - #5619

Closed
lgarczyn wants to merge 1 commit into
getsentry:mainfrom
lgarczyn:feat/gzip-file-attachment
Closed

lgarczyn wants to merge 1 commit into
getsentry:mainfrom
lgarczyn:feat/gzip-file-attachment

Conversation

@lgarczyn

Copy link
Copy Markdown
Contributor

Adds GzipFileAttachmentContent, a FileAttachmentContent whose stream gzips the file as it is read, plus an AddAttachment(filePath, compress: true) overload on Scope and SentryHint.

Ai-generated (Fable)

Why

We like to attach logs to crashes. But it's expensive as hell, even if the logs are the same thing over and over.

Compressing it on the capturing thread is not an option either, since IAttachmentContent.GetStream() runs synchronously inside CaptureEvent. The SDK already buffers non-seekable payloads on its background worker when serializing the envelope, so a stream that compresses lazily on Read is entirely outside the main thread.

What

  • GzipFileAttachmentContent : FileAttachmentContent. Opens the file when the event is captured (same as the base class, so a missing file logs and skips the attachment), reads and compresses only when the envelope is serialized. Because it is a FileAttachmentContent, scope observers that sync file attachments to native SDKs still receive the path of the uncompressed file.
  • FileAttachmentContent.GetStream() becomes virtual.
  • Scope.AddAttachment(string filePath, bool compress, ...) and SentryHint.AddAttachment(string filePath, bool compress, ...). A compressed attachment is named <file>.gz with content type application/gzip unless one is given.
  • Internal GzipReadStream: read-only, non-seekable, pulls the source through GZipStream chunk by chunk.

Tests

Round trip through gunzip for empty, small and multi-chunk inputs, small-buffer reads, no read at construction, file not locked, envelope round trip with the length header matching the compressed bytes, the scope observer receiving a FileAttachmentContent with the raw path. API approval snapshots updated for all target frameworks.

Changelog Entry

  • feat: GzipFileAttachmentContent and Scope/SentryHint.AddAttachment(filePath, compress: true) attach a file gzip-compressed, with the compression done on the background worker

Adds GzipFileAttachmentContent, a FileAttachmentContent whose stream gzips the file as it is read, and an AddAttachment(filePath, compress) overload on Scope and SentryHint. The file is opened when the event is captured but only read and compressed when the envelope is serialized on the background worker, so a large log costs the capturing thread nothing. Scope observers that sync file attachments to native SDKs still receive the uncompressed path.
@github-actions github-actions Bot added the risk: high PR risk score: high label Sep 23, 2026
@codecov

codecov Bot commented Sep 23, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 91.04478% with 6 lines in your changes missing coverage. Please review.
✅ Project coverage is 74.91%. Comparing base (0a820d4) to head (898940a).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
src/Sentry/Internal/GzipReadStream.cs 85.00% 6 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #5619      +/-   ##
==========================================
+ Coverage   74.87%   74.91%   +0.04%     
==========================================
  Files         515      517       +2     
  Lines       18962    19029      +67     
  Branches     3694     3704      +10     
==========================================
+ Hits        14197    14255      +58     
- Misses       3888     3897       +9     
  Partials      877      877              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@jamescrosswell

Copy link
Copy Markdown
Collaborator

Thanks for the contribution @lgarczyn. It's an intersting idea but we've got a number of concerns:

  • Sentry UI does not support previewing application/gzip content type attachments...
  • MaxAttachmentSize is never enforced because the stream reports no length. Logs of any size get compressed in memory and sent
  • We already gzip the whole request by default, so it saves almost nothing on the wire
  • None of the other SDKs do anything like this

I'll close this PR for changes to the SDK.

However, IAttachmentContent and SentryAttachment are public so you should be able to build this as a "GzipFileAttachmentContent" class, without any SDK change.

@lgarczyn

Copy link
Copy Markdown
Contributor Author

Thanks for the contribution @lgarczyn. It's an intersting idea but we've got a number of concerns:

* Sentry UI [does not support](https://docs.sentry.io/platforms/dotnet/enriching-events/attachments/) previewing application/gzip content type attachments...

* MaxAttachmentSize is never enforced because the stream reports no length. Logs of any size get compressed in memory and sent

* We already gzip the whole request by default, so it saves almost nothing on the wire

* None of the other SDKs do anything like this

I'll close this PR for changes to the SDK.

However, IAttachmentContent and SentryAttachment are public so you should be able to build this as a "GzipFileAttachmentContent" class, without any SDK change.

I mean, if the text file is gzipped when sent to sentry, and gzipped on the way back, maybe it should be gzipped in storage on sentry. I don't mind losing the preview.

@jamescrosswell

Copy link
Copy Markdown
Collaborator

I don't mind losing the preview.

Did you look at implementing IAttachmentContent? I think that would let you do what you want (without breaking previews for all the other SDK users).

I mean, if the text file is gzipped when sent to sentry, and gzipped on the way back, maybe it should be gzipped in storage on sentry.

That's probably something to take up in the core Sentry repo... we don't control that (directly) via the SDKs. Doing it via that route would also make it possible to implement without breaking the preview functionality and would mean consistent behaviour across all of the SDKs (vs carving out an exception for just the sentry-dotnet SDK).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

risk: high PR risk score: high

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants