Skip to content

Commit 0f8d1aa

Browse files
[CDAPI-118]: Reintroduce Filter to add corr id to every log
1 parent 2723a93 commit 0f8d1aa

File tree

5 files changed

+51
-54
lines changed

5 files changed

+51
-54
lines changed

pathology-api/lambda_handler.py

Lines changed: 17 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -114,35 +114,31 @@ def status() -> Response[str]:
114114
def post_result() -> Response[str]:
115115
correlation_id = app.current_event.headers.get(_CORRELATION_ID_HEADER)
116116

117-
_logger.info("RFL app = %s", app)
118-
119117
if not correlation_id:
120118
raise ValueError(f"Missing required header: {_CORRELATION_ID_HEADER}")
121-
elif not app.current_event.raw_path.__contains__("_status"):
122-
set_correlation_id(correlation_id)
123-
124-
_logger.debug("Post result endpoint called.")
119+
with set_correlation_id(correlation_id):
120+
_logger.debug("Post result endpoint called.")
125121

126-
try:
127-
payload = app.current_event.json_body
128-
except JSONDecodeError as e:
129-
raise ValidationError("Invalid payload provided.") from e
122+
try:
123+
payload = app.current_event.json_body
124+
except JSONDecodeError as e:
125+
raise ValidationError("Invalid payload provided.") from e
130126

131-
_logger.debug("Payload received: %s", payload)
127+
_logger.debug("Payload received: %s", payload)
132128

133-
if payload is None:
134-
raise ValidationError(
135-
"Resources must be provided as a bundle of type 'document'"
136-
)
129+
if payload is None:
130+
raise ValidationError(
131+
"Resources must be provided as a bundle of type 'document'"
132+
)
137133

138-
bundle = Bundle.model_validate(payload, by_alias=True)
134+
bundle = Bundle.model_validate(payload, by_alias=True)
139135

140-
response = handle_request(bundle)
136+
response = handle_request(bundle)
141137

142-
return _with_default_headers(
143-
status_code=200,
144-
body=response,
145-
)
138+
return _with_default_headers(
139+
status_code=200,
140+
body=response,
141+
)
146142

147143

148144
def handler(data: dict[str, Any], context: LambdaContext) -> dict[str, Any]:

pathology-api/src/pathology_api/logging.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,19 @@
1+
import logging
12
from typing import Any, Protocol
23

34
from aws_lambda_powertools import Logger
45

56
from pathology_api.request_context import get_correlation_id
67

78

9+
class _CorrelationIdFilter(logging.Filter):
10+
"""Injects the current correlation ID into every log record."""
11+
12+
def filter(self, record: logging.LogRecord) -> bool:
13+
record.correlation_id = get_correlation_id()
14+
return True
15+
16+
817
class LogProvider(Protocol):
918
"""Protocol defining required contract for a logger."""
1019

@@ -22,5 +31,5 @@ def exception(self, msg: str, *args: Any, **kwargs: Any) -> None: ...
2231
def get_logger(service: str) -> LogProvider:
2332
"""Get a configured logger instance."""
2433
logger = Logger(service=service, level="DEBUG", serialize_stacktrace=True)
25-
logger.set_correlation_id(get_correlation_id())
34+
logger.addFilter(_CorrelationIdFilter())
2635
return logger

pathology-api/src/pathology_api/test_request_context.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,6 @@
22

33

44
class TestSetAndGetCorrelationId:
5-
def test_set_and_get_correlation_id_within_context(self) -> None:
6-
with set_correlation_id("round-trip-test-123"):
7-
assert get_correlation_id() == "round-trip-test-123"
8-
95
def test_correlation_id_is_cleared_after_context_exit(self) -> None:
106
with set_correlation_id("round-trip-test-123"):
117
assert get_correlation_id() == "round-trip-test-123"

pathology-api/test_lambda_handler.py

Lines changed: 12 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,8 @@ def test_create_test_result_success(self) -> None:
6767
response = handler(event, context)
6868

6969
assert response["statusCode"] == 200
70-
assert response["headers"] == {
71-
"Content-Type": "application/fhir+json",
72-
"nhsd-correlation-id": "test-correlation-id",
73-
}
70+
assert response["headers"]["Content-Type"] == "application/fhir+json"
71+
assert response["headers"]["nhsd-correlation-id"] == "test-correlation-id"
7472

7573
response_body = response["body"]
7674
assert isinstance(response_body, str)
@@ -127,10 +125,8 @@ def test_create_test_result_no_payload(self) -> None:
127125
response = handler(event, context)
128126

