You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							538 lines
						
					
					
						
							13 KiB
						
					
					
				
			
		
		
	
	
							538 lines
						
					
					
						
							13 KiB
						
					
					
				| 'use strict'
 | |
| 
 | |
| const { test } = require('tap')
 | |
| const { sink, once, check } = require('./helper')
 | |
| const pino = require('../')
 | |
| 
 | |
| const levelsLib = require('../lib/levels')
 | |
| 
 | |
| // Silence all warnings for this test
 | |
| process.removeAllListeners('warning')
 | |
| process.on('warning', () => {})
 | |
| 
 | |
| test('set the level by string', async ({ equal }) => {
 | |
|   const expected = [{
 | |
|     level: 50,
 | |
|     msg: 'this is an error'
 | |
|   }, {
 | |
|     level: 60,
 | |
|     msg: 'this is fatal'
 | |
|   }]
 | |
|   const stream = sink()
 | |
|   const instance = pino(stream)
 | |
|   instance.level = 'error'
 | |
|   instance.info('hello world')
 | |
|   instance.error('this is an error')
 | |
|   instance.fatal('this is fatal')
 | |
|   const result = await once(stream, 'data')
 | |
|   const current = expected.shift()
 | |
|   check(equal, result, current.level, current.msg)
 | |
| })
 | |
| 
 | |
| test('the wrong level throws', async ({ throws }) => {
 | |
|   const instance = pino()
 | |
|   throws(() => {
 | |
|     instance.level = 'kaboom'
 | |
|   })
 | |
| })
 | |
| 
 | |
| test('set the level by number', async ({ equal }) => {
 | |
|   const expected = [{
 | |
|     level: 50,
 | |
|     msg: 'this is an error'
 | |
|   }, {
 | |
|     level: 60,
 | |
|     msg: 'this is fatal'
 | |
|   }]
 | |
|   const stream = sink()
 | |
|   const instance = pino(stream)
 | |
| 
 | |
|   instance.level = 50
 | |
|   instance.info('hello world')
 | |
|   instance.error('this is an error')
 | |
|   instance.fatal('this is fatal')
 | |
|   const result = await once(stream, 'data')
 | |
|   const current = expected.shift()
 | |
|   check(equal, result, current.level, current.msg)
 | |
| })
 | |
| 
 | |
| test('exposes level string mappings', async ({ equal }) => {
 | |
|   equal(pino.levels.values.error, 50)
 | |
| })
 | |
| 
 | |
| test('exposes level number mappings', async ({ equal }) => {
 | |
|   equal(pino.levels.labels[50], 'error')
 | |
| })
 | |
| 
 | |
| test('returns level integer', async ({ equal }) => {
 | |
|   const instance = pino({ level: 'error' })
 | |
|   equal(instance.levelVal, 50)
 | |
| })
 | |
| 
 | |
| test('child returns level integer', async ({ equal }) => {
 | |
|   const parent = pino({ level: 'error' })
 | |
|   const child = parent.child({ foo: 'bar' })
 | |
|   equal(child.levelVal, 50)
 | |
| })
 | |
| 
 | |
| test('set the level via exported pino function', async ({ equal }) => {
 | |
|   const expected = [{
 | |
|     level: 50,
 | |
|     msg: 'this is an error'
 | |
|   }, {
 | |
|     level: 60,
 | |
|     msg: 'this is fatal'
 | |
|   }]
 | |
|   const stream = sink()
 | |
|   const instance = pino({ level: 'error' }, stream)
 | |
| 
 | |
|   instance.info('hello world')
 | |
|   instance.error('this is an error')
 | |
|   instance.fatal('this is fatal')
 | |
|   const result = await once(stream, 'data')
 | |
|   const current = expected.shift()
 | |
|   check(equal, result, current.level, current.msg)
 | |
| })
 | |
| 
 | |
