Run events in the database: Runs page and Agent run card on the Dokploy Portal

Snapshot of D:\Ecw Process 65c92c5 (2026-10-30).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ManikandanandClaude Opus 5.5 committed 2026-10-30 11:28:26 +05:30
1 parent d500f750da
commit 840272032a
9 files changed
+655 -46

No files matched your search

+68 -5
View File
@@ -8,6 +8,11 @@ writes `Logs\\events_<run>.jsonl` beside `run_<run>.log`: one JSON object a
line, each with its time to the millisecond, flushed at once so a killed Run
keeps what it wrote. The Portal's Runs page reads these files.
Every event is also copied to `dbo.CV_RunEvent` (2026-10-30, with
`RESULTS_DB=1`): the Portal on Dokploy cannot read this PC's Logs folder.
The file stays the record; the row is the same JSON line. The first
database error switches the copy off for the rest of the Run.
An event never stops a Run: anything that goes wrong writing one drops that
event and nothing else. Nothing secret goes in — no OTP code, no password,
no eCW username: the callers pass fixed fields, never a whole log line.
@@ -22,6 +27,9 @@ from pathlib import Path
_handle = None
_attempt = 0
_run_id = ""
_db_on = True
_conn = None
def close() -> None:
@@ -31,13 +39,62 @@ def close() -> None:
def _reset() -> None:
"""Close the file and forget the attempt count (tests; a new Run)."""
global _handle, _attempt
global _handle, _attempt, _run_id, _db_on, _conn
try:
if _handle:
_handle.close()
except Exception:
pass
_handle, _attempt = None, 0
try:
if _conn is not None:
_conn.close()
except Exception:
pass
_handle, _attempt, _run_id, _db_on, _conn = None, 0, "", True, None
def _sql_insert(run_id, at, kind, body) -> None:
"""The real copy: one INSERT on a connection kept for the Run."""
global _conn
from .. import settings as cfg
# A test that points LOGS at a temp folder never writes the real table.
if not cfg.RESULTS_DB or \
Path(cfg.LOGS).resolve() != (Path(cfg.ROOT) / "Logs").resolve():
return
if _conn is None:
import pyodbc
_conn = pyodbc.connect(cfg.RESULTS_DB_CONN, timeout=10,
autocommit=True)
_conn.cursor().execute(
"INSERT INTO dbo.CV_RunEvent (run_id, at, kind, body) "
"VALUES (?, ?, ?, ?)", [run_id, at, kind, body])
_db_writer = _sql_insert
def _real_run(log_path) -> bool:
"""A Run in the real Logs folder: a test's log in a temp folder never
reaches the database, whatever this PC's `.env` says."""
try:
from .. import settings as cfg
return Path(log_path).resolve().parent == Path(cfg.LOGS).resolve()
except Exception:
return False
def _copy(run_id: str, kind: str, line: str) -> None:
"""The line to the database too. Never raises."""
global _db_on
if not _db_on or not run_id:
return
try:
_db_writer(run_id, datetime.now(), kind, line)
except Exception as exc:
_db_on = False
why = str(exc).splitlines()[0] if str(exc) else type(exc).__name__
print(f"[EVENTS] Not copied to the database for the rest of this "
f"run: {why}")
def run_id_of(log_path) -> str:
@@ -77,8 +134,9 @@ def _line(kind: str, fields: dict) -> str:
def start(log_path, mode: str, claim_limit=None, test_sheet: str = "") -> None:
"""Open this Run's events file and write `run_start`."""
global _handle
global _handle, _run_id
_reset()
_run_id = run_id_of(log_path) if _real_run(log_path) else ""
try:
_handle = open(path_for(log_path), "a", encoding="utf-8",
buffering=1)
@@ -98,20 +156,25 @@ def emit(kind: str, **fields) -> None:
"""Write one event. Never raises."""
if _handle is None:
return
line = _line(kind, fields)
try:
_handle.write(_line(kind, fields) + "\n")
_handle.write(line + "\n")
_handle.flush()
except Exception:
pass
_copy(_run_id, kind, line)
def append_to(log_path, kind: str, **fields) -> None:
"""Add one event to another Run's file — the Portal, after a Stop."""
line = _line(kind, fields)
try:
with open(path_for(log_path), "a", encoding="utf-8") as fh:
fh.write(_line(kind, fields) + "\n")
fh.write(line + "\n")
except Exception:
pass
if _real_run(log_path):
_copy(run_id_of(log_path), kind, line)
def warning(code: str, message: str, **fields) -> None: