Skip to content

Commit c7a8a06

Browse files
Feature/CCM 14508 Metrics not showing for batches (#398)
* log EMF object for letter-status-update failure * log separate entries for batch post letters * fix lint error * log process.env for lambda runtime environment * stringify the process.env object * correct metrics for the rest of the lambdas * add another dimension to postLetters success * correct dimensions creation * clarify post-mi metrics * record the quantity of the lineItems * correct dimensions in transformers * add metric name for mi-updates-transformer * add metrics to amendment-event-transformer * resolve npm vulnerabilities * move metrics to internal folder * use metrics from internal helpers folder * use metrics from internal helpers folder for amendment-event-transformer * revert minimatch to original version * npm ci * resolve unit test and lint failures --------- Co-authored-by: Mark Slowey <113013138+masl2@users.noreply.github.com> Co-authored-by: Mark Slowey <mark.slowey1@nhs.net>
1 parent e837eda commit c7a8a06

14 files changed

Lines changed: 1368 additions & 1139 deletions

File tree

internal/helpers/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{
22
"dependencies": {
3+
"aws-embedded-metrics": "^4.2.1",
34
"pino": "^10.3.0",
45
"zod": "^4.1.11"
56
},

internal/helpers/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@ export * from "./version";
88
export { default as $Environment } from "./environment";
99
export * from "./id-ref";
1010
export * from "./logger";
11+
export * from "./metrics";

internal/helpers/src/metrics.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { MetricsLogger, Unit } from "aws-embedded-metrics";
2+
3+
export function emitForSingleSupplier(
4+
metrics: MetricsLogger,
5+
functionName: string,
6+
supplierId: string,
7+
count: number,
8+
message: string,
9+
dimensions?: Record<string, string>,
10+
) {
11+
metrics.setNamespace(process.env.AWS_LAMBDA_FUNCTION_NAME || functionName);
12+
metrics.putDimensions({
13+
...dimensions,
14+
Supplier: supplierId,
15+
});
16+
metrics.putMetric(message, count, Unit.Count);
17+
}
18+
19+
export enum MetricStatus {
20+
Success = "success",
21+
Failure = "failure",
22+
}
23+
24+
export interface MetricEntry {
25+
key: string;
26+
value: number;
27+
unit: Unit;
28+
}
29+
30+
// build EMF object
31+
export function buildEMFObject(
32+
functionName: string,
33+
dimensions: Record<string, string>,
34+
metric: MetricEntry,
35+
) {
36+
const namespace = process.env.AWS_LAMBDA_FUNCTION_NAME || functionName;
37+
return {
38+
LogGroup: namespace,
39+
ServiceName: namespace,
40+
...dimensions,
41+
_aws: {
42+
Timestamp: Date.now(),
43+
CloudWatchMetrics: [
44+
{
45+
Namespace: namespace,
46+
Dimensions: [[...Object.keys(dimensions), "ServiceName", "LogGroup"]],
47+
Metrics: [
48+
{ Name: metric.key, Value: metric.value, Unit: metric.unit },
49+
],
50+
},
51+
],
52+
},
53+
[metric.key]: metric.value,
54+
};
55+
}

lambdas/api-handler/src/handlers/amendment-event-transformer.ts

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ import { SQSBatchItemFailure, SQSEvent, SQSHandler } from "aws-lambda";
22
import { PublishCommand } from "@aws-sdk/client-sns";
33
import { LetterEvent } from "@nhsdigital/nhs-notify-event-schemas-supplier-api/src/events/letter-events";
44
import { mapLetterToCloudEvent } from "@nhsdigital/nhs-notify-event-schemas-supplier-api/src/events/letter-mapper";
5+
import { Unit } from "aws-embedded-metrics";
6+
import pino from "pino";
7+
import { MetricEntry, MetricStatus, buildEMFObject } from "@internal/helpers";
58
import {
69
UpdateLetterCommand,
710
UpdateLetterCommandSchema,
@@ -39,6 +42,11 @@ export default function createTransformAmendmentEventHandler(
3942
messageId: message.messageId,
4043
correlationId: message.messageAttributes.CorrelationId.stringValue,
4144
});
45+
emitSuccessMetrics(
46+
updateLetterCommand.supplierId,
47+
updateLetterCommand.status,
48+
deps.logger,
49+
);
4250
} catch (error) {
4351
deps.logger.error({
4452
description: "Error processing letter status update",
@@ -52,7 +60,7 @@ export default function createTransformAmendmentEventHandler(
5260
});
5361

5462
await Promise.all(tasks);
55-
63+
emitFailedItems(batchItemFailures, deps.logger);
5664
return { batchItemFailures };
5765
};
5866
}
@@ -66,3 +74,43 @@ function buildSnsCommand(
6674
Message: JSON.stringify(letterEvent),
6775
});
6876
}
77+
78+
function emitSuccessMetrics(
79+
supplierId: string,
80+
status: string,
81+
logger: pino.Logger,
82+
) {
83+
const dimensions: Record<string, string> = {
84+
supplier: supplierId,
85+
status,
86+
};
87+
const metric: MetricEntry = {
88+
key: MetricStatus.Success,
89+
value: 1,
90+
unit: Unit.Count,
91+
};
92+
const emf = buildEMFObject("amendment-event-transformer", dimensions, metric);
93+
logger.info(emf);
94+
}
95+
96+
function emitFailedItems(
97+
batchFailures: SQSBatchItemFailure[],
98+
logger: pino.Logger,
99+
) {
100+
for (const item of batchFailures) {
101+
const dimensions: Record<string, string> = {
102+
identifier: item.itemIdentifier,
103+
};
104+
const metric: MetricEntry = {
105+
key: MetricStatus.Failure,
106+
value: 1,
107+
unit: Unit.Count,
108+
};
109+
const emf = buildEMFObject(
110+
"amendment-event-transformer",
111+
dimensions,
112+
metric,
113+
);
114+
logger.info(emf);
115+
}
116+
}

