diff --git a/straceanalyse.py b/straceanalyse.py index 53ccb69..a11f4f6 100644 --- a/straceanalyse.py +++ b/straceanalyse.py @@ -22,22 +22,22 @@ class ShopPerformanceAnalyzer: 'file_paths': Counter(), 'errors': Counter() } - self.debug = False # Weniger Output bei vielen Prozessen + self.debug = False self.output_dir = f"/root/shop_analysis_{domain}_{datetime.now().strftime('%Y%m%d_%H%M%S')}" def get_php_fpm_pids(self): - """Finde alle PHP-FPM PIDs für den Shop""" + """Finde alle PHP-FPM PIDs fuer den Shop""" try: cmd = f"ps aux | grep 'php-fpm: pool {self.domain}' | grep -v grep | awk '{{print $2}}'" result = subprocess.run(cmd, shell=True, capture_output=True, text=True) pids = [int(pid) for pid in result.stdout.strip().split('\n') if pid] return pids except Exception as e: - print(f"❌ Fehler beim Finden der PIDs: {e}") + print(f"Fehler beim Finden der PIDs: {e}") return [] def run_strace(self, pid, duration=5): - """Führe strace auf einem Prozess aus""" + """Fuehre strace auf einem Prozess aus""" try: cmd = [ 'strace', @@ -137,10 +137,10 @@ class ShopPerformanceAnalyzer: os.makedirs(self.output_dir, exist_ok=True) - # 1. Komplette Liste (sortiert nach Häufigkeit) + # 1. Komplette Liste (sortiert nach Haeufigkeit) list_file = os.path.join(self.output_dir, 'missing_files_all.txt') with open(list_file, 'w') as f: - f.write(f"# Fehlende Dateien für {self.domain}\n") + f.write(f"# Fehlende Dateien fuer {self.domain}\n") f.write(f"# Erstellt: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n") f.write(f"# Total: {len(self.results['missing_files'])} Dateien\n") f.write(f"# Zugriffe: {sum(self.results['missing_files'].values())}\n") @@ -192,7 +192,7 @@ class ShopPerformanceAnalyzer: f.write(f'SHOP_ROOT="/var/www/vhosts/{self.domain}/httpdocs"\n') f.write('PLACEHOLDER="$SHOP_ROOT/gfx/keinBild.gif"\n\n') f.write('if [ ! -f "$PLACEHOLDER" ]; then\n') - f.write(' echo "❌ Fehler: Placeholder nicht gefunden: $PLACEHOLDER"\n') + f.write(' echo "Fehler: Placeholder nicht gefunden: $PLACEHOLDER"\n') f.write(' exit 1\n') f.write('fi\n\n') f.write('echo "Erstelle fehlende Dateien..."\n') @@ -217,9 +217,9 @@ class ShopPerformanceAnalyzer: f.write(f'fi\n') f.write('\necho ""\n') - f.write('echo "✅ Fertig!"\n') + f.write('echo "Fertig!"\n') f.write('echo " Erstellt: $CREATED Platzhalter"\n') - f.write('echo " Übersprungen: $SKIPPED (existieren bereits)"\n') + f.write('echo " Uebersprungen: $SKIPPED (existieren bereits)"\n') f.write(f'echo " Total Dateien: {len(image_files)}"\n') os.chmod(script_file, 0o755) @@ -257,11 +257,11 @@ class ShopPerformanceAnalyzer: with open(manufacturer_file, 'w') as f: f.write(f"# Hersteller IDs mit fehlenden Bildern\n") f.write(f"# Total: {len(manufacturer_ids)} Hersteller\n") - f.write(f"# Verwendung: Im JTL-Shop Admin diese Hersteller prüfen\n\n") + f.write(f"# Verwendung: Im JTL-Shop Admin diese Hersteller pruefen\n\n") for mid in sorted(manufacturer_ids, key=int): f.write(f"{mid}\n") - # 6. Nur Dateipfade (für weitere Verarbeitung) + # 6. Nur Dateipfade (fuer weitere Verarbeitung) paths_only_file = os.path.join(self.output_dir, 'missing_files_paths_only.txt') with open(paths_only_file, 'w') as f: for filepath in self.results['missing_files'].keys(): @@ -272,36 +272,36 @@ class ShopPerformanceAnalyzer: def generate_report(self): """Generiere Analyse-Report""" print("\n" + "="*80) - print(f"🔍 PERFORMANCE ANALYSE: {self.domain}") - print(f"📅 {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}") + print(f"PERFORMANCE ANALYSE: {self.domain}") + print(f"Datum: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}") print("="*80 + "\n") total_syscalls = sum(self.results['syscalls'].values()) if total_syscalls == 0: - print("⚠️ WARNUNG: Keine Syscalls aufgezeichnet!") - print(" Mögliche Gründe:") + print("WARNUNG: Keine Syscalls aufgezeichnet!") + print(" Moegliche Gruende:") print(" - Prozesse sind gerade idle (wenig Traffic)") print(" - Strace hat keine Berechtigung") print(" - Prozesse wurden zwischen Analyse beendet\n") print(" Versuche: python3 script.py spiel-und-modellbau.com 10\n") return - print("📊 SYSCALL STATISTIK") + print("SYSCALL STATISTIK") print("-" * 80) for syscall, count in self.results['syscalls'].most_common(15): percentage = (count / total_syscalls * 100) if total_syscalls > 0 else 0 - bar = '█' * int(percentage / 2) + bar = '#' * int(percentage / 2) print(f" {syscall:20s}: {count:6d} ({percentage:5.1f}%) {bar}") print() # Fehlende Dateien missing_count = len(self.results['missing_files']) if missing_count > 0: - print("❌ FEHLENDE DATEIEN (ENOENT)") + print("FEHLENDE DATEIEN (ENOENT)") print("-" * 80) - print(f" ⚠️ {missing_count} verschiedene Dateien nicht gefunden!") - print(f" ⚠️ {sum(self.results['missing_files'].values())} Zugriffe auf nicht-existierende Dateien!\n") + print(f" WARNUNG: {missing_count} verschiedene Dateien nicht gefunden!") + print(f" WARNUNG: {sum(self.results['missing_files'].values())} Zugriffe auf nicht-existierende Dateien!\n") # Kategorien categories = defaultdict(int) @@ -319,7 +319,7 @@ class ShopPerformanceAnalyzer: print(" Kategorien:") for category, count in sorted(categories.items(), key=lambda x: x[1], reverse=True): - print(f" • {category:25s}: {count:4d} Dateien") + print(f" * {category:25s}: {count:4d} Dateien") print() print(" Top 15 fehlende Dateien:") @@ -328,13 +328,13 @@ class ShopPerformanceAnalyzer: print(f" [{count:3d}x] {short_path}") if len(self.results['missing_files']) > 15: - print(f"\n ℹ️ ... und {len(self.results['missing_files'])-15} weitere") - print(f" ℹ️ Vollständige Liste siehe Export-Dateien!") + print(f"\n INFO: ... und {len(self.results['missing_files'])-15} weitere") + print(f" INFO: Vollstaendige Liste siehe Export-Dateien!") print() # Errors if self.results['errors']: - print("⚠️ FEHLER") + print("FEHLER") print("-" * 80) for error, count in self.results['errors'].items(): print(f" {error:20s}: {count:6d}x") @@ -342,19 +342,19 @@ class ShopPerformanceAnalyzer: # MySQL Queries if self.results['mysql_queries']: - print("🗄️ MYSQL QUERIES") + print("MYSQL QUERIES") print("-" * 80) query_counter = Counter(self.results['mysql_queries']) print(f" Total Queries: {len(self.results['mysql_queries'])}") print(f" Unique Queries: {len(query_counter)}") - print("\n Häufigste Queries:") + print("\n Haeufigste Queries:") for query, count in query_counter.most_common(10): print(f" [{count:3d}x] {query[:70]}...") print() # File Paths if self.results['file_paths']: - print("📁 HÄUFIGSTE DATEIZUGRIFFE") + print("HAEUFIGSTE DATEIZUGRIFFE") print("-" * 80) for path, count in self.results['file_paths'].most_common(15): print(f" [{count:3d}x] {path}") @@ -362,7 +362,7 @@ class ShopPerformanceAnalyzer: # Redis if self.results['redis_operations']: - print("🔴 REDIS OPERATIONEN") + print("REDIS OPERATIONEN") print("-" * 80) total_redis = sum(self.results['redis_operations'].values()) for op, count in self.results['redis_operations'].most_common(): @@ -372,29 +372,29 @@ class ShopPerformanceAnalyzer: # Slow Operations if self.results['slow_operations']: - print("🐌 LANGSAME OPERATIONEN") + print("LANGSAME OPERATIONEN") print("-" * 80) slow_counter = Counter(self.results['slow_operations']) for op, count in slow_counter.items(): - print(f" ⚠️ {op}: {count}x") + print(f" WARNUNG: {op}: {count}x") print() # Export fehlende Dateien if missing_count > 0: print("="*80) - print("💾 EXPORTIERE FEHLENDE DATEIEN") + print("EXPORTIERE FEHLENDE DATEIEN") print("="*80 + "\n") export_dir = self.export_missing_files() if export_dir: - print(f"✅ Dateien exportiert nach: {export_dir}\n") + print(f"Dateien exportiert nach: {export_dir}\n") print(" Erstellt:") - print(f" 📄 missing_files_all.txt - Komplette Liste (sortiert nach Häufigkeit)") - print(f" 📁 missing_files_by_category.txt - Nach Kategorie gruppiert") - print(f" 📊 missing_files.csv - CSV für Excel") - print(f" 🔧 create_placeholders.sh - Bash Script (ausführbar)") - print(f" 🏷️ missing_manufacturer_ids.txt - Hersteller IDs") - print(f" 📝 missing_files_paths_only.txt - Nur Pfade (für Scripts)\n") + print(f" missing_files_all.txt - Komplette Liste (sortiert nach Haeufigkeit)") + print(f" missing_files_by_category.txt - Nach Kategorie gruppiert") + print(f" missing_files.csv - CSV fuer Excel") + print(f" create_placeholders.sh - Bash Script (ausfuehrbar)") + print(f" missing_manufacturer_ids.txt - Hersteller IDs") + print(f" missing_files_paths_only.txt - Nur Pfade (fuer Scripts)\n") print(" Quick-Fix:") print(f" bash {export_dir}/create_placeholders.sh\n") @@ -405,7 +405,7 @@ class ShopPerformanceAnalyzer: def generate_recommendations(self, total_syscalls, missing_count): """Generiere Handlungsempfehlungen""" print("="*80) - print("💡 HANDLUNGSEMPFEHLUNGEN FÜR DEN KUNDEN") + print("HANDLUNGSEMPFEHLUNGEN FUER DEN KUNDEN") print("="*80 + "\n") recommendations = [] @@ -418,10 +418,10 @@ class ShopPerformanceAnalyzer: if manufacturer_missing > 0: recommendations.append({ 'priority': priority, - 'severity': '🔥 KRITISCH', + 'severity': 'KRITISCH', 'problem': f'{manufacturer_missing} Hersteller-Bilder fehlen', 'impact': f'Jedes fehlende Bild = 6-8 stat() Calls. Bei {sum(v for k,v in self.results["missing_files"].items() if "manufacturer" in k)} Zugriffen!', - 'solution': '1. JTL-Shop Admin einloggen\n2. Bilder → Hersteller-Bilder → "Fehlende generieren"\n3. ODER: Bash Script ausführen (siehe Export)', + 'solution': '1. JTL-Shop Admin einloggen\n2. Bilder -> Hersteller-Bilder -> "Fehlende generieren"\n3. ODER: Bash Script ausfuehren (siehe Export)', 'files': [f"{self.output_dir}/create_placeholders.sh", f"{self.output_dir}/missing_manufacturer_ids.txt"] }) @@ -430,10 +430,10 @@ class ShopPerformanceAnalyzer: if product_missing > 0: recommendations.append({ 'priority': priority, - 'severity': '⚠️ WICHTIG', + 'severity': 'WICHTIG', 'problem': f'{product_missing} Produkt-Bilder fehlen', - 'impact': 'Erhöhte I/O Last', - 'solution': 'JTL-Shop Admin → Bilder → "Bildcache regenerieren"' + 'impact': 'Erhoehte I/O Last', + 'solution': 'JTL-Shop Admin -> Bilder -> "Bildcache regenerieren"' }) priority += 1 @@ -442,11 +442,11 @@ class ShopPerformanceAnalyzer: if stat_calls > 500: recommendations.append({ 'priority': priority, - 'severity': '⚠️ WICHTIG', + 'severity': 'WICHTIG', 'problem': f'{stat_calls} Filesystem stat() Calls', 'impact': 'Filesystem-Thrashing, langsame Response-Times', - 'solution': 'PHP Realpath Cache erhöhen in PHP-Einstellungen', - 'technical': 'Plesk → Domain → PHP Settings:\nrealpath_cache_size = 4096K\nrealpath_cache_ttl = 600' + 'solution': 'PHP Realpath Cache erhoehen in PHP-Einstellungen', + 'technical': 'Plesk -> Domain -> PHP Settings:\nrealpath_cache_size = 4096K\nrealpath_cache_ttl = 600' }) priority += 1 @@ -454,20 +454,20 @@ class ShopPerformanceAnalyzer: if imagemagick_count > 3: recommendations.append({ 'priority': priority, - 'severity': '🔥 KRITISCH', + 'severity': 'KRITISCH', 'problem': f'ImageMagick wird {imagemagick_count}x aufgerufen', 'impact': 'CPU-intensive Bildverarbeitung bei jedem Request!', - 'solution': '1. Bild-Cache in JTL-Shop aktivieren\n2. Alle Bildgrößen vorher generieren\n3. Prüfen ob Bilder wirklich vorhanden sind' + 'solution': '1. Bild-Cache in JTL-Shop aktivieren\n2. Alle Bildgroessen vorher generieren\n3. Pruefen ob Bilder wirklich vorhanden sind' }) priority += 1 if len(self.results['mysql_queries']) > 50: recommendations.append({ 'priority': priority, - 'severity': '⚠️ WICHTIG', + 'severity': 'WICHTIG', 'problem': f'{len(self.results["mysql_queries"])} MySQL Queries', 'impact': 'N+1 Query Problem, Database Overhead', - 'solution': 'JTL-Shop: System → Cache → Object Cache aktivieren (Redis)' + 'solution': 'JTL-Shop: System -> Cache -> Object Cache aktivieren (Redis)' }) priority += 1 @@ -475,41 +475,41 @@ class ShopPerformanceAnalyzer: if eagain_count > 100: recommendations.append({ 'priority': priority, - 'severity': '⚠️ WICHTIG', + 'severity': 'WICHTIG', 'problem': f'{eagain_count}x EAGAIN', - 'impact': 'Redis/MySQL Verbindungen überlastet', - 'solution': 'Redis Connection Pool erhöhen oder PHP-FPM Worker erhöhen' + 'impact': 'Redis/MySQL Verbindungen ueberlastet', + 'solution': 'Redis Connection Pool erhoehen oder PHP-FPM Worker erhoehen' }) priority += 1 if recommendations: for rec in recommendations: - print(f"{rec['severity']} PRIORITÄT {rec['priority']}: {rec['problem']}") - print(f" 📊 Impact: {rec['impact']}") - print(f" ✅ Lösung: {rec['solution']}") + print(f"[{rec['severity']}] PRIORITAET {rec['priority']}: {rec['problem']}") + print(f" Impact: {rec['impact']}") + print(f" Loesung: {rec['solution']}") if 'files' in rec: - print(f" 📁 Dateien:") + print(f" Dateien:") for file in rec['files']: - print(f" • {file}") + print(f" * {file}") if 'technical' in rec: lines = rec['technical'].split('\n') - print(f" 🔧 Technisch:") + print(f" Technisch:") for line in lines: print(f" {line}") print() else: - print("✅ Keine kritischen Probleme gefunden!\n") + print("Keine kritischen Probleme gefunden!\n") print("="*80) - print("📋 ZUSAMMENFASSUNG") + print("ZUSAMMENFASSUNG") print("="*80) - print(f" • Total Syscalls: {total_syscalls}") - print(f" • Fehlende Dateien: {missing_count}") - print(f" • MySQL Queries: {len(self.results['mysql_queries'])}") - print(f" • Redis Operations: {sum(self.results['redis_operations'].values())}") - print(f" • Handlungsempfehlungen: {len(recommendations)}") + print(f" * Total Syscalls: {total_syscalls}") + print(f" * Fehlende Dateien: {missing_count}") + print(f" * MySQL Queries: {len(self.results['mysql_queries'])}") + print(f" * Redis Operations: {sum(self.results['redis_operations'].values())}") + print(f" * Handlungsempfehlungen: {len(recommendations)}") if missing_count > 0: - print(f"\n 📁 Export-Verzeichnis: {self.output_dir}") + print(f"\n Export-Verzeichnis: {self.output_dir}") print() def main(): @@ -533,27 +533,27 @@ def main(): duration = int(sys.argv[2]) if len(sys.argv) > 2 else 5 max_processes = int(sys.argv[3]) if len(sys.argv) > 3 else None - print(f"\n🚀 Starte Performance-Analyse für: {domain}") - print(f"⏱️ Analyse-Dauer: {duration} Sekunden pro Prozess") + print(f"\nStarte Performance-Analyse fuer: {domain}") + print(f"Analyse-Dauer: {duration} Sekunden pro Prozess") if max_processes: - print(f"🔢 Max Prozesse: {max_processes}") + print(f"Max Prozesse: {max_processes}") else: - print(f"🔢 Prozesse: ALLE gefundenen PHP-FPM Worker") + print(f"Prozesse: ALLE gefundenen PHP-FPM Worker") print() analyzer = ShopPerformanceAnalyzer(domain) pids = analyzer.get_php_fpm_pids() if not pids: - print("❌ Keine PHP-FPM Prozesse gefunden!") + print("Keine PHP-FPM Prozesse gefunden!") sys.exit(1) # Limit anwenden falls gesetzt if max_processes and len(pids) > max_processes: - print(f"✅ {len(pids)} PHP-FPM Prozesse gefunden (analysiere {max_processes})") + print(f"{len(pids)} PHP-FPM Prozesse gefunden (analysiere {max_processes})") pids = pids[:max_processes] else: - print(f"✅ {len(pids)} PHP-FPM Prozesse gefunden (analysiere alle)") + print(f"{len(pids)} PHP-FPM Prozesse gefunden (analysiere alle)") print(f" PIDs: {pids}\n") @@ -562,7 +562,7 @@ def main(): analyzed = 0 failed = 0 - print("🔄 Analyse läuft...") + print("Analyse laeuft...") print("-" * 80) for i, pid in enumerate(pids, 1): @@ -570,7 +570,7 @@ def main(): percent = int((i / total) * 100) bar_length = 40 filled = int((percent / 100) * bar_length) - bar = '█' * filled + '░' * (bar_length - filled) + bar = '#' * filled + '-' * (bar_length - filled) print(f"\r[{bar}] {percent:3d}% | PID {pid:6d} ({i}/{total})", end='', flush=True) @@ -581,17 +581,17 @@ def main(): else: failed += 1 - print(f"\r[{'█' * bar_length}] 100% | Fertig!{' ' * 30}") + print(f"\r[{'#' * bar_length}] 100% | Fertig!{' ' * 30}") print("-" * 80) - print(f"\n✅ Analyse abgeschlossen!") - print(f" • Erfolgreich analysiert: {analyzed}/{total}") + print(f"\nAnalyse abgeschlossen!") + print(f" * Erfolgreich analysiert: {analyzed}/{total}") if failed > 0: - print(f" • Idle/Keine Daten: {failed}") + print(f" * Idle/Keine Daten: {failed}") print() if analyzed == 0: - print("⚠️ Konnte kesine Daten sammeln!") - print(" Shop hat gerade wenig Traffic. Versuche später nochmal oder erhöhe duration.\n") + print("Konnte keine Daten sammeln!") + print(" Shop hat gerade wenig Traffic. Versuche spaeter nochmal oder erhoehe duration.\n") sys.exit(1) analyzer.generate_report()