UNPKG

@grafana/faro-transport-otlp-http

Version:

Faro transport which converts the Faro data model to the Otlp data model.

123 lines 5.92 kB
var __rest = (this && this.__rest) || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; import { BaseTransport, createPromiseBuffer, isArray, noop, VERSION } from '@grafana/faro-core'; import { OtelPayload } from './payload'; const DEFAULT_BUFFER_SIZE = 30; const DEFAULT_CONCURRENCY = 5; // chrome supports 10 total, firefox 17 const DEFAULT_RATE_LIMIT_BACKOFF_MS = 5000; const BEACON_BODY_SIZE_LIMIT = 60000; const TOO_MANY_REQUESTS = 429; export class OtlpHttpTransport extends BaseTransport { constructor(options) { var _a, _b, _c; super(); this.options = options; this.name = '@grafana/faro-web-sdk:transport-otlp-http'; this.version = VERSION; this.sendingTracesDisabledUntil = new Date(); this.sendingLogsDisabledUntil = new Date(); this.rateLimitBackoffMs = (_a = options.defaultRateLimitBackoffMs) !== null && _a !== void 0 ? _a : DEFAULT_RATE_LIMIT_BACKOFF_MS; this.promiseBuffer = createPromiseBuffer({ size: (_b = options === null || options === void 0 ? void 0 : options.bufferSize) !== null && _b !== void 0 ? _b : DEFAULT_BUFFER_SIZE, concurrency: (_c = options === null || options === void 0 ? void 0 : options.concurrency) !== null && _c !== void 0 ? _c : DEFAULT_CONCURRENCY, }); } getIgnoreUrls() { var _a; const { tracesURL = '', logsURL = '' } = this.options; return [tracesURL, logsURL].filter(Boolean).concat((_a = this.config.ignoreUrls) !== null && _a !== void 0 ? _a : []); } isBatched() { return true; } send(items) { const otelPayload = new OtelPayload({ internalLogger: this.internalLogger, customOtlpTransform: this.options.otlpTransform, }); items.forEach((item) => otelPayload.addResourceItem(item)); this.sendPayload(otelPayload.getPayload()); } sendPayload(payload) { try { const { tracesURL = '', logsURL = '' } = this.options; for (const [key, value] of Object.entries(payload)) { if (!(isArray(value) && value.length > 0)) { continue; } let disabledUntil; let updateDisabledUntil = (_) => { }; let url = ''; switch (key) { case 'resourceSpans': url = tracesURL; disabledUntil = this.sendingTracesDisabledUntil; updateDisabledUntil = (retryAfterDate) => { this.sendingTracesDisabledUntil = retryAfterDate; }; break; case 'resourceLogs': url = logsURL; disabledUntil = this.sendingLogsDisabledUntil; updateDisabledUntil = (retryAfterDate) => { this.sendingLogsDisabledUntil = retryAfterDate; }; break; } if (disabledUntil && disabledUntil > new Date(Date.now())) { this.logWarn(`Dropping transport item due to too many requests. Backoff until ${disabledUntil}`); return undefined; } const body = JSON.stringify({ [key]: value }); const { requestOptions, apiKey } = this.options; const _a = requestOptions !== null && requestOptions !== void 0 ? requestOptions : {}, { headers } = _a, restOfRequestOptions = __rest(_a, ["headers"]); if (!url) { continue; } this.promiseBuffer.add(() => { return fetch(url, Object.assign({ method: 'POST', headers: Object.assign(Object.assign({ 'Content-Type': 'application/json' }, (headers !== null && headers !== void 0 ? headers : {})), (apiKey ? { 'x-api-key': apiKey } : {})), body, keepalive: body.length <= BEACON_BODY_SIZE_LIMIT }, (restOfRequestOptions !== null && restOfRequestOptions !== void 0 ? restOfRequestOptions : {}))) .then((response) => { if (response.status === TOO_MANY_REQUESTS) { updateDisabledUntil(this.getRetryAfterDate(response)); this.logWarn(`Too many requests, backing off until ${disabledUntil}`); } // read the body so the connection can be closed response.text().catch(noop); return response; }) .catch((error) => { this.logError('Failed sending payload to the receiver\n', JSON.parse(body), error); }); }); } } catch (error) { this.logError(error); } } getRetryAfterDate(response) { const now = Date.now(); const retryAfterHeader = response.headers.get('Retry-After'); if (retryAfterHeader) { const delay = Number(retryAfterHeader); if (!isNaN(delay)) { return new Date(delay * 1000 + now); } const date = Date.parse(retryAfterHeader); if (!isNaN(date)) { return new Date(date); } } return new Date(now + this.rateLimitBackoffMs); } } //# sourceMappingURL=transport.js.map