| test('level-change event', async ({ equal }) => {
 | |
|   const instance = pino()
 | |
|   function handle (lvl, val, prevLvl, prevVal) {
 | |
|     equal(lvl, 'trace')
 | |
|     equal(val, 10)
 | |
|     equal(prevLvl, 'info')
 | |
|     equal(prevVal, 30)
 | |
|   }
 | |
|   instance.on('level-change', handle)
 | |
|   instance.level = 'trace'
 | |
|   instance.removeListener('level-change', handle)
 | |
|   instance.level = 'info'
 | |
| 
 | |
|   let count = 0
 | |
| 
 | |
|   const l1 = () => count++
 | |
|   const l2 = () => count++
 | |
|   const l3 = () => count++
 | |
|   instance.on('level-change', l1)
 | |
|   instance.on('level-change', l2)
 | |
|   instance.on('level-change', l3)
 | |
| 
 | |
|   instance.level = 'trace'
 | |
|   instance.removeListener('level-change', l3)
 | |
|   instance.level = 'fatal'
 | |
|   instance.removeListener('level-change', l1)
 | |
|   instance.level = 'debug'
 | |
|   instance.removeListener('level-change', l2)
 | |
|   instance.level = 'info'
 | |
| 
 | |
|   equal(count, 6)
 | |
| })
 | |
| 
 | |
| test('enable', async ({ fail }) => {
 | |
|   const instance = pino({
 | |
|     level: 'trace',
 | |
|     enabled: false
 | |
|   }, sink((result, enc) => {
 | |
|     fail('no data should be logged')
 | |
|   }))
 | |
| 
 | |
|   Object.keys(pino.levels.values).forEach((level) => {
 | |
|     instance[level]('hello world')
 | |
|   })
 | |
| })
 | |
| 
 | |
| test('silent level', async ({ fail }) => {
 | |
|   const instance = pino({
 | |
|     level: 'silent'
 | |
|   }, sink((result, enc) => {
 | |
|     fail('no data should be logged')
 | |
|   }))
 | |
| 
 | |
|   Object.keys(pino.levels.values).forEach((level) => {
 | |
|     instance[level]('hello world')
 | |
|   })
 | |
| })
 | |
| 
 | |
| test('set silent via Infinity', async ({ fail }) => {
 | |
|   const instance = pino({
 | |
|     level: Infinity
 | |
|   }, sink((result, enc) => {
 | |
|     fail('no data should be logged')
 | |
|   }))
 | |
| 
 | |
|   Object.keys(pino.levels.values).forEach((level) => {
 | |
|     instance[level]('hello world')
 | |
|   })
 | |
| })
 | |
| 
 | |
| test('exposed levels', async ({ same }) => {
 | |
|   same(Object.keys(pino.levels.values), [
 | |
|     'trace',
 | |
|     'debug',
 | |
|     'info',
 | |
|     'warn',
 | |
|     'error',
 | |
|     'fatal'
 | |
|   ])
 | |
| })
 | |
| 
 | |
| test('exposed labels', async ({ same }) => {
 | |
|   same(Object.keys(pino.levels.labels), [
 | |
|     '10',
 | |
|     '20',
 | |
|     '30',
 | |
|     '40',
 | |
|     '50',
 | |
|     '60'
 | |
|   ])
 | |
| })
 | |
| 
 | |
| test('setting level in child', async ({ equal }) => {
 | |
|   const expected = [{
 | |
|     level: 50,
 | |
|     msg: 'this is an error'
 | |
|   }, {
 | |
|     level: 60,
 | |
|     msg: 'this is fatal'
 | |
|   }]
 | |
|   const instance = pino(sink((result, enc, cb) => {
 | |
|     const current = expected.shift()
 | |
|     check(equal, result, current.level, current.msg)
 | |
|     cb()
 | |
|   })).child({ level: 30 })
 | |
| 
 | |
|   instance.level = 'error'
 | |
|   instance.info('hello world')
 | |
|   instance.error('this is an error')
 | |
|   instance.fatal('this is fatal')
 | |
| })
 | |
| 
 | |
| test('setting level by assigning a number to level', async ({ equal }) => {
 | |
|   const instance = pino()
 | |
|   equal(instance.levelVal, 30)
 | |
|   equal(instance.level, 'info')
 | |
|   instance.level = 50
 | |
|   equal(instance.levelVal, 50)
 | |
|   equal(instance.level, 'error')
 | |
| })
 | |
