@gati-framework/runtime
Version:
Gati runtime execution engine for running handler-based applications
172 lines • 4.7 kB
JavaScript
/**
* @module runtime/trace-collector
* @description Collects request traces through the pipeline
*/
/**
* Collects request traces through pipeline stages
*/
export class TraceCollector {
activeTraces = new Map();
config;
constructor(config = {}) {
this.config = {
enabled: config.enabled ?? false,
maxTraces: config.maxTraces ?? 1000,
retentionMs: config.retentionMs ?? 300000, // 5 minutes
};
}
/**
* Start collecting a trace for a request
*/
startTrace(request, traceId) {
if (!this.config.enabled)
return;
const trace = {
id: traceId,
timestamp: Date.now(),
request,
stages: [],
snapshots: {},
duration: 0,
status: 'pending',
};
this.activeTraces.set(traceId, {
trace,
stageStack: [],
});
this.enforceMemoryLimits();
}
/**
* Capture a pipeline stage
*/
captureStage(traceId, stage, metadata = {}) {
if (!this.config.enabled)
return;
const active = this.activeTraces.get(traceId);
if (!active)
return;
const traceStage = {
name: stage,
startTime: Date.now(),
snapshotId: `${traceId}_${stage}_${Date.now()}`,
metadata,
};
// Add to parent stage if exists, otherwise to root
const parent = active.stageStack[active.stageStack.length - 1];
if (parent) {
parent.children = parent.children || [];
parent.children.push(traceStage);
}
else {
active.trace.stages.push(traceStage);
}
active.stageStack.push(traceStage);
}
/**
* Capture a snapshot at current stage
*/
captureSnapshot(traceId, lctx) {
if (!this.config.enabled)
return;
const active = this.activeTraces.get(traceId);
if (!active)
return;
const currentStage = active.stageStack[active.stageStack.length - 1];
if (!currentStage)
return;
const snapshot = lctx.snapshot.create();
active.trace.snapshots[currentStage.snapshotId] = snapshot;
}
/**
* Complete current stage
*/
completeStage(traceId) {
if (!this.config.enabled)
return;
const active = this.activeTraces.get(traceId);
if (!active || active.stageStack.length === 0)
return;
const stage = active.stageStack.pop();
stage.endTime = Date.now();
}
/**
* End trace collection
*/
endTrace(traceId, response, error) {
if (!this.config.enabled)
return null;
const active = this.activeTraces.get(traceId);
if (!active)
return null;
const trace = active.trace;
trace.duration = Date.now() - trace.timestamp;
trace.response = response;
trace.status = error ? 'error' : 'success';
if (error) {
trace.error = {
message: error.message,
stack: error.stack,
code: error.code,
};
}
this.activeTraces.delete(traceId);
return trace;
}
/**
* Get active trace
*/
getActiveTrace(traceId) {
const active = this.activeTraces.get(traceId);
return active ? active.trace : null;
}
/**
* Enable trace collection
*/
enable() {
this.config.enabled = true;
}
/**
* Disable trace collection
*/
disable() {
this.config.enabled = false;
}
/**
* Check if enabled
*/
isEnabled() {
return this.config.enabled;
}
/**
* Clear all active traces
*/
clear() {
this.activeTraces.clear();
}
/**
* Get active trace count
*/
getActiveCount() {
return this.activeTraces.size;
}
/**
* Enforce memory limits
*/
enforceMemoryLimits() {
if (this.activeTraces.size <= this.config.maxTraces)
return;
// Remove oldest traces
const entries = Array.from(this.activeTraces.entries());
const toRemove = entries
.sort((a, b) => a[1].trace.timestamp - b[1].trace.timestamp)
.slice(0, entries.length - this.config.maxTraces);
toRemove.forEach(([id]) => this.activeTraces.delete(id));
}
}
/**
* Create a trace collector instance
*/
export function createTraceCollector(config) {
return new TraceCollector(config);
}
//# sourceMappingURL=trace-collector.js.map