Adapters
LogTape adapters enable seamless integration between LogTape-enabled libraries and applications using existing logging infrastructure. They bridge the gap between LogTape's library-friendly design and established logging ecosystems.
Overview
LogTape's zero-dependency, tree-shakable design makes it ideal for library authors who want to provide logging without imposing dependencies on their users. However, this creates a practical challenge: when a library uses LogTape but an application already has an established logging system (like winston, Pino, or log4js), developers face an integration dilemma.
Adapters solve this by forwarding LogTape log records to existing logging systems, allowing applications to:
- Use LogTape-enabled libraries without changing their logging infrastructure
- Maintain consistent log formatting and routing across their entire application
- Leverage existing log management, monitoring, and alerting systems
- Gradually adopt LogTape without requiring a complete migration
When to use adapters
Library integration scenarios
- Your application uses winston/Pino/log4js and you want to use a library that logs with LogTape
- You need all logs to flow through your existing logging pipeline
- You have established log formatting, filtering, or routing requirements
- You want to maintain consistency with existing operational procedures
Migration scenarios
- Transitioning from another logging library to LogTape incrementally
- Testing LogTape compatibility before a full migration
- Running LogTape alongside existing loggers during a transition period
Not recommended when
- Starting a new project where you can choose LogTape directly
- Your existing logging system has significant performance or functionality limitations
- You want to take full advantage of LogTape's structured logging capabilities
Pino adapter
This API is available since LogTape 1.0.0.
The @logtape/adaptor-pino forwards LogTape log records to Pino loggers, preserving structured logging data and providing flexible category formatting:
deno add jsr:@logtape/adaptor-pino
npm add @logtape/adaptor-pino
pnpm add @logtape/adaptor-pino
yarn add @logtape/adaptor-pino
bun add @logtape/adaptor-pino
The adapter provides a getPinoSink()
function that creates a LogTape sink that forwards log records to a Pino logger. This allows you to configure LogTape to use Pino as a sink, enabling LogTape-enabled libraries to log directly to your existing Pino logger:
import { configure } from "@logtape/logtape";
import { getPinoSink } from "@logtape/adaptor-pino";
import { pino } from "pino";
const logger = pino();
await configure({
sinks: {
pino: getPinoSink(logger)
},
loggers: [
{ category: "my-library", sinks: ["pino"] }
]
});
winston adapter
This API is available since LogTape 1.0.0.
The @logtape/adaptor-winston forwards LogTape log records to winston loggers, providing flexible level mapping and category formatting options:
deno add jsr:@logtape/adaptor-winston
npm add @logtape/adaptor-winston
pnpm add @logtape/adaptor-winston
yarn add @logtape/adaptor-winston
bun add @logtape/adaptor-winston
Quick setup
The simplest way to integrate LogTape with winston is using the auto-installer:
import "@logtape/adaptor-winston/install";
// All LogTape logs will now be routed to winston's default logger
import { getLogger } from "@logtape/logtape";
const logger = getLogger("my-app");
logger.info("This will be logged through winston");
Manual configuration
For more control, you can configure LogTape to use winston as a sink:
import { configure } from "@logtape/logtape";
import { getWinstonSink } from "@logtape/adaptor-winston";
import winston from "winston";
const winstonLogger = winston.createLogger({
level: "info",
format: winston.format.combine(
winston.format.timestamp(),
winston.format.json()
),
transports: [
new winston.transports.Console(),
new winston.transports.File({ filename: "app.log" })
]
});
await configure({
sinks: {
winston: getWinstonSink(winstonLogger, {
category: {
position: "start",
decorator: "[]",
separator: "."
}
})
},
loggers: [
{ category: "my-library", sinks: ["winston"] }
]
});
Using the install() function
The install()
function provides a convenient middle ground between automatic setup and manual configuration:
import winston from "winston";
import { install } from "@logtape/adaptor-winston";
const customLogger = winston.createLogger({
transports: [new winston.transports.File({ filename: "app.log" })]
});
// Install with custom logger and options
install(customLogger, {
category: { position: "start", decorator: "[]" },
levelsMap: {
"trace": "silly",
"debug": "debug",
"info": "verbose",
"warning": "info",
"error": "warn",
"fatal": "error",
}
});
Planned adapters
The following adapters are planned for future releases:
- log4js adapter (@logtape/adaptor-log4js)
- Integration with log4js for applications using log4j-style logging
- bunyan adapter (@logtape/adaptor-bunyan)
- Integration with Bunyan structured logging
To request priority for a specific adapter or contribute an implementation, please see the adapter development roadmap on GitHub.
Best practices
Use your existing logger's adapter when:
- You have established logging infrastructure
- Your team is familiar with the existing logging system
- You need compatibility with existing log processing tools
Consider migrating to native LogTape when:
- Starting new projects
- Your current logging system lacks features you need
- You want to simplify dependencies and improve performance
Creating custom adapters
If you need an adapter for a logging system not yet supported, you can create your own by implementing the Sink
interface:
import type { LogLevel, LogRecord, Sink } from "@logtape/logtape";
export function getCustomLoggerSink(customLogger: CustomLogger): Sink {
return (record: LogRecord) => {
// Map LogTape levels to your logger's levels
const level = mapLogLevel(record.level);
// Format the message (handle LogTape's template system)
const message = formatMessage(record.message);
// Forward to your logging system
customLogger[level](message, record.properties);
};
}
function mapLogLevel(logTapeLevel: LogLevel): CustomLoggerLevel {
switch (logTapeLevel) {
case "trace": return "verbose";
case "debug": return "debug";
case "info": return "info";
case "warning": return "warn";
case "error": return "error";
case "fatal": return "critical";
default: return "info";
}
}
function formatMessage(messageParts: readonly (string | unknown)[]): string {
let result = "";
for (let i = 0; i < messageParts.length; i += 2) {
result += messageParts[i];
if (i + 1 < messageParts.length) {
// Handle interpolated values appropriately for your logger
result += JSON.stringify(messageParts[i + 1]);
}
}
return result;
}
Contribution guidelines
When creating adapters for popular logging libraries:
- Follow naming conventions: Use
@logtape/adaptor-{library-name}
- Implement comprehensive tests: Cover level mapping, category formatting, and error scenarios
- Provide TypeScript definitions: Ensure full type safety
- Document thoroughly: Include usage examples and configuration options
- Consider performance: Minimize overhead in the adaptation layer
See the @logtape/adaptor-pino source code as a reference implementation.