@sentry/core
Version:
Base implementation for all Sentry JavaScript SDKs
109 lines (105 loc) • 3.9 kB
JavaScript
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
const debugBuild = require('../../debug-build.js');
const debugLogger = require('../../utils/debug-logger.js');
const timer = require('../../utils/timer.js');
const dynamicSamplingContext = require('../dynamicSamplingContext.js');
const envelope = require('./envelope.js');
const estimateSize = require('./estimateSize.js');
const MAX_SPANS_PER_ENVELOPE = 1e3;
const MAX_TRACE_WEIGHT_IN_BYTES = 5e6;
class SpanBuffer {
constructor(client, options) {
this._traceBuckets = /* @__PURE__ */ new Map();
this._client = client;
const { maxSpanLimit, flushInterval, maxTraceWeightInBytes } = options ?? {};
this._maxSpanLimit = maxSpanLimit && maxSpanLimit > 0 && maxSpanLimit <= MAX_SPANS_PER_ENVELOPE ? maxSpanLimit : MAX_SPANS_PER_ENVELOPE;
this._flushInterval = flushInterval && flushInterval > 0 ? flushInterval : 5e3;
this._maxTraceWeight = maxTraceWeightInBytes && maxTraceWeightInBytes > 0 ? maxTraceWeightInBytes : MAX_TRACE_WEIGHT_IN_BYTES;
this._client.on("flush", () => {
this.drain();
});
this._client.on("close", () => {
this._traceBuckets.forEach((bucket) => {
clearTimeout(bucket.timeout);
});
this._traceBuckets.clear();
});
}
/**
* Add a span to the buffer.
*/
add(spanJSON) {
const traceId = spanJSON.trace_id;
let bucket = this._traceBuckets.get(traceId);
if (!bucket) {
bucket = {
spans: /* @__PURE__ */ new Set(),
size: 0,
timeout: timer.safeUnref(
setTimeout(() => {
this.flush(traceId);
}, this._flushInterval)
)
};
this._traceBuckets.set(traceId, bucket);
}
bucket.spans.add(spanJSON);
bucket.size += estimateSize.estimateSerializedSpanSizeInBytes(spanJSON);
if (bucket.spans.size >= this._maxSpanLimit || bucket.size >= this._maxTraceWeight) {
this.flush(traceId);
}
}
/**
* Drain and flush all buffered traces.
*/
drain() {
if (!this._traceBuckets.size) {
return;
}
debugBuild.DEBUG_BUILD && debugLogger.debug.log(`Flushing span tree map with ${this._traceBuckets.size} traces`);
this._traceBuckets.forEach((_, traceId) => {
this.flush(traceId);
});
}
/**
* Flush spans of a specific trace.
* In contrast to {@link SpanBuffer.drain}, this method does not flush all traces, but only the one with the given traceId.
*/
flush(traceId) {
const bucket = this._traceBuckets.get(traceId);
if (!bucket) {
return;
}
if (!bucket.spans.size) {
this._removeTrace(traceId);
return;
}
const spans = Array.from(bucket.spans);
const segmentSpan = spans[0]?._segmentSpan;
if (!segmentSpan) {
debugBuild.DEBUG_BUILD && debugLogger.debug.warn("No segment span reference found on span JSON, cannot compute DSC");
this._removeTrace(traceId);
return;
}
const dsc = dynamicSamplingContext.getDynamicSamplingContextFromSpan(segmentSpan);
const cleanedSpans = spans.map((spanJSON) => {
const { _segmentSpan, ...cleanSpanJSON } = spanJSON;
return cleanSpanJSON;
});
const envelope$1 = envelope.createStreamedSpanEnvelope(cleanedSpans, dsc, this._client);
debugBuild.DEBUG_BUILD && debugLogger.debug.log(`Sending span envelope for trace ${traceId} with ${cleanedSpans.length} spans`);
this._client.sendEnvelope(envelope$1).then(null, (reason) => {
debugBuild.DEBUG_BUILD && debugLogger.debug.error("Error while sending streamed span envelope:", reason);
});
this._removeTrace(traceId);
}
_removeTrace(traceId) {
const bucket = this._traceBuckets.get(traceId);
if (bucket) {
clearTimeout(bucket.timeout);
}
this._traceBuckets.delete(traceId);
}
}
exports.SpanBuffer = SpanBuffer;
//# sourceMappingURL=spanBuffer.js.map