Skip to content

Message Logging Middleware

Logs request and response envelopes to a separate service group for monitoring and debugging. Designed as a companion to the Strata Queue Monitor (SQM), but works with any service that accepts the logged payload format.

Installation

bash
npm install @strata-js/middleware-message-logging

Peer dependency: @strata-js/strata ^2.0.0

Usage

Create a MessageLoggingMiddleware instance with a StrataClient and configuration, then register it -- typically as global middleware so all requests are logged.

typescript
import { StrataService, StrataClient } from '@strata-js/strata';
import { MessageLoggingMiddleware } from '@strata-js/middleware-message-logging';

const service = new StrataService(serviceConfig);
const client = new StrataClient(clientConfig);

const logging = new MessageLoggingMiddleware(client, {
    serviceGroup: {
        toMonitor: 'UserService',
        toLogTo: 'StrataQueueMonitor',
    },
});

service.useMiddleware(logging);

await client.start();
await service.start();

The middleware uses the provided StrataClient to fire-and-forget (post) each logged envelope to the target service group. Logging is non-blocking -- it does not wait for the monitoring service to acknowledge receipt.

Configuration

MessageLoggingConfig

typescript
interface MessageLoggingConfig
{
    serviceGroup : {
        toMonitor : string;
        toLogTo : string;
    };
    endpoint ?: {
        context ?: string;
        operation ?: string;
    };
    exclusions ?: string[];
    logOnFailure ?: string[];
    shouldLogRequestFn ?: (request : Request) => boolean;
    transformRequestFn ?: (request : RequestEnvelope) => void;
    transformResponseFn ?: (response : ResponseEnvelope) => void;
}
OptionTypeDefaultDescription
serviceGroup.toMonitorstring--The service group being monitored (used as the queue field in the logged payload).
serviceGroup.toLogTostring--The service group to send logged messages to (e.g., 'StrataQueueMonitor').
endpoint.contextstring'monitor'The context on the logging service to post to.
endpoint.operationstring'logMessage'The operation on the logging service to post to.
exclusionsstring[][]Endpoints to skip entirely (no request or response logging).
logOnFailurestring[][]Endpoints that are only logged when the request fails.
shouldLogRequestFn(request) => boolean--Custom filter function. Return false to skip logging for a request.
transformRequestFn(envelope) => void--Mutate the request envelope before it is logged (operates on a deep clone).
transformResponseFn(envelope) => void--Mutate the response envelope before it is logged (operates on a deep clone).

Exclusions

Skip logging for specific endpoints by listing them in exclusions. Each entry is a string in one of two forms:

  • 'context.operation' -- exclude a specific operation
  • 'context.*' -- exclude an entire context
typescript
const logging = new MessageLoggingMiddleware(client, {
    serviceGroup: { toMonitor: 'UserService', toLogTo: 'StrataQueueMonitor' },
    exclusions: [
        'health.*',                  // Skip all health-check operations
        'internal.refreshCache',     // Skip a specific noisy operation
    ],
});

Excluded endpoints are completely skipped -- neither request nor response envelopes are logged, even on failure.

Log on Failure

The logOnFailure list uses the same format as exclusions, but instead of silencing logging it defers it: the request envelope is only logged when the operation fails. The response envelope is always logged on failure.

typescript
const logging = new MessageLoggingMiddleware(client, {
    serviceGroup: { toMonitor: 'UserService', toLogTo: 'StrataQueueMonitor' },
    logOnFailure: [
        'batch.*',                   // Only log batch operations when they fail
        'reports.generate',          // Only log report generation on failure
    ],
});

This is useful for high-volume endpoints where you only care about failures.

Custom Filtering

For filtering logic beyond simple string matching, provide a shouldLogRequestFn. It receives the full Request object and returns a boolean:

typescript
const logging = new MessageLoggingMiddleware(client, {
    serviceGroup: { toMonitor: 'UserService', toLogTo: 'StrataQueueMonitor' },
    shouldLogRequestFn: (request) =>
    {
        // Only log requests from external clients
        return !request.metadata?.internal;
    },
});

INFO

shouldLogRequestFn is checked in addition to exclusions and logOnFailure. If an endpoint is in exclusions, it is always skipped regardless of shouldLogRequestFn.

Transform Functions

The transformRequestFn and transformResponseFn options let you modify envelopes before they are logged. The middleware deep-clones the envelope first, so your mutations won't affect the actual request or response flowing through the service.

typescript
const logging = new MessageLoggingMiddleware(client, {
    serviceGroup: { toMonitor: 'UserService', toLogTo: 'StrataQueueMonitor' },
    transformRequestFn: (envelope) =>
    {
        // Redact sensitive fields before logging
        if(envelope.payload?.password)
        {
            envelope.payload.password = '[REDACTED]';
        }
    },
    transformResponseFn: (envelope) =>
    {
        // Strip large payloads from logged responses
        if(envelope.payload && JSON.stringify(envelope.payload).length > 10000)
        {
            envelope.payload = { _truncated: true };
        }
    },
});

Logged Payload Format

Each logged message is posted with the following payload structure:

typescript
interface LoggedPayload
{
    queue : string;
    envelope : RequestEnvelope | ResponseEnvelope;
}
  • queue -- For request envelopes, this is serviceGroup.toMonitor. For response envelopes, this is the request's responseQueue.
  • envelope -- The full serialized request or response envelope.

Middleware Lifecycle

HookBehavior
beforeRequestIf the request passes all filters, posts the request envelope to the logging service.
successIf the request passes all filters, posts the response envelope.
failureIf not excluded, posts the response envelope. Also posts the request envelope if the endpoint is in logOnFailure.

Examples

Global Logging with SQM

The most common setup -- log everything to the Strata Queue Monitor:

typescript
import { StrataService, StrataClient } from '@strata-js/strata';
import { MessageLoggingMiddleware } from '@strata-js/middleware-message-logging';

const service = new StrataService({
    service: { serviceGroup: 'OrderService' },
    backend: { type: 'redis-streams', redis: { host: 'localhost', port: 6379 } },
});

const client = new StrataClient({
    backend: { type: 'redis-streams', redis: { host: 'localhost', port: 6379 } },
});

const logging = new MessageLoggingMiddleware(client, {
    serviceGroup: {
        toMonitor: 'OrderService',
        toLogTo: 'StrataQueueMonitor',
    },
});

service.useMiddleware(logging);

await client.start();
await service.start();

Custom Logging Endpoint

Route logs to a custom monitoring service:

typescript
const logging = new MessageLoggingMiddleware(client, {
    serviceGroup: {
        toMonitor: 'OrderService',
        toLogTo: 'CustomMonitor',
    },
    endpoint: {
        context: 'audit',
        operation: 'recordMessage',
    },
});

Selective Logging with Exclusions

Log everything except health checks and an internal cache refresh operation:

typescript
const logging = new MessageLoggingMiddleware(client, {
    serviceGroup: {
        toMonitor: 'OrderService',
        toLogTo: 'StrataQueueMonitor',
    },
    exclusions: [
        'service.*',
        'internal.refreshCache',
    ],
    logOnFailure: [
        'batch.*',
    ],
});