Files
saqib mirandClaude Sonnet 5 5a0fe9f847 docs(014-full-observability): plan, research, data model, contract, quickstart
Documents the exact hook point chosen for each of the 3 dead observability
primitives (access log, request-duration histogram, tracer provider) and
the 11 named business-health metrics, verified against the real current
code rather than assumed — including a pre-existing SLA-run status data
quality gap surfaced along the way (documented, not fixed here).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-08 10:51:22 +05:30

4.9 KiB

Data Model: Full Observability

No Prisma schema changes — every entity here is in-process or exported to an external observability sink, never persisted to Postgres.

Request Context Store

AsyncLocalStorage<RequestContextSnapshot>, populated once per request in request-context.plugin.ts's existing onRequest hook (the same hook that already builds request.reqContext), read by logger.ts's Pino mixin function on every subsequent log call made anywhere during that request's handling.

Field Type Notes
requestId string Same value already assigned to request.reqContext.requestId
correlationId string Same value already assigned to request.reqContext.correlationId

Access Log Line (shape, not a stored entity)

Emitted once per completed request via the existing logger singleton from the new onResponse hook.

Field Type Notes
method string HTTP method
route string Parameterized route pattern (request.routeOptions.url), not the raw URL
statusCode number Response status
durationMs number reply.elapsedTime
requestId / correlationId string Via the mixin, same as every other line for this request
event string Fixed value "http_request_completed" — lets log queries filter to access-log lines specifically

Log level: info for 2xx/3xx, warn for 4xx, error for 5xx — mirrors the existing error-handler's own level choices (app.ts) so severity is consistent across both sources of request-outcome logging.

Metrics (Prometheus, via prom-client)

All registered in infrastructure/observability/metrics.ts on the existing default registry (metricsRegistry, already exposed at GET /metrics), all prefixed supporthub_ to match the existing histogram and default-metrics prefix.

Metric name Type Labels Incremented/observed when
supporthub_http_request_duration_seconds Histogram (existing, now actually observed) method, route, status_code Every completed HTTP request
supporthub_ai_session_outcomes_total Counter outcome (resolved | escalated) An AI support session reaches a terminal resolved/escalated status
supporthub_ticket_resolutions_total Counter resolved_by (ai | human) A ticket reaches RESOLVED, labeled from the ticket's Resolution.resolvedBy
supporthub_ticket_resolution_duration_seconds Histogram A ticket reaches RESOLVED — observes resolvedAt - ticket.createdAt
supporthub_ticket_first_response_duration_seconds Histogram The first AGENT_MESSAGE is posted on a ticket — observes firstResponseAt - ticket.createdAt
supporthub_sla_run_outcomes_total Counter outcome (met | breached) An SLA run completes on time (met) or is flagged by the breach sweep (breached)
supporthub_escalations_total Counter reason An ESCALATION_TRIGGERED domain event fires (already published unconditionally today)
supporthub_problems_created_total Counter category_id (or uncategorized) A Problem row is created (at ticket-intake time)
supporthub_known_error_lookups_total Counter code A valid error code's known issues are looked up
supporthub_knowledge_retrieval_outcomes_total Counter matched (true | false) The AI's searchProductKnowledge tool call returns zero vs. one-or-more results
supporthub_tool_invocations_total Counter tool, outcome (success | failed) Every AI tool-call result, any tool

Deliberately not separate metrics (per spec.md's Assumptions): "recurring problems" and "most common errors" are read directly off supporthub_problems_created_total and supporthub_known_error_lookups_total respectively via a monitoring stack's own topk/rate query — no additional "top N" metric or logic is computed by this application.

Traces / Spans (exported, not persisted)

Span Parent Attributes Created in
ticket.create (root) ticket.id, product.externalProductId ticketing/tickets/service/tickets.service.ts
ai.escalation ticket.create (if within the same request) or its own root (async paths) ticket.id, session.id ai-support/sessions/service/session.service.ts, around the escalation branch
orchestration.assignment ai.escalation (via the TICKET_UPDATED/HUMAN_ESCALATION subscriber) ticket.id, strategy orchestration/orchestration + orchestration/assignments, wrapping the existing handleHumanEscalation call

Span context propagation across the domain-event bus relies on the OpenTelemetry Context API's own async-local propagation — since eventBus.publish(...) is awaited synchronously within the same call chain (confirmed in tickets.service.ts/escalation.service.ts), no manual context-carrying payload field is needed.