cloud6 min read

Observability Starter Kit: Logs, Metrics, Traces

A practical baseline for SaaS teams—structured logs, RED metrics, and traces without buying every vendor on day one.

You cannot fix what you cannot see. Early SaaS teams often oscillate between “no logs” and “log everything until the bill explodes.” A starter kit—structured logs, a handful of metrics, and traces on critical paths—gets you incident-ready without a platform engineering department.

This post defines a minimum viable observability stack for Node and Next.js products. It ties to background jobs (queue depth and DLQ alerts), error contracts (consistent error codes in logs), and env and secrets (redaction in log pipelines).

The three signals

OpenTelemetry describes three pillars:

  • Logs — discrete events with context (what happened, to whom, with which request id).
  • Metrics — aggregated numbers over time (rate, errors, duration, queue depth).
  • Traces — causality across services (request entered API, called DB, enqueued job).

You do not need perfect coverage on day one. Cover the user-visible path and money path first: sign-in, checkout, webhook processing, AI inference if billed.

Structured logging first

Plain console.log strings resist search and correlation. Emit JSON (or your platform’s structured format) with stable fields:

  • timestamp, level, message
  • requestId / traceId propagated from edge or generated at entry
  • orgId or userId where policy allows—never passwords or tokens
  • durationMs for completed operations

The OpenTelemetry logs data model aligns with eventual export to a log backend. Even before OTel, pick one schema and stick to it.

Log at boundaries: HTTP response sent, job started/finished, external API failure with status code—not every loop iteration.

Metrics that answer on-call questions

Start with RED for HTTP services:

  • Rate — requests per second
  • Errors — 5xx and business-critical 4xx
  • Duration — latency percentiles

Add USE for workers and databases when you own the resource: utilization, saturation, errors.

Export via Prometheus, CloudWatch, Datadog, or OTLP metrics—tool matters less than consistency. Alert on symptoms users feel: error rate spike, p95 latency doubling, oldest queue job age exceeding SLO.

Avoid alert fatigue: page humans on user impact, ticket on slow-burn trends.

Traces on hot paths

Instrument one trace per inbound HTTP request and propagate context to downstream calls and workers. OpenTelemetry tracing concepts explain spans, parent-child relationships, and context propagation.

In Node, auto-instrumentation covers HTTP and many libraries; manual spans around “charge card” and “call model” pay off in incidents. Next.js server components and route handlers should attach trace context when your hosting supports it—check your provider’s OTel guide.

Sampling is fine at scale: always sample errors; head-sample happy paths at 1–10% until cost forces tuning.

Correlation across logs, metrics, and traces

Use one traceId in logs when a span exists. Dashboards linking metric spikes to exemplar traces save hours. If your vendor lacks exemplars, log traceId on error lines at minimum.

Workers should inherit trace context from enqueue metadata when jobs originate from user requests—debugging “API said OK but email never sent” requires the chain.

Frontend and static sites

Browser errors belong in a separate channel (Sentry, etc.) with release version and route. Marketing sites on static export still need RUM or error tracking—CDN logs alone miss client exceptions.

Do not ship API keys to the browser for “debug logging.”

Dashboards as living runbooks

One dashboard per service: RED metrics, dependency health, queue depth. Link to the runbook doc for “what to do when queue age alerts.” Update dashboards when architecture changes—stale graphs erode trust during incidents.

Log retention and cost

Retain hot logs for days to weeks; archive to cheaper storage for compliance if required. Index high-cardinality fields carefully—userId in every metric label can explode cardinality. Prefer logs for drill-down, metrics for aggregates.

Security and compliance

Scrub PII per policy before export. EU customers may constrain log regions—know where your observability backend stores data. Access to prod logs should be role-gated and audited.

Ownership and on-call

Name an owner for dashboards and alert routing—even if that owner is “whoever is on call this week.” Ungowned alerts get silenced permanently after the third false positive. Document which alerts page versus ticket in the same repo as the runbooks.

Local dev parity

Developers should see the same log shape locally. LOG_LEVEL=debug in compose is enough for many teams; full local Jaeger is optional until microservices multiply.

Uptime checks versus deep health

A /health route that returns 200 without checking database connectivity is a vanity metric. Deep health endpoints can verify dependencies for load balancers; keep them unauthenticated but rate-limited and free of sensitive detail in response bodies.

What to add after the starter kit

  • Synthetic checks for critical flows
  • SLOs with error budgets
  • Profiling on CPU-heavy workers
  • Database query insights from managed Postgres

Postgres basics pairs with slow-query logs and EXPLAIN in staging.

SLIs and user-visible symptoms

Define Service Level Indicators tied to experience: “checkout completes,” “webhook processed within N minutes,” “search returns first page.” Your metrics should approximate those SLIs even before you formalize SLO contracts. Error budgets come later; symptom-based alerts come now.

Pair SLIs with runbook links in alert messages so the first responder knows which dashboard to open.

OpenTelemetry collector pattern

Many teams run an OpenTelemetry Collector as a sidecar or shared agent: apps export OTLP locally; the collector batches, filters, and forwards to vendors. That keeps vendor SDK churn out of application code and centralizes redaction rules once.

Start without a collector if a single SaaS backend accepts OTLP directly; add the collector when you fan out to multiple destinations or need tail sampling.

Anti-patterns

  • Logging full request bodies with credit card fields
  • Metrics without labels that identify service and environment
  • Traces that stop at the API gateway while the failure is in a worker
  • Buying three overlapping APM tools because each team preferred a different UI

Closing

Observability starter kit means structured logs everywhere, RED metrics on services, traces on checkout/auth/job paths, and correlation ids that tie them together. Instrument incrementally; prove value in one incident retrospective before expanding. OpenTelemetry gives you a portable path as vendors change—start simple, stay consistent, and redact secrets before they hit the sink.

After your first real incident, write a short “what we wished we had logged” note and promote one field or metric from that list into the default schema. Observability maturity is iterative guilt, not a big-bang platform project.

Practice reading your own logs as a new engineer would: search by requestId from a support ticket and see if the story makes sense end to end. If not, fix fields before adding another dashboard panel.

For Dockerized services, stdout/stderr from containers should already be JSON—configure your orchestrator to ship logs without double-wrapping lines into unreadable blobs.

Set a default log level per environment: info in production, debug in local compose only. Promoting debug to prod during an incident is fine; leaving it on for weeks floods storage and hides the signal you needed.

Include deployment.version or git SHA in log context after each release so you can filter errors to “only since the bad deploy”—without it, you argue whether a stack trace predates the rollback.

Let's talk