"""report_demo.py — demo pelaporan dari artikel "Pemanfaatan Script Python untuk Otomatisasi Administrasi Server" (subbab 4.2, skrip pelaporan). Membaca log terstruktur JSON Lines (satu objek JSON per baris, format keluaran common.py), lalu merangkum hasil eksekusi per tugas dan per hari, menampilkan kegagalan beserta pesan galatnya, dan menggambar grafik ringkasan. Aman dijalankan di sandbox (Lab Reproduksi / WebAssembly): tidak memakai jaringan, SSH, atau perintah sistem. Hanya membaca berkas log contoh *.jsonl. Dapat juga dijalankan biasa: python3 report_demo.py [berkas.jsonl] """ import json import sys from collections import Counter, defaultdict from pathlib import Path def find_log(): """Berkas log dari argumen, atau *.jsonl pertama di folder kerja / folder skrip.""" if len(sys.argv) > 1 and Path(sys.argv[1]).is_file(): return Path(sys.argv[1]) for folder in (Path.cwd(), Path(__file__).resolve().parent if "__file__" in globals() else Path.cwd()): found = sorted(folder.glob("*.jsonl")) if found: return found[0] sys.exit("Berkas log *.jsonl tidak ditemukan. Unggah ops-log.jsonl sebagai Dataset.") def load(path): """Baca JSON Lines; baris rusak dihitung, tidak menghentikan proses.""" records, broken = [], 0 for line in path.read_text(encoding="utf-8").splitlines(): line = line.strip() if not line: continue try: records.append(json.loads(line)) except json.JSONDecodeError: broken += 1 return records, broken def summarize(records): per_task = defaultdict(Counter) # tugas -> {ok, gagal} per_day = defaultdict(Counter) # tanggal -> {ok, gagal} failures = [] for r in records: status = "gagal" if r.get("level") == "ERROR" else "ok" task = r.get("task", "?") day = str(r.get("ts", ""))[:10] per_task[task][status] += 1 per_day[day][status] += 1 if status == "gagal": failures.append((r.get("ts", ""), task, r.get("error", r.get("msg", "")))) return per_task, per_day, failures def main(): path = find_log() records, broken = load(path) per_task, per_day, failures = summarize(records) total = len(records) fails = sum(c["gagal"] for c in per_task.values()) days = sorted(per_day) print(f"Berkas log : {path.name}") print(f"Periode : {days[0]} s.d. {days[-1]} ({len(days)} hari)") print(f"Total eksekusi : {total} (baris rusak dilewati: {broken})") print(f"Berhasil : {total - fails} ({(total - fails) / total:.1%})") print(f"Gagal : {fails}") print() print(f"{'Tugas':<10} {'Berhasil':>9} {'Gagal':>6} {'Tingkat sukses':>15}") for task in sorted(per_task): c = per_task[task] n = c["ok"] + c["gagal"] print(f"{task:<10} {c['ok']:>9} {c['gagal']:>6} {c['ok'] / n:>15.1%}") print() print("Kegagalan (dikirim sebagai notifikasi oleh sistem):") for ts, task, err in failures: print(f" {ts} {task:<8} {err}") # Peringatan ambang dari skrip pemantauan (alert yang tercatat di log). alerts = Counter(a for r in records if r.get("task") == "monitor" for a in r.get("alerts", [])) if alerts: print() print("Peringatan ambang terbanyak (monitor):") for text, n in alerts.most_common(5): print(f" {n:>3}x {text}") # Grafik: eksekusi berhasil vs gagal per hari (tampil di Lab bila matplotlib tersedia). try: import matplotlib.pyplot as plt except ImportError: return ok = [per_day[d]["ok"] for d in days] bad = [per_day[d]["gagal"] for d in days] labels = [d[5:] for d in days] fig, ax = plt.subplots(figsize=(7, 3.2)) ax.bar(labels, ok, label="Berhasil", color="#2f6fdb") ax.bar(labels, bad, bottom=ok, label="Gagal", color="#d9480f") ax.set_ylabel("Jumlah eksekusi") ax.set_title("Eksekusi tugas otomatis per hari") ax.set_ylim(0, max(o + b for o, b in zip(ok, bad)) * 1.2) ax.legend(frameon=False, loc="upper left", bbox_to_anchor=(1.0, 1.0)) for side in ("top", "right"): ax.spines[side].set_visible(False) fig.tight_layout() main()