autotel
Version:
Write Once, Observe Anywhere
99 lines (97 loc) • 3.33 kB
JavaScript
import { a as asNumberArray, c as asScalar, n as asBooleanArray, u as asStringArray } from "./values-xBtdXtjA.js";
import { SpanStatusCode } from "@opentelemetry/api";
//#region src/test-span-collector.ts
/** @see ExportResultCode from @opentelemetry/core */
const ExportResultCode = {
SUCCESS: 0,
FAILED: 1
};
var TestSpanCollector = class {
traces = /* @__PURE__ */ new Map();
export(spans, callback) {
for (const span of spans) {
const traceId = span.spanContext().traceId;
let list = this.traces.get(traceId);
if (!list) {
list = [];
this.traces.set(traceId, list);
}
list.push(span);
}
callback({ code: ExportResultCode.SUCCESS });
}
/**
* Drain and serialize spans that are descendants of `rootSpanId` within `traceId`.
* Filters to the subtree rooted at the test span to prevent cross-test mixing.
* Removes the entire traceId entry from the collector.
*/
drainTrace(traceId, rootSpanId) {
const spans = this.peekTrace(traceId, rootSpanId);
this.traces.delete(traceId);
return spans;
}
/**
* Serialize the finished spans of `traceId` without removing them — safe to
* call repeatedly while a trace is still growing (e.g. a scenario checker
* polling until an async flow's completion boundary closes).
*
* With `rootSpanId`, filters to that span's subtree like {@link drainTrace}.
*/
peekTrace(traceId, rootSpanId) {
const allSpans = this.traces.get(traceId);
if (!allSpans?.length) return [];
if (rootSpanId === void 0) return allSpans.map((s) => serializeSpan(s));
const byId = /* @__PURE__ */ new Map();
for (const s of allSpans) byId.set(s.spanContext().spanId, s);
return allSpans.filter((s) => {
let id = s.spanContext().spanId;
while (id) {
if (id === rootSpanId) return true;
const parentId = byId.get(id)?.parentSpanContext?.spanId || void 0;
if (parentId === id) break;
id = parentId;
}
return false;
}).map((s) => serializeSpan(s));
}
shutdown() {
this.traces.clear();
return Promise.resolve();
}
forceFlush() {
return Promise.resolve();
}
};
function hrTimeToMs(hr) {
return hr[0] * 1e3 + hr[1] / 1e6;
}
/**
* An attribute value in the form that survives a round trip through JSON: a
* scalar, or a list whose entries are all the same kind of scalar. Anything
* else - a mixed list, an empty one - is left out of the serialized span.
*/
function asSerializable(value) {
const scalar = asScalar(value);
if (scalar !== void 0) return scalar;
if (!Array.isArray(value) || value.length === 0) return void 0;
return asStringArray(value) ?? asNumberArray(value) ?? asBooleanArray(value);
}
function serializeSpan(span) {
const attrs = {};
for (const [k, v] of Object.entries(span.attributes)) {
const serializable = asSerializable(v);
if (serializable !== void 0) attrs[k] = serializable;
}
return {
spanId: span.spanContext().spanId,
parentSpanId: span.parentSpanContext?.spanId || void 0,
name: span.name,
startTimeMs: hrTimeToMs(span.startTime),
durationMs: hrTimeToMs(span.duration),
status: span.status.code === SpanStatusCode.ERROR ? "error" : span.status.code === SpanStatusCode.OK ? "ok" : "unset",
statusMessage: span.status.message || void 0,
attributes: Object.keys(attrs).length > 0 ? attrs : void 0
};
}
//#endregion
export { TestSpanCollector, serializeSpan };