Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Winston as Custom Logger #33

Merged
merged 7 commits into from
Jun 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 39 additions & 30 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ JSON logging patcher for Next.js

This is a library to patch the logging functions used by [Next.js](https://nextjs.org/), to have them output to `stdout` as newline-delimited JSON. This allows a Next.js application to log service events in a format that's compatible with log aggregators, without needing a custom Next.js server.

This works by importing Next.js' inbuilt [logger](https://github.com/vercel/next.js/blob/canary/packages/next/build/output/log.ts) via `require`, and replacing the logging methods with custom ones. It uses [`pino`](https://github.com/pinojs/pino) to output JSON formatted logs, preserving Next.js' message and prefix, but adding timestamp, hostname and more.
This works by importing Next.js' inbuilt [logger](https://github.com/vercel/next.js/blob/canary/packages/next/build/output/log.ts) via `require`, and replacing the logging methods with custom ones. It uses [`pino`](https://github.com/pinojs/pino) to output JSON formatted logs, preserving Next.js' message and prefix, but adding timestamp, hostname and more. Although the library was mainly developed based on `pino`, it also supports [`winston`](https://github.com/winstonjs/winston) as the logger backend. See the [Custom Logger](#custom-logger) section below for more details.

From v2.0.0 onwards, this library also patches the global `console` methods, to catch additional logs that Next.js makes directly to `console`. While `pino` logging remains intact, this may cause issues with other libraries which patch or use `console` methods. Use the `next-only` preset to opt-out of this patching.

Expand All @@ -30,36 +30,40 @@ After:

## Usage

First, install this package. You can do this with whatever Node package manager you're using in your project.
First, install this package and `pino`. You can do this with whatever Node package manager you're using in your project.

```sh
npm install next-logger
npm install next-logger pino

# or for Yarn

yarn add next-logger
yarn add next-logger pino
```

Then add a [`NODE_OPTIONS`](https://nextjs.org/docs/api-reference/cli) string to your Next.js start script, to require in the logger.

```sh
NODE_OPTIONS='-r next-logger' next start
```

### Adding to `package.json` Scripts

You can add this directly to your `package.json` scripts, to make it easier to start your service.

```json
"scripts": {
"start": "NODE_OPTIONS='-r next-logger' next start",
// ...your other scripts
},
```
Then use the Next [Instrumentation](https://nextjs.org/docs/app/building-your-application/optimizing/instrumentation) hook to load this library.

- Create `instrumentation.ts|js` file in the root directory of your project (or inside the src folder if using one)
```js
export async function register() {
if (process.env.NEXT_RUNTIME === 'nodejs') {
await require('pino')
await require('next-logger')
}
}
```
- Enable the instrumentation hook in `next.config.js`
```js
const nextConfig = {
// [...]
experimental: {
instrumentationHook: true,
},
}
```

### Presets

To support opting out of some patches, this library supports "presets". These can be used as above, with `/presets/<PRESET_NAME>` appended, for example: `NODE_OPTIONS='-r next-logger/presets/next-only'`.
To support opting out of some patches, this library supports "presets". These can be used as above, with `/presets/<PRESET_NAME>` appended, for example: `await require("next-logger/presets/next-only")`.

The following presets are supported:

Expand Down Expand Up @@ -92,19 +96,24 @@ module.exports = {
}
```

Or with [`winston`](https://github.com/winstonjs/winston):
Or with `winston`:

```sh
npm install winston
```

```js
const { createLogger, format, transports } = require('winston')

const logger = createLogger({
transports: [
new transports.Console({
handleExceptions: true,
format: format.combine(format.colorize(), format.simple()),
}),
],
})
const logger = defaultConfig =>
createLogger({
transports: [
new transports.Console({
handleExceptions: true,
format: format.json(),
}),
],
})

module.exports = {
logger,
Expand Down
4 changes: 2 additions & 2 deletions lib/patches/console.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const logger = require('../logger')

const getPinoMethod = consoleMethod => {
const getLogMethod = consoleMethod => {
const childLogger = logger.child({ name: 'console' })

switch (consoleMethod) {
Expand All @@ -20,5 +20,5 @@ const getPinoMethod = consoleMethod => {
const consoleMethods = ['log', 'debug', 'info', 'warn', 'error']
consoleMethods.forEach(method => {
// eslint-disable-next-line no-console
console[method] = getPinoMethod(method)
console[method] = getLogMethod(method)
})
10 changes: 7 additions & 3 deletions lib/patches/next.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ const nextLogger = require('next/dist/build/output/log')

const logger = require('../logger')

const getPinoMethod = nextMethod => {
const getLogMethod = nextMethod => {
const childLogger = logger.child({ name: 'next.js', prefix: nextMethod })

switch (nextMethod) {
Expand All @@ -11,7 +11,11 @@ const getPinoMethod = nextMethod => {
case 'warn':
return childLogger.warn.bind(childLogger)
case 'trace':
return childLogger.trace.bind(childLogger)
if ('trace' in childLogger) {
return childLogger.trace.bind(childLogger)
}
// To support Winston which doesn't have logger.trace()
return childLogger.debug.bind(childLogger)
default:
return childLogger.info.bind(childLogger)
}
Expand All @@ -25,5 +29,5 @@ const cacheObject = require.cache[cachePath]
cacheObject.exports = { ...cacheObject.exports }

Object.keys(nextLogger.prefixes).forEach(method => {
Object.defineProperty(cacheObject.exports, method, { value: getPinoMethod(method) })
Object.defineProperty(cacheObject.exports, method, { value: getLogMethod(method) })
})
Loading
Loading