| 
 | |
| test('setting level by number to unknown value results in a throw', async ({ throws }) => {
 | |
|   const instance = pino()
 | |
|   throws(() => { instance.level = 973 })
 | |
| })
 | |
| 
 | |
| test('setting level by assigning a known label to level', async ({ equal }) => {
 | |
|   const instance = pino()
 | |
|   equal(instance.levelVal, 30)
 | |
|   equal(instance.level, 'info')
 | |
|   instance.level = 'error'
 | |
|   equal(instance.levelVal, 50)
 | |
|   equal(instance.level, 'error')
 | |
| })
 | |
| 
 | |
| test('levelVal is read only', async ({ throws }) => {
 | |
|   const instance = pino()
 | |
|   throws(() => { instance.levelVal = 20 })
 | |
| })
 | |
| 
 | |
| test('produces labels when told to', async ({ equal }) => {
 | |
|   const expected = [{
 | |
|     level: 'info',
 | |
|     msg: 'hello world'
 | |
|   }]
 | |
|   const instance = pino({
 | |
|     formatters: {
 | |
|       level (label, number) {
 | |
|         return { level: label }
 | |
|       }
 | |
|     }
 | |
|   }, sink((result, enc, cb) => {
 | |
|     const current = expected.shift()
 | |
|     check(equal, result, current.level, current.msg)
 | |
|     cb()
 | |
|   }))
 | |
| 
 | |
|   instance.info('hello world')
 | |
| })
 | |
| 
 | |
| test('resets levels from labels to numbers', async ({ equal }) => {
 | |
|   const expected = [{
 | |
|     level: 30,
 | |
|     msg: 'hello world'
 | |
|   }]
 | |
|   pino({ useLevelLabels: true })
 | |
|   const instance = pino({ useLevelLabels: false }, sink((result, enc, cb) => {
 | |
|     const current = expected.shift()
 | |
|     check(equal, result, current.level, current.msg)
 | |
|     cb()
 | |
|   }))
 | |
| 
 | |
|   instance.info('hello world')
 | |
| })
 | |
| 
 | |
| test('aliases changeLevelName to levelKey', async ({ equal }) => {
 | |
|   const instance = pino({ changeLevelName: 'priority' }, sink((result, enc, cb) => {
 | |
|     equal(result.priority, 30)
 | |
|     cb()
 | |
|   }))
 | |
| 
 | |
|   instance.info('hello world')
 | |
| })
 | |
| 
 | |
| test('changes label naming when told to', async ({ equal }) => {
 | |
|   const expected = [{
 | |
|     priority: 30,
 | |
|     msg: 'hello world'
 | |
|   }]
 | |
|   const instance = pino({
 | |
|     formatters: {
 | |
|       level (label, number) {
 | |
|         return { priority: number }
 | |
|       }
 | |
|     }
 | |
|   }, sink((result, enc, cb) => {
 | |
|     const current = expected.shift()
 | |
|     equal(result.priority, current.priority)
 | |
|     equal(result.msg, current.msg)
 | |
|     cb()
 | |
|   }))
 | |
| 
 | |
|   instance.info('hello world')
 | |
| })
 | |
| 
 | |
| test('children produce labels when told to', async ({ equal }) => {
 | |
|   const expected = [
 | |
|     {
 | |
|       level: 'info',
 | |
|       msg: 'child 1'
 | |
|     },
 | |
|     {
 | |
|       level: 'info',
 | |
|       msg: 'child 2'
 | |
|     }
 | |
|   ]
 | |
|   const instance = pino({
 | |
|     formatters: {
 | |
|       level (label, number) {
 | |
|         return { level: label }
 | |
|       }
 | |
|     }
 | |
|   }, sink((result, enc, cb) => {
 | |
|     const current = expected.shift()
 | |
|     check(equal, result, current.level, current.msg)
 | |
|     cb()
 | |
|   }))
 | |
| 
 | |
|   const child1 = instance.child({ name: 'child1' })
 | |
|   const child2 = child1.child({ name: 'child2' })
 | |
| 
 | |
|   child1.info('child 1')
 | |
|   child2.info('child 2')
 | |
| })
 | |
