fastify 5.8.5 → 5.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/PROJECT_CHARTER.md +1 -1
- package/README.md +7 -10
- package/SECURITY.md +1 -1
- package/SPONSORS.md +6 -4
- package/build/build-validation.js +2 -2
- package/docs/Guides/Database.md +0 -28
- package/docs/Guides/Delay-Accepting-Requests.md +1 -1
- package/docs/Guides/Ecosystem.md +23 -9
- package/docs/Guides/Getting-Started.md +2 -2
- package/docs/Guides/Migration-Guide-V4.md +2 -2
- package/docs/Guides/Migration-Guide-V5.md +1 -1
- package/docs/Guides/Plugins-Guide.md +1 -1
- package/docs/Guides/Prototype-Poisoning.md +3 -3
- package/docs/Guides/Serverless.md +8 -15
- package/docs/Guides/Style-Guide.md +1 -1
- package/docs/Guides/Write-Plugin.md +1 -1
- package/docs/Reference/Encapsulation.md +27 -26
- package/docs/Reference/Errors.md +12 -4
- package/docs/Reference/HTTP2.md +10 -10
- package/docs/Reference/Hooks.md +5 -5
- package/docs/Reference/Index.md +14 -16
- package/docs/Reference/LTS.md +12 -13
- package/docs/Reference/Lifecycle.md +9 -8
- package/docs/Reference/Logging.md +47 -39
- package/docs/Reference/Middleware.md +21 -25
- package/docs/Reference/Principles.md +2 -2
- package/docs/Reference/Reply.md +6 -1
- package/docs/Reference/Request.md +29 -18
- package/docs/Reference/Routes.md +5 -2
- package/docs/Reference/Server.md +138 -11
- package/docs/Reference/Type-Providers.md +53 -9
- package/docs/Reference/TypeScript.md +3 -3
- package/docs/Reference/Validation-and-Serialization.md +15 -2
- package/docs/Reference/Warnings.md +11 -6
- package/eslint.config.js +7 -2
- package/fastify.d.ts +13 -3
- package/fastify.js +60 -31
- package/lib/content-type-parser.js +2 -0
- package/lib/content-type.js +34 -1
- package/lib/context.js +0 -3
- package/lib/decorate.js +11 -3
- package/lib/error-handler.js +10 -28
- package/lib/error-serializer.js +59 -59
- package/lib/errors.js +23 -1
- package/lib/four-oh-four.js +22 -19
- package/lib/handle-request.js +12 -5
- package/lib/log-controller.js +169 -0
- package/lib/logger-factory.js +25 -4
- package/lib/plugin-override.js +2 -1
- package/lib/plugin-utils.js +5 -5
- package/lib/reply.js +96 -50
- package/lib/req-id-gen-factory.js +4 -1
- package/lib/request.js +16 -6
- package/lib/route.js +47 -41
- package/lib/schema-controller.js +1 -1
- package/lib/schemas.js +37 -30
- package/lib/symbols.js +4 -2
- package/lib/validation.js +10 -13
- package/lib/warnings.js +22 -4
- package/package.json +15 -17
- package/scripts/validate-ecosystem-links.js +1 -0
- package/test/bundler/esbuild/package.json +1 -1
- package/test/close-pipelining.test.js +1 -2
- package/test/content-type.test.js +20 -0
- package/test/custom-http-server.test.js +38 -0
- package/test/decorator-instance-properties.test.js +63 -0
- package/test/diagnostics-channel/async-error-handler.test.js +74 -0
- package/test/genReqId.test.js +24 -0
- package/test/hooks.test.js +94 -0
- package/test/http-methods/get.test.js +1 -1
- package/test/http2/plain.test.js +135 -0
- package/test/http2/secure-with-fallback.test.js +1 -1
- package/test/https/https.test.js +1 -2
- package/test/internals/errors.test.js +31 -1
- package/test/internals/logger.test.js +322 -0
- package/test/internals/plugin.test.js +3 -1
- package/test/internals/reply.test.js +35 -4
- package/test/internals/request.test.js +37 -10
- package/test/internals/schema-controller-perf.test.js +33 -0
- package/test/logger/logging.test.js +57 -1
- package/test/logger/options.test.js +38 -1
- package/test/reply-error.test.js +1 -1
- package/test/reply-trailers.test.js +70 -0
- package/test/request-media-type.test.js +105 -0
- package/test/route-prefix.test.js +34 -0
- package/test/router-options.test.js +222 -11
- package/test/schema-serialization.test.js +108 -0
- package/test/schema-validation.test.js +24 -0
- package/test/scripts/validate-ecosystem-links.test.js +40 -57
- package/test/stream.4.test.js +2 -2
- package/test/throw.test.js +14 -0
- package/test/trust-proxy.test.js +70 -30
- package/test/types/content-type-parser.tst.ts +70 -0
- package/test/types/{decorate-request-reply.test-d.ts → decorate-request-reply.tst.ts} +4 -4
- package/test/types/{dummy-plugin.ts → dummy-plugin.mts} +1 -1
- package/test/types/errors.tst.ts +91 -0
- package/test/types/fastify.tst.ts +351 -0
- package/test/types/hooks.tst.ts +578 -0
- package/test/types/instance.tst.ts +597 -0
- package/test/types/{logger.test-d.ts → logger.tst.ts} +61 -62
- package/test/types/plugin.tst.ts +96 -0
- package/test/types/register.tst.ts +245 -0
- package/test/types/reply.tst.ts +297 -0
- package/test/types/request.tst.ts +199 -0
- package/test/types/route.tst.ts +576 -0
- package/test/types/{schema.test-d.ts → schema.tst.ts} +22 -22
- package/test/types/{serverFactory.test-d.ts → serverFactory.tst.ts} +4 -4
- package/test/types/tsconfig.json +9 -0
- package/test/types/{type-provider.test-d.ts → type-provider.tst.ts} +256 -250
- package/test/types/using.tst.ts +14 -0
- package/types/errors.d.ts +4 -0
- package/types/instance.d.ts +2 -0
- package/types/logger.d.ts +22 -0
- package/types/request.d.ts +23 -2
- package/test/types/content-type-parser.test-d.ts +0 -72
- package/test/types/errors.test-d.ts +0 -90
- package/test/types/fastify.test-d.ts +0 -352
- package/test/types/hooks.test-d.ts +0 -550
- package/test/types/import.ts +0 -2
- package/test/types/instance.test-d.ts +0 -588
- package/test/types/plugin.test-d.ts +0 -97
- package/test/types/register.test-d.ts +0 -237
- package/test/types/reply.test-d.ts +0 -254
- package/test/types/request.test-d.ts +0 -188
- package/test/types/route.test-d.ts +0 -553
- package/test/types/using.test-d.ts +0 -17
package/lib/errors.js
CHANGED
|
@@ -243,6 +243,13 @@ const codes = {
|
|
|
243
243
|
TypeError
|
|
244
244
|
),
|
|
245
245
|
|
|
246
|
+
FST_ERR_LOG_INVALID_LOG_CONTROLLER: createError(
|
|
247
|
+
'FST_ERR_LOG_INVALID_LOG_CONTROLLER',
|
|
248
|
+
"logController option must be an instance of LogController, got '%s'.",
|
|
249
|
+
500,
|
|
250
|
+
TypeError
|
|
251
|
+
),
|
|
252
|
+
|
|
246
253
|
/**
|
|
247
254
|
* reply
|
|
248
255
|
*/
|
|
@@ -364,6 +371,12 @@ const codes = {
|
|
|
364
371
|
400,
|
|
365
372
|
URIError
|
|
366
373
|
),
|
|
374
|
+
FST_ERR_MAX_PARAM_LENGTH: createError(
|
|
375
|
+
'FST_ERR_MAX_PARAM_LENGTH',
|
|
376
|
+
"'%s' is exceeding the max param length",
|
|
377
|
+
414,
|
|
378
|
+
URIError
|
|
379
|
+
),
|
|
367
380
|
FST_ERR_ASYNC_CONSTRAINT: createError(
|
|
368
381
|
'FST_ERR_ASYNC_CONSTRAINT',
|
|
369
382
|
'Unexpected error from async constraint',
|
|
@@ -408,6 +421,12 @@ const codes = {
|
|
|
408
421
|
'%s method is not supported.',
|
|
409
422
|
500
|
|
410
423
|
),
|
|
424
|
+
FST_ERR_ROUTE_LOG_LEVEL_INVALID: createError(
|
|
425
|
+
'FST_ERR_ROUTE_LOG_LEVEL_INVALID',
|
|
426
|
+
"Log level for '%s:%s' route must be a valid logger level. Received: '%s'",
|
|
427
|
+
500,
|
|
428
|
+
TypeError
|
|
429
|
+
),
|
|
411
430
|
FST_ERR_ROUTE_BODY_VALIDATION_SCHEMA_NOT_SUPPORTED: createError(
|
|
412
431
|
'FST_ERR_ROUTE_BODY_VALIDATION_SCHEMA_NOT_SUPPORTED',
|
|
413
432
|
'Body validation schema for %s:%s route is not supported!',
|
|
@@ -470,7 +489,10 @@ const codes = {
|
|
|
470
489
|
500,
|
|
471
490
|
TypeError
|
|
472
491
|
),
|
|
473
|
-
|
|
492
|
+
FST_ERR_PLUGIN_DEPENDENCY_NOT_REGISTERED: createError(
|
|
493
|
+
'FST_ERR_PLUGIN_DEPENDENCY_NOT_REGISTERED',
|
|
494
|
+
"The dependency '%s' of plugin '%s' is not registered"
|
|
495
|
+
),
|
|
474
496
|
/**
|
|
475
497
|
* Avvio Errors
|
|
476
498
|
*/
|
package/lib/four-oh-four.js
CHANGED
|
@@ -11,7 +11,8 @@ const {
|
|
|
11
11
|
kFourOhFourLevelInstance,
|
|
12
12
|
kFourOhFourContext,
|
|
13
13
|
kHooks,
|
|
14
|
-
kErrorHandler
|
|
14
|
+
kErrorHandler,
|
|
15
|
+
kLogController
|
|
15
16
|
} = require('./symbols.js')
|
|
16
17
|
const { lifecycleHooks } = require('./hooks')
|
|
17
18
|
const { buildErrorHandler } = require('./error-handler.js')
|
|
@@ -29,11 +30,15 @@ const { getGenReqId } = require('./req-id-gen-factory.js')
|
|
|
29
30
|
* kFourOhFourContext: the context in the reply object where the handler will be executed
|
|
30
31
|
*/
|
|
31
32
|
function fourOhFour (options) {
|
|
32
|
-
const { logger
|
|
33
|
+
const { logger } = options
|
|
33
34
|
|
|
34
35
|
// 404 router, used for handling encapsulated 404 handlers
|
|
35
|
-
const router = FindMyWay({
|
|
36
|
-
|
|
36
|
+
const router = FindMyWay({
|
|
37
|
+
onBadUrl: createRouteEventHandler(),
|
|
38
|
+
onMaxParamLength: createRouteEventHandler(),
|
|
39
|
+
defaultRoute: fourOhFourFallBack
|
|
40
|
+
})
|
|
41
|
+
let _routeEventHandler = null
|
|
37
42
|
|
|
38
43
|
return { router, setNotFoundHandler, setContext, arrange404 }
|
|
39
44
|
|
|
@@ -43,32 +48,29 @@ function fourOhFour (options) {
|
|
|
43
48
|
instance[kCanSetNotFoundHandler] = true
|
|
44
49
|
// we need to bind instance for the context
|
|
45
50
|
router.onBadUrl = router.onBadUrl.bind(instance)
|
|
51
|
+
router.onMaxParamLength = router.onMaxParamLength.bind(instance)
|
|
46
52
|
router.defaultRoute = router.defaultRoute.bind(instance)
|
|
47
53
|
}
|
|
48
54
|
|
|
49
55
|
function basic404 (request, reply) {
|
|
56
|
+
request.server[kLogController].routeNotFound(request, reply)
|
|
50
57
|
const { url, method } = request.raw
|
|
51
|
-
const message = `Route ${method}:${url} not found`
|
|
52
|
-
const resolvedDisableRequestLogging = typeof disableRequestLogging === 'function' ? disableRequestLogging(request.raw) : disableRequestLogging
|
|
53
|
-
if (!resolvedDisableRequestLogging) {
|
|
54
|
-
request.log.info(message)
|
|
55
|
-
}
|
|
56
58
|
reply.code(404).send({
|
|
57
|
-
message
|
|
59
|
+
message: `Route ${method}:${url} not found`,
|
|
58
60
|
error: 'Not Found',
|
|
59
61
|
statusCode: 404
|
|
60
62
|
})
|
|
61
63
|
}
|
|
62
64
|
|
|
63
|
-
function
|
|
64
|
-
return function
|
|
65
|
+
function createRouteEventHandler () {
|
|
66
|
+
return function onRouteEvent (path, req, res) {
|
|
65
67
|
const fourOhFourContext = this[kFourOhFourLevelInstance][kFourOhFourContext]
|
|
66
68
|
const id = getGenReqId(fourOhFourContext.server, req)
|
|
67
69
|
const childLogger = createChildLogger(fourOhFourContext, logger, req, id)
|
|
68
70
|
const request = new Request(id, null, req, null, childLogger, fourOhFourContext)
|
|
69
71
|
const reply = new Reply(res, request, childLogger)
|
|
70
72
|
|
|
71
|
-
|
|
73
|
+
_routeEventHandler(request, reply)
|
|
72
74
|
}
|
|
73
75
|
}
|
|
74
76
|
|
|
@@ -121,12 +123,12 @@ function fourOhFour (options) {
|
|
|
121
123
|
if (handler) {
|
|
122
124
|
this[kFourOhFourLevelInstance][kCanSetNotFoundHandler] = false
|
|
123
125
|
handler = handler.bind(this)
|
|
124
|
-
// update
|
|
125
|
-
|
|
126
|
+
// update route event handler
|
|
127
|
+
_routeEventHandler = handler
|
|
126
128
|
} else {
|
|
127
129
|
handler = basic404
|
|
128
|
-
// update
|
|
129
|
-
|
|
130
|
+
// update route event handler
|
|
131
|
+
_routeEventHandler = basic404
|
|
130
132
|
}
|
|
131
133
|
|
|
132
134
|
this.after((notHandledErr, done) => {
|
|
@@ -176,11 +178,12 @@ function fourOhFour (options) {
|
|
|
176
178
|
const id = getGenReqId(fourOhFourContext.server, req)
|
|
177
179
|
const childLogger = createChildLogger(fourOhFourContext, logger, req, id)
|
|
178
180
|
|
|
179
|
-
childLogger.info({ req }, 'incoming request')
|
|
180
|
-
|
|
181
181
|
const request = new Request(id, null, req, null, childLogger, fourOhFourContext)
|
|
182
182
|
const reply = new Reply(res, request, childLogger)
|
|
183
183
|
|
|
184
|
+
fourOhFourContext.server[kLogController].incomingRequest(request, reply)
|
|
185
|
+
|
|
186
|
+
// Since this could be a bug, we log it without checking if the logging is disabled
|
|
184
187
|
request.log.warn('the default handler for 404 did not catch this, this is likely a fastify bug, please report it')
|
|
185
188
|
request.log.warn(router.prettyPrint())
|
|
186
189
|
reply.code(404).send(new FST_ERR_NOT_FOUND())
|
package/lib/handle-request.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const diagnostics = require('node:diagnostics_channel')
|
|
4
|
-
const ContentType = require('./content-type')
|
|
5
4
|
const wrapThenable = require('./wrap-thenable')
|
|
6
5
|
const { validate: validateSchema } = require('./validation')
|
|
7
6
|
const { preValidationHookRunner, preHandlerHookRunner } = require('./hooks')
|
|
@@ -11,8 +10,11 @@ const {
|
|
|
11
10
|
kReplyIsError,
|
|
12
11
|
kRouteContext,
|
|
13
12
|
kFourOhFourContext,
|
|
14
|
-
kSupportedHTTPMethods
|
|
13
|
+
kSupportedHTTPMethods,
|
|
14
|
+
kRequestContentType,
|
|
15
|
+
kDiagnosticsStore
|
|
15
16
|
} = require('./symbols')
|
|
17
|
+
const ContentType = require('./content-type')
|
|
16
18
|
|
|
17
19
|
const channels = diagnostics.tracingChannel('fastify.request.handler')
|
|
18
20
|
|
|
@@ -51,13 +53,17 @@ function handleRequest (err, request, reply) {
|
|
|
51
53
|
return
|
|
52
54
|
}
|
|
53
55
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
+
// Conditional assignment to avoid creating a new ContentType instance
|
|
57
|
+
// It can be assigned when accessing the .mediaType in hooks
|
|
58
|
+
if (!request[kRequestContentType]) {
|
|
59
|
+
request[kRequestContentType] = ContentType.from(ctHeader)
|
|
60
|
+
}
|
|
61
|
+
if (request[kRequestContentType].isValid === false) {
|
|
56
62
|
reply[kReplyIsError] = true
|
|
57
63
|
reply.status(415).send(new FST_ERR_CTP_INVALID_MEDIA_TYPE())
|
|
58
64
|
return
|
|
59
65
|
}
|
|
60
|
-
request[kRouteContext].contentTypeParser.run(
|
|
66
|
+
request[kRouteContext].contentTypeParser.run(request[kRequestContentType].toString(), handler, request, reply)
|
|
61
67
|
return
|
|
62
68
|
}
|
|
63
69
|
|
|
@@ -142,6 +148,7 @@ function preHandlerCallback (err, request, reply) {
|
|
|
142
148
|
method: context.config.method
|
|
143
149
|
}
|
|
144
150
|
}
|
|
151
|
+
reply[kDiagnosticsStore] = store
|
|
145
152
|
channels.start.runStores(store, preHandlerCallbackInner, undefined, err, request, reply, store)
|
|
146
153
|
}
|
|
147
154
|
}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { defaultInitOptions } = require('./initial-config-validation')
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Internal logger layer that wraps Fastify's log calls with
|
|
7
|
+
* request-lifecycle awareness (disable-check, level selection).
|
|
8
|
+
* Keeps the surface intentionally small so hot paths stay fast.
|
|
9
|
+
*
|
|
10
|
+
* Users can extend this class to customize internal log lines.
|
|
11
|
+
*/
|
|
12
|
+
class LogController {
|
|
13
|
+
/**
|
|
14
|
+
* @param {object} [options]
|
|
15
|
+
* @param {boolean | ((req: object) => boolean)} [options.disableRequestLogging=false]
|
|
16
|
+
* When `true` (or a function returning `true`), per-request log lines
|
|
17
|
+
* (incoming, completed, errors) are suppressed.
|
|
18
|
+
* @param {string} [options.requestIdLogLabel='reqId']
|
|
19
|
+
* The label used for the request identifier when logging the request.
|
|
20
|
+
*/
|
|
21
|
+
constructor (options) {
|
|
22
|
+
const opts = options || {}
|
|
23
|
+
this.disableRequestLogging = opts.disableRequestLogging || defaultInitOptions.disableRequestLogging
|
|
24
|
+
this.isDisableRequestLoggingFunction = typeof this.disableRequestLogging === 'function'
|
|
25
|
+
this.requestIdLogLabel = opts.requestIdLogLabel || defaultInitOptions.requestIdLogLabel
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Checks whether request logging is disabled for the given request.
|
|
30
|
+
*
|
|
31
|
+
* @param {object} req Raw or Fastify request object.
|
|
32
|
+
* @returns {boolean} `true` when logging should be skipped.
|
|
33
|
+
*/
|
|
34
|
+
isLogDisabled (req) {
|
|
35
|
+
return this.isDisableRequestLoggingFunction
|
|
36
|
+
? this.disableRequestLogging(req)
|
|
37
|
+
: this.disableRequestLogging
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Logs an incoming request at `info` level.
|
|
42
|
+
*
|
|
43
|
+
* @param {object} request Fastify request object.
|
|
44
|
+
* @param {object} reply Fastify reply object.
|
|
45
|
+
* @param {object} [metadata] Extra contextual data (unused).
|
|
46
|
+
*/
|
|
47
|
+
incomingRequest (request, reply, metadata) {
|
|
48
|
+
if (this.isLogDisabled(request)) { return }
|
|
49
|
+
|
|
50
|
+
request.log.info({ req: request }, 'incoming request')
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Logs the outcome of a completed request.
|
|
55
|
+
* Uses `error` level when an error is present, `info` otherwise.
|
|
56
|
+
*
|
|
57
|
+
* @param {Error | null} error Error that occurred during the response, if any.
|
|
58
|
+
* @param {object} request Fastify request object.
|
|
59
|
+
* @param {object} reply Fastify reply object.
|
|
60
|
+
* @param {object} [metadata] Extra contextual data (unused).
|
|
61
|
+
*/
|
|
62
|
+
requestCompleted (error, request, reply, metadata) {
|
|
63
|
+
if (this.isLogDisabled(request)) { return }
|
|
64
|
+
|
|
65
|
+
if (error) {
|
|
66
|
+
reply.log.error({ res: reply, err: error, responseTime: reply.elapsedTime }, 'request errored')
|
|
67
|
+
} else {
|
|
68
|
+
reply.log.info({ res: reply, responseTime: reply.elapsedTime }, 'request completed')
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Logs an error handled by the default error handler.
|
|
74
|
+
* Uses `error` for 5xx status codes, `info` for 4xx.
|
|
75
|
+
*
|
|
76
|
+
* @param {Error} error The error being handled.
|
|
77
|
+
* @param {object} request Fastify request object.
|
|
78
|
+
* @param {object} reply Fastify reply object (must have `statusCode`).
|
|
79
|
+
* @param {object} [metadata] Extra contextual data (unused).
|
|
80
|
+
*/
|
|
81
|
+
defaultErrorLog (error, request, reply, metadata) {
|
|
82
|
+
if (this.isLogDisabled(request)) { return }
|
|
83
|
+
|
|
84
|
+
if (reply.statusCode >= 500) {
|
|
85
|
+
reply.log.error({ req: request, res: reply, err: error }, error?.message)
|
|
86
|
+
} else {
|
|
87
|
+
reply.log.info({ res: reply, err: error }, error?.message)
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Logs stream-level errors that occur after headers have been sent.
|
|
93
|
+
* Premature closes are logged at `info`; other errors at `warn`.
|
|
94
|
+
*
|
|
95
|
+
* @param {Error} error The stream error.
|
|
96
|
+
* @param {object} request Fastify request object.
|
|
97
|
+
* @param {object} reply Fastify reply object.
|
|
98
|
+
* @param {object} [metadata] Extra contextual data (unused).
|
|
99
|
+
*/
|
|
100
|
+
streamError (error, request, reply, metadata) {
|
|
101
|
+
if (this.isLogDisabled(request)) { return }
|
|
102
|
+
|
|
103
|
+
if (error.code === 'ERR_STREAM_PREMATURE_CLOSE') {
|
|
104
|
+
reply.log.info({ res: reply }, 'stream closed prematurely')
|
|
105
|
+
} else {
|
|
106
|
+
reply.log.warn({ err: error }, 'response terminated with an error with headers already sent')
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Logs a "route not found" message at `info` level.
|
|
112
|
+
* Used by the default 404 handler.
|
|
113
|
+
*
|
|
114
|
+
* @param {object} request Fastify request object.
|
|
115
|
+
* @param {object} reply Fastify reply object.
|
|
116
|
+
* @param {object} [metadata] Extra contextual data (unused).
|
|
117
|
+
*/
|
|
118
|
+
routeNotFound (request, reply, metadata) {
|
|
119
|
+
if (this.isLogDisabled(request)) { return }
|
|
120
|
+
|
|
121
|
+
const { url, method } = request.raw
|
|
122
|
+
request.log.info(`Route ${method}:${url} not found`)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Logs a warning when writeHead fails during error handling.
|
|
127
|
+
*
|
|
128
|
+
* @param {Error} error The error thrown by writeHead.
|
|
129
|
+
* @param {object} request Fastify request object.
|
|
130
|
+
* @param {object} reply Fastify reply object.
|
|
131
|
+
* @param {object} [metadata] Extra contextual data (unused).
|
|
132
|
+
*/
|
|
133
|
+
writeHeadError (error, request, reply, metadata) {
|
|
134
|
+
if (this.isLogDisabled(request)) { return }
|
|
135
|
+
|
|
136
|
+
reply.log.warn(
|
|
137
|
+
{ req: request, res: reply, err: error },
|
|
138
|
+
error?.message
|
|
139
|
+
)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Logs an error when the serializer for a given status code fails.
|
|
144
|
+
*
|
|
145
|
+
* @param {Error} error The serialization error.
|
|
146
|
+
* @param {object} request Fastify request object.
|
|
147
|
+
* @param {object} reply Fastify reply object.
|
|
148
|
+
* @param {object} metadata Extra contextual data.
|
|
149
|
+
* @param {number} metadata.statusCode The status code that triggered the serializer.
|
|
150
|
+
*/
|
|
151
|
+
serializerError (error, request, reply, metadata) {
|
|
152
|
+
if (this.isLogDisabled(request)) { return }
|
|
153
|
+
|
|
154
|
+
reply.log.error({ err: error, statusCode: metadata.statusCode }, 'The serializer for the given status code failed')
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Logs a 503 Service Unavailable when the server is closing.
|
|
159
|
+
* Always emitted (not gated by `disableRequestLogging`).
|
|
160
|
+
*
|
|
161
|
+
* @param {import('../fastify').FastifyBaseLogger} logger
|
|
162
|
+
* @param {import('../fastify').FastifyInstance} server
|
|
163
|
+
*/
|
|
164
|
+
serviceUnavailable (logger, server) {
|
|
165
|
+
logger.info({ res: { statusCode: 503 } }, 'request aborted - refusing to accept new requests as server is closing')
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
module.exports = { LogController }
|
package/lib/logger-factory.js
CHANGED
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const { performance } = require('node:perf_hooks')
|
|
4
|
+
|
|
3
5
|
const {
|
|
4
6
|
FST_ERR_LOG_LOGGER_AND_LOGGER_INSTANCE_PROVIDED,
|
|
5
7
|
FST_ERR_LOG_INVALID_LOGGER_CONFIG,
|
|
6
8
|
FST_ERR_LOG_INVALID_LOGGER_INSTANCE,
|
|
7
|
-
FST_ERR_LOG_INVALID_LOGGER
|
|
9
|
+
FST_ERR_LOG_INVALID_LOGGER,
|
|
10
|
+
FST_ERR_LOG_INVALID_LOG_CONTROLLER
|
|
8
11
|
} = require('./errors')
|
|
12
|
+
const {
|
|
13
|
+
kLogController
|
|
14
|
+
} = require('./symbols.js')
|
|
15
|
+
const { LogController } = require('./log-controller.js')
|
|
9
16
|
|
|
10
17
|
/**
|
|
11
18
|
* Utility for creating a child logger with the appropriate bindings, logger factory
|
|
@@ -21,7 +28,7 @@ const {
|
|
|
21
28
|
*/
|
|
22
29
|
function createChildLogger (context, logger, req, reqId, loggerOpts) {
|
|
23
30
|
const loggerBindings = {
|
|
24
|
-
[context.requestIdLogLabel]: reqId
|
|
31
|
+
[context.server[kLogController].requestIdLogLabel]: reqId
|
|
25
32
|
}
|
|
26
33
|
const child = context.childLoggerFactory.call(context.server, logger, loggerBindings, loggerOpts || {}, req)
|
|
27
34
|
|
|
@@ -122,15 +129,29 @@ function createLogger (options) {
|
|
|
122
129
|
return { logger, hasLogger: true }
|
|
123
130
|
}
|
|
124
131
|
|
|
132
|
+
function createLogController (options) {
|
|
133
|
+
const userController = options.logController
|
|
134
|
+
if (!userController) {
|
|
135
|
+
return new LogController(options)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (userController instanceof LogController) {
|
|
139
|
+
return userController
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
throw new FST_ERR_LOG_INVALID_LOG_CONTROLLER(typeof userController)
|
|
143
|
+
}
|
|
144
|
+
|
|
125
145
|
function now () {
|
|
126
|
-
|
|
127
|
-
return (ts[0] * 1e3) + (ts[1] / 1e6)
|
|
146
|
+
return performance.now()
|
|
128
147
|
}
|
|
129
148
|
|
|
130
149
|
module.exports = {
|
|
131
150
|
createChildLogger,
|
|
132
151
|
defaultChildLoggerFactory,
|
|
152
|
+
createLogController,
|
|
133
153
|
createLogger,
|
|
154
|
+
LogController,
|
|
134
155
|
validateLogger,
|
|
135
156
|
now
|
|
136
157
|
}
|
package/lib/plugin-override.js
CHANGED
|
@@ -82,7 +82,8 @@ function buildRoutePrefix (instancePrefix, pluginPrefix) {
|
|
|
82
82
|
if (instancePrefix.endsWith('/') && pluginPrefix[0] === '/') {
|
|
83
83
|
// Remove the extra '/' to avoid: '/first//second'
|
|
84
84
|
pluginPrefix = pluginPrefix.slice(1)
|
|
85
|
-
} else if (pluginPrefix[0] !== '/') {
|
|
85
|
+
} else if (pluginPrefix[0] !== '/' && !instancePrefix.endsWith('/')) {
|
|
86
|
+
// Add the missing '/' to avoid: '/firstsecond'
|
|
86
87
|
pluginPrefix = '/' + pluginPrefix
|
|
87
88
|
}
|
|
88
89
|
|
package/lib/plugin-utils.js
CHANGED
|
@@ -10,7 +10,8 @@ const { exist, existReply, existRequest } = require('./decorate.js')
|
|
|
10
10
|
const {
|
|
11
11
|
FST_ERR_PLUGIN_VERSION_MISMATCH,
|
|
12
12
|
FST_ERR_PLUGIN_NOT_PRESENT_IN_INSTANCE,
|
|
13
|
-
FST_ERR_PLUGIN_INVALID_ASYNC_HANDLER
|
|
13
|
+
FST_ERR_PLUGIN_INVALID_ASYNC_HANDLER,
|
|
14
|
+
FST_ERR_PLUGIN_DEPENDENCY_NOT_REGISTERED
|
|
14
15
|
} = require('./errors.js')
|
|
15
16
|
|
|
16
17
|
const rcRegex = /-(?:rc|pre|alpha).+$/u
|
|
@@ -70,10 +71,9 @@ function checkDependencies (fn) {
|
|
|
70
71
|
assert(Array.isArray(dependencies), 'The dependencies should be an array of strings')
|
|
71
72
|
|
|
72
73
|
dependencies.forEach(dependency => {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
)
|
|
74
|
+
if (!this[kRegisteredPlugins].includes(dependency)) {
|
|
75
|
+
throw new FST_ERR_PLUGIN_DEPENDENCY_NOT_REGISTERED(dependency, meta.name)
|
|
76
|
+
}
|
|
77
77
|
})
|
|
78
78
|
}
|
|
79
79
|
|