Third-party integrations
This guide shows how to integrate LogTape with popular web frameworks, ORMs, and other third-party libraries for unified logging across different JavaScript environments.
Drizzle ORM
Drizzle ORM is a lightweight, TypeScript-first ORM that supports PostgreSQL, MySQL, and SQLite. LogTape provides a Drizzle ORM adapter through the @logtape/drizzle-orm package, allowing you to use LogTape as Drizzle's logging backend for database query logging:
deno add jsr:@logtape/drizzle-ormnpm add @logtape/drizzle-ormpnpm add @logtape/drizzle-ormyarn add @logtape/drizzle-ormbun add @logtape/drizzle-ormHere's an example of using LogTape with Drizzle ORM:
import { configure, getConsoleSink } from "@logtape/logtape";
import { getLogger } from "@logtape/drizzle-orm";
import { drizzle } from "drizzle-orm/postgres-js";
import postgres from "postgres";
await configure({
sinks: { console: getConsoleSink() },
loggers: [
{ category: ["drizzle-orm"], sinks: ["console"], lowestLevel: "debug" }
],
});
const client = postgres(process.env.DATABASE_URL!);
const db = drizzle(client, {
logger: getLogger(),
});
// Now all database queries will be logged through LogTapeCustom category
You can specify a custom category for the logger:
const logger = getLogger({
category: ["myapp", "database"],
});Custom log level
By default, queries are logged at the debug level. You can change this:
const logger = getLogger({
level: "info",
});Structured logging output
The adapter logs queries with structured data that includes:
formattedQuery: The query with parameter placeholders (e.g.,$1,$2) replaced with actual values for easier readingquery: The original query string with placeholdersparams: The original parameters array
This allows you to:
- Get human-readable output with text formatters
- Get machine-parseable output with JSON Lines formatter
- Use full query and params data with OpenTelemetry, Sentry, and other sinks
Express
Express is the most popular Node.js web framework. LogTape provides an Express middleware adapter through the @logtape/express package, which provides HTTP request logging using LogTape as the backend, as an alternative to Morgan:
deno add jsr:@logtape/expressnpm add @logtape/expresspnpm add @logtape/expressyarn add @logtape/expressbun add @logtape/expressHere's an example of using LogTape with Express:
import { configure, getConsoleSink } from "@logtape/logtape";
import { expressLogger } from "@logtape/express";
import express from "express";
await configure({
sinks: { console: getConsoleSink() },
loggers: [
{ category: ["express"], sinks: ["console"], lowestLevel: "info" }
],
});
const app = express();
app.use(expressLogger());
app.get("/", (req, res) => {
res.json({ hello: "world" });
});
app.listen(3000);Custom category
You can specify a custom category for the logger:
const middleware = expressLogger({
category: ["myapp", "http"],
});Options
The middleware accepts the following options:
const middleware = expressLogger({
category: ["myapp", "http"], // Custom category (default: ["express"])
level: "debug", // Log level (default: "info")
format: "dev", // Predefined format (default: "combined")
skip: (req, res) => res.statusCode < 400, // Skip successful requests
immediate: false, // Log after response (default: false)
});Predefined formats
The middleware supports Morgan-compatible predefined formats:
"combined": Apache Combined Log Format with all properties (default)"common": Apache Common Log Format (without referrer/userAgent)"dev": Concise output for development (e.g.,GET /path 200 1.234 ms - 123)"short": Shorter format with remote address"tiny": Minimal output
Custom format function
You can also provide a custom format function that returns either a string or an object with structured properties:
const middleware = expressLogger({
format: (req, res, responseTime) => ({
method: req.method,
path: req.path,
status: res.statusCode,
duration: responseTime,
}),
});Structured logging output
When using the "combined" format (default), the middleware logs structured data that includes:
method: HTTP request methodurl: Request URLstatus: HTTP response status coderesponseTime: Response time in millisecondscontentLength: Response content-length header valueremoteAddr: Remote client addressuserAgent: User-Agent header valuereferrer: Referrer header valuehttpVersion: HTTP version (e.g.,"1.1")
Hono
Hono is a modern web framework that works across multiple JavaScript runtimes. LogTape provides a Hono adapter through the @logtape/hono package, allowing you to use LogTape as Hono's logging backend with HTTP request logging:
deno add jsr:@logtape/hononpm add @logtape/honopnpm add @logtape/honoyarn add @logtape/honobun add @logtape/honoHere's an example of using LogTape with Hono:
import { Hono } from "hono";
import { configure, getConsoleSink } from "@logtape/logtape";
import { honoLogger } from "@logtape/hono";
await configure({
sinks: { console: getConsoleSink() },
loggers: [
{ category: ["hono"], sinks: ["console"], lowestLevel: "info" }
],
});
const app = new Hono();
app.use(honoLogger());
app.get("/", (c) => c.json({ hello: "world" }));
// Works in Deno, Node.js, Bun, Cloudflare Workers, etc.
export default app;Custom category
You can specify a custom category for the logger:
const middleware = honoLogger({
category: ["myapp", "http"],
});Options
The middleware accepts the following options:
const middleware = honoLogger({
category: ["myapp", "http"], // Custom category (default: ["hono"])
level: "debug", // Log level (default: "info")
format: "dev", // Predefined format (default: "combined")
skip: (c) => c.req.path === "/health", // Skip health check endpoint
logRequest: false, // Log after response (default: false)
});Predefined formats
The middleware supports Morgan-compatible predefined formats:
"combined": Apache Combined Log Format with all properties (default)"common": Apache Common Log Format (without referrer/userAgent)"dev": Concise output for development (e.g.,GET /path 200 1.234 ms - 123)"short": Shorter format with URL"tiny": Minimal output
Custom format function
You can also provide a custom format function that returns either a string or an object with structured properties:
const middleware = honoLogger({
format: (c, responseTime) => ({
method: c.req.method,
path: c.req.path,
status: c.res.status,
duration: responseTime,
}),
});Structured logging output
When using the "combined" format (default), the middleware logs structured data that includes:
method: HTTP request methodurl: Request URLpath: Request pathstatus: HTTP response status coderesponseTime: Response time in millisecondscontentLength: Response content-length header valueuserAgent: User-Agent header valuereferrer: Referrer header value
Fastify
Fastify is a fast and low-overhead web framework for Node.js. LogTape provides a Pino-compatible logger adapter through the @logtape/fastify package, allowing you to use LogTape as Fastify's logging backend with seamless integration:
deno add jsr:@logtape/fastifynpm add @logtape/fastifypnpm add @logtape/fastifyyarn add @logtape/fastifybun add @logtape/fastifyHere's an example of using LogTape with Fastify:
import { configure, getConsoleSink } from "@logtape/logtape";
import { getLogTapeFastifyLogger } from "@logtape/fastify";
import Fastify from "fastify";
await configure({
sinks: { console: getConsoleSink() },
loggers: [
{ category: ["fastify"], sinks: ["console"], lowestLevel: "info" }
],
});
const fastify = Fastify({
loggerInstance: getLogTapeFastifyLogger(),
});
fastify.get("/", async (request, reply) => {
// Uses LogTape under the hood with request context
request.log.info("Handling request");
return { hello: "world" };
});
await fastify.listen({ port: 3000 });Custom category
You can specify a custom category for the logger:
const logger = getLogTapeFastifyLogger({
category: ["myapp", "http"],
});Child loggers
Fastify automatically creates child loggers with request-scoped bindings (like reqId). These bindings are passed to LogTape's structured logging:
const fastify = Fastify({
loggerInstance: getLogTapeFastifyLogger(),
});
fastify.get("/users/:id", async (request, reply) => {
// Child logger automatically includes reqId and any additional bindings
request.log.info({ userId: request.params }, "Fetching user");
return { user: "data" };
});Pino method signatures
The adapter supports all Pino-style logging signatures:
// Simple message
logger.info("Hello world");
// Printf-style interpolation
logger.info("User %s logged in %d times", "alice", 3);
// Object with message
logger.info({ userId: 123, action: "login" }, "User logged in");
// Object with msg property
logger.info({ msg: "User logged in", userId: 123 });
// Object only
logger.info({ data: { key: "value" } });Koa
Koa is a modern, lightweight web framework for Node.js that uses async/await throughout. LogTape provides a Koa middleware adapter through the @logtape/koa package, which provides HTTP request logging using LogTape as the backend, as an alternative to koa-logger:
deno add jsr:@logtape/koanpm add @logtape/koapnpm add @logtape/koayarn add @logtape/koabun add @logtape/koaHere's an example of using LogTape with Koa:
import { configure, getConsoleSink } from "@logtape/logtape";
import { koaLogger } from "@logtape/koa";
import Koa from "koa";
await configure({
sinks: { console: getConsoleSink() },
loggers: [
{ category: ["koa"], sinks: ["console"], lowestLevel: "info" }
],
});
const app = new Koa();
app.use(koaLogger());
app.use((ctx) => {
ctx.body = { hello: "world" };
});
app.listen(3000);Custom category
You can specify a custom category for the logger:
const middleware = koaLogger({
category: ["myapp", "http"],
});Options
The middleware accepts the following options:
const middleware = koaLogger({
category: ["myapp", "http"], // Custom category (default: ["koa"])
level: "debug", // Log level (default: "info")
format: "dev", // Predefined format (default: "combined")
skip: (ctx) => ctx.path === "/health", // Skip health check endpoint
logRequest: false, // Log after response (default: false)
});Predefined formats
The middleware supports Morgan-compatible predefined formats:
"combined": Apache Combined Log Format with all properties (default)"common": Apache Common Log Format (without referrer/userAgent)"dev": Concise output for development (e.g.,GET /path 200 1.234 ms - 123)"short": Shorter format with remote address"tiny": Minimal output
Custom format function
You can also provide a custom format function that returns either a string or an object with structured properties:
const middleware = koaLogger({
format: (ctx, responseTime) => ({
method: ctx.method,
path: ctx.path,
status: ctx.status,
duration: responseTime,
}),
});Structured logging output
When using the "combined" format (default), the middleware logs structured data that includes:
method: HTTP request methodurl: Request URLpath: Request pathstatus: HTTP response status coderesponseTime: Response time in millisecondscontentLength: Response content-lengthremoteAddr: Remote client addressuserAgent: User-Agent header valuereferrer: Referrer header value
Proxy configuration
When your Koa application runs behind a reverse proxy, you need to set app.proxy = true in your Koa configuration for remoteAddr to correctly reflect the client's IP address from the X-Forwarded-For header. See Koa's proxy documentation for details.
SvelteKit
SvelteKit can use LogTape in both server-side hooks and API routes. Use hooks for global request logging:
import {
configure,
getConsoleSink,
getLogger,
withContext,
} from "@logtape/logtape";
import type { Handle } from "@sveltejs/kit";
import { AsyncLocalStorage } from "node:async_hooks";
await configure({
sinks: { console: getConsoleSink() },
loggers: [
{ category: ["sveltekit"], sinks: ["console"], lowestLevel: "info" }
],
contextLocalStorage: new AsyncLocalStorage(),
});
const logger = getLogger(["sveltekit"]);
export const handle: Handle = async ({ event, resolve }) => {
const requestId = crypto.randomUUID();
const startTime = Date.now();
return await withContext({
requestId,
method: event.request.method,
url: event.url.pathname,
userAgent: event.request.headers.get("user-agent")
}, async () => {
logger.info("Request started", {
method: event.request.method,
url: event.url.pathname,
requestId
});
try {
const response = await resolve(event);
const duration = Date.now() - startTime;
logger.info("Request completed", {
status: response.status,
duration,
requestId
});
return response;
} catch (error) {
const duration = Date.now() - startTime;
logger.error("Request error", {
error: {
name: error instanceof Error ? error.name : "Unknown",
message: error instanceof Error ? error.message : String(error)
},
duration,
requestId
});
throw error;
}
});
};And in individual API routes:
import { getLogger } from "@logtape/logtape";
import { json, type RequestHandler } from "@sveltejs/kit";
const logger = getLogger(["sveltekit", "api"]);
export const GET: RequestHandler = async ({ request }) => {
logger.info("Users API called");
try {
const users = await getUsers();
logger.info("Users retrieved successfully", {
count: users.length
});
return json(users);
} catch (error) {
logger.error("Failed to retrieve users", {
error: error instanceof Error ? error.message : String(error)
});
return json({ error: "Internal server error" }, { status: 500 });
}
};
async function getUsers(): Promise<any[]> {
return [];
}Third-party log integration
This API is available since LogTape 1.1.0.
When integrating logs from external systems, you may want to preserve their original timestamps and metadata. LogTape provides the Logger.emit() method for this purpose, giving you full control over log record fields.
Basic usage
The Logger.emit() method accepts a log record without the category field:
import { getLogger } from "@logtape/logtape";
const logger = getLogger(["my-app", "external"]);
// Forward external log with preserved timestamp
logger.emit({
timestamp: externalLog.timestamp,
level: "info",
message: ["External system event"],
rawMessage: "External system event",
properties: {
source: "external-service",
eventId: externalLog.id,
},
});Best practices
Request ID correlation: Always generate and use request IDs to correlate logs across your application:
const requestId = crypto.randomUUID(); // Preferred method // For environments without crypto.randomUUID(): const requestId = Math.random().toString(36).substring(2, 11);Context management: Use
withContext()to automatically include request metadata in all logs within the request scope.Error handling: Always log errors with structured information including exception object and request context.
Performance monitoring: Log request duration and response sizes to monitor application performance.
Security considerations: Be careful not to log sensitive information like passwords, tokens, or personal data. Use the @logtape/redaction package for data sanitization patterns.
Environment-specific configuration: Configure different log levels and sinks for development vs production environments to balance verbosity with performance.