Extensible TypeScript Logger for Node.js and Browser with customizable log levels, pretty or JSON output formatting, and universal compatibility.
—
External logging system for integrating with log services, file systems, databases, and other logging destinations. Transports receive all log objects after processing and can forward them to external systems.
Transport functions receive processed log objects and can handle them according to their specific requirements.
/**
* Transport function interface for handling log objects
* @template LogObj - Type for log object structure
*/
type TransportLogger<LogObj> = (transportLogger: LogObj & ILogObjMeta) => void;
/**
* Log object with metadata passed to transports
*/
interface LogObj & ILogObjMeta {
/** User-defined log data */
[key: string]: unknown;
/** Metadata property containing log information */
[metaProperty: string]: IMeta;
}
interface IMeta {
/** Timestamp when log was created */
date: Date;
/** Numeric log level (0-6) */
logLevelId: number;
/** String log level name */
logLevelName: string;
/** Stack frame information if available */
path?: IStackFrame;
/** Logger name */
name?: string;
/** Parent logger names for hierarchical loggers */
parentNames?: string[];
/** Runtime environment identifier */
runtime: string;
}Method to attach external transport functions to logger instances.
/**
* Attach external transport for log forwarding
* @param transportLogger - Function to handle log objects
*/
attachTransport(transportLogger: (transportLogger: LogObj & ILogObjMeta) => void): void;Usage Examples:
import { Logger } from "tslog";
const logger = new Logger({ name: "MyApp" });
// Simple console transport
logger.attachTransport((logObj) => {
console.log("External:", JSON.stringify(logObj));
});
// File transport example
import * as fs from "fs";
logger.attachTransport((logObj) => {
const logLine = JSON.stringify(logObj) + "\n";
fs.appendFileSync("application.log", logLine);
});
// Database transport example
logger.attachTransport(async (logObj) => {
await database.logs.insert({
timestamp: logObj._meta.date,
level: logObj._meta.logLevelName,
message: logObj[0], // First argument
metadata: logObj,
});
});Loggers can have multiple transports attached, and each log message will be sent to all attached transports.
/**
* Multiple transports are stored in settings
*/
interface ISettings<LogObj> {
attachedTransports: ((transportLogger: LogObj & ILogObjMeta) => void)[];
}Usage Examples:
import { Logger } from "tslog";
const logger = new Logger({ name: "MultiTransportApp" });
// Attach multiple transports
logger.attachTransport((logObj) => {
// Send to external logging service
externalLogService.send(logObj);
});
logger.attachTransport((logObj) => {
// Write to file
fs.appendFileSync("app.log", JSON.stringify(logObj) + "\n");
});
logger.attachTransport((logObj) => {
// Send critical errors to monitoring system
if (logObj._meta.logLevelId >= 5) { // ERROR and FATAL
monitoringSystem.alert(logObj);
}
});
// All transports will receive this log
logger.error("Database connection failed", { database: "users" });Transports can be configured during logger initialization or added later.
/**
* Configure transports during initialization
*/
interface ISettingsParam<LogObj> {
attachedTransports?: ((transportLogger: LogObj & ILogObjMeta) => void)[];
}Usage Examples:
import { Logger } from "tslog";
// Configure transports during initialization
const logger = new Logger({
name: "PreConfiguredApp",
attachedTransports: [
(logObj) => {
// File transport
fs.appendFileSync("startup.log", JSON.stringify(logObj) + "\n");
},
(logObj) => {
// External service transport
if (logObj._meta.logLevelId >= 4) { // WARN and above
alertingService.send(logObj);
}
},
],
});
// Add additional transports later
logger.attachTransport((logObj) => {
// Development debugging transport
if (process.env.NODE_ENV === "development") {
debugConsole.log(logObj);
}
});Guidelines for implementing robust transport functions.
/**
* Example robust transport implementation
*/
function createRobustTransport(config: TransportConfig): TransportLogger<any> {
return (logObj) => {
try {
// Handle the log object
processLogObject(logObj, config);
} catch (error) {
// Don't let transport errors crash the application
console.error("Transport error:", error);
}
};
}
interface TransportConfig {
/** Maximum retries for failed sends */
maxRetries?: number;
/** Timeout for transport operations */
timeout?: number;
/** Buffer size for batching logs */
bufferSize?: number;
/** Filter function for selective logging */
filter?: (logObj: any) => boolean;
}Usage Examples:
import { Logger } from "tslog";
// HTTP transport with error handling
function createHttpTransport(endpoint: string) {
return (logObj: any) => {
try {
fetch(endpoint, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(logObj),
}).catch((error) => {
// Handle network errors without crashing
console.error("HTTP transport failed:", error);
});
} catch (error) {
console.error("HTTP transport error:", error);
}
};
}
// Filtered transport (only errors and fatals)
function createErrorOnlyTransport(handler: (logObj: any) => void) {
return (logObj: any) => {
if (logObj._meta.logLevelId >= 5) { // ERROR and FATAL only
handler(logObj);
}
};
}
// Batching transport for performance
function createBatchTransport(sender: (batch: any[]) => void, batchSize = 10) {
let batch: any[] = [];
return (logObj: any) => {
batch.push(logObj);
if (batch.length >= batchSize) {
sender([...batch]);
batch = [];
}
};
}
const logger = new Logger({ name: "AdvancedApp" });
// Use advanced transports
logger.attachTransport(createHttpTransport("https://logs.example.com/api"));
logger.attachTransport(createErrorOnlyTransport((logObj) => {
alertingSystem.send(logObj);
}));
logger.attachTransport(createBatchTransport((batch) => {
analytics.sendBatch(batch);
}, 5));Sub-loggers inherit transports from their parent loggers, and additional transports can be added.
/**
* Sub-loggers inherit parent transports
*/
getSubLogger(settings?: ISettingsParam<LogObj>, logObj?: LogObj): Logger<LogObj>;Usage Examples:
import { Logger } from "tslog";
// Parent logger with transports
const parentLogger = new Logger({ name: "ParentApp" });
parentLogger.attachTransport((logObj) => {
console.log("Parent transport:", logObj._meta.name);
});
// Child logger inherits parent transports
const childLogger = parentLogger.getSubLogger({ name: "ChildModule" });
// Child logger can add its own transports
childLogger.attachTransport((logObj) => {
console.log("Child transport:", logObj._meta.name);
});
// This log will go to both parent and child transports
childLogger.info("Child logger message");
// Output:
// Parent transport: ChildModule
// Child transport: ChildModuleimport * as fs from "fs";
import * as path from "path";
function createFileTransport(filePath: string) {
return (logObj: any) => {
const logLine = JSON.stringify(logObj) + "\n";
fs.appendFileSync(filePath, logLine);
};
}function createSyslogTransport() {
return (logObj: any) => {
const syslogLevel = mapLogLevelToSyslog(logObj._meta.logLevelId);
const message = `<${syslogLevel}>${JSON.stringify(logObj)}`;
// Send to syslog daemon
sendToSyslog(message);
};
}function createDatabaseTransport(database: any) {
return async (logObj: any) => {
await database.collection('logs').insertOne({
timestamp: logObj._meta.date,
level: logObj._meta.logLevelName,
logger: logObj._meta.name,
data: logObj,
});
};
}Install with Tessl CLI
npx tessl i tessl/npm-tslog