next-logger: next standalone breaks next-logger

In next 12.1, they added support for standalone output, which outputs a minimal standalone folder with everything you need to run the server.

This naturally causes some issues with how next-logger works.

The first issue is that next-logger is omitted from .next/standalone/node_modules. This is pretty hard for next-logger to anything with. But a “hack” is to copy the library over to the standalone output, e.g:

cp -r node_modules/next-logger .next/standalone/node_modules/next-logger

This works fine.

The bigger issue however, is that using the standalone output seems to break how next-logger patches the internal next logger.

Example when running the standalone server (the JSON-output is my own application logging):

NODE_ENV=production NODE_OPTIONS='-r next-logger/presets/next-only' node server.js
{"level":"info","pid":236240,"hostname":"karlx1","name":"next.js","prefix":"info","msg":"Loaded env from /<snip>/.next/standalone/.env.production"}
Listening on port 3000

About this issue

  • Original URL
  • State: open
  • Created 2 years ago
  • Reactions: 6
  • Comments: 26

Most upvoted comments

I got this working in app dir as well now.

in your root layout.tsx: import 'next'logger'

☝️ This will make sure it’s part of the standalone output.

Here’s the crux, in your next.config.js add this:

experimental: {
   serverComponentsExternalPackages: ['next-logger'],
}

Then next will leave it alone, and simply copy it over to the output node_modules. 👍 Happy logging.

Using @karl-run’s solution, I was never able to patch logging unless I specifically re-built a page in dev mode. For my use case, I found a pretty elegant solution for those who are willing to let the first few logs be in a non-JSON format. The following works on Next 13.5.4:

// next.config.js

const nextConfig = {
    // [...]
    experimental: {
      instrumentationHook: true
    }
}

Then, create a file under src (or in the project root) named instrumentation.js (or instrumentation.ts if you wish)

// src/instrumentation.js

export async function register() {
    // Make sure that we don't attempt to patch in the edge runtime
    if (process.env.NEXT_RUNTIME === 'nodejs') {
        await require('pino')
        await require('next-logger')
    }
}

This way, there’s also no need to manually specify modules in next.config.js (and in my opinion allows for a marginally higher level of SoC)

I migrated a few of my apps to using the standalone output now. What I ended up doing:

In the custom document (_document.tsx)

import 'next-logger'

This will allow the output file tracing process to include next-logger in the standalone output.

And my Dockerfile looks something like this:

FROM gcr.io/distroless/nodejs:18

WORKDIR /app

COPY package.json /app/
COPY next-logger.config.js /app/
COPY .next/standalone /app/
COPY public /app/public/

EXPOSE 3000

ENV NODE_ENV=production
ENV NODE_OPTIONS '-r next-logger'

CMD ["server.js"]

Although, goes without saying, interacting over nextLogger.prefixes every call is expensive

To clarify, next-logger at present only does this iteration once, when the module is loaded.