node-fastify 5.8.3

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.
Files changed (354) hide show
  1. package/.borp.yaml +3 -0
  2. package/.markdownlint-cli2.yaml +22 -0
  3. package/.prettierignore +1 -0
  4. package/GOVERNANCE.md +4 -0
  5. package/LICENSE +21 -0
  6. package/PROJECT_CHARTER.md +126 -0
  7. package/README.md +423 -0
  8. package/SECURITY.md +220 -0
  9. package/SPONSORS.md +24 -0
  10. package/build/build-error-serializer.js +35 -0
  11. package/build/build-validation.js +169 -0
  12. package/build/sync-version.js +11 -0
  13. package/docs/Guides/Benchmarking.md +60 -0
  14. package/docs/Guides/Database.md +321 -0
  15. package/docs/Guides/Delay-Accepting-Requests.md +608 -0
  16. package/docs/Guides/Detecting-When-Clients-Abort.md +172 -0
  17. package/docs/Guides/Ecosystem.md +726 -0
  18. package/docs/Guides/Fluent-Schema.md +127 -0
  19. package/docs/Guides/Getting-Started.md +620 -0
  20. package/docs/Guides/Index.md +43 -0
  21. package/docs/Guides/Migration-Guide-V3.md +287 -0
  22. package/docs/Guides/Migration-Guide-V4.md +267 -0
  23. package/docs/Guides/Migration-Guide-V5.md +727 -0
  24. package/docs/Guides/Plugins-Guide.md +520 -0
  25. package/docs/Guides/Prototype-Poisoning.md +383 -0
  26. package/docs/Guides/Recommendations.md +378 -0
  27. package/docs/Guides/Serverless.md +604 -0
  28. package/docs/Guides/Style-Guide.md +246 -0
  29. package/docs/Guides/Testing.md +481 -0
  30. package/docs/Guides/Write-Plugin.md +103 -0
  31. package/docs/Guides/Write-Type-Provider.md +34 -0
  32. package/docs/Reference/ContentTypeParser.md +271 -0
  33. package/docs/Reference/Decorators.md +436 -0
  34. package/docs/Reference/Encapsulation.md +194 -0
  35. package/docs/Reference/Errors.md +377 -0
  36. package/docs/Reference/HTTP2.md +94 -0
  37. package/docs/Reference/Hooks.md +958 -0
  38. package/docs/Reference/Index.md +73 -0
  39. package/docs/Reference/LTS.md +86 -0
  40. package/docs/Reference/Lifecycle.md +99 -0
  41. package/docs/Reference/Logging.md +268 -0
  42. package/docs/Reference/Middleware.md +79 -0
  43. package/docs/Reference/Plugins.md +245 -0
  44. package/docs/Reference/Principles.md +73 -0
  45. package/docs/Reference/Reply.md +1001 -0
  46. package/docs/Reference/Request.md +295 -0
  47. package/docs/Reference/Routes.md +802 -0
  48. package/docs/Reference/Server.md +2389 -0
  49. package/docs/Reference/Type-Providers.md +256 -0
  50. package/docs/Reference/TypeScript.md +1729 -0
  51. package/docs/Reference/Validation-and-Serialization.md +1130 -0
  52. package/docs/Reference/Warnings.md +58 -0
  53. package/docs/index.md +24 -0
  54. package/docs/resources/encapsulation_context.drawio +1 -0
  55. package/docs/resources/encapsulation_context.svg +3 -0
  56. package/eslint.config.js +35 -0
  57. package/examples/asyncawait.js +38 -0
  58. package/examples/benchmark/body.json +3 -0
  59. package/examples/benchmark/hooks-benchmark-async-await.js +44 -0
  60. package/examples/benchmark/hooks-benchmark.js +52 -0
  61. package/examples/benchmark/parser.js +47 -0
  62. package/examples/benchmark/simple.js +30 -0
  63. package/examples/benchmark/webstream.js +27 -0
  64. package/examples/hooks.js +91 -0
  65. package/examples/http2.js +39 -0
  66. package/examples/https.js +38 -0
  67. package/examples/parser.js +53 -0
  68. package/examples/plugin.js +12 -0
  69. package/examples/route-prefix.js +38 -0
  70. package/examples/shared-schema.js +38 -0
  71. package/examples/simple-stream.js +20 -0
  72. package/examples/simple.js +32 -0
  73. package/examples/simple.mjs +27 -0
  74. package/examples/typescript-server.ts +79 -0
  75. package/examples/use-plugin.js +29 -0
  76. package/fastify.d.ts +253 -0
  77. package/fastify.js +985 -0
  78. package/integration/server.js +29 -0
  79. package/integration/test.sh +23 -0
  80. package/lib/config-validator.js +1266 -0
  81. package/lib/content-type-parser.js +413 -0
  82. package/lib/content-type.js +160 -0
  83. package/lib/context.js +98 -0
  84. package/lib/decorate.js +152 -0
  85. package/lib/error-handler.js +173 -0
  86. package/lib/error-serializer.js +134 -0
  87. package/lib/error-status.js +14 -0
  88. package/lib/errors.js +516 -0
  89. package/lib/four-oh-four.js +190 -0
  90. package/lib/handle-request.js +195 -0
  91. package/lib/head-route.js +45 -0
  92. package/lib/hooks.js +429 -0
  93. package/lib/initial-config-validation.js +37 -0
  94. package/lib/logger-factory.js +136 -0
  95. package/lib/logger-pino.js +68 -0
  96. package/lib/noop-set.js +10 -0
  97. package/lib/plugin-override.js +90 -0
  98. package/lib/plugin-utils.js +169 -0
  99. package/lib/promise.js +23 -0
  100. package/lib/reply.js +1030 -0
  101. package/lib/req-id-gen-factory.js +52 -0
  102. package/lib/request.js +391 -0
  103. package/lib/route.js +686 -0
  104. package/lib/schema-controller.js +164 -0
  105. package/lib/schemas.js +207 -0
  106. package/lib/server.js +441 -0
  107. package/lib/symbols.js +71 -0
  108. package/lib/validation.js +280 -0
  109. package/lib/warnings.js +57 -0
  110. package/lib/wrap-thenable.js +84 -0
  111. package/package.json +225 -0
  112. package/scripts/validate-ecosystem-links.js +179 -0
  113. package/test/404s.test.js +2035 -0
  114. package/test/500s.test.js +422 -0
  115. package/test/allow-unsafe-regex.test.js +92 -0
  116. package/test/als.test.js +65 -0
  117. package/test/async-await.test.js +705 -0
  118. package/test/async-dispose.test.js +20 -0
  119. package/test/async_hooks.test.js +52 -0
  120. package/test/body-limit.test.js +224 -0
  121. package/test/buffer.test.js +74 -0
  122. package/test/build/error-serializer.test.js +36 -0
  123. package/test/build/version.test.js +14 -0
  124. package/test/build-certificate.js +109 -0
  125. package/test/bundler/README.md +29 -0
  126. package/test/bundler/esbuild/bundler-test.js +32 -0
  127. package/test/bundler/esbuild/package.json +10 -0
  128. package/test/bundler/esbuild/src/fail-plugin-version.js +14 -0
  129. package/test/bundler/esbuild/src/index.js +9 -0
  130. package/test/bundler/webpack/bundler-test.js +32 -0
  131. package/test/bundler/webpack/package.json +11 -0
  132. package/test/bundler/webpack/src/fail-plugin-version.js +14 -0
  133. package/test/bundler/webpack/src/index.js +9 -0
  134. package/test/bundler/webpack/webpack.config.js +15 -0
  135. package/test/case-insensitive.test.js +102 -0
  136. package/test/chainable.test.js +40 -0
  137. package/test/child-logger-factory.test.js +128 -0
  138. package/test/client-timeout.test.js +38 -0
  139. package/test/close-pipelining.test.js +78 -0
  140. package/test/close.test.js +706 -0
  141. package/test/conditional-pino.test.js +47 -0
  142. package/test/connection-timeout.test.js +42 -0
  143. package/test/constrained-routes.test.js +1138 -0
  144. package/test/content-length.test.js +174 -0
  145. package/test/content-parser.test.js +739 -0
  146. package/test/content-type.test.js +181 -0
  147. package/test/context-config.test.js +164 -0
  148. package/test/custom-http-server.test.js +118 -0
  149. package/test/custom-parser-async.test.js +59 -0
  150. package/test/custom-parser.0.test.js +701 -0
  151. package/test/custom-parser.1.test.js +266 -0
  152. package/test/custom-parser.2.test.js +91 -0
  153. package/test/custom-parser.3.test.js +208 -0
  154. package/test/custom-parser.4.test.js +218 -0
  155. package/test/custom-parser.5.test.js +130 -0
  156. package/test/custom-querystring-parser.test.js +129 -0
  157. package/test/decorator.test.js +1330 -0
  158. package/test/delete.test.js +344 -0
  159. package/test/diagnostics-channel/404.test.js +49 -0
  160. package/test/diagnostics-channel/async-delay-request.test.js +65 -0
  161. package/test/diagnostics-channel/async-request.test.js +64 -0
  162. package/test/diagnostics-channel/error-before-handler.test.js +35 -0
  163. package/test/diagnostics-channel/error-request.test.js +53 -0
  164. package/test/diagnostics-channel/error-status.test.js +123 -0
  165. package/test/diagnostics-channel/init.test.js +50 -0
  166. package/test/diagnostics-channel/sync-delay-request.test.js +49 -0
  167. package/test/diagnostics-channel/sync-request-reply.test.js +51 -0
  168. package/test/diagnostics-channel/sync-request.test.js +54 -0
  169. package/test/encapsulated-child-logger-factory.test.js +69 -0
  170. package/test/encapsulated-error-handler.test.js +237 -0
  171. package/test/esm/errorCodes.test.mjs +10 -0
  172. package/test/esm/esm.test.mjs +13 -0
  173. package/test/esm/index.test.js +8 -0
  174. package/test/esm/named-exports.mjs +14 -0
  175. package/test/esm/other.mjs +8 -0
  176. package/test/esm/plugin.mjs +8 -0
  177. package/test/fastify-instance.test.js +300 -0
  178. package/test/find-route.test.js +152 -0
  179. package/test/fluent-schema.test.js +209 -0
  180. package/test/genReqId.test.js +426 -0
  181. package/test/handler-context.test.js +45 -0
  182. package/test/handler-timeout.test.js +367 -0
  183. package/test/has-route.test.js +88 -0
  184. package/test/header-overflow.test.js +55 -0
  185. package/test/helper.js +496 -0
  186. package/test/hooks-async.test.js +1099 -0
  187. package/test/hooks.on-listen.test.js +1162 -0
  188. package/test/hooks.on-ready.test.js +421 -0
  189. package/test/hooks.test.js +3578 -0
  190. package/test/http-methods/copy.test.js +35 -0
  191. package/test/http-methods/custom-http-methods.test.js +114 -0
  192. package/test/http-methods/get.test.js +412 -0
  193. package/test/http-methods/head.test.js +263 -0
  194. package/test/http-methods/lock.test.js +108 -0
  195. package/test/http-methods/mkcalendar.test.js +143 -0
  196. package/test/http-methods/mkcol.test.js +35 -0
  197. package/test/http-methods/move.test.js +42 -0
  198. package/test/http-methods/propfind.test.js +136 -0
  199. package/test/http-methods/proppatch.test.js +105 -0
  200. package/test/http-methods/report.test.js +142 -0
  201. package/test/http-methods/search.test.js +233 -0
  202. package/test/http-methods/trace.test.js +21 -0
  203. package/test/http-methods/unlock.test.js +38 -0
  204. package/test/http2/closing.test.js +270 -0
  205. package/test/http2/constraint.test.js +109 -0
  206. package/test/http2/head.test.js +34 -0
  207. package/test/http2/plain.test.js +68 -0
  208. package/test/http2/secure-with-fallback.test.js +113 -0
  209. package/test/http2/secure.test.js +67 -0
  210. package/test/http2/unknown-http-method.test.js +34 -0
  211. package/test/https/custom-https-server.test.js +58 -0
  212. package/test/https/https.test.js +136 -0
  213. package/test/imports.test.js +17 -0
  214. package/test/inject.test.js +502 -0
  215. package/test/input-validation.js +335 -0
  216. package/test/internals/all.test.js +38 -0
  217. package/test/internals/content-type-parser.test.js +111 -0
  218. package/test/internals/context.test.js +31 -0
  219. package/test/internals/decorator.test.js +156 -0
  220. package/test/internals/errors.test.js +982 -0
  221. package/test/internals/handle-request.test.js +270 -0
  222. package/test/internals/hook-runner.test.js +449 -0
  223. package/test/internals/hooks.test.js +96 -0
  224. package/test/internals/initial-config.test.js +383 -0
  225. package/test/internals/logger.test.js +163 -0
  226. package/test/internals/plugin.test.js +170 -0
  227. package/test/internals/promise.test.js +63 -0
  228. package/test/internals/reply-serialize.test.js +714 -0
  229. package/test/internals/reply.test.js +1920 -0
  230. package/test/internals/req-id-gen-factory.test.js +133 -0
  231. package/test/internals/request-validate.test.js +1402 -0
  232. package/test/internals/request.test.js +506 -0
  233. package/test/internals/schema-controller-perf.test.js +40 -0
  234. package/test/internals/server.test.js +91 -0
  235. package/test/internals/validation.test.js +352 -0
  236. package/test/issue-4959.test.js +118 -0
  237. package/test/keep-alive-timeout.test.js +42 -0
  238. package/test/listen.1.test.js +154 -0
  239. package/test/listen.2.test.js +113 -0
  240. package/test/listen.3.test.js +83 -0
  241. package/test/listen.4.test.js +168 -0
  242. package/test/listen.5.test.js +122 -0
  243. package/test/logger/instantiation.test.js +341 -0
  244. package/test/logger/logger-test-utils.js +47 -0
  245. package/test/logger/logging.test.js +460 -0
  246. package/test/logger/options.test.js +579 -0
  247. package/test/logger/request.test.js +292 -0
  248. package/test/logger/response.test.js +183 -0
  249. package/test/logger/tap-parallel-not-ok +0 -0
  250. package/test/max-requests-per-socket.test.js +113 -0
  251. package/test/middleware.test.js +37 -0
  252. package/test/noop-set.test.js +19 -0
  253. package/test/nullable-validation.test.js +187 -0
  254. package/test/options.error-handler.test.js +5 -0
  255. package/test/options.test.js +5 -0
  256. package/test/output-validation.test.js +140 -0
  257. package/test/patch.error-handler.test.js +5 -0
  258. package/test/patch.test.js +5 -0
  259. package/test/plugin.1.test.js +230 -0
  260. package/test/plugin.2.test.js +314 -0
  261. package/test/plugin.3.test.js +287 -0
  262. package/test/plugin.4.test.js +504 -0
  263. package/test/plugin.helper.js +8 -0
  264. package/test/plugin.name.display.js +10 -0
  265. package/test/post-empty-body.test.js +38 -0
  266. package/test/pretty-print.test.js +366 -0
  267. package/test/promises.test.js +125 -0
  268. package/test/proto-poisoning.test.js +145 -0
  269. package/test/put.error-handler.test.js +5 -0
  270. package/test/put.test.js +5 -0
  271. package/test/register.test.js +184 -0
  272. package/test/reply-code.test.js +148 -0
  273. package/test/reply-early-hints.test.js +100 -0
  274. package/test/reply-error.test.js +815 -0
  275. package/test/reply-trailers.test.js +445 -0
  276. package/test/reply-web-stream-locked.test.js +37 -0
  277. package/test/request-error.test.js +624 -0
  278. package/test/request-header-host.test.js +339 -0
  279. package/test/request-id.test.js +118 -0
  280. package/test/request-timeout.test.js +53 -0
  281. package/test/route-hooks.test.js +635 -0
  282. package/test/route-prefix.test.js +904 -0
  283. package/test/route-shorthand.test.js +48 -0
  284. package/test/route.1.test.js +259 -0
  285. package/test/route.2.test.js +100 -0
  286. package/test/route.3.test.js +213 -0
  287. package/test/route.4.test.js +127 -0
  288. package/test/route.5.test.js +211 -0
  289. package/test/route.6.test.js +306 -0
  290. package/test/route.7.test.js +406 -0
  291. package/test/route.8.test.js +225 -0
  292. package/test/router-options.test.js +1108 -0
  293. package/test/same-shape.test.js +124 -0
  294. package/test/schema-examples.test.js +661 -0
  295. package/test/schema-feature.test.js +2198 -0
  296. package/test/schema-serialization.test.js +1171 -0
  297. package/test/schema-special-usage.test.js +1348 -0
  298. package/test/schema-validation.test.js +1572 -0
  299. package/test/scripts/validate-ecosystem-links.test.js +339 -0
  300. package/test/serialize-response.test.js +186 -0
  301. package/test/server.test.js +347 -0
  302. package/test/set-error-handler.test.js +69 -0
  303. package/test/skip-reply-send.test.js +317 -0
  304. package/test/stream-serializers.test.js +40 -0
  305. package/test/stream.1.test.js +94 -0
  306. package/test/stream.2.test.js +129 -0
  307. package/test/stream.3.test.js +198 -0
  308. package/test/stream.4.test.js +176 -0
  309. package/test/stream.5.test.js +188 -0
  310. package/test/sync-routes.test.js +32 -0
  311. package/test/throw.test.js +359 -0
  312. package/test/toolkit.js +63 -0
  313. package/test/trust-proxy.test.js +162 -0
  314. package/test/type-provider.test.js +22 -0
  315. package/test/types/content-type-parser.test-d.ts +72 -0
  316. package/test/types/decorate-request-reply.test-d.ts +18 -0
  317. package/test/types/dummy-plugin.ts +9 -0
  318. package/test/types/errors.test-d.ts +90 -0
  319. package/test/types/fastify.test-d.ts +352 -0
  320. package/test/types/hooks.test-d.ts +550 -0
  321. package/test/types/import.ts +2 -0
  322. package/test/types/instance.test-d.ts +588 -0
  323. package/test/types/logger.test-d.ts +277 -0
  324. package/test/types/plugin.test-d.ts +97 -0
  325. package/test/types/register.test-d.ts +237 -0
  326. package/test/types/reply.test-d.ts +254 -0
  327. package/test/types/request.test-d.ts +188 -0
  328. package/test/types/route.test-d.ts +553 -0
  329. package/test/types/schema.test-d.ts +135 -0
  330. package/test/types/serverFactory.test-d.ts +37 -0
  331. package/test/types/type-provider.test-d.ts +1213 -0
  332. package/test/types/using.test-d.ts +17 -0
  333. package/test/upgrade.test.js +52 -0
  334. package/test/url-rewriting.test.js +122 -0
  335. package/test/use-semicolon-delimiter.test.js +168 -0
  336. package/test/validation-error-handling.test.js +900 -0
  337. package/test/versioned-routes.test.js +603 -0
  338. package/test/web-api.test.js +616 -0
  339. package/test/wrap-thenable.test.js +30 -0
  340. package/types/content-type-parser.d.ts +75 -0
  341. package/types/context.d.ts +22 -0
  342. package/types/errors.d.ts +92 -0
  343. package/types/hooks.d.ts +875 -0
  344. package/types/instance.d.ts +609 -0
  345. package/types/logger.d.ts +107 -0
  346. package/types/plugin.d.ts +44 -0
  347. package/types/register.d.ts +42 -0
  348. package/types/reply.d.ts +81 -0
  349. package/types/request.d.ts +95 -0
  350. package/types/route.d.ts +199 -0
  351. package/types/schema.d.ts +61 -0
  352. package/types/server-factory.d.ts +19 -0
  353. package/types/type-provider.d.ts +130 -0
  354. package/types/utils.d.ts +98 -0
