Overview
The following is a guide for implementing a new Sentry SDK. It covers the protocol for event submission as well as guidelines for how clients should typically look and behave.
Writing an SDK
At its core an SDK is a set of utilities for capturing data about an exceptional state in an application. Given this data, it then builds and sends a JSON payload to the Sentry server.
The following items are expected of production-ready SDKs:
- DSN configuration
- Graceful failures (e.g. Sentry server is unreachable)
- Setting attributes (e.g. tags and extra data)
- Support for Linux, Windows and OS X (where applicable)
Feature based support is required for the following:
- If cookie data is available, it’s not sent by default
- If POST data is available, it’s not sent by default
Additionally, the following features are highly encouraged:
- Automated error capturing (e.g. uncaught exception handlers)
- Logging framework integration
- Non-blocking event submission
- Context data helpers (e.g. setting the current user, recording breadcrumbs)
- Event sampling
- Honor Sentry’s Rate Limiting HTTP headers
- Pre and Post event send hooks
- Local variable values in stack trace (on platforms where this is possible)
- Send an
environment
on each event. If none was detected or set by the user,production
should be used.
Usage for End-users
Generally, using an SDK consists of three steps for the end user, which should look almost identical no matter the language:
Initialization of the SDK (sometimes this is hidden from the user):
CopiedSentry.init({dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0'});
Capturing an event:
Copiedvar resultId = Sentry.captureException(myException);
Using the result of an event capture:
Copiedalert(`Your exception was recorded as ${resultId}`);
init
ideally allows several configuration methods. The first argument should always be the DSN value (if possible):
Sentry.init({
'dsn': 'https://examplePublicKey@o0.ingest.sentry.io/0',
'foo': 'bar'
})
Note
If an SDK is not initialized or if it is initialized with an empty DSN, the SDK should not send any data over the network, such as captured exceptions. Depending on the platform, the SDK may avoid performing unnecessary initialization work and reduce its runtime footprint to a minimum.
Additionally, you should provide global functions which allow for capturing of a basic message or exception:
Sentry.captureMessage(message)
Sentry.captureException(exception)
Parsing the DSN
SDKs are encouraged to allow arbitrary options via the constructor, but must allow the first argument as a DSN string. This string contains the following bits:
'{PROTOCOL}://{PUBLIC_KEY}:{SECRET_KEY}@{HOST}{PATH}/{PROJECT_ID}'
The final endpoint you’ll be sending requests to is constructed per the following:
{BASE_URI} = '{PROTOCOL}://{HOST}{PATH}'
'{BASE_URI}/api/{PROJECT_ID}/{ENDPOINT}/'
Within the HOST
segment you will find the ingest domain for your organization.
For self-hosted instance this will be the base host of your instance, and for
sentry.io it will contain a host in the pattern of
o{orgid}.ingest.{region}.sentry.io
. For US based accounts
o{orgid}.ingest.sentry.io
will also work.
Note
Sentry provides the following endpoints:
- /envelope/ for any submission using Envelopes.
- /store/ for plain JSON event submission.
/minidump/
for multipart requests containing Minidumps./unreal/
for Unreal Engine 4 crash reports./security/
for Browser CSP reports, usually configured in a browser instead of an SDK.
See the respective endpoints for information on how to compose proper request payloads.
For example, given the following constructor:
Sentry.init({dsn: 'https://public@sentry.example.com/1'})
You should parse the following settings:
- URI =
https://sentry.example.com
- Public Key =
public
- Project ID =
1
The resulting POST request for a plain JSON payload would then transmit to:
'https://sentry.example.com/api/1/store/'
Note
Authentication
An authentication header is expected to be sent along with the message body, which acts as an ownership identifier:
X-Sentry-Auth: Sentry sentry_version=7,
sentry_client=<client version, arbitrary>,
sentry_key=<public api key>,
sentry_secret=<secret api key>
The sentry_secret
must only be included if a secret key portion was contained
in the DSN. Future versions of the protocol will fully deprecate the secret
key.
Note
In situations where it’s not possible to send the custom X-Sentry-Auth
header,
it’s possible to send these values via the querystring:
?sentry_version=7&sentry_key=<public api key>&sentry_secret=<secret api key>...
sentry_key
- Required. The public key which should be provided as part of the SDK configuration.
sentry_version
- Required. The protocol version. The current version of the protocol is
7
.
sentry_client
- Recommended. An arbitrary string that identifies your SDK, including its version. The
typical pattern for this is
client_name/client_version
.
For example, the Python SDK might send this assentry.python/1.0
.
sentry_timestamp
- The unix timestamp representing the time at which this event was generated.
This key is effectively deprecated, and it is ignored on the receiving side. Use thesent_at
envelope header instead.
sentry_secret
- The secret key which should be provided as part of the SDK configuration.
This key is effectively deprecated, and no longer needs to be set. However, since it was required in older versions, it should still be allowed and passed through to Sentry if set.
HTTP Headers
We recommend always sending the following headers:
content-type
content-length
user-agent
The following additional headers are permitted as per CORS policy:
x-sentry-auth
x-requested-with
x-forwarded-for
origin
referer
accept
authentication
authorization
content-encoding
transfer-encoding
User Agent
All SDKs are expected to report their name and version via the user-agent
header.
The following format should be used (unless required or recommended differently by the platform,
e.g. for browser SDKs, where the user agent header must be set by the browser itself):
{sdk-name}/{sdk-version}
For example:
sentry.python/1.45.0
sentry.php/4.7.0
sentry-ruby/5.17.3
sentry.cocoa/8.24.0
Additional information about the runtime, operating system, and others can be
appended as comments in parentheses, separated by ;
(semicolon and space), like so:
{sdk-name}/{sdk-version} ({runtime-name} {runtime-version}; {os-name} {os-version})
There is no expectation towards the presence or order of fields. The user agent must not contain PII or otherwise sensitive data. In general it should not contain any information that is not already present in the payload.
Request Compression
SDKs are heavily encouraged to compress the request body before sending it to
the server to keep the data small. The preferred method for this is to send a
content-encoding
header. The following content encodings are accepted by Relay
and Sentry:
gzip
: Using the LZ77 compression algorithm.deflate
: Using zlib structure with the deflate compression algorithm.br
: Using the Brotli algorithm.
Transfer Encoding
Transfer encoding is recommended for only very large requests. Set the header to
transfer-encoding: chunked
, which allows omission of the content-length
header and requires the request body to be wrapped into chunk headers.
See MDN for more details.
Reading the Response
On success, you will receive an HTTP response from the server containing a JSON payload with information on the submitted payload:
HTTP/1.1 200 OK
Content-Type: application/json
{
"id": "fc6d8c0c43fc4630ad850ee518f1b9d0"
}
Note the response code which Sentry will use. Always check for a 200
response, which confirms the message was delivered. A small level of validation
happens immediately that may result in a different response code (and message).
Handling Errors
We highly encourage that your SDK handle failures from the Sentry server
gracefully. Specifically, SDKs must honor the 429
status code and not attempt
sending until the Retry-After
kicks in. SDKs should drop events if Sentry is
unavailable instead of retrying.
To debug an error during development, inspect the response headers and response body. For example, you may get a response similar to:
HTTP/1.1 400 Bad Request
Content-Type: application/json
X-Sentry-Error: failed to read request body
{
"detail":"failed to read request body",
"causes":[
"failed to decode zlib payload",
"corrupt deflate stream"
]
}
The X-Sentry-Error
header and response body will not always contain a message,
but they can still be helpful in debugging clients. When emitted, they will
contain a precise error message, which is useful to identify root cause.
Note
Concurrency (Scope and Hubs)
SDKs are supposed to provide standardized concurrency handling through the concept of hubs and scopes. This is explained in more details in the
Concurrency chapter of the unified API docs.Layer of Integration
SDKs when possible are supposed to integrate on a low level which will capture as much of the runtime as possible. This means that if an SDK can hook the runtime or a framework directly this is preferred over requiring users to subclass specific base classes (or mix in helpers). For instance the Python SDK will monkey patch core functionality in frameworks to automatically pick up on errors and to integrate scope handling.