1 | 'use strict'
|
2 |
|
3 | const Writable = require('stream').Writable
|
4 | const os = require('os')
|
5 | const test = require('tap').test
|
6 | const pino = require('pino')
|
7 | const serializers = pino.stdSerializers
|
8 | const _prettyFactory = require('../')
|
9 |
|
10 | function 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 |
|
20 | const epoch = 1522431328992
|
21 | const pid = process.pid
|
22 | const hostname = os.hostname()
|
23 |
|
24 | test('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 |
|
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 |
|
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 |
|
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 |
|
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 | })
|