tasq/node_modules/agentic-flow/dist/federation/debug/debug-stream.js

420 lines
13 KiB
JavaScript

/**
* Debug Streaming System for Federation
*
* Provides detailed, real-time visibility into agent operations
* with multiple verbosity levels and customizable output formats.
*
* Features:
* - Multiple debug levels (SILENT, BASIC, DETAILED, VERBOSE, TRACE)
* - Real-time event streaming
* - Performance metrics and timing
* - Stack traces and context
* - Customizable formatters
* - File and console output
* - JSON and human-readable formats
*/
import { EventEmitter } from 'events';
import { createWriteStream } from 'fs';
export var DebugLevel;
(function (DebugLevel) {
DebugLevel[DebugLevel["SILENT"] = 0] = "SILENT";
DebugLevel[DebugLevel["BASIC"] = 1] = "BASIC";
DebugLevel[DebugLevel["DETAILED"] = 2] = "DETAILED";
DebugLevel[DebugLevel["VERBOSE"] = 3] = "VERBOSE";
DebugLevel[DebugLevel["TRACE"] = 4] = "TRACE";
})(DebugLevel || (DebugLevel = {}));
export class DebugStream extends EventEmitter {
config;
fileStream;
eventBuffer = [];
metrics = new Map();
constructor(config = {}) {
super();
this.config = {
level: config.level ?? DebugLevel.BASIC,
output: config.output ?? 'console',
format: config.format ?? 'human',
includeTimestamps: config.includeTimestamps ?? true,
includeStackTraces: config.includeStackTraces ?? false,
includeMetadata: config.includeMetadata ?? true,
colorize: config.colorize ?? true,
filterCategories: config.filterCategories,
filterAgents: config.filterAgents,
outputFile: config.outputFile,
customStream: config.customStream,
};
if (this.config.outputFile && (this.config.output === 'file' || this.config.output === 'both')) {
this.fileStream = createWriteStream(this.config.outputFile, { flags: 'a' });
}
}
/**
* Log a debug event
*/
log(event) {
// Check if event should be logged based on level
if (event.level > this.config.level) {
return;
}
// Apply filters
if (this.config.filterCategories && !this.config.filterCategories.includes(event.category)) {
return;
}
if (this.config.filterAgents && event.agentId && !this.config.filterAgents.includes(event.agentId)) {
return;
}
const fullEvent = {
...event,
timestamp: new Date().toISOString(),
stackTrace: this.config.includeStackTraces ? this.captureStackTrace() : undefined,
};
// Buffer event
this.eventBuffer.push(fullEvent);
// Update metrics
if (event.duration !== undefined) {
const key = `${event.category}:${event.operation}`;
const existing = this.metrics.get(key) || { count: 0, totalDuration: 0 };
this.metrics.set(key, {
count: existing.count + 1,
totalDuration: existing.totalDuration + event.duration,
});
}
// Output event
this.outputEvent(fullEvent);
// Emit for external listeners
this.emit('event', fullEvent);
}
/**
* Log connection events
*/
logConnection(operation, data, error) {
this.log({
level: DebugLevel.BASIC,
category: 'connection',
operation,
data,
error,
});
}
/**
* Log database operations
*/
logDatabase(operation, data, duration, error) {
this.log({
level: DebugLevel.DETAILED,
category: 'database',
operation,
data,
duration,
error,
});
}
/**
* Log realtime events
*/
logRealtime(operation, agentId, data, duration) {
this.log({
level: DebugLevel.VERBOSE,
category: 'realtime',
operation,
agentId,
data,
duration,
});
}
/**
* Log memory operations
*/
logMemory(operation, agentId, tenantId, data, duration) {
this.log({
level: DebugLevel.DETAILED,
category: 'memory',
operation,
agentId,
tenantId,
data,
duration,
});
}
/**
* Log task operations
*/
logTask(operation, agentId, tenantId, data, duration) {
this.log({
level: DebugLevel.VERBOSE,
category: 'task',
operation,
agentId,
tenantId,
data,
duration,
});
}
/**
* Log internal state changes
*/
logTrace(operation, data) {
this.log({
level: DebugLevel.TRACE,
category: 'trace',
operation,
data,
});
}
/**
* Output event to configured destinations
*/
outputEvent(event) {
const formatted = this.formatEvent(event);
if (this.config.output === 'console' || this.config.output === 'both') {
console.log(formatted);
}
if (this.config.output === 'file' || this.config.output === 'both') {
if (this.fileStream) {
this.fileStream.write(formatted + '\n');
}
}
if (this.config.output === 'stream' && this.config.customStream) {
this.config.customStream.write(formatted + '\n');
}
}
/**
* Format event for output
*/
formatEvent(event) {
if (this.config.format === 'json') {
return JSON.stringify(event);
}
if (this.config.format === 'compact') {
return this.formatCompact(event);
}
return this.formatHuman(event);
}
/**
* Format event in human-readable format
*/
formatHuman(event) {
const parts = [];
// Timestamp
if (this.config.includeTimestamps) {
const timestamp = this.colorize(event.timestamp, 'gray');
parts.push(`[${timestamp}]`);
}
// Level
const levelStr = this.getLevelString(event.level);
parts.push(this.colorize(levelStr, this.getLevelColor(event.level)));
// Category
parts.push(this.colorize(event.category.toUpperCase(), 'cyan'));
// Agent/Tenant
if (event.agentId) {
parts.push(this.colorize(`agent=${event.agentId}`, 'blue'));
}
if (event.tenantId) {
parts.push(this.colorize(`tenant=${event.tenantId}`, 'blue'));
}
// Operation
parts.push(this.colorize(event.operation, 'white'));
// Duration
if (event.duration !== undefined) {
const durationStr = `${event.duration.toFixed(2)}ms`;
parts.push(this.colorize(durationStr, 'yellow'));
}
let output = parts.join(' ');
// Data
if (event.data && this.config.includeMetadata) {
const dataStr = typeof event.data === 'string'
? event.data
: JSON.stringify(event.data, null, 2);
output += '\n ' + this.colorize('Data:', 'gray') + ' ' + dataStr;
}
// Metadata
if (event.metadata && this.config.includeMetadata) {
output += '\n ' + this.colorize('Metadata:', 'gray') + ' ' + JSON.stringify(event.metadata);
}
// Error
if (event.error) {
output += '\n ' + this.colorize('Error:', 'red') + ' ' + event.error.message;
if (event.error.stack) {
output += '\n ' + this.colorize('Stack:', 'red') + '\n' + event.error.stack
.split('\n')
.map(line => ' ' + line)
.join('\n');
}
}
// Stack trace
if (event.stackTrace && this.config.includeStackTraces) {
output += '\n ' + this.colorize('Trace:', 'gray') + '\n' + event.stackTrace
.split('\n')
.slice(0, 5)
.map(line => ' ' + line)
.join('\n');
}
return output;
}
/**
* Format event in compact format
*/
formatCompact(event) {
const parts = [];
if (this.config.includeTimestamps) {
parts.push(event.timestamp);
}
parts.push(this.getLevelString(event.level));
parts.push(event.category);
if (event.agentId)
parts.push(`a=${event.agentId}`);
if (event.tenantId)
parts.push(`t=${event.tenantId}`);
parts.push(event.operation);
if (event.duration !== undefined) {
parts.push(`${event.duration.toFixed(0)}ms`);
}
if (event.error) {
parts.push(`ERROR: ${event.error.message}`);
}
return parts.join(' | ');
}
/**
* Get level string
*/
getLevelString(level) {
switch (level) {
case DebugLevel.SILENT: return 'SILENT';
case DebugLevel.BASIC: return 'BASIC ';
case DebugLevel.DETAILED: return 'DETAIL';
case DebugLevel.VERBOSE: return 'VERBOS';
case DebugLevel.TRACE: return 'TRACE ';
default: return 'UNKNOWN';
}
}
/**
* Get color for level
*/
getLevelColor(level) {
switch (level) {
case DebugLevel.BASIC: return 'green';
case DebugLevel.DETAILED: return 'blue';
case DebugLevel.VERBOSE: return 'magenta';
case DebugLevel.TRACE: return 'gray';
default: return 'white';
}
}
/**
* Colorize text
*/
colorize(text, color) {
if (!this.config.colorize)
return text;
const colors = {
gray: '\x1b[90m',
red: '\x1b[31m',
green: '\x1b[32m',
yellow: '\x1b[33m',
blue: '\x1b[34m',
magenta: '\x1b[35m',
cyan: '\x1b[36m',
white: '\x1b[37m',
};
const reset = '\x1b[0m';
return (colors[color] || '') + text + reset;
}
/**
* Capture stack trace
*/
captureStackTrace() {
const stack = new Error().stack || '';
return stack
.split('\n')
.slice(3) // Skip DebugStream internal frames
.join('\n');
}
/**
* Get metrics summary
*/
getMetrics() {
const summary = {};
for (const [key, value] of this.metrics.entries()) {
summary[key] = {
count: value.count,
avgDuration: value.totalDuration / value.count,
};
}
return summary;
}
/**
* Print metrics summary
*/
printMetrics() {
console.log('\n' + this.colorize('='.repeat(60), 'cyan'));
console.log(this.colorize('Performance Metrics Summary', 'cyan'));
console.log(this.colorize('='.repeat(60), 'cyan') + '\n');
const metrics = this.getMetrics();
const sorted = Object.entries(metrics).sort((a, b) => b[1].count - a[1].count);
console.log(this.colorize('Operation'.padEnd(40) + 'Count'.padEnd(10) + 'Avg Duration', 'white'));
console.log(this.colorize('-'.repeat(60), 'gray'));
for (const [key, value] of sorted) {
const countStr = value.count.toString().padEnd(10);
const durationStr = value.avgDuration.toFixed(2) + 'ms';
console.log(key.padEnd(40) +
this.colorize(countStr, 'yellow') +
this.colorize(durationStr, 'green'));
}
console.log('\n' + this.colorize('='.repeat(60), 'cyan') + '\n');
}
/**
* Get event buffer
*/
getEvents(filter) {
let events = [...this.eventBuffer];
if (filter?.category) {
events = events.filter(e => e.category === filter.category);
}
if (filter?.agentId) {
events = events.filter(e => e.agentId === filter.agentId);
}
if (filter?.since) {
events = events.filter(e => new Date(e.timestamp) >= filter.since);
}
return events;
}
/**
* Clear event buffer
*/
clearEvents() {
this.eventBuffer = [];
}
/**
* Clear metrics
*/
clearMetrics() {
this.metrics.clear();
}
/**
* Close file stream
*/
close() {
if (this.fileStream) {
this.fileStream.end();
}
}
}
/**
* Create debug stream with sensible defaults
*/
export function createDebugStream(config) {
return new DebugStream(config);
}
/**
* Get debug level from environment variable
*/
export function getDebugLevelFromEnv() {
const level = process.env.DEBUG_LEVEL?.toUpperCase();
switch (level) {
case 'SILENT': return DebugLevel.SILENT;
case 'BASIC': return DebugLevel.BASIC;
case 'DETAILED': return DebugLevel.DETAILED;
case 'VERBOSE': return DebugLevel.VERBOSE;
case 'TRACE': return DebugLevel.TRACE;
default: return DebugLevel.BASIC;
}
}
//# sourceMappingURL=debug-stream.js.map