Announce Every Handled Message

Table of Contents

+#+filetags: :pattern:nats:diagnostics: +#+created: 2026-10-14 +#+updated: 2026-10-14 +#+pattern_group: diagnostics

This page defines the nats pattern Announce Every Handled Message in the NATS service patterns language. It is the first half of what the pattern language calls Log Aggregation.

1. Also known as

  • Handler entry logging; the message receipt line.

2. Context

A message crosses a process boundary and the sender and receiver log into different files. A person investigating a request has one of the two logs, at best, and the failure they are looking for was logged by the side they do not have.

3. Problem

What must a handler write so that its own service's log is sufficient to establish what the service was asked to do and what it did?

4. Forces

  • Duplicate delivery means a handler may run twice for one logical request, and that must be visible.
  • A failure line without the subject cannot be tied to the request that caused it.
  • A tenant-scoped system needs to know whose work a line describes.
  • Logging at entry on every message is cheap; logging in detail on success is not.

5. Solution

One line at entry, at info, carrying the receipt fields; the outcome on the way out at debug:

void provision_step(ores::nats::message msg) {
    [[maybe_unused]] const auto cid = log_handler_entry(lg(), msg);
    // "iam.v1.ops.provision_tenant_step correlation_id=<id> session_id=<id>"

    // ... work ...
    BOOST_LOG_SEV(lg(), debug) << "Completed " << msg.subject;
}

The receipt line carries four things, each answering one question:

Field Answers
The subject Which operation was asked for
correlation_id Which top-level request this is part of
session_id Which login session it belongs to, when there is one
The tenant and actor Whose data the operation acts on

Three rules:

  • Exactly one line per handled message, at info. Not one per field, and not one per retry of the containing loop.
  • At entry, before the work. A line written after the work is a line that a crash never writes.
  • The subject is the prefixed one. The same string the sender logged, so one grep finds both sides.

6. Failure handling

Concern What the pattern does
Errors A failure line follows the receipt line, so the request it belongs to is already established.
Timeouts A receipt with no completion line is itself the signal that the handler did not finish.
Retries A redelivery writes a second receipt line with the same correlation id, so a duplicate is visible.
Idempotency Seeing two receipts for one request is how a duplicate is found rather than guessed.
Observability This pattern is the observability rule for messages.

7. Pitfalls

  • No entry line. A failure is an orphan in the log, with no request around it.
  • The tenant absent. The line names the operation but not whose data it touched, which is the first question in a multi-tenant system.
  • Logging the payload. A request can carry a credential. Log the receipt, not the body.
  • Info-level lines per field or per success. The log fills with noise and the receipt is lost in it.

8. Security

The receipt line names the tenant and the actor but never the token. A rejected request is logged by its subject and reason, and its payload is deliberately not logged, because a rejected request is exactly the one that may carry a credential.

9. Resulting context

Benefits
One service's log is enough to establish what it was asked and what it did, without the sender's log.
Liabilities
Every handler must be written to write the line; a handler that omits it is invisible.

10. In ORE Studio

`log_handler_entry()` extracts or generates the correlation id and logs the subject, the correlation id and the session id at info. Hand-written handlers call it; generated handlers are the place it is added next, so that every subject has a receipt line.

11. Related patterns

12. Sources

13. See also

  • NATS service patterns — the structure note that orders this pattern language, and where to read this page in it.

Emacs 29.3 (Org mode 9.6.15)