design-agent
Version:
Universal AI Design Review Agent - CLI tool for scanning code for design drift
290 lines (241 loc) • 6.61 kB
JavaScript
/**
* Logging utilities for design agent
* Provides structured logging with different levels and outputs
*/
export class Logger {
constructor(options = {}) {
this.level = options.level || 'info';
this.output = options.output || 'console';
this.format = options.format || 'text';
this.includeTimestamp = options.includeTimestamp !== false;
this.includeContext = options.includeContext !== false;
this.levels = {
error: 0,
warn: 1,
info: 2,
debug: 3
};
}
log(level, message, context = {}) {
if (this.levels[level] > this.levels[this.level]) {
return;
}
const logEntry = this.createLogEntry(level, message, context);
switch (this.output) {
case 'console':
this.logToConsole(logEntry);
break;
case 'file':
this.logToFile(logEntry);
break;
case 'json':
this.logToJson(logEntry);
break;
}
}
createLogEntry(level, message, context) {
const entry = {
level,
message,
timestamp: new Date().toISOString()
};
if (this.includeContext && Object.keys(context).length > 0) {
entry.context = context;
}
return entry;
}
logToConsole(entry) {
const prefix = this.getConsolePrefix(entry.level);
const message = this.format === 'json'
? JSON.stringify(entry, null, 2)
: `${prefix} ${entry.message}`;
switch (entry.level) {
case 'error':
console.error(message);
break;
case 'warn':
console.warn(message);
break;
case 'info':
console.info(message);
break;
case 'debug':
console.debug(message);
break;
}
}
logToFile(entry) {
// In a real implementation, this would write to a file
// For now, we'll just log to console
this.logToConsole(entry);
}
logToJson(entry) {
console.log(JSON.stringify(entry));
}
getConsolePrefix(level) {
const prefixes = {
error: '❌',
warn: '⚠️',
info: 'ℹ️',
debug: '🐛'
};
return prefixes[level] || '📝';
}
error(message, context) {
this.log('error', message, context);
}
warn(message, context) {
this.log('warn', message, context);
}
info(message, context) {
this.log('info', message, context);
}
debug(message, context) {
this.log('debug', message, context);
}
}
export function createLogger(options = {}) {
return new Logger(options);
}
export function createProgressLogger(total, options = {}) {
const logger = createLogger(options);
let current = 0;
return {
update(increment = 1) {
current += increment;
const percentage = Math.round((current / total) * 100);
logger.info(`Progress: ${current}/${total} (${percentage}%)`);
},
complete() {
logger.info(`Progress: ${total}/${total} (100%) - Complete!`);
},
reset() {
current = 0;
}
};
}
export function createFileLogger(filePath, options = {}) {
const logger = createLogger({ ...options, output: 'file' });
return {
...logger,
filePath
};
}
export function createJsonLogger(options = {}) {
return createLogger({ ...options, format: 'json' });
}
export function createSilentLogger() {
return createLogger({ level: 'error' });
}
export function createVerboseLogger() {
return createLogger({ level: 'debug' });
}
export function logPerformance(operation, fn, logger) {
const start = Date.now();
try {
const result = fn();
const duration = Date.now() - start;
logger.info(`Operation completed: ${operation}`, {
duration: `${duration}ms`,
operation
});
return result;
} catch (error) {
const duration = Date.now() - start;
logger.error(`Operation failed: ${operation}`, {
duration: `${duration}ms`,
operation,
error: error.message
});
throw error;
}
}
export function logMemoryUsage(logger) {
const usage = process.memoryUsage();
logger.debug('Memory usage', {
rss: `${Math.round(usage.rss / 1024 / 1024)}MB`,
heapTotal: `${Math.round(usage.heapTotal / 1024 / 1024)}MB`,
heapUsed: `${Math.round(usage.heapUsed / 1024 / 1024)}MB`,
external: `${Math.round(usage.external / 1024 / 1024)}MB`
});
}
export function logEnvironment(logger) {
logger.info('Environment info', {
nodeVersion: process.version,
platform: process.platform,
arch: process.arch,
cwd: process.cwd(),
env: process.env.NODE_ENV || 'development'
});
}
export function logConfig(logger, config) {
logger.debug('Configuration loaded', {
project: config.project,
adapters: config.adapters?.length || 0,
scanPaths: config.scanPaths?.length || 0,
checks: Object.keys(config.checks || {}).length
});
}
export function logFindings(logger, findings) {
const bySeverity = findings.reduce((acc, finding) => {
acc[finding.severity] = (acc[finding.severity] || 0) + 1;
return acc;
}, {});
logger.info('Scan results', {
total: findings.length,
critical: bySeverity.critical || 0,
major: bySeverity.major || 0,
minor: bySeverity.minor || 0
});
}
export function logAdapterExecution(logger, adapter, file, success, duration) {
if (success) {
logger.debug(`Adapter executed: ${adapter}`, {
file,
duration: `${duration}ms`
});
} else {
logger.warn(`Adapter failed: ${adapter}`, {
file,
duration: `${duration}ms`
});
}
}
export function logError(logger, error, context = {}) {
logger.error('Error occurred', {
message: error.message,
name: error.name,
stack: error.stack,
...context
});
}
export function createAuditLogger(options = {}) {
const logger = createLogger(options);
return {
startScan(config) {
logger.info('Design agent scan started', {
project: config.project,
adapters: config.adapters,
scanPaths: config.scanPaths
});
},
endScan(findings, duration) {
logger.info('Design agent scan completed', {
findings: findings.length,
duration: `${duration}ms`
});
},
fileProcessed(file, findings) {
logger.debug('File processed', {
file,
findings: findings.length
});
},
adapterExecuted(adapter, file, success, duration) {
logAdapterExecution(logger, adapter, file, success, duration);
},
error(error, context) {
logError(logger, error, context);
}
};
}