Cross-cutting concerns for code that touches the live AMQP layer: telemetry, connection pooling, and compression. Most of this lives in @amqp-contract/core.
@amqp-contract/core keeps a process-wide ConnectionManagerSingleton keyed on the URL set + connection options. TypedAmqpClient and TypedAmqpWorker share connections automatically when they're constructed with the same URLs — you don't need to (and shouldn't) wire connections manually.
Two invariants matter when touching this layer:
- Ref-counted lifecycle.
getConnection(...)increments the count;releaseConnection(...)decrements and closes the underlying connection when the count hits zero. EverygetConnectionmust be paired with areleaseConnection— otherwise the connection lives forever. - Failure-path cleanup. If
waitForConnect()(or any setup step before the worker/client returns to the user) errors, you must callclose()to release the ref-count before returning the error.TypedAmqpClient.createandTypedAmqpWorker.createalready do this; if you write a new factory, mirror the pattern.
AmqpClient.waitForConnect() accepts a connectTimeoutMs (default 30s). null disables it; Infinity/NaN/<= 0 are coerced to null because Node's setTimeout clamps and silently mis-fires on those. See DEFAULT_CONNECT_TIMEOUT_MS in packages/core/src/amqp-client.ts.
@opentelemetry/api is an optional peer dependency of @amqp-contract/core. If a consumer installs it, telemetry flows automatically; if not, the default provider is a no-op.
Public surface from @amqp-contract/core:
| Export | Use |
|---|---|
TelemetryProvider |
Type. Pass a custom one via CreateClientOptions.telemetry etc. |
defaultTelemetryProvider |
Auto-detects @opentelemetry/api; no-op if absent. |
startPublishSpan / startConsumeSpan |
Open a span around a publish or consume operation. |
endSpanSuccess / endSpanError |
Close it; endSpanError(span, error) records the exception too. |
recordPublishMetric / recordConsumeMetric |
Counter for success/failure + duration histogram. |
recordLateRpcReply |
Counter for replies that arrived after the caller gave up. |
MessagingSemanticConventions |
Pre-defined attribute keys (messaging.rabbitmq.message.delivery_tag, etc.) — use these rather than ad-hoc strings. |
When adding a new public method to TypedAmqpClient / TypedAmqpWorker, wrap it in spans and metrics consistent with the surrounding code. See client.ts:publish for the canonical pattern (start span → run → andTee records success metric, orTee records failure metric).
Both ends support gzip and deflate, controlled by the publisher:
- Client opts in per-publish via
options.compression: 'gzip' | 'deflate'. The body is compressed andcontentEncodingis set automatically — don't setcontentEncodingyourself when usingcompression. - Worker decompresses transparently before validation, based on
properties.contentEncoding. Unknown encodings produce aTechnicalError(parse failure → DLQ via singlenack, never enters retry). - RPC requests don't carry compression. The worker's parse/validate path does decompress an RPC request fine if it sees
contentEncoding, and replies are always uncompressed — so a compressed RPC request would round-trip mechanically. Even so,client.call()deliberately strips any inheritedcompressionfromdefaultPublishOptionsbefore publishing, so the on-wire convention stays consistent (no compression in either direction of an RPC). If you're wiring a new code path involving RPCs, mirror that — don't compress RPC requests.
The CompressionAlgorithm type is exported from @amqp-contract/contract.
Logger is a structured-logging interface (compatible with pino's shape). It's optional everywhere — TypedAmqpClient and TypedAmqpWorker accept a logger?: Logger option. When writing a new code path:
- Log at
infofor routine successes (one per published message is fine). - Log at
warnfor recoverable issues (consumer cancelled by server, retry attempts). - Log at
errorfor handler failures and DLQ routing — includeconsumerName,queueName, and the error. - Never log payload contents at info/warn — they may contain PII. Log identifiers (orderId, etc.) instead, and only at error if needed for triage.