@logtape/hono
This package provides Hono middleware for HTTP request logging using LogTape as the backend, as an alternative to Hono's built-in logger middleware.
Installation
deno add jsr:@logtape/hono # for Deno
npm add @logtape/hono # for npm
pnpm add @logtape/hono # for pnpm
yarn add @logtape/hono # for Yarn
bun add @logtape/hono # for BunUsage
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" }));
export default app;Options
The honoLogger() function accepts an optional options object:
app.use(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 logging for specific paths
logRequest: true, // Log at request start (default: false)
context: 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 your route handlers inherit the same requestId, also
configure LogTape with contextLocalStorage:
import { AsyncLocalStorage } from "node:async_hooks";
import { configure } from "@logtape/logtape";
await configure({
// ... sinks and loggers ...
contextLocalStorage: new AsyncLocalStorage(),
});
app.use(honoLogger({ context: true }));The context is still established when skip suppresses the request log, so
application logs inside the skipped request can keep the same request ID.
You can customize request ID headers and add more request fields:
app.use(honoLogger({
context: {
requestId: {
headerNames: ["x-correlation-id", "x-request-id"],
responseHeader: "x-request-id",
},
include: ["requestId", "method", "path", "userAgent"],
enrich: (c) => ({ route: c.req.path }),
},
}));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:
app.use(honoLogger({
format: (c, responseTime) => ({
method: c.req.method,
path: c.req.path,
status: c.res.status,
duration: responseTime,
}),
}));The formatter callback receives the real Hono Context, so you can also read
context variables when your app uses them:
app.use(honoLogger({
format: (c, responseTime) => ({
requestId: c.get("requestId"),
method: c.req.method,
path: c.req.path,
duration: responseTime,
}),
}));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
See also
For more information, see the LogTape documentation.