UNPKG

9.86 kBJavaScriptView Raw
1'use strict'
2
3const Writable = require('stream').Writable
4const os = require('os')
5const test = require('tap').test
6const pino = require('pino')
7const serializers = pino.stdSerializers
8const _prettyFactory = require('../')
9
10function prettyFactory (opts) {
11 if (!opts) {
12 opts = { colorize: false }
13 } else if (!Object.prototype.hasOwnProperty.call(opts, 'colorize')) {
14 opts.colorize = false
15 }
16 return _prettyFactory(opts)
17}
18
19// All dates are computed from 'Fri, 30 Mar 2018 17:35:28 GMT'
20const epoch = 1522431328992
21const pid = process.pid
22const hostname = os.hostname()
23
24test('error like objects tests', (t) => {
25 t.beforeEach((done) => {
26 Date.originalNow = Date.now
27 Date.now = () => epoch
28
29 done()
30 })
31 t.afterEach((done) => {
32 Date.now = Date.originalNow
33 delete Date.originalNow
34
35 done()
36 })
37
38 t.test('pino transform prettifies Error', (t) => {
39 t.plan(2)
40 const pretty = prettyFactory()
41 const err = Error('hello world')
42 const expected = err.stack.split('\n')
43 expected.unshift(err.message)
44
45 const log = pino({}, new Writable({
46 write (chunk, enc, cb) {
47 const formatted = pretty(chunk.toString())
48 const lines = formatted.split('\n')
49 t.is(lines.length, expected.length + 1)
50 t.is(lines[0], `[${epoch}] INFO (${pid} on ${hostname}): hello world`)
51 cb()
52 }
53 }))
54
55 log.info(err)
56 })
57
58 t.test('errorProps recognizes user specified properties', (t) => {
59 t.plan(3)
60 const pretty = prettyFactory({ errorProps: 'statusCode,originalStack' })
61 const log = pino({}, new Writable({
62 write (chunk, enc, cb) {
63 const formatted = pretty(chunk.toString())
64 t.match(formatted, /\s{4}error stack/)
65 t.match(formatted, /statusCode: 500/)
66 t.match(formatted, /originalStack: original stack/)
67 cb()
68 }
69 }))
70
71 const error = Error('error message')
72 error.stack = 'error stack'
73 error.statusCode = 500
74 error.originalStack = 'original stack'
75
76 log.error(error)
77 })
78
79 t.test('prettifies ignores undefined errorLikeObject', (t) => {
80 const pretty = prettyFactory()
81 pretty({ err: undefined })
82 pretty({ error: undefined })
83 t.end()
84 })
85
86 t.test('prettifies Error in property within errorLikeObjectKeys', (t) => {
87 t.plan(8)
88 const pretty = prettyFactory({
89 errorLikeObjectKeys: ['err']
90 })
91
92 const err = Error('hello world')
93 const expected = err.stack.split('\n')
94 expected.unshift(err.message)
95
96 const log = pino({ serializers: { err: serializers.err } }, new Writable({
97 write (chunk, enc, cb) {
98 const formatted = pretty(chunk.toString())
99 const lines = formatted.split('\n')
100 t.is(lines.length, expected.length + 6)
101 t.is(lines[0], `[${epoch}] INFO (${pid} on ${hostname}):`)
102 t.match(lines[1], /\s{4}err: {/)
103 t.match(lines[2], /\s{6}"type": "Error",/)
104 t.match(lines[3], /\s{6}"message": "hello world",/)
105 t.match(lines[4], /\s{6}"stack":/)
106 t.match(lines[5], /\s{6}Error: hello world/)
107 // Node 12 labels the test `<anonymous>`
108 t.match(lines[6], /\s{10}(at Test.t.test|at Test.<anonymous>)/)
109 cb()
110 }
111 }))
112
113 log.info({ err })
114 })
115
116 t.test('prettifies Error in property with singleLine=true', (t) => {
117 // singleLine=true doesn't apply to errors
118 t.plan(8)
119 const pretty = prettyFactory({
120 singleLine: true,
121 errorLikeObjectKeys: ['err']
122 })
123
124 const err = Error('hello world')
125 const expected = [
126 '{"extra":{"a":1,"b":2}}',
127 err.message,
128 ...err.stack.split('\n')
129 ]
130
131 const log = pino({ serializers: { err: serializers.err } }, new Writable({
132 write (chunk, enc, cb) {
133 const formatted = pretty(chunk.toString())
134 const lines = formatted.split('\n')
135 t.is(lines.length, expected.length + 5)
136 t.is(lines[0], `[${epoch}] INFO (${pid} on ${hostname}): {"extra":{"a":1,"b":2}}`)
137 t.match(lines[1], /\s{4}err: {/)
138 t.match(lines[2], /\s{6}"type": "Error",/)
139 t.match(lines[3], /\s{6}"message": "hello world",/)
140 t.match(lines[4], /\s{6}"stack":/)
141 t.match(lines[5], /\s{6}Error: hello world/)
142 // Node 12 labels the test `<anonymous>`
143 t.match(lines[6], /\s{10}(at Test.t.test|at Test.<anonymous>)/)
144 cb()
145 }
146 }))
147
148 log.info({ err, extra: { a: 1, b: 2 } })
149 })
150
151 t.test('prettifies Error in property within errorLikeObjectKeys with custom function', (t) => {
152 t.plan(1)
153 const pretty = prettyFactory({
154 errorLikeObjectKeys: ['err'],
155 customPrettifiers: {
156 err: val => `error is ${val.message}`
157 }
158 })
159
160 const err = Error('hello world')
161 err.stack = 'Error: hello world\n at anonymous (C:\\project\\node_modules\\example\\index.js)'
162 const expected = err.stack.split('\n')
163 expected.unshift(err.message)
164
165 const log = pino({ serializers: { err: serializers.err } }, new Writable({
166 write (chunk, enc, cb) {
167 const formatted = pretty(chunk.toString())
168 t.is(formatted, `[${epoch}] INFO (${pid} on ${hostname}):\n err: error is hello world\n`)
169 cb()
170 }
171 }))
172
173 log.info({ err })
174 })
175
176 t.test('prettifies Error in property within errorLikeObjectKeys when stack has escaped characters', (t) => {
177 t.plan(8)
178 const pretty = prettyFactory({
179 errorLikeObjectKeys: ['err']
180 })
181
182 const err = Error('hello world')
183 err.stack = 'Error: hello world\n at anonymous (C:\\project\\node_modules\\example\\index.js)'
184 const expected = err.stack.split('\n')
185 expected.unshift(err.message)
186
187 const log = pino({ serializers: { err: serializers.err } }, new Writable({
188 write (chunk, enc, cb) {
189 const formatted = pretty(chunk.toString())
190 const lines = formatted.split('\n')
191 t.is(lines.length, expected.length + 6)
192 t.is(lines[0], `[${epoch}] INFO (${pid} on ${hostname}):`)
193 t.match(lines[1], /\s{4}err: {$/)
194 t.match(lines[2], /\s{6}"type": "Error",$/)
195 t.match(lines[3], /\s{6}"message": "hello world",$/)
196 t.match(lines[4], /\s{6}"stack":$/)
197 t.match(lines[5], /\s{10}Error: hello world$/)
198 t.match(lines[6], /\s{10}at anonymous \(C:\\project\\node_modules\\example\\index.js\)$/)
199 cb()
200 }
201 }))
202
203 log.info({ err })
204 })
205
206 t.test('prettifies Error in property within errorLikeObjectKeys when stack is not the last property', (t) => {
207 t.plan(9)
208 const pretty = prettyFactory({
209 errorLikeObjectKeys: ['err']
210 })
211
212 const err = Error('hello world')
213 err.anotherField = 'dummy value'
214 const expected = err.stack.split('\n')
215 expected.unshift(err.message)
216
217 const log = pino({ serializers: { err: serializers.err } }, new Writable({
218 write (chunk, enc, cb) {
219 const formatted = pretty(chunk.toString())
220 const lines = formatted.split('\n')
221 t.is(lines.length, expected.length + 7)
222 t.is(lines[0], `[${epoch}] INFO (${pid} on ${hostname}):`)
223 t.match(lines[1], /\s{4}err: {/)
224 t.match(lines[2], /\s{6}"type": "Error",/)
225 t.match(lines[3], /\s{6}"message": "hello world",/)
226 t.match(lines[4], /\s{6}"stack":/)
227 t.match(lines[5], /\s{6}Error: hello world/)
228 // Node 12 labels the test `<anonymous>`
229 t.match(lines[6], /\s{10}(at Test.t.test|at Test.<anonymous>)/)
230 t.match(lines[lines.length - 3], /\s{6}"anotherField": "dummy value"/)
231 cb()
232 }
233 }))
234
235 log.info({ err })
236 })
237
238 t.test('errorProps flag with "*" (print all nested props)', function (t) {
239 t.plan(9)
240 const pretty = prettyFactory({ errorProps: '*' })
241 const expectedLines = [
242 ' error stack',
243 'statusCode: 500',
244 'originalStack: original stack',
245 'dataBaseSpecificError: {',
246 ' erroMessage: "some database error message"',
247 ' evenMoreSpecificStuff: {',
248 ' "someErrorRelatedObject": "error"',
249 ' }',
250 '}'
251 ]
252 const log = pino({}, new Writable({
253 write (chunk, enc, cb) {
254 const formatted = pretty(chunk.toString())
255 const lines = formatted.split('\n')
256 lines.shift(); lines.pop()
257 for (let i = 0; i < lines.length; i += 1) {
258 t.is(lines[i], expectedLines[i])
259 }
260 cb()
261 }
262 }))
263
264 const error = Error('error message')
265 error.stack = 'error stack'
266 error.statusCode = 500
267 error.originalStack = 'original stack'
268 error.dataBaseSpecificError = {
269 erroMessage: 'some database error message',
270 evenMoreSpecificStuff: {
271 someErrorRelatedObject: 'error'
272 }
273 }
274
275 log.error(error)
276 })
277
278 t.test('handles errors with a null stack', (t) => {
279 t.plan(2)
280 const pretty = prettyFactory()
281 const log = pino({}, new Writable({
282 write (chunk, enc, cb) {
283 const formatted = pretty(chunk.toString())
284 t.match(formatted, /\s{4}message: "foo"/)
285 t.match(formatted, /\s{4}stack: null/)
286 cb()
287 }
288 }))
289
290 const error = { message: 'foo', stack: null }
291 log.error(error)
292 })
293
294 t.test('handles errors with a null stack for Error object', (t) => {
295 t.plan(3)
296 const pretty = prettyFactory()
297 const expectedLines = [
298 ' some: "property"',
299 ' stack: null',
300 ' type: "Error"'
301 ]
302 const log = pino({}, new Writable({
303 write (chunk, enc, cb) {
304 const formatted = pretty(chunk.toString())
305 const lines = formatted.split('\n')
306 lines.shift(); lines.pop()
307 for (let i = 0; i < lines.length; i += 1) {
308 t.true(expectedLines.includes(lines[i]))
309 }
310 cb()
311 }
312 }))
313
314 const error = Error('error message')
315 error.stack = null
316 error.some = 'property'
317
318 log.error(error)
319 })
320
321 t.end()
322})