lambdas/api-handler/src/handlers/get-letter-data.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import { APIGatewayProxyHandler } from "aws-lambda";
22
import { MetricsLogger, metricScope } from "aws-embedded-metrics";
3+
import { MetricStatus, emitForSingleSupplier } from "@internal/helpers";
34
import { assertNotEmpty } from "../utils/validation";
45
import { extractCommonIds } from "../utils/common-ids";
56
import { ApiErrorDetail } from "../contracts/errors";
67
import { processError } from "../mappers/error-mapper";
78
import ValidationError from "../errors/validation-error";
89
import { getLetterDataUrl } from "../services/letter-operations";
910
import type { Deps } from "../config/deps";
10-
import { MetricStatus, emitForSingleSupplier } from "../utils/metrics";
1111

1212
export default function createGetLetterDataHandler(
1313
deps: Deps,

lambdas/api-handler/src/handlers/get-letter.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { APIGatewayProxyHandler } from "aws-lambda";
22
import { MetricsLogger, metricScope } from "aws-embedded-metrics";
3+
import { MetricStatus, emitForSingleSupplier } from "@internal/helpers";
34
import { assertNotEmpty } from "../utils/validation";
45
import { extractCommonIds } from "../utils/common-ids";
56
import ValidationError from "../errors/validation-error";
@@ -8,7 +9,6 @@ import { getLetterById } from "../services/letter-operations";
89
import { processError } from "../mappers/error-mapper";
910
import { mapToGetLetterResponse } from "../mappers/letter-mapper";
1011
import { Deps } from "../config/deps";
11-
import { MetricStatus, emitForSingleSupplier } from "../utils/metrics";
1212

1313
// Get letter data
1414
export default function createGetLetterHandler(

lambdas/api-handler/src/handlers/get-letters.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
} from "aws-lambda";
55
import { Logger } from "pino";
66
import { MetricsLogger, metricScope } from "aws-embedded-metrics";
7+
import { MetricStatus, emitForSingleSupplier } from "@internal/helpers";
78
import { getLettersForSupplier } from "../services/letter-operations";
89
import { extractCommonIds } from "../utils/common-ids";
910
import { requireEnvVar } from "../utils/validation";
@@ -12,9 +13,7 @@ import { processError } from "../mappers/error-mapper";
1213
import ValidationError from "../errors/validation-error";
1314
import { mapToGetLettersResponse } from "../mappers/letter-mapper";
1415
import type { Deps } from "../config/deps";
15-
import { MetricStatus, emitForSingleSupplier } from "../utils/metrics";
1616

17-
// List letters Handlers
1817
// The endpoint should only return pending letters for now
1918
const status = "PENDING";
2019

lambdas/api-handler/src/handlers/patch-letter.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { APIGatewayProxyHandler } from "aws-lambda";
22
import { MetricsLogger, Unit, metricScope } from "aws-embedded-metrics";
3+
import { MetricStatus } from "@internal/helpers";
34
import { enqueueLetterUpdateRequests } from "../services/letter-operations";
45
import {
56
PatchLetterRequest,
@@ -13,7 +14,6 @@ import { assertNotEmpty } from "../utils/validation";
1314
import { extractCommonIds } from "../utils/common-ids";
1415
import { mapToUpdateCommand } from "../mappers/letter-mapper";
1516
import type { Deps } from "../config/deps";
16-
import { MetricStatus } from "../utils/metrics";
1717

1818
export default function createPatchLetterHandler(
1919
deps: Deps,
@@ -56,6 +56,7 @@ export default function createPatchLetterHandler(
5656
try {
5757
patchLetterRequest = PatchLetterRequestSchema.parse(JSON.parse(body));
5858
} catch (error) {
59+
emitErrorMetric(metrics, supplierId);
5960
const typedError =
6061
error instanceof Error
6162
? new ValidationError(ApiErrorDetail.InvalidRequestBody, {
@@ -79,6 +80,7 @@ export default function createPatchLetterHandler(
7980
);
8081

8182
if (updateLetterCommand.id !== letterId) {
83+
emitErrorMetric(metrics, supplierId);
8284
throw new ValidationError(
8385
ApiErrorDetail.InvalidRequestLetterIdsMismatch,
8486
);
@@ -100,12 +102,16 @@ export default function createPatchLetterHandler(
100102
body: "",
101103
};
102104
} catch (error) {
103-
metrics.putDimensions({
104-
supplier: supplierId,
105-
});
106-
metrics.putMetric(MetricStatus.Success, 1, Unit.Count);
105+
emitErrorMetric(metrics, supplierId);
107106
return processError(error, commonIds.value.correlationId, deps.logger);
108107
}
109108
};
110109
});
111110
}
111+
112+
function emitErrorMetric(metrics: MetricsLogger, supplierId: string) {
113+
metrics.putDimensions({
114+
supplier: supplierId,
115+
});
116+
metrics.putMetric(MetricStatus.Failure, 1, Unit.Count);
117+
}

0 commit comments

Comments
 (0)