Feature lifecycle events to Application Insights¶
Motivation¶
Operators asked where to look when a long-running operation (warmup, cluster
provisioning, BLAST database preparation, BLAST submit) silently fails. Phase
transitions were written only to the jobstate Azure Table and to ad-hoc
LOGGER lines, so there was no single, queryable "did this operation succeed or
fail, and why?" signal in Application Insights. We also wanted the behaviour to
be strictly conditional: when Application Insights is not configured, nothing
new should be emitted to Azure and there must be no added cost.
User-facing change¶
The worker / beat sidecars now emit a structured feature event at the
terminal transition (completed / failed / cancelled) of four operation
families:
customEvents.name |
Task | Statuses |
|---|---|---|
warmup |
api.tasks.storage.warmup_database |
completed, failed |
cluster_provision |
api.tasks.azure.provision |
completed, failed |
prepare_db |
api.tasks.storage.prepare_db_via_aks |
completed, failed (partial) |
blast |
api.tasks.blast.* (submit / cancel / poll) |
completed, failed, cancelled |
When APPLICATIONINSIGHTS_CONNECTION_STRING is set, each event lands in both the
traces and customEvents App Insights tables (via the
microsoft.custom_event.name attribute). When it is unset, the call degrades to
a local api.events log line with zero Azure ingestion — no production code path
forces telemetry on. Intermediate (non-terminal) phases continue to live in the
jobstate row + job history the dashboard renders live; only terminal
transitions are promoted to a named customEvent so the table is not flooded.
API / IaC diff summary¶
- New
api/services/feature_events.py—record_feature_event(event, *, status, **attributes)best-effort emitter on theapi.eventslogger. Never raises; dropsNoneattributes; sanitises string values; escapes reservedLogRecordkeys; adds themicrosoft.custom_event.namecustomEvent attribute. ExportsTERMINAL_STATUSES. - Hooked the four shared phase-update wrappers to emit on terminal status:
api/tasks/storage/warmup.py::_update_state,api/tasks/storage/prepare_db_via_aks.py::_update_state,api/tasks/azure/provision.py::_publish,api/tasks/blast/state.py::_update_state(after the successful state write, not the no-op shortcut, so unchanged-state calls do not double-emit). - Docs — new "Feature Lifecycle Events" section in
docs/user-guide/observability.mdwith the event catalogue and KQL queries. - No IaC change. No new dependency (uses the existing
azure-monitor-opentelemetrypipeline and stdliblogging).
Validation evidence¶
uv run pytest -q api/tests/test_feature_events.py→ 6 passed (no-raise, None-drop, sanitise, scalar pass-through, reserved-key escape, custom-event name).uv run pytest -q api/tests/test_warmup_jobs.py api/tests/test_blast_tasks.py api/tests/test_azure_provision_aks.py api/tests/test_prepare_db_aks_task.py→ 199 passed (hooks did not break existing task behaviour).uv run python scripts/docs/check_frontmatter.py→ OK (54 navigated pages).uv run ruff check api→ clean.