fix: deduplicate rcon admin log events by canonical message
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import re
|
||||
import sqlite3
|
||||
from collections.abc import Mapping
|
||||
from pathlib import Path
|
||||
@@ -29,12 +30,15 @@ def initialize_rcon_admin_log_storage(*, db_path: Path | None = None) -> Path:
|
||||
relative_time TEXT,
|
||||
event_type TEXT NOT NULL,
|
||||
raw_message TEXT NOT NULL,
|
||||
canonical_message TEXT NOT NULL,
|
||||
parsed_payload_json TEXT NOT NULL,
|
||||
raw_entry_json TEXT NOT NULL,
|
||||
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
UNIQUE(target_key, server_time, raw_message)
|
||||
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_rcon_admin_log_events_dedupe
|
||||
ON rcon_admin_log_events(target_key, server_time, canonical_message);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_rcon_admin_log_events_target_time
|
||||
ON rcon_admin_log_events(target_key, server_time DESC);
|
||||
|
||||
@@ -42,6 +46,7 @@ def initialize_rcon_admin_log_storage(*, db_path: Path | None = None) -> Path:
|
||||
ON rcon_admin_log_events(event_type);
|
||||
"""
|
||||
)
|
||||
_ensure_canonical_message_column(connection)
|
||||
|
||||
return resolved_path
|
||||
|
||||
@@ -65,9 +70,11 @@ def persist_rcon_admin_log_entries(
|
||||
with connect_sqlite_writer(resolved_path) as connection:
|
||||
for entry in entries:
|
||||
parsed = parse_rcon_admin_log_entry(entry)
|
||||
raw_message = str(parsed.get("raw_message") or "")
|
||||
canonical_message = _canonicalize_admin_log_message(raw_message)
|
||||
cursor = connection.execute(
|
||||
"""
|
||||
INSERT OR IGNORE INTO rcon_admin_log_events (
|
||||
INSERT INTO rcon_admin_log_events (
|
||||
target_key,
|
||||
external_server_id,
|
||||
event_timestamp,
|
||||
@@ -75,9 +82,18 @@ def persist_rcon_admin_log_entries(
|
||||
relative_time,
|
||||
event_type,
|
||||
raw_message,
|
||||
canonical_message,
|
||||
parsed_payload_json,
|
||||
raw_entry_json
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
)
|
||||
SELECT ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM rcon_admin_log_events
|
||||
WHERE target_key = ?
|
||||
AND server_time IS ?
|
||||
AND canonical_message = ?
|
||||
)
|
||||
""",
|
||||
(
|
||||
target_key,
|
||||
@@ -86,9 +102,13 @@ def persist_rcon_admin_log_entries(
|
||||
parsed.get("server_time"),
|
||||
parsed.get("relative_time"),
|
||||
parsed.get("event_type") or "unknown",
|
||||
parsed.get("raw_message") or "",
|
||||
raw_message,
|
||||
canonical_message,
|
||||
json.dumps(parsed, ensure_ascii=False, separators=(",", ":")),
|
||||
json.dumps(entry, ensure_ascii=False, separators=(",", ":")),
|
||||
target_key,
|
||||
parsed.get("server_time"),
|
||||
canonical_message,
|
||||
),
|
||||
)
|
||||
if int(cursor.rowcount or 0):
|
||||
@@ -103,6 +123,39 @@ def persist_rcon_admin_log_entries(
|
||||
}
|
||||
|
||||
|
||||
_PREFIX_RE = re.compile(r"^\[.*?\(\d+\)\]\s+", re.DOTALL)
|
||||
|
||||
|
||||
def _canonicalize_admin_log_message(raw_message: str) -> str:
|
||||
"""Return a stable message body for deduplication across repeated AdminLog reads."""
|
||||
normalized = str(raw_message or "").strip()
|
||||
return _PREFIX_RE.sub("", normalized).strip()
|
||||
|
||||
|
||||
def _ensure_canonical_message_column(connection: sqlite3.Connection) -> None:
|
||||
columns = {
|
||||
row["name"]
|
||||
for row in connection.execute("PRAGMA table_info(rcon_admin_log_events)").fetchall()
|
||||
}
|
||||
if "canonical_message" not in columns:
|
||||
connection.execute(
|
||||
"ALTER TABLE rcon_admin_log_events ADD COLUMN canonical_message TEXT NOT NULL DEFAULT ''"
|
||||
)
|
||||
connection.execute(
|
||||
"""
|
||||
UPDATE rcon_admin_log_events
|
||||
SET canonical_message = raw_message
|
||||
WHERE canonical_message = ''
|
||||
"""
|
||||
)
|
||||
connection.execute(
|
||||
"""
|
||||
CREATE INDEX IF NOT EXISTS idx_rcon_admin_log_events_dedupe
|
||||
ON rcon_admin_log_events(target_key, server_time, canonical_message)
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
def list_rcon_admin_log_event_counts(*, db_path: Path | None = None) -> list[dict[str, object]]:
|
||||
"""Return event counts grouped by target and event type."""
|
||||
resolved_path = db_path or get_storage_path()
|
||||
|
||||
Reference in New Issue
Block a user