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

[WIP] Plain ndjson #58

Merged
merged 34 commits into from Apr 28, 2019
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
2b0fcc3
Replace fast-json-parse
jsumners Mar 30, 2019
6e7bf6b
Initial refactor to generic ndjson prettifier
jsumners Mar 30, 2019
20e9107
Merge branch 'master' into plain-ndjson
jsumners Mar 30, 2019
b16e208
Improve support for primitives
jsumners Mar 30, 2019
7a32b52
Move line joiner to utils and apply standard formatting
jsumners Mar 30, 2019
a1cd48c
Move object prettification to utils function
jsumners Mar 31, 2019
ccd2848
Fix split in prettifyObject
jsumners Mar 31, 2019
bf02c96
Move "error" log prettification into utility function
jsumners Mar 31, 2019
572226d
Add support for string levels
jsumners Mar 31, 2019
dd5f386
Add "timestamp" as possible time key
jsumners Mar 31, 2019
64db81d
v3.0.0-rc.1
jsumners Mar 31, 2019
7ef851c
Remove Node 6 from Travis config
jsumners Apr 6, 2019
413d1d2
Add tests for utils.formatTime
jsumners Apr 6, 2019
fda9682
Fix lint issue
jsumners Apr 6, 2019
55e5eab
Add tests for colors.js
jsumners Apr 6, 2019
11c42cc
Add docblock for joinLinesWithIndentation
jsumners Apr 6, 2019
6ebee3c
Add tests for prettifyLevel
jsumners Apr 7, 2019
42e5bc7
Add tests for prettifyMessage
jsumners Apr 7, 2019
438f8fb
Add tests for prettifyTime
jsumners Apr 7, 2019
8bed001
Fix lint error
jsumners Apr 9, 2019
20d9a05
Pino seems to have changed order of some properties in the log. 🤷‍♂️
jsumners Apr 9, 2019
ca7767e
Fix regex for time tests
jsumners Apr 9, 2019
ff3cb0e
Remove Node 12 until release
jsumners Apr 9, 2019
9a997e2
Add tests for prettifyMetadata
jsumners Apr 9, 2019
87b65bf
Added tests (#59)
ovhemert Apr 11, 2019
613d250
Improve prettifyObject test and add docblock
jsumners Apr 13, 2019
b08e946
Add docblock for prettifyErrorLog
jsumners Apr 13, 2019
e5b2176
Update readme
jsumners Apr 13, 2019
af3e853
Update dependencies
jsumners Apr 13, 2019
410c1e8
v3.0.0-rc.2
jsumners Apr 21, 2019
6c38b01
Merge branch 'master' into plain-ndjson
jsumners Apr 21, 2019
1635a78
Remove Node 6 specific tests
jsumners Apr 28, 2019
6c78469
Add back Node 12 to test suite
jsumners Apr 28, 2019
e22b11a
Fix tests for Node 12
jsumners Apr 28, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
159 changes: 52 additions & 107 deletions index.js
@@ -1,22 +1,20 @@
'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 CONSTANTS = require('./lib/constants')

const levels = {
default: 'USERLVL',
60: 'FATAL',
50: 'ERROR',
40: 'WARN ',
30: 'INFO ',
20: 'DEBUG',
10: 'TRACE'
const colors = require('./lib/colors')
const { MESSAGE_KEY } = require('./lib/constants')
const { prettifyLevel, prettifyMessage, prettifyMetadata, prettifyTime, internals } = require('./lib/utils')
const { joinLinesWithIndentation } = internals

const bourne = require('bourne')
const jsonParser = input => {
try {
return { value: bourne.parse(input, { protoAction: 'remove' }) }
} catch (err) {
return { err }
}
}

const defaultOptions = {
Expand All @@ -25,7 +23,7 @@ const defaultOptions = {
errorLikeObjectKeys: ['err', 'error'],
errorProps: '',
levelFirst: false,
messageKey: CONSTANTS.MESSAGE_KEY,
messageKey: MESSAGE_KEY,
translateTime: false,
useMetadata: false,
outputStream: process.stdout
Expand All @@ -35,28 +33,6 @@ 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) {
const opts = Object.assign({}, defaultOptions, options)
const EOL = opts.crlf ? '\r\n' : '\n'
Expand All @@ -66,28 +42,7 @@ module.exports = function prettyFactory (options) {
const errorProps = opts.errorProps.split(',')
const ignoreKeys = opts.ignore ? new Set(opts.ignore.split(',')) : undefined

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

return pretty
Expand All @@ -97,14 +52,19 @@ module.exports = function prettyFactory (options) {
if (!isObject(inputData)) {
const parsed = jsonParser(inputData)
log = parsed.value
if (parsed.err || !isPinoLog(log)) {
if (parsed.err) {
// pass through
return inputData + EOL
}
} else {
log = inputData
}

// Short-circuit for spec allowed primitive values.
if ([null, true, false].includes(log)) {
return `${log}\n`
}

if (search && !jmespath.search(log, search)) {
return
}
Expand All @@ -127,58 +87,51 @@ module.exports = function prettyFactory (options) {
'v'
]

if (opts.translateTime) {
log.time = formatTime(log.time, opts.translateTime)
}

var line = log.time ? `[${log.time}]` : ''
const prettifiedLevel = prettifyLevel({ log, colorizer })
const prettifiedMessage = prettifyMessage({ log, messageKey, colorizer })
const prettifiedMetadata = prettifyMetadata({ log })
const prettifiedTime = prettifyTime({ log, translateFormat: opts.translateTime })

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}`
let line = ''
if (opts.levelFirst && prettifiedLevel) {
line = `${prettifiedLevel}`
}

if (log.name || log.pid || log.hostname) {
line += ' ('

if (log.name) {
line += log.name
}
if (prettifiedTime && line === '') {
line = `${prettifiedTime}`
} else if (prettifiedTime) {
line = `${line} ${prettifiedTime}`
}

if (log.name && log.pid) {
line += '/' + log.pid
} else if (log.pid) {
line += log.pid
if (!opts.levelFirst && prettifiedLevel) {
if (line.length > 0) {
line = `${line} ${prettifiedLevel}`
} else {
line = prettifiedLevel
}
}

if (log.hostname) {
if (line.slice(-1) !== '(') {
line += ' '
}
line += 'on ' + log.hostname
}
if (prettifiedMetadata) {
line = `${line} ${prettifiedMetadata}:`
}

line += ')'
if (line.endsWith(':') === false && line !== '') {
line += ':'
}

line += ': '
if (prettifiedMessage) {
line = `${line} ${prettifiedMessage}`
}

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
line += IDENT + joinLinesWithIndentation({ input: stack }) + EOL

let propsForPrint
if (errorProps && errorProps.length > 0) {
Expand Down Expand Up @@ -212,14 +165,6 @@ module.exports = function prettyFactory (options) {

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 || []

Expand All @@ -242,7 +187,7 @@ module.exports = function prettyFactory (options) {
if (lines === undefined) continue
const arrayOfLines = (
IDENT + keys[i] + ': ' +
joinLinesWithIndentation(lines) +
joinLinesWithIndentation({ input: lines }) +
EOL
).split('\n')

Expand Down Expand Up @@ -270,7 +215,7 @@ module.exports = function prettyFactory (options) {
if (value[keys[i]] !== undefined) {
const lines = stringifySafe(value[keys[i]], null, 2)
if (lines !== undefined) {
result += IDENT + keys[i] + ': ' + joinLinesWithIndentation(lines) + EOL
result += IDENT + keys[i] + ': ' + joinLinesWithIndentation({ input: lines }) + EOL
}
}
}
Expand Down
30 changes: 30 additions & 0 deletions lib/colors.js
@@ -0,0 +1,30 @@
'use strict'

const nocolor = input => input
const plain = {
default: nocolor,
60: nocolor,
50: nocolor,
40: nocolor,
30: nocolor,
20: nocolor,
10: nocolor,
message: nocolor
}

const chalk = require('chalk')
const ctx = new chalk.constructor({ enabled: true, level: 3 })
const colored = {
default: ctx.white,
60: ctx.bgRed,
50: ctx.red,
40: ctx.yellow,
30: ctx.green,
20: ctx.blue,
10: ctx.grey,
message: ctx.cyan
}

module.exports = function getColorizer (useColors) {
return useColors ? colored : plain
}
11 changes: 10 additions & 1 deletion lib/constants.js
Expand Up @@ -2,5 +2,14 @@

module.exports = {
DATE_FORMAT: 'yyyy-mm-dd HH:MM:ss.l o',
MESSAGE_KEY: 'msg'
MESSAGE_KEY: 'msg',
LEVELS: {
default: 'USERLVL',
60: 'FATAL',
50: 'ERROR',
40: 'WARN ',
30: 'INFO ',
20: 'DEBUG',
10: 'TRACE'
}
}
84 changes: 84 additions & 0 deletions lib/utils.js
@@ -0,0 +1,84 @@
'use strict'

const dateformat = require('dateformat')
const defaultColorizer = require('./colors')()
const { DATE_FORMAT, MESSAGE_KEY, LEVELS } = require('./constants')

module.exports = {
prettifyLevel,
prettifyMessage,
prettifyMetadata,
prettifyTime
}

module.exports.internals = {
formatTime,
joinLinesWithIndentation
}

function formatTime (epoch, translateTime) {
const instant = new Date(epoch)
if (translateTime === true) {
return dateformat(instant, 'UTC:' + DATE_FORMAT)
} else {
const upperFormat = translateTime.toUpperCase()
return (!upperFormat.startsWith('SYS:'))
? dateformat(instant, 'UTC:' + translateTime)
: (upperFormat === 'SYS:STANDARD')
? dateformat(instant, DATE_FORMAT)
: dateformat(instant, translateTime.slice(4))
}
}

function joinLinesWithIndentation ({ input, ident = ' ', eol = '\n' }) {
const lines = input.split(/\r?\n/)
for (var i = 1; i < lines.length; i += 1) {
lines[i] = ident + lines[i]
}
return lines.join(eol)
}

function prettifyLevel ({ log, colorizer = defaultColorizer }) {
if ('level' in log === false) return undefined
return LEVELS.hasOwnProperty(log.level) ? colorizer[log.level](LEVELS[log.level]) : colorizer.default(LEVELS.default)
}

function prettifyMessage ({ log, messageKey = MESSAGE_KEY, colorizer = defaultColorizer }) {
if (messageKey in log === false) return undefined
if (typeof log[messageKey] !== 'string') return undefined
return colorizer.message(log[messageKey])
}

function prettifyMetadata ({ log }) {
if (log.name || log.pid || log.hostname) {
let line = '('

if (log.name) {
line += log.name
}

if (log.name && log.pid) {
line += '/' + log.pid
} else if (log.pid) {
line += log.pid
}

if (log.hostname) {
// If `pid` and `name` were in the ignore keys list then we don't need
// the leading space.
line += `${line === '(' ? 'on' : ' on'} ${log.hostname}`
}

line += ')'
return line
}
return undefined
}

function prettifyTime ({ log, translateFormat = undefined }) {
if ('time' in log === false) return undefined
if (translateFormat) {
return '[' + formatTime(log.time, translateFormat) + ']'
}
return `[${log.time}]`
}
2 changes: 1 addition & 1 deletion package.json
Expand Up @@ -31,9 +31,9 @@
],
"dependencies": {
"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",
"jmespath": "^0.15.0",
"pump": "^3.0.0",
Expand Down