Observability (Instrumentation + Verification)
Overview
Make observability consistent and actionable: every boundary emits traces, metrics, and structured logs that correlate via IDs and stable fields.
This is intentionally opinionated: you should be able to answer “what happened?” with log → trace → metrics within a minute.
Workflow
- Name the decision this telemetry will inform (for example: keep current architecture, tune a timeout, roll out a migration).
- Define the unit of work (one trace): HTTP request, gRPC call, job run, queue message, WebSocket action.
- Define the measurement ladder:
- 3 leading indicators (move within days)
- 3 lagging outcomes (move within weeks/months)
- owner + review cadence + trigger for action
- Instrument end-to-end:
- traces: spans around the unit of work + key downstream calls
- metrics: RED for the boundary + a few domain metrics
- logs: structured JSON that includes correlation IDs
- Declare the field contract (stable keys).
- Add guardrails (PII rules, label cardinality rules, sampling/log levels).
- Verify correlation in a failure case (error log includes
traceId; trace contains downstream spans; metrics show error rate).
- Define the operating ritual:
- where metrics/logs/traces are reviewed
- who decides and who executes follow-up
- what threshold triggers rollback/escalation
Clarifying Questions
- What decision does this telemetry inform (keep current architecture, tune a timeout, roll out a migration, etc.)?
- What boundaries need instrumentation (HTTP handlers, gRPC methods, DB clients, consumers, jobs, WebSockets)?
- Is there existing telemetry infrastructure (OpenTelemetry, Prometheus, Grafana, Datadog, etc.)?
- What correlation IDs already exist in the system (traceId, requestId, sessionId)?
- Are there PII/privacy constraints on what can be logged or labeled?
- Who will review these metrics, and how often (owner + cadence)?
Chooser (What To Instrument)
Start with the user-impact boundaries:
- HTTP handlers: one root span per request + RED metrics per route template.
- gRPC methods: one root span per RPC + RED metrics per service/method.
- DB/cache clients: child spans per query/command; include target system and operation.
- Async jobs / schedulers: one root span per run; metrics for runs/success/failure/duration.
- Event consumers: one root span per message (or per batch); include message type and dedupe/idempotency metadata.
- WebSockets: session context + per-action spans; metrics for connections, messages, disconnect reasons.
Field Contract (Opinionated Defaults)
Logs (structured JSON)
Include these keys where applicable:
service: stable service/app identifier
env: environment (local/dev/staging/prod)
traceId, spanId: correlation IDs (when tracing exists)
requestId: if you use a separate request ID (often equals traceId)
op: operation name (route template, RPC method, job name)
userId / actorId: only if policy allows; never as a metric label
durationMs: for timing logs (prefer metrics for aggregates)
err: structured error (type/code, message, stack for unknown failures)
Spans (traces)
- Name spans by operation (
HTTP GET /api/foo, grpc PlayerService/GetProfile, redis GET gateway:...).
- Set attributes for routing and outcome (status code, error code, retry count).
- Prefer stable, low-cardinality attributes; avoid raw request bodies.
Metrics (RED + domain)
- RED for each boundary (per route/RPC): request count, error count, duration histogram.
- Add a few domain metrics that align with product intent (tables created, orders completed, etc.).
- Avoid high-cardinality labels (no
userId, no unbounded IDs); use logs/traces for per-entity detail.
Guardrails (Prevent “Telemetry Debt”)
- Cardinality discipline: metric label values must be bounded sets; default to route templates, not raw URLs.
- PII discipline: never log secrets; be explicit about what IDs are safe to log.
- Log once: avoid logging the same error in every layer; log at the boundary with enough context.
- Sample intentionally: if you sample traces, keep error traces at higher priority.
- Always end spans: long-running work should have explicit shutdown and cancellation semantics.
- Decision linkage: no metric without a named decision and action threshold.
Minimal TypeScript Snippet (Trace IDs in Logs)
If you use OpenTelemetry, you can enrich logs with the active span context:
import { context, trace } from '@opentelemetry/api';
export function getTraceLogFields(): { traceId?: string; spanId?: string } {
const span = trace.getSpan(context.active());
if (!span) return {};
const { traceId, spanId } = span.spanContext();
return { traceId, spanId };
}
Testing / Verification
- Exercise a failing request and verify:
- the error log includes
traceId
- the trace contains downstream span(s)
- boundary RED metrics reflect the error
- Prefer consumer-visible tests for behavior; treat telemetry verification as a local/dev smoke check unless the project already has telemetry assertions.
References
Output Template
When applying this skill, return:
- The decision being informed and the measurement ladder (leading/lagging, owner, cadence, trigger).
- The instrumentation plan (which boundaries, what telemetry, what fields).
- The minimal code changes (where to start spans, where to log, what metrics to add).
- The verification steps (how to reproduce and correlate log → trace → metrics).
1---2name: observability-113description: Add or change observability instrumentation (structured logging, OpenTelemetry traces/spans, RED metrics, dashboards, alerts). Use when adding logs/metrics/traces to code, defining telemetry field contracts, or building monitoring runbooks. NOT for diagnosing existing issues with existing telemetry (use debug); NOT for security-specific logging concerns (use security).4---5
6# Observability (Instrumentation + Verification)
7
8## Overview
9
10Make observability consistent and actionable: every boundary emits traces, metrics, and structured logs that correlate via IDs and stable fields.
11
12This is intentionally opinionated: you should be able to answer “what happened?” with **log → trace → metrics** within a minute.
13
14## Workflow
15
161. Name the **decision** this telemetry will inform (for example: keep current architecture, tune a timeout, roll out a migration).
172. Define the **unit of work** (one trace): HTTP request, gRPC call, job run, queue message, WebSocket action.
183. Define the measurement ladder:
19 - 3 leading indicators (move within days)
20 - 3 lagging outcomes (move within weeks/months)
21 - owner + review cadence + trigger for action
224. Instrument end-to-end:
23 - traces: spans around the unit of work + key downstream calls
24 - metrics: RED for the boundary + a few domain metrics
25 - logs: structured JSON that includes correlation IDs
265. Declare the field contract (stable keys).
276. Add guardrails (PII rules, label cardinality rules, sampling/log levels).
287. Verify correlation in a failure case (error log includes `traceId`; trace contains downstream spans; metrics show error rate).
298. Define the operating ritual:
30 - where metrics/logs/traces are reviewed
31 - who decides and who executes follow-up
32 - what threshold triggers rollback/escalation
33
34## Clarifying Questions
35
36- What decision does this telemetry inform (keep current architecture, tune a timeout, roll out a migration, etc.)?
37- What boundaries need instrumentation (HTTP handlers, gRPC methods, DB clients, consumers, jobs, WebSockets)?
38- Is there existing telemetry infrastructure (OpenTelemetry, Prometheus, Grafana, Datadog, etc.)?
39- What correlation IDs already exist in the system (traceId, requestId, sessionId)?
40- Are there PII/privacy constraints on what can be logged or labeled?
41- Who will review these metrics, and how often (owner + cadence)?
42
43## Chooser (What To Instrument)
44
45Start with the user-impact boundaries:
46
47- **HTTP handlers**: one root span per request + RED metrics per route template.
48- **gRPC methods**: one root span per RPC + RED metrics per service/method.
49- **DB/cache clients**: child spans per query/command; include target system and operation.
50- **Async jobs / schedulers**: one root span per run; metrics for runs/success/failure/duration.
51- **Event consumers**: one root span per message (or per batch); include message type and dedupe/idempotency metadata.
52- **WebSockets**: session context + per-action spans; metrics for connections, messages, disconnect reasons.
53
54## Field Contract (Opinionated Defaults)
55
56### Logs (structured JSON)
57
58Include these keys where applicable:
59
60- `service`: stable service/app identifier
61- `env`: environment (local/dev/staging/prod)
62- `traceId`, `spanId`: correlation IDs (when tracing exists)
63- `requestId`: if you use a separate request ID (often equals `traceId`)
64- `op`: operation name (route template, RPC method, job name)
65- `userId` / `actorId`: only if policy allows; never as a metric label
66- `durationMs`: for timing logs (prefer metrics for aggregates)
67- `err`: structured error (`type`/`code`, message, stack for unknown failures)
68
69### Spans (traces)
70
71- Name spans by operation (`HTTP GET /api/foo`, `grpc PlayerService/GetProfile`, `redis GET gateway:...`).
72- Set attributes for routing and outcome (status code, error code, retry count).
73- Prefer stable, low-cardinality attributes; avoid raw request bodies.
74
75### Metrics (RED + domain)
76
77- **RED** for each boundary (per route/RPC): request count, error count, duration histogram.
78- Add a few **domain metrics** that align with product intent (tables created, orders completed, etc.).
79- Avoid high-cardinality labels (no `userId`, no unbounded IDs); use logs/traces for per-entity detail.
80
81## Guardrails (Prevent “Telemetry Debt”)
82
83- **Cardinality discipline**: metric label values must be bounded sets; default to route templates, not raw URLs.
84- **PII discipline**: never log secrets; be explicit about what IDs are safe to log.
85- **Log once**: avoid logging the same error in every layer; log at the boundary with enough context.
86- **Sample intentionally**: if you sample traces, keep error traces at higher priority.
87- **Always end spans**: long-running work should have explicit shutdown and cancellation semantics.
88- **Decision linkage**: no metric without a named decision and action threshold.
89
90## Minimal TypeScript Snippet (Trace IDs in Logs)
91
92If you use OpenTelemetry, you can enrich logs with the active span context:
93
94```ts
95import { context, trace } from '@opentelemetry/api';
96
97export function getTraceLogFields(): { traceId?: string; spanId?: string } {
98 const span = trace.getSpan(context.active());
99 if (!span) return {};
100 const { traceId, spanId } = span.spanContext();
101 return { traceId, spanId };
102}
103```
104
105## Testing / Verification
106
107- Exercise a failing request and verify:
108 - the error log includes `traceId`
109 - the trace contains downstream span(s)
110 - boundary RED metrics reflect the error
111- Prefer consumer-visible tests for behavior; treat telemetry verification as a local/dev smoke check unless the project already has telemetry assertions.
112
113## References
114
115- Deeper checklists: [`references/checklists.md`](references/checklists.md)
116- TypeScript instrumentation snippets: [`references/snippets/typescript.md`](references/snippets/typescript.md)
117- Related patterns: [`Application metrics`](../architecture/references/application-metrics.md), [`Log aggregation`](../architecture/references/log-aggregation.md), [`Distributed tracing`](../architecture/references/distributed-tracing.md), [`Health Check API`](../architecture/references/health-check-api.md), [`Audit logging`](../architecture/references/audit-logging.md), [`Exception tracking`](../architecture/references/exception-tracking.md), [`Log deployments and changes`](../architecture/references/log-deployments-and-changes.md)
118- Boundary tests: [`testing`](../testing/SKILL.md)
119- Typed errors + explicit lifetimes: [`typescript`](../typescript/SKILL.md)
120
121## Output Template
122
123When applying this skill, return:
124
125- The decision being informed and the measurement ladder (leading/lagging, owner, cadence, trigger).
126- The instrumentation plan (which boundaries, what telemetry, what fields).
127- The minimal code changes (where to start spans, where to log, what metrics to add).
128- The verification steps (how to reproduce and correlate log → trace → metrics).