@@ -0,0 +1,460 @@
1
+ 'use strict'
2
+
3
+ const stream = require('node:stream')
4
+
5
+ const t = require('node:test')
6
+ const split = require('split2')
7
+ const pino = require('pino')
8
+
9
+ const Fastify = require('../../fastify')
10
+ const helper = require('../helper')
11
+ const { once, on } = stream
12
+ const { request } = require('./logger-test-utils')
13
+ const { partialDeepStrictEqual } = require('../toolkit')
14
+
15
+ t.test('logging', { timeout: 60000 }, async (t) => {
16
+ let localhost
17
+ let localhostForURL
18
+
19
+ t.plan(14)
20
+
21
+ t.before(async function () {
22
+ [localhost, localhostForURL] = await helper.getLoopbackHost()
23
+ })
24
+
25
+ await t.test('The default 404 handler logs the incoming request', async (t) => {
26
+ const lines = ['incoming request', 'Route GET:/not-found not found', 'request completed']
27
+ t.plan(lines.length + 1)
28
+
29
+ const stream = split(JSON.parse)
30
+
31
+ const loggerInstance = pino({ level: 'trace' }, stream)
32
+
33
+ const fastify = Fastify({
34
+ loggerInstance
35
+ })
36
+ t.after(() => fastify.close())
37
+
38
+ await fastify.ready()
39
+
40
+ {
41
+ const response = await fastify.inject({ method: 'GET', url: '/not-found' })
42
+ t.assert.strictEqual(response.statusCode, 404)
43
+ }
44
+
45
+ for await (const [line] of on(stream, 'data')) {
46
+ t.assert.strictEqual(line.msg, lines.shift())
47
+ if (lines.length === 0) break
48
+ }
49
+ })
50
+
51
+ await t.test('should not rely on raw request to log errors', async (t) => {
52
+ const stream = split(JSON.parse)
53
+ const fastify = Fastify({
54
+ logger: {
55
+ stream,
56
+ level: 'info'
57
+ }
58
+ })
59
+ t.after(() => fastify.close())
60
+ fastify.get('/error', function (req, reply) {
61
+ t.assert.ok(req.log)
62
+ reply.status(415).send(new Error('something happened'))
63
+ })
64
+
65
+ await fastify.ready()
66
+ const server = await fastify.listen({ port: 0, host: localhost })
67
+ const lines = [
68
+ { msg: `Server listening at ${server}` },
69
+ { level: 30, msg: 'incoming request' },
70
+ { res: { statusCode: 415 }, msg: 'something happened' },
71
+ { res: { statusCode: 415 }, msg: 'request completed' }
72
+ ]
73
+ t.plan(lines.length + 1)
74
+
75
+ await request(`http://${localhostForURL}:` + fastify.server.address().port + '/error')
76
+
77
+ for await (const [line] of on(stream, 'data')) {
78
+ t.assert.ok(partialDeepStrictEqual(line, lines.shift()))
79
+ if (lines.length === 0) break
80
+ }
81
+ })
82
+
83
+ await t.test('should log the error if no error handler is defined', async (t) => {
84
+ const stream = split(JSON.parse)
85
+ const fastify = Fastify({
86
+ logger: {
87
+ stream,
88
+ level: 'info'
89
+ }
90
+ })
91
+ t.after(() => fastify.close())
92
+
93
+ fastify.get('/error', function (req, reply) {
94
+ t.assert.ok(req.log)
95
+ reply.send(new Error('a generic error'))
96
+ })
97
+
98
+ await fastify.ready()
99
+ const server = await fastify.listen({ port: 0, host: localhost })
100
+ const lines = [
101
+ { msg: `Server listening at ${server}` },
102
+ { msg: 'incoming request' },
103
+ { level: 50, msg: 'a generic error' },
104
+ { res: { statusCode: 500 }, msg: 'request completed' }
105
+ ]
106
+ t.plan(lines.length + 1)
107
+
108
+ await request(`http://${localhostForURL}:` + fastify.server.address().port + '/error')
109
+
110
+ for await (const [line] of on(stream, 'data')) {
111
+ t.assert.ok(partialDeepStrictEqual(line, lines.shift()))
112
+ if (lines.length === 0) break
113
+ }
114
+ })
115
+
116
+ await t.test('should log as info if error status code >= 400 and < 500 if no error handler is defined', async (t) => {
117
+ const stream = split(JSON.parse)
118
+ const fastify = Fastify({
119
+ logger: {
120
+ stream,
121
+ level: 'info'
122
+ }
123
+ })
124
+ t.after(() => fastify.close())
125
+
126
+ fastify.get('/400', function (req, reply) {
127
+ t.assert.ok(req.log)
128
+ reply.send(Object.assign(new Error('a 400 error'), { statusCode: 400 }))
129
+ })
130
+ fastify.get('/503', function (req, reply) {
131
+ t.assert.ok(req.log)
132
+ reply.send(Object.assign(new Error('a 503 error'), { statusCode: 503 }))
133
+ })
134
+
135
+ await fastify.ready()
136
+ const server = await fastify.listen({ port: 0, host: localhost })
137
+ const lines = [
138
+ { msg: `Server listening at ${server}` },
139
+ { msg: 'incoming request' },
140
+ { level: 30, msg: 'a 400 error' },
141
+ { res: { statusCode: 400 }, msg: 'request completed' }
142
+ ]
143
+ t.plan(lines.length + 1)
144
+
145
+ await request(`http://${localhostForURL}:` + fastify.server.address().port + '/400')
146
+
147
+ for await (const [line] of on(stream, 'data')) {
148
+ t.assert.ok(partialDeepStrictEqual(line, lines.shift()))
149
+ if (lines.length === 0) break
150
+ }
151
+ })
152
+
153
+ await t.test('should log as error if error status code >= 500 if no error handler is defined', async (t) => {
154
+ const stream = split(JSON.parse)
155
+ const fastify = Fastify({
156
+ logger: {
157
+ stream,
158
+ level: 'info'
159
+ }
160
+ })
161
+ t.after(() => fastify.close())
162
+ fastify.get('/503', function (req, reply) {
163
+ t.assert.ok(req.log)
164
+ reply.send(Object.assign(new Error('a 503 error'), { statusCode: 503 }))
165
+ })
166
+
167
+ await fastify.ready()
168
+ const server = await fastify.listen({ port: 0, host: localhost })
169
+ const lines = [
170
+ { msg: `Server listening at ${server}` },
171
+ { msg: 'incoming request' },
172
+ { level: 50, msg: 'a 503 error' },
173
+ { res: { statusCode: 503 }, msg: 'request completed' }
174
+ ]
175
+ t.plan(lines.length + 1)
176
+
177
+ await request(`http://${localhostForURL}:` + fastify.server.address().port + '/503')
178
+
179
+ for await (const [line] of on(stream, 'data')) {
180
+ t.assert.ok(partialDeepStrictEqual(line, lines.shift()))
181
+ if (lines.length === 0) break
182
+ }
183
+ })
184
+
185
+ await t.test('should not log the error if error handler is defined and it does not error', async (t) => {
186
+ const stream = split(JSON.parse)
187
+ const fastify = Fastify({
188
+ logger: {
189
+ stream,
190
+ level: 'info'
191
+ }
192
+ })
193
+ t.after(() => fastify.close())
194
+ fastify.get('/error', function (req, reply) {
195
+ t.assert.ok(req.log)
196
+ reply.send(new Error('something happened'))
197
+ })
198
+ fastify.setErrorHandler((err, req, reply) => {
199
+ t.assert.ok(err)
200
+ reply.send('something bad happened')
201
+ })
202
+
203
+ await fastify.ready()
204
+ const server = await fastify.listen({ port: 0, host: localhost })
205
+ const lines = [
206
+ { msg: `Server listening at ${server}` },
207
+ { level: 30, msg: 'incoming request' },
208
+ { res: { statusCode: 200 }, msg: 'request completed' }
209
+ ]
210
+ t.plan(lines.length + 2)
211
+
212
+ await request(`http://${localhostForURL}:` + fastify.server.address().port + '/error')
213
+
214
+ for await (const [line] of on(stream, 'data')) {
215
+ t.assert.ok(partialDeepStrictEqual(line, lines.shift()))
216
+ if (lines.length === 0) break
217
+ }
218
+ })
219
+
220
+ await t.test('reply.send logs an error if called twice in a row', async (t) => {
221
+ const lines = [
222
+ 'incoming request',
223
+ 'request completed',
224
+ 'Reply was already sent, did you forget to "return reply" in "/" (GET)?',
225
+ 'Reply was already sent, did you forget to "return reply" in "/" (GET)?'
226
+ ]
227
+ t.plan(lines.length + 1)
228
+
229
+ const stream = split(JSON.parse)
230
+ const loggerInstance = pino(stream)
231
+
232
+ const fastify = Fastify({
233
+ loggerInstance
234
+ })
235
+ t.after(() => fastify.close())
236
+
237
+ fastify.get('/', (req, reply) => {
238
+ reply.send({ hello: 'world' })
239
+ reply.send({ hello: 'world2' })
240
+ reply.send({ hello: 'world3' })
241
+ })
242
+
243
+ const response = await fastify.inject({ method: 'GET', url: '/' })
244
+ const body = await response.json()
245
+ t.assert.ok(partialDeepStrictEqual(body, { hello: 'world' }))
246
+
247
+ for await (const [line] of on(stream, 'data')) {
248
+ t.assert.strictEqual(line.msg, lines.shift())
249
+ if (lines.length === 0) break
250
+ }
251
+ })
252
+
253
+ await t.test('should not log incoming request and outgoing response when disabled', async (t) => {
254
+ t.plan(1)
255
+ const stream = split(JSON.parse)
256
+ const fastify = Fastify({ disableRequestLogging: true, logger: { level: 'info', stream } })
257
+ t.after(() => fastify.close())
258
+
259
+ fastify.get('/500', (req, reply) => {
260
+ reply.code(500).send(Error('500 error'))
261
+ })
262
+
263
+ await fastify.ready()
264
+
265
+ await fastify.inject({ method: 'GET', url: '/500' })
266
+
267
+ // no more readable data
268
+ t.assert.strictEqual(stream.readableLength, 0)
269
+ })
270
+
271
+ await t.test('should not log incoming request, outgoing response and route not found for 404 onBadUrl when disabled', async (t) => {
272
+ t.plan(1)
273
+ const stream = split(JSON.parse)
274
+ const fastify = Fastify({ disableRequestLogging: true, logger: { level: 'info', stream } })
275
+ t.after(() => fastify.close())
276
+
277
+ await fastify.ready()
278
+
279
+ await fastify.inject({ method: 'GET', url: '/%c0' })
280
+
281
+ // no more readable data
282
+ t.assert.strictEqual(stream.readableLength, 0)
283
+ })
284
+
285
+ await t.test('should log incoming request and outgoing response based on disableRequestLogging function', async (t) => {
286
+ const lines = [
287
+ 'incoming request',
288
+ 'request completed'
289
+ ]
290
+ t.plan(lines.length)
291
+
292
+ const stream = split(JSON.parse)
293
+ const loggerInstance = pino(stream)
294
+
295
+ const fastify = Fastify({
296
+ disableRequestLogging: (request) => {
297
+ return request.url !== '/not-logged'
298
+ },
299
+ loggerInstance
300
+ })
301
+ t.after(() => fastify.close())
302
+
303
+ fastify.get('/logged', (req, reply) => {
304
+ return reply.code(200).send({})
305
+ })
306
+
307
+ fastify.get('/not-logged', (req, reply) => {
308
+ return reply.code(200).send({})
309
+ })
310
+
311
+ await fastify.ready()
312
+
313
+ await fastify.inject({ method: 'GET', url: '/not-logged' })
314
+ await fastify.inject({ method: 'GET', url: '/logged' })
315
+
316
+ for await (const [line] of on(stream, 'data')) {
317
+ t.assert.strictEqual(line.msg, lines.shift())
318
+ if (lines.length === 0) break
319
+ }
320
+ })
321
+
322
+ await t.test('defaults to info level', async (t) => {
323
+ const lines = [
324
+ { req: { method: 'GET' }, msg: 'incoming request' },
325
+ { res: { statusCode: 200 }, msg: 'request completed' }
326
+ ]
327
+ t.plan(lines.length * 2 + 1)
328
+ const stream = split(JSON.parse)
329
+ const fastify = Fastify({
330
+ logger: {
331
+ stream
332
+ }
333
+ })
334
+ t.after(() => fastify.close())
335
+
336
+ fastify.get('/', function (req, reply) {
337
+ t.assert.ok(req.log)
338
+ reply.send({ hello: 'world' })
339
+ })
340
+
341
+ await fastify.ready()
342
+ await fastify.listen({ port: 0 })
343
+
344
+ await request(`http://${localhostForURL}:` + fastify.server.address().port)
345
+
346
+ let id
347
+ for await (const [line] of on(stream, 'data')) {
348
+ // we skip the non-request log
349
+ if (typeof line.reqId !== 'string') continue
350
+ if (id === undefined && line.reqId) id = line.reqId
351
+ if (id !== undefined && line.reqId) t.assert.strictEqual(line.reqId, id)
352
+ t.assert.ok(partialDeepStrictEqual(line, lines.shift()))
353
+ if (lines.length === 0) break
354
+ }
355
+ })
356
+
357
+ await t.test('test log stream', async (t) => {
358
+ const stream = split(JSON.parse)
359
+ const fastify = Fastify({
360
+ logger: {
361
+ stream,
362
+ level: 'info'
363
+ }
364
+ })
365
+ t.after(() => fastify.close())
366
+
367
+ fastify.get('/', function (req, reply) {
368
+ t.assert.ok(req.log)
369
+ reply.send({ hello: 'world' })
370
+ })
371
+
372
+ await fastify.ready()
373
+ const server = await fastify.listen({ port: 0, host: localhost })
374
+ const lines = [
375
+ { msg: `Server listening at ${server}` },
376
+ { req: { method: 'GET' }, msg: 'incoming request' },
377
+ { res: { statusCode: 200 }, msg: 'request completed' }
378
+ ]
379
+ t.plan(lines.length + 3)
380
+
381
+ await request(`http://${localhostForURL}:` + fastify.server.address().port)
382
+
383
+ let id
384
+ for await (const [line] of on(stream, 'data')) {
385
+ if (id === undefined && line.reqId) id = line.reqId
386
+ if (id !== undefined && line.reqId) t.assert.strictEqual(line.reqId, id)
387
+ t.assert.ok(partialDeepStrictEqual(line, lines.shift()))
388
+ if (lines.length === 0) break
389
+ }
390
+ })
391
+
392
+ await t.test('test error log stream', async (t) => {
393
+ const stream = split(JSON.parse)
394
+ const fastify = Fastify({
395
+ logger: {
396
+ stream,
397
+ level: 'info'
398
+ }
399
+ })
400
+ t.after(() => fastify.close())
401
+
402
+ fastify.get('/error', function (req, reply) {
403
+ t.assert.ok(req.log)
404
+ reply.send(new Error('kaboom'))
405
+ })
406
+
407
+ await fastify.ready()
408
+ const server = await fastify.listen({ port: 0, host: localhost })
409
+ const lines = [
410
+ { msg: `Server listening at ${server}` },
411
+ { req: { method: 'GET' }, msg: 'incoming request' },
412
+ { res: { statusCode: 500 }, msg: 'kaboom' },
413
+ { res: { statusCode: 500 }, msg: 'request completed' }
414
+ ]
415
+ t.plan(lines.length + 4)
416
+
417
+ await request(`http://${localhostForURL}:` + fastify.server.address().port + '/error')
418
+
419
+ let id
420
+ for await (const [line] of on(stream, 'data')) {
421
+ if (id === undefined && line.reqId) id = line.reqId
422
+ if (id !== undefined && line.reqId) t.assert.strictEqual(line.reqId, id)
423
+ t.assert.ok(partialDeepStrictEqual(line, lines.shift()))
424
+ if (lines.length === 0) break
425
+ }
426
+ })
427
+
428
+ await t.test('should not log the error if request logging is disabled', async (t) => {
429
+ t.plan(4)
430
+
431
+ const stream = split(JSON.parse)
432
+ const fastify = Fastify({
433
+ logger: {
434
+ stream,
435
+ level: 'info'
436
+ },
437
+ disableRequestLogging: true
438
+ })
439
+ t.after(() => fastify.close())
440
+
441
+ fastify.get('/error', function (req, reply) {
442
+ t.assert.ok(req.log)
443
+ reply.send(new Error('a generic error'))
444
+ })
445
+
446
+ await fastify.ready()
447
+ await fastify.listen({ port: 0, host: localhost })
448
+
449
+ await request(`http://${localhostForURL}:` + fastify.server.address().port + '/error')
450
+
451
+ {
452
+ const [line] = await once(stream, 'data')
453
+ t.assert.ok(typeof line.msg === 'string')
454
+ t.assert.ok(line.msg.startsWith('Server listening at'), 'message is set')
455
+ }
456
+
457
+ // no more readable data
458
+ t.assert.strictEqual(stream.readableLength, 0)
459
+ })
460
+ })