feat: log line preprocessing (ts strip + router/child classify)
This commit is contained in:
26
log_parser.py
Normal file
26
log_parser.py
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import re
|
||||||
|
import json
|
||||||
|
from dataclasses import dataclass, field, asdict
|
||||||
|
from datetime import datetime
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
# ── Preprocessing ────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
_TS_RE = re.compile(r'^\[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\]\s*')
|
||||||
|
_CHILD_RE = re.compile(r'^\[(\d+)\]\s*')
|
||||||
|
|
||||||
|
|
||||||
|
def strip_ts(line: str) -> str:
|
||||||
|
"""Remove optional `ts`-injected timestamp prefix from a log line."""
|
||||||
|
return _TS_RE.sub('', line.strip())
|
||||||
|
|
||||||
|
|
||||||
|
def classify_line(line: str) -> tuple[str, Optional[int], str]:
|
||||||
|
"""
|
||||||
|
Return (kind, port, content) where kind is 'router' or 'child'.
|
||||||
|
`line` must already have the ts prefix stripped.
|
||||||
|
"""
|
||||||
|
m = _CHILD_RE.match(line)
|
||||||
|
if m:
|
||||||
|
return ('child', int(m.group(1)), line[m.end():])
|
||||||
|
return ('router', None, line)
|
||||||
47
tests/test_log_parser.py
Normal file
47
tests/test_log_parser.py
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
import pytest
|
||||||
|
from log_parser import strip_ts, classify_line
|
||||||
|
|
||||||
|
# ── strip_ts ────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
def test_strip_ts_with_prefix():
|
||||||
|
line = "[2026-06-22 14:29:49] 0.00.044.403 I srv init: running without SSL"
|
||||||
|
assert strip_ts(line) == "0.00.044.403 I srv init: running without SSL"
|
||||||
|
|
||||||
|
def test_strip_ts_without_prefix():
|
||||||
|
line = "0.00.044.403 I srv init: running without SSL"
|
||||||
|
assert strip_ts(line) == "0.00.044.403 I srv init: running without SSL"
|
||||||
|
|
||||||
|
def test_strip_ts_child_line():
|
||||||
|
line = "[2026-06-22 14:31:59] [41803] 0.00.041.673 I log_info: verbosity = 3"
|
||||||
|
assert strip_ts(line) == "[41803] 0.00.041.673 I log_info: verbosity = 3"
|
||||||
|
|
||||||
|
def test_strip_ts_cmd_line():
|
||||||
|
line = '[2026-06-22 14:31:59] [41803] cmd_child_to_router:state:{"state":"loading"}'
|
||||||
|
assert strip_ts(line) == '[41803] cmd_child_to_router:state:{"state":"loading"}'
|
||||||
|
|
||||||
|
# ── classify_line ────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
def test_classify_router_line():
|
||||||
|
kind, port, content = classify_line("0.00.044.403 I srv init: running without SSL")
|
||||||
|
assert kind == "router"
|
||||||
|
assert port is None
|
||||||
|
assert content == "0.00.044.403 I srv init: running without SSL"
|
||||||
|
|
||||||
|
def test_classify_child_line():
|
||||||
|
kind, port, content = classify_line("[41803] 0.00.041.673 I log_info: verbosity = 3")
|
||||||
|
assert kind == "child"
|
||||||
|
assert port == 41803
|
||||||
|
assert content == "0.00.041.673 I log_info: verbosity = 3"
|
||||||
|
|
||||||
|
def test_classify_child_cmd_line():
|
||||||
|
payload = '{"state":"loading","payload":null}'
|
||||||
|
kind, port, content = classify_line(f"[41803] cmd_child_to_router:state:{payload}")
|
||||||
|
assert kind == "child"
|
||||||
|
assert port == 41803
|
||||||
|
assert content == f"cmd_child_to_router:state:{payload}"
|
||||||
|
|
||||||
|
def test_classify_router_proxy_line():
|
||||||
|
line = "2.10.286.889 I srv proxy_reques: proxying request to model Qwen3 on port 41803"
|
||||||
|
kind, port, content = classify_line(line)
|
||||||
|
assert kind == "router"
|
||||||
|
assert port is None
|
||||||
Reference in New Issue
Block a user