strong-tie/inbound-calls
0
1'use strict'2 3const stream = require('node:stream')4 5const t = require('tap')6const split = require('split2')7const pino = require('pino')8 9const Fastify = require('../../fastify')10const { on } = stream11 12t.test('logger options', (t) => {13 t.setTimeout(60000)14 15 t.plan(16)16 17 t.test('logger can be silenced', (t) => {18 t.plan(17)19 const fastify = Fastify({20 logger: false21 })22 t.teardown(fastify.close.bind(fastify))23 t.ok(fastify.log)24 t.equal(typeof fastify.log, 'object')25 t.equal(typeof fastify.log.fatal, 'function')26 t.equal(typeof fastify.log.error, 'function')27 t.equal(typeof fastify.log.warn, 'function')28 t.equal(typeof fastify.log.info, 'function')29 t.equal(typeof fastify.log.debug, 'function')30 t.equal(typeof fastify.log.trace, 'function')31 t.equal(typeof fastify.log.child, 'function')32 33 const childLog = fastify.log.child()34 35 t.equal(typeof childLog, 'object')36 t.equal(typeof childLog.fatal, 'function')37 t.equal(typeof childLog.error, 'function')38 t.equal(typeof childLog.warn, 'function')39 t.equal(typeof childLog.info, 'function')40 t.equal(typeof childLog.debug, 'function')41 t.equal(typeof childLog.trace, 'function')42 t.equal(typeof childLog.child, 'function')43 })44 45 t.test('Should set a custom logLevel for a plugin', async (t) => {46 const lines = ['incoming request', 'Hello', 'request completed']47 t.plan(lines.length + 2)48 49 const stream = split(JSON.parse)50 51 const loggerInstance = pino({ level: 'error' }, stream)52 53 const fastify = Fastify({54 loggerInstance55 })56 t.teardown(fastify.close.bind(fastify))57 58 fastify.get('/', (req, reply) => {59 req.log.info('Not Exist') // we should not see this log60 reply.send({ hello: 'world' })61 })62 63 fastify.register(function (instance, opts, done) {64 instance.get('/plugin', (req, reply) => {65 req.log.info('Hello') // we should see this log66 reply.send({ hello: 'world' })67 })68 done()69 }, { logLevel: 'info' })70 71 await fastify.ready()72 73 {74 const response = await fastify.inject({ method: 'GET', url: '/' })75 const body = await response.json()76 t.same(body, { hello: 'world' })77 }78 79 {80 const response = await fastify.inject({ method: 'GET', url: '/plugin' })81 const body = await response.json()82 t.same(body, { hello: 'world' })83 }84 85 for await (const [line] of on(stream, 'data')) {86 t.same(line.msg, lines.shift())87 if (lines.length === 0) break88 }89 })90 91 t.test('Should set a custom logSerializers for a plugin', async (t) => {92 const lines = ['incoming request', 'XHello', 'request completed']93 t.plan(lines.length + 1)94 95 const stream = split(JSON.parse)96 97 const loggerInstance = pino({ level: 'error' }, stream)98 99 const fastify = Fastify({100 loggerInstance101 })102 t.teardown(fastify.close.bind(fastify))103 104 fastify.register(function (instance, opts, done) {105 instance.get('/plugin', (req, reply) => {106 req.log.info({ test: 'Hello' }) // we should see this log107 reply.send({ hello: 'world' })108 })109 done()110 }, { logLevel: 'info', logSerializers: { test: value => 'X' + value } })111 112 await fastify.ready()113 114 {115 const response = await fastify.inject({ method: 'GET', url: '/plugin' })116 const body = await response.json()117 t.same(body, { hello: 'world' })118 }119 120 for await (const [line] of on(stream, 'data')) {121 // either test or msg122 t.equal(line.test || line.msg, lines.shift())123 if (lines.length === 0) break124 }125 })126 127 t.test('Should set a custom logLevel for every plugin', async (t) => {128 const lines = ['incoming request', 'info', 'request completed', 'incoming request', 'debug', 'request completed']129 t.plan(lines.length * 2 + 3)130 131 const stream = split(JSON.parse)132 133 const loggerInstance = pino({ level: 'error' }, stream)134 135 const fastify = Fastify({136 loggerInstance137 })138 t.teardown(fastify.close.bind(fastify))139 140 fastify.get('/', (req, reply) => {141 req.log.warn('Hello') // we should not see this log142 reply.send({ hello: 'world' })143 })144 145 fastify.register(function (instance, opts, done) {146 instance.get('/info', (req, reply) => {147 req.log.info('info') // we should see this log148 req.log.debug('hidden log')149 reply.send({ hello: 'world' })150 })151 done()152 }, { logLevel: 'info' })153 154 fastify.register(function (instance, opts, done) {155 instance.get('/debug', (req, reply) => {156 req.log.debug('debug') // we should see this log157 req.log.trace('hidden log')158 reply.send({ hello: 'world' })159 })160 done()161 }, { logLevel: 'debug' })162 163 await fastify.ready()164 165 {166 const response = await fastify.inject({ method: 'GET', url: '/' })167 const body = await response.json()168 t.same(body, { hello: 'world' })169 }170 171 {172 const response = await fastify.inject({ method: 'GET', url: '/info' })173 const body = await response.json()174 t.same(body, { hello: 'world' })175 }176 177 {178 const response = await fastify.inject({ method: 'GET', url: '/debug' })179 const body = await response.json()180 t.same(body, { hello: 'world' })181 }182 183 for await (const [line] of on(stream, 'data')) {184 t.ok(line.level === 30 || line.level === 20)185 t.equal(line.msg, lines.shift())186 if (lines.length === 0) break187 }188 })189 190 t.test('Should set a custom logSerializers for every plugin', async (t) => {191 const lines = ['incoming request', 'Hello', 'request completed', 'incoming request', 'XHello', 'request completed', 'incoming request', 'ZHello', 'request completed']192 t.plan(lines.length + 3)193 194 const stream = split(JSON.parse)195 196 const loggerInstance = pino({ level: 'info' }, stream)197 const fastify = Fastify({198 loggerInstance199 })200 t.teardown(fastify.close.bind(fastify))201 202 fastify.get('/', (req, reply) => {203 req.log.warn({ test: 'Hello' })204 reply.send({ hello: 'world' })205 })206 207 fastify.register(function (instance, opts, done) {208 instance.get('/test1', (req, reply) => {209 req.log.info({ test: 'Hello' })210 reply.send({ hello: 'world' })211 })212 done()213 }, { logSerializers: { test: value => 'X' + value } })214 215 fastify.register(function (instance, opts, done) {216 instance.get('/test2', (req, reply) => {217 req.log.info({ test: 'Hello' })218 reply.send({ hello: 'world' })219 })220 done()221 }, { logSerializers: { test: value => 'Z' + value } })222 223 await fastify.ready()224 225 {226 const response = await fastify.inject({ method: 'GET', url: '/' })227 const body = await response.json()228 t.same(body, { hello: 'world' })229 }230 231 {232 const response = await fastify.inject({ method: 'GET', url: '/test1' })233 const body = await response.json()234 t.same(body, { hello: 'world' })235 }236 237 {238 const response = await fastify.inject({ method: 'GET', url: '/test2' })239 const body = await response.json()240 t.same(body, { hello: 'world' })241 }242 243 for await (const [line] of on(stream, 'data')) {244 t.equal(line.test || line.msg, lines.shift())245 if (lines.length === 0) break246 }247 })248 249 t.test('Should override serializers from route', async (t) => {250 const lines = ['incoming request', 'ZHello', 'request completed']251 t.plan(lines.length + 1)252 253 const stream = split(JSON.parse)254 255 const loggerInstance = pino({ level: 'info' }, stream)256 const fastify = Fastify({257 loggerInstance258 })259 t.teardown(fastify.close.bind(fastify))260 261 fastify.register(function (instance, opts, done) {262 instance.get('/', {263 logSerializers: {264 test: value => 'Z' + value // should override265 }266 }, (req, reply) => {267 req.log.info({ test: 'Hello' })268 reply.send({ hello: 'world' })269 })270 done()271 }, { logSerializers: { test: value => 'X' + value } })272 273 await fastify.ready()274 275 {276 const response = await fastify.inject({ method: 'GET', url: '/' })277 const body = await response.json()278 t.same(body, { hello: 'world' })279 }280 281 for await (const [line] of on(stream, 'data')) {282 t.equal(line.test || line.msg, lines.shift())283 if (lines.length === 0) break284 }285 })286 287 t.test('Should override serializers from plugin', async (t) => {288 const lines = ['incoming request', 'ZHello', 'request completed']289 t.plan(lines.length + 1)290 291 const stream = split(JSON.parse)292 293 const loggerInstance = pino({ level: 'info' }, stream)294 const fastify = Fastify({295 loggerInstance296 })297 t.teardown(fastify.close.bind(fastify))298 299 fastify.register(function (instance, opts, done) {300 instance.register(context1, {301 logSerializers: {302 test: value => 'Z' + value // should override303 }304 })305 done()306 }, { logSerializers: { test: value => 'X' + value } })307 308 function context1 (instance, opts, done) {309 instance.get('/', (req, reply) => {310 req.log.info({ test: 'Hello' })311 reply.send({ hello: 'world' })312 })313 done()314 }315 316 await fastify.ready()317 318 {319 const response = await fastify.inject({ method: 'GET', url: '/' })320 const body = await response.json()321 t.same(body, { hello: 'world' })322 }323 324 for await (const [line] of on(stream, 'data')) {325 t.equal(line.test || line.msg, lines.shift())326 if (lines.length === 0) break327 }328 })329 330 t.test('Should increase the log level for a specific plugin', async (t) => {331 const lines = ['Hello']332 t.plan(lines.length * 2 + 1)333 334 const stream = split(JSON.parse)335 336 const loggerInstance = pino({ level: 'info' }, stream)337 338 const fastify = Fastify({339 loggerInstance340 })341 t.teardown(fastify.close.bind(fastify))342 343 fastify.register(function (instance, opts, done) {344 instance.get('/', (req, reply) => {345 req.log.error('Hello') // we should see this log346 reply.send({ hello: 'world' })347 })348 done()349 }, { logLevel: 'error' })350 351 await fastify.ready()352 353 {354 const response = await fastify.inject({ method: 'GET', url: '/' })355 const body = await response.json()356 t.same(body, { hello: 'world' })357 }358 359 for await (const [line] of on(stream, 'data')) {360 t.equal(line.level, 50)361 t.equal(line.msg, lines.shift())362 if (lines.length === 0) break363 }364 })365 366 t.test('Should set the log level for the customized 404 handler', async (t) => {367 const lines = ['Hello']368 t.plan(lines.length * 2 + 1)369 370 const stream = split(JSON.parse)371 372 const loggerInstance = pino({ level: 'warn' }, stream)373 374 const fastify = Fastify({375 loggerInstance376 })377 t.teardown(fastify.close.bind(fastify))378 379 fastify.register(function (instance, opts, done) {380 instance.setNotFoundHandler(function (req, reply) {381 req.log.error('Hello')382 reply.code(404).send()383 })384 done()385 }, { logLevel: 'error' })386 387 await fastify.ready()388 389 {390 const response = await fastify.inject({ method: 'GET', url: '/' })391 t.equal(response.statusCode, 404)392 }393 394 for await (const [line] of on(stream, 'data')) {395 t.equal(line.level, 50)396 t.equal(line.msg, lines.shift())397 if (lines.length === 0) break398 }399 })400 401 t.test('Should set the log level for the customized 500 handler', async (t) => {402 const lines = ['Hello']403 t.plan(lines.length * 2 + 1)404 405 const stream = split(JSON.parse)406 407 const loggerInstance = pino({ level: 'warn' }, stream)408 409 const fastify = Fastify({410 loggerInstance411 })412 t.teardown(fastify.close.bind(fastify))413 414 fastify.register(function (instance, opts, done) {415 instance.get('/', (req, reply) => {416 req.log.error('kaboom')417 reply.send(new Error('kaboom'))418 })419 420 instance.setErrorHandler(function (e, request, reply) {421 reply.log.fatal('Hello')422 reply.code(500).send()423 })424 done()425 }, { logLevel: 'fatal' })426 427 await fastify.ready()428 429 {430 const response = await fastify.inject({ method: 'GET', url: '/' })431 t.equal(response.statusCode, 500)432 }433 434 for await (const [line] of on(stream, 'data')) {435 t.equal(line.level, 60)436 t.equal(line.msg, lines.shift())437 if (lines.length === 0) break438 }439 })440 441 t.test('Should set a custom log level for a specific route', async (t) => {442 const lines = ['incoming request', 'Hello', 'request completed']443 t.plan(lines.length + 2)444 445 const stream = split(JSON.parse)446 447 const loggerInstance = pino({ level: 'error' }, stream)448 449 const fastify = Fastify({450 loggerInstance451 })452 t.teardown(fastify.close.bind(fastify))453 454 fastify.get('/log', { logLevel: 'info' }, (req, reply) => {455 req.log.info('Hello')456 reply.send({ hello: 'world' })457 })458 459 fastify.get('/no-log', (req, reply) => {460 req.log.info('Hello')461 reply.send({ hello: 'world' })462 })463 464 await fastify.ready()465 466 {467 const response = await fastify.inject({ method: 'GET', url: '/log' })468 const body = await response.json()469 t.same(body, { hello: 'world' })470 }471 472 {473 const response = await fastify.inject({ method: 'GET', url: '/no-log' })474 const body = await response.json()475 t.same(body, { hello: 'world' })476 }477 478 for await (const [line] of on(stream, 'data')) {479 t.equal(line.msg, lines.shift())480 if (lines.length === 0) break481 }482 })483 484 t.test('should pass when using unWritable props in the logger option', (t) => {485 t.plan(8)486 const fastify = Fastify({487 logger: Object.defineProperty({}, 'level', { value: 'info' })488 })489 t.teardown(fastify.close.bind(fastify))490 491 t.equal(typeof fastify.log, 'object')492 t.equal(typeof fastify.log.fatal, 'function')493 t.equal(typeof fastify.log.error, 'function')494 t.equal(typeof fastify.log.warn, 'function')495 t.equal(typeof fastify.log.info, 'function')496 t.equal(typeof fastify.log.debug, 'function')497 t.equal(typeof fastify.log.trace, 'function')498 t.equal(typeof fastify.log.child, 'function')499 })500 501 t.test('Should throw an error if logger instance is passed to `logger`', async (t) => {502 t.plan(2)503 const stream = split(JSON.parse)504 505 const logger = require('pino')(stream)506 507 try {508 Fastify({ logger })509 } catch (err) {510 t.ok(err)511 t.equal(err.code, 'FST_ERR_LOG_INVALID_LOGGER_CONFIG')512 }513 })514 515 t.test('Should throw an error if options are passed to `loggerInstance`', async (t) => {516 t.plan(2)517 try {518 Fastify({ loggerInstance: { level: 'log' } })519 } catch (err) {520 t.ok(err)521 t.equal(err.code, 'FST_ERR_LOG_INVALID_LOGGER_INSTANCE')522 }523 })524 525 t.test('If both `loggerInstance` and `logger` are provided, an error should be thrown', async (t) => {526 t.plan(2)527 const loggerInstanceStream = split(JSON.parse)528 const loggerInstance = pino({ level: 'error' }, loggerInstanceStream)529 const loggerStream = split(JSON.parse)530 try {531 Fastify({532 logger: {533 stream: loggerStream,534 level: 'info'535 },536 loggerInstance537 })538 } catch (err) {539 t.ok(err)540 t.equal(err.code, 'FST_ERR_LOG_LOGGER_AND_LOGGER_INSTANCE_PROVIDED')541 }542 })543 544 t.test('`logger` should take pino configuration and create a pino logger', async (t) => {545 const lines = ['hello', 'world']546 t.plan(2 * lines.length + 2)547 const loggerStream = split(JSON.parse)548 const fastify = Fastify({549 logger: {550 stream: loggerStream,551 level: 'error'552 }553 })554 t.teardown(fastify.close.bind(fastify))555 fastify.get('/hello', (req, reply) => {556 req.log.error('hello')557 reply.code(404).send()558 })559 560 fastify.get('/world', (req, reply) => {561 req.log.error('world')562 reply.code(201).send()563 })564 565 await fastify.ready()566 {567 const response = await fastify.inject({ method: 'GET', url: '/hello' })568 t.equal(response.statusCode, 404)569 }570 {571 const response = await fastify.inject({ method: 'GET', url: '/world' })572 t.equal(response.statusCode, 201)573 }574 575 for await (const [line] of on(loggerStream, 'data')) {576 t.equal(line.level, 50)577 t.equal(line.msg, lines.shift())578 if (lines.length === 0) break579 }580 })581})582 