pinojs / pino-pretty

🌲Basic prettifier for Pino log lines
MIT License
1.27k stars 150 forks source link

Programmatic Integration - Production Mode #293

Closed laurenzhonauer closed 1 year ago

laurenzhonauer commented 2 years ago

Description

In the Readme under Programmatic Integration it is stated that pino-pretty should be added as dev-dependency and then be configured as following:

const pino = require('pino')
const logger = pino({
  transport: {
    target: 'pino-pretty'
  },
})

logger.info('hi')

If one would follow those steps exactly, they would get following error in Production.

Error: Cannot find module 'pino-pretty'
Require stack:
- /workspace/.next/server/chunks/617.js
    at Function.Module._resolveFilename (node:internal/modules/cjs/loader:933:15)
    at Function.mod._resolveFilename (/workspace/node_modules/next/dist/build/webpack/require-hook.js:171:28)
    at Function.resolve (node:internal/modules/cjs/helpers:100:19)
    at fixTarget (/workspace/node_modules/pino/lib/transport.js:128:30)
    at transport (/workspace/node_modules/pino/lib/transport.js:111:22)
    at normalizeArgs (/workspace/node_modules/pino/lib/tools.js:409:16)
    at pino (/workspace/node_modules/pino/pino.js:84:28)
    at Object.4724 (/workspace/.next/server/chunks/617.js:114:59)
    at __webpack_require__ (/workspace/.next/server/webpack-api-runtime.js:25:42)
    at Object.73 (/workspace/.next/server/chunks/617.js:17:65) {
  code: 'MODULE_NOT_FOUND',
  requireStack: [
    '/workspace/.next/server/chunks/617.js'
  ]
}

Steps to reproduce

  1. Create some node program that can use pino
  2. Follow the programmatic integration guide
  3. set NODE_ENV to production (in Linux: export NODE_ENV=production)
  4. run npm ci
  5. run npm start

Expected Result

Pino should print un-prettified messages

Actual Result

Error is thrown due to missing pino-pretty (Because it is only a dev dependency)

Proposal

I guess the Documentation just needs to say that the transport option should only be added after a NODE_ENV check. Maybe its enough to just add that to the Readme. If you accept that i can create a pr myself :)

mcollina commented 2 years ago

Don't use NODE_ENV, it's an antipattern that results in production issues and it makes it harder to debug things. We do not recommend that for this reason. The best way to handle this is to use dotenv and have PINO_PRETTY env to trigger this on and off.

laurenzhonauer commented 2 years ago

Yes I agree with you. But I think this is not mentioned in the documentation.

So if you create blank node program, add pino and follow the programmatic integration documentation for pino-pretty, you will get the error automatically. With blank programm i mean, no extra dependencies, so no dotenv (I assume you mean this).

Also I would argue that the majority of people are not using dotenv and are relying on NODE_ENV - antipattern or not. (For example Next.js relies on NODE_ENV out of the box)

Anyway, could you provide a piece of example code that connects the dots between pino-pretty and dotenv. That would be very helpful :)

mcollina commented 2 years ago

Unfortuantely I do not have much time. Some piece of docs around this would be great, feel free to send a PR but do not mention NODE_ENV.

laurenzhonauer commented 2 years ago

Fair enough, in that case even bigger thanks for taking the time to look over the issue.

I will try to find some time to create a pr for this.

In case anyone stumbles upon this, before a pr is open - Feel free ^^

etiennejcharles commented 1 year ago

Thanks @laurenzhonauer for the info - if I had not stumbled upon this probably wouldn't have been able to fix my own issue

sinisastanic commented 1 year ago

just for anyone reading:

import pino from "pino";

import { env } from "@/env.mjs";

const levelFormatter = (_: unknown, number: number): { severity: string } => {
  let severity;
  switch (number) {
    case 10:
    case 20: {
      severity = "DEBUG";
      break;
    }

    case 30: {
      severity = "INFO";
      break;
    }

    case 40: {
      severity = "WARNING";
      break;
    }

    case 50: {
      severity = "ERROR";
      break;
    }

    case 60: {
      severity = "CRITICAL";
      break;
    }

    default: {
      severity = "DEFAULT";
      break;
    }
  }

  return { severity };
};

const timestampFunction = (): string =>
  `,"timestamp":"${new Date().toISOString()}"`;

const prodOptions: pino.LoggerOptions = {
  base: undefined,
  formatters: {
    level: levelFormatter,
  },
  messageKey: "message",
  timestamp: timestampFunction,
};

const getDevStream = (): pino.DestinationStream => {
  // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-var-requires
  const pretty = require("pino-pretty");

  // eslint-disable-next-line @typescript-eslint/no-unsafe-call
  return pretty({
    levelFirst: true,
    colorize: true,
    ignore: "time,hostname,pid",
  }) as pino.DestinationStream;
};

export const logger =
  env.NODE_ENV === "production"
    ? pino(prodOptions)
    : pino({ level: "debug" }, getDevStream());