Skip to content

JSON logs and correlation IDs

2 min read · main part only; folded sections are optional

After this you can answer: How do you follow one request through the API, the queue and the worker?

flowchart TB
B["browser"] -->|"x-correlation-id (optional)"| A["api: request.id = UUID<br/>every line: correlationId"]
A -->|"message.correlationId"| Q["SNS → SQS"]
Q --> W["worker: every line:<br/>correlationId, sqsMessageId, tries"]
A --> L[("CloudWatch Logs")]
W --> L
  1. Pick the id. If the caller sends a valid UUID in x-correlation-id, the api keeps it; anything else gets a new random UUID. A strict format keeps fake text out of our logs.
  2. Write it everywhere. Fastify keeps it as the request id and writes it on every line of that request as correlationId. Every answer, errors too, sends it back in the same header.
  3. Carry it on. The booking service puts it in the “booking confirmed” message.
  4. The worker continues. Each message gets a child logger with the correlationId, the SQS message id and the try number.
  5. Search. One Logs Insights query over the api and worker log groups, filtered by that id.
Log groupWritten byKept
/minitickets/dev/apiapi tasks (and the migration task)7 days
/minitickets/dev/webweb tasks7 days
/minitickets/dev/worker, /minitickets/dev/ticketsthe two Lambdas7 days

ECS tasks send their output with the awslogs driver; each task writes its own log stream. The Lambdas name their group in loggingConfig, so they do not use the default /aws/lambda/… group, which keeps logs forever. The api uses pino through Fastify; the worker writes the same JSON shape (level 30 info, 50 error; msg) straight to stdout, because console.log on Lambda would add a text prefix.

Three saved queries live in the folder minitickets-dev: follow-one-request, errors and worker-durations. No line ever holds an email address.

From → toWhat happensWhat allows it
Saved Logs Insights queries → CloudWatch log groupsonly when computeEnabled = truethe saved queries run over /minitickets/dev/api and /worker
  • config logGroupNames on each QueryDefinition; whoever runs a query needs logs:StartQuerycreateLogQueries
See the log groups on the map →

CloudWatch → Logs Insights → Saved queries → minitickets-dev/follow-one-request, paste the id from the browser’s network tab.

SymptomCauseWhere to look
follow-one-request finds api lines but no worker linesThe message lacked the id, or the worker logged with console.*The worker’s raw lines
A new /aws/lambda/… group keeps growingA Lambda without loggingConfig uses its default groupCloudWatch → Log groups
A task fails to start with an awslogs errorThe log group in the task definition does not existECS → stopped task
Why a random UUID and not Fastify's default ids?

The default restarts at req-1 in every process, so two api tasks would both have req-1. A UUID is unique across tasks and services.

How is this different from distributed tracing?

Tracing (for example AWS X-Ray or OpenTelemetry) also records timing per hop and draws the call tree. A correlation id is the cheap first step: it links lines, not timings.

What is the trade-off of 7 days of retention?

Storage stays cheap, but an issue found after a week has no logs. Production would keep longer, or export to cheaper storage.

Advanced: Details: the queries

errors shows every line with level >= 50, newest first. worker-durations reads Lambda’s own REPORT lines and shows calls, p95 and max duration, and memory in 5-minute buckets. The dashboard’s log box uses the same query text (infra/src/observability/dashboard-body.ts → ERRORS_QUERY).

Answer these without scrolling up. If one feels hard, reread that section.

  1. Where does the correlation id come from?
  2. How does it reach the worker?
  3. Why does the worker not use console.log on Lambda?
  4. How long are log lines kept?
  5. Which saved query would you run first after an alarm?

Redraw it from memory: browser → api (correlationId on every line, header back) → message.correlationId → SNS/SQS → worker (correlationId, sqsMessageId, tries) → one Logs Insights query

Try this: on the map, select the saved Logs Insights queries and see which log groups they read

Read this book online: https://book.theversehub.in/