import { LogParser, LogEntry } from './logParser'; import fs from 'fs/promises'; import path from 'path'; import { STAGING_CONFIG } from '../../../playwright.config'; import { Client } from 'ssh2'; export interface LogSource { name: string; path: string; type: 'wordpress' | 'php' | 'nginx-access' | 'nginx-error'; } export class LogIntegrator { private logParser: LogParser; private sources: LogSource[] = [ { name: 'WordPress Debug', path: `${STAGING_CONFIG.path}/wp-content/debug.log`, type: 'wordpress' }, { name: 'PHP Error', path: '/var/log/php_errors.log', type: 'php' }, { name: 'Nginx Access', path: '/var/log/nginx/access.log', type: 'nginx-access' }, { name: 'Nginx Error', path: '/var/log/nginx/error.log', type: 'nginx-error' } ]; constructor() { this.logParser = new LogParser(); } async collectLogs(testStartTime: Date, testEndTime: Date): Promise> { const logs = new Map(); for (const source of this.sources) { const entries = await this.fetchAndFilterLogs(source, testStartTime, testEndTime); logs.set(source.name, entries); } return logs; } async assertLogCondition(condition: { source: string; level?: 'INFO' | 'WARNING' | 'ERROR'; message?: string | RegExp; component?: string; timeWindow?: number; }): Promise { const source = this.sources.find(s => s.name === condition.source); if (!source) throw new Error(`Unknown log source: ${condition.source}`); const entries = await this.logParser.parseLogFile(source.path); return entries.some(entry => { if (condition.level && entry.level !== condition.level) return false; if (condition.component && entry.component !== condition.component) return false; if (condition.message) { if (condition.message instanceof RegExp) { if (!condition.message.test(entry.message)) return false; } else { if (entry.message !== condition.message) return false; } } return true; }); } private async fetchAndFilterLogs( source: LogSource, startTime: Date, endTime: Date ): Promise { const entries = await this.logParser.parseLogFile(source.path); return entries.filter(entry => { const timestamp = new Date(entry.timestamp); return timestamp >= startTime && timestamp <= endTime; }); } async correlateTestAction( actionName: string, timestamp: Date, timeWindow: number = 5000 ): Promise { const correlatedEntries: LogEntry[] = []; const startTime = new Date(timestamp.getTime() - timeWindow); const endTime = new Date(timestamp.getTime() + timeWindow); for (const source of this.sources) { const entries = await this.fetchAndFilterLogs(source, startTime, endTime); correlatedEntries.push(...entries); } return correlatedEntries.sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime() ); } async saveLogsToFile( outputPath: string, logs: Map ): Promise { const output: Record = {}; logs.forEach((entries, source) => { output[source] = entries.map(entry => ({ timestamp: entry.timestamp, level: entry.level, component: entry.component, message: entry.message, context: entry.context })); }); await fs.mkdir(path.dirname(outputPath), { recursive: true }); await fs.writeFile( outputPath, JSON.stringify(output, null, 2) ); } }