From 6ef4d58389e8db0978dabb241cf9eba288b2bc68 Mon Sep 17 00:00:00 2001 From: Jez Barnsley Date: Wed, 29 Apr 2026 15:04:33 +0100 Subject: [PATCH 1/2] createLogger() as a singleton --- src/index.ts | 4 +--- src/server/common/helpers/logging/logger.ts | 6 +++++- src/server/common/helpers/redis-client.js | 4 +--- src/server/messaging/formAdapterEventPublisher.ts | 3 +-- src/server/messaging/publish-base.js | 4 +--- src/server/plugins/nunjucks/context.js | 4 +--- src/server/routes/save-and-exit.js | 3 +-- src/server/services/outputService.test.js | 4 ++-- src/server/services/outputService.ts | 4 +--- 9 files changed, 14 insertions(+), 22 deletions(-) diff --git a/src/index.ts b/src/index.ts index b332b5ec5..c2084e183 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,11 +1,9 @@ import { getErrorMessage } from '@defra/forms-model' import { config } from '~/src/config/index.js' -import { createLogger } from '~/src/server/common/helpers/logging/logger.js' +import { logger } from '~/src/server/common/helpers/logging/logger.js' import { createServer } from '~/src/server/index.js' -const logger = createLogger() - process.on('unhandledRejection', (err) => { logger.info('Unhandled rejection') logger.error( diff --git a/src/server/common/helpers/logging/logger.ts b/src/server/common/helpers/logging/logger.ts index 14117d479..f7bc8fe68 100644 --- a/src/server/common/helpers/logging/logger.ts +++ b/src/server/common/helpers/logging/logger.ts @@ -2,6 +2,10 @@ import { pino } from 'pino' import { loggerOptions } from '~/src/server/common/helpers/logging/logger-options.js' -export function createLogger() { +function createPinoLogger() { return pino(loggerOptions) } + +// Singleton logger instance - pino adds 'exit' listeners to process, +// so we reuse a single instance to avoid MaxListenersExceededWarning +export const logger = createPinoLogger() diff --git a/src/server/common/helpers/redis-client.js b/src/server/common/helpers/redis-client.js index 497ea967d..df4ad93cc 100644 --- a/src/server/common/helpers/redis-client.js +++ b/src/server/common/helpers/redis-client.js @@ -2,7 +2,7 @@ import { getErrorMessage } from '@defra/forms-model' import { Cluster, Redis } from 'ioredis' import { config } from '~/src/config/index.js' -import { createLogger } from '~/src/server/common/helpers/logging/logger.js' +import { logger } from '~/src/server/common/helpers/logging/logger.js' /** * Setup Redis and provide a redis client @@ -11,8 +11,6 @@ import { createLogger } from '~/src/server/common/helpers/logging/logger.js' * Out in the wild - Elasticache / Redis Cluster with username and password */ export function buildRedisClient() { - const logger = createLogger() - const port = 6379 const db = 0 const redisConfig = config.get('redis') diff --git a/src/server/messaging/formAdapterEventPublisher.ts b/src/server/messaging/formAdapterEventPublisher.ts index 5c1c8aa57..499b940c8 100644 --- a/src/server/messaging/formAdapterEventPublisher.ts +++ b/src/server/messaging/formAdapterEventPublisher.ts @@ -3,10 +3,9 @@ import { formAdapterSubmissionMessagePayloadSchema } from '@defra/forms-engine-p import { type FormAdapterSubmissionMessagePayload } from '@defra/forms-engine-plugin/engine/types.js' import { config } from '~/src/config/index.js' -import { createLogger } from '~/src/server/common/helpers/logging/logger.js' +import { logger } from '~/src/server/common/helpers/logging/logger.js' import { getSNSClient } from '~/src/server/messaging/sns.js' -const logger = createLogger() const snsAdapterTopicArn = config.get('snsAdapterTopicArn') const snsFormTopicArnMapRaw = config.get('snsFormTopicArnMap') const snsFormTopicArnMap: Record = snsFormTopicArnMapRaw diff --git a/src/server/messaging/publish-base.js b/src/server/messaging/publish-base.js index 45145bda1..05fbd44cd 100644 --- a/src/server/messaging/publish-base.js +++ b/src/server/messaging/publish-base.js @@ -1,11 +1,9 @@ import { PublishCommand } from '@aws-sdk/client-sns' import { config } from '~/src/config/index.js' -import { createLogger } from '~/src/server/common/helpers/logging/logger.js' +import { logger } from '~/src/server/common/helpers/logging/logger.js' import { getSNSClient } from '~/src/server/messaging/sns.js' -const logger = createLogger() - const snsSaveTopicArn = config.get('snsSaveTopicArn') const client = getSNSClient() diff --git a/src/server/plugins/nunjucks/context.js b/src/server/plugins/nunjucks/context.js index f605a93bc..efef23880 100644 --- a/src/server/plugins/nunjucks/context.js +++ b/src/server/plugins/nunjucks/context.js @@ -8,9 +8,7 @@ import { StatusCodes } from 'http-status-codes' import pkg from '~/package.json' with { type: 'json' } import { parseCookieConsent } from '~/src/common/cookies.js' import { config } from '~/src/config/index.js' -import { createLogger } from '~/src/server/common/helpers/logging/logger.js' - -const logger = createLogger() +import { logger } from '~/src/server/common/helpers/logging/logger.js' /** @type {Record | undefined} */ let webpackManifest diff --git a/src/server/routes/save-and-exit.js b/src/server/routes/save-and-exit.js index f867d716b..6ac1ddbab 100644 --- a/src/server/routes/save-and-exit.js +++ b/src/server/routes/save-and-exit.js @@ -11,7 +11,7 @@ import * as Hoek from '@hapi/hoek' import { StatusCodes } from 'http-status-codes' import Joi from 'joi' -import { createLogger } from '~/src/server/common/helpers/logging/logger.js' +import { logger } from '~/src/server/common/helpers/logging/logger.js' import { createJoiError } from '~/src/server/helpers/error-helper.js' import { publishSaveAndExitEvent } from '~/src/server/messaging/publish.js' import { @@ -38,7 +38,6 @@ import { getSaveAndExitDetails, validateSaveAndExitCredentials } from '~/src/server/services/formsService.js' -const logger = createLogger() const maxInvalidPasswordAttempts = 5 diff --git a/src/server/services/outputService.test.js b/src/server/services/outputService.test.js index f6f609eec..3099a9db3 100644 --- a/src/server/services/outputService.test.js +++ b/src/server/services/outputService.test.js @@ -13,10 +13,10 @@ import { jest.mock('@defra/forms-engine-plugin/engine/helpers.js') jest.mock('@defra/forms-engine-plugin/engine/outputFormatters/index.js') jest.mock('~/src/server/common/helpers/logging/logger.ts', () => ({ - createLogger: jest.fn(() => ({ + logger: { info: jest.fn(), error: jest.fn() - })) + } })) jest.mock('~/src/server/messaging/formAdapterEventPublisher.ts') jest.mock('~/src/server/services/formsService.js') diff --git a/src/server/services/outputService.ts b/src/server/services/outputService.ts index ae411ab48..95984d717 100644 --- a/src/server/services/outputService.ts +++ b/src/server/services/outputService.ts @@ -17,12 +17,10 @@ import { type SubmitResponsePayload } from '@defra/forms-model' -import { createLogger } from '~/src/server/common/helpers/logging/logger.js' +import { logger } from '~/src/server/common/helpers/logging/logger.js' import { publishFormAdapterEvent } from '~/src/server/messaging/formAdapterEventPublisher.js' import { getFormMetadataById } from '~/src/server/services/formsService.js' -const logger = createLogger() - /** * Output service for handling form submission notifications */ From 5e7dc1c078cdd5ae33ea49ac6f281f1094c37ca8 Mon Sep 17 00:00:00 2001 From: Jez Barnsley Date: Wed, 29 Apr 2026 15:30:14 +0100 Subject: [PATCH 2/2] Engine bump --- package-lock.json | 78 ++++++++++++++++++++++++++++------------------- package.json | 2 +- 2 files changed, 47 insertions(+), 33 deletions(-) diff --git a/package-lock.json b/package-lock.json index 943a0af4d..e908d1afa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "license": "SEE LICENSE IN LICENSE", "dependencies": { "@aws-sdk/client-sns": "^3.997.0", - "@defra/forms-engine-plugin": "^4.9.1", + "@defra/forms-engine-plugin": "^4.10.0", "@defra/forms-model": "^3.0.647", "@defra/hapi-tracing": "^1.30.0", "@elastic/ecs-pino-format": "^1.5.0", @@ -747,13 +747,14 @@ } }, "node_modules/@aws-sdk/xml-builder": { - "version": "3.972.17", - "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.17.tgz", - "integrity": "sha512-Ra7hjqAZf1OXRRMueB13qex7mFJRDK/pgCvdSFemXBT8KCGnQDPoKzHY1SjN+TjJVmnpSF14W5tJ1vDamFu+Gg==", + "version": "3.972.21", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.21.tgz", + "integrity": "sha512-qxNiHUtlrsjTeSlrPWiFkWps7uD6YB4eKzg7eLAFH8jbiHTlt0ePNlo2Xu+WlftP38JIcMaIX4jTUjOlE2ySWw==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.14.0", - "fast-xml-parser": "5.5.8", + "@nodable/entities": "2.1.0", + "@smithy/types": "^4.14.1", + "fast-xml-parser": "5.7.2", "tslib": "^2.6.2" }, "engines": { @@ -4043,9 +4044,9 @@ } }, "node_modules/@defra/forms-engine-plugin": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@defra/forms-engine-plugin/-/forms-engine-plugin-4.9.1.tgz", - "integrity": "sha512-VC063tb40/PA8mapU2FxRVw8zYi65UG/aXbAQYsRKt1q1YDT+ZaNprZGszN1yibd0HT5ZUIrsnLszJNfZ8O/0A==", + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@defra/forms-engine-plugin/-/forms-engine-plugin-4.10.0.tgz", + "integrity": "sha512-qJoCbR3TZVaHwH5UfTbdZgGUrWRm9RcBz2pOWr1gPjy2Y8S293K09s+HllNDyMYYw280078SP7lgd2Z1FFIQtg==", "hasInstallScript": true, "license": "SEE LICENSE IN LICENSE", "dependencies": { @@ -4158,9 +4159,9 @@ } }, "node_modules/@defra/forms-model": { - "version": "3.0.649", - "resolved": "https://registry.npmjs.org/@defra/forms-model/-/forms-model-3.0.649.tgz", - "integrity": "sha512-zmJlDrPeBFjNaF26Zv1Gxm79J4Kgqu90mns3N59LCNQnEYFwb0zgmSEam9xbMltHDjbAtbdm5R7/RnxMbzPZMg==", + "version": "3.0.651", + "resolved": "https://registry.npmjs.org/@defra/forms-model/-/forms-model-3.0.651.tgz", + "integrity": "sha512-fEf7cldtT+4gthN1Na2l57S6H4oL9CdOfAus3W1Q8KXP9PbU5kuc8NY8jHCsa87PlwJL86q2v93FS14pFQZ2Bg==", "license": "OGL-UK-3.0", "dependencies": { "@joi/date": "^2.1.1", @@ -8163,6 +8164,18 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/@nodable/entities": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@nodable/entities/-/entities-2.1.0.tgz", + "integrity": "sha512-nyT7T3nbMyBI/lvr6L5TyWbFJAI9FTgVRakNoBqCD+PmID8DzFrrNdLLtHMwMszOtqZa8PAOV24ZqDnQrhQINA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/nodable" + } + ], + "license": "MIT" + }, "node_modules/@node-rs/jieba": { "version": "1.10.4", "resolved": "https://registry.npmjs.org/@node-rs/jieba/-/jieba-1.10.4.tgz", @@ -9465,9 +9478,9 @@ } }, "node_modules/@smithy/types": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.14.0.tgz", - "integrity": "sha512-OWgntFLW88kx2qvf/c/67Vno1yuXm/f9M7QFAtVkkO29IJXGBIg0ycEaBTH0kvCtwmvZxRujrgP5a86RvsXJAQ==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.14.1.tgz", + "integrity": "sha512-59b5HtSVrVR/eYNei3BUj3DCPKD/G7EtDDe7OEJE7i7FtQFugYo6MxbotS8mVJkLNVf8gYaAlEBwwtJ9HzhWSg==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -12915,9 +12928,9 @@ } }, "node_modules/basic-ftp": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.2.2.tgz", - "integrity": "sha512-1tDrzKsdCg70WGvbFss/ulVAxupNauGnOlgpyjKzeQxzyllBLS0CGLV7tjIXTK3ZQA9/FBEm9qyFFN1bciA6pw==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.3.1.tgz", + "integrity": "sha512-bopVNp6ugyA150DDuZfPFdt1KZ5a94ZDiwX4hMgZDzF+GttD80lEy8kj98kbyhLXnPvhtIo93mdnLIjpCAeeOw==", "license": "MIT", "engines": { "node": ">=10.0.0" @@ -18029,9 +18042,9 @@ "license": "BSD-3-Clause" }, "node_modules/fast-xml-builder": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.1.4.tgz", - "integrity": "sha512-f2jhpN4Eccy0/Uz9csxh3Nu6q4ErKxf0XIsasomfOihuSUa3/xw6w8dnOtCDgEItQFJG8KyXPzQXzcODDrrbOg==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.1.5.tgz", + "integrity": "sha512-4TJn/8FKLeslLAH3dnohXqE3QSoxkhvaMzepOIZytwJXZO69Bfz0HBdDHzOTOon6G59Zrk6VQ2bEiv1t61rfkA==", "funding": [ { "type": "github", @@ -18044,9 +18057,9 @@ } }, "node_modules/fast-xml-parser": { - "version": "5.5.11", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.5.11.tgz", - "integrity": "sha512-QL0eb0YbSTVWF6tTf1+LEMSgtCEjBYPpnAjoLC8SscESlAjXEIRJ7cHtLG0pLeDFaZLa4VKZLArtA/60ZS7vyA==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.7.2.tgz", + "integrity": "sha512-P7oW7tLbYnhOLQk/Gv7cZgzgMPP/XN03K02/Jy6Y/NHzyIAIpxuZIM/YqAkfiXFPxA2CTm7NtCijK9EDu09u2w==", "funding": [ { "type": "github", @@ -18055,8 +18068,9 @@ ], "license": "MIT", "dependencies": { - "fast-xml-builder": "^1.1.4", - "path-expression-matcher": "^1.4.0", + "@nodable/entities": "^2.1.0", + "fast-xml-builder": "^1.1.5", + "path-expression-matcher": "^1.5.0", "strnum": "^2.2.3" }, "bin": { @@ -22505,9 +22519,9 @@ } }, "node_modules/liquidjs": { - "version": "10.25.5", - "resolved": "https://registry.npmjs.org/liquidjs/-/liquidjs-10.25.5.tgz", - "integrity": "sha512-GKiKeZjJDdVoQAu+S9rzkYsYnYhcep5W3WwZXgb5f+yq484P/k9JqamBbGYu+LBEixcUAXZr2jogdAIjB3ki1w==", + "version": "10.25.7", + "resolved": "https://registry.npmjs.org/liquidjs/-/liquidjs-10.25.7.tgz", + "integrity": "sha512-rPCjJLiD4eDhQjvv964AeXFC+HbeYBbZrd7Z82Q6hqv1lX7G+5w4SJcKLn9CAAAwHI4aS3dTdo083UB79K3pDA==", "license": "MIT", "dependencies": { "commander": "^10.0.0" @@ -26905,9 +26919,9 @@ } }, "node_modules/postcss": { - "version": "8.5.6", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", - "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "version": "8.5.12", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.12.tgz", + "integrity": "sha512-W62t/Se6rA0Az3DfCL0AqJwXuKwBeYg6nOaIgzP+xZ7N5BFCI7DYi1qs6ygUYT6rvfi6t9k65UMLJC+PHZpDAA==", "funding": [ { "type": "opencollective", diff --git a/package.json b/package.json index 96a038222..eb81a3d5c 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "license": "SEE LICENSE IN LICENSE", "dependencies": { "@aws-sdk/client-sns": "^3.997.0", - "@defra/forms-engine-plugin": "^4.9.1", + "@defra/forms-engine-plugin": "^4.10.0", "@defra/forms-model": "^3.0.647", "@defra/hapi-tracing": "^1.30.0", "@elastic/ecs-pino-format": "^1.5.0",