From 1f8250e3b9ee70c1cfcaf96fea82b25c86516c01 Mon Sep 17 00:00:00 2001 From: karthikeyannhs <174426205+Karthikeyannhs@users.noreply.github.com> Date: Wed, 9 Jul 2025 16:04:22 +0100 Subject: [PATCH 1/4] log request id and correlation id --- .../views/eligibility.py | 5 +++ tests/unit/views/test_eligibility.py | 32 +++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/src/eligibility_signposting_api/views/eligibility.py b/src/eligibility_signposting_api/views/eligibility.py index 17432ce50..df512b08e 100644 --- a/src/eligibility_signposting_api/views/eligibility.py +++ b/src/eligibility_signposting_api/views/eligibility.py @@ -30,6 +30,11 @@ @eligibility_blueprint.before_request def before_request() -> None: + logger.info( + "X-Request-ID: %s, X-Correlation-ID: %s", + request.headers.get("X-Request-ID"), + request.headers.get("X-Correlation-ID"), + ) AuditContext.add_request_details(request) diff --git a/tests/unit/views/test_eligibility.py b/tests/unit/views/test_eligibility.py index ec8d7e2ed..546c37c07 100644 --- a/tests/unit/views/test_eligibility.py +++ b/tests/unit/views/test_eligibility.py @@ -687,3 +687,35 @@ def test_build_response_include_values_that_are_not_null(client: FlaskClient): assert action["description"] == "Contact GP" assert action["urlLink"] == "https://example.dummy/" assert action["urlLabel"] == "GP contact" + + +@pytest.mark.parametrize( + ("headers", "expected_log_msg"), + [ + ( + {"X-Request-ID": "test-request-id-123", "X-Correlation-ID": "test-correlation-id-456"}, + "X-Request-ID: test-request-id-123, X-Correlation-ID: test-correlation-id-456", + ), + ( + {"X-Request-ID": "", "X-Correlation-ID": ""}, + "X-Request-ID: , X-Correlation-ID: ", + ), + ( + {}, # No headers provided + "X-Request-ID: None, X-Correlation-ID: None", + ), + ], +) +def test_request_id_and_correlation_id_logging_variants( + app: Flask, client: FlaskClient, caplog, headers, expected_log_msg +): + with ( + get_app_container(app).override.service(EligibilityService, new=FakeEligibilityService()), + get_app_container(app).override.service(AuditService, new=FakeAuditService()), + ): + with caplog.at_level(logging.INFO): + response = client.get("/patient-check/12345", headers=headers) + + log_messages = [record.getMessage() for record in caplog.records] + assert any(expected_log_msg in message for message in log_messages) + assert response.status_code == HTTPStatus.OK From 2c58a108ed9c4f7e8b69202673629f3c2bbfd939 Mon Sep 17 00:00:00 2001 From: karthikeyannhs <174426205+Karthikeyannhs@users.noreply.github.com> Date: Wed, 9 Jul 2025 17:10:12 +0100 Subject: [PATCH 2/4] reformatted: log request id --- .../views/eligibility.py | 8 ++++-- tests/unit/views/test_eligibility.py | 28 +++++++++++-------- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/src/eligibility_signposting_api/views/eligibility.py b/src/eligibility_signposting_api/views/eligibility.py index df512b08e..628962369 100644 --- a/src/eligibility_signposting_api/views/eligibility.py +++ b/src/eligibility_signposting_api/views/eligibility.py @@ -31,9 +31,11 @@ @eligibility_blueprint.before_request def before_request() -> None: logger.info( - "X-Request-ID: %s, X-Correlation-ID: %s", - request.headers.get("X-Request-ID"), - request.headers.get("X-Correlation-ID"), + "request details", + extra={ + "X-Request-ID": request.headers.get("X-Request-ID"), + "X-Correlation-ID": request.headers.get("X-Correlation-ID"), + }, ) AuditContext.add_request_details(request) diff --git a/tests/unit/views/test_eligibility.py b/tests/unit/views/test_eligibility.py index 546c37c07..79de00793 100644 --- a/tests/unit/views/test_eligibility.py +++ b/tests/unit/views/test_eligibility.py @@ -690,24 +690,21 @@ def test_build_response_include_values_that_are_not_null(client: FlaskClient): @pytest.mark.parametrize( - ("headers", "expected_log_msg"), + ("headers", "expected_request_id"), [ + ({"X-Request-ID": "test-request-id-123"}, "test-request-id-123"), ( - {"X-Request-ID": "test-request-id-123", "X-Correlation-ID": "test-correlation-id-456"}, - "X-Request-ID: test-request-id-123, X-Correlation-ID: test-correlation-id-456", - ), - ( - {"X-Request-ID": "", "X-Correlation-ID": ""}, - "X-Request-ID: , X-Correlation-ID: ", + {"X-Request-ID": ""}, + "", ), ( {}, # No headers provided - "X-Request-ID: None, X-Correlation-ID: None", + None, ), ], ) -def test_request_id_and_correlation_id_logging_variants( - app: Flask, client: FlaskClient, caplog, headers, expected_log_msg +def test_request_id_from_header_logging_variants( + app: Flask, client: FlaskClient, caplog, headers: dict[str, str], expected_request_id: str ): with ( get_app_container(app).override.service(EligibilityService, new=FakeEligibilityService()), @@ -716,6 +713,13 @@ def test_request_id_and_correlation_id_logging_variants( with caplog.at_level(logging.INFO): response = client.get("/patient-check/12345", headers=headers) - log_messages = [record.getMessage() for record in caplog.records] - assert any(expected_log_msg in message for message in log_messages) + request_id_logged = False + for record in caplog.records: + request_id = getattr(record, "X-Request-ID", None) + + if request_id == expected_request_id: + request_id_logged = True + break + + assert request_id_logged assert response.status_code == HTTPStatus.OK From 5320fc00413b019ed74f65fe607ae77a29d2c5b2 Mon Sep 17 00:00:00 2001 From: karthikeyannhs <174426205+Karthikeyannhs@users.noreply.github.com> Date: Wed, 9 Jul 2025 17:10:12 +0100 Subject: [PATCH 3/4] reformatted: log request id --- .../views/eligibility.py | 8 ++++-- tests/unit/views/test_eligibility.py | 28 +++++++++++-------- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/src/eligibility_signposting_api/views/eligibility.py b/src/eligibility_signposting_api/views/eligibility.py index df512b08e..628962369 100644 --- a/src/eligibility_signposting_api/views/eligibility.py +++ b/src/eligibility_signposting_api/views/eligibility.py @@ -31,9 +31,11 @@ @eligibility_blueprint.before_request def before_request() -> None: logger.info( - "X-Request-ID: %s, X-Correlation-ID: %s", - request.headers.get("X-Request-ID"), - request.headers.get("X-Correlation-ID"), + "request details", + extra={ + "X-Request-ID": request.headers.get("X-Request-ID"), + "X-Correlation-ID": request.headers.get("X-Correlation-ID"), + }, ) AuditContext.add_request_details(request) diff --git a/tests/unit/views/test_eligibility.py b/tests/unit/views/test_eligibility.py index 546c37c07..79de00793 100644 --- a/tests/unit/views/test_eligibility.py +++ b/tests/unit/views/test_eligibility.py @@ -690,24 +690,21 @@ def test_build_response_include_values_that_are_not_null(client: FlaskClient): @pytest.mark.parametrize( - ("headers", "expected_log_msg"), + ("headers", "expected_request_id"), [ + ({"X-Request-ID": "test-request-id-123"}, "test-request-id-123"), ( - {"X-Request-ID": "test-request-id-123", "X-Correlation-ID": "test-correlation-id-456"}, - "X-Request-ID: test-request-id-123, X-Correlation-ID: test-correlation-id-456", - ), - ( - {"X-Request-ID": "", "X-Correlation-ID": ""}, - "X-Request-ID: , X-Correlation-ID: ", + {"X-Request-ID": ""}, + "", ), ( {}, # No headers provided - "X-Request-ID: None, X-Correlation-ID: None", + None, ), ], ) -def test_request_id_and_correlation_id_logging_variants( - app: Flask, client: FlaskClient, caplog, headers, expected_log_msg +def test_request_id_from_header_logging_variants( + app: Flask, client: FlaskClient, caplog, headers: dict[str, str], expected_request_id: str ): with ( get_app_container(app).override.service(EligibilityService, new=FakeEligibilityService()), @@ -716,6 +713,13 @@ def test_request_id_and_correlation_id_logging_variants( with caplog.at_level(logging.INFO): response = client.get("/patient-check/12345", headers=headers) - log_messages = [record.getMessage() for record in caplog.records] - assert any(expected_log_msg in message for message in log_messages) + request_id_logged = False + for record in caplog.records: + request_id = getattr(record, "X-Request-ID", None) + + if request_id == expected_request_id: + request_id_logged = True + break + + assert request_id_logged assert response.status_code == HTTPStatus.OK From 58f71beb45d174190b617d5a272130a68bd17f21 Mon Sep 17 00:00:00 2001 From: karthikeyannhs <174426205+Karthikeyannhs@users.noreply.github.com> Date: Thu, 10 Jul 2025 01:37:53 +0100 Subject: [PATCH 4/4] added docstring --- tests/unit/views/test_eligibility.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/unit/views/test_eligibility.py b/tests/unit/views/test_eligibility.py index 79de00793..b8a140d80 100644 --- a/tests/unit/views/test_eligibility.py +++ b/tests/unit/views/test_eligibility.py @@ -706,6 +706,10 @@ def test_build_response_include_values_that_are_not_null(client: FlaskClient): def test_request_id_from_header_logging_variants( app: Flask, client: FlaskClient, caplog, headers: dict[str, str], expected_request_id: str ): + """ + This test checks that the x-request-ID is logged so that it can be used to correlate logs + with that of the logs from api-gateway + """ with ( get_app_container(app).override.service(EligibilityService, new=FakeEligibilityService()), get_app_container(app).override.service(AuditService, new=FakeAuditService()),