The Craftsman standard for production observability — error tracking (Sentry), metrics & dashboards (Grafana), structured logging, tracing, SLOs, and alerting. Use this WHENEVER the work touches observability: adding or reviewing Sentry, wiring Grafana/Prometheus/OpenTelemetry, setting up structured logs, defining alerts or SLOs, instrumenting a service, or debugging "we have no visibility into X". Trigger even when the user only says "add monitoring", "why can't we see errors", "set up dashboards", or "make this observable" without naming a tool. Deploy, runtime, and CI production-readiness → craft-infra; whole-project readiness → craft-audit.
This skill encodes one engineer's standard for making a service observable, applied the same way
across every repo. The method and opinions live here; the project specifics (which logger,
which DSN, which dashboard) live in the target repo's code and config — always discover them, never
assume or hardcode.
Operating principle — discover before you build
Different repos already have different pieces. Before adding anything, spend two minutes mapping
what exists so you extend rather than duplicate:
grep for an existing logger, Sentry.init, /health, /metrics, or an env schema
(env.ts, config.ts) — wire into these, don't fork them.
Deployment target (serverless vs long-lived) decides the metrics approach — see
references/serverless-vs-server.md before reaching for prom-client.
State what you found, then propose the smallest set of additions that closes the gaps.
The four pillars (do them in this order)
Errors — Sentry first. It's the highest signal-per-minute. See references/sentry.md.
Structured logs — JSON logs with a request/trace id, never console.log in production code.
See references/logging.md.
Metrics & dashboards — Grafana over a metrics source appropriate to the runtime. See
references/grafana.md.
SLOs & alerts — alerts ride on user-facing symptoms, not raw resource graphs. See
references/slo-alerts.md.
A service is "observable enough to ship" when an on-call engineer can answer is it broken?,
since when?, and where? from these four without SSH-ing into anything.
Then the fifth thing, which the four pillars don't give you. The pillars make the service
observable; they say nothing about whether the transaction the product exists to perform actually
completed, or whether a human is on the other end of an alert. Before calling a service production-
ready, close that gap: the core business transaction visible end to end, stuck and half-finished
work detectable, a named person receiving alerts, and the whole loop proven once by breaking it on
purpose. See references/operational-readiness.md.
Standing opinions (the non-negotiables)
These are the judgments that make output consistent across repos — apply them unless the user
overrides:
Sentry is the default error tracker. Capture unhandled errors + rejections, scrub PII, tag
every event with release + environment, and set tracesSampleRate deliberately (not 1.0 in prod).
Logs are structured JSON. One line per event, a stable traceId/requestId on every line,
log levels used honestly (error means paged-if-frequent, not "FYI").
Alert on symptoms, page on pain. An alert that doesn't map to a user-visible problem or a
runbook is noise — delete it. Every paging alert links a runbook.
No silent degradation. If a dependency is down, the service says so (health endpoint + log +
metric), it doesn't fail quietly.
The business transaction is a first-class signal. Whatever the product exists to do — the run,
the checkout, the send — has a visible lifecycle and a detectable stuck state, not just the infra
underneath it.
Untested notification paths don't count. Fire one alert down the real path to a real human
before claiming the service is monitored.
Workflow
Discover the current state (above) and report the gaps.
Propose the closing set, ordered by the four pillars, smallest viable first.
Implement against the repo's existing patterns (its env schema, its logger, its CI).
Verify — trigger a test error to Sentry, confirm a dashboard renders, fire a test alert.
Observability you haven't seen work isn't done. For anything with a background job or a core
customer transaction, run the acceptance drill in references/operational-readiness.md: success,
system failure, expected business failure, stuck detection, and one alert delivered to a human.
Reference index
Read the one matching the current task — they hold the concrete setup, not this overview:
references/serverless-vs-server.md — why prom-client dies on serverless and what to do instead
references/browser-rum.md — browser RUM with @sentry/react, error boundaries, sourcemap upload for Next.js, session replay sampling, Core Web Vitals as SLIs
Audit checklist (for craft-audit)
When craft-audit plans an observability pass for a scope, it turns this checklist into the
plan.md todo list — the checklist is owned by this skill, not improvised by the orchestrator. Tailor
to what discovery found: skip a step that genuinely doesn't apply with a one-line reason; never
silently drop one. Emit findings using craft-audit workspace.md → "Canonical findings.md emission
format" (authority). Heading grammar (variables required — do not hardcode NNN/severity/status):
## <scopeLabel>-OBS-<NNN> · severity <🔴|🟡|🟢> · status <open|fixed|wontfix (reason)|regressed|fixed (merged into <ID>)>
Example only: ## <scopeLabel>-OBS-001 · severity 🔴 · status open
Required fields under each heading, in order, with these exact labels:
**What breaks (plain language):** · **Technical:** · **Fix:** · **Fingerprint:** ·
**Last-checked:** (optional **Confidence:** — verified | inferred | unverified-from-repo, absent
means verified — then optional **Fix-attempt:** only from craft-fix).
Assign sequential NNN per (scope, domain); judge severity with craft-audit prioritization.md.
Forbidden: ### headings; ## ID · 🔴 · open shorthand; severity/status as body bullets.
Map what observability already exists (Sentry, logger, prom-client, OTel, /health,
/metrics, env schema) before proposing anything — flag duplicated or forked instrumentation →
SKILL.md "Operating principle — discover before you build"
Confirm the runtime (serverless vs long-lived) drives the metrics/tracing approach — flag
prom-client or other pull-based scraping on serverless, and missing flush-before-exit →
references/serverless-vs-server.md
Audit Sentry: unhandled errors + rejections captured, PII scrubbed in beforeSend, events
tagged with release + environment, tracesSampleRate set deliberately (not 1.0 in prod), sourcemaps
uploaded — flag missing init, leaked PII, or untagged events → references/sentry.md
Check logs are structured JSON from one central logger with a stable traceId/requestId on
every line — flag bare console.log in production code, missing trace propagation, or unredacted
secrets/PII → references/logging.md
Audit error logging: errors logged with err.{type,message,code} + entity ids + durationMs,
not plain strings; error not logged twice (catch site + global handler); err.stack gated on env or
Sentry availability → references/logging.md § Structured error logging
Confirm one HTTP request completion event per request (status, duration, path, identity) — flag
missing request lifecycle logging or raw URL with query params → references/logging.md § Request lifecycle events
If OTel is present, verify traceId/spanId are injected from the active span (not a parallel
self-generated UUID), instrumentation loads before logger imports, and outbound calls propagate
traceparent — flag absent or misaligned trace context → references/otel-integration.md
Verify dashboards are provisioned as code over a runtime-appropriate datasource with RED/USE
panels that matter — flag hand-clicked dashboards, wrong datasource, or vanity panels with no signal
→ references/grafana.md
Audit alerts against user-facing symptoms — flag alerts that don't map to pain. SLO
applicability gate: multi-window burn-rate / full error-budget programs are for services with
real traffic and on-call expectations — not every pre-launch MVP. Sentry + basic error visibility
stay high priority regardless of stage; do not demand a full SLO program on an early-stage app
with no pager → references/slo-alerts.md
Confirm every paging alert links a runbook — flag any paging alert with no runbook as noise
to delete or document (N-A if the project has no pager yet) → references/slo-alerts.md
Check for silent degradation: a down dependency surfaces via health endpoint + log + metric —
flag dependencies that fail quietly with no observable signal → SKILL.md "Standing opinions"
Name the project's core business transaction (the run, checkout, send, sync) and confirm its
lifecycle is observable: start + terminal state emitted as events and counted as a metric, with
ids only and no payload/PII — flag an observability setup that watches only infrastructure while
the transaction the product exists to perform is invisible; flag a success rate inferred purely
from a mutable status column when the transaction retries →
references/operational-readiness.md § Name the core transaction first
Check expected business failures (bad customer input, declined card, rejected upload) are
counted but routed away from the error tracker, while system failures (timeouts, unhandled
exceptions) reach it tagged with the transaction id — flag either direction: customer mistakes
opening incidents, or system failures visible only as a status value →
references/operational-readiness.md § Instrument the lifecycle
Verify stuck and half-finished work is detectable: a query or gauge for non-terminal rows past
a per-state threshold, and — where a terminal state promises an artifact (report, invoice,
outbound message) — a terminal-without-artifact check with no trailing window that forgets
unresolved violations; queries committed as a file, not prose in a doc — flag a threshold with no
measured duration or timeout/retry budget behind it, a check polled less often than the threshold
it enforces, and either check missing →
references/operational-readiness.md § Detect stuck and half-finished work
Confirm the human loop exists in writing: who operates it, where alerts land (a destination
seen away from the laptop), console links, and a <5-minute "is it broken?" tree — flag alerts
configured with no named recipient. Scale to maturity: solo pre-launch, "operator: me, alerts to
my phone, no escalation" passes; named backup, ack convention, and escalation times are required
only once a second person could respond → references/operational-readiness.md § The human loop
Check the loop has been proven, not assumed: success path, a forced system failure, a
plausible business failure, stuck detection, and one alert delivered down the production
notification path — with the drill date recorded — flag an alerting setup that has never delivered
to a human. Acked-by-someone-else applies only where there is a team →
references/operational-readiness.md § Prove the loop: the acceptance drill
Where an availability/readiness number is claimed, verify one declared source of truth retains
history across the SLO window and the calculation is documented (manual weekly is acceptable at
early stage) — flag "green right now" presented as an SLO, tail-only log sources, a red synthetic
check that is the sole history source, and competing uncanonical sources →
references/operational-readiness.md § Measure readiness from retained history
When the service has real traffic / on-call: error-budget policy exists (who is notified at
50%/25% remaining? deploy freeze gate?). For early MVPs, skip or mark partial with a one-line
reason — do not invent a full SLO program → references/slo-alerts.md § Error-budget policy
Confirm the deploy pipeline's post-deploy gate (craft-infra) has an error-rate signal to
read when that gate exists → references/slo-alerts.md § Error-budget policy
1---2name: craft-observability3description: The Craftsman standard for production observability — error tracking (Sentry), metrics & dashboards (Grafana), structured logging, tracing, SLOs, and alerting. Use this WHENEVER the work touches observability: adding or reviewing Sentry, wiring Grafana/Prometheus/OpenTelemetry, setting up structured logs, defining alerts or SLOs, instrumenting a service, or debugging "we have no visibility into X". Trigger even when the user only says "add monitoring", "why can't we see errors", "set up dashboards", or "make this observable" without naming a tool. Deploy, runtime, and CI production-readiness → craft-infra; whole-project readiness → craft-audit.4---56# Observability Craft78This skill encodes one engineer's standard for making a service observable, applied the same way9across every repo. The **method and opinions** live here; the **project specifics** (which logger,10which DSN, which dashboard) live in the target repo's code and config — always discover them, never11assume or hardcode.1213## Operating principle — discover before you build1415Different repos already have different pieces. Before adding anything, spend two minutes mapping16what exists so you extend rather than duplicate:1718- `package.json` / lockfile → is `@sentry/*`, `pino`/`winston`, `prom-client`, `@opentelemetry/*`19 already present?20- `grep` for an existing logger, `Sentry.init`, `/health`, `/metrics`, or an env schema21 (`env.ts`, `config.ts`) — wire into these, don't fork them.22- Deployment target (serverless vs long-lived) decides the metrics approach — see23 `references/serverless-vs-server.md` before reaching for `prom-client`.2425State what you found, then propose the smallest set of additions that closes the gaps.2627## The four pillars (do them in this order)28291. **Errors** — Sentry first. It's the highest signal-per-minute. See `references/sentry.md`.302. **Structured logs** — JSON logs with a request/trace id, never `console.log` in production code.31 See `references/logging.md`.323. **Metrics & dashboards** — Grafana over a metrics source appropriate to the runtime. See33 `references/grafana.md`.344. **SLOs & alerts** — alerts ride on user-facing symptoms, not raw resource graphs. See35 `references/slo-alerts.md`.3637A service is "observable enough to ship" when an on-call engineer can answer _is it broken?_,38_since when?_, and _where?_ from these four without SSH-ing into anything.3940**Then the fifth thing, which the four pillars don't give you.** The pillars make the *service*41observable; they say nothing about whether the transaction the product exists to perform actually42completed, or whether a human is on the other end of an alert. Before calling a service production-43ready, close that gap: the core business transaction visible end to end, stuck and half-finished44work detectable, a named person receiving alerts, and the whole loop proven once by breaking it on45purpose. See `references/operational-readiness.md`.4647## Standing opinions (the non-negotiables)4849These are the judgments that make output consistent across repos — apply them unless the user50overrides:5152- **Sentry is the default error tracker.** Capture unhandled errors + rejections, scrub PII, tag53 every event with release + environment, and set `tracesSampleRate` deliberately (not 1.0 in prod).54- **Logs are structured JSON.** One line per event, a stable `traceId`/`requestId` on every line,55 log levels used honestly (`error` means paged-if-frequent, not "FYI").56- **Alert on symptoms, page on pain.** An alert that doesn't map to a user-visible problem or a57 runbook is noise — delete it. Every paging alert links a runbook.58- **No silent degradation.** If a dependency is down, the service says so (health endpoint + log +59 metric), it doesn't fail quietly.60- **The business transaction is a first-class signal.** Whatever the product exists to do — the run,61 the checkout, the send — has a visible lifecycle and a detectable stuck state, not just the infra62 underneath it.63- **Untested notification paths don't count.** Fire one alert down the real path to a real human64 before claiming the service is monitored.6566## Workflow67681. **Discover** the current state (above) and report the gaps.692. **Propose** the closing set, ordered by the four pillars, smallest viable first.703. **Implement** against the repo's existing patterns (its env schema, its logger, its CI).714. **Verify** — trigger a test error to Sentry, confirm a dashboard renders, fire a test alert.72 Observability you haven't seen work isn't done. For anything with a background job or a core73 customer transaction, run the acceptance drill in `references/operational-readiness.md`: success,74 system failure, expected business failure, stuck detection, and one alert delivered to a human.7576## Reference index7778Read the one matching the current task — they hold the concrete setup, not this overview:7980- `references/sentry.md` — init patterns, PII scrubbing, release/sourcemaps, sampling81- `references/logging.md` — structured logging, trace propagation, error logging, request lifecycle, redaction, sampling82- `references/otel-integration.md` — OTel SDK wiring to Pino/Winston, TracerProvider setup, span instrumentation, W3C traceparent propagation, OTLP/Loki/ELK export, multi-service distributed correlation, async queue context propagation83- `references/grafana.md` — datasource choice, dashboard-as-code, the panels that matter84- `references/slo-alerts.md` — SLO definition, burn-rate alerts, runbook linking85- `references/operational-readiness.md` — business-transaction lifecycle instrumentation, stuck /86 terminal-without-artifact detection and committed ops queries, on-call + ack + escalation +87 "is it broken?" tree, the acceptance drill, history-backed readiness measurement,88 honest coverage claims89- `references/serverless-vs-server.md` — why `prom-client` dies on serverless and what to do instead90- `references/browser-rum.md` — browser RUM with `@sentry/react`, error boundaries, sourcemap upload for Next.js, session replay sampling, Core Web Vitals as SLIs9192## Audit checklist (for craft-audit)9394When `craft-audit` plans an observability pass for a scope, it turns this checklist into the95`plan.md` todo list — the checklist is owned by this skill, not improvised by the orchestrator. Tailor96to what discovery found: skip a step that genuinely doesn't apply with a one-line reason; never97silently drop one. Emit findings using craft-audit `workspace.md` → "Canonical findings.md emission98format" (authority). Heading grammar (variables required — do not hardcode NNN/severity/status):99100`## <scopeLabel>-OBS-<NNN> · severity <🔴|🟡|🟢> · status <open|fixed|wontfix (reason)|regressed|fixed (merged into <ID>)>`101102Example only: `## <scopeLabel>-OBS-001 · severity 🔴 · status open`103104Required fields under each heading, in order, with these exact labels:105`**What breaks (plain language):**` · `**Technical:**` · `**Fix:**` · `**Fingerprint:**` ·106`**Last-checked:**` (optional `**Confidence:**` — `verified | inferred | unverified-from-repo`, absent107means `verified` — then optional `**Fix-attempt:**` only from craft-fix).108Assign sequential NNN per (scope, domain); judge severity with craft-audit `prioritization.md`.109Forbidden: `###` headings; `## ID · 🔴 · open` shorthand; severity/status as body bullets.110111- [ ] Map what observability already exists (Sentry, logger, `prom-client`, OTel, `/health`,112 `/metrics`, env schema) before proposing anything — flag duplicated or forked instrumentation →113 SKILL.md "Operating principle — discover before you build"114- [ ] Confirm the runtime (serverless vs long-lived) drives the metrics/tracing approach — flag115 `prom-client` or other pull-based scraping on serverless, and missing flush-before-exit →116 `references/serverless-vs-server.md`117- [ ] Audit Sentry: unhandled errors + rejections captured, PII scrubbed in `beforeSend`, events118 tagged with release + environment, `tracesSampleRate` set deliberately (not 1.0 in prod), sourcemaps119 uploaded — flag missing init, leaked PII, or untagged events → `references/sentry.md`120- [ ] Check logs are structured JSON from one central logger with a stable `traceId`/`requestId` on121 every line — flag bare `console.log` in production code, missing trace propagation, or unredacted122 secrets/PII → `references/logging.md`123- [ ] Audit error logging: errors logged with `err.{type,message,code}` + entity ids + `durationMs`,124 not plain strings; error not logged twice (catch site + global handler); `err.stack` gated on env or125 Sentry availability → `references/logging.md § Structured error logging`126- [ ] Confirm one HTTP request completion event per request (status, duration, path, identity) — flag127 missing request lifecycle logging or raw URL with query params → `references/logging.md § Request lifecycle events`128- [ ] If OTel is present, verify `traceId`/`spanId` are injected from the active span (not a parallel129 self-generated UUID), instrumentation loads before logger imports, and outbound calls propagate130 `traceparent` — flag absent or misaligned trace context → `references/otel-integration.md`131- [ ] Verify dashboards are provisioned as code over a runtime-appropriate datasource with RED/USE132 panels that matter — flag hand-clicked dashboards, wrong datasource, or vanity panels with no signal133 → `references/grafana.md`134- [ ] Audit alerts against user-facing symptoms — flag alerts that don't map to pain. **SLO135 applicability gate:** multi-window burn-rate / full error-budget programs are for services with136 real traffic and on-call expectations — not every pre-launch MVP. Sentry + basic error visibility137 stay high priority regardless of stage; do not demand a full SLO program on an early-stage app138 with no pager → `references/slo-alerts.md`139- [ ] Confirm every *paging* alert links a runbook — flag any paging alert with no runbook as noise140 to delete or document (N-A if the project has no pager yet) → `references/slo-alerts.md`141- [ ] Check for silent degradation: a down dependency surfaces via health endpoint + log + metric —142 flag dependencies that fail quietly with no observable signal → SKILL.md "Standing opinions"143- [ ] Name the project's core business transaction (the run, checkout, send, sync) and confirm its144 lifecycle is observable: start + terminal state emitted as events *and* counted as a metric, with145 ids only and no payload/PII — flag an observability setup that watches only infrastructure while146 the transaction the product exists to perform is invisible; flag a success rate inferred purely147 from a mutable `status` column when the transaction retries →148 `references/operational-readiness.md § Name the core transaction first`149- [ ] Check expected business failures (bad customer input, declined card, rejected upload) are150 counted but routed away from the error tracker, while system failures (timeouts, unhandled151 exceptions) reach it tagged with the transaction id — flag either direction: customer mistakes152 opening incidents, or system failures visible only as a status value →153 `references/operational-readiness.md § Instrument the lifecycle`154- [ ] Verify stuck and half-finished work is detectable: a query or gauge for non-terminal rows past155 a per-state threshold, and — where a terminal state promises an artifact (report, invoice,156 outbound message) — a terminal-without-artifact check with no trailing window that forgets157 unresolved violations; queries committed as a file, not prose in a doc — flag a threshold with no158 measured duration or timeout/retry budget behind it, a check polled less often than the threshold159 it enforces, and either check missing →160 `references/operational-readiness.md § Detect stuck and half-finished work`161- [ ] Confirm the human loop exists in writing: who operates it, where alerts land (a destination162 seen away from the laptop), console links, and a <5-minute "is it broken?" tree — flag alerts163 configured with no named recipient. Scale to maturity: solo pre-launch, "operator: me, alerts to164 my phone, no escalation" passes; named backup, ack convention, and escalation times are required165 only once a second person could respond → `references/operational-readiness.md § The human loop`166- [ ] Check the loop has been proven, not assumed: success path, a forced *system* failure, a167 plausible *business* failure, stuck detection, and one alert delivered down the production168 notification path — with the drill date recorded — flag an alerting setup that has never delivered169 to a human. Acked-by-someone-else applies only where there is a team →170 `references/operational-readiness.md § Prove the loop: the acceptance drill`171- [ ] Where an availability/readiness number is claimed, verify one declared source of truth retains172 history across the SLO window and the calculation is documented (manual weekly is acceptable at173 early stage) — flag "green right now" presented as an SLO, tail-only log sources, a red synthetic174 check that is the sole history source, and competing uncanonical sources →175 `references/operational-readiness.md § Measure readiness from retained history`176- [ ] When the service has real traffic / on-call: error-budget policy exists (who is notified at177 50%/25% remaining? deploy freeze gate?). For early MVPs, skip or mark partial with a one-line178 reason — do not invent a full SLO program → `references/slo-alerts.md § Error-budget policy`179- [ ] Confirm the deploy pipeline's post-deploy gate (craft-infra) has an error-rate signal to180 read when that gate exists → `references/slo-alerts.md § Error-budget policy`181
Run npx skillmds@latest add gul-labs/craft-observability in your terminal (requires Node.js), paste this page's agent-chat prompt into Claude, Cursor, or any MCP-connected agent, or download the SKILL.md file and copy it into your agent's skills directory.
The Craftsman standard for production observability — error tracking (Sentry), metrics & dashboards (Grafana), structured logging, tracing, SLOs, and alerting. Use this WHENEVER the work touches observability: adding or reviewing Sentry, wiring Grafana/Prometheus/OpenTelemetry, setting up structured logs, defining alerts or SLOs, instrumenting a service, or debugging "we have no visibility into X". Trigger even when the user only says "add monitoring", "why can't we see errors", "set up dashboards", or "make this observable" without naming a tool. Deploy, runtime, and CI production-readiness → craft-infra; whole-project readiness → craft-audit. It is listed under DevOps & Infra on SkillMD.
This skill has not completed SkillMD's automated safety review yet. SkillMD never runs a skill's scripts for you; review the SKILL.md before installing.
This skill is tagged as working with Claude Code, Claude.ai, OpenAI Codex. SKILL.md is an open format, so most agents that read a skills directory can load it too.
Yes. Installing skills from SkillMD is free, and the skill stays under its author's original license.
gul-labs (@gul-labs) published this skill. Their other Agent Skills are listed on their SkillMD profile.