129127
assert response["statusCode"] == 400
130-
assert response["headers"] == {
131-
"Content-Type": "application/fhir+json",
132-
"nhsd-correlation-id": "test-correlation-id",
133-
}
128+
assert response["headers"]["Content-Type"] == "application/fhir+json"
129+
assert response["headers"]["nhsd-correlation-id"] == "test-correlation-id"
134130

135131
returned_issue = self._parse_returned_issue(response["body"])
136132

@@ -153,10 +149,8 @@ def test_create_test_result_empty_payload(self) -> None:
153149
response = handler(event, context)
154150

155151
assert response["statusCode"] == 400
156-
assert response["headers"] == {
157-
"Content-Type": "application/fhir+json",
158-
"nhsd-correlation-id": "test-correlation-id",
159-
}
152+
assert response["headers"]["Content-Type"] == "application/fhir+json"
153+
assert response["headers"]["nhsd-correlation-id"] == "test-correlation-id"
160154

161155
returned_issue = self._parse_returned_issue(response["body"])
162156

@@ -179,10 +173,8 @@ def test_create_test_result_invalid_json(self) -> None:
179173
response = handler(event, context)
180174

181175
assert response["statusCode"] == 400
182-
assert response["headers"] == {
183-
"Content-Type": "application/fhir+json",
184-
"nhsd-correlation-id": "test-correlation-id",
185-
}
176+
assert response["headers"]["Content-Type"] == "application/fhir+json"
177+
assert response["headers"]["nhsd-correlation-id"] == "test-correlation-id"
186178

187179
returned_issue = self._parse_returned_issue(response["body"])
188180
assert returned_issue["severity"] == "error"
@@ -234,10 +226,8 @@ def test_create_test_result_processing_error(
234226
response = handler(event, context)
235227

236228
assert response["statusCode"] == expected_status_code
237-
assert response["headers"] == {
238-
"Content-Type": "application/fhir+json",
239-
"nhsd-correlation-id": "test-correlation-id",
240-
}
229+
assert response["headers"]["Content-Type"] == "application/fhir+json"
230+
assert response["headers"]["nhsd-correlation-id"] == "test-correlation-id"
241231

242232
returned_issue = self._parse_returned_issue(response["body"])
243233
assert returned_issue == expected_issue
@@ -279,10 +269,8 @@ def test_create_test_result_model_validate_error(
279269
response = handler(event, context)
280270

281271
assert response["statusCode"] == 400
282-
assert response["headers"] == {
283-
"Content-Type": "application/fhir+json",
284-
"nhsd-correlation-id": "test-correlation-id",
285-
}
272+
assert response["headers"]["Content-Type"] == "application/fhir+json"
273+
assert response["headers"]["nhsd-correlation-id"] == "test-correlation-id"
286274

287275
returned_issue = self._parse_returned_issue(response["body"])
288276
assert returned_issue["severity"] == "error"

pathology-api/tests/integration/test_endpoints.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ def test_bundle_returns_200(self, client: Client) -> None:
3636

3737
assert response.status_code == 200
3838
assert response.headers["Content-Type"] == "application/fhir+json"
39+
assert response.headers["X-Correlation-ID"] == "test-correlation-id-555666777"
3940
assert response.headers["nhsd-correlation-id"].startswith(
4041
".test-correlation-id-555666777"
4142
)
@@ -263,19 +264,26 @@ def test_status_returns_200(self, client: Client) -> None:
263264
response = client.send_without_payload(
264265
request_method="GET",
265266
path="_status",
266-
headers={"X-Correlation-ID": "test-correlation-id-555666777"},
267+
headers={"X-Correlation-ID": "test-correlation-id-111222333"},
267268
)
268269
assert response.status_code == 200
269270
assert response.headers["Content-Type"] == "application/json"
270271
assert response.headers["nhsd-correlation-id"].startswith(
271-
".test-correlation-id-555666777"
272+
".test-correlation-id-111222333.rrt-"
273+
)
274+
275+
import json
276+
import logging
277+
278+
logger = logging.getLogger(__name__)
279+
logger.warning(
280+
"/_status response JSON: %s", json.dumps(response.json(), indent=2)
272281
)
273282

274283
parsed = StatusResponse.model_validate(response.json())
275284

276285
assert parsed.status == "pass"
277286
assert parsed.checks.healthcheck.responseCode == 200
278-
assert parsed.checks.healthcheck.outcome == "OK"
279287

280288

281289
class StatusLinks(BaseModel):
@@ -286,7 +294,7 @@ class HealthCheck(BaseModel):
286294
status: Literal["pass", "fail"]
287295
timeout: Literal["true", "false"]
288296
responseCode: int
289-
outcome: str
297+
outcome: dict[Any, Any]
290298
links: StatusLinks
291299

292300

0 commit comments

Comments
 (0)