chore: added logging

This commit is contained in:
Khaushik-keygraph
2025-10-17 13:52:13 +05:30
parent 6cdd067dd4
commit e193d04594
3 changed files with 91 additions and 0 deletions

View File

@@ -25,6 +25,7 @@ import { assembleFinalReport } from './src/phases/reporting.js';
// Utils
import { timingResults, costResults, displayTimingSummary, Timer, formatDuration } from './src/utils/metrics.js';
import { setupLogging } from './src/utils/logger.js';
// CLI
import { handleDeveloperCommand } from './src/cli/command-handler.js';
@@ -44,16 +45,21 @@ import {
// Configure zx to disable timeouts (let tools run as long as needed)
$.timeout = 0;
// Global cleanup function for logging
let cleanupLogging = null;
// Setup graceful cleanup on process signals
process.on('SIGINT', async () => {
console.log(chalk.yellow('\n⚠ Received SIGINT, cleaning up...'));
await cleanupMCP();
if (cleanupLogging) await cleanupLogging();
process.exit(0);
});
process.on('SIGTERM', async () => {
console.log(chalk.yellow('\n⚠ Received SIGTERM, cleaning up...'));
await cleanupMCP();
if (cleanupLogging) await cleanupLogging();
process.exit(0);
});
@@ -377,6 +383,7 @@ if (args[0] && args[0].includes('shannon.mjs')) {
// Parse flags and arguments
let configPath = null;
let pipelineTestingMode = false;
let logFilePath = null;
const nonFlagArgs = [];
let developerCommand = null;
const developerCommands = ['--run-phase', '--run-all', '--rollback-to', '--rerun', '--status', '--list-agents', '--cleanup'];
@@ -390,6 +397,16 @@ for (let i = 0; i < args.length; i++) {
console.log(chalk.red('❌ --config flag requires a file path'));
process.exit(1);
}
} else if (args[i] === '--log') {
// --log can optionally take a file path, otherwise use default
if (i + 1 < args.length && !args[i + 1].startsWith('-')) {
logFilePath = args[i + 1];
i++; // Skip the next argument
} else {
// Generate default log filename with timestamp
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, -5);
logFilePath = `shannon-${timestamp}.log`;
}
} else if (args[i] === '--pipeline-testing') {
pipelineTestingMode = true;
} else if (developerCommands.includes(args[i])) {
@@ -416,10 +433,25 @@ if (args.includes('--help') || args.includes('-h') || args.includes('help')) {
process.exit(0);
}
// Setup logging if --log flag is present
if (logFilePath) {
try {
cleanupLogging = await setupLogging(logFilePath);
const absoluteLogPath = path.isAbsolute(logFilePath)
? logFilePath
: path.join(process.cwd(), logFilePath);
console.log(chalk.green(`📝 Logging enabled: ${absoluteLogPath}`));
} catch (error) {
console.log(chalk.yellow(`⚠️ Failed to setup logging: ${error.message}`));
console.log(chalk.gray('Continuing without logging...'));
}
}
// Handle developer commands
if (developerCommand) {
await handleDeveloperCommand(developerCommand, nonFlagArgs, pipelineTestingMode, runClaudePromptWithRetry, loadPrompt);
await cleanupMCP();
if (cleanupLogging) await cleanupLogging();
process.exit(0);
}
@@ -470,6 +502,7 @@ try {
console.log(chalk.green.bold('\n📄 FINAL REPORT AVAILABLE:'));
console.log(chalk.cyan(finalReportPath));
await cleanupMCP();
if (cleanupLogging) await cleanupLogging();
} catch (error) {
// Enhanced error boundary with proper logging
if (error instanceof PentestError) {
@@ -490,5 +523,6 @@ try {
}
}
await cleanupMCP();
if (cleanupLogging) await cleanupLogging();
process.exit(1);
}

View File

@@ -21,6 +21,7 @@ export function showHelp() {
console.log(chalk.yellow.bold('OPTIONS:'));
console.log(' --config <file> YAML configuration file for authentication and testing parameters');
console.log(' --log [file] Capture all output to log file (default: shannon-<timestamp>.log)');
console.log(' --pipeline-testing Use minimal prompts for fast pipeline testing (creates minimal deliverables)\n');
console.log(chalk.yellow.bold('DEVELOPER COMMANDS:'));
@@ -36,6 +37,7 @@ export function showHelp() {
console.log(' # Normal mode - create new session');
console.log(' ./shannon.mjs "https://example.com" "/path/to/local/repo"');
console.log(' ./shannon.mjs "https://example.com" "/path/to/local/repo" --config auth.yaml');
console.log(' ./shannon.mjs "https://example.com" "/path/to/local/repo" --log pentest.log');
console.log(' ./shannon.mjs "https://example.com" "/path/to/local/repo" --setup-only # Setup only\n');
console.log(' # Developer mode - operate on existing session');

55
src/utils/logger.js Normal file
View File

@@ -0,0 +1,55 @@
import { fs } from 'zx';
import { path } from 'zx';
/**
* Sets up logging to capture all stdout and stderr to a file
* @param {string} logFilePath - Path to the log file
* @returns {Promise<Function>} Cleanup function to restore original streams
*/
export async function setupLogging(logFilePath) {
// Resolve to absolute path
const absoluteLogPath = path.isAbsolute(logFilePath)
? logFilePath
: path.join(process.cwd(), logFilePath);
// Ensure the directory exists
await fs.ensureDir(path.dirname(absoluteLogPath));
// Create write stream for the log file
const logStream = fs.createWriteStream(absoluteLogPath, { flags: 'a' });
// Store original stdout/stderr write functions
const originalStdoutWrite = process.stdout.write.bind(process.stdout);
const originalStderrWrite = process.stderr.write.bind(process.stderr);
// Override stdout
process.stdout.write = function(chunk, encoding, callback) {
// Write to both original stdout and log file
originalStdoutWrite(chunk, encoding, callback);
logStream.write(chunk, encoding);
return true;
};
// Override stderr
process.stderr.write = function(chunk, encoding, callback) {
// Write to both original stderr and log file
originalStderrWrite(chunk, encoding, callback);
logStream.write(chunk, encoding);
return true;
};
// Return cleanup function
return async function cleanup() {
// Restore original streams
process.stdout.write = originalStdoutWrite;
process.stderr.write = originalStderrWrite;
// Close the log stream
return new Promise((resolve, reject) => {
logStream.end((err) => {
if (err) reject(err);
else resolve();
});
});
};
}