| 
 | |
| test('produces labels for custom levels', async ({ equal }) => {
 | |
|   const expected = [
 | |
|     {
 | |
|       level: 'info',
 | |
|       msg: 'hello world'
 | |
|     },
 | |
|     {
 | |
|       level: 'foo',
 | |
|       msg: 'foobar'
 | |
|     }
 | |
|   ]
 | |
|   const opts = {
 | |
|     formatters: {
 | |
|       level (label, number) {
 | |
|         return { level: label }
 | |
|       }
 | |
|     },
 | |
|     customLevels: {
 | |
|       foo: 35
 | |
|     }
 | |
|   }
 | |
|   const instance = pino(opts, sink((result, enc, cb) => {
 | |
|     const current = expected.shift()
 | |
|     check(equal, result, current.level, current.msg)
 | |
|     cb()
 | |
|   }))
 | |
| 
 | |
|   instance.info('hello world')
 | |
|   instance.foo('foobar')
 | |
| })
 | |
| 
 | |
| test('setting levelKey does not affect labels when told to', async ({ equal }) => {
 | |
|   const instance = pino(
 | |
|     {
 | |
|       formatters: {
 | |
|         level (label, number) {
 | |
|           return { priority: label }
 | |
|         }
 | |
|       }
 | |
|     },
 | |
|     sink((result, enc, cb) => {
 | |
|       equal(result.priority, 'info')
 | |
|       cb()
 | |
|     })
 | |
|   )
 | |
| 
 | |
|   instance.info('hello world')
 | |
| })
 | |
| 
 | |
| test('throws when creating a default label that does not exist in logger levels', async ({ throws }) => {
 | |
|   const defaultLevel = 'foo'
 | |
|   throws(() => {
 | |
|     pino({
 | |
|       customLevels: {
 | |
|         bar: 5
 | |
|       },
 | |
|       level: defaultLevel
 | |
|     })
 | |
|   }, `default level:${defaultLevel} must be included in custom levels`)
 | |
| })
 | |
| 
 | |
| test('throws when creating a default value that does not exist in logger levels', async ({ throws }) => {
 | |
|   const defaultLevel = 15
 | |
|   throws(() => {
 | |
|     pino({
 | |
|       customLevels: {
 | |
|         bar: 5
 | |
|       },
 | |
|       level: defaultLevel
 | |
|     })
 | |
|   }, `default level:${defaultLevel} must be included in custom levels`)
 | |
| })
 | |
| 
 | |
| test('throws when creating a default value that does not exist in logger levels', async ({ equal, throws }) => {
 | |
|   throws(() => {
 | |
|     pino({
 | |
|       customLevels: {
 | |
|         foo: 5
 | |
|       },
 | |
|       useOnlyCustomLevels: true
 | |
|     })
 | |
|   }, 'default level:info must be included in custom levels')
 | |
| })
 | |
| 
 | |
| test('passes when creating a default value that exists in logger levels', async ({ equal, throws }) => {
 | |
|   pino({
 | |
|     level: 30
 | |
|   })
 | |
| })
 | |
| 
 | |
| test('log null value when message is null', async ({ equal }) => {
 | |
|   const expected = {
 | |
|     msg: null,
 | |
|     level: 30
 | |
|   }
 | |
| 
 | |
|   const stream = sink()
 | |
|   const instance = pino(stream)
 | |
|   instance.level = 'info'
 | |
|   instance.info(null)
 | |
| 
 | |
|   const result = await once(stream, 'data')
 | |
|   check(equal, result, expected.level, expected.msg)
 | |
| })
 | |
| 
 | |
| test('formats when base param is null', async ({ equal }) => {
 | |
|   const expected = {
 | |
|     msg: 'a string',
 | |
|     level: 30
 | |
|   }
 | |
| 
 | |
|   const stream = sink()
 | |
|   const instance = pino(stream)
 | |
|   instance.level = 'info'
 | |
|   instance.info(null, 'a %s', 'string')
 | |
| 
 | |
|   const result = await once(stream, 'data')
 | |
|   check(equal, result, expected.level, expected.msg)
 | |
| })
 | |
