fastify
Version:
Fast and low overhead web framework, for Node.js
170 lines (150 loc) • 6.15 kB
JavaScript
'use strict'
const { defaultInitOptions } = require('./initial-config-validation')
/**
* Internal logger layer that wraps Fastify's log calls with
* request-lifecycle awareness (disable-check, level selection).
* Keeps the surface intentionally small so hot paths stay fast.
*
* Users can extend this class to customize internal log lines.
*/
class LogController {
/**
* @param {object} [options]
* @param {boolean | ((req: object) => boolean)} [options.disableRequestLogging=false]
* When `true` (or a function returning `true`), per-request log lines
* (incoming, completed, errors) are suppressed.
* @param {string} [options.requestIdLogLabel='reqId']
* The label used for the request identifier when logging the request.
*/
constructor (options) {
const opts = options || {}
this.disableRequestLogging = opts.disableRequestLogging || defaultInitOptions.disableRequestLogging
this.isDisableRequestLoggingFunction = typeof this.disableRequestLogging === 'function'
this.requestIdLogLabel = opts.requestIdLogLabel || defaultInitOptions.requestIdLogLabel
}
/**
* Checks whether request logging is disabled for the given request.
*
* @param {object} req Raw or Fastify request object.
* @returns {boolean} `true` when logging should be skipped.
*/
isLogDisabled (req) {
return this.isDisableRequestLoggingFunction
? this.disableRequestLogging(req)
: this.disableRequestLogging
}
/**
* Logs an incoming request at `info` level.
*
* @param {object} request Fastify request object.
* @param {object} reply Fastify reply object.
* @param {object} [metadata] Extra contextual data (unused).
*/
incomingRequest (request, reply, metadata) {
if (this.isLogDisabled(request)) { return }
request.log.info({ req: request }, 'incoming request')
}
/**
* Logs the outcome of a completed request.
* Uses `error` level when an error is present, `info` otherwise.
*
* @param {Error | null} error Error that occurred during the response, if any.
* @param {object} request Fastify request object.
* @param {object} reply Fastify reply object.
* @param {object} [metadata] Extra contextual data (unused).
*/
requestCompleted (error, request, reply, metadata) {
if (this.isLogDisabled(request)) { return }
if (error) {
reply.log.error({ res: reply, err: error, responseTime: reply.elapsedTime }, 'request errored')
} else {
reply.log.info({ res: reply, responseTime: reply.elapsedTime }, 'request completed')
}
}
/**
* Logs an error handled by the default error handler.
* Uses `error` for 5xx status codes, `info` for 4xx.
*
* @param {Error} error The error being handled.
* @param {object} request Fastify request object.
* @param {object} reply Fastify reply object (must have `statusCode`).
* @param {object} [metadata] Extra contextual data (unused).
*/
defaultErrorLog (error, request, reply, metadata) {
if (this.isLogDisabled(request)) { return }
if (reply.statusCode >= 500) {
reply.log.error({ req: request, res: reply, err: error }, error?.message)
} else {
reply.log.info({ res: reply, err: error }, error?.message)
}
}
/**
* Logs stream-level errors that occur after headers have been sent.
* Premature closes are logged at `info`; other errors at `warn`.
*
* @param {Error} error The stream error.
* @param {object} request Fastify request object.
* @param {object} reply Fastify reply object.
* @param {object} [metadata] Extra contextual data (unused).
*/
streamError (error, request, reply, metadata) {
if (this.isLogDisabled(request)) { return }
if (error.code === 'ERR_STREAM_PREMATURE_CLOSE') {
reply.log.info({ res: reply }, 'stream closed prematurely')
} else {
reply.log.warn({ err: error }, 'response terminated with an error with headers already sent')
}
}
/**
* Logs a "route not found" message at `info` level.
* Used by the default 404 handler.
*
* @param {object} request Fastify request object.
* @param {object} reply Fastify reply object.
* @param {object} [metadata] Extra contextual data (unused).
*/
routeNotFound (request, reply, metadata) {
if (this.isLogDisabled(request)) { return }
const { url, method } = request.raw
request.log.info(`Route ${method}:${url} not found`)
}
/**
* Logs a warning when writeHead fails during error handling.
*
* @param {Error} error The error thrown by writeHead.
* @param {object} request Fastify request object.
* @param {object} reply Fastify reply object.
* @param {object} [metadata] Extra contextual data (unused).
*/
writeHeadError (error, request, reply, metadata) {
if (this.isLogDisabled(request)) { return }
reply.log.warn(
{ req: request, res: reply, err: error },
error?.message
)
}
/**
* Logs an error when the serializer for a given status code fails.
*
* @param {Error} error The serialization error.
* @param {object} request Fastify request object.
* @param {object} reply Fastify reply object.
* @param {object} metadata Extra contextual data.
* @param {number} metadata.statusCode The status code that triggered the serializer.
*/
serializerError (error, request, reply, metadata) {
if (this.isLogDisabled(request)) { return }
reply.log.error({ err: error, statusCode: metadata.statusCode }, 'The serializer for the given status code failed')
}
/**
* Logs a 503 Service Unavailable when the server is closing.
* Always emitted (not gated by `disableRequestLogging`).
*
* @param {import('../fastify').FastifyBaseLogger} logger
* @param {import('../fastify').FastifyInstance} server
*/
serviceUnavailable (logger, server) {
logger.info({ res: { statusCode: 503 } }, 'request aborted - refusing to accept new requests as server is closing')
}
}
module.exports = { LogController }