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 { , } from "@logtape/logtape";
import { } from "@logtape/drizzle-orm";
import { } from "drizzle-orm/postgres-js";
import from "postgres";
await ({
: { : () },
: [
{ : ["drizzle-orm"], : ["console"], : "debug" }
],
});
const = (..!);
const = (, {
: (),
});
// Now all database queries will be logged through LogTapeCustom category
You can specify a custom category for the logger:
const = ({
: ["myapp", "database"],
});Custom log level
By default, queries are logged at the debug level. You can change this:
const = ({
: "info",
});Dialects
By default, the PostgreSQL dialect ("pg") is used. You can change this:
const = ({
: "sqlite", // "pg" | "sqlite"
});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 { , } from "@logtape/logtape";
import { } from "@logtape/express";
import from "express";
await ({
: { : () },
: [
{ : ["express"], : ["console"], : "info" }
],
});
const = ();
.(());
.("/", (, ) => {
.({ : "world" });
});
.(3000);Custom category
You can specify a custom category for the logger:
const = ({
: ["myapp", "http"],
});Options
The middleware accepts the following options:
const = ({
: ["myapp", "http"], // Custom category (default: ["express"])
: "debug", // Log level (default: "info")
: "dev", // Predefined format (default: "combined")
: (, ) => . < 400, // Skip successful requests
: false, // Log after response (default: false)
: true, // Add requestId to request-scoped logs
});Request context
Set context: true to add request-scoped correlation fields. By default, the middleware reads the x-request-id request header, generates an ID when the header is missing, writes the resolved ID to the x-request-id response header, and adds requestId to the request log record.
To make logs emitted by route handlers inherit the same requestId, configure LogTape with contextLocalStorage as described in implicit contexts:
import { } from "node:async_hooks";
import { } from "@logtape/logtape";
import { } from "@logtape/express";
await ({
: {},
: [],
: new (),
});
const = ({ : true });The context is established even when skip suppresses the request log, so application logs inside the skipped request can still carry the same request ID.
You can customize request ID headers and include additional request fields:
const = ({
: {
: {
: ["x-correlation-id", "x-request-id"],
: "x-request-id",
},
: ["requestId", "method", "url", "httpVersion"],
: () => ({ : . }),
},
});Predefined formats
The middleware supports structured presets and text presets:
"structured-combined": Structured request properties with all fields (default)"structured-common": Structured request properties withoutreferrer/userAgent"combined": Deprecated alias for"structured-combined""common": Deprecated alias for"structured-common""morgan-combined": Morgan-compatible Apache combined access log output"morgan-common": Morgan-compatible Apache common access log output"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 = ({
: (, , ) => ({
: .,
: .,
: .,
: ,
}),
});Structured logging output
When using the "structured-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")
Elysia
Elysia is a fast, Bun-first web framework with end-to-end type safety. LogTape provides an Elysia plugin adapter through the @logtape/elysia package, which provides HTTP request logging using LogTape as the backend:
deno add jsr:@logtape/elysianpm add @logtape/elysiapnpm add @logtape/elysiayarn add @logtape/elysiabun add @logtape/elysiaHere's an example of using LogTape with Elysia:
import { } from "elysia";
import { , } from "@logtape/logtape";
import { } from "@logtape/elysia";
await ({
: { : () },
: [
{ : ["elysia"], : ["console"], : "info" }
],
});
const = new ()
.(())
.("/", () => ({ : "world" }))
.(3000);
.(`Server running at ${.?.}`);Custom category
You can specify a custom category for the logger:
const = ({
: ["myapp", "http"],
});Options
The plugin accepts the following options:
const = ({
: ["myapp", "http"], // Custom category (default: ["elysia"])
: "debug", // Log level (default: "info")
: "dev", // Predefined format (default: "combined")
: () => . === "/health", // Skip health check endpoint
: false, // Log after response (default: false)
: "global", // Plugin scope (default: "global")
: true, // Add requestId to request-scoped logs
});Request context
Set context: true to add request-scoped correlation fields. By default, the plugin reads the x-request-id request header, generates an ID when the header is missing, writes the resolved ID to the x-request-id response header, and adds requestId to request and error log records.
To make logs emitted by route handlers inherit the same requestId, configure LogTape with contextLocalStorage as described in implicit contexts:
import { } from "node:async_hooks";
import { } from "@logtape/logtape";
import { } from "@logtape/elysia";
await ({
: {},
: [],
: new (),
});
const = ({ : true });The context is established even when skip suppresses the request log, so application logs inside the skipped request can still carry the same request ID.
You can customize request ID headers and include additional request fields:
const = ({
: {
: {
: ["x-correlation-id", "x-request-id"],
: "x-request-id",
},
: ["requestId", "method", "path", "userAgent"],
: () => ({ : . }),
},
});Plugin scope
Elysia supports plugin scoping to control how lifecycle hooks propagate:
"global": Hooks apply to all routes in the application (default)"scoped": Hooks apply to the parent instance where the plugin is used"local": Hooks only apply within the plugin itself
// Only log requests within a specific group
const = ({
: "scoped",
});Predefined formats
The plugin supports structured presets and text presets:
"structured-combined": Structured request properties (default)"structured-common": Structured request properties withoutreferrer/userAgent"combined": Deprecated alias for"structured-combined""common": Deprecated alias for"structured-common""morgan-combined": Morgan-compatible Apache combined access log output"morgan-common": Morgan-compatible Apache common access log output"dev": Concise output for development (e.g.,GET /path 200 1.234 ms - 123)"short": Shorter format with URL"tiny": Minimal output
Elysia does not expose socket-level fields consistently across runtimes. The Morgan-compatible text formats use X-Forwarded-For for the remote address and render unavailable fields, such as the HTTP version, as -.
Custom format function
You can also provide a custom format function that returns either a string or an object with structured properties:
const = ({
: (, ) => ({
: ..,
: .,
: ..,
: ,
}),
});Error logging
The plugin automatically logs errors at the error level using Elysia's onError hook. Error logs include the error message and error code in addition to standard request properties.
Structured logging output
When using the "structured-combined" format (default), the plugin 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 valueremoteAddr: Remote client address (from X-Forwarded-For header)userAgent: User-Agent header valuereferrer: Referrer header value
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 { } from "hono";
import { , } from "@logtape/logtape";
import { } from "@logtape/hono";
await ({
: { : () },
: [
{ : ["hono"], : ["console"], : "info" }
],
});
const = new ();
.(());
.("/", () => .({ : "world" }));
// Works in Deno, Node.js, Bun, Cloudflare Workers, etc.
export default ;Custom category
You can specify a custom category for the logger:
const = ({
: ["myapp", "http"],
});Options
The middleware accepts the following options:
const = ({
: ["myapp", "http"], // Custom category (default: ["hono"])
: "debug", // Log level (default: "info")
: "dev", // Predefined format (default: "combined")
: () => .. === "/health", // Skip health check endpoint
: false, // Log after response (default: false)
: true, // Add requestId to request-scoped logs
});Request context
Set context: true to add request-scoped correlation fields. By default, the middleware reads the x-request-id request header, generates an ID when the header is missing, writes the resolved ID to the x-request-id response header, and adds requestId to the request log record.
To make logs emitted by route handlers inherit the same requestId, configure LogTape with contextLocalStorage as described in implicit contexts:
import { } from "node:async_hooks";
import { } from "@logtape/logtape";
import { } from "@logtape/hono";
await ({
: {},
: [],
: new (),
});
const = ({ : true });The context is established even when skip suppresses the request log, so application logs inside the skipped request can still carry the same request ID.
You can customize request ID headers and include additional request fields:
const = ({
: {
: {
: ["x-correlation-id", "x-request-id"],
: "x-request-id",
},
: ["requestId", "method", "path", "userAgent"],
: () => ({ : .. }),
},
});Predefined formats
The middleware supports structured presets and text presets:
"structured-combined": Structured request properties (default)"structured-common": Structured request properties withoutreferrer/userAgent"combined": Deprecated alias for"structured-combined""common": Deprecated alias for"structured-common""morgan-combined": Morgan-compatible Apache combined access log output"morgan-common": Morgan-compatible Apache common access log output"dev": Concise output for development (e.g.,GET /path 200 1.234 ms - 123)"short": Shorter format with URL"tiny": Minimal output
Hono does not expose socket-level fields consistently across runtimes. The Morgan-compatible text formats use X-Forwarded-For for the remote address and render unavailable fields, such as the HTTP version, as -.
Custom format function
You can also provide a custom format function that returns either a string or an object with structured properties:
const = ({
: (, ) => ({
: ..,
: ..,
: ..,
: ,
}),
});Structured logging output
When using the "structured-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 { , } from "@logtape/logtape";
import { } from "@logtape/fastify";
import from "fastify";
await ({
: { : () },
: [
{ : ["fastify"], : ["console"], : "info" }
],
});
const = ({
: (),
});
.("/", async (, ) => {
// Uses LogTape under the hood with request context
..("Handling request");
return { : "world" };
});
await .({ : 3000 });Custom category
You can specify a custom category for the logger:
const = ({
: ["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 = ({
: (),
});
.("/users/:id", async (, ) => {
// Child logger automatically includes reqId and any additional bindings
..({ : . }, "Fetching user");
return { : "data" };
});Pino method signatures
The adapter supports all Pino-style logging signatures:
// Simple message
.("Hello world");
// Printf-style interpolation
.("User %s logged in %d times", "alice", 3);
// Object with message
.({ : 123, : "login" }, "User logged in");
// Object with msg property
.({ : "User logged in", : 123 });
// Object only
.({ : { : "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 { , } from "@logtape/logtape";
import { } from "@logtape/koa";
import from "koa";
await ({
: { : () },
: [
{ : ["koa"], : ["console"], : "info" }
],
});
const = new ();
.(());
.(() => {
. = { : "world" };
});
.(3000);Custom category
You can specify a custom category for the logger:
const = ({
: ["myapp", "http"],
});Options
The middleware accepts the following options:
const = ({
: ["myapp", "http"], // Custom category (default: ["koa"])
: "debug", // Log level (default: "info")
: "dev", // Predefined format (default: "combined")
: () => . === "/health", // Skip health check endpoint
: false, // Log after response (default: false)
: true, // Add requestId to request-scoped logs
});Request context
Set context: true to add request-scoped correlation fields. By default, the middleware reads the x-request-id request header, generates an ID when the header is missing, writes the resolved ID to the x-request-id response header, and adds requestId to the request log record.
To make logs emitted by route handlers inherit the same requestId, configure LogTape with contextLocalStorage as described in implicit contexts:
import { } from "node:async_hooks";
import { } from "@logtape/logtape";
import { } from "@logtape/koa";
await ({
: {},
: [],
: new (),
});
const = ({ : true });The context is established even when skip suppresses the request log, so application logs inside the skipped request can still carry the same request ID.
You can customize request ID headers and include additional request fields:
const = ({
: {
: {
: ["x-correlation-id", "x-request-id"],
: "x-request-id",
},
: ["requestId", "method", "path", "remoteAddr"],
: () => ({ : . }),
},
});Predefined formats
The middleware supports structured presets and text presets:
"structured-combined": Structured request properties (default)"structured-common": Structured request properties withoutreferrer/userAgent"combined": Deprecated alias for"structured-combined""common": Deprecated alias for"structured-common""morgan-combined": Morgan-compatible Apache combined access log output"morgan-common": Morgan-compatible Apache common access log output"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 = ({
: (, ) => ({
: .,
: .,
: .,
: ,
}),
});Structured logging output
When using the "structured-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. Unlike Express, Elysia, Hono, and Koa, LogTape does not provide a dedicated SvelteKit adapter. Use hooks with withContext() for global request logging:
import {
,
,
,
,
} from "@logtape/logtape";
import type { } from "@sveltejs/kit";
import { } from "node:async_hooks";
await ({
: { : () },
: [
{ : ["sveltekit"], : ["console"], : "info" }
],
: new (),
});
const = (["sveltekit"]);
export const : = async ({ , }) => {
const = .();
const = .();
return await ({
,
: ..,
: ..,
: ...("user-agent")
}, async () => {
.("Request started", {
: ..,
: ..,
});
try {
const = await ();
const = .() - ;
.("Request completed", {
: .,
,
});
return ;
} catch () {
const = .() - ;
.("Request error", {
: {
: instanceof ? . : "Unknown",
: instanceof ? . : ()
},
,
});
throw ;
}
});
};And in individual API routes:
import { } from "@logtape/logtape";
import { , type } from "@sveltejs/kit";
const = (["sveltekit", "api"]);
export const : = async ({ }) => {
.("Users API called");
try {
const = await ();
.("Users retrieved successfully", {
: .
});
return ();
} catch () {
.("Failed to retrieve users", {
: instanceof ? . : ()
});
return ({ : "Internal server error" }, { : 500 });
}
};
async function (): <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 { } from "@logtape/logtape";
const = (["my-app", "external"]);
// Forward external log with preserved timestamp
.({
: .,
: "info",
: ["External system event"],
: "External system event",
: {
: "external-service",
: .,
},
});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.