plugins/frontend-toolkit/skills/observability-setup/SKILL.md
Set up Sentry for Next.js (error.tsx integration, Server Action capture), source-map upload for readable traces, PII scrubbing, release tagging, sampling/quota control, useReportWebVitals for CWV, structured logging, console.log replacement. Use before production deploy or when unexplained errors recur. Not for building error fallback UI (use async-ux-states) or optimizing CWV (use rendering-performance) — this only captures and reports them.
npx skillsauth add jaykim88/claude-ai-engineering observability-setupInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Detect production errors and performance regressions proactively. Every silent failure costs a user; observability turns silence into signal.
Universal — error capture, CWV reporting, structured logging, console.log replacement, and alert thresholds apply to any frontend. Only the error-tracking SDK and framework-specific integration hooks differ.
Install and initialize an error-tracking SDK (validation loop)
throw new Error('observability test') in a request handler)a.b.c, source-map upload isn't wired (the wizard sets the auth token; CI must upload maps on build). Upload maps privately to the SDK but don't serve them publicly (see security-audit)cicd-pipeline)Wire error boundaries to the SDK
captureException) in addition to rendering fallback UIasync-ux-statesCapture server-side and mutation errors
catch that returns gracefully MUST report the error before returning — otherwise the failure is silent in productionAdd user context (anonymized) AND scrub auto-captured PII
beforeSend scrubbing + the platform's data-scrubbing, and mask text/inputs in Session Replay. "I didn't call setUser with an email" is not enough. (see Implementation)Capture Core Web Vitals
web-vitals library directly) at a top-level layoutStructured logging utility
console.log with a logger utilitylevel (debug / info / warn / error), timestamp, route, userId (anonymized)Strip console.log from production code
grep -rn 'console\.log' src/ → 0 in production code (debugger / test files OK)no-console: ['error', { allow: ['warn', 'error'] }]7b. Control sampling, quota, and noise
tracesSampleRate and replaysSessionSampleRate deliberately (sample a fraction in prod) or quota and cost blow upignoreErrors / denyUrls): browser-extension errors, ResizeObserver loop warnings, network aborts — left in, they bury real signal and burn quotaerror.tsx files wired to Sentry.captureExceptionwithServerActionInstrumentationbeforeSend + data scrubbing; Session Replay masked)useReportWebVitals reporting CWVignoreErrors/denyUrls)console.log count in src/ (production code) = 0sentry.server.config.ts, instrumentation.ts, instrumentation-client.ts)src/lib/logger.ts with JSON structured output + level supportuseReportWebVitals (or equivalent) in root layout, forwarding to analytics/Sentrydocs/observability-alerts.md — thresholds, channels, runbook linkschore(observability): wire <SDK> + verify test capturenpx @sentry/wizard@latest -i nextjs — creates sentry.server.config.ts, sentry.edge.config.ts, instrumentation-client.ts (replaces the older sentry.client.config.ts), instrumentation.ts; wraps next.config.ts in withSentryConfigerror.tsx and global-error.tsx, useEffect(() => Sentry.captureException(error), [error]); route-level error.tsx catches first, global-error.tsx only when the root layout itself throwsonRequestError = Sentry.captureRequestError in instrumentation.ts (route handlers / middleware, automatic)Sentry.withServerActionInstrumentation(name, options?, callback)Sentry.setUser({ id: anonymizedUserId }) — id only, never PII (email/name)SENTRY_AUTH_TOKEN; withSentryConfig uploads on build; keep productionBrowserSourceMaps: false so maps aren't public (see security-audit)beforeSend to drop sensitive fields; enable server-side data scrubbing; replayIntegration({ maskAllText: true, blockAllMedia: true })tracesSampleRate, replaysSessionSampleRate / replaysOnErrorSampleRate; ignoreErrors / denyUrls for noise; tracePropagationTargets to link frontend spans to your APIrelease in init + SENTRY_RELEASE (git SHA) on deploy for suspect-commit attributionuseReportWebVitals hook → forward to analytics@sentry/vue + @sentry/nuxt (Nuxt module auto-installs everything)@sentry/sveltekit — wraps handle hook for server-side capture@sentry/angular — ErrorHandler injectable wires to all uncaught errorsweb-vitals library reports CWV from any client; structured JSON logging is framework-agnostic; alert thresholds (error rate, p95 latency, CWV regression) configured in the observability platform, not the appasync-ux-states — error.tsx components must wire Sentry.captureExceptioncicd-pipeline — Sentry release tagging on deployrendering-performance — CWV reported here for the field-data pictureglobal-error.tsx is last-resort only — route-level error.tsx catches first. Any catch that returns gracefully (Server Actions, route handlers) MUST call captureException before returning, otherwise the failure is silent in production. Wire CWV reporting from day one — performance regressions are invisible without it. Three setup gaps that quietly defeat observability: minified traces (upload source maps — privately, not public), the SDK auto-capturing PII (scrub via beforeSend + mask Session Replay), and unbounded trace/replay sampling (cost blowup). Tag releases with the git commit so errors attribute to a version.development
Design webhooks correctly on both sides — sending (HMAC signing, retries with backoff, at-least-once) and receiving (verify signature on raw body, enqueue + 200 fast, dedupe on event id). Use when adding webhook delivery or consuming a provider's webhooks. Not for internal service-to-service events (use async-messaging) or general outbound-call retry policy (use resilience-patterns).
testing
Use transactions and isolation levels correctly — keep them short, no network calls inside, explicit isolation, retry on serialization conflicts, and choose optimistic vs pessimistic locking. Use when a write spans multiple tables, when concurrent updates corrupt data, or when designing money/inventory flows. Not for cross-service event delivery (use async-messaging Outbox) or schema-level constraints (use schema-design).
development
Backend testing pyramid — unit for pure logic, integration against a real DB (Testcontainers), and consumer-driven contract testing (Pact) for service boundaries. Use before a feature, after a bug fix, or when services break each other on deploy. Not for load testing (use performance-profiling) or security testing (use backend-security-audit).
data-ai
Design a relational schema — normalize to 3NF then denormalize with justification, choose the right Postgres index type per data shape, enforce constraints at the DB. Use when modeling a new domain, when queries are slow, or before a migration. Not for diagnosing slow queries (use query-optimization) or shipping the change without downtime (use migration-strategy).