Assert Structured Log Fields
Capture structured logs in tests and assert on fields rather than substrings so logging behaviour is regression-tested.
Task
Write tests that assert required log fields such as request id and status without brittle substring matching.
When to use this
A refactor silently drops the request id from logs, breaking correlation in production with no test catching it.
Prerequisites
- Structured JSON logging
- A test framework that can capture stdout
- Agreed log field contracts
Steps
1. Capture log lines and parse them as JSON.
const logs = [];
const sink = { write: (s) => logs.push(JSON.parse(s)), end: () => {} };
await handler(req, res, sink);Expected output: Log lines are available as objects for field-level assertions.
2. Assert required fields, not substrings.
expect(logs[0]).toMatchObject({ level: 'info', requestId: expect.any(String), status: 200 });Expected output: Missing or renamed fields fail the test even if the message text is unchanged.
Troubleshooting
Problem
Logs not captured
Solution
Inject the logger or a sink into the handler instead of reading global stdout.
Problem
Timestamps break equality
Solution
Assert presence and format with matchers rather than exact values.
Limitations
- Does not validate log content semantics.
- Requires a structured logger.
- Capturing hooks add test setup.
What this recipe does
Write tests that assert required log fields such as request id and status without brittle substring matching.
A refactor silently drops the request id from logs, breaking correlation in production with no test catching it.
Prerequisites
- Structured JSON logging
- A test framework that can capture stdout
- Agreed log field contracts
Procedure
- Capture log lines and parse them as JSON.
const logs = [];
const sink = { write: (s) => logs.push(JSON.parse(s)), end: () => {} };
await handler(req, res, sink);
Expected result: Log lines are available as objects for field-level assertions.
- Assert required fields, not substrings.
expect(logs[0]).toMatchObject({ level: 'info', requestId: expect.any(String), status: 200 });
Expected result: Missing or renamed fields fail the test even if the message text is unchanged.
How to confirm it worked
Run the commands above and confirm every expected result in order. If a step does not produce the stated output, use the troubleshooting notes before continuing. A passing run shows removing a required log field fails the field assertion while message-text changes do not break tests.
Troubleshooting
Logs not captured — Inject the logger or a sink into the handler instead of reading global stdout.
Timestamps break equality — Assert presence and format with matchers rather than exact values.
Limitations
- Does not validate log content semantics.
- Requires a structured logger.
- Capturing hooks add test setup.