← Recipes

阅读中文版本

Assert Structured Log Fields

Capture structured logs in tests and assert on fields rather than substrings so logging behaviour is regression-tested.

~25 mintestingobservabilitylogging

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

  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 result: Log lines are available as objects for field-level assertions.

  1. 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.