- LogHub samples (real system logs): 3.0x over 1.15M tokens - LogChunks (797 Travis failures, human-labeled failure chunks): 1.9x over 66M tokens, 78.5% of labeled failure-explaining lines survive verbatim - LogDx-CI (35 real GitHub Actions failures, arXiv 2605.28876): 2.2x over 15M tokens, 95.2% critical diagnostic signals retained; with --budget 8000, 6,334 tok/case at 77.4% retention; haiku diagnosed 12/12 from compressed vs 12/12 raw on the 12-case subset - README: Real logs, real benchmarks section; updated synthetic receipts (templates lift ANSI CI to 15.9x, synthetic eval total to 6.0x, 8/8 re-verified); honest 2-4x expectation row for production logs Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
130 lines
5.2 KiB
Python
130 lines
5.2 KiB
Python
"""bench_real.py — measure lessismore on REAL public log corpora, not synthetic ones.
|
|
|
|
Three benchmarks, all free, all with a deterministic metric (no model calls):
|
|
|
|
loghub 10 LogHub sample logs (HDFS, BGL, OpenSSH, ...) — token ratios on
|
|
real system logs. Downloads ~2.8 MB from GitHub on first run.
|
|
https://github.com/logpai/loghub (CC-BY-4.0)
|
|
|
|
logchunks 797 Travis CI failure logs with HUMAN-labeled failure-explaining
|
|
chunks (MSR 2020). Metric: do the labeled chunk lines survive?
|
|
curl -L -o LogChunks.zip \\
|
|
"https://zenodo.org/api/records/3632351/files/LogChunks.zip/content"
|
|
unzip LogChunks.zip
|
|
python3 bench_real.py logchunks ./LogChunks
|
|
|
|
logdx LogDx-CI: 35 real GitHub Actions failures with per-case
|
|
required-signal ground truth (arXiv 2605.28876, CC-BY-4.0).
|
|
Metric: are the critical diagnostic signals still present?
|
|
pip install huggingface_hub
|
|
hf download eyuansu71/logdx-ci --repo-type dataset --local-dir ./logdx-ci
|
|
python3 bench_real.py logdx ./logdx-ci
|
|
|
|
Chunk/signal matching normalizes whitespace and ANSI on both sides (the labels
|
|
themselves contain terminal color remnants), nothing else.
|
|
"""
|
|
import json
|
|
import re
|
|
import sys
|
|
import urllib.request
|
|
import xml.etree.ElementTree as ET
|
|
from pathlib import Path
|
|
|
|
from lessismore import compress, count_tokens
|
|
|
|
_CSI = re.compile(r"\x1b?\[[0-9;]*[A-Za-z]") # ANSI with or without the ESC byte
|
|
|
|
|
|
def norm(s):
|
|
return re.sub(r"\s+", " ", _CSI.sub("", s)).strip()
|
|
|
|
|
|
LOGHUB = ["HDFS", "BGL", "OpenStack", "Zookeeper", "Apache",
|
|
"OpenSSH", "Spark", "Thunderbird", "Linux", "HealthApp"]
|
|
|
|
|
|
def bench_loghub(cache=Path("loghub_samples")):
|
|
cache.mkdir(exist_ok=True)
|
|
tot_b = tot_a = 0
|
|
print(f"{'dataset':14} {'raw tok':>8} {'small':>8} {'ratio':>6}")
|
|
for name in LOGHUB:
|
|
f = cache / f"{name}_2k.log"
|
|
if not f.exists():
|
|
url = f"https://raw.githubusercontent.com/logpai/loghub/master/{name}/{name}_2k.log"
|
|
f.write_bytes(urllib.request.urlopen(url, timeout=60).read())
|
|
text = f.read_text(encoding="utf-8", errors="replace")
|
|
b, a = count_tokens(text), count_tokens(compress(text, 2))
|
|
tot_b += b; tot_a += a
|
|
print(f"{name:14} {b:>8,} {a:>8,} {b/a:>5.1f}x")
|
|
print(f"{'TOTAL':14} {tot_b:>8,} {tot_a:>8,} {tot_b/tot_a:>5.1f}x")
|
|
|
|
|
|
def bench_logchunks(root):
|
|
root = Path(root)
|
|
tot_b = tot_a = n = lv = la = 0
|
|
full_v = 0
|
|
for xf in sorted(root.glob("build-failure-reason/*/*.xml")):
|
|
try:
|
|
tree = ET.parse(xf)
|
|
except ET.ParseError:
|
|
continue
|
|
for ex in tree.getroot().iter("Example"):
|
|
p = root / "logs" / ex.findtext("Log", "").strip()
|
|
chunk = ex.findtext("Chunk", "")
|
|
if not (p.exists() and chunk.strip()):
|
|
continue
|
|
out = compress(p.read_text(encoding="utf-8", errors="replace"), 2)
|
|
tot_b += count_tokens(p.read_text(encoding="utf-8", errors="replace"))
|
|
tot_a += count_tokens(out)
|
|
n += 1
|
|
nout = norm(out)
|
|
clines = [norm(l) for l in chunk.split("\n") if norm(l)]
|
|
v = sum(1 for l in clines if l in nout)
|
|
lv += v; la += len(clines); full_v += v == len(clines)
|
|
print(f"cases: {n} tokens {tot_b:,} -> {tot_a:,} ({tot_b/tot_a:.1f}x, {1-tot_a/tot_b:.0%} saved)")
|
|
print(f"labeled chunk lines retained verbatim: {lv}/{la} ({lv/la:.1%})")
|
|
print(f"chunks fully verbatim: {full_v}/{n} ({full_v/n:.1%})")
|
|
print("(the rest is mostly similar-line collapse: the message survives once "
|
|
"with a value summary — see README)")
|
|
|
|
|
|
def bench_logdx(root):
|
|
root = Path(root)
|
|
gts = sorted(root.glob("cases/**/ground_truth.json"))
|
|
tot_b = tot_a = ck = ca = 0
|
|
for gt_path in gts:
|
|
raw_p = gt_path.parent / "raw.log"
|
|
if not raw_p.exists():
|
|
continue
|
|
raw = raw_p.read_text(encoding="utf-8", errors="replace")
|
|
out = compress(raw, 2)
|
|
tot_b += count_tokens(raw); tot_a += count_tokens(out)
|
|
nout = norm(out)
|
|
for sig in json.loads(gt_path.read_text()).get("required_signals", []):
|
|
if sig.get("importance") != "critical":
|
|
continue
|
|
texts = [t for t in [sig.get("value"), sig.get("file")] + sig.get("aliases", []) if t]
|
|
ca += 1
|
|
ck += any(norm(t) in nout for t in texts)
|
|
print(f"cases: {len(gts)} tokens {tot_b:,} -> {tot_a:,} ({tot_b/tot_a:.1f}x, {1-tot_a/tot_b:.0%} saved)")
|
|
print(f"critical diagnostic signals retained: {ck}/{ca} ({ck/ca:.1%})")
|
|
|
|
|
|
def main():
|
|
if len(sys.argv) < 2 or sys.argv[1] not in ("loghub", "logchunks", "logdx"):
|
|
sys.exit(__doc__)
|
|
if sys.argv[1] == "loghub":
|
|
bench_loghub()
|
|
elif sys.argv[1] == "logchunks":
|
|
if len(sys.argv) < 3:
|
|
sys.exit("usage: bench_real.py logchunks <path-to-extracted-LogChunks>")
|
|
bench_logchunks(sys.argv[2])
|
|
else:
|
|
if len(sys.argv) < 3:
|
|
sys.exit("usage: bench_real.py logdx <path-to-logdx-ci-dataset>")
|
|
bench_logdx(sys.argv[2])
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|