mirror of
https://github.com/JogmansonReal/Radicade.git
synced 2026-10-25 23:18:56 -07:00
101 lines
3.2 KiB
Python
101 lines
3.2 KiB
Python
#!/usr/bin/env python3
|
|
"""Slice the client's Player.log by wall-clock window and summarise it.
|
|
|
|
The Unity log is one flat file per launch whose lines start with
|
|
`[HH:MM:SS] [frame] [seconds] [Level] [Tag] ...`; anything that matters after a
|
|
test is usually a handful of error lines inside a one-minute window.
|
|
|
|
Usage:
|
|
python tools/player-log-window.py --from 14:21 --to 14:23 [--errors]
|
|
python tools/player-log-window.py --grep "studio|rrs" --from 14:22:30
|
|
python tools/player-log-window.py --log <path> --from ... --to ... --show
|
|
|
|
Options:
|
|
--errors dedupe and count Error/Warning/Exception lines instead of printing all
|
|
--show print every matching line (default when a --grep matches)
|
|
--tail N additionally print the last N raw lines of the file
|
|
"""
|
|
|
|
import argparse
|
|
import os
|
|
import re
|
|
import sys
|
|
|
|
DEFAULT_LOG = os.path.join(
|
|
os.environ.get("LOCALAPPDATA", ""), "..", "LocalLow",
|
|
"Against Gravity", "Rec Room", "Player.log")
|
|
|
|
LINE_RE = re.compile(r"^\[(\d\d:\d\d:\d\d(?:\.\d+)?)\]")
|
|
|
|
|
|
def pad(value, tail):
|
|
"""`14:22` means the whole minute, `14:22:30` the whole second."""
|
|
if not value:
|
|
return None
|
|
return value if value.count(":") == 2 else value + tail
|
|
|
|
|
|
def in_window(stamp, start, stop):
|
|
if start and stamp < start:
|
|
return False
|
|
if stop and stamp > stop:
|
|
return False
|
|
return True
|
|
|
|
|
|
def main(argv):
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument("--log", default=os.path.normpath(DEFAULT_LOG))
|
|
parser.add_argument("--from", dest="start")
|
|
parser.add_argument("--to", dest="stop")
|
|
parser.add_argument("--grep")
|
|
parser.add_argument("--errors", action="store_true")
|
|
parser.add_argument("--show", action="store_true")
|
|
parser.add_argument("--tail", type=int, default=0)
|
|
args = parser.parse_args(argv[1:])
|
|
|
|
with open(args.log, "rb") as handle:
|
|
blob = handle.read().decode("utf-8", "replace")
|
|
lines = blob.split("\n")
|
|
print(f"{args.log}: {len(lines)} lines, {len(blob)} bytes")
|
|
|
|
pattern = re.compile(args.grep, re.I) if args.grep else None
|
|
selected = []
|
|
for text in lines:
|
|
match = LINE_RE.match(text)
|
|
if not match:
|
|
continue
|
|
if not in_window(match.group(1), pad(args.start, ":00"), pad(args.stop, ":59")):
|
|
continue
|
|
if pattern and not pattern.search(text):
|
|
continue
|
|
if args.errors and not re.search(r"\[(Error|Warning)\]|Exception:|Error:", text):
|
|
continue
|
|
selected.append(text.rstrip())
|
|
|
|
if args.errors or not (args.show or pattern):
|
|
counts = {}
|
|
order = []
|
|
for text in selected:
|
|
key = re.sub(r"\d+", "#", text[:220])
|
|
if key not in counts:
|
|
counts[key] = 0
|
|
order.append(key)
|
|
counts[key] += 1
|
|
for key in order:
|
|
print(f" x{counts[key]:<4} {key}")
|
|
else:
|
|
for text in selected:
|
|
print(" " + text[:300])
|
|
print(f"--- {len(selected)} selected line(s)")
|
|
|
|
if args.tail:
|
|
print(f"--- last {args.tail} lines ---")
|
|
for text in [t for t in lines if t.strip()][-args.tail:]:
|
|
print(" " + text[:300])
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main(sys.argv))
|