Base stream implementations for winston@3 and up.
npx @tessl/cli install tessl/npm-winston-transport@4.9.0winston-transport provides the base TransportStream implementation for winston logging library version 3 and above. It serves as the foundational building block for creating custom winston transports with a standardized stream-based interface.
npm install winston-transportDefault export (main TransportStream class):
const Transport = require('winston-transport');Named exports (legacy transport):
const { LegacyTransportStream } = require('winston-transport');TypeScript import patterns:
import TransportStream = require('winston-transport');
import { LegacyTransportStream } from 'winston-transport';ESM imports (Node.js with "type": "module"):
import Transport from 'winston-transport';
import { LegacyTransportStream } from 'winston-transport';const Transport = require('winston-transport');
class CustomTransport extends Transport {
constructor(opts) {
super(opts);
// Custom initialization
}
log(info, callback) {
setImmediate(() => {
this.emit('logged', info);
});
// Perform the writing to the remote service
console.log(info.message);
callback();
}
}
module.exports = CustomTransport;winston-transport is designed around several key components:
/** Log information object structure */
interface LogInfo {
/** Log level (error, warn, info, debug, etc.) */
level: string;
/** Log message content */
message: string;
/** Exception flag for error handling */
exception?: boolean;
/** Timestamp (added by format) */
timestamp?: string;
/** Additional metadata */
[key: string]: any;
}
/** Logform format interface for log transformation */
interface LogformFormat {
/** Transform log info object */
transform: (info: LogInfo, options?: any) => LogInfo | false;
/** Format options */
options?: any;
}
/** Log levels mapping (set by winston logger) */
interface LogLevels {
[level: string]: number;
}
/** Node.js stream namespace reference */
declare namespace stream {
class Writable {
constructor(options?: any);
write(chunk: any, encoding?: string, callback?: Function): boolean;
end(chunk?: any, encoding?: string, callback?: Function): void;
on(event: string, listener: Function): this;
emit(event: string, ...args: any[]): boolean;
}
}Core transport functionality providing a stream-based interface that integrates with winston v3+ logging architecture. Handles log level filtering, formatting, and lifecycle management.
/**
* Base class for winston v3+ transports extending readable-stream's Writable
* @param {TransportStreamOptions} options - Configuration options
*/
class TransportStream extends stream.Writable {
/** Log formatter instance */
format?: LogformFormat;
/** Current log level */
level?: string;
/** Silent mode flag */
silent?: boolean;
/** Exception handling flag */
handleExceptions?: boolean;
/** Rejection handling flag */
handleRejections?: boolean;
/** Log levels map (set when piped from logger) */
levels?: LogLevels;
/** Parent logger reference (set when piped from logger) */
parent?: any;
constructor(options?: TransportStreamOptions);
/** Log single message - must be implemented by subclasses */
log?(info: LogInfo, next: () => void): any;
/** Log multiple messages in batch - optional optimization */
logv?(info: LogInfo[], next: () => void): any;
/** Cleanup on transport removal - optional */
close?(): void;
}
interface TransportStreamOptions {
/** Log formatter from logform */
format?: LogformFormat;
/** Log level filter (e.g., 'info', 'error') */
level?: string;
/** Suppress all log output */
silent?: boolean;
/** Handle exception logs */
handleExceptions?: boolean;
/** Handle rejection logs */
handleRejections?: boolean;
/** Stream buffer size */
highWaterMark?: number;
/** Custom log function for simple transport creation */
log?(info: LogInfo, next: () => void): any;
/** Custom batch log function */
logv?(info: LogInfo[], next: () => void): any;
/** Custom close function */
close?(): void;
}Advanced stream methods for customizing transport behavior. Generally not needed for basic transport implementations.
/**
* Internal write method called by stream when log data is written
* @param {LogInfo} info - Log information object
* @param {string} enc - Encoding (usually ignored in object mode)
* @param {Function} callback - Completion callback
*/
TransportStream.prototype._write = function(info, enc, callback) {
// Internal implementation - handles format transformation and level filtering
};
/**
* Internal batch write method for multiple log entries
* @param {Array} chunks - Array of write requests with chunk and callback
* @param {Function} callback - Completion callback
*/
TransportStream.prototype._writev = function(chunks, callback) {
// Internal implementation - handles batch processing
};
/**
* Filter predicate to determine if a write request should be accepted
* @param {Object} write - Write request object with chunk property
* @returns {boolean} - True if the write should be processed
*/
TransportStream.prototype._accept = function(write) {
// Internal implementation - checks level, silent mode, and exception handling
};Wrapper functionality for winston v2 transports to work with winston v3+ logger infrastructure. Provides backward compatibility while encouraging migration to modern transport interface.
/**
* Wrapper for winston v2 transports to work with winston v3+
* @param {LegacyTransportStreamOptions} options - Configuration options
*/
class LegacyTransportStream extends TransportStream {
/** The wrapped winston v2 transport instance */
transport: LegacyTransport;
constructor(options: LegacyTransportStreamOptions);
/** Cleanup legacy transport and error handlers */
close(): void;
}
interface LegacyTransportStreamOptions extends TransportStreamOptions {
/** winston v2 transport instance with log method */
transport: LegacyTransport;
}
/** Winston v2 transport interface */
interface LegacyTransport {
/** Transport name for identification */
name?: string;
/** Log method with v2 signature */
log: (level: string, message: string, meta: any, callback: () => void) => void;
/** Optional cleanup method */
close?: () => void;
/** Optional log level */
level?: string;
/** Optional exception handling */
handleExceptions?: boolean;
/** Event emitter methods for error handling */
on?: (event: string, listener: Function) => void;
removeListener?: (event: string, listener: Function) => void;
}const Transport = require('winston-transport');
class CustomTransport extends Transport {
constructor(opts) {
super(opts);
// Initialize custom transport properties
this.name = 'custom';
this.filename = opts.filename;
}
log(info, callback) {
setImmediate(() => {
this.emit('logged', info);
});
// Write log to your custom destination
// info object contains: { level, message, timestamp, ...metadata }
const logLine = `${info.timestamp} [${info.level}]: ${info.message}\n`;
// Perform actual writing (file, database, API, etc.)
this.writeToDestination(logLine);
callback();
}
close() {
// Cleanup resources
if (this.connection) {
this.connection.close();
}
}
}
module.exports = CustomTransport;const winston = require('winston');
const CustomTransport = require('./custom-transport');
const logger = winston.createLogger({
level: 'info',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.json()
),
transports: [
new CustomTransport({
filename: 'app.log',
level: 'error'
})
]
});
logger.error('This will be written by CustomTransport');
logger.info('This will be filtered out due to level');const { LegacyTransportStream } = require('winston-transport');
const OldTransport = require('winston-old-transport');
// Wrap a winston v2 transport for v3+ compatibility
const legacyTransport = new LegacyTransportStream({
transport: new OldTransport({
level: 'info',
filename: 'legacy.log'
})
});
// Use with winston v3+ logger
const logger = winston.createLogger({
transports: [legacyTransport]
});winston-transport provides comprehensive error handling through multiple mechanisms:
Transport errors bubble up through the standard Node.js stream error event system:
const transport = new CustomTransport();
transport.on('error', (err) => {
console.error('Transport error:', err);
});
// Errors in log() method will emit 'error' event
transport.log({ level: 'info', message: 'test' }, (err) => {
if (err) {
// Handle callback errors
console.error('Log callback error:', err);
}
});Control how the transport handles uncaught exceptions and unhandled promise rejections:
const transport = new CustomTransport({
handleExceptions: true, // Handle uncaught exceptions
handleRejections: true // Handle unhandled promise rejections
});
// These will be captured and logged by the transport:
// throw new Error('Uncaught exception');
// Promise.reject('Unhandled rejection');Format transformation errors are trapped and re-thrown after callback invocation to ensure stream continuation:
const transport = new CustomTransport({
format: winston.format.combine(
winston.format.timestamp(),
winston.format.printf(info => {
// If this throws an error, it will be caught and re-thrown
// after the callback is invoked
return `${info.timestamp} ${info.level}: ${info.message}`;
})
)
});Legacy transports have automatic error event forwarding with proper cleanup:
const legacyTransport = new LegacyTransportStream({
transport: new OldTransport()
});
// Errors from the wrapped transport are automatically forwarded
legacyTransport.on('error', (err, transport) => {
console.error('Legacy transport error:', err);
console.error('From transport:', transport.name);
});
// Cleanup is automatic when transport is removed
logger.remove(legacyTransport); // Calls close() and removes error listenersUse silent mode to suppress all log output while maintaining error handling:
const transport = new CustomTransport({
silent: true // No logs will be written, but errors are still handled
});