Skip to content

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-orm
npm add @logtape/drizzle-orm
pnpm add @logtape/drizzle-orm
yarn add @logtape/drizzle-orm
bun add @logtape/drizzle-orm

Here'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 LogTape

Custom 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 reading
  • query: The original query string with placeholders
  • params: 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/express
npm add @logtape/express
pnpm add @logtape/express
yarn add @logtape/express
bun add @logtape/express

Here'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 without referrer/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 method
  • url: Request URL
  • status: HTTP response status code
  • responseTime: Response time in milliseconds
  • contentLength: Response content-length header value
  • remoteAddr: Remote client address
  • userAgent: User-Agent header value
  • referrer: Referrer header value
  • httpVersion: 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/elysia
npm add @logtape/elysia
pnpm add @logtape/elysia
yarn add @logtape/elysia
bun add @logtape/elysia

Here'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 without referrer/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 method
  • url: Request URL
  • path: Request path
  • status: HTTP response status code
  • responseTime: Response time in milliseconds
  • contentLength: Response content-length header value
  • remoteAddr: Remote client address (from X-Forwarded-For header)
  • userAgent: User-Agent header value
  • referrer: 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/hono
npm add @logtape/hono
pnpm add @logtape/hono
yarn add @logtape/hono
bun add @logtape/hono

Here'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 without referrer/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 method
  • url: Request URL
  • path: Request path
  • status: HTTP response status code
  • responseTime: Response time in milliseconds
  • contentLength: Response content-length header value
  • userAgent: User-Agent header value
  • referrer: 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/fastify
npm add @logtape/fastify
pnpm add @logtape/fastify
yarn add @logtape/fastify
bun add @logtape/fastify

Here'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/koa
npm add @logtape/koa
pnpm add @logtape/koa
yarn add @logtape/koa
bun add @logtape/koa

Here'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 without referrer/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 method
  • url: Request URL
  • path: Request path
  • status: HTTP response status code
  • responseTime: Response time in milliseconds
  • contentLength: Response content-length
  • remoteAddr: Remote client address
  • userAgent: User-Agent header value
  • referrer: 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:

src/hooks.server.ts
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:

src/routes/api/users/+server.ts
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

  1. 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);
  2. Context management: Use withContext() to automatically include request metadata in all logs within the request scope.

  3. Error handling: Always log errors with structured information including exception object and request context.

  4. Performance monitoring: Log request duration and response sizes to monitor application performance.

  5. Security considerations: Be careful not to log sensitive information like passwords, tokens, or personal data. Use the @logtape/redaction package for data sanitization patterns.

  6. Environment-specific configuration: Configure different log levels and sinks for development vs production environments to balance verbosity with performance.

Released under the MIT License.