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

Provide solution for creating custom loggers easily #1100

Closed
kibertoad opened this issue Aug 26, 2021 · 13 comments
Closed

Provide solution for creating custom loggers easily #1100

kibertoad opened this issue Aug 26, 2021 · 13 comments
Labels

Comments

@kibertoad
Copy link
Contributor

kibertoad commented Aug 26, 2021

Judging by https://github.com/fastify/fastify/blob/main/test/types/logger.test-d.ts, the following used to be OK in pino 6:

class CustomLoggerImpl implements CustomLogger {
  customMethod (msg: string, ...args: unknown[]) { console.log(msg, args) }

  // Implementation signature must be compatible with all overloads of FastifyLogFn
  info (arg1: unknown, arg2?: unknown, ...args: unknown[]): void {
    console.log(arg1, arg2, ...args)
  }

  warn (...args: unknown[]) { console.log(args) }
  error (...args: unknown[]) { console.log(args) }
  fatal (...args: unknown[]) { console.log(args) }
  trace (...args: unknown[]) { console.log(args) }
  debug (...args: unknown[]) { console.log(args) }
  child () { return new CustomLoggerImpl() }
}

const customLogger = new CustomLoggerImpl()

However, our Logger interface relies on having such things as

        level: LevelWithSilent | string;
        /**
         * Returns the integer value for the logger instance's logging level.
         */
        levelVal: number;

which rely on internal getters. We probably don't want our users to implement all that from a scratch, so I wonder if we should expose some kind of a class they could extend for their custom loggers, or at least a prototype they could inherit from.

Alternatively we can officially embrace minimal subset of logger fields for custom loggers, as originally expected by fastify and split within Pino in #1099

@kibertoad kibertoad added the v7 label Aug 26, 2021
@mcollina
Copy link
Member

pino relies on prototypical inheritance for implementing the child loggers, so we can't provide a prototype to extend.

Maybe an interface?

@kibertoad
Copy link
Contributor Author

Interface we already have, but it cannot provide any logic by itself, only types. And what I'm trying to say is that only having interface forces users to implement way too much internal stuff on their own. If BaseLogger as defined in #1099 is ok, though, this might be a non-issue, as that provides a more sensible surface for users to cover.

@mcollina
Copy link
Member

Maybe add a test for this use case?

@jsumners
Copy link
Member

Wrapping a Pino instance is really not that difficult. There shouldn't be any need to mutate the prototype.

@replete
Copy link

replete commented Oct 30, 2021

I ditched prettyprint and wrote this minimal transformer to output colorised terminal output, with a short timestamp. It could be extended easiy with the options object.

logger.js

import pino from 'pino'

const log = pino({
	transport: {
		pipeline: [
			{
				// logger output for terminal, colorised by level for easy reading
				// https://gist.github.com/replete/3220dd280c60279cee5396c6f65b02dd
				target: './logger.colorisedTerminalTransform.mjs',
				options: {},
			},
		],
	},
})

export default log

logger.colorisedTerminalTransform.mjs (source in this gist)

import { Writable } from 'stream'

export default (options) => {
	const levels = {
		// https://github.com/pinojs/pino/blob/master/docs/api.md#logger-level
		10: `\x1b[2m[>]\x1b[0m`, //trace
		20: `\x1b[35m[DEBUG]\x1b[0m`, //debug
		30: `\x1b[36m`, //info
		40: `\x1b[33m[?]`, //warn
		50: `\x1b[31m[!]`, //error
		60: `\x1b[41m[!!!]`, //fatal
		Infinity: ``,
	}

	const colorizedLoggerTransportStream = new Writable({
		write(chunk, enc, cb) {
			const logEvents = chunk.toString().match(/[^\r\n]+/g) // split into lines
			logEvents.forEach((logEvent) => {
				const { level, time, msg } = JSON.parse(logEvent)
				const timestamp = new Date(time).toISOString().slice(11).substring(8, 0)
				console.log(`\x1b[2m${timestamp}\x1b[0m ${levels[level] || ''} ${msg}\x1b[0m`)
			})
			cb()
		},
	})
	return colorizedLoggerTransportStream
}

@mcollina
Copy link
Member

Don't use console.log, use pino.destination() instead (otherwise you loose all the benefit of worker_threads).

@replete
Copy link

replete commented Oct 30, 2021

Don't use console.log, use pino.destination() instead (otherwise you loose all the benefit of worker_threads).
Screen Shot 2021-10-30 at 16 07 24
The documentation gave an example using console.log

The 'transport' implementation I wrote above sometimes misses errors, which has been pretty frustrating given I only wrote this transformer because I got a 'pino-pretty is deprecated' type warnings.

What I want to do seems like a typical use-case to me, could you please recommend how I approach this?

This is what I want to do:

  • In 'development environment:
    • Write transformed (human-readable, colors, short timestamp, json expansion etc) output to stdout
  • In 'production' environment:
    • write raw json to stdout
    • write raw json to a logfile
    • maybe pipe raw json to some third-party logging service
  • In 'test' environment:
    • Write transformed (human-readable, colors, short timestamp, json expansion etc) output to stdout
    • write raw json to a logfile
  • Anywhere:
    • write logfile contents to stdout with the same human-readable transformer used in 'development'

@mcollina
Copy link
Member

mcollina commented Oct 30, 2021

pino-pretty is not deprecated, only the prettyPrint option.

The following would do what you want:

const pino = require('pino')
const transport = pino.transport({
  target: 'pino-pretty',
  options: { destination: 1 } // use 2 for stderr
})
pino(transport)

@jlenon7
Copy link

jlenon7 commented Sep 9, 2022

Don't use console.log, use pino.destination() instead (otherwise you loose all the benefit of worker_threads).

But how I could use pino.destination() to show the log in the stdout of the application?

@mcollina
Copy link
Member

mcollina commented Sep 9, 2022

I don't understand the question, could you clarify?

@jlenon7
Copy link

jlenon7 commented Sep 13, 2022

Please excuse my ignorance @mcollina

I was trying to create a custom transporter which would format the log similarly to pino-pretty. Then this formatted log would be transport to the stdout of the application and then to a Telegram ChatId.

The problem I was facing was: How to send the log to stdout but without using the console.log()?

It wasn't so clear to me that to send the log to stdout or stderr I should pass 1 or 2 as destination to SonicBoom destination. I thought SonicBoom should only be used to transport logs to files. But now I got it. 😅

@mcollina
Copy link
Member

fd: 1 is standard output. fd: 2 is standard error.

@github-actions
Copy link

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Mar 20, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

5 participants