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-transportBuild 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
| Option | Type | Default | Description |
|---|---|---|---|
client | AxonPush | required | The SDK client to publish through. |
channelId | string | required | Channel UUID for log events. |
serviceName | string | OTel service.name. | |
serviceVersion | string | OTel service.version. | |
environment | string | OTel deployment.environment. | |
agentId | string | Agent correlation id. | |
traceId | string | new trace | Seed an existing trace. |
mode | "background" | "sync" | "bullmq" | "background" | Publishing strategy. |
queueSize | number | 1000 | Records buffered before overflow. |
overflowPolicy | "drop-oldest" | "drop-newest" | "block" | "drop-oldest" | What a full queue does. |
shutdownTimeoutMs | number | 2000 | Drain budget on close(). |
concurrency | number | 1 | Parallel in-flight publishes. |
bullmqOptions | BullMQPublisherOptions | Required 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 level | severityNumber | severityText |
|---|---|---|
silly, trace | 1 | TRACE |
debug, verbose | 5 | DEBUG |
http, info | 9 | INFO |
notice | 11 | INFO |
warn, warning | 13 | WARN |
error | 17 | ERROR |
crit, alert, emerg, fatal | 21 | FATAL |
Unrecognised levels fall back to INFO.
Event shape
| Field | Value |
|---|---|
identifier | "winston" |
eventType | "app.log" |
payload.body | the message field |
payload.severityNumber / severityText | from the table above |
payload.timeUnixNano | winston’s timestamp, when the format supplies one |
payload.attributes | every field other than level, message, timestamp |
payload.resource | service.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.