Observability
Overview
Define how the system tells you it's broken — before it's broken. Output is .forge/observability.md: structured logging conventions, correlation ID flow, the metrics taxonomy (golden signals per service, USE for resources), trace sampling policy, SLO + alert thresholds (page-worthy vs ticket-worthy), dashboard layouts, log retention, and PII redaction rules. Pairs with error-handling-and-resilience (errors classified there get observed here) and incident-response-and-postmortems (alerts there route to runbooks).
When to Use
- A service is going to production and there's no monitoring beyond
console.log
- Logs exist but are unstructured (free-text) and unsearchable
- Alerts fire constantly (fatigue) or don't fire when they should (gaps)
- Distributed system has no correlation IDs and debugging requires log archaeology
- A new component has been added without dashboards or alerts
- The team can't answer "what's the p95 latency right now?" in under 30 seconds
When NOT to Use
- Local-only scripts, prototypes, or one-off jobs
- Trivial CRUD additions to a service that already has observability
- Pure documentation or refactoring tasks with no runtime surface
Common Rationalizations
| Thought |
Reality |
| "Log everything, we'll filter later" |
Noise drowns signal. INFO-everything logs are unsearchable in production at scale. |
| "We'll add monitoring later" |
You can't debug what you can't see. Add the dashboard before the first user hits the endpoint. |
| "Console.log is fine for now" |
Unstructured logs can't be queried, aggregated, or correlated across services. |
| "Alerts can wait" |
The first outage you miss without an alert costs more than every alert you'll set up this quarter. |
| "We don't need traces, we have logs" |
Logs tell you what happened; traces tell you why it took 4 seconds. They're different. |
| "Sample 100% of traces" |
Tracing cost grows linearly with throughput. Sample, with head-based + tail-based sampling for errors. |
Red Flags
- A service in production with no dashboard
- An alert without a linked runbook
- Logs that contain PII (emails, names, tokens) at INFO
- The same alert firing >10x/day without ack — fatigue
- An "ERROR" log line at INFO level (severity drift)
- A correlation ID that stops at a service boundary
- Traces sampled at 100% in a >10 RPS service
- A dashboard nobody has opened in 30 days
Core Process
Step 1: Define correlation ID flow
Every request entering the system gets a trace ID at the edge. Every downstream call (HTTP, queue, RPC) propagates it via the standard header (traceparent or x-request-id). Every log line carries it. Document the flow end-to-end in the architecture doc.
Step 2: List golden signals per service
For each service, define the four REDs:
- Rate — requests per second
- Errors — 4xx / 5xx rate
- Duration — p50, p95, p99
- Saturation — queue depth, CPU, memory headroom
For data stores, define USE:
- Utilization — % busy
- Saturation — wait queue
- Errors — counts
Step 3: Define SLOs and alert thresholds
For each user-facing endpoint:
- SLO (e.g., "99.9% of requests succeed within 500ms p95 over 30 days")
- Page-worthy threshold (burning the budget — alert the on-call)
- Ticket-worthy threshold (degraded — file a ticket, fix this week)
- Never-alert noise (background warnings, expected churn)
Every alert MUST link to a runbook (see incident-response-and-postmortems).
Step 4: Establish log levels and structure
| Level |
When |
| ERROR |
Failure requiring human attention |
| WARN |
Anomaly that retried or recovered |
| INFO |
State transitions: started, completed, deployed |
| DEBUG |
High-volume internal detail; off in production by default |
Structured JSON only. Fixed top-level fields: timestamp, level, service, trace_id, span_id, user_id (hashed if PII-sensitive), message, error.kind (matching error-handling-and-resilience taxonomy).
Step 5: Design dashboards
One dashboard per service, with a standard layout:
- Top row: SLO compliance + error rate + latency p95
- Middle: RED signals broken down by endpoint
- Bottom: dependency latencies and saturation
Plus one "user journey" dashboard per critical path from .forge/testing-strategy.md.
Step 6: Configure trace sampling
- Head-based 1-10% baseline
- Tail-based 100% for errors, anomalous latency
- Always-sample for traces tagged
priority=high (e.g., paying customer endpoints)
Step 7: Log retention + PII redaction policy
- INFO/DEBUG retention: 7-14 days
- ERROR retention: 90 days
- Audit log retention: per compliance (1+ years)
- PII redaction rules: list every field that must be hashed, redacted, or excluded entirely (cross-reference
security-and-compliance skill's PII inventory)
Step 8: Header
Prepend a forge:meta header to .forge/observability.md (generated_by: observability, generated_at: <ISO 8601 UTC with Z>, depends_on: [.forge/architecture.md] — paths only, never hashes, generated_from: {.forge/architecture.md: <upstream content_hash AT generation time>}, content_hash: <sha256 first 8 of THIS file's body>). See forge-dependency-graph.
Verification
1---2name: observability3description: Use when adding logging, structured logs, metrics, traces, or alerts to a service, when designing dashboards, when defining SLOs, when investigating "how will I know if this breaks", or when production logs are too noisy to debug from.4---56# Observability78## Overview910Define how the system tells you it's broken — *before* it's broken. Output is `.forge/observability.md`: structured logging conventions, correlation ID flow, the metrics taxonomy (golden signals per service, USE for resources), trace sampling policy, SLO + alert thresholds (page-worthy vs ticket-worthy), dashboard layouts, log retention, and PII redaction rules. Pairs with `error-handling-and-resilience` (errors classified there get observed here) and `incident-response-and-postmortems` (alerts there route to runbooks).1112## When to Use1314- A service is going to production and there's no monitoring beyond `console.log`15- Logs exist but are unstructured (free-text) and unsearchable16- Alerts fire constantly (fatigue) or don't fire when they should (gaps)17- Distributed system has no correlation IDs and debugging requires log archaeology18- A new component has been added without dashboards or alerts19- The team can't answer "what's the p95 latency right now?" in under 30 seconds2021## When NOT to Use2223- Local-only scripts, prototypes, or one-off jobs24- Trivial CRUD additions to a service that already has observability25- Pure documentation or refactoring tasks with no runtime surface2627## Common Rationalizations2829| Thought | Reality |30|---------|---------|31| "Log everything, we'll filter later" | Noise drowns signal. INFO-everything logs are unsearchable in production at scale. |32| "We'll add monitoring later" | You can't debug what you can't see. Add the dashboard before the first user hits the endpoint. |33| "Console.log is fine for now" | Unstructured logs can't be queried, aggregated, or correlated across services. |34| "Alerts can wait" | The first outage you miss without an alert costs more than every alert you'll set up this quarter. |35| "We don't need traces, we have logs" | Logs tell you what happened; traces tell you why it took 4 seconds. They're different. |36| "Sample 100% of traces" | Tracing cost grows linearly with throughput. Sample, with head-based + tail-based sampling for errors. |3738## Red Flags3940- A service in production with no dashboard41- An alert without a linked runbook42- Logs that contain PII (emails, names, tokens) at INFO43- The same alert firing >10x/day without ack — fatigue44- An "ERROR" log line at INFO level (severity drift)45- A correlation ID that stops at a service boundary46- Traces sampled at 100% in a >10 RPS service47- A dashboard nobody has opened in 30 days4849## Core Process5051### Step 1: Define correlation ID flow5253Every request entering the system gets a trace ID at the edge. Every downstream call (HTTP, queue, RPC) propagates it via the standard header (`traceparent` or `x-request-id`). Every log line carries it. Document the flow end-to-end in the architecture doc.5455### Step 2: List golden signals per service5657For each service, define the four REDs:58- **Rate** — requests per second59- **Errors** — 4xx / 5xx rate60- **Duration** — p50, p95, p9961- **Saturation** — queue depth, CPU, memory headroom6263For data stores, define USE:64- **Utilization** — % busy65- **Saturation** — wait queue66- **Errors** — counts6768### Step 3: Define SLOs and alert thresholds6970For each user-facing endpoint:71- SLO (e.g., "99.9% of requests succeed within 500ms p95 over 30 days")72- Page-worthy threshold (burning the budget — alert the on-call)73- Ticket-worthy threshold (degraded — file a ticket, fix this week)74- Never-alert noise (background warnings, expected churn)7576Every alert MUST link to a runbook (see `incident-response-and-postmortems`).7778### Step 4: Establish log levels and structure7980| Level | When |81|-------|------|82| ERROR | Failure requiring human attention |83| WARN | Anomaly that retried or recovered |84| INFO | State transitions: started, completed, deployed |85| DEBUG | High-volume internal detail; off in production by default |8687Structured JSON only. Fixed top-level fields: `timestamp`, `level`, `service`, `trace_id`, `span_id`, `user_id` (hashed if PII-sensitive), `message`, `error.kind` (matching `error-handling-and-resilience` taxonomy).8889### Step 5: Design dashboards9091One dashboard per service, with a standard layout:92- Top row: SLO compliance + error rate + latency p9593- Middle: RED signals broken down by endpoint94- Bottom: dependency latencies and saturation9596Plus one "user journey" dashboard per critical path from `.forge/testing-strategy.md`.9798### Step 6: Configure trace sampling99100- Head-based 1-10% baseline101- Tail-based 100% for errors, anomalous latency102- Always-sample for traces tagged `priority=high` (e.g., paying customer endpoints)103104### Step 7: Log retention + PII redaction policy105106- INFO/DEBUG retention: 7-14 days107- ERROR retention: 90 days108- Audit log retention: per compliance (1+ years)109- PII redaction rules: list every field that must be hashed, redacted, or excluded entirely (cross-reference `security-and-compliance` skill's PII inventory)110111### Step 8: Header112113Prepend a `forge:meta` header to `.forge/observability.md` (`generated_by: observability`, `generated_at: <ISO 8601 UTC with Z>`, `depends_on: [.forge/architecture.md]` — paths only, never hashes, `generated_from: {.forge/architecture.md: <upstream content_hash AT generation time>}`, `content_hash: <sha256 first 8 of THIS file's body>`). See [forge-dependency-graph](../../references/forge-dependency-graph.md).114115## Verification116117- [ ] `.forge/observability.md` written118- [ ] Every request has a trace ID propagated through every service119- [ ] Every service has a dashboard with RED/USE signals120- [ ] Every endpoint has an SLO and at least one alert121- [ ] Every alert links to a runbook122- [ ] No PII (email, name, token, raw IP) in logs above DEBUG123- [ ] Trace sampling configured (not 100% in high-volume services)124- [ ] Log levels used consistently (no ERROR-at-INFO)125- [ ] Log retention + redaction policy documented126- [ ] Correlation IDs verified across every service boundary