Projektstart

This commit is contained in:
2026-01-22 15:49:12 +01:00
parent 7212eb6f7a
commit 57e5f652f8
10637 changed files with 2598792 additions and 64 deletions

1088
backend/node_modules/pino-pretty/test/basic.test.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

255
backend/node_modules/pino-pretty/test/cli-rc.test.js generated vendored Normal file
View File

@@ -0,0 +1,255 @@
'use strict'
process.env.TZ = 'UTC'
const path = require('path')
const spawn = require('child_process').spawn
const test = require('tap').test
const fs = require('fs')
const rimraf = require('rimraf')
const bin = require.resolve('../bin')
const logLine = '{"level":30,"time":1522431328992,"msg":"hello world","pid":42,"hostname":"foo"}\n'
const noop = () => {}
test('cli', (t) => {
const tmpDir = path.join(__dirname, '.tmp_' + Date.now())
fs.mkdirSync(tmpDir)
t.teardown(() => rimraf(tmpDir, noop))
t.test('loads and applies default config file: pino-pretty.config.js', (t) => {
t.plan(1)
// Set translateTime: true on run configuration
const configFile = path.join(tmpDir, 'pino-pretty.config.js')
fs.writeFileSync(configFile, 'module.exports = { translateTime: true }')
const env = { TERM: 'dumb', TZ: 'UTC' }
const child = spawn(process.argv[0], [bin], { env, cwd: tmpDir })
// Validate that the time has been translated
child.on('error', t.threw)
child.stdout.on('data', (data) => {
t.equal(data.toString(), '[17:35:28.992] INFO (42): hello world\n')
})
child.stdin.write(logLine)
t.teardown(() => {
fs.unlinkSync(configFile)
child.kill()
})
})
t.test('loads and applies default config file: pino-pretty.config.cjs', (t) => {
t.plan(1)
// Set translateTime: true on run configuration
const configFile = path.join(tmpDir, 'pino-pretty.config.cjs')
fs.writeFileSync(configFile, 'module.exports = { translateTime: true }')
// Tell the loader to expect ESM modules
const packageJsonFile = path.join(tmpDir, 'package.json')
fs.writeFileSync(packageJsonFile, JSON.stringify({ type: 'module' }, null, 4))
const env = { TERM: ' dumb', TZ: 'UTC' }
const child = spawn(process.argv[0], [bin], { env, cwd: tmpDir })
// Validate that the time has been translated
child.on('error', t.threw)
child.stdout.on('data', (data) => {
t.equal(data.toString(), '[17:35:28.992] INFO (42): hello world\n')
})
child.stdin.write(logLine)
t.teardown(() => {
fs.unlinkSync(configFile)
fs.unlinkSync(packageJsonFile)
child.kill()
})
})
t.test('loads and applies default config file: .pino-prettyrc', (t) => {
t.plan(1)
// Set translateTime: true on run configuration
const configFile = path.join(tmpDir, '.pino-prettyrc')
fs.writeFileSync(configFile, JSON.stringify({ translateTime: true }, null, 4))
const env = { TERM: ' dumb', TZ: 'UTC' }
const child = spawn(process.argv[0], [bin], { env, cwd: tmpDir })
// Validate that the time has been translated
child.on('error', t.threw)
child.stdout.on('data', (data) => {
t.equal(data.toString(), '[17:35:28.992] INFO (42): hello world\n')
})
child.stdin.write(logLine)
t.teardown(() => {
fs.unlinkSync(configFile)
child.kill()
})
})
t.test('loads and applies default config file: .pino-prettyrc.json', (t) => {
t.plan(1)
// Set translateTime: true on run configuration
const configFile = path.join(tmpDir, '.pino-prettyrc.json')
fs.writeFileSync(configFile, JSON.stringify({ translateTime: true }, null, 4))
const env = { TERM: ' dumb', TZ: 'UTC' }
const child = spawn(process.argv[0], [bin], { env, cwd: tmpDir })
// Validate that the time has been translated
child.on('error', t.threw)
child.stdout.on('data', (data) => {
t.equal(data.toString(), '[17:35:28.992] INFO (42): hello world\n')
})
child.stdin.write(logLine)
t.teardown(() => {
fs.unlinkSync(configFile)
child.kill()
})
})
t.test('loads and applies custom config file: pino-pretty.config.test.json', (t) => {
t.plan(1)
// Set translateTime: true on run configuration
const configFile = path.join(tmpDir, 'pino-pretty.config.test.json')
fs.writeFileSync(configFile, JSON.stringify({ translateTime: true }, null, 4))
const env = { TERM: ' dumb', TZ: 'UTC' }
const child = spawn(process.argv[0], [bin, '--config', configFile], { env, cwd: tmpDir })
// Validate that the time has been translated
child.on('error', t.threw)
child.stdout.on('data', (data) => {
t.equal(data.toString(), '[17:35:28.992] INFO (42): hello world\n')
})
child.stdin.write(logLine)
t.teardown(() => child.kill())
})
t.test('loads and applies custom config file: pino-pretty.config.test.js', (t) => {
t.plan(1)
// Set translateTime: true on run configuration
const configFile = path.join(tmpDir, 'pino-pretty.config.test.js')
fs.writeFileSync(configFile, 'module.exports = { translateTime: true }')
const env = { TERM: ' dumb', TZ: 'UTC' }
const child = spawn(process.argv[0], [bin, '--config', configFile], { env, cwd: tmpDir })
// Validate that the time has been translated
child.on('error', t.threw)
child.stdout.on('data', (data) => {
t.equal(data.toString(), '[17:35:28.992] INFO (42): hello world\n')
})
child.stdin.write(logLine)
t.teardown(() => child.kill())
})
;['--messageKey', '-m'].forEach((optionName) => {
t.test(`cli options override config options via ${optionName}`, (t) => {
t.plan(1)
// Set translateTime: true on run configuration
const configFile = path.join(tmpDir, 'pino-pretty.config.js')
fs.writeFileSync(configFile, `
module.exports = {
translateTime: true,
messageKey: 'custom_msg'
}
`.trim())
// Set messageKey: 'new_msg' using command line option
const env = { TERM: ' dumb', TZ: 'UTC' }
const child = spawn(process.argv[0], [bin, optionName, 'new_msg'], { env, cwd: tmpDir })
// Validate that the time has been translated and correct message key has been used
child.on('error', t.threw)
child.stdout.on('data', (data) => {
t.equal(data.toString(), '[17:35:28.992] INFO (42): hello world\n')
})
child.stdin.write(logLine.replace(/"msg"/, '"new_msg"'))
t.teardown(() => {
fs.unlinkSync(configFile)
child.kill()
})
})
})
t.test('cli options with defaults can be overridden by config', (t) => {
t.plan(1)
// Set errorProps: '*' on run configuration
const configFile = path.join(tmpDir, 'pino-pretty.config.js')
fs.writeFileSync(configFile, `
module.exports = {
errorProps: '*'
}
`.trim())
// Set messageKey: 'new_msg' using command line option
const env = { TERM: ' dumb', TZ: 'UTC' }
const child = spawn(process.argv[0], [bin], { env, cwd: tmpDir })
// Validate that the time has been translated and correct message key has been used
child.on('error', t.threw)
child.stdout.on('data', (data) => {
t.equal(data.toString(), '[21:31:36.006] FATAL: There was an error starting the process.\n QueryError: Error during sql query: syntax error at or near SELECTT\n at /home/me/projects/example/sql.js\n at /home/me/projects/example/index.js\n querySql: SELECTT * FROM "test" WHERE id = $1;\n queryArgs: 12\n')
})
child.stdin.write('{"level":60,"time":1594416696006,"msg":"There was an error starting the process.","type":"Error","stack":"QueryError: Error during sql query: syntax error at or near SELECTT\\n at /home/me/projects/example/sql.js\\n at /home/me/projects/example/index.js","querySql":"SELECTT * FROM \\"test\\" WHERE id = $1;","queryArgs":[12]}\n')
t.teardown(() => {
fs.unlinkSync(configFile)
child.kill()
})
})
t.test('throws on missing config file', (t) => {
t.plan(2)
const args = [bin, '--config', 'pino-pretty.config.missing.json']
const env = { TERM: ' dumb', TZ: 'UTC' }
const child = spawn(process.argv[0], args, { env, cwd: tmpDir })
child.on('close', (code) => t.equal(code, 1))
child.stdout.pipe(process.stdout)
child.stderr.setEncoding('utf8')
let data = ''
child.stderr.on('data', (chunk) => {
data += chunk
})
child.on('close', function () {
t.match(
data.toString(), 'Error: Failed to load runtime configuration file: pino-pretty.config.missing.json')
})
t.teardown(() => child.kill())
})
t.test('throws on invalid default config file', (t) => {
t.plan(2)
const configFile = path.join(tmpDir, 'pino-pretty.config.js')
fs.writeFileSync(configFile, 'module.exports = () => {}')
const env = { TERM: ' dumb', TZ: 'UTC' }
const child = spawn(process.argv[0], [bin], { env, cwd: tmpDir })
child.on('close', (code) => t.equal(code, 1))
child.stdout.pipe(process.stdout)
child.stderr.setEncoding('utf8')
let data = ''
child.stderr.on('data', (chunk) => {
data += chunk
})
child.on('close', function () {
t.match(data, 'Error: Invalid runtime configuration file: pino-pretty.config.js')
})
t.teardown(() => child.kill())
})
t.test('throws on invalid custom config file', (t) => {
t.plan(2)
const configFile = path.join(tmpDir, 'pino-pretty.config.invalid.js')
fs.writeFileSync(configFile, 'module.exports = () => {}')
const args = [bin, '--config', path.relative(tmpDir, configFile)]
const env = { TERM: ' dumb', TZ: 'UTC' }
const child = spawn(process.argv[0], args, { env, cwd: tmpDir })
child.on('close', (code) => t.equal(code, 1))
child.stdout.pipe(process.stdout)
child.stderr.setEncoding('utf8')
let data = ''
child.stderr.on('data', (chunk) => {
data += chunk
})
child.on('close', function () {
t.match(data, 'Error: Invalid runtime configuration file: pino-pretty.config.invalid.js')
})
t.teardown(() => child.kill())
})
t.test('test help', (t) => {
t.plan(1)
const env = { TERM: ' dumb', TZ: 'UTC' }
const child = spawn(process.argv[0], [bin, '--help'], { env })
const file = fs.readFileSync('help/help.txt').toString()
child.on('error', t.threw)
child.stdout.on('data', (data) => {
t.equal(data.toString(), file)
})
t.teardown(() => child.kill())
})
t.end()
})

