Table of contents
Open Table of contents
Intro
Next.js is great in many ways, but something that surprised me is the lack of request / response logs.
You start the Next.js server, it tells you everything is ready… and that’s it. Open a page, or make requests to the API if you have - nothing is logged!
This is all you will see. Not very helpful...
If you deploy Next.js in Vercel, that is fine as they offer Runtime Logs as part of the platform1.
But what if you are deploying somewhere else? Like to your own Kubernetes cluster? You need to see what is happening with requests and responses, preferably in a standardized JSON format so it can be ingested and analyzed easily.
Some approaches
The problem here is that Next.js uses its own internal server and doesn’t expose the request / response objects.
A custom server would allow us to access the requests and responses, but it comes with downsides like losing automatic optimization of static pages, and added complexity. This is overkill just to have decent logging!
Using a middleware looks like a good alternative at first, until you realize you can only log requests this way, not responses, as the middleware doesn’t have access to the response object.
Hacking Next.js for the logs
It turns out the log-it-all solution means patching the Next.js server itself to be able to access the request and response objects. Not ideal, but let’s try to make it as clean and sustainable as possible.
-
We will use patch-package 2 to modify
node_modules/next/dist/server/lib/start-server.js
automatically, even if we update Next.js. -
We will also use pino-http to do the actual logging in JSON with high performance.
1. Setup
In your package.json
file, add a postinstall
entry to scripts
:
Then install patch-package
as a dev dependency (as it’s only needed at package install time), and pino-http
as a regular dependency (as it will be used when the Next.js server is running).
2. Modify the Next.js server
Open node_modules/next/dist/server/lib/start-server.js
in your favorite editor.
First, add the initialization of pino-http
after _ispostpone
(on line 41 as of Next.js 14.1.2):
Now scroll further down, until you find the requestListener
method (around line 134). At long last, we have direct access to the request and response here!
Time to log them. Add a call to the pino-http
logger we just initialized, with the request and response.
The actual logging only happens after res.end
is called by the framework, so we will get the full, completed response in the logs always.
3. Save the patch
Run patch-package
on the next
package to create a patch file with the changes we just did.
From now own, the patch will be auto-applied on npm install
!
patch-package
will try to apply the patch even if the version of Next.js changed, and in most cases it will succeed. But if it fails because of large changes in Next.js, now you know where to look at to fix it again :).
4. Enjoy proper logs
Start the Next.js server with next dev
or next start
and behold the glorious, detailed, JSON-formatted logs for requests and responses!
BONUS
Skipping some requests
It might be useful to skip some requests from being logged, like for static assets, otherwise the logs can become very noisy.
For this, we can use a regular expression before calling the logger:
Pretty print logs for development
The default JSON format of pino-http
is minified so logs take as little space as possible. But they are hard to read in development.
We can use pino-pretty for a more friendly output:
Much nicer!
Example repo
You can check a working version of Next.js with full logging in this repo:
https://github.com/tomups/nextjs-pino-http
Feel free to copy the patch code to your codebase to save you from having to edit start-server.js
yourself.