diff --git a/notifications_utils/logging.py b/notifications_utils/logging.py index 076df9d3..7a6062f2 100644 --- a/notifications_utils/logging.py +++ b/notifications_utils/logging.py @@ -4,7 +4,7 @@ import sys from flask import request, g -from flask.ctx import has_request_context +from flask.ctx import has_request_context, has_app_context from pythonjsonlogger.jsonlogger import JsonFormatter as BaseJSONFormatter from time import monotonic @@ -141,6 +141,7 @@ def configure_handler(handler, app, formatter): handler.setFormatter(formatter) handler.addFilter(AppNameFilter(app.config['NOTIFY_APP_NAME'])) handler.addFilter(RequestIdFilter()) + handler.addFilter(AppContextFilter()) return handler @@ -169,6 +170,18 @@ def filter(self, record): return record +class AppContextFilter(logging.Filter): + + fields_to_add_from_app_context_to_record = ['service_id', 'service_name'] + + def filter(self, record): + for field_name in self.fields_to_add_from_app_context_to_record: + if has_app_context() and field_name in g: + setattr(record, field_name, g.get(field_name)) + + return record + + class CustomLogFormatter(logging.Formatter): """Accepts a format string for the message and formats it with the extra fields""" diff --git a/tests/test_logging.py b/tests/test_logging.py index 00b35f98..25e1690d 100644 --- a/tests/test_logging.py +++ b/tests/test_logging.py @@ -1,7 +1,26 @@ import logging as builtin_logging import uuid +import pytest +from flask import g + from notifications_utils import logging +from notifications_utils.logging import AppContextFilter + + +@pytest.fixture +def log_record(): + return builtin_logging.LogRecord( + name='some-logger', + level=0, + pathname='some-path', + lineno=95, + msg='some-msg', + args={}, + exc_info=None, + func='some-func', + sinfo='some-info' + ) def test_should_build_complete_log_line(): @@ -92,6 +111,26 @@ def test_should_build_statsd_line_without_service_id_or_time_taken(): assert "method.endpoint.200" == logging.build_statsd_line(extra_fields) +class TestAppContextFilter: + + expected_fields_to_be_added_from_app_context = ['service_id', 'service_name'] + + @pytest.mark.parametrize('field_name', expected_fields_to_be_added_from_app_context) + def test_does_not_add_field_if_not_in_app_context(self, app, log_record, field_name): + with app.app_context(): + AppContextFilter().filter(log_record) + + assert not hasattr(log_record, field_name) + + @pytest.mark.parametrize('field_name', expected_fields_to_be_added_from_app_context) + def test_adds_field_if_in_app_context(self, app, log_record, field_name): + with app.app_context(): + setattr(g, field_name, 'some-value') + AppContextFilter().filter(log_record) + + assert getattr(log_record, field_name) == 'some-value' + + def test_get_handlers_sets_up_logging_appropriately_with_debug(tmpdir): class App: config = {