Expected Features
The following is a description of features that are commonly expected in Sentry SDKs. Make sure to also have read the unified API design documentation which explains the common API design.
Background Sending
Events should be transmitted in a background thread or similar system. This queue must be flushed when the application shuts down with a specific timeout. This feature is typically user facing and explained as part of shutdown and draining.
Uncaught Exception Handler
Ability for the SDK to be set as a hook to record any uncaught exceptions. At the language level this is typically a global hook provided by the language itself. For framework integrations this might be part of middleware or some other system.
This behavior is typically provided by a default integration that can be disabled.
Scopes
Scopes should be provided by SDKs to set common attributes and context data on events sent to Sentry emitted from the current scope. They should be inherited to lower scopes so that they can be set "globally" on startup. Note that some attributes can only be set in the client options (release
, environment
) and not on scopes.
What scope means depends on the application, for a web framework it is most likely a single request/response cycle. For a mobile application there is often just one single scope that represents the single user and their actions. Scoping can be difficult to implement because it often has to deal with threads or concurrency and can involve deep integration with frameworks. see the scopes page for more information.
Automatic Context Data
Automatic addition of useful attributes such as tags
or extra
or specific contexts
. Typically means the SDK hooks into a framework so that it can set attributes that are known to be useful for most users. Please check Data Handling for considerations.
Breadcrumbs
Manually record application events (into the current scope) during the lifecycle of an application. Implement a ring buffer so as not to grow indefinitely. The most recent breadcrumbs should be attached to events as they occur.
With deeper framework integration, the automatic recording of breadcrumbs is possible and recommended, for example:
- UI Events: button clicks, touch events, etc.
- System Events: low battery, low storage space, airplane mode started, memory warnings, device orientation changed, etc.
- Outgoing HTTP requests
Check out the complete breadcrumb documentation for more types.
Event Sampling
SDKs should allow the user to configure what percentage of events are actually sent to the server (the rest should be silently ignored). For example:
sample_rate = options.get('sample_rate', 1.0)
# assuming random() returns a value between 0.0 (inclusive) and 1.0 (exclusive)
if random() < sample_rate:
transport.capture_event(event)
To further simplfiy ignoring certain events from being sent to sentry, it is also suggested to provide ignoreTransactions
and ignoreErrors
(or exception, choose terminology which is best for the platform). The array for ignoreTransactions
specifically should contain an array of transaction names, as is stored in the transcation event schema (ie GET /info
). These options should provide a simple way to allow users to discard events (ignore) from before they are sent to sentry. Prevents sending data which is is undesired and may consume quota or resources on the Sentry server.
ignore_transactions = ['GET /api/health','/api/v1/*']
Rate Limiting
Respect Sentry’s HTTP 429 Retry-After
header, or, if the SDK supports multiple payload types (e.g. errors and transactions), the X-Sentry-Rate-Limits
header. Outgoing SDK requests should be dropped during the backoff period.
See Rate Limiting for details.
In-App frames
Stack parsing can tell which frames should be identified as part of the user’s application (as opposed to part of the language, a library, or a framework), either automatically or by user configuration at startup, often declared as a package/module prefix.
Surrounding Source in Stack Trace
Lines of source code to provide context in stack traces. This is easier in interpreted languages, may be hard or impossible in compiled ones.
Local Variables
Local variable names and values for each stack frame, where possible. Restrictions apply on some platforms, for example it may only be possible to collect the values of parameters passed into each function, or it may be completely impossible to collect this information at all.
This functionality should be gated behind the includeLocalVariables
option, which is true
by default.
Desymbolication
Turn compiled or obfuscated code/method names in stack traces back into the original. Desymbolication always requires Sentry backend support. Not necessary for many languages.
Retrieve Last Event ID
Ability to get the ID of the last event sent. Event IDs are useful for correlation, logging, customers rolling their own feedback forms, etc.
User Feedback
For all SDKs, it is strongly recommended to send the User Feedback
as an envelope. Alternatively, the SDKs can
use the User Feedback endpoint, which is not recommended.
User Facing Platforms
On user-facing platforms such as mobile, desktop, or browser this means first-class support for requesting User Feedback when an error or crash occurs. To see some examples of the API check out the user-facing docs for Apple and Java.
On mobile and desktop, it is common to prompt the user for feedback after a crash happened on the previous run of the application. Therefore the SDKs should
implement the onCrashedLastRun
callback on the options. This callback gets called shortly after the initialization of the SDK when the last program execution
terminated with a crash. The SDK should execute the callback only once during the entire run of the program to avoid multiple callbacks if there are multiple
crash events to send.
Backend Platforms
On backend platforms, SDKs should document how to use the last event ID to prompt the user for feedback themselves.
Example implementations
User Feedback class:
Envelope item:
Attachments
Attachments are files stored alongside an event. To send an attachment, add it as an envelope item to the corresponding event.
We recommend implementing two types of attachments, one with a path and another with a byte array. If the programming language allows it, create one class with multiple constructors to keep things simple and guess the content type of the attachment via the filename.
The overload that takes a path
should consider:
- The SDK should read the file when an event gets captured and not when the user adds an attachment to the scope.
- If reading the attachment fails, the SDK should not drop the whole envelope, but just the attachment's envelope item.
- If the SDK is in debug mode log (
debug=true
) out errors to make debugging easier.
If the SDK supports transactions, the attachments should offer a flag addToTransactions
,
that specifies if SDK adds the attachment to every transaction or not. The default should be false
.
Use the implementations of Java, Objective-C, or Python as a reference for the API.
Max Attachment Size
Alongside the implementation of attachments, add maxAttachmentSize
to the options and set the default to 20 MiB. When converting an attachment to an
envelope item, the SDK must discard items larger than the maxAttachmentSize
. Especially on SDKs with offline caching, typical on mobile, this is
useful because attachments could quickly eat up the users' disk space. Furthermore, Relay has a
maximum size for attachments, and we want to reduce unnecessary requests.
Screenshots
When the user opts-in, if technically possible, take a screenshot of the application during a crash or error and include it as an attachment to the envelope with the event.
This feature only applies to SDKs with a user interface, such as Mobile and Desktop.
In some environments such as native iOS, taking a screenshot requires the UI thread and in the event of a crash, that might not be available. So inherently this feature will be a best effort solution.
Also, some environments don't allow access to the UI or some features during a hard crash, iOS, for example, doesn't allow running Objective-C code after a signal break, therefore no hard crash screenshot capture will be possible.
It's advised to provide this feature through a single option called attachScreenshot
. That's the preferred way but in platforms such as Flutter, a wrapping widget is required so documentation can point users to that instead of the suggested option name.
The feature is achieved by adding an attachment with:
- File name
screenshot.jpg
orscreenshot.png
- Subsequent screenshots in the same event should be named
screenshot-n
, where n is the screenshot number starting with 2
- Subsequent screenshots in the same event should be named
- Image size, if possible should stay below 2 MB but quality/size could be configurable
ContentType: image/jpg
orContentType: image/png
Whenever possible, avoid adding the attachment altogether if taking the screenshot fails. Alternatively, when streaming, it's possible the envelope header was already flushed through before the attempt to take the screenshot happens. In this case, a 0 byte attachment will be included. In that case, Sentry will not show a screenshot preview.
Before-Send Hook
Hook called with the event (and on some platforms the hint) that allows the user to decide whether an event should be sent or not. This can also be used to further modify the event. This only works for error
events. For transactions
it is recommended to have beforeSendTransaction
implemented in SDKs.
Before-Breadcrumb Hook
Hook called with the breadcrumb (and on some platforms the hint) that allow the user to decide whether and how a breadcrumb should be sent.
List Loaded Libraries
Include a list of loaded libraries (and versions) when sending an event.
Buffer to Disk
This feature is also known as 'Offline Caching'.
Write events to disk before attempting to send, so that they can be retried in the event of a temporary network failure. Needs to implement a cap on the number of stored events. This is mostly useful on mobile and desktop(e.g: laptop) apps, where stable connectivity is often not available.
Dealing with failures
It's important to note that retry is only considered in the event of a network failure. For example:
- Connection timeout
- DSN resolution failure
- Connection reset by peer
Other failures, like those caused by processing the file in the SDK itself, the payload should be discarded since those are likely to end up on an endless retry.
If the event reached Sentry and a HTTP response status code was received, even in the event of a 500
response, the event should be discarded.
Additional capabilities
Consider having the SDK retry sending events once the device is back online, when such notification exists in the platform.
Once the device is back online, the SDK is likely going to empty its disk queue in a quick burst of requests. This can trigger different abuse filters in Sentry. To account for that, it's considered to add a small delay between cached event captures. A recommended value is 100 milliseconds.
If the SDK is being rate-limited, which causes the SDK to drop any event that reaches its HTTP transport, cosider stop consuming the disk cache until the Retry-After
timeout is reached or the app restarts.
Example implementations
Start-Up Crash Detection
We recommend implementing this feature for mobile and desktop SDKs.
If the application crashes shortly after the init of the SDK, the SDK should provide a mechanism to guarantee transmission to Sentry. Ideally, SDKs could send the events in a separate process not impacted by the crashing application. With the limitations on mobile platforms, spawning an extra process only for sending envelopes is hard to achieve or impossible. The SDKs on these platforms send envelopes on a background thread to not block the UI thread or because they forbid network operations on the UI thread. A crash occurring shortly after the SDK init could lead to never reporting such crashes, keeping the users unaware of a critical bug.
When the app crashes, the SDK needs to check if it happens within two seconds after the SDK init. If it does, it needs to store that information on the disk. We recommend using a marker file, which the SDK checks on initialization. Suppose the SDK allows storing this information in another place to avoid creating an additional marker file and causing extra IO. In that case, the recommendation is to use such an approach to prevent additional IO. We accept the tradeoff of extra IO to be able to detect start-up crashes.
If the platform allows it, the SDK may call flush directly after the detected start-up crash occurs and before the application terminates. If the SDK can guarantee transmission to Sentry while crashing, it can skip creating a marker file and making a blocking flush call on the next initialization.
If the marker file exists upon the next SDK initialization, the SDK should clear the marker and block the init
execution up
to five seconds, in order to flush out pending envelopes. If the timeout of five seconds is exceeded, the SDK should release
the init
lock and continue flushing on a background thread.
While, ideally, the SDK should only flush out the crash event envelope, it is acceptable to call flush for all envelopes to reduce the complexity, as most of the time, there shouldn't be too many envelopes in the offline cache.
We decided against making this feature configurable. The only reason to disable it should be if the feature is broken; hence users can't disable it. The users can't modify the duration for detecting the start-up crashes, which is two seconds, and the flush duration, which is five seconds, because users usually don't know which values to pick so that we can choose the proper ones. We can always add these values later.
Example implementations
HTTP Proxy
Ability to use an HTTP proxy. Often easy to implement using the existing HTTP client. This should be picked up from the system config if possible or explicit config in the client options.
HTTP Client Integrations
Every HTTP client integration must exclude HTTP requests that match the configured DSN in the Options to exclude HTTP requests to Sentry.
Add a breadcrumb for each outgoing HTTP request after the request finishes:
- type:
http
- category:
http
- data (all fields are optional but recommended):
url
- The URL used in the HTTP requestmethod
- uppercase HTTP method, i.e: GET, HEADstatus_code
- Numeric status code such as200
or404
http.query
- The query part of the URLhttp.fragment
- The fragment part of the URI (Browser SDKs only)request_body_size
Size in bytesresponse_body_size
Size in bytes
If Performance Monitoring is both supported by the SDK and enabled in the client application when the transaction is active a new Span
must be created around the HTTP request:
- operation:
http.client
- description:
$METHOD $url
(uppercase HTTP method), e.g.GET https://sentry.io
- HTTP requests must be enhanced with a
sentry-trace
HTTP header to support distributed tracing - HTTP requests must be enhanced with a
baggage
HTTP header to support dynamic sampling - span status must match HTTP response status code (see Span status to HTTP status code mapping)
- when network error occurs, span status must be set to
internal_error
- span data must follow the Span Data Conventions
HTTP Client Errors
The SDK automatically captures HTTP Client errors and sends them to sentry.io.
The HTTP Client integration should have 3 configuration options:
captureFailedRequests
defaults tofalse
due to PII reasons.- The SDK will only capture HTTP Client errors if it is enabled.
failedRequestStatusCodes
defaults to500 - 599
, this configuration option accepts aList
ofHttpStatusCodeRange
which is a range of HTTP status code ->min
tomax
or a singlestatus_code
.- The SDK will only capture HTTP Client errors if the HTTP Response status code is within the defined ranges in
failedRequestStatusCodes
. - If the language has a
Range
type, it should be used instead ofHttpStatusCodeRange
.
- The SDK will only capture HTTP Client errors if the HTTP Response status code is within the defined ranges in
failedRequestTargets
defaults to (.*
), this configuration option accepts aList
ofString
that may be Regular expressions as well, similar to tracePropagationTargets.- The SDK will only capture HTTP Client errors if the HTTP Request URL is a match for any of the
failedRequestsTargets
.
- The SDK will only capture HTTP Client errors if the HTTP Request URL is a match for any of the
- sensitive
headers
should only be set ifsendDefaultPii
is enabled, e.g.Cookie
andSet-Cookie
.
The HTTP Client integration should capture error events with the following properties:
The Request interface, see the Spec for details.
The Response context, see the Spec for details.
{
"contexts": {
"response": {
"type": "response",
"cookies": "PHPSESSID=298zf09hf012fh2; csrftoken=u32t4o3tb3gg43; _gat=1;",
"headers": {
"content-type": "text/html"
/// ...
},
"status_code": 500,
"body_size": 1000, // in bytes
}
}
}
The Exception Interface, see the Spec for details.
If the HTTP Client integration does not throw an exception for unsuccessful requests, you can create a synthetic exception following this Spec:
- Set the Exception Mechanism with a proper
type
such asSentryOkHttpInterceptor
. - Set the Stack Trace Interface with
snapshot=true
.HTTP Client Error with status code: $code
.
When capturing error events, pass the original Request
and Response
objects from the HTTP Client as hints
, so the users may filter out events in beforeSend
with the full context.
Automatically captured HTTP Client error events can be searchable and alertable with the http.url
and http.status_code
properties, learn more about it in the Searchable Properties docs.
As an example, see the OkHTTP Client integration for Android.
GraphQL Client Integrations
The GraphQL Client integrations should match the guidelines for HTTP Client Integrations with a few differences:
The failedRequestStatusCodes
parameter does not exist because GraphQL errors are not HTTP errors, so that the request can be errored even though the HTTP status code of the response is successful.
Instead, the error has to be captured if the GraphQL response contains an errors
array. This can be done by regexing the response body, e.g.:
val regex = "(?i)\"errors\"\\s*:\\s*\\[".toRegex()
// [body] is the stringified GraphQL response body
if (regex.containsMatchIn(body)) {
// captures the error
}
Additional fields for breadcrumbs:
- data (all fields are optional but recommended):
operation_name
- The GraphQL operation nameoperation_type
- The GraphQL operation type, i.e:query
,mutation
,subscription
operation_id
- The GraphQL operation ID
Required fields for the Request interface:
{
"request": {
"api_target": "graphql",
"data": {
"foo": "bar"
}
}
}
The data
field is a JSON object that contains the GraphQL request payload.
Required fields for the Response interface:
{
"contexts": {
"response": {
"data": {
"foo": "bar"
}
}
}
}
The data
field is a JSON object that contains the GraphQL response payload.
Required fields for the Event interface:
The fingerprints
field should be set to ["$operationName", "$operationType", "$statusCode"]
.
{
"fingerprints": ["$operationName", "$operationType", "$statusCode"]
}
Attaching Request Body in Server SDKs
Ability for the SDK to attach request body to events and triggered during the execution of request.
User should be able to set a configuration option maxRequestBodySize
to instruct SDK how big requests bodies should be attached.
SDK controls what is an actual size in bytes for each option:
none
(default)small
-1000
bytesmedium
-10000
bytesalways
Log context
Some logging frameworks provide an option to set logging context. In Java this is called MDC (Mapped Diagnostic Context).
Users should be able to set a list of logging context entries in a configuration option contextTags
to tell the SDK to convert the entries to Sentry tags.