From 4475612176fa1b9699bac57db24c2fff13fb7c27 Mon Sep 17 00:00:00 2001 From: David Wass Date: Mon, 17 Aug 2026 09:44:46 +0100 Subject: [PATCH 01/11] CCM-17942: Add Log Reference --- .../terraform/components/api/module_authorizer_lambda.tf | 1 + .../api/module_lambda_amendment_event_transformer.tf | 4 +++- .../terraform/components/api/module_lambda_get_letter.tf | 4 +++- .../components/api/module_lambda_get_letter_data.tf | 4 +++- .../terraform/components/api/module_lambda_get_letters.tf | 3 ++- .../terraform/components/api/module_lambda_get_mi.tf | 4 +++- .../terraform/components/api/module_lambda_get_status.tf | 4 +++- .../api/module_lambda_letter_updates_transformer.tf | 1 + .../components/api/module_lambda_mi_updates_transformer.tf | 1 + .../terraform/components/api/module_lambda_patch_letter.tf | 3 ++- .../terraform/components/api/module_lambda_post_letters.tf | 5 +++-- .../terraform/components/api/module_lambda_post_mi.tf | 4 +++- .../components/api/module_lambda_supplier_allocator.tf | 1 + .../api/module_lambda_supplier_config_ingress.tf | 4 +++- .../components/api/module_lambda_supplier_mock.tf | 1 + .../components/api/module_lambda_update_letter_queue.tf | 4 +++- .../components/api/module_lambda_upsert_letter.tf | 1 + internal/helpers/src/logger.ts | 7 ++++++- lambdas/api-handler/src/config/deps.ts | 5 ++++- lambdas/api-handler/src/config/env.ts | 1 + lambdas/authorizer/src/deps.ts | 5 ++++- lambdas/authorizer/src/env.ts | 1 + lambdas/letter-updates-transformer/src/deps.ts | 5 ++++- lambdas/letter-updates-transformer/src/env.ts | 1 + lambdas/mi-updates-transformer/src/deps.ts | 5 ++++- lambdas/mi-updates-transformer/src/env.ts | 1 + lambdas/supplier-allocator/src/config/deps.ts | 5 ++++- lambdas/supplier-allocator/src/config/env.ts | 1 + lambdas/supplier-config-ingress/src/config/deps.ts | 5 ++++- lambdas/supplier-config-ingress/src/config/env.ts | 1 + lambdas/supplier-mock/src/handler/deps.ts | 6 +++++- lambdas/supplier-mock/src/handler/env.ts | 1 + lambdas/update-letter-queue/src/deps.ts | 5 ++++- lambdas/update-letter-queue/src/env.ts | 1 + lambdas/upsert-letter/src/config/deps.ts | 5 ++++- lambdas/upsert-letter/src/config/env.ts | 1 + 36 files changed, 89 insertions(+), 22 deletions(-) diff --git a/infrastructure/terraform/components/api/module_authorizer_lambda.tf b/infrastructure/terraform/components/api/module_authorizer_lambda.tf index 9a9b98acc..6d7ce3de3 100644 --- a/infrastructure/terraform/components/api/module_authorizer_lambda.tf +++ b/infrastructure/terraform/components/api/module_authorizer_lambda.tf @@ -39,6 +39,7 @@ module "authorizer_lambda" { CLIENT_CERTIFICATE_EXPIRATION_ALERT_DAYS = 30, APIM_SUPPLIER_ID_HEADER = "NHSD-Supplier-ID", SUPPLIERS_TABLE_NAME = aws_dynamodb_table.suppliers.name + LOG_REFERENCE = "AUTHORIZER" } } diff --git a/infrastructure/terraform/components/api/module_lambda_amendment_event_transformer.tf b/infrastructure/terraform/components/api/module_lambda_amendment_event_transformer.tf index 3cd0d5de2..65509b440 100644 --- a/infrastructure/terraform/components/api/module_lambda_amendment_event_transformer.tf +++ b/infrastructure/terraform/components/api/module_lambda_amendment_event_transformer.tf @@ -34,7 +34,9 @@ module "amendment_event_transformer" { log_destination_arn = local.destination_arn log_subscription_role_arn = local.acct.log_subscription_role_arn - lambda_env_vars = merge(local.common_lambda_env_vars, {}) + lambda_env_vars = merge(local.common_lambda_env_vars, { + LOG_REFERENCE = "AMENDMENT_EVENT_TRANSFORMER" + }) } data "aws_iam_policy_document" "amendment_event_transformer" { diff --git a/infrastructure/terraform/components/api/module_lambda_get_letter.tf b/infrastructure/terraform/components/api/module_lambda_get_letter.tf index 845266816..2b267910c 100644 --- a/infrastructure/terraform/components/api/module_lambda_get_letter.tf +++ b/infrastructure/terraform/components/api/module_lambda_get_letter.tf @@ -34,7 +34,9 @@ module "get_letter" { log_destination_arn = local.destination_arn log_subscription_role_arn = local.acct.log_subscription_role_arn - lambda_env_vars = merge(local.common_lambda_env_vars, {}) + lambda_env_vars = merge(local.common_lambda_env_vars, { + LOG_REFERENCE = "GET_LETTER" + }) } data "aws_iam_policy_document" "get_letter_lambda" { diff --git a/infrastructure/terraform/components/api/module_lambda_get_letter_data.tf b/infrastructure/terraform/components/api/module_lambda_get_letter_data.tf index a60cfa4e7..9ffb37e99 100644 --- a/infrastructure/terraform/components/api/module_lambda_get_letter_data.tf +++ b/infrastructure/terraform/components/api/module_lambda_get_letter_data.tf @@ -34,7 +34,9 @@ module "get_letter_data" { log_destination_arn = local.destination_arn log_subscription_role_arn = local.acct.log_subscription_role_arn - lambda_env_vars = merge(local.common_lambda_env_vars, {}) + lambda_env_vars = merge(local.common_lambda_env_vars, { + LOG_REFERENCE = "GET_LETTER_DATA" + }) } data "aws_iam_policy_document" "get_letter_data_lambda" { diff --git a/infrastructure/terraform/components/api/module_lambda_get_letters.tf b/infrastructure/terraform/components/api/module_lambda_get_letters.tf index 8c8482136..52da89cd5 100644 --- a/infrastructure/terraform/components/api/module_lambda_get_letters.tf +++ b/infrastructure/terraform/components/api/module_lambda_get_letters.tf @@ -35,7 +35,8 @@ module "get_letters" { log_subscription_role_arn = local.acct.log_subscription_role_arn lambda_env_vars = merge(local.common_lambda_env_vars, { - MAX_LIMIT = var.max_get_limit + MAX_LIMIT = var.max_get_limit + LOG_REFERENCE = "GET_LETTERS" }) } diff --git a/infrastructure/terraform/components/api/module_lambda_get_mi.tf b/infrastructure/terraform/components/api/module_lambda_get_mi.tf index 456bd588f..d5071bb87 100644 --- a/infrastructure/terraform/components/api/module_lambda_get_mi.tf +++ b/infrastructure/terraform/components/api/module_lambda_get_mi.tf @@ -34,7 +34,9 @@ module "get_mi" { log_destination_arn = local.destination_arn log_subscription_role_arn = local.acct.log_subscription_role_arn - lambda_env_vars = merge(local.common_lambda_env_vars, {}) + lambda_env_vars = merge(local.common_lambda_env_vars, { + LOG_REFERENCE = "GET_MI" + }) } data "aws_iam_policy_document" "get_mi_lambda" { diff --git a/infrastructure/terraform/components/api/module_lambda_get_status.tf b/infrastructure/terraform/components/api/module_lambda_get_status.tf index 7582de805..6bba89514 100644 --- a/infrastructure/terraform/components/api/module_lambda_get_status.tf +++ b/infrastructure/terraform/components/api/module_lambda_get_status.tf @@ -34,7 +34,9 @@ module "get_status" { log_destination_arn = local.destination_arn log_subscription_role_arn = local.acct.log_subscription_role_arn - lambda_env_vars = merge(local.common_lambda_env_vars, {}) + lambda_env_vars = merge(local.common_lambda_env_vars, { + LOG_REFERENCE = "GET_STATUS" + }) } data "aws_iam_policy_document" "get_status_lambda" { diff --git a/infrastructure/terraform/components/api/module_lambda_letter_updates_transformer.tf b/infrastructure/terraform/components/api/module_lambda_letter_updates_transformer.tf index 20a117f50..cbe4f17db 100644 --- a/infrastructure/terraform/components/api/module_lambda_letter_updates_transformer.tf +++ b/infrastructure/terraform/components/api/module_lambda_letter_updates_transformer.tf @@ -37,6 +37,7 @@ module "letter_updates_transformer" { lambda_env_vars = merge(local.common_lambda_env_vars, { EVENTPUB_SNS_TOPIC_ARN = "${module.eventpub.sns_topic.arn}", EVENT_SOURCE = var.letter_event_source + LOG_REFERENCE = "LETTER_UPDATES_TRANSFORMER" }) } diff --git a/infrastructure/terraform/components/api/module_lambda_mi_updates_transformer.tf b/infrastructure/terraform/components/api/module_lambda_mi_updates_transformer.tf index 47bdbbfe0..dbd54e66b 100644 --- a/infrastructure/terraform/components/api/module_lambda_mi_updates_transformer.tf +++ b/infrastructure/terraform/components/api/module_lambda_mi_updates_transformer.tf @@ -37,6 +37,7 @@ module "mi_updates_transformer" { lambda_env_vars = merge(local.common_lambda_env_vars, { EVENTPUB_SNS_TOPIC_ARN = "${module.eventpub.sns_topic.arn}" + LOG_REFERENCE = "MI_UPDATES_TRANSFORMER" }) } diff --git a/infrastructure/terraform/components/api/module_lambda_patch_letter.tf b/infrastructure/terraform/components/api/module_lambda_patch_letter.tf index afc612c71..17dc47132 100644 --- a/infrastructure/terraform/components/api/module_lambda_patch_letter.tf +++ b/infrastructure/terraform/components/api/module_lambda_patch_letter.tf @@ -35,7 +35,8 @@ module "patch_letter" { log_subscription_role_arn = local.acct.log_subscription_role_arn lambda_env_vars = merge(local.common_lambda_env_vars, { - QUEUE_URL = module.amendments_queue.sqs_queue_url + QUEUE_URL = module.amendments_queue.sqs_queue_url + LOG_REFERENCE = "PATCH_LETTERS" }) } diff --git a/infrastructure/terraform/components/api/module_lambda_post_letters.tf b/infrastructure/terraform/components/api/module_lambda_post_letters.tf index 272f4c9e9..60d0ea2df 100644 --- a/infrastructure/terraform/components/api/module_lambda_post_letters.tf +++ b/infrastructure/terraform/components/api/module_lambda_post_letters.tf @@ -35,8 +35,9 @@ module "post_letters" { log_subscription_role_arn = local.acct.log_subscription_role_arn lambda_env_vars = merge(local.common_lambda_env_vars, { - QUEUE_URL = module.amendments_queue.sqs_queue_url, - MAX_LIMIT = var.max_get_limit + QUEUE_URL = module.amendments_queue.sqs_queue_url, + MAX_LIMIT = var.max_get_limit + LOG_REFERENCE = "POST_LETTERS" }) } diff --git a/infrastructure/terraform/components/api/module_lambda_post_mi.tf b/infrastructure/terraform/components/api/module_lambda_post_mi.tf index be7ae4eed..6f8a096ca 100644 --- a/infrastructure/terraform/components/api/module_lambda_post_mi.tf +++ b/infrastructure/terraform/components/api/module_lambda_post_mi.tf @@ -34,7 +34,9 @@ module "post_mi" { log_destination_arn = local.destination_arn log_subscription_role_arn = local.acct.log_subscription_role_arn - lambda_env_vars = merge(local.common_lambda_env_vars, {}) + lambda_env_vars = merge(local.common_lambda_env_vars, { + LOG_REFERENCE = "POST_MI" + }) } data "aws_iam_policy_document" "post_mi_lambda" { diff --git a/infrastructure/terraform/components/api/module_lambda_supplier_allocator.tf b/infrastructure/terraform/components/api/module_lambda_supplier_allocator.tf index 3ab822ecc..2fedc86b0 100644 --- a/infrastructure/terraform/components/api/module_lambda_supplier_allocator.tf +++ b/infrastructure/terraform/components/api/module_lambda_supplier_allocator.tf @@ -38,6 +38,7 @@ module "supplier_allocator" { UPSERT_LETTERS_QUEUE_URL = module.sqs_letter_updates.sqs_queue_url, SUPPLIER_ALLOCATOR_DLQ_URL = module.sqs_supplier_allocator.sqs_dlq_url, IDEMPOTENCY_TABLE_NAME = aws_dynamodb_table.idempotency.name + LOG_REFERENCE = "SUPPLIER_ALLOCATOR" }) } diff --git a/infrastructure/terraform/components/api/module_lambda_supplier_config_ingress.tf b/infrastructure/terraform/components/api/module_lambda_supplier_config_ingress.tf index 8073ffef6..d2033d63d 100644 --- a/infrastructure/terraform/components/api/module_lambda_supplier_config_ingress.tf +++ b/infrastructure/terraform/components/api/module_lambda_supplier_config_ingress.tf @@ -34,7 +34,9 @@ module "supplier_config_ingress" { log_destination_arn = local.destination_arn log_subscription_role_arn = local.acct.log_subscription_role_arn - lambda_env_vars = merge(local.common_lambda_env_vars, {}) + lambda_env_vars = merge(local.common_lambda_env_vars, { + LOG_REFERENCE = "SUPPLIER_CONFIG_INGRESS" + }) } data "aws_iam_policy_document" "supplier_config_ingress_lambda" { diff --git a/infrastructure/terraform/components/api/module_lambda_supplier_mock.tf b/infrastructure/terraform/components/api/module_lambda_supplier_mock.tf index eb17faf34..87f91106a 100644 --- a/infrastructure/terraform/components/api/module_lambda_supplier_mock.tf +++ b/infrastructure/terraform/components/api/module_lambda_supplier_mock.tf @@ -40,6 +40,7 @@ module "supplier_mock" { GET_LETTERS_FUNCTION_NAME = module.get_letters.function_name PATCH_LETTER_FUNCTION_NAME = module.patch_letter.function_name SUPPLIER_MOCK_CONFIG_PARAM_NAME = aws_ssm_parameter.supplier_mock_config[0].name + LOG_REFERENCE = "SUPPLIER_MOCK" }) } diff --git a/infrastructure/terraform/components/api/module_lambda_update_letter_queue.tf b/infrastructure/terraform/components/api/module_lambda_update_letter_queue.tf index 418bd216a..f28eb51a2 100644 --- a/infrastructure/terraform/components/api/module_lambda_update_letter_queue.tf +++ b/infrastructure/terraform/components/api/module_lambda_update_letter_queue.tf @@ -34,7 +34,9 @@ module "update_letter_queue" { log_destination_arn = local.destination_arn log_subscription_role_arn = local.acct.log_subscription_role_arn - lambda_env_vars = merge(local.common_lambda_env_vars, {}) + lambda_env_vars = merge(local.common_lambda_env_vars, { + LOG_REFERENCE = "UPDATE_LETTER" + }) } data "aws_iam_policy_document" "update_letter_queue_lambda" { diff --git a/infrastructure/terraform/components/api/module_lambda_upsert_letter.tf b/infrastructure/terraform/components/api/module_lambda_upsert_letter.tf index eeaed3420..2f560e3b0 100644 --- a/infrastructure/terraform/components/api/module_lambda_upsert_letter.tf +++ b/infrastructure/terraform/components/api/module_lambda_upsert_letter.tf @@ -36,6 +36,7 @@ module "upsert_letter" { lambda_env_vars = merge(local.common_lambda_env_vars, { IDEMPOTENCY_TABLE_NAME = aws_dynamodb_table.idempotency.name + LOG_REFERENCE = "UPSERT_LETTER" }) } diff --git a/internal/helpers/src/logger.ts b/internal/helpers/src/logger.ts index 051e793be..1a7144522 100644 --- a/internal/helpers/src/logger.ts +++ b/internal/helpers/src/logger.ts @@ -2,6 +2,7 @@ import pino, { Logger } from "pino"; export type LoggerOptions = { logLevel?: string; + logReference?: string; }; /** @@ -9,13 +10,17 @@ export type LoggerOptions = { * * @param options - Optional configuration for the logger * @param options.logLevel - The log level (defaults to "info") + * @param options.logReference - Optional reference value to include on every log record * @returns A configured pino Logger instance */ export function createLogger(options: LoggerOptions = {}): Logger { - const { logLevel = "info" } = options; + const { logLevel = "info", logReference } = options; return pino({ level: logLevel, + mixin: () => { + return logReference ? { log_reference: logReference } : {}; + }, formatters: { level: (label) => { return { level: label.toUpperCase() }; diff --git a/lambdas/api-handler/src/config/deps.ts b/lambdas/api-handler/src/config/deps.ts index 427d883fa..610a43cc1 100644 --- a/lambdas/api-handler/src/config/deps.ts +++ b/lambdas/api-handler/src/config/deps.ts @@ -73,7 +73,10 @@ function createMIRepository(log: Logger, environment: EnvVars): MIRepository { } export function createDependenciesContainer(): Deps { - const log = createLogger({ logLevel: envVars.PINO_LOG_LEVEL }); + const log = createLogger({ + logLevel: envVars.PINO_LOG_LEVEL, + logReference: envVars.LOG_REFERENCE, + }); return { s3Client: new S3Client(), diff --git a/lambdas/api-handler/src/config/env.ts b/lambdas/api-handler/src/config/env.ts index a7402eee6..da32e2fa8 100644 --- a/lambdas/api-handler/src/config/env.ts +++ b/lambdas/api-handler/src/config/env.ts @@ -16,6 +16,7 @@ const EnvVarsSchema = z.object({ PINO_LOG_LEVEL: z.coerce.string().optional(), EVENT_SOURCE: z.string(), SNS_TOPIC_ARN: z.string(), + LOG_REFERENCE: z.string().optional(), }); export type EnvVars = z.infer; diff --git a/lambdas/authorizer/src/deps.ts b/lambdas/authorizer/src/deps.ts index 6ea7ee079..445b4f2d0 100644 --- a/lambdas/authorizer/src/deps.ts +++ b/lambdas/authorizer/src/deps.ts @@ -29,7 +29,10 @@ function createSupplierRepository( } export function createDependenciesContainer(): Deps { - const log = createLogger({ logLevel: envVars.PINO_LOG_LEVEL }); + const log = createLogger({ + logLevel: envVars.PINO_LOG_LEVEL, + logReference: envVars.LOG_REFERENCE, + }); return { supplierRepo: createSupplierRepository( diff --git a/lambdas/authorizer/src/env.ts b/lambdas/authorizer/src/env.ts index a81f82f2d..2196d64bc 100644 --- a/lambdas/authorizer/src/env.ts +++ b/lambdas/authorizer/src/env.ts @@ -6,6 +6,7 @@ const EnvVarsSchema = z.object({ APIM_SUPPLIER_ID_HEADER: z.string(), CLIENT_CERTIFICATE_EXPIRATION_ALERT_DAYS: z.coerce.number().int(), PINO_LOG_LEVEL: z.coerce.string().optional(), + LOG_REFERENCE: z.string().optional(), }); export type EnvVars = z.infer; diff --git a/lambdas/letter-updates-transformer/src/deps.ts b/lambdas/letter-updates-transformer/src/deps.ts index 57831c7a3..79d0d8425 100644 --- a/lambdas/letter-updates-transformer/src/deps.ts +++ b/lambdas/letter-updates-transformer/src/deps.ts @@ -14,7 +14,10 @@ function createSNSClient(): SNSClient { } export function createDependenciesContainer(): Deps { - const log = createLogger({ logLevel: envVars.PINO_LOG_LEVEL }); + const log = createLogger({ + logLevel: envVars.PINO_LOG_LEVEL, + logReference: envVars.LOG_REFERENCE, + }); return { snsClient: createSNSClient(), diff --git a/lambdas/letter-updates-transformer/src/env.ts b/lambdas/letter-updates-transformer/src/env.ts index 33030ad8b..72a04f0ce 100644 --- a/lambdas/letter-updates-transformer/src/env.ts +++ b/lambdas/letter-updates-transformer/src/env.ts @@ -4,6 +4,7 @@ const EnvVarsSchema = z.object({ EVENTPUB_SNS_TOPIC_ARN: z.string(), EVENT_SOURCE: z.string(), PINO_LOG_LEVEL: z.coerce.string().optional(), + LOG_REFERENCE: z.string().optional(), }); export type EnvVars = z.infer; diff --git a/lambdas/mi-updates-transformer/src/deps.ts b/lambdas/mi-updates-transformer/src/deps.ts index 57831c7a3..79d0d8425 100644 --- a/lambdas/mi-updates-transformer/src/deps.ts +++ b/lambdas/mi-updates-transformer/src/deps.ts @@ -14,7 +14,10 @@ function createSNSClient(): SNSClient { } export function createDependenciesContainer(): Deps { - const log = createLogger({ logLevel: envVars.PINO_LOG_LEVEL }); + const log = createLogger({ + logLevel: envVars.PINO_LOG_LEVEL, + logReference: envVars.LOG_REFERENCE, + }); return { snsClient: createSNSClient(), diff --git a/lambdas/mi-updates-transformer/src/env.ts b/lambdas/mi-updates-transformer/src/env.ts index fb4fe6089..fb5d51ee8 100644 --- a/lambdas/mi-updates-transformer/src/env.ts +++ b/lambdas/mi-updates-transformer/src/env.ts @@ -3,6 +3,7 @@ import { z } from "zod"; const EnvVarsSchema = z.object({ EVENTPUB_SNS_TOPIC_ARN: z.string(), PINO_LOG_LEVEL: z.coerce.string().optional(), + LOG_REFERENCE: z.string().optional(), }); export type EnvVars = z.infer; diff --git a/lambdas/supplier-allocator/src/config/deps.ts b/lambdas/supplier-allocator/src/config/deps.ts index e937d64ef..75b31fa4e 100644 --- a/lambdas/supplier-allocator/src/config/deps.ts +++ b/lambdas/supplier-allocator/src/config/deps.ts @@ -47,7 +47,10 @@ function createIdempotencyLayer(): DynamoDBPersistenceLayer { } export function createDependenciesContainer(): Deps { - const log = createLogger({ logLevel: envVars.PINO_LOG_LEVEL }); + const log = createLogger({ + logLevel: envVars.PINO_LOG_LEVEL, + logReference: envVars.LOG_REFERENCE, + }); return { supplierConfigRepo: createSupplierConfigRepository(), diff --git a/lambdas/supplier-allocator/src/config/env.ts b/lambdas/supplier-allocator/src/config/env.ts index 7d99ef0df..53eb22834 100644 --- a/lambdas/supplier-allocator/src/config/env.ts +++ b/lambdas/supplier-allocator/src/config/env.ts @@ -5,6 +5,7 @@ const EnvVarsSchema = z.object({ SUPPLIER_QUOTAS_TABLE_NAME: z.string(), PINO_LOG_LEVEL: z.coerce.string().optional(), IDEMPOTENCY_TABLE_NAME: z.string(), + LOG_REFERENCE: z.string().optional(), }); export type EnvVars = z.infer; diff --git a/lambdas/supplier-config-ingress/src/config/deps.ts b/lambdas/supplier-config-ingress/src/config/deps.ts index ec8ab9c6e..fc5c1dc91 100644 --- a/lambdas/supplier-config-ingress/src/config/deps.ts +++ b/lambdas/supplier-config-ingress/src/config/deps.ts @@ -28,7 +28,10 @@ function createSupplierConfigRepository( } export function createDependenciesContainer(): Deps { - const log = createLogger({ logLevel: envVars.PINO_LOG_LEVEL }); + const log = createLogger({ + logLevel: envVars.PINO_LOG_LEVEL, + logReference: envVars.LOG_REFERENCE, + }); return { supplierConfigRepo: createSupplierConfigRepository(envVars), diff --git a/lambdas/supplier-config-ingress/src/config/env.ts b/lambdas/supplier-config-ingress/src/config/env.ts index e037d73d2..fd65f1d03 100644 --- a/lambdas/supplier-config-ingress/src/config/env.ts +++ b/lambdas/supplier-config-ingress/src/config/env.ts @@ -3,6 +3,7 @@ import { z } from "zod"; const EnvVarsSchema = z.object({ SUPPLIER_CONFIG_TABLE_NAME: z.string(), PINO_LOG_LEVEL: z.coerce.string().optional(), + LOG_REFERENCE: z.string().optional(), }); export type EnvVars = z.infer; diff --git a/lambdas/supplier-mock/src/handler/deps.ts b/lambdas/supplier-mock/src/handler/deps.ts index 98775641e..45103e0fa 100644 --- a/lambdas/supplier-mock/src/handler/deps.ts +++ b/lambdas/supplier-mock/src/handler/deps.ts @@ -16,7 +16,11 @@ export type Deps = { }; export function createDependenciesContainer(): Deps { - const log = createLogger({ logLevel: envVars.PINO_LOG_LEVEL }); + const log = createLogger({ + logLevel: envVars.PINO_LOG_LEVEL, + logReference: envVars.LOG_REFERENCE, + }); + const lambdaClient = new LambdaClient(); const parameterStoreConfig = getParameterStoreConfig(); diff --git a/lambdas/supplier-mock/src/handler/env.ts b/lambdas/supplier-mock/src/handler/env.ts index e076faac3..c957f0e22 100644 --- a/lambdas/supplier-mock/src/handler/env.ts +++ b/lambdas/supplier-mock/src/handler/env.ts @@ -7,6 +7,7 @@ const EnvVarsSchema = z.object({ GET_LETTERS_FUNCTION_NAME: z.string().optional(), PATCH_LETTER_FUNCTION_NAME: z.string().optional(), SUPPLIER_MOCK_CONFIG_PARAM_NAME: z.string().optional(), + LOG_REFERENCE: z.string().optional(), }); export type EnvVars = z.infer; diff --git a/lambdas/update-letter-queue/src/deps.ts b/lambdas/update-letter-queue/src/deps.ts index 9db089111..926e9f9bd 100644 --- a/lambdas/update-letter-queue/src/deps.ts +++ b/lambdas/update-letter-queue/src/deps.ts @@ -17,7 +17,10 @@ function createDynamoDBDocumentClient(): DynamoDBDocumentClient { } export function createDependenciesContainer(): Deps { - const log = createLogger({ logLevel: envVars.PINO_LOG_LEVEL }); + const log = createLogger({ + logLevel: envVars.PINO_LOG_LEVEL, + logReference: envVars.LOG_REFERENCE, + }); const ddbClient = createDynamoDBDocumentClient(); const letterQueueRepository = new LetterQueueRepository(ddbClient, log, { diff --git a/lambdas/update-letter-queue/src/env.ts b/lambdas/update-letter-queue/src/env.ts index d54353325..94836b5b7 100644 --- a/lambdas/update-letter-queue/src/env.ts +++ b/lambdas/update-letter-queue/src/env.ts @@ -4,6 +4,7 @@ const EnvVarsSchema = z.object({ LETTER_QUEUE_TABLE_NAME: z.string(), LETTER_QUEUE_TTL_HOURS: z.coerce.number().int(), PINO_LOG_LEVEL: z.coerce.string().optional(), + LOG_REFERENCE: z.string().optional(), }); export type EnvVars = z.infer; diff --git a/lambdas/upsert-letter/src/config/deps.ts b/lambdas/upsert-letter/src/config/deps.ts index 09292c6a3..4f2f1b871 100644 --- a/lambdas/upsert-letter/src/config/deps.ts +++ b/lambdas/upsert-letter/src/config/deps.ts @@ -34,7 +34,10 @@ function createIdempotencyLayer(): DynamoDBPersistenceLayer { } export function createDependenciesContainer(): Deps { - const log = createLogger({ logLevel: envVars.PINO_LOG_LEVEL }); + const log = createLogger({ + logLevel: envVars.PINO_LOG_LEVEL, + logReference: envVars.LOG_REFERENCE, + }); return { letterRepo: createLetterRepository(log), diff --git a/lambdas/upsert-letter/src/config/env.ts b/lambdas/upsert-letter/src/config/env.ts index 1f3a3bdf8..f93d61a79 100644 --- a/lambdas/upsert-letter/src/config/env.ts +++ b/lambdas/upsert-letter/src/config/env.ts @@ -5,6 +5,7 @@ const EnvVarsSchema = z.object({ LETTER_TTL_HOURS: z.coerce.number().int(), PINO_LOG_LEVEL: z.coerce.string().optional(), IDEMPOTENCY_TABLE_NAME: z.string(), + LOG_REFERENCE: z.string().optional(), }); export type EnvVars = z.infer; From 6b926df747713c568f6cea9914fa1c66238e033b Mon Sep 17 00:00:00 2001 From: David Wass Date: Wed, 16 Sep 2026 11:55:52 +0100 Subject: [PATCH 02/11] Add log reference code and description --- internal/helpers/src/logger.ts | 15 ++++++++++++++- .../src/config/log-references.ts | 12 ++++++++++++ .../src/handler/allocate-handler.ts | 7 +++++-- 3 files changed, 31 insertions(+), 3 deletions(-) create mode 100644 lambdas/supplier-allocator/src/config/log-references.ts diff --git a/internal/helpers/src/logger.ts b/internal/helpers/src/logger.ts index 1a7144522..d9ab4bf77 100644 --- a/internal/helpers/src/logger.ts +++ b/internal/helpers/src/logger.ts @@ -18,7 +18,20 @@ export function createLogger(options: LoggerOptions = {}): Logger { return pino({ level: logLevel, - mixin: () => { + redact: { + paths: ["logRef"], + remove: true, + }, + mixin: (context) => { + const messageLogReference = + "logRef" in context && typeof context.logRef === "string" + ? context.logRef + : undefined; + + if (logReference && messageLogReference) { + return { log_reference: `${logReference} - ${messageLogReference}` }; + } + return logReference ? { log_reference: logReference } : {}; }, formatters: { diff --git a/lambdas/supplier-allocator/src/config/log-references.ts b/lambdas/supplier-allocator/src/config/log-references.ts new file mode 100644 index 000000000..26089329e --- /dev/null +++ b/lambdas/supplier-allocator/src/config/log-references.ts @@ -0,0 +1,12 @@ +const LogRefs = { + EXTRACT_EVENT: { + code: "001", + description: "Extracted letter event", + }, + RESOLVED_SUPPLIER: { + code: "002", + description: "Resolved supplier details from config", + }, +}; + +export default LogRefs; diff --git a/lambdas/supplier-allocator/src/handler/allocate-handler.ts b/lambdas/supplier-allocator/src/handler/allocate-handler.ts index a4dd059f0..1cefc61ad 100644 --- a/lambdas/supplier-allocator/src/handler/allocate-handler.ts +++ b/lambdas/supplier-allocator/src/handler/allocate-handler.ts @@ -37,6 +37,7 @@ import { suppliersWithValidPack, } from "./allocation-config"; import { Deps } from "../config/deps"; +import LogRefs from "../config/log-references"; import { PreparedEventSchema, PreparedEvents, SupplierDetails } from "./types"; import SupplierConfigValidationError from "../errors/supplier-config-validation-error"; import RejectedError from "../errors/rejected-error"; @@ -297,7 +298,8 @@ async function processSupplierAllocation( deps, ); deps.logger.info({ - description: "Resolved supplier details from config", + logRef: LogRefs.RESOLVED_SUPPLIER.code, + description: LogRefs.RESOLVED_SUPPLIER.description, supplierDetails, }); const supplierSpec = supplierDetails?.allocationDetails?.supplierSpec; @@ -404,7 +406,8 @@ export default function createSupplierAllocatorHandler(deps: Deps): SQSHandler { const letterEvent: PreparedEvents = parseQueueMessage(sqsMessage); deps.logger.info({ - description: "Extracted letter event", + logRef: LogRefs.EXTRACT_EVENT.code, + description: LogRefs.EXTRACT_EVENT.description, messageId: record.messageId, domainId: letterEvent.data.domainId, letterVariantId: letterEvent.data.letterVariantId, From ecc442b72b62480331d532b297a517dba6d67b9b Mon Sep 17 00:00:00 2001 From: David Wass Date: Thu, 17 Sep 2026 08:51:13 +0100 Subject: [PATCH 03/11] supplier allocator --- .../api/module_lambda_supplier_allocator.tf | 2 +- .../src/config/log-references.ts | 103 +++++++++++++++++- .../__tests__/allocation-config.test.ts | 4 + .../src/handler/allocate-handler.ts | 23 +++- .../src/handler/allocation-config.ts | 14 ++- .../__tests__/supplier-config.test.ts | 4 + .../src/services/supplier-config.ts | 28 +++-- 7 files changed, 156 insertions(+), 22 deletions(-) diff --git a/infrastructure/terraform/components/api/module_lambda_supplier_allocator.tf b/infrastructure/terraform/components/api/module_lambda_supplier_allocator.tf index 2fedc86b0..37245b1ee 100644 --- a/infrastructure/terraform/components/api/module_lambda_supplier_allocator.tf +++ b/infrastructure/terraform/components/api/module_lambda_supplier_allocator.tf @@ -38,7 +38,7 @@ module "supplier_allocator" { UPSERT_LETTERS_QUEUE_URL = module.sqs_letter_updates.sqs_queue_url, SUPPLIER_ALLOCATOR_DLQ_URL = module.sqs_supplier_allocator.sqs_dlq_url, IDEMPOTENCY_TABLE_NAME = aws_dynamodb_table.idempotency.name - LOG_REFERENCE = "SUPPLIER_ALLOCATOR" + LOG_REFERENCE = "SUP_ALLOC" }) } diff --git a/lambdas/supplier-allocator/src/config/log-references.ts b/lambdas/supplier-allocator/src/config/log-references.ts index 26089329e..732964426 100644 --- a/lambdas/supplier-allocator/src/config/log-references.ts +++ b/lambdas/supplier-allocator/src/config/log-references.ts @@ -1,12 +1,111 @@ const LogRefs = { EXTRACT_EVENT: { - code: "001", + code: "001-EXTRACT", description: "Extracted letter event", }, + FILTERING_ALLOCATIONS: { + code: "002-FILTER", + description: "Filtering allocations for letter variant supplier", + }, + FILTERED_PACK_SPECIFICATION: { + code: "003-FILTER-PACK", + description: + "Pack specification filtered out based on pageCount constraints", + }, + SUPPLIER_CAPACITY_EXCEEDED: { + code: "004-CAPACITY", + description: "Supplier has exceeded daily capacity", + }, + CALCULATED_SUPPLIER_FACTORS: { + code: "005-FACTOR", + description: "Calculated supplier factors for allocation", + }, + FETCHED_SUPPLIER_DETAILS: { + code: "006-FETCH", + description: "Fetched supplier details for supplier allocations", + }, RESOLVED_SUPPLIER: { - code: "002", + code: "007-RESOLVED", description: "Resolved supplier details from config", }, + DATA_METRIC: { + code: "008-DATA-METRIC", + description: "Emitted supplier allocation data metric", + }, + UPDATED_ALLOCATIONS: { + code: "009-UPDATED", + description: "Updated allocations for volume group and supplier", + }, + SENDING_UPSERT_MESSAGE: { + code: "010-UPSERT", + description: "Sending message to upsert letter queue", + }, + ALLOCATION_METRIC: { + code: "011-METRIC", + description: "Emitted supplier allocation metric", + }, + SENDING_DLQ_MESSAGE: { + code: "012-DLQ", + description: "Sending record to supplier allocator DLQ", + }, + NO_ALLOCATIONS_FOR_VARIANT_SUPPLIER: { + code: "013-NO-ALLOCATIONS", + description: "No allocations found for specified letter variant supplier", + }, + ALLOCATIONS_DO_NOT_SUM_TO_100: { + code: "014-SUM-NOT-100", + description: "Supplier allocations do not sum to 100%", + }, + SUPPLIER_DETAILS_MISMATCH: { + code: "015-MISMATCH", + description: "Mismatch between supplier allocations and supplier details", + }, + LETTER_REQUEST_REJECTED: { + code: "016-REJECTED", + description: "Letter request rejected", + }, + ERROR_PROCESSING_ALLOCATION: { + code: "017-PROCESSING-ERROR", + description: "Error processing allocation of record", + }, + FAILED_TO_SEND_DLQ: { + code: "018-DLQ-FAILED", + description: "Failed to send record to supplier allocator DLQ", + }, + ZERO_SUPPLIER_ALLOCATION: { + code: "019-ZERO-ALLOCATION", + description: "Supplier allocation has zero percentage", + }, + INACTIVE_VOLUME_GROUP: { + code: "020-INACTIVE-VOLUME-GROUP", + description: "Volume group is not active based on status and dates", + }, + NO_SUPPLIER_ALLOCATIONS: { + code: "021-NO-ALLOCATIONS", + description: + "No supplier allocations found for variantsupplier id in volume group", + }, + NO_SUPPLIER_DETAILS: { + code: "022-NO-DETAILS", + description: "No supplier details found for supplier allocations", + }, + NO_ACTIVE_SUPPLIERS: { + code: "023-NO-ACTIVE-SUPPLIERS", + description: "No active suppliers found for supplier allocations", + }, + NO_PREFERRED_SUPPLIER_PACKS: { + code: "024-NO-PREFERRED-PACKS", + description: + "No preferred supplier packs found for pack specification ids and suppliers", + }, + INACTIVE_PACK_SPECIFICATION: { + code: "025-INACTIVE-PACK", + description: "Pack specification is not active based on status", + }, + NO_ELIGIBLE_PACK_SPECIFICATIONS: { + code: "026-NO-ELIGIBLE-PACKS", + description: "No eligible pack specifications found for letter", + }, }; export default LogRefs; diff --git a/lambdas/supplier-allocator/src/handler/__tests__/allocation-config.test.ts b/lambdas/supplier-allocator/src/handler/__tests__/allocation-config.test.ts index eca009e65..5db22dcd2 100644 --- a/lambdas/supplier-allocator/src/handler/__tests__/allocation-config.test.ts +++ b/lambdas/supplier-allocator/src/handler/__tests__/allocation-config.test.ts @@ -175,6 +175,7 @@ describe("eligibleSuppliers", () => { ); await eligibleSuppliers(mockVolumeGroup, mockDeps); expect(mockDeps.logger.warn).toHaveBeenCalledWith({ + logRef: "014-SUM-NOT-100", description: "Supplier allocations do not sum to 100%", volumeGroupId: "volume-group-1", allocationPercentageSum: 70, @@ -231,6 +232,7 @@ describe("eligibleSuppliers", () => { const letterVariantSupplierId = "supplier-1"; await eligibleSuppliers(mockVolumeGroup, mockDeps, letterVariantSupplierId); expect(mockDeps.logger.warn).toHaveBeenCalledWith({ + logRef: "013-NO-ALLOCATIONS", description: "No allocations found for specified letter variant supplier", volumeGroupId: mockVolumeGroup.id, letterVariantSupplierId, @@ -663,6 +665,7 @@ describe("filterSuppliersWithCapacity", () => { expect(result).toEqual([mockSuppliers[0], mockSuppliers[2]]); expect(mockDeps.logger.info).toHaveBeenCalledTimes(1); expect(mockDeps.logger.info).toHaveBeenCalledWith({ + logRef: "004-CAPACITY", description: "Supplier has exceeded daily capacity", supplierId: "supplier-2", allocated: 600, @@ -954,6 +957,7 @@ describe("selectSupplierByFactor", () => { ); expect(mockDeps.logger.error).toHaveBeenCalledWith({ + logRef: "019-ZERO-ALLOCATION", description: "Supplier allocation has zero percentage", supplierId: "supplier-2", allocationPercentage: 0, diff --git a/lambdas/supplier-allocator/src/handler/allocate-handler.ts b/lambdas/supplier-allocator/src/handler/allocate-handler.ts index 1cefc61ad..b64fe673a 100644 --- a/lambdas/supplier-allocator/src/handler/allocate-handler.ts +++ b/lambdas/supplier-allocator/src/handler/allocate-handler.ts @@ -146,6 +146,7 @@ async function getSupplierFromConfig( )); deps.logger.info({ + logRef: LogRefs.FETCHED_SUPPLIER_DETAILS.code, description: "Fetched supplier details for supplier allocations", domainId: letterEvent.data.domainId, variantId: letterEvent.data.letterVariantId, @@ -170,7 +171,8 @@ async function getSupplierFromConfig( } catch (error) { if (error instanceof RejectedError) { deps.logger.error({ - description: "Letter request rejected", + logRef: LogRefs.LETTER_REQUEST_REJECTED.code, + description: LogRefs.LETTER_REQUEST_REJECTED.description, err: error, variantId: letterEvent.data.letterVariantId, }); @@ -219,7 +221,10 @@ function emitMetrics( value: count, unit: Unit.Count, }; - deps.logger.info(buildEMFObject(namespace, dimensions, metric)); + deps.logger.info({ + ...buildEMFObject(namespace, dimensions, metric), + logRef: LogRefs.ALLOCATION_METRIC.code, + }); } } } @@ -244,7 +249,10 @@ function emitDataMetrics( value: 1, unit: Unit.Count, }; - deps.logger.info(buildEMFObject(namespace, dimensions, metric)); + deps.logger.info({ + ...buildEMFObject(namespace, dimensions, metric), + logRef: LogRefs.DATA_METRIC.code, + }); } function incrementAllocation( @@ -259,6 +267,7 @@ function incrementAllocation( (groupAllocations[supplierId] ?? 0) + allocation; volumeGroupAllocations.set(volumeGroupId, groupAllocations); deps.logger.info({ + logRef: LogRefs.UPDATED_ALLOCATIONS.code, description: "Updated allocations for volume group and supplier", volumeGroupId, groupAllocations, @@ -330,6 +339,7 @@ async function processSupplierAllocation( }; deps.logger.info({ + logRef: LogRefs.SENDING_UPSERT_MESSAGE.code, description: "Sending message to upsert letter queue", msg: queueMessage, url: queueUrl, @@ -354,6 +364,7 @@ async function placeOnDeadLetterQueue(record: SQSRecord, deps: Deps) { } deps.logger.info({ + logRef: LogRefs.SENDING_DLQ_MESSAGE.code, description: "Sending record to supplier allocator DLQ", messageId: record.messageId, deadLetterQueueUrl, @@ -421,7 +432,8 @@ export default function createSupplierAllocatorHandler(deps: Deps): SQSHandler { ({ priority, supplier } = supplierAllocationResult); } catch (error) { deps.logger.error({ - description: "Error processing allocation of record", + logRef: LogRefs.ERROR_PROCESSING_ALLOCATION.code, + description: LogRefs.ERROR_PROCESSING_ALLOCATION.description, err: error, messageId: record.messageId, message: record.body, @@ -435,7 +447,8 @@ export default function createSupplierAllocatorHandler(deps: Deps): SQSHandler { await placeOnDeadLetterQueue(record, deps); } catch (dlqError) { deps.logger.error({ - description: "Failed to send record to supplier allocator DLQ", + logRef: LogRefs.FAILED_TO_SEND_DLQ.code, + description: LogRefs.FAILED_TO_SEND_DLQ.description, err: dlqError, messageId: record.messageId, message: record.body, diff --git a/lambdas/supplier-allocator/src/handler/allocation-config.ts b/lambdas/supplier-allocator/src/handler/allocation-config.ts index 6a31b54dd..cd3024042 100644 --- a/lambdas/supplier-allocator/src/handler/allocation-config.ts +++ b/lambdas/supplier-allocator/src/handler/allocation-config.ts @@ -18,6 +18,7 @@ import { import { calculateSupplierAllocatedFactor } from "../services/supplier-quotas"; import { Deps } from "../config/deps"; +import LogRefs from "../config/log-references"; import { PreparedEvents } from "./types"; import SupplierConfigValidationError from "../errors/supplier-config-validation-error"; @@ -35,6 +36,7 @@ export async function eligibleSuppliers( ); if (letterVariantSupplierId) { deps.logger.info({ + logRef: LogRefs.FILTERING_ALLOCATIONS.code, description: "Filtering allocations for letter variant supplier", volumeGroupId: volumeGroup.id, letterVariantSupplierId, @@ -44,8 +46,8 @@ export async function eligibleSuppliers( ); if (!filteredAllocations) { deps.logger.warn({ - description: - "No allocations found for specified letter variant supplier", + logRef: LogRefs.NO_ALLOCATIONS_FOR_VARIANT_SUPPLIER.code, + description: LogRefs.NO_ALLOCATIONS_FOR_VARIANT_SUPPLIER.description, volumeGroupId: volumeGroup.id, letterVariantSupplierId, }); @@ -65,7 +67,8 @@ export async function eligibleSuppliers( ); if (allocationPercentageSum !== 100) { deps.logger.warn({ - description: "Supplier allocations do not sum to 100%", + logRef: LogRefs.ALLOCATIONS_DO_NOT_SUM_TO_100.code, + description: LogRefs.ALLOCATIONS_DO_NOT_SUM_TO_100.description, volumeGroupId: volumeGroup.id, allocationPercentageSum, }); @@ -133,6 +136,7 @@ export async function filterSuppliersWithCapacity( const hasCapacity = allocated < supplier.dailyCapacity; if (!hasCapacity) { deps.logger.info({ + logRef: LogRefs.SUPPLIER_CAPACITY_EXCEEDED.code, description: "Supplier has exceeded daily capacity", supplierId: supplier.id, allocated, @@ -155,7 +159,8 @@ export async function selectSupplierByFactor( const supplierAllocationsForPack = supplierAllocations.filter((alloc) => { if (alloc.allocationPercentage === 0) { deps.logger.error({ - description: "Supplier allocation has zero percentage", + logRef: LogRefs.ZERO_SUPPLIER_ALLOCATION.code, + description: LogRefs.ZERO_SUPPLIER_ALLOCATION.description, supplierId: alloc.supplier, allocationPercentage: alloc.allocationPercentage, }); @@ -178,6 +183,7 @@ export async function selectSupplierByFactor( } deps.logger.info({ + logRef: LogRefs.CALCULATED_SUPPLIER_FACTORS.code, description: "Calculated supplier factors for allocation", domainId, supplierFactors, diff --git a/lambdas/supplier-allocator/src/services/__tests__/supplier-config.test.ts b/lambdas/supplier-allocator/src/services/__tests__/supplier-config.test.ts index 1bd5fb9c3..b2b89110d 100644 --- a/lambdas/supplier-allocator/src/services/__tests__/supplier-config.test.ts +++ b/lambdas/supplier-allocator/src/services/__tests__/supplier-config.test.ts @@ -283,6 +283,7 @@ describe("supplier-config service", () => { await getSupplierDetails(supplierIds, deps); expect(deps.logger.warn).toHaveBeenCalledWith({ + logRef: "015-MISMATCH", description: "Mismatch between supplier allocations and supplier details", allocationsCount: 3, @@ -534,6 +535,7 @@ describe("supplier-config service", () => { "No eligible pack specifications found for letter variant id undefined and pack specification ids spec1", ); expect(deps.logger.info).toHaveBeenCalledWith({ + logRef: "003-FILTER-PACK", description: "Pack specification filtered out based on pageCount constraints", packSpecId: "spec1", @@ -605,6 +607,7 @@ describe("supplier-config service", () => { ); expect(deps.logger.info).toHaveBeenCalledWith({ + logRef: "003-FILTER-PACK", description: "Pack specification filtered out based on pageCount constraints", packSpecId: "spec1", @@ -646,6 +649,7 @@ describe("supplier-config service", () => { ); expect(deps.logger.info).toHaveBeenCalledWith({ + logRef: "003-FILTER-PACK", description: "Pack specification filtered out based on pageCount constraints", packSpecId: "spec1", diff --git a/lambdas/supplier-allocator/src/services/supplier-config.ts b/lambdas/supplier-allocator/src/services/supplier-config.ts index dcb71abc1..c42b54b54 100644 --- a/lambdas/supplier-allocator/src/services/supplier-config.ts +++ b/lambdas/supplier-allocator/src/services/supplier-config.ts @@ -8,6 +8,7 @@ import { } from "@nhsdigital/nhs-notify-event-schemas-supplier-config"; import { Deps } from "../config/deps"; +import LogRefs from "../config/log-references"; import { PreparedEvents } from "../handler/types"; import SupplierConfigValidationError from "../errors/supplier-config-validation-error"; import RejectedError from "../errors/rejected-error"; @@ -42,7 +43,8 @@ export async function getVolumeGroupDetails( } deps.logger.error({ - description: "Volume group is not active based on status and dates", + logRef: LogRefs.INACTIVE_VOLUME_GROUP.code, + description: LogRefs.INACTIVE_VOLUME_GROUP.description, groupId, status: groupDetails.status, startDate: groupDetails.startDate, @@ -67,8 +69,8 @@ export async function getSupplierAllocationsForVolumeGroup( ); if (filteredAllocations.length === 0) { deps.logger.error({ - description: - "No supplier allocations found for variantsupplier id in volume group", + logRef: LogRefs.NO_SUPPLIER_ALLOCATIONS.code, + description: LogRefs.NO_SUPPLIER_ALLOCATIONS.description, groupId, supplierId, }); @@ -91,7 +93,8 @@ export async function getSupplierDetails( if (Object.keys(supplierDetails).length === 0) { deps.logger.error({ - description: "No supplier details found for supplier allocations", + logRef: LogRefs.NO_SUPPLIER_DETAILS.code, + description: LogRefs.NO_SUPPLIER_DETAILS.description, supplierIds, }); throw new SupplierConfigValidationError( @@ -105,7 +108,8 @@ export async function getSupplierDetails( (id) => !foundSupplierIds.has(id), ); deps.logger.warn({ - description: "Mismatch between supplier allocations and supplier details", + logRef: LogRefs.SUPPLIER_DETAILS_MISMATCH.code, + description: LogRefs.SUPPLIER_DETAILS_MISMATCH.description, allocationsCount: supplierIds.length, detailsCount: supplierDetails.length, missingSuppliers: missingSupplierIds, @@ -114,7 +118,8 @@ export async function getSupplierDetails( const activeSuppliers = supplierDetails.filter((s) => s.status === "PROD"); if (activeSuppliers.length === 0) { deps.logger.error({ - description: "No active suppliers found for supplier allocations", + logRef: LogRefs.NO_ACTIVE_SUPPLIERS.code, + description: LogRefs.NO_ACTIVE_SUPPLIERS.description, supplierIds, }); throw new SupplierConfigValidationError( @@ -144,8 +149,8 @@ export async function getPreferredSupplierPacks( } } deps.logger.error({ - description: - "No preferred supplier packs found for pack specification ids and suppliers", + logRef: LogRefs.NO_PREFERRED_SUPPLIER_PACKS.code, + description: LogRefs.NO_PREFERRED_SUPPLIER_PACKS.description, packSpecificationIds, supplierIds: suppliers.map((s) => s.id), }); @@ -162,7 +167,8 @@ export async function getPackSpecification( await deps.supplierConfigRepo.getPackSpecification(packSpecId); if (packSpec.status !== "PROD") { deps.logger.error({ - description: "Pack specification is not active based on status", + logRef: LogRefs.INACTIVE_PACK_SPECIFICATION.code, + description: LogRefs.INACTIVE_PACK_SPECIFICATION.description, packSpecId, status: packSpec.status, }); @@ -306,6 +312,7 @@ export async function filterPacksForLetter( if (violatedConstraints.length > 0) { deps.logger.info({ + logRef: LogRefs.FILTERED_PACK_SPECIFICATION.code, description: `Pack specification filtered out based on pageCount constraints`, domainId: letterEvent.data.domainId, packSpecId, @@ -324,7 +331,8 @@ export async function filterPacksForLetter( if (filteredPackIds.length === 0) { deps.logger.error({ - description: "No eligible pack specifications found for letter", + logRef: LogRefs.NO_ELIGIBLE_PACK_SPECIFICATIONS.code, + description: LogRefs.NO_ELIGIBLE_PACK_SPECIFICATIONS.description, letterVariantId: letterEvent.data.letterVariantId, packSpecificationIds, }); From 0778e53bd57287ec99ad454845081d7aeeca3afd Mon Sep 17 00:00:00 2001 From: David Wass Date: Thu, 17 Sep 2026 13:16:58 +0100 Subject: [PATCH 04/11] api-handlers --- ...dule_lambda_amendment_event_transformer.tf | 2 +- .../api/module_lambda_get_letter.tf | 2 +- .../api/module_lambda_get_letter_data.tf | 2 +- .../api/module_lambda_get_letters.tf | 2 +- .../api/module_lambda_get_status.tf | 2 +- .../api/module_lambda_patch_letter.tf | 2 +- .../api/module_lambda_post_letters.tf | 2 +- .../components/api/module_lambda_post_mi.tf | 2 +- .../api-handler/src/config/log-references.ts | 96 +++++++++++++++++++ .../amendment-event-transformer.test.ts | 1 + .../handlers/amendment-event-transformer.ts | 11 ++- .../src/handlers/get-letter-data.ts | 4 +- .../api-handler/src/handlers/get-letter.ts | 4 +- .../api-handler/src/handlers/get-letters.ts | 13 ++- lambdas/api-handler/src/handlers/get-mi.ts | 6 +- .../api-handler/src/handlers/get-status.ts | 7 +- .../api-handler/src/handlers/patch-letter.ts | 4 +- .../api-handler/src/handlers/post-letters.ts | 8 +- lambdas/api-handler/src/handlers/post-mi.ts | 10 +- .../api-handler/src/mappers/error-mapper.ts | 21 +++- .../__tests__/letter-operations.test.ts | 1 + .../src/services/letter-operations.ts | 11 ++- 22 files changed, 176 insertions(+), 37 deletions(-) create mode 100644 lambdas/api-handler/src/config/log-references.ts diff --git a/infrastructure/terraform/components/api/module_lambda_amendment_event_transformer.tf b/infrastructure/terraform/components/api/module_lambda_amendment_event_transformer.tf index 65509b440..30703e4e2 100644 --- a/infrastructure/terraform/components/api/module_lambda_amendment_event_transformer.tf +++ b/infrastructure/terraform/components/api/module_lambda_amendment_event_transformer.tf @@ -35,7 +35,7 @@ module "amendment_event_transformer" { log_subscription_role_arn = local.acct.log_subscription_role_arn lambda_env_vars = merge(local.common_lambda_env_vars, { - LOG_REFERENCE = "AMENDMENT_EVENT_TRANSFORMER" + LOG_REFERENCE = "AMD_EVT_TRM" }) } diff --git a/infrastructure/terraform/components/api/module_lambda_get_letter.tf b/infrastructure/terraform/components/api/module_lambda_get_letter.tf index 2b267910c..3dd33db0b 100644 --- a/infrastructure/terraform/components/api/module_lambda_get_letter.tf +++ b/infrastructure/terraform/components/api/module_lambda_get_letter.tf @@ -35,7 +35,7 @@ module "get_letter" { log_subscription_role_arn = local.acct.log_subscription_role_arn lambda_env_vars = merge(local.common_lambda_env_vars, { - LOG_REFERENCE = "GET_LETTER" + LOG_REFERENCE = "GET_LTR" }) } diff --git a/infrastructure/terraform/components/api/module_lambda_get_letter_data.tf b/infrastructure/terraform/components/api/module_lambda_get_letter_data.tf index 9ffb37e99..0461457a5 100644 --- a/infrastructure/terraform/components/api/module_lambda_get_letter_data.tf +++ b/infrastructure/terraform/components/api/module_lambda_get_letter_data.tf @@ -35,7 +35,7 @@ module "get_letter_data" { log_subscription_role_arn = local.acct.log_subscription_role_arn lambda_env_vars = merge(local.common_lambda_env_vars, { - LOG_REFERENCE = "GET_LETTER_DATA" + LOG_REFERENCE = "GET_LTR_DATA" }) } diff --git a/infrastructure/terraform/components/api/module_lambda_get_letters.tf b/infrastructure/terraform/components/api/module_lambda_get_letters.tf index 52da89cd5..9587c17be 100644 --- a/infrastructure/terraform/components/api/module_lambda_get_letters.tf +++ b/infrastructure/terraform/components/api/module_lambda_get_letters.tf @@ -36,7 +36,7 @@ module "get_letters" { lambda_env_vars = merge(local.common_lambda_env_vars, { MAX_LIMIT = var.max_get_limit - LOG_REFERENCE = "GET_LETTERS" + LOG_REFERENCE = "GET_LTRS" }) } diff --git a/infrastructure/terraform/components/api/module_lambda_get_status.tf b/infrastructure/terraform/components/api/module_lambda_get_status.tf index 6bba89514..a8420b3e2 100644 --- a/infrastructure/terraform/components/api/module_lambda_get_status.tf +++ b/infrastructure/terraform/components/api/module_lambda_get_status.tf @@ -35,7 +35,7 @@ module "get_status" { log_subscription_role_arn = local.acct.log_subscription_role_arn lambda_env_vars = merge(local.common_lambda_env_vars, { - LOG_REFERENCE = "GET_STATUS" + LOG_REFERENCE = "GET_STS" }) } diff --git a/infrastructure/terraform/components/api/module_lambda_patch_letter.tf b/infrastructure/terraform/components/api/module_lambda_patch_letter.tf index 17dc47132..d888fe7ce 100644 --- a/infrastructure/terraform/components/api/module_lambda_patch_letter.tf +++ b/infrastructure/terraform/components/api/module_lambda_patch_letter.tf @@ -36,7 +36,7 @@ module "patch_letter" { lambda_env_vars = merge(local.common_lambda_env_vars, { QUEUE_URL = module.amendments_queue.sqs_queue_url - LOG_REFERENCE = "PATCH_LETTERS" + LOG_REFERENCE = "PTCH_LTRS" }) } diff --git a/infrastructure/terraform/components/api/module_lambda_post_letters.tf b/infrastructure/terraform/components/api/module_lambda_post_letters.tf index 60d0ea2df..8dacd1e9a 100644 --- a/infrastructure/terraform/components/api/module_lambda_post_letters.tf +++ b/infrastructure/terraform/components/api/module_lambda_post_letters.tf @@ -37,7 +37,7 @@ module "post_letters" { lambda_env_vars = merge(local.common_lambda_env_vars, { QUEUE_URL = module.amendments_queue.sqs_queue_url, MAX_LIMIT = var.max_get_limit - LOG_REFERENCE = "POST_LETTERS" + LOG_REFERENCE = "PST_LTRS" }) } diff --git a/infrastructure/terraform/components/api/module_lambda_post_mi.tf b/infrastructure/terraform/components/api/module_lambda_post_mi.tf index 6f8a096ca..445418075 100644 --- a/infrastructure/terraform/components/api/module_lambda_post_mi.tf +++ b/infrastructure/terraform/components/api/module_lambda_post_mi.tf @@ -35,7 +35,7 @@ module "post_mi" { log_subscription_role_arn = local.acct.log_subscription_role_arn lambda_env_vars = merge(local.common_lambda_env_vars, { - LOG_REFERENCE = "POST_MI" + LOG_REFERENCE = "PST_MI" }) } diff --git a/lambdas/api-handler/src/config/log-references.ts b/lambdas/api-handler/src/config/log-references.ts new file mode 100644 index 000000000..62a4dcf90 --- /dev/null +++ b/lambdas/api-handler/src/config/log-references.ts @@ -0,0 +1,96 @@ +const LogRefs = { + HEALTHCHECK_PASSED: { + code: "001-HEALTHCHECK", + description: "Healthcheck passed", + }, + UNEXPECTED_QUERY_PARAMETERS: { + code: "002-UNEXPECTED-PARAMS", + description: "Unexpected query parameter(s) present", + }, + LIMIT_NOT_NUMBER: { + code: "003-NOT-NUMBER", + description: "limit parameter is not a number", + }, + LIMIT_INVALID: { + code: "004-LIMIT-INVALID", + description: "Limit value is invalid", + }, + PENDING_LETTERS_FETCHED: { + code: "005-PENDING-FETCHED", + description: "Pending letters successfully fetched", + }, + LETTER_FETCHED: { + code: "006-LETTER-FETCHED", + description: "Letter successfully fetched by id", + }, + PRESIGNED_URL_GENERATED: { + code: "007-URL-GENERATED", + description: "Generated presigned URL", + }, + PATCH_LETTER_RECEIVED: { + code: "008-PATCH-LETTER", + description: "Received patch letter request", + }, + POST_LETTERS_RECEIVED: { + code: "009-POST-LETTERS", + description: "Received post letters request", + }, + ENQUEUED_LETTER_UPDATES: { + code: "010-UPDATES-QUEUED", + description: "Enqueued letter updates", + }, + SOME_BATCH_ENTRIES_FAILED: { + code: "011-BATCH-FAILED", + description: "Some batch entries failed", + }, + ERROR_ENQUEUING_LETTER_STATUS_UPDATES: { + code: "012-ENQUEUE-ERROR", + description: "Error enqueuing letter status updates", + }, + LETTER_STATUS_UPDATE_SENT: { + code: "013-STATUS-SENT", + description: "Sent letter status update via topic", + }, + LETTER_STATUS_UPDATE_ERROR: { + code: "014-STATUS-ERROR", + description: "Error processing letter status update", + }, + MI_RETRIEVED: { + code: "015-MI-RETRIEVED", + description: "Retrieved management information", + }, + MI_POSTED: { + code: "016-MI-POSTED", + description: "Posted management information", + }, + VALIDATION_ERROR: { + code: "017-VALIDATION-ERROR", + description: "Validation error", + }, + NOT_FOUND_ERROR: { + code: "018-NOT-FOUND-ERROR", + description: "Not found error", + }, + INTERNAL_SERVER_ERROR: { + code: "019-SERVER-ERROR", + description: "Internal server error", + }, + INTERNAL_SERVER_ERROR_NON_ERROR: { + code: "020-SERVER-ERROR-NON-ERROR", + description: "Internal server error (non-Error thrown)", + }, + STATUS_ENDPOINT_ERROR: { + code: "021-ENDPOINT-ERROR", + description: "Status endpoint error, services not available", + }, + METRIC: { + code: "022-METRIC", + description: "Metric emitted", + }, + ERR_METRIC: { + code: "023-ERR-METRIC", + description: "Error metric emitted", + }, +}; + +export default LogRefs; diff --git a/lambdas/api-handler/src/handlers/__tests__/amendment-event-transformer.test.ts b/lambdas/api-handler/src/handlers/__tests__/amendment-event-transformer.test.ts index 034259d12..872bf0de0 100644 --- a/lambdas/api-handler/src/handlers/__tests__/amendment-event-transformer.test.ts +++ b/lambdas/api-handler/src/handlers/__tests__/amendment-event-transformer.test.ts @@ -149,6 +149,7 @@ describe("createLetterStatusUpdateHandler", () => { ); expect(mockedDeps.logger.error).toHaveBeenCalledWith({ + logRef: "014-STATUS-ERROR", description: "Error processing letter status update", err: mockError, messageId: "mid-id2", diff --git a/lambdas/api-handler/src/handlers/amendment-event-transformer.ts b/lambdas/api-handler/src/handlers/amendment-event-transformer.ts index aa3e981dd..c77ee1761 100644 --- a/lambdas/api-handler/src/handlers/amendment-event-transformer.ts +++ b/lambdas/api-handler/src/handlers/amendment-event-transformer.ts @@ -10,6 +10,7 @@ import { UpdateLetterCommandSchema, } from "../contracts/letters"; import { Deps } from "../config/deps"; +import LogRefs from "../config/log-references"; export default function createTransformAmendmentEventHandler( deps: Deps, @@ -37,7 +38,8 @@ export default function createTransformAmendmentEventHandler( buildSnsCommand(letterEvent, deps.env.SNS_TOPIC_ARN), ); deps.logger.info({ - description: "Sent letter status update via topic", + logRef: LogRefs.LETTER_STATUS_UPDATE_SENT.code, + description: LogRefs.LETTER_STATUS_UPDATE_SENT.description, letterId: updateLetterCommand.id, messageId: message.messageId, correlationId: message.messageAttributes.CorrelationId.stringValue, @@ -49,7 +51,8 @@ export default function createTransformAmendmentEventHandler( ); } catch (error) { deps.logger.error({ - description: "Error processing letter status update", + logRef: LogRefs.LETTER_STATUS_UPDATE_ERROR.code, + description: LogRefs.LETTER_STATUS_UPDATE_ERROR.description, err: error, messageId: message.messageId, correlationId: message.messageAttributes.CorrelationId.stringValue, @@ -90,7 +93,7 @@ function emitSuccessMetrics( unit: Unit.Count, }; const emf = buildEMFObject("amendment-event-transformer", dimensions, metric); - logger.info(emf); + logger.info({ ...emf, logRef: LogRefs.METRIC.code }); } function emitFailedItems( @@ -111,6 +114,6 @@ function emitFailedItems( dimensions, metric, ); - logger.info(emf); + logger.info({ ...emf, logRef: LogRefs.ERR_METRIC.code }); } } diff --git a/lambdas/api-handler/src/handlers/get-letter-data.ts b/lambdas/api-handler/src/handlers/get-letter-data.ts index 24f7804b4..c9370d76f 100644 --- a/lambdas/api-handler/src/handlers/get-letter-data.ts +++ b/lambdas/api-handler/src/handlers/get-letter-data.ts @@ -8,6 +8,7 @@ import { processError } from "../mappers/error-mapper"; import ValidationError from "../errors/validation-error"; import { getLetterDataUrl } from "../services/letter-operations"; import type { Deps } from "../config/deps"; +import LogRefs from "../config/log-references"; export default function createGetLetterDataHandler( deps: Deps, @@ -40,7 +41,8 @@ export default function createGetLetterDataHandler( const presignedUrl = await getLetterDataUrl(supplierId, letterId, deps); deps.logger.info({ - description: "Generated presigned URL", + logRef: LogRefs.PRESIGNED_URL_GENERATED.code, + description: LogRefs.PRESIGNED_URL_GENERATED.description, supplierId, letterId, correlationId: commonIds.value.correlationId, diff --git a/lambdas/api-handler/src/handlers/get-letter.ts b/lambdas/api-handler/src/handlers/get-letter.ts index d77618e8d..809452dc1 100644 --- a/lambdas/api-handler/src/handlers/get-letter.ts +++ b/lambdas/api-handler/src/handlers/get-letter.ts @@ -9,6 +9,7 @@ import { getLetterById } from "../services/letter-operations"; import { processError } from "../mappers/error-mapper"; import { mapToGetLetterResponse } from "../mappers/letter-mapper"; import { Deps } from "../config/deps"; +import LogRefs from "../config/log-references"; // Get letter data export default function createGetLetterHandler( @@ -48,7 +49,8 @@ export default function createGetLetterHandler( const response = mapToGetLetterResponse(letter); deps.logger.info({ - description: "Letter successfully fetched by id", + logRef: LogRefs.LETTER_FETCHED.code, + description: LogRefs.LETTER_FETCHED.description, supplierId, letterId, correlationId: commonIds.value.correlationId, diff --git a/lambdas/api-handler/src/handlers/get-letters.ts b/lambdas/api-handler/src/handlers/get-letters.ts index d2b98bd04..b5e606fb5 100644 --- a/lambdas/api-handler/src/handlers/get-letters.ts +++ b/lambdas/api-handler/src/handlers/get-letters.ts @@ -13,6 +13,7 @@ import { processError } from "../mappers/error-mapper"; import ValidationError from "../errors/validation-error"; import { mapToGetLettersResponse } from "../mappers/letter-mapper"; import type { Deps } from "../config/deps"; +import LogRefs from "../config/log-references"; function validateLimitParamOnly( queryStringParameters: APIGatewayProxyEventQueryStringParameters | null, @@ -23,7 +24,8 @@ function validateLimitParamOnly( Object.keys(queryStringParameters).some((key) => key !== "limit") ) { logger.info({ - description: "Unexpected query parameter(s) present", + logRef: LogRefs.UNEXPECTED_QUERY_PARAMETERS.code, + description: LogRefs.UNEXPECTED_QUERY_PARAMETERS.description, queryStringParameters, }); throw new ValidationError(ApiErrorDetail.InvalidRequestLimitOnly); @@ -33,7 +35,8 @@ function validateLimitParamOnly( function assertIsNumber(limitNumber: number, logger: Logger) { if (Number.isNaN(limitNumber)) { logger.info({ - description: "limit parameter is not a number", + logRef: LogRefs.LIMIT_NOT_NUMBER.code, + description: LogRefs.LIMIT_NOT_NUMBER.description, limitNumber, }); throw new ValidationError(ApiErrorDetail.InvalidRequestLimitNotANumber); @@ -47,7 +50,8 @@ function assertLimitInRange( ) { if (limitNumber <= 0 || limitNumber > maxLimit) { logger.info({ - description: "Limit value is invalid", + logRef: LogRefs.LIMIT_INVALID.code, + description: LogRefs.LIMIT_INVALID.description, limitNumber, }); throw new ValidationError(ApiErrorDetail.InvalidRequestLimitNotInRange, { @@ -117,7 +121,8 @@ export default function createGetLettersHandler( const response = mapToGetLettersResponse(letters); deps.logger.info({ - description: "Pending letters successfully fetched", + logRef: LogRefs.PENDING_LETTERS_FETCHED.code, + description: LogRefs.PENDING_LETTERS_FETCHED.description, supplierId, limitNumber, lettersCount: letters.length, diff --git a/lambdas/api-handler/src/handlers/get-mi.ts b/lambdas/api-handler/src/handlers/get-mi.ts index 0d8b9f05f..08d646d9b 100644 --- a/lambdas/api-handler/src/handlers/get-mi.ts +++ b/lambdas/api-handler/src/handlers/get-mi.ts @@ -9,6 +9,7 @@ import { processError } from "../mappers/error-mapper"; import { assertNotEmpty } from "../utils/validation"; import { extractCommonIds } from "../utils/common-ids"; import { Deps } from "../config/deps"; +import LogRefs from "../config/log-references"; export default function createGetMIHandler(deps: Deps): APIGatewayProxyHandler { return async (event) => { @@ -38,7 +39,8 @@ export default function createGetMIHandler(deps: Deps): APIGatewayProxyHandler { const result = await getMIOperation(miId, supplierId, deps.miRepo); deps.logger.info({ - description: "Retrieved management information", + logRef: LogRefs.MI_RETRIEVED.code, + description: LogRefs.MI_RETRIEVED.description, supplierId: commonIds.value.supplierId, correlationId: commonIds.value.correlationId, }); @@ -81,5 +83,5 @@ function emitMetric( unit: Unit.Count, }; const emf = buildEMFObject(source, dimensions, metric); - logger.info(emf); + logger.info({ ...emf, logRef: LogRefs.METRIC.code }); } diff --git a/lambdas/api-handler/src/handlers/get-status.ts b/lambdas/api-handler/src/handlers/get-status.ts index a7afc6c3a..8e77f708b 100644 --- a/lambdas/api-handler/src/handlers/get-status.ts +++ b/lambdas/api-handler/src/handlers/get-status.ts @@ -1,6 +1,7 @@ import { APIGatewayProxyHandler } from "aws-lambda"; import { ListBucketsCommand, S3Client } from "@aws-sdk/client-s3"; import { Deps } from "../config/deps"; +import LogRefs from "../config/log-references"; async function s3HealthCheck(s3Client: S3Client) { const command: ListBucketsCommand = new ListBucketsCommand({ @@ -18,7 +19,8 @@ export default function createGetStatusHandler( await s3HealthCheck(deps.s3Client); deps.logger.info({ - description: "Healthcheck passed", + logRef: LogRefs.HEALTHCHECK_PASSED.code, + description: LogRefs.HEALTHCHECK_PASSED.description, }); return { @@ -27,8 +29,9 @@ export default function createGetStatusHandler( }; } catch (error) { deps.logger.error({ + logRef: LogRefs.STATUS_ENDPOINT_ERROR.code, + description: LogRefs.STATUS_ENDPOINT_ERROR.description, err: error, - description: "Status endpoint error, services not available", }); return { statusCode: 500, diff --git a/lambdas/api-handler/src/handlers/patch-letter.ts b/lambdas/api-handler/src/handlers/patch-letter.ts index cddf49f6a..f57ed3d2e 100644 --- a/lambdas/api-handler/src/handlers/patch-letter.ts +++ b/lambdas/api-handler/src/handlers/patch-letter.ts @@ -14,6 +14,7 @@ import { assertNotEmpty } from "../utils/validation"; import { extractCommonIds } from "../utils/common-ids"; import { mapToUpdateCommand } from "../mappers/letter-mapper"; import type { Deps } from "../config/deps"; +import LogRefs from "../config/log-references"; export default function createPatchLetterHandler( deps: Deps, @@ -67,7 +68,8 @@ export default function createPatchLetterHandler( } deps.logger.info({ - description: "Received patch letter request", + logRef: LogRefs.PATCH_LETTER_RECEIVED.code, + description: LogRefs.PATCH_LETTER_RECEIVED.description, supplierId: commonIds.value.supplierId, letterId, attributes: patchLetterRequest.data.attributes, diff --git a/lambdas/api-handler/src/handlers/post-letters.ts b/lambdas/api-handler/src/handlers/post-letters.ts index fe6577128..3425c013a 100644 --- a/lambdas/api-handler/src/handlers/post-letters.ts +++ b/lambdas/api-handler/src/handlers/post-letters.ts @@ -15,6 +15,7 @@ import { mapToUpdateCommands } from "../mappers/letter-mapper"; import { enqueueLetterUpdateRequests } from "../services/letter-operations"; import { extractCommonIds } from "../utils/common-ids"; import { assertNotEmpty, requireEnvVar } from "../utils/validation"; +import LogRefs from "../config/log-references"; function duplicateIdsExist(postLettersRequest: PostLettersRequest) { const ids = postLettersRequest.data.map((item) => item.id); @@ -40,7 +41,7 @@ function emitSuccessMetrics( unit: Unit.Count, }; const emf = buildEMFObject("postLetters", dimensions, metric); - logger.info(emf); + logger.info({ ...emf, logRef: LogRefs.METRIC.code }); } } @@ -95,7 +96,8 @@ export default function createPostLettersHandler( } deps.logger.info({ - description: "Received post letters request", + logRef: LogRefs.POST_LETTERS_RECEIVED.code, + description: LogRefs.POST_LETTERS_RECEIVED.description, supplierId: commonIds.value.supplierId, newAttributes: postLettersRequest.data.map((request) => ({ id: request.id, @@ -150,5 +152,5 @@ function emitErrorMetrics(supplierId: string, logger: pino.Logger) { unit: Unit.Count, }; const emf = buildEMFObject("postLetters", dimensions, metric); - logger.info(emf); + logger.info({ ...emf, logRef: LogRefs.ERR_METRIC.code }); } diff --git a/lambdas/api-handler/src/handlers/post-mi.ts b/lambdas/api-handler/src/handlers/post-mi.ts index f39759183..81ad11c56 100644 --- a/lambdas/api-handler/src/handlers/post-mi.ts +++ b/lambdas/api-handler/src/handlers/post-mi.ts @@ -11,6 +11,7 @@ import { extractCommonIds } from "../utils/common-ids"; import { PostMIRequest, PostMIRequestSchema } from "../contracts/mi"; import { mapToMI } from "../mappers/mi-mapper"; import { Deps } from "../config/deps"; +import LogRefs from "../config/log-references"; export default function createPostMIHandler( deps: Deps, @@ -59,7 +60,8 @@ export default function createPostMIHandler( ); deps.logger.info({ - description: "Posted management information", + logRef: LogRefs.MI_POSTED.code, + description: LogRefs.MI_POSTED.description, supplierId: commonIds.value.supplierId, correlationId: commonIds.value.correlationId, }); @@ -72,14 +74,14 @@ export default function createPostMIHandler( unit: Unit.Count, }; let emf = buildEMFObject("postMi", dimensions, metric); - deps.logger.info(emf); + deps.logger.info({ ...emf, logRef: LogRefs.METRIC.code }); // metric displaying the type/number of lineItems posted per supplier dimensions.lineItem = postMIRequest.data.attributes.lineItem; metric.key = "LineItem per supplier"; metric.value = postMIRequest.data.attributes.quantity; emf = buildEMFObject("postMi", dimensions, metric); - deps.logger.info(emf); + deps.logger.info({ ...emf, logRef: LogRefs.METRIC.code }); return { statusCode: 201, @@ -100,5 +102,5 @@ function emitErrorMetric(supplierId: string, logger: pino.Logger) { unit: Unit.Count, }; const emf = buildEMFObject("postMi", dimensions, metric); - logger.info(emf); + logger.info({ ...emf, logRef: LogRefs.ERR_METRIC.code }); } diff --git a/lambdas/api-handler/src/mappers/error-mapper.ts b/lambdas/api-handler/src/mappers/error-mapper.ts index f4875fdee..50924cc12 100644 --- a/lambdas/api-handler/src/mappers/error-mapper.ts +++ b/lambdas/api-handler/src/mappers/error-mapper.ts @@ -11,6 +11,7 @@ import { ErrorResponse, buildApiError, } from "../contracts/errors"; +import LogRefs from "../config/log-references"; function codeToTitle(code: ApiErrorCode): ApiErrorTitle { switch (code) { @@ -72,7 +73,12 @@ export function logAndMapToApiError( logger: Logger, ): ApiError { if (error instanceof ValidationError) { - logger.info({ description: "Validation error", err: error, correlationId }); + logger.info({ + logRef: LogRefs.VALIDATION_ERROR.code, + description: LogRefs.VALIDATION_ERROR.description, + err: error, + correlationId, + }); return mapToApiError( ApiErrorCode.InvalidRequest, error.detail, @@ -80,12 +86,18 @@ export function logAndMapToApiError( ); } if (error instanceof NotFoundError) { - logger.info({ description: "Not found error", err: error, correlationId }); + logger.info({ + logRef: LogRefs.NOT_FOUND_ERROR.code, + description: LogRefs.NOT_FOUND_ERROR.description, + err: error, + correlationId, + }); return mapToApiError(ApiErrorCode.NotFound, error.detail, correlationId); } if (error instanceof Error) { logger.error({ - description: "Internal server error", + logRef: LogRefs.INTERNAL_SERVER_ERROR.code, + description: LogRefs.INTERNAL_SERVER_ERROR.description, err: error, correlationId, }); @@ -96,7 +108,8 @@ export function logAndMapToApiError( ); } logger.error({ - description: "Internal server error (non-Error thrown)", + logRef: LogRefs.INTERNAL_SERVER_ERROR_NON_ERROR.code, + description: LogRefs.INTERNAL_SERVER_ERROR_NON_ERROR.description, correlationId, }); return mapToApiError( diff --git a/lambdas/api-handler/src/services/__tests__/letter-operations.test.ts b/lambdas/api-handler/src/services/__tests__/letter-operations.test.ts index 0d6c1cf1c..8cfb79d30 100644 --- a/lambdas/api-handler/src/services/__tests__/letter-operations.test.ts +++ b/lambdas/api-handler/src/services/__tests__/letter-operations.test.ts @@ -290,6 +290,7 @@ describe("enqueueLetterUpdateRequests function", () => { ); expect(deps.logger.info).toHaveBeenCalledWith({ + logRef: "010-UPDATES-QUEUED", description: "Enqueued letter updates", correlationId: "correlationId1", messageIds: ["msg-001", "msg-002"], diff --git a/lambdas/api-handler/src/services/letter-operations.ts b/lambdas/api-handler/src/services/letter-operations.ts index b0053d8dc..03aee6d20 100644 --- a/lambdas/api-handler/src/services/letter-operations.ts +++ b/lambdas/api-handler/src/services/letter-operations.ts @@ -12,6 +12,7 @@ import NotFoundError from "../errors/not-found-error"; import { UpdateLetterCommand } from "../contracts/letters"; import { ApiErrorDetail } from "../contracts/errors"; import { Deps } from "../config/deps"; +import LogRefs from "../config/log-references"; async function getDownloadUrl( s3Uri: string, @@ -148,22 +149,26 @@ export async function enqueueLetterUpdateRequests( const result = await deps.sqsClient.send(cmd); if (result.Successful && result.Successful.length > 0) { deps.logger.info({ - description: "Enqueued letter updates", + logRef: LogRefs.ENQUEUED_LETTER_UPDATES.code, + description: LogRefs.ENQUEUED_LETTER_UPDATES.description, correlationId, messageIds: result.Successful.map((entry) => entry.MessageId), }); } if (result.Failed && result.Failed.length > 0) { deps.logger.error({ + logRef: LogRefs.SOME_BATCH_ENTRIES_FAILED.code, + description: LogRefs.SOME_BATCH_ENTRIES_FAILED.description, failed: result.Failed, - description: "Some batch entries failed", correlationId, }); } } catch (error) { deps.logger.error({ + logRef: LogRefs.ERROR_ENQUEUING_LETTER_STATUS_UPDATES.code, + description: + LogRefs.ERROR_ENQUEUING_LETTER_STATUS_UPDATES.description, err: error, - description: "Error enqueuing letter status updates", correlationId, }); } From 5ccad5b968f5394709f6b156ec40968ad8ea51ba Mon Sep 17 00:00:00 2001 From: David Wass Date: Thu, 17 Sep 2026 15:32:55 +0100 Subject: [PATCH 05/11] authorizer --- .../api/module_authorizer_lambda.tf | 2 +- .../authorizer/src/__tests__/index.test.ts | 1 + lambdas/authorizer/src/authorizer.ts | 13 ++++++++---- lambdas/authorizer/src/log-references.ts | 20 +++++++++++++++++++ 4 files changed, 31 insertions(+), 5 deletions(-) create mode 100644 lambdas/authorizer/src/log-references.ts diff --git a/infrastructure/terraform/components/api/module_authorizer_lambda.tf b/infrastructure/terraform/components/api/module_authorizer_lambda.tf index 6d7ce3de3..224b841ae 100644 --- a/infrastructure/terraform/components/api/module_authorizer_lambda.tf +++ b/infrastructure/terraform/components/api/module_authorizer_lambda.tf @@ -39,7 +39,7 @@ module "authorizer_lambda" { CLIENT_CERTIFICATE_EXPIRATION_ALERT_DAYS = 30, APIM_SUPPLIER_ID_HEADER = "NHSD-Supplier-ID", SUPPLIERS_TABLE_NAME = aws_dynamodb_table.suppliers.name - LOG_REFERENCE = "AUTHORIZER" + LOG_REFERENCE = "AUTH" } } diff --git a/lambdas/authorizer/src/__tests__/index.test.ts b/lambdas/authorizer/src/__tests__/index.test.ts index ae8770f46..6cf8ff28b 100644 --- a/lambdas/authorizer/src/__tests__/index.test.ts +++ b/lambdas/authorizer/src/__tests__/index.test.ts @@ -117,6 +117,7 @@ describe("Authorizer Lambda Function", () => { expect(metricScope).toHaveBeenCalledTimes(1); expect(mockedDeps.logger.warn).toHaveBeenCalledWith({ + logRef: "004-CERTIFICATE-EXPIRY", description: "APIM Certificate expiry", days: 30, }); diff --git a/lambdas/authorizer/src/authorizer.ts b/lambdas/authorizer/src/authorizer.ts index b26ddd253..8806dd6ee 100644 --- a/lambdas/authorizer/src/authorizer.ts +++ b/lambdas/authorizer/src/authorizer.ts @@ -10,6 +10,7 @@ import { import { MetricsLogger, metricScope } from "aws-embedded-metrics"; import { Supplier } from "@internal/datastore"; import { Deps } from "./deps"; +import LogRefs from "./log-references"; export default function createAuthorizerHandler( deps: Deps, @@ -24,7 +25,8 @@ export default function createAuthorizerHandler( getSupplier(event.headers, deps) .then((supplier: Supplier) => { deps.logger.info({ - description: "Allowed event", + logRef: LogRefs.ALLOWED_EVENT.code, + description: LogRefs.ALLOWED_EVENT.description, methodArn: event.methodArn, supplierId: supplier.id, }); @@ -32,7 +34,8 @@ export default function createAuthorizerHandler( }) .catch((error) => { deps.logger.warn({ - description: "Denied event", + logRef: LogRefs.DENIED_EVENT.code, + description: LogRefs.DENIED_EVENT.description, err: error, methodArn: event.methodArn, }); @@ -106,7 +109,8 @@ async function checkCertificateExpiry( deps: Deps, ): Promise { deps.logger.info({ - description: "Client certificate details", + logRef: LogRefs.CLIENT_CERTIFICATE_DETAILS.code, + description: LogRefs.CLIENT_CERTIFICATE_DETAILS.description, issuerDN: certificate?.issuerDN || "-", subjectDN: certificate?.subjectDN || "-", validity: certificate?.validity || "-", @@ -122,7 +126,8 @@ async function checkCertificateExpiry( if (expiry <= deps.env.CLIENT_CERTIFICATE_EXPIRATION_ALERT_DAYS) { await metricScope((metrics: MetricsLogger) => async () => { deps.logger.warn({ - description: "APIM Certificate expiry", + logRef: LogRefs.APIM_CERTIFICATE_EXPIRY.code, + description: LogRefs.APIM_CERTIFICATE_EXPIRY.description, days: expiry, }); metrics.setNamespace( diff --git a/lambdas/authorizer/src/log-references.ts b/lambdas/authorizer/src/log-references.ts new file mode 100644 index 000000000..3f26b1f43 --- /dev/null +++ b/lambdas/authorizer/src/log-references.ts @@ -0,0 +1,20 @@ +const LogRefs = { + ALLOWED_EVENT: { + code: "001-ALLOWED", + description: "Allowed event", + }, + DENIED_EVENT: { + code: "002-DENIED", + description: "Denied event", + }, + CLIENT_CERTIFICATE_DETAILS: { + code: "003-CERTIFICAT-DETAILS", + description: "Client certificate details", + }, + APIM_CERTIFICATE_EXPIRY: { + code: "004-CERTIFICATE-EXPIRY", + description: "APIM Certificate expiry", + }, +}; + +export default LogRefs; From b53c3b982a6e76c1bc77682b4741e545c2800741 Mon Sep 17 00:00:00 2001 From: David Wass Date: Thu, 17 Sep 2026 15:40:57 +0100 Subject: [PATCH 06/11] letter updates transformer --- ...odule_lambda_letter_updates_transformer.tf | 2 +- .../letter-updates-transformer.test.ts | 1 + .../src/letter-updates-transformer.ts | 36 ++++++++++++----- .../src/log-references.ts | 40 +++++++++++++++++++ 4 files changed, 69 insertions(+), 10 deletions(-) create mode 100644 lambdas/letter-updates-transformer/src/log-references.ts diff --git a/infrastructure/terraform/components/api/module_lambda_letter_updates_transformer.tf b/infrastructure/terraform/components/api/module_lambda_letter_updates_transformer.tf index cbe4f17db..893f37999 100644 --- a/infrastructure/terraform/components/api/module_lambda_letter_updates_transformer.tf +++ b/infrastructure/terraform/components/api/module_lambda_letter_updates_transformer.tf @@ -37,7 +37,7 @@ module "letter_updates_transformer" { lambda_env_vars = merge(local.common_lambda_env_vars, { EVENTPUB_SNS_TOPIC_ARN = "${module.eventpub.sns_topic.arn}", EVENT_SOURCE = var.letter_event_source - LOG_REFERENCE = "LETTER_UPDATES_TRANSFORMER" + LOG_REFERENCE = "LTR_UPD_TFRM" }) } diff --git a/lambdas/letter-updates-transformer/src/__tests__/letter-updates-transformer.test.ts b/lambdas/letter-updates-transformer/src/__tests__/letter-updates-transformer.test.ts index 84aef0062..80c117dd0 100644 --- a/lambdas/letter-updates-transformer/src/__tests__/letter-updates-transformer.test.ts +++ b/lambdas/letter-updates-transformer/src/__tests__/letter-updates-transformer.test.ts @@ -202,6 +202,7 @@ describe("letter-updates-transformer Lambda", () => { expect(mockedDeps.logger.error).toHaveBeenCalledWith( expect.objectContaining({ + logRef: "007-ERROR-EXTRACTING-PAYLOAD", description: "Error extracting payload", err: expect.any(Error), record: expect.objectContaining({ diff --git a/lambdas/letter-updates-transformer/src/letter-updates-transformer.ts b/lambdas/letter-updates-transformer/src/letter-updates-transformer.ts index efe30ee61..a5eb4be2a 100644 --- a/lambdas/letter-updates-transformer/src/letter-updates-transformer.ts +++ b/lambdas/letter-updates-transformer/src/letter-updates-transformer.ts @@ -16,15 +16,21 @@ import { mapLetterToCloudEvent } from "@internal/event-builders/src"; import { Unit } from "aws-embedded-metrics"; import pino from "pino"; import { Deps } from "./deps"; +import LogRefs from "./log-references"; // SNS PublishBatchCommand supports up to 10 messages per batch const BATCH_SIZE = 10; export default function createHandler(deps: Deps): Handler { return async (streamEvent: KinesisStreamEvent) => { - deps.logger.info({ description: "Received event", streamEvent }); deps.logger.info({ - description: "Number of records", + logRef: LogRefs.RECEIVED_EVENT.code, + description: LogRefs.RECEIVED_EVENT.description, + streamEvent, + }); + deps.logger.info({ + logRef: LogRefs.NUMBER_OF_RECORDS.code, + description: LogRefs.NUMBER_OF_RECORDS.description, count: streamEvent.Records?.length || 0, }); @@ -42,7 +48,8 @@ export default function createHandler(deps: Deps): Handler { populateEventTypeMap(cloudEvents); for (const batch of generateBatches(cloudEvents)) { deps.logger.info({ - description: "Publishing batch", + logRef: LogRefs.PUBLISHING_BATCH.code, + description: LogRefs.PUBLISHING_BATCH.description, size: batch.length, letterEvents: batch, }); @@ -77,7 +84,7 @@ function emitMetrics(logger: pino.Logger, eventTypeCount: Map) { unit: Unit.Count, }; const emf = buildEMFObject(namespace, dimensions, metric); - logger.info(emf); + logger.info({ ...emf, logRef: LogRefs.METRIC.code }); } } @@ -95,7 +102,8 @@ function filterRecord(record: DynamoDBRecord, deps: Deps): boolean { } deps.logger.info({ - description: "Filtering record", + logRef: LogRefs.FILTERING_RECORD.code, + description: LogRefs.FILTERING_RECORD.description, eventName: record.eventName, eventId: record.eventID, allowEvent, @@ -110,20 +118,30 @@ function extractPayload( ): DynamoDBRecord { try { deps.logger.info({ - description: "Processing Kinesis record", + logRef: LogRefs.PROCESSING_KINESIS_RECORD.code, + description: LogRefs.PROCESSING_KINESIS_RECORD.description, recordId: record.kinesis.sequenceNumber, }); // Kinesis data is base64 encoded const payload = Buffer.from(record.kinesis.data, "base64").toString("utf8"); - deps.logger.info({ description: "Decoded payload", payload }); + deps.logger.info({ + logRef: LogRefs.DECODED_PAYLOAD.code, + description: LogRefs.DECODED_PAYLOAD.description, + payload, + }); const jsonParsed = JSON.parse(payload); - deps.logger.info({ description: "Extracted dynamoDBRecord", jsonParsed }); + deps.logger.info({ + logRef: LogRefs.EXTRACTED_DYNAMODB_RECORD.code, + description: LogRefs.EXTRACTED_DYNAMODB_RECORD.description, + jsonParsed, + }); return jsonParsed; } catch (error) { deps.logger.error({ - description: "Error extracting payload", + logRef: LogRefs.ERROR_EXTRACTING_PAYLOAD.code, + description: LogRefs.ERROR_EXTRACTING_PAYLOAD.description, err: error, record, }); diff --git a/lambdas/letter-updates-transformer/src/log-references.ts b/lambdas/letter-updates-transformer/src/log-references.ts new file mode 100644 index 000000000..af7c55236 --- /dev/null +++ b/lambdas/letter-updates-transformer/src/log-references.ts @@ -0,0 +1,40 @@ +const LogRefs = { + RECEIVED_EVENT: { + code: "001-RECEIVED-EVENT", + description: "Received event", + }, + NUMBER_OF_RECORDS: { + code: "002-NUMBER-OF-RECORDS", + description: "Number of records", + }, + FILTERING_RECORD: { + code: "003-FILTERING-RECORD", + description: "Filtering record", + }, + PROCESSING_KINESIS_RECORD: { + code: "004-PROCESSING-RECORD", + description: "Processing Kinesis record", + }, + DECODED_PAYLOAD: { + code: "005-DECODED-PAYLOAD", + description: "Decoded payload", + }, + EXTRACTED_DYNAMODB_RECORD: { + code: "006-EXTRACTED-DYNAMODB-RECORD", + description: "Extracted dynamoDBRecord", + }, + ERROR_EXTRACTING_PAYLOAD: { + code: "007-ERROR-EXTRACTING-PAYLOAD", + description: "Error extracting payload", + }, + PUBLISHING_BATCH: { + code: "008-PUBLISHING-BATCH", + description: "Publishing batch", + }, + METRIC: { + code: "009-METRIC", + description: "Metric", + }, +}; + +export default LogRefs; From 91f049faa18ca2bb48033b553bfa193852b7d689 Mon Sep 17 00:00:00 2001 From: David Wass Date: Fri, 18 Sep 2026 08:28:20 +0100 Subject: [PATCH 07/11] mi updates --- .../module_lambda_mi_updates_transformer.tf | 2 +- .../src/log-references.ts | 24 +++++++++++++++++++ .../src/mappers/mi-mapper.ts | 8 ++++++- .../src/mi-updates-transformer.ts | 21 ++++++++++++---- 4 files changed, 49 insertions(+), 6 deletions(-) create mode 100644 lambdas/mi-updates-transformer/src/log-references.ts diff --git a/infrastructure/terraform/components/api/module_lambda_mi_updates_transformer.tf b/infrastructure/terraform/components/api/module_lambda_mi_updates_transformer.tf index dbd54e66b..64012c723 100644 --- a/infrastructure/terraform/components/api/module_lambda_mi_updates_transformer.tf +++ b/infrastructure/terraform/components/api/module_lambda_mi_updates_transformer.tf @@ -37,7 +37,7 @@ module "mi_updates_transformer" { lambda_env_vars = merge(local.common_lambda_env_vars, { EVENTPUB_SNS_TOPIC_ARN = "${module.eventpub.sns_topic.arn}" - LOG_REFERENCE = "MI_UPDATES_TRANSFORMER" + LOG_REFERENCE = "MI_UPD_TFRM" }) } diff --git a/lambdas/mi-updates-transformer/src/log-references.ts b/lambdas/mi-updates-transformer/src/log-references.ts new file mode 100644 index 000000000..f83208e6e --- /dev/null +++ b/lambdas/mi-updates-transformer/src/log-references.ts @@ -0,0 +1,24 @@ +const LogRefs = { + RECEIVED_EVENT: { + code: "001-RECEIVED-EVENT", + description: "Received event", + }, + EXTRACTED_PAYLOAD: { + code: "002-EXTRACTED-PAYLOAD", + description: "Extracted payload", + }, + MAPPING_MI_TO_CLOUD_EVENT: { + code: "003-MAPPING-MI", + description: "Mapping MI to CloudEvent", + }, + BUILT_MESSAGE: { + code: "004-BUILT-MESSAGE", + description: "Built message", + }, + METRIC: { + code: "005-METRIC", + description: "Metric", + }, +}; + +export default LogRefs; diff --git a/lambdas/mi-updates-transformer/src/mappers/mi-mapper.ts b/lambdas/mi-updates-transformer/src/mappers/mi-mapper.ts index 26153c248..7c09ba3db 100644 --- a/lambdas/mi-updates-transformer/src/mappers/mi-mapper.ts +++ b/lambdas/mi-updates-transformer/src/mappers/mi-mapper.ts @@ -2,13 +2,19 @@ import { MI } from "@internal/datastore"; import { MISubmittedEvent } from "@nhsdigital/nhs-notify-event-schemas-supplier-api/src"; import { randomBytes, randomUUID } from "node:crypto"; import { Deps } from "../deps"; +import LogRefs from "../log-references"; export function mapMIToCloudEvent(mi: MI, deps: Deps): MISubmittedEvent { const now = new Date().toISOString(); const eventId = randomUUID(); const dataschemaversion = "1.1.5"; - deps.logger.info({ description: "Mapping MI to CloudEvent", mi, eventId }); + deps.logger.info({ + logRef: LogRefs.MAPPING_MI_TO_CLOUD_EVENT.code, + description: LogRefs.MAPPING_MI_TO_CLOUD_EVENT.description, + mi, + eventId, + }); return { specversion: "1.0", diff --git a/lambdas/mi-updates-transformer/src/mi-updates-transformer.ts b/lambdas/mi-updates-transformer/src/mi-updates-transformer.ts index 416ba7fa5..3584db505 100644 --- a/lambdas/mi-updates-transformer/src/mi-updates-transformer.ts +++ b/lambdas/mi-updates-transformer/src/mi-updates-transformer.ts @@ -16,6 +16,7 @@ import pino from "pino"; import { MetricEntry, buildEMFObject } from "@internal/helpers"; import { mapMIToCloudEvent } from "./mappers/mi-mapper"; import { Deps } from "./deps"; +import LogRefs from "./log-references"; // SNS PublishBatchCommand supports up to 10 messages per batch const BATCH_SIZE = 10; @@ -34,7 +35,11 @@ function buildMessage( Id: event.id, Message: JSON.stringify(event), }; - deps.logger.info({ description: "Built message", message }); + deps.logger.info({ + logRef: LogRefs.BUILT_MESSAGE.code, + description: LogRefs.BUILT_MESSAGE.description, + message, + }); return message; } @@ -43,7 +48,11 @@ function extractPayload( deps: Deps, ): DynamoDBRecord { const payload = Buffer.from(record.kinesis.data, "base64").toString("utf8"); - deps.logger.info({ description: "Extracted payload", payload }); + deps.logger.info({ + logRef: LogRefs.EXTRACTED_PAYLOAD.code, + description: LogRefs.EXTRACTED_PAYLOAD.description, + payload, + }); return JSON.parse(payload); } @@ -62,13 +71,17 @@ function emitMetrics(logger: pino.Logger, eventTypeCount: Map) { unit: Unit.Count, }; const emf = buildEMFObject(namespace, dimensions, metric); - logger.info(emf); + logger.info({ ...emf, logRef: LogRefs.METRIC.code }); } } export default function createHandler(deps: Deps): Handler { return async (streamEvent: KinesisStreamEvent) => { - deps.logger.info({ description: "Received event", streamEvent }); + deps.logger.info({ + logRef: LogRefs.RECEIVED_EVENT.code, + description: LogRefs.RECEIVED_EVENT.description, + streamEvent, + }); const cloudEvents: MISubmittedEvent[] = streamEvent.Records.map((record) => extractPayload(record, deps), From 15e3d5d08c1aedc144bba6e0e90ef3dcbdf2d459 Mon Sep 17 00:00:00 2001 From: David Wass Date: Fri, 18 Sep 2026 08:56:29 +0100 Subject: [PATCH 08/11] supplier config ingress --- .../module_lambda_supplier_config_ingress.tf | 2 +- .../src/config/log-references.ts | 28 ++++++++++++ .../supplier-config-ingress-handler.ts | 44 ++++++++++++++----- 3 files changed, 63 insertions(+), 11 deletions(-) create mode 100644 lambdas/supplier-config-ingress/src/config/log-references.ts diff --git a/infrastructure/terraform/components/api/module_lambda_supplier_config_ingress.tf b/infrastructure/terraform/components/api/module_lambda_supplier_config_ingress.tf index d2033d63d..e3e57cabf 100644 --- a/infrastructure/terraform/components/api/module_lambda_supplier_config_ingress.tf +++ b/infrastructure/terraform/components/api/module_lambda_supplier_config_ingress.tf @@ -35,7 +35,7 @@ module "supplier_config_ingress" { log_subscription_role_arn = local.acct.log_subscription_role_arn lambda_env_vars = merge(local.common_lambda_env_vars, { - LOG_REFERENCE = "SUPPLIER_CONFIG_INGRESS" + LOG_REFERENCE = "SUP_CFG_ING" }) } diff --git a/lambdas/supplier-config-ingress/src/config/log-references.ts b/lambdas/supplier-config-ingress/src/config/log-references.ts new file mode 100644 index 000000000..e75b67e9f --- /dev/null +++ b/lambdas/supplier-config-ingress/src/config/log-references.ts @@ -0,0 +1,28 @@ +const LogRefs = { + PROCESSING_RECORD: { + code: "001-PROCESSING-RECORD", + description: "Processing record", + }, + PROCESSING_SUPPLIER_CONFIG_UPSERT: { + code: "002-PROCESSING-CONFIG", + description: "Processing supplier config upsert", + }, + SUPPLIER_CONFIG_UPSERTED: { + code: "003-CONFIG-UPSERTED", + description: "Supplier config upserted", + }, + FAILED_TO_PROCESS_SUPPLIER_CONFIG_RECORD: { + code: "004-PROCESSING-FAILED", + description: "Failed to process supplier config record", + }, + SUCCESS_METRIC: { + code: "005-SUCCESS-METRIC", + description: "Supplier config ingress success metric", + }, + FAILURE_METRIC: { + code: "006-FAILURE-METRIC", + description: "Supplier config ingress failure metric", + }, +}; + +export default LogRefs; diff --git a/lambdas/supplier-config-ingress/src/handler/supplier-config-ingress-handler.ts b/lambdas/supplier-config-ingress/src/handler/supplier-config-ingress-handler.ts index 90b57bf2c..698620657 100644 --- a/lambdas/supplier-config-ingress/src/handler/supplier-config-ingress-handler.ts +++ b/lambdas/supplier-config-ingress/src/handler/supplier-config-ingress-handler.ts @@ -16,6 +16,7 @@ import { } from "@nhsdigital/nhs-notify-event-schemas-supplier-config"; import { MetricEntry, MetricStatus, buildEMFObject } from "@internal/helpers"; import { Deps } from "../config/deps"; +import LogRefs from "../config/log-references"; const $EventEnvelope = z.object({ type: z.string(), @@ -70,7 +71,10 @@ function emitSuccessMetric( value: 1, unit: Unit.Count, }; - logger.info(buildEMFObject(namespace, dimensions, metric)); + logger.info({ + ...buildEMFObject(namespace, dimensions, metric), + logRef: LogRefs.SUCCESS_METRIC.code, + }); } function emitFailureMetric(logger: Deps["logger"], entity: string) { @@ -81,7 +85,10 @@ function emitFailureMetric(logger: Deps["logger"], entity: string) { value: 1, unit: Unit.Count, }; - logger.info(buildEMFObject(namespace, dimensions, metric)); + logger.info({ + ...buildEMFObject(namespace, dimensions, metric), + logRef: LogRefs.FAILURE_METRIC.code, + }); } export default function createSupplierConfigIngressHandler(deps: Deps) { @@ -95,15 +102,23 @@ export default function createSupplierConfigIngressHandler(deps: Deps) { let entity: string | undefined; try { logger.info( - { messageId: record.messageId, body: record.body }, - "Processing record", + { + logRef: LogRefs.PROCESSING_RECORD.code, + messageId: record.messageId, + body: record.body, + }, + LogRefs.PROCESSING_RECORD.description, ); const parsed = parseSupplierConfigFromRecord(record); entity = parsed.entity; logger.info( - { entity, id: parsed.config.id }, - "Processing supplier config upsert", + { + logRef: LogRefs.PROCESSING_SUPPLIER_CONFIG_UPSERT.code, + entity, + id: parsed.config.id, + }, + LogRefs.PROCESSING_SUPPLIER_CONFIG_UPSERT.description, ); const result = await supplierConfigRepo.upsertSupplierConfig( @@ -114,13 +129,22 @@ export default function createSupplierConfigIngressHandler(deps: Deps) { emitSuccessMetric(logger, parsed.entity, result); logger.info( - { entity, pk: parsed.config.id, result }, - "Supplier config upserted", + { + logRef: LogRefs.SUPPLIER_CONFIG_UPSERTED.code, + entity, + pk: parsed.config.id, + result, + }, + LogRefs.SUPPLIER_CONFIG_UPSERTED.description, ); } catch (error) { logger.error( - { error, messageId: record.messageId }, - "Failed to process supplier config record", + { + logRef: LogRefs.FAILED_TO_PROCESS_SUPPLIER_CONFIG_RECORD.code, + error, + messageId: record.messageId, + }, + LogRefs.FAILED_TO_PROCESS_SUPPLIER_CONFIG_RECORD.description, ); batchItemFailures.push({ itemIdentifier: record.messageId }); failedEntities.push(entity ?? "unknown"); From fffb307239b37e51c95ed00027bd867c4ab1dee0 Mon Sep 17 00:00:00 2001 From: David Wass Date: Fri, 18 Sep 2026 09:03:34 +0100 Subject: [PATCH 09/11] supplier-mock --- .../api/module_lambda_supplier_mock.tf | 2 +- .../src/handler/log-references.ts | 40 +++++++++++++++++++ .../src/handler/supplier-mock.ts | 30 ++++++++++---- 3 files changed, 63 insertions(+), 9 deletions(-) create mode 100644 lambdas/supplier-mock/src/handler/log-references.ts diff --git a/infrastructure/terraform/components/api/module_lambda_supplier_mock.tf b/infrastructure/terraform/components/api/module_lambda_supplier_mock.tf index 87f91106a..9669fee05 100644 --- a/infrastructure/terraform/components/api/module_lambda_supplier_mock.tf +++ b/infrastructure/terraform/components/api/module_lambda_supplier_mock.tf @@ -40,7 +40,7 @@ module "supplier_mock" { GET_LETTERS_FUNCTION_NAME = module.get_letters.function_name PATCH_LETTER_FUNCTION_NAME = module.patch_letter.function_name SUPPLIER_MOCK_CONFIG_PARAM_NAME = aws_ssm_parameter.supplier_mock_config[0].name - LOG_REFERENCE = "SUPPLIER_MOCK" + LOG_REFERENCE = "SUP_MCK" }) } diff --git a/lambdas/supplier-mock/src/handler/log-references.ts b/lambdas/supplier-mock/src/handler/log-references.ts new file mode 100644 index 000000000..de524a35f --- /dev/null +++ b/lambdas/supplier-mock/src/handler/log-references.ts @@ -0,0 +1,40 @@ +const LogRefs = { + STARTING_SUPPLIER_MOCK: { + code: "001-STARTING", + description: "Starting supplier mock lambda", + }, + PARSED_SUPPLIER_MOCK_CONFIG: { + code: "002-CONFIG-PARSED", + description: "Parsed supplier mock config from Parameter Store", + }, + CALLING_GET_LETTERS: { + code: "003-CALLING-GET-LETTERS", + description: "about to call getLetters with limit", + }, + FORWARDING_LETTERS_TO_PATCH: { + code: "004-FORWARDING-LETTERS", + description: "Forwarding letters to patch_letter lambda", + }, + FAILED_TO_INVOKE_GET_LETTERS: { + code: "005-GET-LETTERS-FAILED", + description: "Failed to invoke get_letters lambda", + }, + FAILED_TO_INVOKE_PATCH_LETTER: { + code: "006-PATCH-LETTER-FAILED", + description: "Failed to invoke patch_letter lambda", + }, + PATCH_LETTER_FUNCTION_ERROR: { + code: "007-PATCH-LETTER-ERROR", + description: "patch_letter lambda returned a function error", + }, + FAILED_TO_READ_CONFIG: { + code: "008-CONFIG-READ-FAILED", + description: "Failed to read supplier mock config from Parameter Store", + }, + FINISHED_SUPPLIER_MOCK: { + code: "009-FINISHED", + description: "Finished supplier mock lambda", + }, +}; + +export default LogRefs; diff --git a/lambdas/supplier-mock/src/handler/supplier-mock.ts b/lambdas/supplier-mock/src/handler/supplier-mock.ts index e1df42efc..4b37289bc 100644 --- a/lambdas/supplier-mock/src/handler/supplier-mock.ts +++ b/lambdas/supplier-mock/src/handler/supplier-mock.ts @@ -1,10 +1,14 @@ import { InvokeCommand } from "@aws-sdk/client-lambda"; import { Deps } from "./deps"; import { SupplierMockConfig } from "./types"; +import LogRefs from "./log-references"; export default function createHandler(deps: Deps) { return async () => { - deps.logger.info("Starting supplier mock lambda"); + deps.logger.info( + { logRef: LogRefs.STARTING_SUPPLIER_MOCK.code }, + LogRefs.STARTING_SUPPLIER_MOCK.description, + ); checkDepsAreSet(deps); const config = await parseSupplierMockConfig(deps); const headers = { @@ -15,9 +19,10 @@ export default function createHandler(deps: Deps) { const letters = await callGetLetters(deps, headers, config.limit); deps.logger.info( { + logRef: LogRefs.FORWARDING_LETTERS_TO_PATCH.code, lettersCount: letters.length, }, - "Forwarding letters to patch_letter lambda", + LogRefs.FORWARDING_LETTERS_TO_PATCH.description, ); await callPatchLetter( deps, @@ -25,7 +30,10 @@ export default function createHandler(deps: Deps) { letters, config.specificationIdMapping, ); - deps.logger.info("Finished supplier mock lambda"); + deps.logger.info( + { logRef: LogRefs.FINISHED_SUPPLIER_MOCK.code }, + LogRefs.FINISHED_SUPPLIER_MOCK.description, + ); }; } @@ -66,11 +74,12 @@ async function callPatchLetter( } catch (error) { deps.logger.error( { + logRef: LogRefs.FAILED_TO_INVOKE_PATCH_LETTER.code, error, functionName: deps.env.PATCH_LETTER_FUNCTION_NAME, letterId: letter.id, }, - "Failed to invoke patch_letter lambda", + LogRefs.FAILED_TO_INVOKE_PATCH_LETTER.description, ); throw error; } @@ -82,12 +91,13 @@ async function callPatchLetter( deps.logger.error( { + logRef: LogRefs.PATCH_LETTER_FUNCTION_ERROR.code, functionName: deps.env.PATCH_LETTER_FUNCTION_NAME, letterId: letter.id, functionError: patchInvokeResponse.FunctionError, payload: patchPayload, }, - "patch_letter lambda returned a function error", + LogRefs.PATCH_LETTER_FUNCTION_ERROR.description, ); throw new Error( @@ -115,6 +125,7 @@ async function callGetLetters( limitValue: string, ): Promise { deps.logger.info({ + logRef: LogRefs.CALLING_GET_LETTERS.code, message: `about to call getLetters with limit ${limitValue}`, }); let invokeResponse; @@ -135,12 +146,13 @@ async function callGetLetters( } catch (error) { deps.logger.error( { + logRef: LogRefs.FAILED_TO_INVOKE_GET_LETTERS.code, error, functionName: deps.env.GET_LETTERS_FUNCTION_NAME, supplierId: headers["nhsd-supplier-id"], limitValue, }, - "Failed to invoke get_letters lambda", + LogRefs.FAILED_TO_INVOKE_GET_LETTERS.description, ); throw error; } @@ -171,10 +183,11 @@ async function parseSupplierMockConfig( deps.logger.info( { + logRef: LogRefs.PARSED_SUPPLIER_MOCK_CONFIG.code, limit, supplierId, }, - "Parsed supplier mock config from Parameter Store", + LogRefs.PARSED_SUPPLIER_MOCK_CONFIG.description, ); return { @@ -187,10 +200,11 @@ async function parseSupplierMockConfig( } catch (error) { deps.logger.error( { + logRef: LogRefs.FAILED_TO_READ_CONFIG.code, error, parameterName: deps.env.SUPPLIER_MOCK_CONFIG_PARAM_NAME, }, - "Failed to read supplier mock config from Parameter Store", + LogRefs.FAILED_TO_READ_CONFIG.description, ); throw error; } From a39cc5811c6466f9696485f6f90bccad8e9f0d92 Mon Sep 17 00:00:00 2001 From: David Wass Date: Fri, 18 Sep 2026 09:11:03 +0100 Subject: [PATCH 10/11] update letter queue --- .../api/module_lambda_update_letter_queue.tf | 2 +- .../update-letter-queue/src/log-references.ts | 56 +++++++++++++++++++ .../src/update-letter-queue.ts | 51 ++++++++++++----- 3 files changed, 95 insertions(+), 14 deletions(-) create mode 100644 lambdas/update-letter-queue/src/log-references.ts diff --git a/infrastructure/terraform/components/api/module_lambda_update_letter_queue.tf b/infrastructure/terraform/components/api/module_lambda_update_letter_queue.tf index f28eb51a2..02b68b9d8 100644 --- a/infrastructure/terraform/components/api/module_lambda_update_letter_queue.tf +++ b/infrastructure/terraform/components/api/module_lambda_update_letter_queue.tf @@ -35,7 +35,7 @@ module "update_letter_queue" { log_subscription_role_arn = local.acct.log_subscription_role_arn lambda_env_vars = merge(local.common_lambda_env_vars, { - LOG_REFERENCE = "UPDATE_LETTER" + LOG_REFERENCE = "UPD_LTR_QUE" }) } diff --git a/lambdas/update-letter-queue/src/log-references.ts b/lambdas/update-letter-queue/src/log-references.ts new file mode 100644 index 000000000..580a17957 --- /dev/null +++ b/lambdas/update-letter-queue/src/log-references.ts @@ -0,0 +1,56 @@ +const LogRefs = { + RECEIVED_EVENT: { + code: "001-RECEIVED-EVENT", + description: "Received event", + }, + NUMBER_OF_RECORDS: { + code: "002-RECORDS", + description: "Number of records", + }, + ERROR_PROCESSING_DDB_RECORD: { + code: "003-DDB-ERROR", + description: "Error processing ddbRecord", + }, + PERSISTING_PENDING_LETTER: { + code: "004-PERSISTING-LETTER", + description: "Persisting pending letter", + }, + LETTER_ALREADY_EXISTS: { + code: "005-LETTER-EXISTS", + description: "Letter already exists", + }, + DELETING_PENDING_LETTER: { + code: "006-DELETING-LETTER", + description: "Deleting pending letter", + }, + LETTER_DOES_NOT_EXIST: { + code: "007-LETTER-NOT-EXIST", + description: "Letter does not exist", + }, + PROCESSING_COMPLETE: { + code: "008-COMPLETE", + description: "Processing complete", + }, + QUEUE_DELTA_METRIC: { + code: "009-METRIC", + description: "Queue delta metric", + }, + PROCESSING_KINESIS_RECORD: { + code: "010-PROCESSING-RECORD", + description: "Processing Kinesis record", + }, + DECODED_PAYLOAD: { + code: "011-DECODED", + description: "Decoded payload", + }, + EXTRACTED_DYNAMODB_RECORD: { + code: "012-DYNAMODB-RECORD", + description: "Extracted dynamoDBRecord", + }, + ERROR_EXTRACTING_PAYLOAD: { + code: "013-ERROR-EXTRACTING-PAYLOAD", + description: "Error extracting payload", + }, +}; + +export default LogRefs; diff --git a/lambdas/update-letter-queue/src/update-letter-queue.ts b/lambdas/update-letter-queue/src/update-letter-queue.ts index cc91a0079..88eda81bd 100644 --- a/lambdas/update-letter-queue/src/update-letter-queue.ts +++ b/lambdas/update-letter-queue/src/update-letter-queue.ts @@ -15,6 +15,7 @@ import { LetterSchema, } from "@internal/datastore"; import { Deps } from "./deps"; +import LogRefs from "./log-references"; export default function createHandler(deps: Deps): Handler { return async (streamEvent: KinesisStreamEvent) => { @@ -23,9 +24,14 @@ export default function createHandler(deps: Deps): Handler { // The change in the size of the pending letters queue, keyed by supplier const deltasBySupplierId = new Map(); - deps.logger.info({ description: "Received event", streamEvent }); deps.logger.info({ - description: "Number of records", + logRef: LogRefs.RECEIVED_EVENT.code, + description: LogRefs.RECEIVED_EVENT.description, + streamEvent, + }); + deps.logger.info({ + logRef: LogRefs.NUMBER_OF_RECORDS.code, + description: LogRefs.NUMBER_OF_RECORDS.description, count: streamEvent.Records?.length || 0, }); @@ -46,7 +52,8 @@ export default function createHandler(deps: Deps): Handler { } } catch (error) { deps.logger.error({ - description: "Error processing ddbRecord", + logRef: LogRefs.ERROR_PROCESSING_DDB_RECORD.code, + description: LogRefs.ERROR_PROCESSING_DDB_RECORD.description, error, ddbRecord, }); @@ -74,7 +81,8 @@ async function addPendingLetterToQueue( try { deps.logger.info({ - description: "Persisting pending letter", + logRef: LogRefs.PERSISTING_PENDING_LETTER.code, + description: LogRefs.PERSISTING_PENDING_LETTER.description, pendingLetter, }); await deps.letterQueueRepository.putLetter(pendingLetter); @@ -82,7 +90,8 @@ async function addPendingLetterToQueue( } catch (error) { if (error instanceof LetterAlreadyExistsError) { deps.logger.warn({ - description: "Letter already exists", + logRef: LogRefs.LETTER_ALREADY_EXISTS.code, + description: LogRefs.LETTER_ALREADY_EXISTS.description, supplierId: pendingLetter.supplierId, letterId: pendingLetter.letterId, }); @@ -98,7 +107,8 @@ async function deletePendingLetterFromQueue( ): Promise { try { deps.logger.info({ - description: "Deleting pending letter", + logRef: LogRefs.DELETING_PENDING_LETTER.code, + description: LogRefs.DELETING_PENDING_LETTER.description, supplierId: letter.supplierId, letterId: letter.id, }); @@ -107,7 +117,8 @@ async function deletePendingLetterFromQueue( } catch (error) { if (error instanceof LetterNotFoundError) { deps.logger.warn({ - description: "Letter does not exist", + logRef: LogRefs.LETTER_DOES_NOT_EXIST.code, + description: LogRefs.LETTER_DOES_NOT_EXIST.description, supplierId: letter.supplierId, letterId: letter.id, }); @@ -124,14 +135,18 @@ function recordProcessing( deltasBySupplierId: Map, ) { deps.logger.info({ - description: "Processing complete", + logRef: LogRefs.PROCESSING_COMPLETE.code, + description: LogRefs.PROCESSING_COMPLETE.description, successCount, failureCount, totalProcessed: successCount + failureCount, }); for (const [supplierId, delta] of deltasBySupplierId) { - deps.logger.info(buildMetric(supplierId, delta)); + deps.logger.info({ + ...buildMetric(supplierId, delta), + logRef: LogRefs.QUEUE_DELTA_METRIC.code, + }); } } @@ -158,20 +173,30 @@ function extractPayload( ): DynamoDBRecord { try { deps.logger.info({ - description: "Processing Kinesis record", + logRef: LogRefs.PROCESSING_KINESIS_RECORD.code, + description: LogRefs.PROCESSING_KINESIS_RECORD.description, recordId: record.kinesis.sequenceNumber, }); // Kinesis data is base64 encoded const payload = Buffer.from(record.kinesis.data, "base64").toString("utf8"); - deps.logger.info({ description: "Decoded payload", payload }); + deps.logger.info({ + logRef: LogRefs.DECODED_PAYLOAD.code, + description: LogRefs.DECODED_PAYLOAD.description, + payload, + }); const jsonParsed = JSON.parse(payload); - deps.logger.info({ description: "Extracted dynamoDBRecord", jsonParsed }); + deps.logger.info({ + logRef: LogRefs.EXTRACTED_DYNAMODB_RECORD.code, + description: LogRefs.EXTRACTED_DYNAMODB_RECORD.description, + jsonParsed, + }); return jsonParsed; } catch (error) { deps.logger.error({ - description: "Error extracting payload", + logRef: LogRefs.ERROR_EXTRACTING_PAYLOAD.code, + description: LogRefs.ERROR_EXTRACTING_PAYLOAD.description, err: error, eventId: record.eventID, }); From 55e07c9be778f80a66df09ca8269cff72d75d95e Mon Sep 17 00:00:00 2001 From: David Wass Date: Fri, 18 Sep 2026 09:15:36 +0100 Subject: [PATCH 11/11] upsert letter --- .../api/module_lambda_upsert_letter.tf | 2 +- .../src/config/log-references.ts | 32 +++++++++++++++++++ .../src/handler/upsert-handler.ts | 24 ++++++++++---- 3 files changed, 50 insertions(+), 8 deletions(-) create mode 100644 lambdas/upsert-letter/src/config/log-references.ts diff --git a/infrastructure/terraform/components/api/module_lambda_upsert_letter.tf b/infrastructure/terraform/components/api/module_lambda_upsert_letter.tf index 2f560e3b0..7c1fd4028 100644 --- a/infrastructure/terraform/components/api/module_lambda_upsert_letter.tf +++ b/infrastructure/terraform/components/api/module_lambda_upsert_letter.tf @@ -36,7 +36,7 @@ module "upsert_letter" { lambda_env_vars = merge(local.common_lambda_env_vars, { IDEMPOTENCY_TABLE_NAME = aws_dynamodb_table.idempotency.name - LOG_REFERENCE = "UPSERT_LETTER" + LOG_REFERENCE = "UPST_LTR" }) } diff --git a/lambdas/upsert-letter/src/config/log-references.ts b/lambdas/upsert-letter/src/config/log-references.ts new file mode 100644 index 000000000..b7088e7fc --- /dev/null +++ b/lambdas/upsert-letter/src/config/log-references.ts @@ -0,0 +1,32 @@ +const LogRefs = { + PROCESSING_RECORD: { + code: "001-PROCESSING", + description: "Processing record", + }, + EXTRACTED_LETTER_EVENT: { + code: "002-EXTRACTED-EVENT", + description: "Extracted letter event", + }, + INSERTED_LETTER: { + code: "003-INSERTED", + description: "Inserted letter", + }, + LETTER_ALREADY_EXISTS: { + code: "004-LETTER-EXISTS", + description: "Letter already exists", + }, + UPDATED_LETTER: { + code: "005-UPDATED", + description: "Updated letter", + }, + ERROR_PROCESSING_UPSERT: { + code: "006-ERROR", + description: "Error processing upsert of record", + }, + INDIVIDUAL_METRIC: { + code: "007-METRIC", + description: "Individual upsert letter metric", + }, +}; + +export default LogRefs; diff --git a/lambdas/upsert-letter/src/handler/upsert-handler.ts b/lambdas/upsert-letter/src/handler/upsert-handler.ts index 6b5199b92..25c42f0b9 100644 --- a/lambdas/upsert-letter/src/handler/upsert-handler.ts +++ b/lambdas/upsert-letter/src/handler/upsert-handler.ts @@ -23,6 +23,7 @@ import { } from "@internal/helpers"; import { Logger } from "pino"; import { Deps } from "../config/deps"; +import LogRefs from "../config/log-references"; import { AllocationDetails, PreparedEvents, @@ -52,7 +53,8 @@ function getOperationFromType(type: string): UpsertOperation { await deps.letterRepo.putLetter(letterToInsert); deps.logger.info({ - description: "Inserted letter", + logRef: LogRefs.INSERTED_LETTER.code, + description: LogRefs.INSERTED_LETTER.description, eventId: preparedRequest.id, letterId: letterToInsert.id, supplierId: letterToInsert.supplierId, @@ -76,7 +78,8 @@ function getOperationFromType(type: string): UpsertOperation { ); if (error instanceof LetterAlreadyExistsError) { deps.logger.warn({ - description: "Letter already exists", + logRef: LogRefs.LETTER_ALREADY_EXISTS.code, + description: LogRefs.LETTER_ALREADY_EXISTS.description, supplierId: letterToInsert.supplierId, letterId: letterToInsert.id, }); @@ -97,7 +100,8 @@ function getOperationFromType(type: string): UpsertOperation { await deps.letterRepo.updateLetterStatus(letterToUpdate); deps.logger.info({ - description: "Updated letter", + logRef: LogRefs.UPDATED_LETTER.code, + description: LogRefs.UPDATED_LETTER.description, eventId: supplierEvent.id, letterId: letterToUpdate.id, supplierId: letterToUpdate.supplierId, @@ -191,7 +195,10 @@ async function emitIndividualMetric( value: 1, unit: Unit.Count, }; - logger.info(buildEMFObject(namespace, dimensions, metric)); + logger.info({ + ...buildEMFObject(namespace, dimensions, metric), + logRef: LogRefs.INDIVIDUAL_METRIC.code, + }); } function getSupplierIdFromEvent(letterEvent: any): string { @@ -226,7 +233,8 @@ export default function createUpsertLetterHandler(deps: Deps): SQSHandler { const tasks = event.Records.map(async (record) => { try { deps.logger.info({ - description: "Processing record", + logRef: LogRefs.PROCESSING_RECORD.code, + description: LogRefs.PROCESSING_RECORD.description, messageId: record.messageId, message: record.body, }); @@ -246,7 +254,8 @@ export default function createUpsertLetterHandler(deps: Deps): SQSHandler { } deps.logger.info({ - description: "Extracted letter event", + logRef: LogRefs.EXTRACTED_LETTER_EVENT.code, + description: LogRefs.EXTRACTED_LETTER_EVENT.description, messageId: record.messageId, type: letterEvent.type, supplier: allocationDetails?.supplierSpec, @@ -256,7 +265,8 @@ export default function createUpsertLetterHandler(deps: Deps): SQSHandler { await processRecordIdempotently(letterEvent, allocationDetails, deps); } catch (error) { deps.logger.error({ - description: "Error processing upsert of record", + logRef: LogRefs.ERROR_PROCESSING_UPSERT.code, + description: LogRefs.ERROR_PROCESSING_UPSERT.description, err: error, messageId: record.messageId, message: record.body,