diff --git a/README.rst b/README.rst index 39c10fd0..461078c0 100644 --- a/README.rst +++ b/README.rst @@ -362,6 +362,30 @@ Json file (\ ``logs/json.log``\ ) Upgrade Guide ============= +.. _upgrade_8.0: + +Upgrading to 8.0+ +^^^^^^^^^^^^^^^^^ + +The optional :class:`django_structlog.signals.bind_extra_request_metadata` signal has now a new keyword argument ``log_kwargs``. + +It should not affect you if you have a ``**kwargs`` in the signature of your receiver. + +``log_kwargs`` is a dictionary containing the log metadata that will be added to the log ``"request_started"``. + +If you use ``bind_extra_request_metadata`` signal, you will need to update your receiver to accept this new argument. + +.. code-block:: python + + from django.contrib.sites.shortcuts import get_current_site + from django.dispatch import receiver + from django_structlog import signals + import structlog + + @receiver(signals.bind_extra_request_metadata) + def my_receiver(request, logger, log_kwargs, **kwargs): # <- add `log_kwargs` if needed + ... + .. _upgrade_7.0: Upgrading to 7.0+ diff --git a/django_structlog/__init__.py b/django_structlog/__init__.py index ae28e1ef..1a0911dc 100644 --- a/django_structlog/__init__.py +++ b/django_structlog/__init__.py @@ -3,6 +3,6 @@ name = "django_structlog" -VERSION = (7, 1, 0) +VERSION = (8, 0, 0) __version__ = ".".join(str(v) for v in VERSION) diff --git a/django_structlog/celery/signals.py b/django_structlog/celery/signals.py index e647924f..efa1b453 100644 --- a/django_structlog/celery/signals.py +++ b/django_structlog/celery/signals.py @@ -12,7 +12,7 @@ >>> import structlog >>> >>> @receiver(signals.bind_extra_task_metadata) -... def receiver_bind_extra_request_metadata(sender, signal, task=None, logger=None, **kwargs): +... def receiver_bind_extra_task_metadata(sender, signal, task=None, logger=None, **kwargs): ... structlog.contextvars.bind_contextvars(correlation_id=task.request.correlation_id) """ diff --git a/django_structlog/middlewares/request.py b/django_structlog/middlewares/request.py index f9007cc4..32c40d8d 100644 --- a/django_structlog/middlewares/request.py +++ b/django_structlog/middlewares/request.py @@ -146,14 +146,14 @@ def prepare(self, request): structlog.contextvars.bind_contextvars(correlation_id=correlation_id) ip, _ = get_client_ip(request) structlog.contextvars.bind_contextvars(ip=ip) + log_kwargs = { + "request": self.format_request(request), + "user_agent": request.META.get("HTTP_USER_AGENT"), + } signals.bind_extra_request_metadata.send( - sender=self.__class__, request=request, logger=logger - ) - logger.info( - "request_started", - request=self.format_request(request), - user_agent=request.META.get("HTTP_USER_AGENT"), + sender=self.__class__, request=request, logger=logger, log_kwargs=log_kwargs ) + logger.info("request_started", **log_kwargs) @staticmethod def format_request(request): diff --git a/django_structlog/signals.py b/django_structlog/signals.py index 551ca8c2..a94adf1e 100644 --- a/django_structlog/signals.py +++ b/django_structlog/signals.py @@ -4,7 +4,9 @@ bind_extra_request_metadata = django.dispatch.Signal() """ Signal to add extra ``structlog`` bindings from ``django``'s request. -:param logger: the logger to bind more metadata or override existing bound metadata +:param request: the request returned by the view +:param logger: the logger +:param log_kwargs: dictionary of log metadata for the ``request_started`` event. It contains ``request`` and ``user_agent`` keys. You may modify it to add extra information. >>> from django.contrib.sites.shortcuts import get_current_site >>> from django.dispatch import receiver @@ -12,7 +14,7 @@ >>> import structlog >>> >>> @receiver(signals.bind_extra_request_metadata) -... def bind_domain(request, logger, **kwargs): +... def bind_domain(request, logger, log_kwargs, **kwargs): ... current_site = get_current_site(request) ... structlog.contextvars.bind_contextvars(domain=current_site.domain) @@ -21,7 +23,7 @@ bind_extra_request_finished_metadata = django.dispatch.Signal() """ Signal to add extra ``structlog`` bindings from ``django``'s finished request and response. -:param logger: the logger to bind more metadata or override existing bound metadata +:param logger: the logger :param response: the response resulting of the request >>> from django.contrib.sites.shortcuts import get_current_site @@ -39,7 +41,7 @@ bind_extra_request_failed_metadata = django.dispatch.Signal() """ Signal to add extra ``structlog`` bindings from ``django``'s failed request and exception. -:param logger: the logger to bind more metadata or override existing bound metadata +:param logger: the logger :param exception: the exception resulting of the request >>> from django.contrib.sites.shortcuts import get_current_site diff --git a/docs/changelog.rst b/docs/changelog.rst index f4909a01..112ad7e1 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,6 +1,15 @@ Change Log ========== +8.0.0 (March 13, 2024) +---------------------- + +See: :ref:`upgrade_8.0` + +*New:* + - add ``log_kwargs`` to :class:`django_structlog.signals.bind_extra_request_metadata`. See `#484 `_. Special thanks to `@shtoltz `_. + + 7.1.0 (December 20, 2023) ------------------------- diff --git a/test_app/tests/middlewares/test_request.py b/test_app/tests/middlewares/test_request.py index 515e946f..894f48ce 100644 --- a/test_app/tests/middlewares/test_request.py +++ b/test_app/tests/middlewares/test_request.py @@ -318,9 +318,15 @@ def get_response(_response): def test_signal_bind_extra_request_metadata(self): @receiver(bind_extra_request_metadata) def receiver_bind_extra_request_metadata( - sender, signal, request=None, logger=None + sender, + signal, + request=None, + logger=None, + log_kwargs=None, + **kwargs, ): current_site = get_current_site(request) + log_kwargs["request_started_log"] = "foo" structlog.contextvars.bind_contextvars(domain=current_site.domain) mock_response = Mock() @@ -338,7 +344,11 @@ def get_response(_response): request.user = mock_user middleware = middlewares.RequestMiddleware(get_response) - middleware(request) + + with self.assertLogs( + "django_structlog.middlewares.request", logging.INFO + ) as django_structlog_results: + middleware(request) self.assertEqual(1, len(self.log_results.records)) record = self.log_results.records[0] @@ -349,6 +359,15 @@ def get_response(_response): self.assertIn("user_id", record.msg) self.assertEqual(mock_user.id, record.msg["user_id"]) + self.assertEqual(2, len(django_structlog_results.records)) + record = django_structlog_results.records[0] + self.assertEqual("request_started", record.msg["event"]) + self.assertEqual("foo", record.msg["request_started_log"]) + + record = django_structlog_results.records[1] + self.assertEqual("request_finished", record.msg["event"]) + self.assertNotIn("request_started_log", record.msg) + def test_signal_bind_extra_request_finished_metadata(self): mock_response = Mock() mock_response.status_code = 200