diff --git a/lambdas/wo/email_processor/handler.py b/lambdas/wo/email_processor/handler.py index 3b7efb8..4da7abd 100644 --- a/lambdas/wo/email_processor/handler.py +++ b/lambdas/wo/email_processor/handler.py @@ -149,13 +149,20 @@ def emit_parse_metric(method, template_id, reason_code, work_order_id): Zero-latency (no PutMetricData API call): the extraction path is async and the role already has logs:PutLogEvents. ParseMethod/TemplateId are the only promoted (dimensioned) fields to keep cardinality low; ReasonCode and - work_order_id ride along as Logs-Insights-queryable properties.""" + work_order_id ride along as Logs-Insights-queryable properties. + + Two dimension sets are published: ["ParseMethod"] (aggregated across all + template ids -- the series the fallback-rate alarm queries) AND + ["ParseMethod", "TemplateId"] (per-template breakdown for Logs Insights / + dashboards). CloudWatch materializes only the exact dimension sets listed + here and does NOT auto-aggregate, so the alarm's single-dimension query + would receive no data unless ["ParseMethod"] is emitted explicitly.""" emf = { "_aws": { "CloudWatchMetrics": [ { "Namespace": METRIC_NAMESPACE, - "Dimensions": [["ParseMethod", "TemplateId"]], + "Dimensions": [["ParseMethod"], ["ParseMethod", "TemplateId"]], "Metrics": [{"Name": METRIC_NAME, "Unit": "Count"}], } ], diff --git a/lambdas/wo/email_processor/tests/test_bedrock_fallback.py b/lambdas/wo/email_processor/tests/test_bedrock_fallback.py index 4fb4f14..864bcdc 100644 --- a/lambdas/wo/email_processor/tests/test_bedrock_fallback.py +++ b/lambdas/wo/email_processor/tests/test_bedrock_fallback.py @@ -165,8 +165,13 @@ def test_emit_parse_metric_writes_emf(capsys): assert emf["TemplateId"] == "update_plaintext" assert emf["ReasonCode"] == "ok" assert emf["ParseOutcome"] == 1 - dims = emf["_aws"]["CloudWatchMetrics"][0]["Dimensions"][0] - assert dims == ["ParseMethod", "TemplateId"] + dims = emf["_aws"]["CloudWatchMetrics"][0]["Dimensions"] + # Two dimension sets must be published: the ParseMethod-only aggregate that + # the fallback-rate alarm queries, AND the per-template breakdown. Without + # the ["ParseMethod"] set the alarm's single-dimension series never receives + # data and can never fire (regression guard for the coverage-collapse alarm). + assert ["ParseMethod"] in dims + assert ["ParseMethod", "TemplateId"] in dims assert ( emf["_aws"]["CloudWatchMetrics"][0]["Namespace"] == "Seahaven/WorkorderIngest" )