pino-pretty
Advanced tools
Comparing version 2.6.0 to 3.0.0-rc.1
278
index.js
'use strict' | ||
const chalk = require('chalk') | ||
const dateformat = require('dateformat') | ||
// remove jsonParser once Node 6 is not supported anymore | ||
const jsonParser = require('fast-json-parse') | ||
const jmespath = require('jmespath') | ||
const stringifySafe = require('fast-safe-stringify') | ||
const colors = require('./lib/colors') | ||
const { ERROR_LIKE_KEYS, MESSAGE_KEY } = require('./lib/constants') | ||
const { | ||
isObject, | ||
prettifyErrorLog, | ||
prettifyLevel, | ||
prettifyMessage, | ||
prettifyMetadata, | ||
prettifyObject, | ||
prettifyTime | ||
} = require('./lib/utils') | ||
const CONSTANTS = require('./lib/constants') | ||
const levels = { | ||
default: 'USERLVL', | ||
60: 'FATAL', | ||
50: 'ERROR', | ||
40: 'WARN ', | ||
30: 'INFO ', | ||
20: 'DEBUG', | ||
10: 'TRACE' | ||
const bourne = require('bourne') | ||
const jsonParser = input => { | ||
try { | ||
return { value: bourne.parse(input, { protoAction: 'remove' }) } | ||
} catch (err) { | ||
return { err } | ||
} | ||
} | ||
@@ -25,6 +29,6 @@ | ||
crlf: false, | ||
errorLikeObjectKeys: ['err', 'error'], | ||
errorLikeObjectKeys: ERROR_LIKE_KEYS, | ||
errorProps: '', | ||
levelFirst: false, | ||
messageKey: CONSTANTS.MESSAGE_KEY, | ||
messageKey: MESSAGE_KEY, | ||
translateTime: false, | ||
@@ -35,28 +39,2 @@ useMetadata: false, | ||
function isObject (input) { | ||
return Object.prototype.toString.apply(input) === '[object Object]' | ||
} | ||
function isPinoLog (log) { | ||
return log && (log.hasOwnProperty('v') && log.v === 1) | ||
} | ||
function formatTime (epoch, translateTime) { | ||
const instant = new Date(epoch) | ||
if (translateTime === true) { | ||
return dateformat(instant, 'UTC:' + CONSTANTS.DATE_FORMAT) | ||
} else { | ||
const upperFormat = translateTime.toUpperCase() | ||
return (!upperFormat.startsWith('SYS:')) | ||
? dateformat(instant, 'UTC:' + translateTime) | ||
: (upperFormat === 'SYS:STANDARD') | ||
? dateformat(instant, CONSTANTS.DATE_FORMAT) | ||
: dateformat(instant, translateTime.slice(4)) | ||
} | ||
} | ||
function nocolor (input) { | ||
return input | ||
} | ||
module.exports = function prettyFactory (options) { | ||
@@ -71,24 +49,3 @@ const opts = Object.assign({}, defaultOptions, options) | ||
const color = { | ||
default: nocolor, | ||
60: nocolor, | ||
50: nocolor, | ||
40: nocolor, | ||
30: nocolor, | ||
20: nocolor, | ||
10: nocolor, | ||
message: nocolor | ||
} | ||
if (opts.colorize) { | ||
const ctx = new chalk.constructor({ enabled: true, level: 3 }) | ||
color.default = ctx.white | ||
color[60] = ctx.bgRed | ||
color[50] = ctx.red | ||
color[40] = ctx.yellow | ||
color[30] = ctx.green | ||
color[20] = ctx.blue | ||
color[10] = ctx.grey | ||
color.message = ctx.cyan | ||
} | ||
const colorizer = colors(opts.colorize) | ||
const search = opts.search | ||
@@ -103,3 +60,3 @@ | ||
log = parsed.value | ||
if (parsed.err || !isPinoLog(log)) { | ||
if (parsed.err) { | ||
// pass through | ||
@@ -112,2 +69,7 @@ return inputData + EOL | ||
// Short-circuit for spec allowed primitive values. | ||
if ([null, true, false].includes(log)) { | ||
return `${log}\n` | ||
} | ||
if (search && !jmespath.search(log, search)) { | ||
@@ -126,163 +88,65 @@ return | ||
const standardKeys = [ | ||
'pid', | ||
'hostname', | ||
'name', | ||
'level', | ||
'time', | ||
'v' | ||
] | ||
const prettifiedLevel = prettifyLevel({ log, colorizer }) | ||
const prettifiedMessage = prettifyMessage({ log, messageKey, colorizer }) | ||
const prettifiedMetadata = prettifyMetadata({ log }) | ||
const prettifiedTime = prettifyTime({ log, translateFormat: opts.translateTime }) | ||
if (opts.translateTime) { | ||
log.time = formatTime(log.time, opts.translateTime) | ||
let line = '' | ||
if (opts.levelFirst && prettifiedLevel) { | ||
line = `${prettifiedLevel}` | ||
} | ||
var line = log.time ? `[${log.time}]` : '' | ||
const coloredLevel = levels.hasOwnProperty(log.level) | ||
? color[log.level](levels[log.level]) | ||
: color.default(levels.default) | ||
if (opts.levelFirst) { | ||
line = `${coloredLevel} ${line}` | ||
} else { | ||
// If the line is not empty (timestamps are enabled) output it | ||
// with a space after it - otherwise output the empty string | ||
const lineOrEmpty = line && line + ' ' | ||
line = `${lineOrEmpty}${coloredLevel}` | ||
if (prettifiedTime && line === '') { | ||
line = `${prettifiedTime}` | ||
} else if (prettifiedTime) { | ||
line = `${line} ${prettifiedTime}` | ||
} | ||
if (log.name || log.pid || log.hostname) { | ||
line += ' (' | ||
if (log.name) { | ||
line += log.name | ||
if (!opts.levelFirst && prettifiedLevel) { | ||
if (line.length > 0) { | ||
line = `${line} ${prettifiedLevel}` | ||
} else { | ||
line = prettifiedLevel | ||
} | ||
} | ||
if (log.name && log.pid) { | ||
line += '/' + log.pid | ||
} else if (log.pid) { | ||
line += log.pid | ||
} | ||
if (prettifiedMetadata) { | ||
line = `${line} ${prettifiedMetadata}:` | ||
} | ||
if (log.hostname) { | ||
if (line.slice(-1) !== '(') { | ||
line += ' ' | ||
} | ||
line += 'on ' + log.hostname | ||
} | ||
if (line.endsWith(':') === false && line !== '') { | ||
line += ':' | ||
} | ||
line += ')' | ||
if (prettifiedMessage) { | ||
line = `${line} ${prettifiedMessage}` | ||
} | ||
line += ': ' | ||
if (log[messageKey] && typeof log[messageKey] === 'string') { | ||
line += color.message(log[messageKey]) | ||
if (line.length > 0) { | ||
line += EOL | ||
} | ||
line += EOL | ||
if (log.type === 'Error' && log.stack) { | ||
const stack = log.stack | ||
line += IDENT + joinLinesWithIndentation(stack) + EOL | ||
let propsForPrint | ||
if (errorProps && errorProps.length > 0) { | ||
// don't need print these props for 'Error' object | ||
const excludedProps = standardKeys.concat([messageKey, 'type', 'stack']) | ||
if (errorProps[0] === '*') { | ||
// print all log props excluding 'excludedProps' | ||
propsForPrint = Object.keys(log).filter((prop) => excludedProps.indexOf(prop) < 0) | ||
} else { | ||
// print props from 'errorProps' only | ||
// but exclude 'excludedProps' | ||
propsForPrint = errorProps.filter((prop) => excludedProps.indexOf(prop) < 0) | ||
} | ||
for (var i = 0; i < propsForPrint.length; i++) { | ||
const key = propsForPrint[i] | ||
if (!log.hasOwnProperty(key)) continue | ||
if (log[key] instanceof Object) { | ||
// call 'filterObjects' with 'excludeStandardKeys' = false | ||
// because nested property might contain property from 'standardKeys' | ||
line += key + ': {' + EOL + filterObjects(log[key], '', errorLikeObjectKeys, false) + '}' + EOL | ||
continue | ||
} | ||
line += key + ': ' + log[key] + EOL | ||
} | ||
} | ||
const prettifiedErrorLog = prettifyErrorLog({ | ||
log, | ||
errorLikeKeys: errorLikeObjectKeys, | ||
errorProperties: errorProps, | ||
ident: IDENT, | ||
eol: EOL | ||
}) | ||
line += prettifiedErrorLog | ||
} else { | ||
line += filterObjects(log, typeof log[messageKey] === 'string' ? messageKey : undefined, errorLikeObjectKeys) | ||
const skipKeys = typeof log[messageKey] === 'string' ? [messageKey] : undefined | ||
const prettifiedObject = prettifyObject({ | ||
input: log, | ||
skipKeys, | ||
errorLikeKeys: errorLikeObjectKeys, | ||
eol: EOL, | ||
ident: IDENT | ||
}) | ||
line += prettifiedObject | ||
} | ||
return line | ||
function joinLinesWithIndentation (value) { | ||
const lines = value.split(/\r?\n/) | ||
for (var i = 1; i < lines.length; i++) { | ||
lines[i] = IDENT + lines[i] | ||
} | ||
return lines.join(EOL) | ||
} | ||
function filterObjects (value, messageKey, errorLikeObjectKeys, excludeStandardKeys) { | ||
errorLikeObjectKeys = errorLikeObjectKeys || [] | ||
const keys = Object.keys(value) | ||
const filteredKeys = [] | ||
if (messageKey) { | ||
filteredKeys.push(messageKey) | ||
} | ||
if (excludeStandardKeys !== false) { | ||
Array.prototype.push.apply(filteredKeys, standardKeys) | ||
} | ||
let result = '' | ||
for (var i = 0; i < keys.length; i += 1) { | ||
if (errorLikeObjectKeys.indexOf(keys[i]) !== -1 && value[keys[i]] !== undefined) { | ||
const lines = stringifySafe(value[keys[i]], null, 2) | ||
if (lines === undefined) continue | ||
const arrayOfLines = ( | ||
IDENT + keys[i] + ': ' + | ||
joinLinesWithIndentation(lines) + | ||
EOL | ||
).split('\n') | ||
for (var j = 0; j < arrayOfLines.length; j += 1) { | ||
if (j !== 0) { | ||
result += '\n' | ||
} | ||
const line = arrayOfLines[j] | ||
if (/^\s*"stack"/.test(line)) { | ||
const matches = /^(\s*"stack":)\s*(".*"),?$/.exec(line) | ||
if (matches && matches.length === 3) { | ||
const indentSize = /^\s*/.exec(line)[0].length + 4 | ||
const indentation = ' '.repeat(indentSize) | ||
result += matches[1] + '\n' + indentation + JSON.parse(matches[2]).replace(/\n/g, '\n' + indentation) | ||
} | ||
} else { | ||
result += line | ||
} | ||
} | ||
} else if (filteredKeys.indexOf(keys[i]) < 0) { | ||
if (value[keys[i]] !== undefined) { | ||
const lines = stringifySafe(value[keys[i]], null, 2) | ||
if (lines !== undefined) { | ||
result += IDENT + keys[i] + ': ' + joinLinesWithIndentation(lines) + EOL | ||
} | ||
} | ||
} | ||
} | ||
return result | ||
} | ||
} | ||
} |
@@ -5,3 +5,36 @@ 'use strict' | ||
DATE_FORMAT: 'yyyy-mm-dd HH:MM:ss.l o', | ||
MESSAGE_KEY: 'msg' | ||
ERROR_LIKE_KEYS: ['err', 'error'], | ||
MESSAGE_KEY: 'msg', | ||
LEVELS: { | ||
default: 'USERLVL', | ||
60: 'FATAL', | ||
50: 'ERROR', | ||
40: 'WARN ', | ||
30: 'INFO ', | ||
20: 'DEBUG', | ||
10: 'TRACE' | ||
}, | ||
LEVEL_NAMES: { | ||
fatal: 60, | ||
error: 50, | ||
warn: 40, | ||
info: 30, | ||
debug: 20, | ||
trace: 10 | ||
}, | ||
// Object keys that probably came from a logger like Pino or Bunyan. | ||
LOGGER_KEYS: [ | ||
'pid', | ||
'hostname', | ||
'name', | ||
'level', | ||
'time', | ||
'timestamp', | ||
'v' | ||
] | ||
} |
{ | ||
"name": "pino-pretty", | ||
"version": "2.6.0", | ||
"version": "3.0.0-rc.1", | ||
"description": "Prettifier for Pino log lines", | ||
@@ -34,5 +34,5 @@ "main": "index.js", | ||
"args": "^5.0.0", | ||
"bourne": "^1.1.2", | ||
"chalk": "^2.3.2", | ||
"dateformat": "^3.0.3", | ||
"fast-json-parse": "^1.0.3", | ||
"fast-safe-stringify": "^2.0.6", | ||
@@ -39,0 +39,0 @@ "jmespath": "^0.15.0", |
@@ -182,2 +182,3 @@ 'use strict' | ||
// TODO: 2019-03-30 -- We don't really want the indentation in this case? Or at least some better formatting. | ||
t.test('handles missing time', (t) => { | ||
@@ -187,3 +188,3 @@ t.plan(1) | ||
const formatted = pretty('{"hello":"world"}') | ||
t.is(formatted, '{"hello":"world"}\n') | ||
t.is(formatted, ' hello: "world"\n') | ||
}) | ||
@@ -339,7 +340,14 @@ | ||
t.test('handles `null` input', (t) => { | ||
t.plan(1) | ||
t.test('handles spec allowed primitives', (t) => { | ||
const pretty = prettyFactory() | ||
const formatted = pretty(null) | ||
let formatted = pretty(null) | ||
t.is(formatted, 'null\n') | ||
formatted = pretty(true) | ||
t.is(formatted, 'true\n') | ||
formatted = pretty(false) | ||
t.is(formatted, 'false\n') | ||
t.end() | ||
}) | ||
@@ -354,9 +362,2 @@ | ||
t.test('handles `true` input', (t) => { | ||
t.plan(1) | ||
const pretty = prettyFactory() | ||
const formatted = pretty(true) | ||
t.is(formatted, 'true\n') | ||
}) | ||
t.test('handles customLogLevel', (t) => { | ||
@@ -527,3 +528,3 @@ t.plan(1) | ||
chunk + '', | ||
`[${epoch}] INFO (${pid} on ${hostname}): \n a: {\n "b": "c"\n }\n n: null\n` | ||
`[${epoch}] INFO (${pid} on ${hostname}):\n a: {\n "b": "c"\n }\n n: null\n` | ||
) | ||
@@ -530,0 +531,0 @@ cb() |
@@ -92,3 +92,3 @@ 'use strict' | ||
t.is(lines.length, expected.length + 6) | ||
t.is(lines[0], `[${epoch}] INFO (${pid} on ${hostname}): `) | ||
t.is(lines[0], `[${epoch}] INFO (${pid} on ${hostname}):`) | ||
t.match(lines[1], /\s{4}err: {/) | ||
@@ -124,3 +124,3 @@ t.match(lines[2], /\s{6}"type": "Error",/) | ||
t.is(lines.length, expected.length + 6) | ||
t.is(lines[0], `[${epoch}] INFO (${pid} on ${hostname}): `) | ||
t.is(lines[0], `[${epoch}] INFO (${pid} on ${hostname}):`) | ||
t.match(lines[1], /\s{4}err: {$/) | ||
@@ -155,3 +155,3 @@ t.match(lines[2], /\s{6}"type": "Error",$/) | ||
t.is(lines.length, expected.length + 7) | ||
t.is(lines[0], `[${epoch}] INFO (${pid} on ${hostname}): `) | ||
t.is(lines[0], `[${epoch}] INFO (${pid} on ${hostname}):`) | ||
t.match(lines[1], /\s{4}err: {/) | ||
@@ -158,0 +158,0 @@ t.match(lines[2], /\s{6}"type": "Error",/) |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
226004
16
1225
1
+ Addedbourne@^1.1.2
+ Addedbourne@1.3.3(transitive)
- Removedfast-json-parse@^1.0.3
- Removedfast-json-parse@1.0.3(transitive)