UNPKG

pino

Version:

super fast, all natural json logger

514 lines (476 loc) 13.7 kB
'use strict' const os = require('os') const { join } = require('path') const { once } = require('events') const { readFile, writeFile } = require('fs').promises const { watchFileCreated, watchForWrite } = require('../helper') const { test } = require('tap') const pino = require('../../') const url = require('url') const strip = require('strip-ansi') const execa = require('execa') const writer = require('flush-write-stream') const { pid } = process const hostname = os.hostname() test('pino.transport with file', async ({ same, teardown }) => { const destination = join( os.tmpdir(), '_' + Math.random().toString(36).substr(2, 9) ) const transport = pino.transport({ target: join(__dirname, '..', 'fixtures', 'to-file-transport.js'), options: { destination } }) teardown(transport.end.bind(transport)) const instance = pino(transport) instance.info('hello') await watchFileCreated(destination) const result = JSON.parse(await readFile(destination)) delete result.time same(result, { pid, hostname, level: 30, msg: 'hello' }) }) test('pino.transport with file (no options + error handling)', async ({ equal }) => { const transport = pino.transport({ target: join(__dirname, '..', 'fixtures', 'to-file-transport.js') }) const [err] = await once(transport, 'error') equal(err.message, 'kaboom') }) test('pino.transport with file URL', async ({ same, teardown }) => { const destination = join( os.tmpdir(), '_' + Math.random().toString(36).substr(2, 9) ) const transport = pino.transport({ target: url.pathToFileURL(join(__dirname, '..', 'fixtures', 'to-file-transport.js')).href, options: { destination } }) teardown(transport.end.bind(transport)) const instance = pino(transport) instance.info('hello') await watchFileCreated(destination) const result = JSON.parse(await readFile(destination)) delete result.time same(result, { pid, hostname, level: 30, msg: 'hello' }) }) test('pino.transport errors if file does not exists', ({ plan, pass }) => { plan(1) const instance = pino.transport({ target: join(__dirname, '..', 'fixtures', 'non-existent-file'), worker: { stdin: true, stdout: true, stderr: true } }) instance.on('error', function () { pass('error received') }) }) test('pino.transport with esm', async ({ same, teardown }) => { const destination = join( os.tmpdir(), '_' + Math.random().toString(36).substr(2, 9) ) const transport = pino.transport({ target: join(__dirname, '..', 'fixtures', 'to-file-transport.mjs'), options: { destination } }) const instance = pino(transport) teardown(transport.end.bind(transport)) instance.info('hello') await watchFileCreated(destination) const result = JSON.parse(await readFile(destination)) delete result.time same(result, { pid, hostname, level: 30, msg: 'hello' }) }) test('pino.transport with two files', async ({ same, teardown }) => { const dest1 = join( os.tmpdir(), '_' + Math.random().toString(36).substr(2, 9) ) const dest2 = join( os.tmpdir(), '_' + Math.random().toString(36).substr(2, 9) ) const transport = pino.transport({ targets: [{ level: 'info', target: join(__dirname, '..', 'fixtures', 'to-file-transport.js'), options: { destination: dest1 } }, { level: 'info', target: join(__dirname, '..', 'fixtures', 'to-file-transport.js'), options: { destination: dest2 } }] }) teardown(transport.end.bind(transport)) const instance = pino(transport) instance.info('hello') await Promise.all([watchFileCreated(dest1), watchFileCreated(dest2)]) const result1 = JSON.parse(await readFile(dest1)) delete result1.time same(result1, { pid, hostname, level: 30, msg: 'hello' }) const result2 = JSON.parse(await readFile(dest2)) delete result2.time same(result2, { pid, hostname, level: 30, msg: 'hello' }) }) test('pino.transport with an array including a pino-pretty destination', async ({ same, match, teardown }) => { const dest1 = join( os.tmpdir(), '_' + Math.random().toString(36).substr(2, 9) ) const dest2 = join( os.tmpdir(), '_' + Math.random().toString(36).substr(2, 9) ) const transport = pino.transport({ targets: [{ level: 'info', target: 'pino/file', options: { destination: dest1 } }, { level: 'info', target: 'pino-pretty', options: { destination: dest2 } }] }) teardown(transport.end.bind(transport)) const instance = pino(transport) instance.info('hello') await Promise.all([watchFileCreated(dest1), watchFileCreated(dest2)]) const result1 = JSON.parse(await readFile(dest1)) delete result1.time same(result1, { pid, hostname, level: 30, msg: 'hello' }) const actual = (await readFile(dest2)).toString() match(strip(actual), /\[.*\] INFO.*hello/) }) test('no transport.end()', async ({ same, teardown }) => { const destination = join( os.tmpdir(), '_' + Math.random().toString(36).substr(2, 9) ) const transport = pino.transport({ target: join(__dirname, '..', 'fixtures', 'to-file-transport.js'), options: { destination } }) const instance = pino(transport) instance.info('hello') await watchFileCreated(destination) const result = JSON.parse(await readFile(destination)) delete result.time same(result, { pid, hostname, level: 30, msg: 'hello' }) }) test('autoEnd = false', async ({ equal, same, teardown }) => { const destination = join( os.tmpdir(), '_' + Math.random().toString(36).substr(2, 9) ) const count = process.listenerCount('exit') const transport = pino.transport({ target: join(__dirname, '..', 'fixtures', 'to-file-transport.js'), options: { destination }, worker: { autoEnd: false } }) teardown(transport.end.bind(transport)) await once(transport, 'ready') const instance = pino(transport) instance.info('hello') await watchFileCreated(destination) equal(count, process.listenerCount('exit')) const result = JSON.parse(await readFile(destination)) delete result.time same(result, { pid, hostname, level: 30, msg: 'hello' }) }) test('pino.transport with target and targets', async ({ fail, equal }) => { try { pino.transport({ target: '/a/file', targets: [{ target: '/a/file' }] }) fail('must throw') } catch (err) { equal(err.message, 'only one of target or targets can be specified') } }) test('pino.transport with target pino/file', async ({ same, teardown }) => { const destination = join( os.tmpdir(), '_' + Math.random().toString(36).substr(2, 9) ) const transport = pino.transport({ target: 'pino/file', options: { destination } }) teardown(transport.end.bind(transport)) const instance = pino(transport) instance.info('hello') await watchFileCreated(destination) const result = JSON.parse(await readFile(destination)) delete result.time same(result, { pid, hostname, level: 30, msg: 'hello' }) }) test('pino.transport with target pino/file and mkdir option', async ({ same, teardown }) => { const folder = '_' + Math.random().toString(36).substr(2, 9) const destination = join(os.tmpdir(), folder, folder) const transport = pino.transport({ target: 'pino/file', options: { destination, mkdir: true } }) teardown(transport.end.bind(transport)) const instance = pino(transport) instance.info('hello') await watchFileCreated(destination) const result = JSON.parse(await readFile(destination)) delete result.time same(result, { pid, hostname, level: 30, msg: 'hello' }) }) test('pino.transport with target pino/file and append option', async ({ same, teardown }) => { const destination = join( os.tmpdir(), '_' + Math.random().toString(36).substr(2, 9) ) await writeFile(destination, JSON.stringify({ pid, hostname, time: Date.now(), level: 30, msg: 'hello' })) const transport = pino.transport({ target: 'pino/file', options: { destination, append: false } }) teardown(transport.end.bind(transport)) const instance = pino(transport) instance.info('goodbye') await watchForWrite(destination, '"goodbye"') const result = JSON.parse(await readFile(destination)) delete result.time same(result, { pid, hostname, level: 30, msg: 'goodbye' }) }) test('pino.transport with target pino-pretty', async ({ match, teardown }) => { const destination = join( os.tmpdir(), '_' + Math.random().toString(36).substr(2, 9) ) const transport = pino.transport({ target: 'pino-pretty', options: { destination } }) teardown(transport.end.bind(transport)) const instance = pino(transport) instance.info('hello') await watchFileCreated(destination) const actual = await readFile(destination, 'utf8') match(strip(actual), /\[.*\] INFO.*hello/) }) test('stdout in worker', async ({ not }) => { let actual = '' const child = execa(process.argv[0], [join(__dirname, '..', 'fixtures', 'transport-main.js')]) child.stdout.pipe(writer((s, enc, cb) => { actual += s cb() })) await once(child, 'close') not(strip(actual).match(/Hello/), null) }) test('log and exit on ready', async ({ not }) => { let actual = '' const child = execa(process.argv[0], [join(__dirname, '..', 'fixtures', 'transport-exit-on-ready.js')]) child.stdout.pipe(writer((s, enc, cb) => { actual += s cb() })) await once(child, 'close') not(strip(actual).match(/Hello/), null) }) test('log and exit before ready', async ({ not }) => { let actual = '' const child = execa(process.argv[0], [join(__dirname, '..', 'fixtures', 'transport-exit-immediately.js')]) child.stdout.pipe(writer((s, enc, cb) => { actual += s cb() })) await once(child, 'close') not(strip(actual).match(/Hello/), null) }) test('log and exit before ready with async dest', async ({ not }) => { const destination = join( os.tmpdir(), '_' + Math.random().toString(36).substr(2, 9) ) const child = execa(process.argv[0], [join(__dirname, '..', 'fixtures', 'transport-exit-immediately-with-async-dest.js'), destination]) await once(child, 'exit') const actual = await readFile(destination, 'utf8') not(strip(actual).match(/HELLO/), null) not(strip(actual).match(/WORLD/), null) }) test('string integer destination', async ({ not }) => { let actual = '' const child = execa(process.argv[0], [join(__dirname, '..', 'fixtures', 'transport-string-stdout.js')]) child.stdout.pipe(writer((s, enc, cb) => { actual += s cb() })) await once(child, 'close') not(strip(actual).match(/Hello/), null) }) test('pino transport options with target', async ({ teardown, same }) => { const destination = join( os.tmpdir(), '_' + Math.random().toString(36).substr(2, 9) ) const instance = pino({ transport: { target: 'pino/file', options: { destination } } }) const transportStream = instance[pino.symbols.streamSym] teardown(transportStream.end.bind(transportStream)) instance.info('transport option test') await watchFileCreated(destination) const result = JSON.parse(await readFile(destination)) delete result.time same(result, { pid, hostname, level: 30, msg: 'transport option test' }) }) test('pino transport options with targets', async ({ teardown, same }) => { const dest1 = join( os.tmpdir(), '_' + Math.random().toString(36).substr(2, 9) ) const dest2 = join( os.tmpdir(), '_' + Math.random().toString(36).substr(2, 9) ) const instance = pino({ transport: { targets: [ { target: 'pino/file', options: { destination: dest1 } }, { target: 'pino/file', options: { destination: dest2 } } ] } }) const transportStream = instance[pino.symbols.streamSym] teardown(transportStream.end.bind(transportStream)) instance.info('transport option test') await Promise.all([watchFileCreated(dest1), watchFileCreated(dest2)]) const result1 = JSON.parse(await readFile(dest1)) delete result1.time same(result1, { pid, hostname, level: 30, msg: 'transport option test' }) const result2 = JSON.parse(await readFile(dest2)) delete result2.time same(result2, { pid, hostname, level: 30, msg: 'transport option test' }) }) test('transport options with target and targets', async ({ fail, equal }) => { try { pino({ transport: { target: {}, targets: {} } }) fail('must throw') } catch (err) { equal(err.message, 'only one of target or targets can be specified') } }) test('transport options with target and stream', async ({ fail, equal }) => { try { pino({ transport: { target: {} } }, '/log/null') fail('must throw') } catch (err) { equal(err.message, 'only one of option.transport or stream can be specified') } }) test('transport options with stream', async ({ fail, equal, teardown }) => { try { const dest1 = join( os.tmpdir(), '_' + Math.random().toString(36).substr(2, 9) ) const transportStream = pino.transport({ target: 'pino/file', options: { destination: dest1 } }) teardown(transportStream.end.bind(transportStream)) pino({ transport: transportStream }) fail('must throw') } catch (err) { equal(err.message, 'option.transport do not allow stream, please pass to option directly. e.g. pino(transport)') } })