axonpush
TypeScript SDKIntegrations

winston

A winston transport that ships log records to axonpush as OpenTelemetry-shaped app.log events, with winston's level vocabulary mapped to OTel severities.

createAxonPushWinstonTransport builds a winston transport that forwards every record to axonpush as an app.log event with an OpenTelemetry-shaped payload.

Tested against winston@^3 and winston-transport@^4, both optional peer dependencies.

Install

npm install @axonpush/sdk winston winston-transport

Build the transport

The factory is async: winston-transport is imported lazily so the SDK loads cleanly for users who never touch winston. Await it before constructing the logger.

import winston from "winston";
import { AxonPush } from "@axonpush/sdk";
import { createAxonPushWinstonTransport } from "@axonpush/sdk/integrations/winston";

const client = new AxonPush();

const axonpushTransport = (await createAxonPushWinstonTransport({
  client,
  channelId: process.env.AXONPUSH_CHANNEL_ID!,
  serviceName: "my-api",
  serviceVersion: process.env.npm_package_version,
  environment: "production",
})) as winston.transport;

const log = winston.createLogger({
  level: "info",
  transports: [new winston.transports.Console(), axonpushTransport],
});

log.info("user signed in", { userId: 42, method: "oauth" });
log.warn("rate limit approaching", { endpoint: "/api/search", remaining: 3 });
log.error("downstream timeout", { endpoint: "/api/search", elapsedMs: 5000 });

The factory returns Promise<unknown> rather than a winston type, the SDK does not take a type-level dependency on an optional peer, so a cast is expected at the call site.

If winston-transport is not installed the factory throws with an install hint rather than failing at import time.

Non-blocking by default

log() pushes the record onto a bounded in-memory queue, calls winston’s callback, and returns. A background loop drains the queue. Records live in memory until then, see Durability.

Options

OptionTypeDefaultDescription
clientAxonPushrequiredThe SDK client to publish through.
channelIdstringrequiredChannel UUID for log events.
serviceNamestringOTel service.name.
serviceVersionstringOTel service.version.
environmentstringOTel deployment.environment.
agentIdstringAgent correlation id.
traceIdstringnew traceSeed an existing trace.
mode"background" | "sync" | "bullmq""background"Publishing strategy.
queueSizenumber1000Records buffered before overflow.
overflowPolicy"drop-oldest" | "drop-newest" | "block""drop-oldest"What a full queue does.
shutdownTimeoutMsnumber2000Drain budget on close().
concurrencynumber1Parallel in-flight publishes.
bullmqOptionsBullMQPublisherOptionsRequired when mode: "bullmq".

Flushing and closing

The transport adds one method winston does not know about, so the name is prefixed to avoid a collision:

interface AxonPushTransport {
  flushAxonPush(timeoutMs?: number): Promise<void>;
}

await (axonpushTransport as unknown as AxonPushTransport).flushAxonPush(2000);

close() is winston’s own hook, log.close() calls it, and it drains the queue. The module-level beforeExit / SIGTERM / SIGINT hooks also close every live publisher at normal process exit.

In a Lambda handler, wrap with flushAfterInvocation; it is re-exported from this module and works on the transport the same way it works on a pino stream. See the pino serverless section.

Durability

The default queue is in-memory

A full queue drops records according to overflowPolicy (drop-oldest by default) and warns at most once every 10 seconds with a running count. Anything still queued is lost if the process is killed. Set mode: "bullmq" to hand records to Redis instead, see BullMQ.

Level mapping

winston levelseverityNumberseverityText
silly, trace1TRACE
debug, verbose5DEBUG
http, info9INFO
notice11INFO
warn, warning13WARN
error17ERROR
crit, alert, emerg, fatal21FATAL

Unrecognised levels fall back to INFO.

Event shape

FieldValue
identifier"winston"
eventType"app.log"
payload.bodythe message field
payload.severityNumber / severityTextfrom the table above
payload.timeUnixNanowinston’s timestamp, when the format supplies one
payload.attributesevery field other than level, message, timestamp
payload.resourceservice.name, service.version, deployment.environment
metadata.framework"winston"

Re-entrancy

Records produced while the publisher is mid-publish, the SDK’s own warnings, for instance, are detected and skipped, so a patched logger cannot feed itself.