335
backend/node_modules/pino-pretty/test/cli.test.js generated vendored Normal file
View File

@@ -0,0 +1,335 @@
'use strict'
process.env.TZ = 'UTC'
const path = require('path')
const spawn = require('child_process').spawn
const test = require('tap').test
const bin = require.resolve(path.join(__dirname, '..', 'bin.js'))
const epoch = 1522431328992
const logLine = '{"level":30,"time":1522431328992,"msg":"hello world","pid":42,"hostname":"foo"}\n'
const env = { TERM: 'dumb', TZ: 'UTC' }
const formattedEpoch = '17:35:28.992'
test('cli', (t) => {
t.test('does basic reformatting', (t) => {
t.plan(1)
const child = spawn(process.argv[0], [bin], { env })
child.on('error', t.threw)
child.stdout.on('data', (data) => {
t.equal(data.toString(), `[${formattedEpoch}] INFO (42): hello world\n`)
})
child.stdin.write(logLine)
t.teardown(() => child.kill())
})
;['--levelFirst', '-l'].forEach((optionName) => {
t.test(`flips epoch and level via ${optionName}`, (t) => {
t.plan(1)
const child = spawn(process.argv[0], [bin, optionName], { env })
child.on('error', t.threw)
child.stdout.on('data', (data) => {
t.equal(data.toString(), `INFO [${formattedEpoch}] (42): hello world\n`)
})
child.stdin.write(logLine)
t.teardown(() => child.kill())
})
})
;['--translateTime', '-t'].forEach((optionName) => {
t.test(`translates time to default format via ${optionName}`, (t) => {
t.plan(1)
const child = spawn(process.argv[0], [bin, optionName], { env })
child.on('error', t.threw)
child.stdout.on('data', (data) => {
t.equal(data.toString(), `[${formattedEpoch}] INFO (42): hello world\n`)
})
child.stdin.write(logLine)
t.teardown(() => child.kill())
})
})
;['--ignore', '-i'].forEach((optionName) => {
t.test('does ignore multiple keys', (t) => {
t.plan(1)
const child = spawn(process.argv[0], [bin, optionName, 'pid,hostname'], { env })
child.on('error', t.threw)
child.stdout.on('data', (data) => {
t.equal(data.toString(), `[${formattedEpoch}] INFO: hello world\n`)
})
child.stdin.write(logLine)
t.teardown(() => child.kill())
})
})
;['--customLevels', '-x'].forEach((optionName) => {
t.test(`customize levels via ${optionName}`, (t) => {
t.plan(1)
const logLine = '{"level":1,"time":1522431328992,"msg":"hello world","pid":42,"hostname":"foo"}\n'
const child = spawn(process.argv[0], [bin, optionName, 'err:99,info:1'], { env })
child.on('error', t.threw)
child.stdout.on('data', (data) => {
t.equal(data.toString(), `[${formattedEpoch}] INFO (42): hello world\n`)
})
child.stdin.write(logLine)
t.teardown(() => child.kill())
})
t.test(`customize levels via ${optionName} without index`, (t) => {
t.plan(1)
const logLine = '{"level":1,"time":1522431328992,"msg":"hello world","pid":42,"hostname":"foo"}\n'
const child = spawn(process.argv[0], [bin, optionName, 'err:99,info'], { env })
child.on('error', t.threw)
child.stdout.on('data', (data) => {
t.equal(data.toString(), `[${formattedEpoch}] INFO (42): hello world\n`)
})
child.stdin.write(logLine)
t.teardown(() => child.kill())
})
t.test(`customize levels via ${optionName} with minimumLevel`, (t) => {
t.plan(1)
const child = spawn(process.argv[0], [bin, '--minimumLevel', 'err', optionName, 'err:99,info:1'], { env })
child.on('error', t.threw)
child.stdout.on('data', (data) => {
t.equal(data.toString(), `[${formattedEpoch}] ERR (42): hello world\n`)
})
child.stdin.write('{"level":1,"time":1522431328992,"msg":"hello world","pid":42,"hostname":"foo"}\n')
child.stdin.write('{"level":99,"time":1522431328992,"msg":"hello world","pid":42,"hostname":"foo"}\n')
t.teardown(() => child.kill())
})
t.test(`customize levels via ${optionName} with minimumLevel, customLevels and useOnlyCustomProps false`, (t) => {
t.plan(1)
const child = spawn(process.argv[0], [bin, '--minimumLevel', 'custom', '--useOnlyCustomProps', 'false', optionName, 'custom:99,info:1'], { env })
child.on('error', t.threw)
child.stdout.on('data', (data) => {
t.equal(data.toString(), `[${formattedEpoch}] CUSTOM (42): hello world\n`)
})
child.stdin.write('{"level":1,"time":1522431328992,"msg":"hello world","pid":42,"hostname":"foo"}\n')
child.stdin.write('{"level":99,"time":1522431328992,"msg":"hello world","pid":42,"hostname":"foo"}\n')
t.teardown(() => child.kill())
})
t.test(`customize levels via ${optionName} with minimumLevel, customLevels and useOnlyCustomProps true`, (t) => {
t.plan(1)
const child = spawn(process.argv[0], [bin, '--minimumLevel', 'custom', '--useOnlyCustomProps', 'true', optionName, 'custom:99,info:1'], { env })
child.on('error', t.threw)
child.stdout.on('data', (data) => {
t.equal(data.toString(), `[${formattedEpoch}] CUSTOM (42): hello world\n`)
})
child.stdin.write('{"level":1,"time":1522431328992,"msg":"hello world","pid":42,"hostname":"foo"}\n')
child.stdin.write('{"level":99,"time":1522431328992,"msg":"hello world","pid":42,"hostname":"foo"}\n')
t.teardown(() => child.kill())
})
})
;['--customColors', '-X'].forEach((optionName) => {
t.test(`customize levels via ${optionName}`, (t) => {
t.plan(1)
const child = spawn(process.argv[0], [bin, optionName, 'info:blue,message:red'], { env })
child.on('error', t.threw)
child.stdout.on('data', (data) => {
t.equal(data.toString(), `[${formattedEpoch}] INFO (42): hello world\n`)
})
child.stdin.write(logLine)
t.teardown(() => child.kill())
})
t.test(`customize levels via ${optionName} with customLevels`, (t) => {
t.plan(1)
const logLine = '{"level":1,"time":1522431328992,"msg":"hello world","pid":42,"hostname":"foo"}\n'
const child = spawn(process.argv[0], [bin, '--customLevels', 'err:99,info', optionName, 'info:blue,message:red'], { env })
child.on('error', t.threw)
child.stdout.on('data', (data) => {
t.equal(data.toString(), `[${formattedEpoch}] INFO (42): hello world\n`)
})
child.stdin.write(logLine)
t.teardown(() => child.kill())
})
})
;['--useOnlyCustomProps', '-U'].forEach((optionName) => {
t.test(`customize levels via ${optionName} false and customColors`, (t) => {
t.plan(1)
const child = spawn(process.argv[0], [bin, '--customColors', 'err:blue,info:red', optionName, 'false'], { env })
child.on('error', t.threw)
child.stdout.on('data', (data) => {
t.equal(data.toString(), `[${formattedEpoch}] INFO (42): hello world\n`)
})
child.stdin.write(logLine)
t.teardown(() => child.kill())
})
t.test(`customize levels via ${optionName} true and customColors`, (t) => {
t.plan(1)
const child = spawn(process.argv[0], [bin, '--customColors', 'err:blue,info:red', optionName, 'true'], { env })
child.on('error', t.threw)
child.stdout.on('data', (data) => {
t.equal(data.toString(), `[${formattedEpoch}] INFO (42): hello world\n`)
})
child.stdin.write(logLine)
t.teardown(() => child.kill())
})
t.test(`customize levels via ${optionName} true and customLevels`, (t) => {
t.plan(1)
const child = spawn(process.argv[0], [bin, '--customLevels', 'err:99,custom:30', optionName, 'true'], { env })
child.on('error', t.threw)
child.stdout.on('data', (data) => {
t.equal(data.toString(), `[${formattedEpoch}] CUSTOM (42): hello world\n`)
})
child.stdin.write(logLine)
t.teardown(() => child.kill())
})
t.test(`customize levels via ${optionName} true and no customLevels`, (t) => {
t.plan(1)
const child = spawn(process.argv[0], [bin, optionName, 'true'], { env })
child.on('error', t.threw)
child.stdout.on('data', (data) => {
t.equal(data.toString(), `[${formattedEpoch}] INFO (42): hello world\n`)
})
child.stdin.write(logLine)
t.teardown(() => child.kill())
})
t.test(`customize levels via ${optionName} false and customLevels`, (t) => {
t.plan(1)
const child = spawn(process.argv[0], [bin, '--customLevels', 'err:99,custom:25', optionName, 'false'], { env })
child.on('error', t.threw)
child.stdout.on('data', (data) => {
t.equal(data.toString(), `[${formattedEpoch}] INFO (42): hello world\n`)
})
child.stdin.write(logLine)
t.teardown(() => child.kill())
})
t.test(`customize levels via ${optionName} false and no customLevels`, (t) => {
t.plan(1)
const child = spawn(process.argv[0], [bin, optionName, 'false'], { env })
child.on('error', t.threw)
child.stdout.on('data', (data) => {
t.equal(data.toString(), `[${formattedEpoch}] INFO (42): hello world\n`)
})
child.stdin.write(logLine)
t.teardown(() => child.kill())
})
})
t.test('does ignore escaped keys', (t) => {
t.plan(1)
const child = spawn(process.argv[0], [bin, '-i', 'log\\.domain\\.corp/foo'], { env })
child.on('error', t.threw)
child.stdout.on('data', (data) => {
t.equal(data.toString(), `[${formattedEpoch}] INFO: hello world\n`)
})
const logLine = '{"level":30,"time":1522431328992,"msg":"hello world","log.domain.corp/foo":"bar"}\n'
child.stdin.write(logLine)
t.teardown(() => child.kill())
})
t.test('passes through stringified date as string', (t) => {
t.plan(1)
const child = spawn(process.argv[0], [bin], { env })
child.on('error', t.threw)
const date = JSON.stringify(new Date(epoch))
child.stdout.on('data', (data) => {
t.equal(data.toString(), date + '\n')
})
child.stdin.write(date)
child.stdin.write('\n')
t.teardown(() => child.kill())
})
t.test('end stdin does not end the destination', (t) => {
t.plan(2)
const child = spawn(process.argv[0], [bin], { env })
child.on('error', t.threw)
child.stdout.on('data', (data) => {
t.equal(data.toString(), 'aaa\n')
})
child.stdin.end('aaa\n')
child.on('exit', function (code) {
t.equal(code, 0)
})
t.teardown(() => child.kill())
})
;['--timestampKey', '-a'].forEach((optionName) => {
t.test(`uses specified timestamp key via ${optionName}`, (t) => {
t.plan(1)
const child = spawn(process.argv[0], [bin, optionName, '@timestamp'], { env })
child.on('error', t.threw)
child.stdout.on('data', (data) => {
t.equal(data.toString(), `[${formattedEpoch}] INFO: hello world\n`)
})
const logLine = '{"level":30,"@timestamp":1522431328992,"msg":"hello world"}\n'
child.stdin.write(logLine)
t.teardown(() => child.kill())
})
})
;['--singleLine', '-S'].forEach((optionName) => {
t.test(`singleLine=true via ${optionName}`, (t) => {
t.plan(1)
const logLineWithExtra = JSON.stringify(Object.assign(JSON.parse(logLine), {
extra: {
foo: 'bar',
number: 42
}
})) + '\n'
const child = spawn(process.argv[0], [bin, optionName], { env })
child.on('error', t.threw)
child.stdout.on('data', (data) => {
t.equal(data.toString(), `[${formattedEpoch}] INFO (42): hello world {"extra":{"foo":"bar","number":42}}\n`)
})
child.stdin.write(logLineWithExtra)
t.teardown(() => child.kill())
})
})
t.test('does ignore nested keys', (t) => {
t.plan(1)
const logLineNested = JSON.stringify(Object.assign(JSON.parse(logLine), {
extra: {
foo: 'bar',
number: 42,
nested: {
foo2: 'bar2'
}
}
})) + '\n'
const child = spawn(process.argv[0], [bin, '-S', '-i', 'extra.foo,extra.nested,extra.nested.miss'], { env })
child.on('error', t.threw)
child.stdout.on('data', (data) => {
t.equal(data.toString(), `[${formattedEpoch}] INFO (42 on foo): hello world {"extra":{"number":42}}\n`)
})
child.stdin.write(logLineNested)
t.teardown(() => child.kill())
})
t.test('change TZ', (t) => {
t.plan(1)
const child = spawn(process.argv[0], [bin], { env: { ...env, TZ: 'Europe/Amsterdam' } })
child.on('error', t.threw)
child.stdout.on('data', (data) => {
t.equal(data.toString(), '[19:35:28.992] INFO (42): hello world\n')
})
child.stdin.write(logLine)
t.teardown(() => child.kill())
})
t.end()
})

