straceanalyse.py aktualisiert

This commit is contained in:
2025-10-27 16:28:49 +01:00
parent 6d6c9a4251
commit 341bab0345

View File

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