Back to path
AdvancedInvoiceDesk · Project 11 of 12 ~6h· 5 milestones

See inside it: logs, errors and timings

Continues from the last build: CI runs unit, integration and the money-path e2e on every PR.

A client emails you Monday morning: "my invoice never sent, this is the third time." You ask when.

pino structured loggingrequest id propagationSentry error trackingDrizzle query timinghealth checksgolden signalsBullMQ metricsthreshold alerting

What you'll build

You can answer "what happened to this specific invoice send yesterday around lunch" in under two minutes: grep the requestId, see the full request-to-job timeline as structured JSON, see the exact query that was slow, see the Sentry issue if one was thrown, and know within seconds whether Postgres, Redis or the worker was the actual cause.

See how we teach, before you sign up

You don't just get code dumped on you. Every starter file and every solution is explained line-by-line, in plain English. Here's one real file from this project:

lib/log.tsts
import pino from 'pino';

export const baseLogger = pino({
  level: process.env.LOG_LEVEL ?? 'info',
  transport:
    process.env.NODE_ENV === 'production'
      ? undefined
      : { target: 'pino-pretty', options: { colorize: true } },
});

export function requestLogger(requestId: string, orgId: string | null, route: string) {
  return baseLogger.child({ requestId, orgId, route });
}

Reading this file

  • level: process.env.LOG_LEVEL ?? 'info'Log level is env-configurable, set to debug locally to see per-query timing, leave at info in production.
  • target: 'pino-pretty', options: { colorize: true }Human-readable output only in dev, production always emits raw JSON lines.
  • return baseLogger.child({ requestId, orgId, route })This child logger factory is what every route handler and the worker call to get a logger pre-tagged with the request's identity.

Starter file for this rung.

That's 1 of 9 explained code blocks in this single project.

The build, milestone by milestone

  1. 1

    Structured logs with request IDs

    5 guided steps

    The client's "it failed around lunch" is useless without a way to isolate one request. A requestId that travels from HTTP request into the job payload turns a wall of scrolling text into one JSON line per event you can grep and pipe to jq.

  2. 2

    Track errors with Sentry

    5 guided steps

    A stack trace with minified variable names and no source map tells you nothing at 11pm. And if every 401 from an expired session shows up as a Sentry alert, you will start ignoring Sentry, which defeats the point.

  3. 3

    Time the queries and the routes

    5 guided steps

    "It feels slow" is not a number. A route that takes 640ms because of an N+1 query looks identical to a route that takes 640ms because Stripe's API is slow, until you have durationMs on the route AND on every query inside it.

  4. 4

    A /healthz that means something

    5 guided steps

    A deploy platform restarts or routes traffic based on health checks. A /healthz that always returns 200 tells the platform to keep sending traffic to an instance whose database connection is dead, which is worse than no health check at all.

  5. 5

    Alert on what matters

    5 guided steps

    Watching individual causes (a specific query, a specific 3rd party API) means writing an alert for every new thing that can break. Watching symptoms (error rate, latency, queue depth, failed jobs) catches every cause including ones you have not thought of yet, because they all eventually show up as one of these four numbers moving.

What's inside when you start

4 starter files, ready to clone
5 guided milestones
5 full reference solutions
9 code blocks explained line-by-line
5 "is it working?" checks
6 interview questions it prepares you for

You'll walk away with

Every route handler and worker job emits one structured JSON log line with requestId, orgId, route, status and durationMs
Sentry captures unexpected server and client errors with requestId tags and readable, source-mapped stack traces
Slow queries (over 200ms) and slow routes are visible as warn-level log lines naming the exact query or route
/healthz returns per-component status for Postgres, Redis and the worker, and a non-200 status when any is down
A repeatable alert job checks error rate, p95 latency, queue depth and failed job count every 5 minutes and emails on breach
README documents how to trace a single request end to end using its requestId

This is portfolio-grade. Build it free.

Sign up to unlock every milestone step-by-step, the code skeletons, full reference solutions, and checkable tasks, with your progress saved as you build.

Start building