| 
 | |
| test('fatal method sync-flushes the destination if sync flushing is available', async ({ pass, doesNotThrow, plan }) => {
 | |
|   plan(2)
 | |
|   const stream = sink()
 | |
|   stream.flushSync = () => {
 | |
|     pass('destination flushed')
 | |
|   }
 | |
|   const instance = pino(stream)
 | |
|   instance.fatal('this is fatal')
 | |
|   await once(stream, 'data')
 | |
|   doesNotThrow(() => {
 | |
|     stream.flushSync = undefined
 | |
|     instance.fatal('this is fatal')
 | |
|   })
 | |
| })
 | |
| 
 | |
| test('fatal method should call async when sync-flushing fails', ({ equal, fail, doesNotThrow, plan }) => {
 | |
|   plan(2)
 | |
|   const messages = [
 | |
|     'this is fatal 1'
 | |
|   ]
 | |
|   const stream = sink((result) => equal(result.msg, messages.shift()))
 | |
|   stream.flushSync = () => { throw new Error('Error') }
 | |
|   stream.flush = () => fail('flush should be called')
 | |
| 
 | |
|   const instance = pino(stream)
 | |
|   doesNotThrow(() => instance.fatal(messages[0]))
 | |
| })
 | |
| 
 | |
| test('calling silent method on logger instance', async ({ fail }) => {
 | |
|   const instance = pino({ level: 'silent' }, sink((result, enc) => {
 | |
|     fail('no data should be logged')
 | |
|   }))
 | |
|   instance.silent('hello world')
 | |
| })
 | |
| 
 | |
| test('calling silent method on child logger', async ({ fail }) => {
 | |
|   const child = pino({ level: 'silent' }, sink((result, enc) => {
 | |
|     fail('no data should be logged')
 | |
|   })).child({})
 | |
|   child.silent('hello world')
 | |
| })
 | |
| 
 | |
| test('changing level from info to silent and back to info', async ({ equal }) => {
 | |
|   const expected = {
 | |
|     level: 30,
 | |
|     msg: 'hello world'
 | |
|   }
 | |
|   const stream = sink()
 | |
|   const instance = pino({ level: 'info' }, stream)
 | |
| 
 | |
|   instance.level = 'silent'
 | |
|   instance.info('hello world')
 | |
|   let result = stream.read()
 | |
|   equal(result, null)
 | |
| 
 | |
|   instance.level = 'info'
 | |
|   instance.info('hello world')
 | |
|   result = await once(stream, 'data')
 | |
|   check(equal, result, expected.level, expected.msg)
 | |
| })
 | |
| 
 | |
| test('changing level from info to silent and back to info in child logger', async ({ equal }) => {
 | |
|   const expected = {
 | |
|     level: 30,
 | |
|     msg: 'hello world'
 | |
|   }
 | |
|   const stream = sink()
 | |
|   const child = pino({ level: 'info' }, stream).child({})
 | |
| 
 | |
|   child.level = 'silent'
 | |
|   child.info('hello world')
 | |
|   let result = stream.read()
 | |
|   equal(result, null)
 | |
| 
 | |
|   child.level = 'info'
 | |
|   child.info('hello world')
 | |
|   result = await once(stream, 'data')
 | |
|   check(equal, result, expected.level, expected.msg)
 | |
| })
 | |
| 
 | |
| // testing for potential loss of Pino constructor scope from serializers - an edge case with circular refs see:  https://github.com/pinojs/pino/issues/833
 | |
| test('trying to get levels when `this` is no longer a Pino instance returns an empty string', async ({ equal }) => {
 | |
|   const notPinoInstance = { some: 'object', getLevel: levelsLib.getLevel }
 | |
|   const blankedLevelValue = notPinoInstance.getLevel()
 | |
|   equal(blankedLevelValue, '')
 | |
| })
 |