Stop 15 · DevOps & DeploymentTopics · 7/8

Monitoring & Logging

Part of the Full Stack Developer Roadmap

In short

A user says 'checkout failed'. A React crash, an API 500 or a slow database? With error tracking on both ends and one request id linking them, you'll know in minutes.

Tracking errors and performance across both your frontend (Sentry for React) and backend (structured logs, APM) — a full-stack issue can originate on either side, so you need visibility into both.

The blob character in a doctor's coat holds a clipboard beside a hospital bed and heart monitor.

Think of it like this

One patient file, every department

In a good hospital, your file travels with you from OPD to X-ray to the ward, with one patient number on every page. Any doctor can read the whole story. A request id does that for your app: the same id on the browser error, the API log and the slow query.

Watch it happen

Following one failed checkout

Browser
Your API
Postgres
Error tracker

x-request-id: r-81f2

1/6The frontend sends the order request with a request id in a header.

The big ideas

What you need to know

Structured logs

Log JSON with fields like reqId, userId and durationMs, not loose strings. Then you can search and filter.

One id end to end

Create a request id at the edge, pass it into every log line, and return it to the user in error messages.

Error tracking on both sides

Tools like Sentry capture React crashes and API exceptions with stack traces, browser details and the release version.

Alert on symptoms

Alert when the error rate or response time jumps, not on every single error, or people learn to ignore alerts.

See it in code

Walk through the code

js
1import pino from "pino";2import { randomUUID } from "node:crypto";3 4const logger = pino();5 6app.use((req, res, next) => {7  req.id = req.get("x-request-id") ?? randomUUID();8  res.set("x-request-id", req.id);9  req.log = logger.child({ reqId: req.id, method: req.method, path: req.path });10  next();11});12 13app.use((err, req, res, _next) => {14  req.log.error({ err }, "request failed");15  res.status(500).json({ error: "Something went wrong", reqId: req.id });16});

1/3Reuse the id the frontend sent, or create one, and echo it back in the response header.

pino writes one JSON line per log. The pino-http package does much of this for you; this version shows the moving parts.

Save yourself the pain

Common mistakes

Oops

Logging console.log('error!!') and nothing else.

Do this instead

Log structured JSON with the request id, user id and error object, so you can actually find it later.

Oops

Logging passwords, tokens or full card details.

Do this instead

Redact sensitive fields. pino's redact option can blank out paths like req.headers.authorization.

Oops

Only tracking backend errors.

Do this instead

Add error tracking to the frontend too. Many bugs never reach the API at all.

Your mission

Trace one request across the stack

~30 min

Quick check

A user reports 'Something went wrong (ref r-81f2)'. What's the fastest next step?

Go deeper

  1. 1Set up Sentry (or similar) on both your frontend and backend.
  2. 2Correlate a frontend error with the backend request that caused it, using a shared request ID.
  3. 3Set up basic uptime monitoring for your deployed backend API.

More topics at this stop

Stuck on this topic? Ask an Insider

Get 1:1 guidance from people who've walked this exact path — on the InsideEdge app.

Download
InsideEdge