UNPKG

rollbar

Version:

Effortlessly track and debug errors in your JavaScript applications with Rollbar. This package includes advanced error tracking features and an intuitive interface to help you identify and fix issues more quickly.

204 lines (181 loc) 5.49 kB
import hrtime from './hrtime.js'; /** * SpanExporter is responsible for exporting ReadableSpan objects * and transforming them into the OTLP-compatible format. */ export class SpanExporter { constructor(api, options = {}) { this.api = api; this.options = options; } /** * Export spans to the span export queue * * @param {Array} spans - Array of ReadableSpan objects to export * @param {Function} _resultCallback - Optional callback (not used) */ export(spans, _resultCallback) { spanExportQueue.push(...spans); } /** * Transforms an array of ReadableSpan objects into the OTLP format payload * compatible with the Rollbar API. This follows the OpenTelemetry protocol * specification for traces. * * @returns {Object} OTLP format payload for API transmission */ toPayload() { const spans = spanExportQueue.slice(); spanExportQueue.length = 0; if (!spans || !spans.length) { return { resourceSpans: [] }; } const resource = (spans[0] && spans[0].resource) || {}; const scopeMap = new Map(); for (const span of spans) { const scopeKey = span.instrumentationScope ? `${span.instrumentationScope.name}:${span.instrumentationScope.version}` : 'default:1.0.0'; if (!scopeMap.has(scopeKey)) { scopeMap.set(scopeKey, { scope: span.instrumentationScope || { name: 'default', version: '1.0.0', attributes: [], }, spans: [], }); } scopeMap.get(scopeKey).spans.push(this._transformSpan(span)); } return { resourceSpans: [ { resource: this._transformResource(resource), scopeSpans: Array.from(scopeMap.values()).map((scopeData) => ({ scope: this._transformInstrumentationScope(scopeData.scope), spans: scopeData.spans, })), }, ], }; } /** * Sends the given payload to the Rollbar API. * * @param {String} payload - Serialized OTLP format payload * @param {Object} headers - Optional request headers * @returns {Promise} Promise that resolves when the request completes */ post(payload, headers = {}) { return this.api.postSpans(payload, headers); } /** * Transforms a ReadableSpan into the OTLP Span format * * @private * @param {Object} span - ReadableSpan object to transform * @returns {Object} OTLP Span format */ _transformSpan(span) { const transformAttributes = (attributes) => { return Object.entries(attributes || {}).map(([key, value]) => ({ key, value: this._transformAnyValue(value), })); }; const transformEvents = (events) => { return (events || []).map((event) => ({ timeUnixNano: hrtime.toNanos(event.time), name: event.name, attributes: transformAttributes(event.attributes), })); }; return { traceId: span.spanContext.traceId, spanId: span.spanContext.spanId, parentSpanId: span.parentSpanId || '', name: span.name, kind: span.kind || 1, // INTERNAL by default startTimeUnixNano: hrtime.toNanos(span.startTime), endTimeUnixNano: hrtime.toNanos(span.endTime), attributes: transformAttributes(span.attributes), events: transformEvents(span.events), }; } /** * Transforms a resource object into OTLP Resource format * * @private * @param {Object} resource - Resource information * @returns {Object} OTLP Resource format */ _transformResource(resource) { const attributes = resource.attributes || {}; const keyValues = Object.entries(attributes).map(([key, value]) => ({ key, value: this._transformAnyValue(value), })); return { attributes: keyValues, }; } /** * Transforms an instrumentation scope into OTLP InstrumentationScope format * * @private * @param {Object} scope - Instrumentation scope information * @returns {Object} OTLP InstrumentationScope format */ _transformInstrumentationScope(scope) { return { name: scope.name || '', version: scope.version || '', attributes: (scope.attributes || []).map((attr) => ({ key: attr.key, value: this._transformAnyValue(attr.value), })), }; } /** * Transforms a JavaScript value into an OTLP AnyValue * * @private * @param {any} value - Value to transform * @returns {Object} OTLP AnyValue format */ _transformAnyValue(value) { if (value === null || value === undefined) { return { stringValue: '' }; } const type = typeof value; if (type === 'string') { return { stringValue: value }; } else if (type === 'number') { if (Number.isInteger(value)) { return { intValue: value.toString() }; } else { return { doubleValue: value }; } } else if (type === 'boolean') { return { boolValue: value }; } else if (Array.isArray(value)) { return { arrayValue: { values: value.map((v) => this._transformAnyValue(v)), }, }; } else if (type === 'object') { return { kvlistValue: { values: Object.entries(value).map(([k, v]) => ({ key: k, value: this._transformAnyValue(v), })), }, }; } return { stringValue: String(value) }; } } export const spanExportQueue = [];