35
backend/node_modules/pino-pretty/test/crlf.test.js generated vendored Normal file
View File

@@ -0,0 +1,35 @@
'use strict'
process.env.TZ = 'UTC'
const test = require('tap').test
const _prettyFactory = require('../').prettyFactory
function prettyFactory (opts) {
if (!opts) {
opts = { colorize: false }
} else if (!Object.prototype.hasOwnProperty.call(opts, 'colorize')) {
opts.colorize = false
}
return _prettyFactory(opts)
}
const logLine = '{"level":30,"time":1522431328992,"msg":"hello world","pid":42,"hostname":"foo"}\n'
test('crlf', (t) => {
t.test('uses LF by default', (t) => {
t.plan(1)
const pretty = prettyFactory()
const formatted = pretty(logLine)
t.equal(formatted.substr(-2), 'd\n')
})
t.test('can use CRLF', (t) => {
t.plan(1)
const pretty = prettyFactory({ crlf: true })
const formatted = pretty(logLine)
t.equal(formatted.substr(-3), 'd\r\n')
})
t.end()
})

View File

@@ -0,0 +1,454 @@
'use strict'
process.env.TZ = 'UTC'
const Writable = require('stream').Writable
const test = require('tap').test
const pino = require('pino')
const serializers = pino.stdSerializers
const _prettyFactory = require('../').prettyFactory
function prettyFactory (opts) {
if (!opts) {
opts = { colorize: false }
} else if (!Object.prototype.hasOwnProperty.call(opts, 'colorize')) {
opts.colorize = false
}
return _prettyFactory(opts)
}
// All dates are computed from 'Fri, 30 Mar 2018 17:35:28 GMT'
const epoch = 1522431328992
const formattedEpoch = '17:35:28.992'
const pid = process.pid
test('error like objects tests', (t) => {
t.beforeEach(() => {
Date.originalNow = Date.now
Date.now = () => epoch
})
t.afterEach(() => {
Date.now = Date.originalNow
delete Date.originalNow
})
t.test('pino transform prettifies Error', (t) => {
t.plan(2)
const pretty = prettyFactory()
const err = Error('hello world')
const expected = err.stack.split('\n')
expected.unshift(err.message)
const log = pino({}, new Writable({
write (chunk, enc, cb) {
const formatted = pretty(chunk.toString())
const lines = formatted.split('\n')
t.equal(lines.length, expected.length + 6)
t.equal(lines[0], `[${formattedEpoch}] INFO (${pid}): hello world`)
cb()
}
}))
log.info(err)
})
t.test('errorProps recognizes user specified properties', (t) => {
t.plan(3)
const pretty = prettyFactory({ errorProps: 'statusCode,originalStack' })
const log = pino({}, new Writable({
write (chunk, enc, cb) {
const formatted = pretty(chunk.toString())
t.match(formatted, /\s{4}error stack/)
t.match(formatted, /"statusCode": 500/)
t.match(formatted, /"originalStack": "original stack"/)
cb()
}
}))
const error = Error('error message')
error.stack = 'error stack'
error.statusCode = 500
error.originalStack = 'original stack'
log.error(error)
})
t.test('prettifies ignores undefined errorLikeObject', (t) => {
const pretty = prettyFactory()
pretty({ err: undefined })
pretty({ error: undefined })
t.end()
})
t.test('prettifies Error in property within errorLikeObjectKeys', (t) => {
t.plan(8)
const pretty = prettyFactory({
errorLikeObjectKeys: ['err']
})
const err = Error('hello world')
const expected = err.stack.split('\n')
expected.unshift(err.message)
const log = pino({ serializers: { err: serializers.err } }, new Writable({
write (chunk, enc, cb) {
const formatted = pretty(chunk.toString())
const lines = formatted.split('\n')
t.equal(lines.length, expected.length + 6)
t.equal(lines[0], `[${formattedEpoch}] INFO (${pid}): hello world`)
t.match(lines[1], /\s{4}err: {/)
t.match(lines[2], /\s{6}"type": "Error",/)
t.match(lines[3], /\s{6}"message": "hello world",/)
t.match(lines[4], /\s{6}"stack":/)
t.match(lines[5], /\s{6}Error: hello world/)
// Node 12 labels the test `<anonymous>`
t.match(lines[6], /\s{10}(at Test.t.test|at Test.<anonymous>)/)
cb()
}
}))
log.info({ err })
})
t.test('prettifies Error in property with singleLine=true', (t) => {
// singleLine=true doesn't apply to errors
t.plan(8)
const pretty = prettyFactory({
singleLine: true,
errorLikeObjectKeys: ['err']
})
const err = Error('hello world')
const expected = [
'{"extra":{"a":1,"b":2}}',
err.message,
...err.stack.split('\n')
]
const log = pino({ serializers: { err: serializers.err } }, new Writable({
write (chunk, enc, cb) {
const formatted = pretty(chunk.toString())
const lines = formatted.split('\n')
t.equal(lines.length, expected.length + 5)
t.equal(lines[0], `[${formattedEpoch}] INFO (${pid}): hello world {"extra":{"a":1,"b":2}}`)
t.match(lines[1], /\s{4}err: {/)
t.match(lines[2], /\s{6}"type": "Error",/)
t.match(lines[3], /\s{6}"message": "hello world",/)
t.match(lines[4], /\s{6}"stack":/)
t.match(lines[5], /\s{6}Error: hello world/)
// Node 12 labels the test `<anonymous>`
t.match(lines[6], /\s{10}(at Test.t.test|at Test.<anonymous>)/)
cb()
}
}))
log.info({ err, extra: { a: 1, b: 2 } })
})
t.test('prettifies Error in property within errorLikeObjectKeys with custom function', (t) => {
t.plan(4)
const pretty = prettyFactory({
errorLikeObjectKeys: ['err'],
customPrettifiers: {
err: val => `error is ${val.message}`
}
})
const err = Error('hello world')
err.stack = 'Error: hello world\n at anonymous (C:\\project\\node_modules\\example\\index.js)'
const expected = err.stack.split('\n')
expected.unshift(err.message)
const log = pino({ serializers: { err: serializers.err } }, new Writable({
write (chunk, enc, cb) {
const formatted = pretty(chunk.toString())
const lines = formatted.split('\n')
t.equal(lines.length, 3)
t.equal(lines[0], `[${formattedEpoch}] INFO (${pid}): hello world`)
t.equal(lines[1], ' err: error is hello world')
t.equal(lines[2], '')
cb()
}
}))
log.info({ err })
})
t.test('prettifies Error in property within errorLikeObjectKeys when stack has escaped characters', (t) => {
t.plan(8)
const pretty = prettyFactory({
errorLikeObjectKeys: ['err']
})
const err = Error('hello world')
err.stack = 'Error: hello world\n at anonymous (C:\\project\\node_modules\\example\\index.js)'
const expected = err.stack.split('\n')
expected.unshift(err.message)
const log = pino({ serializers: { err: serializers.err } }, new Writable({
write (chunk, enc, cb) {
const formatted = pretty(chunk.toString())
const lines = formatted.split('\n')
t.equal(lines.length, expected.length + 6)
t.equal(lines[0], `[${formattedEpoch}] INFO (${pid}): hello world`)
t.match(lines[1], /\s{4}err: {$/)
t.match(lines[2], /\s{6}"type": "Error",$/)
t.match(lines[3], /\s{6}"message": "hello world",$/)
t.match(lines[4], /\s{6}"stack":$/)
t.match(lines[5], /\s{10}Error: hello world$/)
t.match(lines[6], /\s{10}at anonymous \(C:\\project\\node_modules\\example\\index.js\)$/)
cb()
}
}))
log.info({ err })
})
t.test('prettifies Error in property within errorLikeObjectKeys when stack is not the last property', (t) => {
t.plan(9)
const pretty = prettyFactory({
errorLikeObjectKeys: ['err']
})
const err = Error('hello world')
err.anotherField = 'dummy value'
const expected = err.stack.split('\n')
expected.unshift(err.message)
const log = pino({ serializers: { err: serializers.err } }, new Writable({
write (chunk, enc, cb) {
const formatted = pretty(chunk.toString())
const lines = formatted.split('\n')
t.equal(lines.length, expected.length + 7)
t.equal(lines[0], `[${formattedEpoch}] INFO (${pid}): hello world`)
t.match(lines[1], /\s{4}err: {/)
t.match(lines[2], /\s{6}"type": "Error",/)
t.match(lines[3], /\s{6}"message": "hello world",/)
t.match(lines[4], /\s{6}"stack":/)
t.match(lines[5], /\s{6}Error: hello world/)
// Node 12 labels the test `<anonymous>`
t.match(lines[6], /\s{10}(at Test.t.test|at Test.<anonymous>)/)
t.match(lines[lines.length - 3], /\s{6}"anotherField": "dummy value"/)
cb()
}
}))
log.info({ err })
})
t.test('errorProps flag with "*" (print all nested props)', function (t) {
const pretty = prettyFactory({ errorProps: '*' })
const expectedLines = [
' err: {',
' "type": "Error",',
' "message": "error message",',
' "stack":',
' error stack',
' "statusCode": 500,',
' "originalStack": "original stack",',
' "dataBaseSpecificError": {',
' "erroMessage": "some database error message",',
' "evenMoreSpecificStuff": {',
' "someErrorRelatedObject": "error"',
' }',
' }',
' }'
]
t.plan(expectedLines.length)
const log = pino({}, new Writable({
write (chunk, enc, cb) {
const formatted = pretty(chunk.toString())
const lines = formatted.split('\n')
lines.shift(); lines.pop()
for (let i = 0; i < lines.length; i += 1) {
t.equal(lines[i], expectedLines[i])
}
cb()
}
}))
const error = Error('error message')
error.stack = 'error stack'
error.statusCode = 500
error.originalStack = 'original stack'
error.dataBaseSpecificError = {
erroMessage: 'some database error message',
evenMoreSpecificStuff: {
someErrorRelatedObject: 'error'
}
}
log.error(error)
})
t.test('prettifies legacy error object at top level when singleLine=true', function (t) {
t.plan(4)
const pretty = prettyFactory({ singleLine: true })
const err = Error('hello world')
const expected = err.stack.split('\n')
expected.unshift(err.message)
const log = pino({}, new Writable({
write (chunk, enc, cb) {
const formatted = pretty(chunk.toString())
const lines = formatted.split('\n')
t.equal(lines.length, expected.length + 1)
t.equal(lines[0], `[${formattedEpoch}] INFO (${pid}): ${expected[0]}`)
t.equal(lines[1], ` ${expected[1]}`)
t.equal(lines[2], ` ${expected[2]}`)
cb()
}
}))
log.info({ type: 'Error', stack: err.stack, msg: err.message })
})
t.test('errorProps: legacy error object at top level', function (t) {
const pretty = prettyFactory({ errorProps: '*' })
const expectedLines = [
'INFO:',
' error stack',
' message: hello message',
' statusCode: 500',
' originalStack: original stack',
' dataBaseSpecificError: {',
' errorMessage: "some database error message"',
' evenMoreSpecificStuff: {',
' "someErrorRelatedObject": "error"',
' }',
' }',
''
]
t.plan(expectedLines.length)
const error = {}
error.level = 30
error.message = 'hello message'
error.type = 'Error'
error.stack = 'error stack'
error.statusCode = 500
error.originalStack = 'original stack'
error.dataBaseSpecificError = {
errorMessage: 'some database error message',
evenMoreSpecificStuff: {
someErrorRelatedObject: 'error'
}
}
const formatted = pretty(JSON.stringify(error))
const lines = formatted.split('\n')
for (let i = 0; i < lines.length; i += 1) {
t.equal(lines[i], expectedLines[i])
}
})
t.test('errorProps flag with a single property', function (t) {
const pretty = prettyFactory({ errorProps: 'originalStack' })
const expectedLines = [
'INFO:',
' error stack',
' originalStack: original stack',
''
]
t.plan(expectedLines.length)
const error = {}
error.level = 30
error.message = 'hello message'
error.type = 'Error'
error.stack = 'error stack'
error.statusCode = 500
error.originalStack = 'original stack'
error.dataBaseSpecificError = {
erroMessage: 'some database error message',
evenMoreSpecificStuff: {
someErrorRelatedObject: 'error'
}
}
const formatted = pretty(JSON.stringify(error))
const lines = formatted.split('\n')
for (let i = 0; i < lines.length; i += 1) {
t.equal(lines[i], expectedLines[i])
}
})
t.test('errorProps flag with a single property non existent', function (t) {
const pretty = prettyFactory({ errorProps: 'originalStackABC' })
const expectedLines = [
'INFO:',
' error stack',
''
]
t.plan(expectedLines.length)
const error = {}
error.level = 30
error.message = 'hello message'
error.type = 'Error'
error.stack = 'error stack'
error.statusCode = 500
error.originalStack = 'original stack'
error.dataBaseSpecificError = {
erroMessage: 'some database error message',
evenMoreSpecificStuff: {
someErrorRelatedObject: 'error'
}
}
const formatted = pretty(JSON.stringify(error))
const lines = formatted.split('\n')
for (let i = 0; i < lines.length; i += 1) {
t.equal(lines[i], expectedLines[i])
}
})
t.test('handles errors with a null stack', (t) => {
t.plan(2)
const pretty = prettyFactory()
const log = pino({}, new Writable({
write (chunk, enc, cb) {
const formatted = pretty(chunk.toString())
t.match(formatted, /\s{4}message: "foo"/)
t.match(formatted, /\s{4}stack: null/)
cb()
}
}))
const error = { message: 'foo', stack: null }
log.error(error)
})
t.test('handles errors with a null stack for Error object', (t) => {
const pretty = prettyFactory()
const expectedLines = [
' "type": "Error",',
' "message": "error message",',
' "stack":',
' ',
' "some": "property"'
]
t.plan(expectedLines.length)
const log = pino({}, new Writable({
write (chunk, enc, cb) {
const formatted = pretty(chunk.toString())
const lines = formatted.split('\n')
lines.shift(); lines.shift(); lines.pop(); lines.pop()
for (let i = 0; i < lines.length; i += 1) {
t.ok(lines[i].includes(expectedLines[i]))
}
cb()
}
}))
const error = Error('error message')
error.stack = null
error.some = 'property'
log.error(error)
})
t.end()
})

View File

@@ -0,0 +1,31 @@
// Run this to see how colouring works
const _prettyFactory = require('../../')
const pino = require('pino')
const { Writable } = require('readable-stream')
function prettyFactory () {
return _prettyFactory({
colorize: true
})
}
const pretty = prettyFactory()
const formatted = pretty('this is not json\nit\'s just regular output\n')
console.log(formatted)
const opts = {
base: {
hostname: 'localhost',
pid: process.pid
}
}
const log = pino(opts, new Writable({
write (chunk, enc, cb) {
const formatted = pretty(chunk.toString())
console.log(formatted)
cb()
}
}))
log.info('foobar')

View File

@@ -0,0 +1,55 @@
import { expectType } from "tsd";
import pretty from "../../";
import PinoPretty, {
PinoPretty as PinoPrettyNamed,
PrettyOptions,
colorizerFactory,
prettyFactory
} from "../../";
import PinoPrettyDefault from "../../";
import * as PinoPrettyStar from "../../";
import PinoPrettyCjsImport = require("../../");
import PrettyStream = PinoPretty.PrettyStream;
const PinoPrettyCjs = require("../../");
const options: PinoPretty.PrettyOptions = {
colorize: true,
crlf: false,
errorLikeObjectKeys: ["err", "error"],
errorProps: "",
hideObject: true,
levelKey: "level",
levelLabel: "foo",
messageFormat: false,
ignore: "",
levelFirst: false,
messageKey: "msg",
timestampKey: "timestamp",
minimumLevel: "trace",
translateTime: "UTC:h:MM:ss TT Z",
singleLine: false,
customPrettifiers: {
key: (value) => {
return value.toString().toUpperCase();
}
},
customLevels: 'verbose:5',
customColors: 'default:white,verbose:gray',
sync: false,
destination: 2,
append: true,
mkdir: true,
};
expectType<PrettyStream>(pretty()); // #326
expectType<PrettyStream>(pretty(options));
expectType<PrettyStream>(PinoPrettyNamed(options));
expectType<PrettyStream>(PinoPrettyDefault(options));
expectType<PrettyStream>(PinoPrettyStar.PinoPretty(options));
expectType<PrettyStream>(PinoPrettyStar.default(options));
expectType<PrettyStream>(PinoPrettyCjsImport.PinoPretty(options));
expectType<PrettyStream>(PinoPrettyCjsImport.default(options));
expectType<any>(PinoPrettyCjs(options));
expectType<PinoPretty.ColorizerFactory>(colorizerFactory);
expectType<PinoPretty.PrettyFactory>(prettyFactory);