fix: deduplicate rcon admin log events by canonical message

This commit is contained in:
devRaGonSa
2026-05-19 13:54:57 +02:00
parent 784b797f5f
commit a5231ab584

View File

@@ -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()