UNPKG

docker-pilot

Version:

A powerful, scalable Docker CLI library for managing containerized applications of any size

214 lines 8.87 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.LogsCommand = void 0; const BaseCommand_1 = require("./BaseCommand"); const child_process_1 = require("child_process"); const util_1 = require("util"); const execAsync = (0, util_1.promisify)(child_process_1.exec); class LogsCommand extends BaseCommand_1.BaseCommand { constructor(context) { super('logs', 'View output from containers', 'docker-pilot logs [service-name] [options]', context); } async execute(args, _options) { const { args: parsedArgs, options: parsedOptions } = this.parseOptions(args); const serviceName = parsedArgs[0]; try { if (!(await this.checkDockerAvailable())) { return this.createErrorResult(this.i18n.t('cmd.docker_not_available')); } // Show loading message if (serviceName) { this.logger.info(this.i18n.t('cmd.logs.loading', { service: serviceName })); } else { this.logger.info('Getting logs for all services...'); } const { result: logsOutput, executionTime } = await this.measureExecutionTime(async () => { return await this.getContainerLogs(serviceName, parsedOptions); }); // Display logs this.showContainerLogs(logsOutput, serviceName); // Create summary for interactive menu const logSummary = this.createLogsSummary(logsOutput, serviceName); return this.createSuccessResult(logSummary, executionTime); } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; this.logger.error(this.i18n.t('cmd.logs.failed', { error: errorMessage })); return this.createErrorResult(errorMessage); } } /** * Get real container logs using docker compose logs */ async getContainerLogs(serviceName, options) { try { // Build Docker command const composeFile = this.context.composeFile; const logsArgs = ['docker', 'compose']; // Add compose file if available (always should be from context) if (composeFile) { logsArgs.push('-f', composeFile); } logsArgs.push('logs'); // Add options const isFollowMode = options?.['follow'] || options?.['f']; if (isFollowMode) { logsArgs.push('--follow'); } if (options?.['tail']) { logsArgs.push('--tail', options['tail']); } else if (!isFollowMode) { // Default to last 50 lines if not following and no tail specified logsArgs.push('--tail', '50'); } if (options?.['since']) { logsArgs.push('--since', options['since']); } if (options?.['until']) { logsArgs.push('--until', options['until']); } if (options?.['timestamps'] || options?.['t']) { logsArgs.push('--timestamps'); } // Add specific service if provided if (serviceName) { logsArgs.push(serviceName); } const command = logsArgs.join(' '); // Handle follow mode differently if (isFollowMode) { // For follow mode, we need to handle the streaming output return await this.execDockerCommandStream(command); } else { const result = await this.execDockerCommand(command); return result.stdout || 'No logs available'; } } catch (error) { this.logger.warn(`Failed to get container logs: ${error instanceof Error ? error.message : 'Unknown error'}`); return 'Failed to retrieve logs'; } } /** * Execute Docker command with streaming support for follow mode */ async execDockerCommandStream(command) { try { return new Promise((resolve, reject) => { const args = command.split(' '); const cmd = args.shift(); const child = (0, child_process_1.spawn)(cmd, args, { cwd: this.context.workingDirectory, stdio: ['pipe', 'pipe', 'pipe'] }); let output = ''; let isFirstOutput = true; child.stdout.on('data', (data) => { const chunk = data.toString(); output += chunk; // Stream output directly to console for follow mode if (isFirstOutput) { this.logger.newLine(); isFirstOutput = false; } process.stdout.write(chunk); }); child.stderr.on('data', (data) => { const chunk = data.toString(); process.stderr.write(chunk); }); child.on('error', (error) => { reject(error); }); child.on('close', (code) => { if (code === 0 || output.length > 0) { resolve(output || 'Logs streaming completed'); } else { reject(new Error(`Command failed with exit code ${code}`)); } }); // Handle Ctrl+C to gracefully stop following process.on('SIGINT', () => { this.logger.newLine(); this.logger.info('🛑 Stopping log stream...'); child.kill('SIGTERM'); resolve(output || 'Log streaming stopped by user'); }); }); } catch (error) { throw error; } } /** * Execute Docker command with proper error handling */ async execDockerCommand(command) { try { const result = await execAsync(command, { cwd: this.context.workingDirectory, maxBuffer: 1024 * 1024 * 10 // 10MB buffer for logs }); return result; } catch (error) { // Some docker commands return non-zero exit codes but still have useful output if (error.stdout) { return { stdout: error.stdout, stderr: error.stderr || '' }; } throw error; } } /** * Display container logs with formatting */ showContainerLogs(logs, serviceName) { this.logger.newLine(); if (serviceName) { this.logger.info(`📋 Logs for service: ${serviceName}`); } else { this.logger.info('📋 Logs for all services'); } this.logger.separator('-', 50); this.logger.newLine(); if (!logs || logs.trim() === '' || logs === 'No logs available') { this.logger.info('No logs available for the specified criteria.'); this.logger.info('💡 Tip: Make sure the service is running and has generated some logs.'); return; } // Display the logs directly console.log(logs); this.logger.newLine(); } /** * Create a summary of logs for interactive menu */ createLogsSummary(logs, serviceName) { const target = serviceName ? `service "${serviceName}"` : 'all services'; if (!logs || logs.trim() === '' || logs === 'No logs available') { return `📭 No logs available for ${target}. Make sure the service is running and has generated some logs.`; } const lines = logs.split('\n').filter(line => line.trim() !== ''); const lineCount = lines.length; return `📋 Retrieved ${lineCount} log lines for ${target}`; } showExamples() { this.logger.info(` Examples: docker-pilot logs # Show last 50 lines for all services docker-pilot logs mailhog # Show last 50 lines for mailhog service docker-pilot logs --follow # Follow log output (tail -f) for all services docker-pilot logs --tail 100 # Show last 100 lines for all services docker-pilot logs --since 2h # Show logs from last 2 hours docker-pilot logs --timestamps # Show logs with timestamps docker-pilot logs mailhog --follow --tail 20 # Follow last 20 lines of mailhog service docker-pilot logs --since "2023-01-01 12:00:00" # Show logs since specific time `); } } exports.LogsCommand = LogsCommand; //# sourceMappingURL=LogsCommand.js.map