Skip to content
/practice·structured-logging

Structured Logging

beginnerloggingjsonobservabilityhttp
requires:ExpressJSON
/concept
Structured Logging
Making your systems observable and debuggable
Read full guide

Problem

Implement request logging middleware that emits structured JSON logs for every HTTP request. Each log entry must include the request method, path, status code, response time, and a correlation ID that traces a request through your system.

Background

Unstructured text logs (console.log("user logged in")) are useless at scale. You can't search them, aggregate them, or alert on them. Structured JSON logs let tools like Elasticsearch, Datadog, and CloudWatch parse, index, and query your logs automatically.

Every production system needs structured logging with correlation IDs — a unique identifier that follows a request through all services, making it possible to trace a single user action across distributed systems.

Requirements

  • Every request should produce a JSON log line on stdout (one line per request)
  • Each log must include: method, path, statusCode, durationMs, requestId
  • The requestId should be taken from the x-request-id header if present, or generated as a UUID
  • The requestId must be returned in the response x-request-id header
  • Log entries must include a timestamp field in ISO 8601 format
  • Error responses (4xx, 5xx) should include level: "error", success responses level: "info"
  • Implement POST /echo which returns the request body — this is the endpoint the harness will test

Your Server

  • Your server receives PORT (default 3000)
  • No database or external dependencies needed
  • Logs must go to stdout (console.log), one JSON object per line

API Contract

`GET /health`

  • Status: 200
  • Body: { "status": "ok" }

`POST /echo`

Returns the request body as-is.

Request:

  • Body: any JSON object

Response:

  • Status: 200
  • Body: same JSON as request body

`GET /error`

Returns a server error (for testing error-level logging).

  • Status: 500
  • Body: { "error": "Something went wrong" }

Log Format

Each log entry should be a single JSON line on stdout:

json
{
  class="text-pass">"timestamp": class="text-pass">"2024-01-15T10:30:00.000Z",
  class="text-pass">"level": class="text-pass">"info",
  class="text-pass">"method": class="text-pass">"POST",
  class="text-pass">"path": class="text-pass">"/echo",
  class="text-pass">"statusCode": 200,
  class="text-pass">"durationMs": 3,
  class="text-pass">"requestId": class="text-pass">"abc-123"
}

Useful APIs

These are the key APIs you'll need. How you combine them is the challenge.

code
class=class="text-pass">"text-fg-subtle">// Generate a UUID (built-in, no dependencies)
const id = crypto.randomUUID()

class=class="text-pass">"text-fg-subtle">// Get the current time as ISO 8601
new Date().toISOString()

class=class="text-pass">"text-fg-subtle">// Measure elapsed time in milliseconds
const start = Date.now()
class=class="text-pass">"text-fg-subtle">// ... later ...
const durationMs = Date.now() - start

class=class="text-pass">"text-fg-subtle">// Run code after the response is sent (to capture status code)
res.on(class="text-pass">'finish', () => { console.log(res.statusCode) })

class=class="text-pass">"text-fg-subtle">// Output a single JSON line to stdout
console.log(JSON.stringify({ key: class="text-pass">'value' }))

class=class="text-pass">"text-fg-subtle">// Express middleware signature
app.use((req, res, next) => { /* before */ next() /* after fires on res events */ })
Scenarios·4 visible + hidden

Your solution will be tested against these scenarios plus a hidden set.

  1. 01Server emits structured JSON log lines to stdout
  2. 02Log entries include method, path, statusCode, durationMs, requestId, and timestamp
  3. 03x-request-id from request header is used as requestId and returned in response header
  4. 04A UUID requestId is generated when x-request-id header is not provided

Hints

Click each hint to reveal it. Take your time — try before you peek.