Provider limitations
Flare does not paper over a provider. When a provider cannot do something, the adapter records a loss on the receipt, or skips the report with a reason. It never imitates the feature by changing global state around a call, because that is how one report's data ends up on another.
Each receipt says what happened to that one report:
const status = await flare.capture(error).settled;
if (status.state === "settled") {
const sentry = status.outcomes.sentry;
sentry?.status; // "submitted", "skipped", "failed", ...
if (sentry?.status === "submitted") {
sentry.evidence; // what "submitted" proves
sentry.losses; // what this provider could not carry
}
}A destination whose SDK is not set up fails to start with a FlareError whose code is NOT_INITIALIZED, or UNSUPPORTED when the SDK lacks what the adapter needs. Its reports wait in the startup buffer, and flare.start() tries again.
Sentry
- Start after
Sentry.init. Before it, the start fails withNOT_INITIALIZED. - Tags are limited by Sentry. A tag key over 32 characters or a value over 200 is cut by Sentry. The adapter records it as a
truncatedloss. - Some names are the adapter's. A tag named
flare.report_idis replaced by the report id, and for anAggregateErrora context namedflare.aggregatedis replaced by its errors. Each is anunsupportedloss. - Evidence is
sdk-call-returned. Sentry returns an event id before anything is sent. - On React Native, a swallowed error is a failure. Sentry's
withScopelogs an error thrown inside it and answers nothing, so the outcome isfailed, with aFlareErrorwhose code isSUBMISSION_FAILED. - The browser SDK queues in memory. Events that have not left when the page closes are gone. Flush when the page is hidden.
- In React Native, a flush is a handoff. It reaches the native SDK, which persists events and sends them on its own schedule.
- One SDK, one destination. Sentry is one process-wide instance. Register it under one destination name: two destinations over the same SDK report every event twice.
Bugsnag
- Start after
Bugsnag.start. Before it, the start fails withNOT_INITIALIZED. - No message events. Skipped by default, or sent as an error with
messages: "as-error". - No
fatallevel. Bugsnag haserror,warningandinfo. Afatalreport is sent aserror, with the loss recorded. - Some context names are the adapter's. Contexts named
tags,flareorflare.aggregatedare not written, and each is anunsupportedloss. - Breadcrumbs need the
Breadcrumbclass. The static API does not expose it at runtime, so the adapter takes it from the same package as an option. - No flush and no event id. The SDK offers neither. In the browser there is no queue either. In React Native, the native SDK persists events.
- Filtering is invisible. When one of your Bugsnag
onErrorcallbacks discards an event, Bugsnag still completes the delivery callback without an error, so the outcome issubmitted. The evidence says the callback completed, not that the event was sent. - Mirrored breadcrumbs cannot be removed. After an account switch, a crash Bugsnag reports on its own can still show the previous account's entries.
Crashlytics
Crashlytics has the most limited client API of these providers, and the adapter is honest about it.
- Nothing is per report. The user id, custom keys and logs are global to the app. The adapter records the error and lists
identity.user,tags,contexts,breadcrumbs,operationandexception.aggregatedasunsupportedlosses when the report had them, andlevelwhen it is noterror. - No messages and no levels. A
message()is skipped. Everything recorded is a non-fatal. - Metadata only through the ambient mirror. Turn on
ambientand the session's user, tags, contexts and breadcrumbs are written to Crashlytics' global state, where native crashes pick them up too. Custom keys are limited to 64, values are cut at 1024 characters, and breadcrumbs become log lines. - Account safety costs reports. With
ambient.useron, a report whose user differs from the mirrored user is skipped asidentity-mismatch. Without it, a report whose account changed since it was captured is skipped the same way. Filing it under the wrong account would be worse than not filing it. - Stack traces are rebuilt. The adapter never holds your error object, so it rebuilds an
Errorfrom the sanitized name, message and stack. Thecausechain is kept.
PostHog
@priemskiyyy/flare-posthog in the browser and @priemskiyyy/flare-posthog-react-native on React Native map a report the same way.
- The user is PostHog's person. PostHog files every event under the person it identifies, and one event cannot go to someone else. A report whose user is not PostHog's distinct id is skipped as
identity-mismatch, so giveposthog.identifyandflare.userthe same id. - No messages. PostHog tracks exceptions, so a
message()is skipped. - Some names are PostHog's. A tag or context named with a leading
$, such as$set, or nameddistinct_id,tokenor__proto__, would change the person, move the event or not survive the merge, and the namesflare.report_id,flare.operationandflare.aggregatedbelong to the adapter. Such a tag or context is not written, and the receipt lists it as a loss. So are a context that shares a tag's name and breadcrumb data named$messageor$timestamp. - In the browser, start after
posthog.init. Before it, the start fails withNOT_INITIALIZED, and a slim bundle initialized without error tracking fails withUNSUPPORTED. - Filtering is visible in the browser. posthog-js answers nothing for an event its own filters drop, and the adapter reports that as
droppedwith the reasonprovider-filtered. posthog-react-native answers nothing either way, and gives no event id. - On React Native, the client loads first. The adapter waits until the client has loaded its storage, and a report whose deadline passes meanwhile is not sent.
- Flush only on React Native. posthog-js sends its batches on its own schedule and offers no flush. On React Native, the flush sends the client's queue.
Datadog
@priemskiyyy/flare-datadog sends RUM errors in the browser, and @priemskiyyy/flare-datadog-react-native on React Native.
- The user is Datadog's own. RUM attaches the user it was given to every event. In the browser, a report whose user is not that user is skipped as
identity-mismatch. On React Native the SDK does not reveal its user, so the adapter skips a report whose account changed since it was captured, and lists the user as a loss on every other report that has one. - No messages and no levels. RUM records errors, so a
message()is skipped, and the level travels as the attributeflare.level. - Everything else is one attribute. A report's tags, contexts, breadcrumbs and more travel under
flarein the error's context, because Datadog merges its global context over an error's own attributes. Keep the nameflareout of your global context. In the browser that merge skips__proto__, so a tag or context of that name is anunsupportedloss. - In the browser, start after
datadogRum.init. Before it, the start fails withNOT_INITIALIZED. On React Native, a call made before the SDK initializes waits in the SDK's own bounded buffer. - Filtering is invisible. Sampling,
beforeSend, rate limits and the React Native error event mapper discard an error without saying so, and there is no event id. - No flush. RUM and the native SDKs send batches on their own schedule.
Datadog Logs
- The user is Datadog's own. Datadog attaches the user set with
setUserto every log. A report whose user is not that user is skipped asidentity-mismatch. - Messages are logs. Every report is sent, messages included, with its level as the log's status:
criticalforfatal, thenerror,warnandinfo. __proto__is lost. Datadog's merge skips it, so a tag or context of that name is anunsupportedloss.- Start after
init. Before it, the start fails withNOT_INITIALIZED. - Filtering is invisible. The logger's level and handler, tracking consent, sampling,
beforeSendand rate limits can drop a log without saying so, and there is no event id. - No flush. The SDK sends its batches on its own schedule, and when the page is hidden.
OpenTelemetry
- Nothing is lost. A log record carries the report's own user, severity, exception and attributes.
- Filtering is invisible. A logger that is disabled, below its minimum severity or shut down drops a record without saying so, and there is no event id.
- Attribute limits are the SDK's. The SDK keeps at most 128 attributes per record by default, and cuts values past
attributeValueLengthLimitwhen you set one, without a loss on the receipt. - Flush needs
forceFlush. Without it,flare.flush()reportsunsupported.
Not verified on a device
The adapters are tested against fakes that are typechecked against the real SDK declarations, and the SDK behavior they rely on was read in the SDK source. That is not the same as a run on a device against the real backend. See the verification matrix for exactly what has and has not been exercised.
What no adapter can do
- Tell you that a provider's own filter discarded an event, unless the SDK says so. Only posthog-js does, and its adapter reports
provider-filtered. - Confirm that a provider stored, grouped or displayed an event.
- Report a native crash. The provider's native SDK does that.
- Apply Flare's redaction to what a provider's automatic instrumentation collects by itself.