Skip to content

feat(structured-logger): add @hono/structured-logger middleware#1782

Merged
yusukebe merged 7 commits intohonojs:mainfrom
gabry-ts:feat/structured-logger
Apr 18, 2026
Merged

feat(structured-logger): add @hono/structured-logger middleware#1782
yusukebe merged 7 commits intohonojs:mainfrom
gabry-ts:feat/structured-logger

Conversation

@gabry-ts
Copy link
Copy Markdown
Contributor

@gabry-ts gabry-ts commented Mar 3, 2026

Summary

Adds a new structured logging middleware for Hono that provides a request scoped logger on c.var.logger.

This middleware is library agnostic and works with pino, winston, bunyan, console, or any logger implementing the BaseLogger interface. It has zero dependencies and is compatible with all runtimes supported by Hono.

Core features:

  • Request scoped logger instance via c.var.logger (configurable key)
  • Automatic response time measurement using performance.now()
  • Native integration with hono/request-id
  • Customizable onRequest, onResponse, and onError hooks
  • Full type safety with generics
  • Works on Node.js, Deno, Bun, Cloudflare Workers, AWS Lambda, Vercel Edge, Fastly Compute

Related: honojs/hono#3963

Test plan

  • 20 unit tests covering all options, hooks, error handling, custom context keys, and default behaviors
  • TypeScript compiles with no errors

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Mar 3, 2026

🦋 Changeset detected

Latest commit: b7e3161

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@hono/structured-logger Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Comment thread packages/structured-logger/README.md Outdated
| `createLogger` | `(c: Context) => L` | Yes | | Factory that creates a request scoped logger instance |
| `contextKey` | `string` | No | `'logger'` | Key used to store the logger on `c.var` |
| `onRequest` | `(logger: L, c: Context) => void \| Promise<void>` | No | Logs method + path at info level | Called before handler execution |
| `onResponse` | `(logger: L, c: Context, elapsedMs: number) => void \| Promise<void>` | No | Logs status + elapsed at info level | Called after handler execution |
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

elapsed time word missing

Comment thread packages/structured-logger/README.md Outdated
| Option | Type | Required | Default | Description |
|---|---|---|---|---|
| `createLogger` | `(c: Context) => L` | Yes | | Factory that creates a request scoped logger instance |
| `contextKey` | `string` | No | `'logger'` | Key used to store the logger on `c.var` |
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would the API maybe be simpler if you merge contextKey option into createLogger? like:
createLogger: (c) => ({ logger: rootLogger.child({ requestId: c.var.requestId }) })

could maybe be more flexible, but also more verbose. But I think I prefer your approach...

Comment thread packages/structured-logger/src/index.ts Outdated
}

function defaultOnError<L extends BaseLogger>(logger: L, err: Error, c: Context): void {
logger.error({ err, method: c.req.method, path: c.req.path }, 'request error')
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe also log the status code here as it could be modified by hono's onError handler

Comment thread packages/structured-logger/src/index.ts Outdated
}

function defaultOnResponse<L extends BaseLogger>(logger: L, c: Context, elapsedMs: number): void {
logger.info({ status: c.res.status, elapsedMs }, 'request end')
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer to include method+path also here, so one does not need to always combine this log line with the 'request start' log line to do analytics. This would also more align defaultOnResponse and defaultOnError. But it could also be a personal preference and likely everyone will modify those settings for a production setup.

@gabry-ts
Copy link
Copy Markdown
Contributor Author

gabry-ts commented Mar 3, 2026

Thanks for the review.

  • Fixed "elapsed time" wording in the README
  • Keeping contextKey as a separate option since you also prefer that approach
  • Added status to defaultOnError
  • Added method and path to defaultOnResponse

Let me know if you have any other feedback.

@codecov
Copy link
Copy Markdown

codecov Bot commented Mar 4, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 91.76%. Comparing base (e762ac0) to head (b7e3161).
⚠️ Report is 47 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1782      +/-   ##
==========================================
+ Coverage   91.71%   91.76%   +0.05%     
==========================================
  Files         113      114       +1     
  Lines        3779     3803      +24     
  Branches      956      964       +8     
==========================================
+ Hits         3466     3490      +24     
  Misses        281      281              
  Partials       32       32              
Flag Coverage Δ
structured-logger 100.00% <100.00%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Comment thread packages/structured-logger/deno.json Outdated
Comment thread packages/structured-logger/package.json Outdated
Comment thread packages/structured-logger/README.md
@gabry-ts
Copy link
Copy Markdown
Contributor Author

@yusukebe Sorry for the late reply, I missed the notification.
Reverted the README change as requested in b7e3161.

Copy link
Copy Markdown
Member

@yusukebe yusukebe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@yusukebe
Copy link
Copy Markdown
Member

@gabry-ts

Great! Let's go with this. I'll merge and release an initial version. Thank you!

@yusukebe yusukebe merged commit 03c135b into honojs:main Apr 18, 2026
96 checks passed
@github-actions github-actions Bot mentioned this pull request Apr 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants