diff --git a/develop-docs/sdk/telemetry/attachments.mdx b/develop-docs/sdk/telemetry/attachments.mdx index 59e3b089a7dfd..417171cdd8f4a 100644 --- a/develop-docs/sdk/telemetry/attachments.mdx +++ b/develop-docs/sdk/telemetry/attachments.mdx @@ -1,13 +1,18 @@ --- title: Attachments -description: File attachments associated with events and traces, including screenshots, minidumps, crash reports, and view hierarchies. +description: File attachments associated with events and traces, including screenshots, minidumps, crash reports, view hierarchies, and flame graphs. spec_id: sdk/telemetry/attachments -spec_version: 1.7.0 +spec_version: 1.8.0 spec_status: stable spec_depends_on: - id: sdk/foundations/transport/envelopes version: ">=1.0.0" + - id: sdk/telemetry/profiles + version: ">=2.0.0" spec_changelog: + - version: 1.8.0 + date: 2026-10-02 + summary: Added draft flame graph attachment contract - version: 1.7.0 date: 2026-09-08 summary: Disallowed sending attachments in separate envelopes @@ -55,9 +60,10 @@ spec_changelog: ## Overview -Attachments are files stored alongside an event or trace. They are sent as envelope items with type `"attachment"` and carry raw file payloads — crash dumps, screenshots, view hierarchies, or arbitrary user-provided files. +Attachments are files stored alongside an event or trace. They are sent as envelope items with type `"attachment"` and carry raw file payloads such as crash dumps, screenshots, view hierarchies, flame graphs, or arbitrary user-provided files. Related specs: + - [Envelopes](/sdk/foundations/envelopes/) — transport format - [Envelope Items](/sdk/foundations/envelopes/envelope-items/) — item type reference @@ -72,6 +78,7 @@ Attachments are distinguished by the `attachment_type` header on the envelope it - **Standard attachments** (`event.attachment`) — arbitrary files with no special processing. - **Crash-related attachments** — minidumps (`event.minidump`), Apple crash reports (`event.applecrashreport`), and Unreal Engine metadata (`unreal.context`, `unreal.logs`). These trigger error event creation and/or symbolication. - **View hierarchies** (`event.view_hierarchy`) — JSON snapshots of the application's UI tree. +- **Flame Graphs** (`event.flamegraph`) — aggregated call trees using profiling frames. - **Screenshots** — image captures taken during a crash or error, identified by filename containing `"screenshot"`. ### Association Model @@ -105,14 +112,15 @@ Envelope item type `"attachment"`. Contains the raw payload of an attachment fil The `attachment_type` header declares the special type of an attachment. Possible values: -| Type | Since | Description | -|------|-------|-------------| -| `event.attachment` (default) | 1.0.0 | A standard attachment without special meaning. | -| `event.minidump` | 1.0.0 | A minidump file that creates an error event and is symbolicated. The file **SHOULD** start with the `MDMP` magic bytes. | -| `event.applecrashreport` | 1.0.0 | An Apple crash report file that creates an error event and is symbolicated. | -| `unreal.context` | 1.0.0 | An XML file containing UE4 crash metadata. During event ingestion, event contexts and extra fields are extracted from this file. | -| `unreal.logs` | 1.0.0 | A plain-text log file obtained from UE4 crashes. During event ingestion, the last logs are extracted into event breadcrumbs. | -| `event.view_hierarchy` | 1.3.0 | A JSON file with a predefined structure, see [RFC #33](https://github.com/getsentry/rfcs/blob/main/text/0033-view-hierarchy.md). | +| Type | Since | Description | +| ---------------------------- | ----- | -------------------------------------------------------------------------------------------------------------------------------- | +| `event.attachment` (default) | 1.0.0 | A standard attachment without special meaning. | +| `event.minidump` | 1.0.0 | A minidump file that creates an error event and is symbolicated. The file **SHOULD** start with the `MDMP` magic bytes. | +| `event.applecrashreport` | 1.0.0 | An Apple crash report file that creates an error event and is symbolicated. | +| `unreal.context` | 1.0.0 | An XML file containing UE4 crash metadata. During event ingestion, event contexts and extra fields are extracted from this file. | +| `unreal.logs` | 1.0.0 | A plain-text log file obtained from UE4 crashes. During event ingestion, the last logs are extracted into event breadcrumbs. | +| `event.view_hierarchy` | 1.3.0 | A JSON file with a predefined structure, see [RFC #33](https://github.com/getsentry/rfcs/blob/main/text/0033-view-hierarchy.md). | +| `event.flamegraph` | 1.8.0 | A JSON attachment containing aggregated call trees and symbolication inputs. | @@ -156,19 +164,175 @@ When using `Crashpad` as the crash-capturing backend in the Native SDK, the file + + +### Flame Graph + +SDKs **MAY** attach a `flamegraph.json` file containing aggregated call trees, such as those captured for application hangs. The attachment **MUST** use `attachment_type: "event.flamegraph"` and `content_type: "application/json"`. + +The format reuses profiling frame objects and debug metadata to support symbolication. Tree nodes describe aggregated call paths and sample counts. + +#### Flame Graph Attachment Transport + +The attachment **MUST** use these envelope item headers: + +| Header | Value | +| ----------------- | --------------------------------------------------------- | +| `type` | `"attachment"` | +| `attachment_type` | `"event.flamegraph"` | +| `filename` | `"flamegraph.json"` | +| `content_type` | `"application/json"` | +| `length` | The number of bytes in the serialized UTF-8 JSON payload. | + +The attachment **MUST** be sent in the same envelope as its associated event. The envelope's `event_id` supplies the association. Existing attachment size limits and rate limiting apply. + +Event context such as release, environment, SDK identity, device, OS, and hang duration belongs on the associated event. Symbolication inputs belong in the attachment so they remain available when the diagnostic describes a different process run from the uploading SDK. + +#### Flame Graph Payload + +The top-level object describes one collection of call trees and their shared frame table. + +| Field | Type | Requirement | Description | +| ------------ | ---------------------- | ------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------- | +| `version` | String | **REQUIRED** | `"1"` for this wire format. | +| `platform` | String | **REQUIRED** | Existing Sentry platform identifier. Used as the default platform for frames. Native Apple frames use `"cocoa"`. | +| `frames` | Array of frame objects | **REQUIRED** | Non-empty frame table using the same frame objects as a [V2 profile chunk](/sdk/telemetry/profiles/#profile-data). | +| `trees` | Array of tree objects | **REQUIRED** | Non-empty collection of independent call trees. | +| `debug_meta` | Debug Meta object | **REQUIRED** for native frames with known images | Existing debug metadata for the process that produced the diagnostic. See [Native Symbolication](#flame-graph-native-symbolication). | + +Consumers **MUST** process the payload according to its `version`. Within version `"1"`, consumers **SHOULD** ignore unknown fields and **SHOULD** retain them when rewriting the attachment. + +##### Flame Graph Frames + +Frame entries **MUST** use the same schema as the `profile.frames` entries in a [V2 profile chunk](/sdk/telemetry/profiles/#profile-data). Each entry **MUST** contain at least one of `filename`, `function`, or `instruction_addr`. Native frames **MUST** retain `instruction_addr`, even when already symbolicated. + +Producers **SHOULD** deduplicate frames using their complete frame data, including addressing mode and binary association. The same frame index **MAY** occur at multiple tree positions, including recursive calls. The frame table has no stack ordering. + +Nodes carry sample counts. Debug images carry binary UUIDs. Symbolicated frames use the existing `function`, `filename` or `abs_path`, and `lineno` fields. + +##### Flame Graph Trees + +A tree object groups root nodes that share the same sample population. Each object preserves a separate source call stack or thread. + +| Field | Type | Requirement | Description | +| ------------------- | -------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------- | +| `roots` | Array of nodes | **REQUIRED** | Non-empty forest of call paths. Roots are the oldest retained callers. | +| `thread_id` | String | **OPTIONAL** | Actual source thread identifier, if known. Uses the string representation used by profiling samples. | +| `thread_attributed` | Boolean | **OPTIONAL** | Whether the diagnostic source identifies this tree's thread as responsible for the reported issue, such as a hang. Omit when unknown. | + +Each tree is an independent sample population. Producers **MUST** include `thread_id` only when the source provides a thread identifier. + +##### Flame Graph Nodes + +Each node describes one frame occurrence in a call path. + +| Field | Type | Requirement | Description | +| -------------- | -------------- | ------------ | -------------------------------------------------------------------------------- | +| `frame_id` | Integer | **REQUIRED** | Zero-based index into `frames`. **MUST** be between `0` and `frames.length - 1`. | +| `sample_count` | Integer | **REQUIRED** | Positive inclusive count of samples containing this call-path prefix. | +| `children` | Array of nodes | **OPTIONAL** | Direct callees. Defaults to `[]` when omitted. | + +Paths **MUST** run from caller to callee. Each root-to-leaf path is one observed call path, potentially ending before the true bottom or top of the stack if the source was incomplete. + +Counts **MUST** be exact integers no greater than `9007199254740991`, the largest integer exactly representable by common JSON number implementations. + +`sample_count` includes samples attributed to the node and its descendants. The sum of direct children's counts **MUST NOT** exceed the node's count. The difference represents samples whose call path ends at the node. + +A tree can have multiple roots representing different oldest captured callers. Its total sample count is the sum of its root counts. + +#### Flame Graph Native Symbolication + +Native frames require an `instruction_addr` and a matching debug image for symbolication. The attachment's `debug_meta` **MUST** use the same format as a [V2 profile chunk](/sdk/telemetry/profiles/#v2-sample-format-payload), as defined by the [Debug Meta Interface](/sdk/foundations/envelopes/event-payloads/debugmeta/). Image metadata **MUST** describe the process that produced the diagnostic. + +Frame addresses **MUST** follow the existing [`instruction_addr` and `addr_mode` definitions](/sdk/foundations/envelopes/event-payloads/stacktrace/#frame-attributes). + +Symbolication **MUST** preserve captured addresses, addressing modes, and debug images. Resolved function names and source locations **MUST** populate the standard profiling frame fields. Unresolved frames **MUST** retain their original data. + +Consumers **MUST** track symbolication state and skip symbolication for already-processed payloads. If symbolication is rerun, it **MUST** start from the original, unexpanded frames and trees. + +##### Flame Graph Inline Frames + +Compiler-inlined functions share a physical stack frame with their caller. [Native symbolication](/ingestion/symbolicator/#native-symbolication) can recover these functions as separate logical frames. + +A processor that expands inline frames **MUST** replace each affected node occurrence with a caller-to-callee chain, assign the original `sample_count` to every node in that chain, and attach the original children to its innermost node. The expansion **MUST** preserve the tree's total sample count. Frame-table changes **MUST** update all affected indices. + +#### Flame Graph Example + +This example contains 10 samples. Six continue from `main` into `process` and four end at `main`. Both frames retain their captured addresses alongside symbolicated function names. The leaf omits `children`, which defaults to `[]`. + +```json {filename:flamegraph.json} +{ + "version": "1", + "platform": "cocoa", + "debug_meta": { + "images": [ + { + "type": "macho", + "debug_id": "84a04d24-0e60-3810-a8c0-90a65e2df61a", + "code_file": "ExampleApp", + "image_addr": "0x100000000", + "image_size": 8192, + "arch": "arm64" + } + ] + }, + "frames": [ + { + "instruction_addr": "0x100001000", + "package": "ExampleApp", + "function": "main" + }, + { + "instruction_addr": "0x100001200", + "package": "ExampleApp", + "function": "process", + "filename": "Sources/Processor.swift", + "lineno": 42 + } + ], + "trees": [ + { + "thread_attributed": true, + "roots": [ + { + "frame_id": 0, + "sample_count": 10, + "children": [ + { + "frame_id": 1, + "sample_count": 6 + } + ] + } + ] + } + ] +} +``` + +#### Flame Graph Validation and Limits + +A flame graph attachment **MUST NOT** exceed 10 MiB of serialized UTF-8 JSON. + +Symbolication **MUST** process at most 2,500 entries from the attachment's `frames` array, in frame-index order. This limit applies to input frames, before inline expansion. Each frame-table entry counts once, regardless of how many tree nodes reference it. + +Frames beyond this limit **MUST** retain their original data. Processing **MUST** preserve call-path relationships and sample counts. Inline expansion **MAY** add nodes and remap frame indices as described in [Inline Frames](#flame-graph-inline-frames). + + + ### Trace Attachments -Trace attachments are an experimental feature that is still under development. + Trace attachments are an experimental feature that is still under development. Trace attachments use item type `"attachment"` with content type `"application/vnd.sentry.trace-attachment"` (since 1.5.1). Unlike standard attachments, trace attachments are associated with a **trace** rather than an event. They are only optionally associated with other trace items (spans, logs). **Envelope headers:** -- `trace` *(optional)*: If the envelope containing the trace attachment has a [Dynamic Sampling Context](/sdk/foundations/trace-propagation/dynamic-sampling-context/), it will be subject to trace-based dynamic sampling rules and potentially dropped. +- `trace` _(optional)_: If the envelope containing the trace attachment has a [Dynamic Sampling Context](/sdk/foundations/trace-propagation/dynamic-sampling-context/), it will be subject to trace-based dynamic sampling rules and potentially dropped. @@ -177,7 +341,8 @@ Trace attachments use item type `"attachment"` with content type `"application/v ### Attachment Placeholders -Attachment placeholders are an experimental feature, the protocol is subject to change. + Attachment placeholders are an experimental feature, the protocol is subject + to change. Attachment placeholders use item type `"attachment"` with content type `"application/vnd.sentry.attachment-ref+json"`. A placeholder contains a **reference** to an attachment uploaded elsewhere — it does _not_ contain the actual attachment payload. The purpose is for the attachment to be handled (rate limited, filtered, etc.) together with the event payload, even if it was uploaded separately. @@ -188,6 +353,7 @@ SDKs **MUST** send placeholders in the same envelope as the event the file is at 1. If an attachment is larger than ~100 MiB, upload it to the [TUS](https://tus.io/protocols/resumable-upload#creation-with-upload)-conformant upload endpoint: 1. Create the upload: + ```shell curl -X POST https://o0.ingest.sentry.io/api/0/upload/ \ --header "Tus-Resumable: 1.0.0" \ @@ -204,10 +370,9 @@ SDKs **MUST** send placeholders in the same envelope as the event the file is at ``` | Field | Type | Required | Description | - | ----------------- | ------ | ------------ | -------------------------------------------------------------------| + | ----------------- | ------ | ------------ | ------------------------------------------------------------------ | | `attachment_type` | String | **REQUIRED** | One of the values listed in [Attachment Types](#attachment-types). | - 1. Get the location response header: ``` HTTP/1.1 201 Created @@ -222,6 +387,7 @@ SDKs **MUST** send placeholders in the same envelope as the event the file is at --header "Upload-Offset: 0" \ --data @myfile.log ``` + 1. Upon successful upload, attach a placeholder item to the event envelope, using the `Location` header from the upload response. @@ -236,19 +402,19 @@ SDKs **MUST** send placeholders in the same envelope as the event the file is at **Envelope headers:** -| Field | Type | Required | Since | Description | -|-------|------|----------|-------|-------------| +| Field | Type | Required | Since | Description | +| ---------- | ----------- | ------------ | ----- | ------------------------------------------- | | `event_id` | UUID string | **REQUIRED** | 1.0.0 | The identifier of the event or transaction. | **Item headers:** -| Field | Type | Required | Since | Description | -|-------|------|----------|-------|-------------| -| `type` | String | **REQUIRED** | 1.0.0 | **MUST** be `"attachment"`. | -| `length` | Integer | **REQUIRED** | 1.0.0 | Payload size in bytes. | -| `filename` | String | **REQUIRED** | 1.0.0 | The name of the uploaded file without a path component. | -| `attachment_type` | String | **OPTIONAL** | 1.0.0 | The special type of this attachment. See [Attachment Types](#attachment-types). Defaults to `event.attachment`. | -| `content_type` | String | **OPTIONAL** | 1.0.0 | The content type of the attachment payload. Any [MIME type](https://www.iana.org/assignments/media-types/media-types.xhtml) may be used; the default is `application/octet-stream` (since 1.0.1). | +| Field | Type | Required | Since | Description | +| ----------------- | ------- | ------------ | ----- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `type` | String | **REQUIRED** | 1.0.0 | **MUST** be `"attachment"`. | +| `length` | Integer | **REQUIRED** | 1.0.0 | Payload size in bytes. | +| `filename` | String | **REQUIRED** | 1.0.0 | The name of the uploaded file without a path component. | +| `attachment_type` | String | **OPTIONAL** | 1.0.0 | The special type of this attachment. See [Attachment Types](#attachment-types). Defaults to `event.attachment`. | +| `content_type` | String | **OPTIONAL** | 1.0.0 | The content type of the attachment payload. Any [MIME type](https://www.iana.org/assignments/media-types/media-types.xhtml) may be used; the default is `application/octet-stream` (since 1.0.1). | @@ -258,19 +424,19 @@ SDKs **MUST** send placeholders in the same envelope as the event the file is at **Envelope headers:** -| Field | Type | Required | Description | -|-------|------|----------|-------------| +| Field | Type | Required | Description | +| ---------- | ----------- | ------------ | ------------------------------------------- | | `event_id` | UUID string | **REQUIRED** | The identifier of the event or transaction. | **Item headers:** -| Field | Type | Required | Description | -|-------|------|----------|-------------| -| `type` | String | **REQUIRED** | **MUST** be `"attachment"`. | -| `content_type` | String | **REQUIRED** | **MUST** be `"application/vnd.sentry.attachment-ref+json"`. | -| `length` | Integer | **REQUIRED** | Size of the placeholder payload in bytes. | -| `attachment_length` | Integer | **REQUIRED** | Size of the referenced attachment in bytes. | -| `filename` | String | **REQUIRED** | The name of the uploaded file without a path component. | +| Field | Type | Required | Description | +| ------------------- | ------- | ------------ | ----------------------------------------------------------- | +| `type` | String | **REQUIRED** | **MUST** be `"attachment"`. | +| `content_type` | String | **REQUIRED** | **MUST** be `"application/vnd.sentry.attachment-ref+json"`. | +| `length` | Integer | **REQUIRED** | Size of the placeholder payload in bytes. | +| `attachment_length` | Integer | **REQUIRED** | Size of the referenced attachment in bytes. | +| `filename` | String | **REQUIRED** | The name of the uploaded file without a path component. | The item header **MUST** also contain the original file's `attachment_type` if it was set. The item payload **MUST** contain the original file's `content_type` as a field in the JSON object (see [Standard Attachment Item](#standard-attachment-item)). @@ -283,9 +449,9 @@ The item header **MUST** also contain the original file's `attachment_type` if i } ``` -| Field | Type | Required | Description | -|-------|------|----------|-------------| -| `location` | String | **REQUIRED** | The identifier of the upload, as returned in the `Location` header of the upload endpoint response. | +| Field | Type | Required | Description | +| -------------- | ------ | ------------ | --------------------------------------------------------------------------------------------------------------- | +| `location` | String | **REQUIRED** | The identifier of the upload, as returned in the `Location` header of the upload endpoint response. | | `content_type` | String | **OPTIONAL** | The content type of the represented attachment (as opposed to the content type of the placeholder item itself). | @@ -296,12 +462,12 @@ The item header **MUST** also contain the original file's `attachment_type` if i **Item headers:** -| Field | Type | Required | Since | Description | -|-------|------|----------|-------|-------------| -| `type` | String | **REQUIRED** | 1.5.0 | **MUST** be `"attachment"`. | -| `content_type` | String | **REQUIRED** | 1.5.0 | **MUST** be `"application/vnd.sentry.trace-attachment"` (since 1.5.1). | -| `length` | Integer | **REQUIRED** | 1.5.0 | Total payload size in bytes (metadata JSON + attachment body). | -| `meta_length` | Integer | **REQUIRED** | 1.5.0 | Size of the metadata JSON portion of the payload in bytes. | +| Field | Type | Required | Since | Description | +| -------------- | ------- | ------------ | ----- | ---------------------------------------------------------------------- | +| `type` | String | **REQUIRED** | 1.5.0 | **MUST** be `"attachment"`. | +| `content_type` | String | **REQUIRED** | 1.5.0 | **MUST** be `"application/vnd.sentry.trace-attachment"` (since 1.5.1). | +| `length` | Integer | **REQUIRED** | 1.5.0 | Total payload size in bytes (metadata JSON + attachment body). | +| `meta_length` | Integer | **REQUIRED** | 1.5.0 | Size of the metadata JSON portion of the payload in bytes. | **Item payload:** @@ -322,14 +488,14 @@ The trace attachment payload consists of a JSON metadata object immediately foll **Metadata fields:** -| Field | Type | Required | Description | -|-------|------|----------|-------------| -| `trace_id` | String | **REQUIRED** | 32-character hexadecimal string identifying the trace. | -| `attachment_id` | String | **REQUIRED** | 32-character hexadecimal string (Version 7 or Version 4 UUID without dashes). | -| `timestamp` | Float | **REQUIRED** | UNIX timestamp of the attachment's occurrence. | -| `filename` | String | **OPTIONAL** | The file name of the attachment. | -| `content_type` | String | **REQUIRED** | Content type of the attachment body (not the envelope item content type). | -| `attributes` | Object | **OPTIONAL** | Arbitrary key-value pairs queryable on the attachment trace item, similar to spans, logs, and trace metrics. | +| Field | Type | Required | Description | +| --------------- | ------ | ------------ | ------------------------------------------------------------------------------------------------------------ | +| `trace_id` | String | **REQUIRED** | 32-character hexadecimal string identifying the trace. | +| `attachment_id` | String | **REQUIRED** | 32-character hexadecimal string (Version 7 or Version 4 UUID without dashes). | +| `timestamp` | Float | **REQUIRED** | UNIX timestamp of the attachment's occurrence. | +| `filename` | String | **OPTIONAL** | The file name of the attachment. | +| `content_type` | String | **REQUIRED** | Content type of the attachment body (not the envelope item content type). | +| `attributes` | Object | **OPTIONAL** | Arbitrary key-value pairs queryable on the attachment trace item, similar to spans, logs, and trace metrics. | @@ -363,10 +529,10 @@ Attachments **SHOULD** offer a flag `addToTransactions` that specifies whether t ### Configuration Options -| Option | Type | Default | Since | Description | -|--------|------|---------|-------|-------------| -| `maxAttachmentSize` | Integer (bytes) | 20 MiB | 1.1.0 | Maximum size of a single attachment. The SDK **MUST** discard items larger than this when converting to envelope items. | -| `attachScreenshot` | Boolean | `false` | 1.2.0 | When `true`, the SDK captures a screenshot during crashes/errors and attaches it (mobile/desktop SDKs only). | +| Option | Type | Default | Since | Description | +| ------------------- | --------------- | ------- | ----- | ----------------------------------------------------------------------------------------------------------------------- | +| `maxAttachmentSize` | Integer (bytes) | 20 MiB | 1.1.0 | Maximum size of a single attachment. The SDK **MUST** discard items larger than this when converting to envelope items. | +| `attachScreenshot` | Boolean | `false` | 1.2.0 | When `true`, the SDK captures a screenshot during crashes/errors and attaches it (mobile/desktop SDKs only). |