123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168 |
- 'use strict'
- const { log } = require('proc-log')
- const { format } = require('util')
- // helper to emit log messages with a predefined prefix
- const withPrefix = (prefix) => log.LEVELS.reduce((acc, level) => {
- acc[level] = (...args) => log[level](prefix, ...args)
- return acc
- }, {})
- // very basic ansi color generator
- const COLORS = {
- wrap: (str, colors) => {
- const codes = colors.filter(c => typeof c === 'number')
- return `\x1b[${codes.join(';')}m${str}\x1b[0m`
- },
- inverse: 7,
- fg: {
- black: 30,
- red: 31,
- green: 32,
- yellow: 33,
- blue: 34,
- magenta: 35,
- cyan: 36,
- white: 37
- },
- bg: {
- black: 40,
- red: 41,
- green: 42,
- yellow: 43,
- blue: 44,
- magenta: 45,
- cyan: 46,
- white: 47
- }
- }
- class Logger {
- #buffer = []
- #paused = null
- #level = null
- #stream = null
- // ordered from loudest to quietest
- #levels = [{
- id: 'silly',
- display: 'sill',
- style: { inverse: true }
- }, {
- id: 'verbose',
- display: 'verb',
- style: { fg: 'cyan', bg: 'black' }
- }, {
- id: 'info',
- style: { fg: 'green' }
- }, {
- id: 'http',
- style: { fg: 'green', bg: 'black' }
- }, {
- id: 'notice',
- style: { fg: 'cyan', bg: 'black' }
- }, {
- id: 'warn',
- display: 'WARN',
- style: { fg: 'black', bg: 'yellow' }
- }, {
- id: 'error',
- display: 'ERR!',
- style: { fg: 'red', bg: 'black' }
- }]
- constructor (stream) {
- process.on('log', (...args) => this.#onLog(...args))
- this.#levels = new Map(this.#levels.map((level, index) => [level.id, { ...level, index }]))
- this.level = 'info'
- this.stream = stream
- log.pause()
- }
- get stream () {
- return this.#stream
- }
- set stream (stream) {
- this.#stream = stream
- }
- get level () {
- return this.#levels.get(this.#level) ?? null
- }
- set level (level) {
- this.#level = this.#levels.get(level)?.id ?? null
- }
- isVisible (level) {
- return this.level?.index <= this.#levels.get(level)?.index ?? -1
- }
- #onLog (...args) {
- const [level] = args
- if (level === 'pause') {
- this.#paused = true
- return
- }
- if (level === 'resume') {
- this.#paused = false
- this.#buffer.forEach((b) => this.#log(...b))
- this.#buffer.length = 0
- return
- }
- if (this.#paused) {
- this.#buffer.push(args)
- return
- }
- this.#log(...args)
- }
- #color (str, { fg, bg, inverse }) {
- if (!this.#stream?.isTTY) {
- return str
- }
- return COLORS.wrap(str, [
- COLORS.fg[fg],
- COLORS.bg[bg],
- inverse && COLORS.inverse
- ])
- }
- #log (levelId, msgPrefix, ...args) {
- if (!this.isVisible(levelId) || typeof this.#stream?.write !== 'function') {
- return
- }
- const level = this.#levels.get(levelId)
- const prefixParts = [
- this.#color('gyp', { fg: 'white', bg: 'black' }),
- this.#color(level.display ?? level.id, level.style)
- ]
- if (msgPrefix) {
- prefixParts.push(this.#color(msgPrefix, { fg: 'magenta' }))
- }
- const prefix = prefixParts.join(' ').trim() + ' '
- const lines = format(...args).split(/\r?\n/).map(l => prefix + l.trim())
- this.#stream.write(lines.join('\n') + '\n')
- }
- }
- // used to suppress logs in tests
- const NULL_LOGGER = !!process.env.NODE_GYP_NULL_LOGGER
- module.exports = {
- logger: new Logger(NULL_LOGGER ? null : process.stderr),
- stdout: NULL_LOGGER ? () => {} : (...args) => console.log(...args),
- withPrefix,
- ...log
- }
|