feat(cloud-api): add correlated lifecycle logging

This commit is contained in:
2026-07-12 18:37:51 +08:00
parent a5de7399f8
commit 8131c0124b
7 changed files with 256 additions and 1 deletions
@@ -1,6 +1,7 @@
from __future__ import annotations
import json
import logging
from dataclasses import asdict
from datetime import datetime
from typing import Any
@@ -16,9 +17,13 @@ from cloud.db_models import (
ScheduledTaskRow,
TaskAttemptRow,
)
from cloud.observability import current_correlation_id
from core.models import utc_now
logger = logging.getLogger(__name__)
class SQLAlchemyCloudRepository:
"""SQLAlchemy adapter preserving the existing CloudStore CRUD surface."""
@@ -288,6 +293,7 @@ class SQLAlchemyCloudRepository:
)
)
session.flush()
_log_task_lifecycle("assigned", task)
return _leased_assignment_from_row(task)
def claim_assignment(
@@ -331,6 +337,7 @@ class SQLAlchemyCloudRepository:
task.updated_at = _iso(now)
attempt.status = "dispatched"
session.flush()
_log_task_lifecycle("claimed", task)
return _leased_assignment_from_row(task)
def renew_lease(
@@ -381,6 +388,7 @@ class SQLAlchemyCloudRepository:
task.lease_expires_at = renewed_until
task.updated_at = _iso(now)
attempt_row.lease_expires_at = renewed_until
_log_task_lifecycle("renewed", task)
return "renewed"
def record_task_result(
@@ -449,6 +457,7 @@ class SQLAlchemyCloudRepository:
attempt_row.completed_at = completed_at_iso
attempt_row.failure_reason = failure_reason
attempt_row.result_json = result_json
_log_task_lifecycle("completed" if status == "done" else "failed", task)
return "recorded"
def reap_expired_leases(
@@ -501,6 +510,10 @@ class SQLAlchemyCloudRepository:
task.failure_reason = (
f"lease expired after {task.attempt_count} attempts"
)
_log_task_lifecycle(
"retried" if task.status == "queued" else "failed",
task,
)
reaped_task_ids.append(task.id)
return reaped_task_ids
@@ -641,6 +654,21 @@ def _parse_json_object(value: str | None) -> dict[str, Any] | None:
return parsed if isinstance(parsed, dict) else None
def _log_task_lifecycle(event: str, task: ScheduledTaskRow) -> None:
logger.info(
"cloud task lifecycle",
extra={
"event": event,
"correlation_id": current_correlation_id(),
"task_id": task.id,
"host_id": task.assigned_host_id,
"device_id": task.assigned_device_id,
"attempt": task.attempt_count,
"lease_id": task.lease_id,
},
)
def _plugin_from_row(row: PluginRow) -> tuple[Any, bool]:
from cloud.plugins import PluginManifest