@beignet/core
Version:
Core framework primitives for Beignet
435 lines (387 loc) • 11.9 kB
text/typescript
/**
* @beignet/core/tracing
*
* W3C trace context primitives used to correlate Beignet activity across
* requests, use cases, providers, and instrumentation sinks.
*
* This module is dependency-free so client bundles that import app context
* types stay clean.
*/
const TRACEPARENT_PATTERN = /^00-([0-9a-f]{32})-([0-9a-f]{16})-([0-9a-f]{2})$/;
const MAX_TRACESTATE_LENGTH = 512;
const MAX_TRACESTATE_MEMBERS = 32;
const TRACESTATE_SIMPLE_KEY_PATTERN = /^[a-z][a-z0-9_*/-]{0,255}$/;
const TRACESTATE_VENDOR_KEY_PATTERN =
/^[a-z0-9][a-z0-9_*/-]{0,240}@[a-z][a-z0-9_*/-]{0,13}$/;
const TRACESTATE_VALUE_PATTERN =
/^[\x20-\x2b\x2d-\x3c\x3e-\x7e]{0,255}[\x21-\x2b\x2d-\x3c\x3e-\x7e]$/;
/** Current version of Beignet's durable trace carrier. */
export const TRACE_CARRIER_VERSION = 1 as const;
/**
* Vendor-neutral trace context stored in durable messages and transport
* envelopes. Unknown versions and malformed values are ignored by consumers.
*/
export interface TraceCarrier {
/** Carrier schema version. */
readonly version: typeof TRACE_CARRIER_VERSION;
/** W3C traceparent value captured at the producing boundary. */
readonly traceparent: string;
/** Optional W3C tracestate value captured at the producing boundary. */
readonly tracestate?: string;
}
/**
* Trace context used to correlate related activity.
*/
export interface TraceContext {
/**
* W3C trace ID.
*/
traceId: string;
/**
* Current span ID.
*/
spanId: string;
/**
* Parent span ID when available.
*/
parentSpanId?: string;
/**
* W3C traceparent header value.
*/
traceparent: string;
/**
* Optional W3C tracestate value associated with this context.
*/
tracestate?: string;
}
/**
* Parsed W3C traceparent header.
*/
export interface ParsedTraceparent {
/**
* W3C trace ID.
*/
traceId: string;
/**
* Span ID from the traceparent header.
*/
spanId: string;
/**
* Trace flags from the traceparent header.
*/
traceFlags: string;
/**
* Normalized traceparent header value.
*/
traceparent: string;
}
/**
* Input accepted when creating trace context.
*/
export interface TraceContextInput {
traceId?: string;
spanId?: string;
parentSpanId?: string;
traceparent?: string;
tracestate?: string;
}
/** Values accepted as tracing attributes. */
export type TraceAttributeValue =
| string
| number
| boolean
| readonly string[]
| readonly number[]
| readonly boolean[];
/** Attributes attached to a traced Beignet operation. */
export type TraceAttributes = Readonly<Record<string, TraceAttributeValue>>;
/** Span kinds understood by tracing providers. */
export type TraceSpanKind =
| "internal"
| "server"
| "client"
| "producer"
| "consumer";
/** Stable Beignet operation categories used by tracing and metrics providers. */
export type TraceOperationType =
| "request"
| "useCase"
| "agentCapability"
| "listener"
| "job"
| "schedule"
| "task"
| "outbox"
| "provider";
/** Description of one operation to run inside an active trace span. */
export interface TraceOperation {
name: string;
type: TraceOperationType;
kind?: TraceSpanKind;
parent?: TraceContextInput;
/** Attributes attached to the operation span. */
attributes?: TraceAttributes;
/**
* Bounded attributes attached to operation metrics. Values must describe a
* stable operation dimension, never a request, actor, tenant, or payload.
*/
metricAttributes?: TraceAttributes;
}
/** Framework-neutral span handle exposed to Beignet execution wrappers. */
export interface TraceSpan {
readonly context: TraceContext;
setAttribute(name: string, value: TraceAttributeValue): void;
setAttributes(attributes: TraceAttributes): void;
addEvent(name: string, attributes?: TraceAttributes): void;
setStatus(status: "ok" | "error"): void;
recordError(error: unknown): void;
}
/** Optional app port implemented by production tracing integrations. */
export interface TracingPort {
current(): TraceContext | undefined;
startActiveSpan<T>(operation: TraceOperation, run: (span: TraceSpan) => T): T;
}
function isObject(value: unknown): value is Record<string, unknown> {
return typeof value === "object" && value !== null;
}
/** Return whether a value implements `TracingPort`. */
export function isTracingPort(value: unknown): value is TracingPort {
return (
isObject(value) &&
"current" in value &&
"startActiveSpan" in value &&
typeof value.current === "function" &&
typeof value.startActiveSpan === "function"
);
}
/** Resolve a tracing port from a direct port, ports object, or app context. */
export function resolveTracingPort(target: unknown): TracingPort | undefined {
if (!target) return undefined;
if (isTracingPort(target)) return target;
if (!isObject(target)) return undefined;
const direct = "tracing" in target ? (target.tracing as unknown) : undefined;
if (isTracingPort(direct)) return direct;
const ports = "ports" in target ? target.ports : undefined;
if (!ports || ports === target) return undefined;
return resolveTracingPort(ports);
}
function validTracestate(value: unknown): string | undefined {
if (typeof value !== "string") return undefined;
const normalized = value.trim();
if (normalized.length === 0 || normalized.length > MAX_TRACESTATE_LENGTH) {
return undefined;
}
const members = normalized.split(",");
if (members.length > MAX_TRACESTATE_MEMBERS) return undefined;
const keys = new Set<string>();
for (const rawMember of members) {
const member = rawMember.trim();
const separator = member.indexOf("=");
if (separator <= 0) return undefined;
const key = member.slice(0, separator).trim();
const memberValue = member.slice(separator + 1).trimStart();
if (
(!TRACESTATE_SIMPLE_KEY_PATTERN.test(key) &&
!TRACESTATE_VENDOR_KEY_PATTERN.test(key)) ||
!TRACESTATE_VALUE_PATTERN.test(memberValue) ||
keys.has(key)
) {
return undefined;
}
keys.add(key);
}
return normalized;
}
/**
* Parse an untrusted durable trace carrier.
*
* Invalid carriers return `undefined`; trace metadata must never prevent
* message delivery.
*/
export function parseTraceCarrier(value: unknown): TraceCarrier | undefined {
if (!isObject(value) || value.version !== TRACE_CARRIER_VERSION) {
return undefined;
}
const parsed = parseTraceparent(
typeof value.traceparent === "string" ? value.traceparent : undefined,
);
if (!parsed) return undefined;
if (value.tracestate !== undefined) {
const tracestate = validTracestate(value.tracestate);
if (!tracestate) return undefined;
return {
version: TRACE_CARRIER_VERSION,
traceparent: parsed.traceparent,
tracestate,
};
}
return {
version: TRACE_CARRIER_VERSION,
traceparent: parsed.traceparent,
};
}
/**
* Capture the current trace context from a tracing port, ports object, app
* context, or explicit trace context. Returns `undefined` when no valid
* context is available.
*/
export function captureTraceCarrier(target: unknown): TraceCarrier | undefined {
let context: TraceContextInput | undefined;
try {
context = resolveTraceContextInput(target);
} catch {
// Context-like inputs may be proxy-backed and reject unknown properties.
}
try {
context = resolveTracingPort(target)?.current() ?? context;
} catch {
// Tracing is best-effort and must not block the owning operation.
}
try {
if (!context?.traceparent) return undefined;
return parseTraceCarrier({
version: TRACE_CARRIER_VERSION,
traceparent: context.traceparent,
...(context.tracestate ? { tracestate: context.tracestate } : {}),
});
} catch {
// Trace contexts are app-provided and remain best-effort inputs.
return undefined;
}
}
/** Resolve trace fields from a trace context or context-like object. */
export function resolveTraceContextInput(
target: unknown,
): TraceContextInput | undefined {
if (!isObject(target)) return undefined;
const context = target as TraceContextInput;
if (
context.traceId ||
context.spanId ||
context.parentSpanId ||
context.traceparent ||
context.tracestate
) {
return {
...(context.traceId ? { traceId: context.traceId } : {}),
...(context.spanId ? { spanId: context.spanId } : {}),
...(context.parentSpanId ? { parentSpanId: context.parentSpanId } : {}),
...(context.traceparent ? { traceparent: context.traceparent } : {}),
...(context.tracestate ? { tracestate: context.tracestate } : {}),
};
}
const traceContext = "trace" in target ? target.trace : undefined;
if (!traceContext || traceContext === target) return undefined;
return resolveTraceContextInput(traceContext);
}
/** Run a callback inside a tracing span when an app tracing port is installed. */
export function runWithTracing<T>(
target: unknown,
operation: TraceOperation,
run: (span?: TraceSpan) => T,
): T {
const tracing = resolveTracingPort(target);
if (!tracing) return run();
return tracing.startActiveSpan(operation, (span) => run(span));
}
function createHexId(length: number): string {
const bytes = new Uint8Array(length / 2);
if (typeof crypto !== "undefined" && "getRandomValues" in crypto) {
crypto.getRandomValues(bytes);
return Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join(
"",
);
}
let value = "";
while (value.length < length) {
value += Math.floor(Math.random() * 16).toString(16);
}
return value.slice(0, length);
}
function isNonZeroHex(value: string): boolean {
return !/^0+$/.test(value);
}
/**
* Create a non-zero W3C trace ID.
*/
export function createTraceId(): string {
let traceId = createHexId(32);
while (!isNonZeroHex(traceId)) {
traceId = createHexId(32);
}
return traceId;
}
/**
* Create a non-zero W3C span ID.
*/
export function createSpanId(): string {
let spanId = createHexId(16);
while (!isNonZeroHex(spanId)) {
spanId = createHexId(16);
}
return spanId;
}
/**
* Create a W3C traceparent header value.
*/
export function createTraceparent(args: {
traceId: string;
spanId: string;
traceFlags?: string;
}): string {
return `00-${args.traceId}-${args.spanId}-${args.traceFlags ?? "01"}`;
}
/**
* Parse and validate a W3C traceparent header value.
*/
export function parseTraceparent(
value: string | null | undefined,
): ParsedTraceparent | undefined {
if (!value) return undefined;
const normalized = value.trim().toLowerCase();
const match = TRACEPARENT_PATTERN.exec(normalized);
if (!match) return undefined;
const [, traceId, spanId, traceFlags] = match;
if (!isNonZeroHex(traceId) || !isNonZeroHex(spanId)) return undefined;
return {
traceId,
spanId,
traceFlags,
traceparent: normalized,
};
}
/**
* Create trace context from explicit IDs or an existing traceparent.
*/
export function createTraceContext(
input: TraceContextInput = {},
): TraceContext {
const parsed = parseTraceparent(input.traceparent);
const traceId = input.traceId ?? parsed?.traceId ?? createTraceId();
const parentSpanId = input.parentSpanId ?? parsed?.spanId;
const spanId = input.spanId ?? createSpanId();
return {
traceId,
spanId,
...(parentSpanId ? { parentSpanId } : {}),
traceparent: createTraceparent({
traceId,
spanId,
traceFlags: parsed?.traceFlags,
}),
...(input.tracestate ? { tracestate: input.tracestate } : {}),
};
}
/**
* Create child trace context from a parent context.
*/
export function createChildTraceContext(
parent: TraceContextInput,
): TraceContext {
return createTraceContext({
traceId: parent.traceId,
parentSpanId: parent.spanId,
traceparent: parent.traceparent,
tracestate: parent.tracestate,
});
}