feat(observability): backfill logging for upload, auth, and domain services
Why: - resolve_auth_context() runs on every authenticated request and logged nothing; four distinct rejection reasons (malformed/unknown/inactive/ expired key, inactive tenant) were all invisible. - The domain-allowlist rejection in upload_source_file() happens before any ingestion_jobs row exists, so it wasn't covered by the job-level ingestion.job.failed event either -- a rejected upload left zero trace. - Four of upload_source_file()'s five failure branches (parse_failed, chunk_limit_exceeded, embedding_failed, index_failed) called _mark_job_failed(), which wrote to Postgres but never logged; only storage_failed and timeout had an ad-hoc logger.warning duplicated at their own call sites. Changes: - auth/service.py: auth.succeeded / auth.failed (with a reason field per rejection type), matching ADR-0011's own event catalog. - domains/service.py: domain.rejected on the allowlist check; domain.created / domain.updated / domain.status_changed on the three mutations. - files/upload.py: centralized failure logging inside _mark_job_failed (every failure branch already calls it, so logging there once closes all five branches instead of duplicating a log call at each site) as ingestion.job.failed; added ingestion.job.started; renamed the ad-hoc files.upload.succeeded to ingestion.job.completed for catalog consistency. Impact: - None to request/response behavior -- log events only.
This commit is contained in:
@@ -15,6 +15,7 @@ a request body (ADR-0002).
|
||||
|
||||
import uuid
|
||||
|
||||
import structlog
|
||||
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker
|
||||
|
||||
from src.application.domains.errors import DomainAlreadyExistsError, UnknownDomainError
|
||||
@@ -22,6 +23,8 @@ from src.application.domains.models import DomainResult
|
||||
from src.infrastructure.postgres.models.tenant_domain import TenantDomain
|
||||
from src.infrastructure.postgres.repositories import tenant_domains as repo
|
||||
|
||||
logger = structlog.get_logger(__name__)
|
||||
|
||||
|
||||
async def _flush_and_refresh(session: AsyncSession, tenant_domain: TenantDomain) -> None:
|
||||
"""Materialize server-generated columns before the row leaves the session.
|
||||
@@ -55,14 +58,25 @@ async def ensure_domain_allowed(
|
||||
Takes a session rather than a sessionmaker: the upload path calls this
|
||||
inside its existing txn A, so the check costs no extra connection and
|
||||
cannot pass and then go stale before the row is written.
|
||||
|
||||
Logs the rejection here rather than at the call site: this runs before any
|
||||
`ingestion_jobs` row exists, so `upload_source_file`'s job-level
|
||||
`ingestion.job.failed` event (ADR-0011) never fires for it -- without a log
|
||||
here, a rejected upload would leave no operational trace at all.
|
||||
"""
|
||||
tenant_domain = await repo.get(session, tenant_id=tenant_id, domain=domain)
|
||||
if tenant_domain is None:
|
||||
logger.warning(
|
||||
"domain.rejected", tenant_id=str(tenant_id), domain=domain, reason="unregistered"
|
||||
)
|
||||
raise UnknownDomainError(
|
||||
f"domain '{domain}' is not registered for this tenant; "
|
||||
f"create it via POST /v1/domains before uploading to it"
|
||||
)
|
||||
if tenant_domain.status != "active":
|
||||
logger.warning(
|
||||
"domain.rejected", tenant_id=str(tenant_id), domain=domain, reason="disabled"
|
||||
)
|
||||
raise UnknownDomainError(f"domain '{domain}' is disabled for this tenant")
|
||||
|
||||
|
||||
@@ -98,7 +112,9 @@ async def create_domain(
|
||||
metadata=metadata,
|
||||
)
|
||||
await session.commit()
|
||||
return _to_result(created)
|
||||
|
||||
logger.info("domain.created", tenant_id=str(tenant_id), domain=domain)
|
||||
return _to_result(created)
|
||||
|
||||
|
||||
async def update_domain(
|
||||
@@ -116,7 +132,10 @@ async def update_domain(
|
||||
repo.update_display_name(found, display_name=display_name)
|
||||
await _flush_and_refresh(session, found)
|
||||
await session.commit()
|
||||
return _to_result(found)
|
||||
result = _to_result(found)
|
||||
|
||||
logger.info("domain.updated", tenant_id=str(tenant_id), domain=domain)
|
||||
return result
|
||||
|
||||
|
||||
async def set_domain_status(
|
||||
@@ -139,4 +158,7 @@ async def set_domain_status(
|
||||
repo.set_status(found, status=status)
|
||||
await _flush_and_refresh(session, found)
|
||||
await session.commit()
|
||||
return _to_result(found)
|
||||
result = _to_result(found)
|
||||
|
||||
logger.info("domain.status_changed", tenant_id=str(tenant_id), domain=domain, status=status)
|
||||
return result
|
||||
|
||||
Reference in New Issue
Block a user