@sentry/core
Version:
Base implementation for all Sentry JavaScript SDKs
1 lines • 7.83 kB
Source Map (JSON)
{"version":3,"file":"offline.js","sources":["../../../src/transports/offline.ts"],"sourcesContent":["import type { Envelope, InternalBaseTransportOptions, Transport, TransportMakeRequestResponse } from '@sentry/types';\nimport { envelopeContainsItemType, logger, parseRetryAfterHeader } from '@sentry/utils';\n\nexport const MIN_DELAY = 100; // 100 ms\nexport const START_DELAY = 5_000; // 5 seconds\nconst MAX_DELAY = 3.6e6; // 1 hour\n\nfunction log(msg: string, error?: Error): void {\n __DEBUG_BUILD__ && logger.info(`[Offline]: ${msg}`, error);\n}\n\nexport interface OfflineStore {\n insert(env: Envelope): Promise<void>;\n pop(): Promise<Envelope | undefined>;\n}\n\nexport type CreateOfflineStore = (options: OfflineTransportOptions) => OfflineStore;\n\nexport interface OfflineTransportOptions extends InternalBaseTransportOptions {\n /**\n * A function that creates the offline store instance.\n */\n createStore?: CreateOfflineStore;\n\n /**\n * Flush the offline store shortly after startup.\n *\n * Defaults: false\n */\n flushAtStartup?: boolean;\n\n /**\n * Called before an event is stored.\n *\n * Return false to drop the envelope rather than store it.\n *\n * @param envelope The envelope that failed to send.\n * @param error The error that occurred.\n * @param retryDelay The current retry delay in milliseconds.\n */\n shouldStore?: (envelope: Envelope, error: Error, retryDelay: number) => boolean | Promise<boolean>;\n}\n\ntype Timer = number | { unref?: () => void };\n\n/**\n * Wraps a transport and stores and retries events when they fail to send.\n *\n * @param createTransport The transport to wrap.\n */\nexport function makeOfflineTransport<TO>(\n createTransport: (options: TO) => Transport,\n): (options: TO & OfflineTransportOptions) => Transport {\n return options => {\n const transport = createTransport(options);\n const store = options.createStore ? options.createStore(options) : undefined;\n\n let retryDelay = START_DELAY;\n let flushTimer: Timer | undefined;\n\n function shouldQueue(env: Envelope, error: Error, retryDelay: number): boolean | Promise<boolean> {\n // We don't queue Session Replay envelopes because they are:\n // - Ordered and Replay relies on the response status to know when they're successfully sent.\n // - Likely to fill the queue quickly and block other events from being sent.\n // We also want to drop client reports because they can be generated when we retry sending events while offline.\n if (envelopeContainsItemType(env, ['replay_event', 'replay_recording', 'client_report'])) {\n return false;\n }\n\n if (options.shouldStore) {\n return options.shouldStore(env, error, retryDelay);\n }\n\n return true;\n }\n\n function flushIn(delay: number): void {\n if (!store) {\n return;\n }\n\n if (flushTimer) {\n clearTimeout(flushTimer as ReturnType<typeof setTimeout>);\n }\n\n flushTimer = setTimeout(async () => {\n flushTimer = undefined;\n\n const found = await store.pop();\n if (found) {\n log('Attempting to send previously queued event');\n void send(found).catch(e => {\n log('Failed to retry sending', e);\n });\n }\n }, delay) as Timer;\n\n // We need to unref the timer in node.js, otherwise the node process never exit.\n if (typeof flushTimer !== 'number' && flushTimer.unref) {\n flushTimer.unref();\n }\n }\n\n function flushWithBackOff(): void {\n if (flushTimer) {\n return;\n }\n\n flushIn(retryDelay);\n\n retryDelay = Math.min(retryDelay * 2, MAX_DELAY);\n }\n\n async function send(envelope: Envelope): Promise<void | TransportMakeRequestResponse> {\n try {\n const result = await transport.send(envelope);\n\n let delay = MIN_DELAY;\n\n if (result) {\n // If there's a retry-after header, use that as the next delay.\n if (result.headers && result.headers['retry-after']) {\n delay = parseRetryAfterHeader(result.headers['retry-after']);\n } // If we have a server error, return now so we don't flush the queue.\n else if ((result.statusCode || 0) >= 400) {\n return result;\n }\n }\n\n flushIn(delay);\n retryDelay = START_DELAY;\n return result;\n } catch (e) {\n if (store && (await shouldQueue(envelope, e, retryDelay))) {\n await store.insert(envelope);\n flushWithBackOff();\n log('Error sending. Event queued', e);\n return {};\n } else {\n throw e;\n }\n }\n }\n\n if (options.flushAtStartup) {\n flushWithBackOff();\n }\n\n return {\n send,\n flush: t => transport.flush(t),\n };\n };\n}\n"],"names":["logger","envelopeContainsItemType","parseRetryAfterHeader"],"mappings":"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"}