Skip to content

Commit

Permalink
PB-511: Refactor django request context
Browse files Browse the repository at this point in the history
Rename files and variables for better readability and understanding.
  • Loading branch information
benschs committed Aug 27, 2024
1 parent f5c44cb commit 5e7a358
Show file tree
Hide file tree
Showing 6 changed files with 52 additions and 27 deletions.
21 changes: 15 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ All features can be fully configured from the configuration file.
- [Django Request Config Example](#django-request-config-example)
- [Django request log records](#django-request-log-records)
- [Usage \& Configuration](#usage--configuration)
- [Configure the filter `DjangoAppendRequestFilter`](#configure-the-filter-djangoappendrequestfilter)
- [Filter out LogRecord attributes based on their types](#filter-out-logrecord-attributes-based-on-their-types)
- [Attribute Type Filter Constructor](#attribute-type-filter-constructor)
- [Attribute Type Filter Config Example](#attribute-type-filter-config-example)
Expand Down Expand Up @@ -512,19 +511,29 @@ filters:

## Django request log records

To add context information from the current request to log records. For this the filter `DjangoAppendRequestFilter` must be added as well as the middleware `AddRequestToLogMiddleware`.
To add context information from the current request to each log record. For this the filter `DjangoAppendRequestFilter` must be added as well as the middleware `AddRequestToLogMiddleware`.

### Usage & Configuration

Add `logging_utilities.request_middleware.AddRequestToLogMiddleware` to the `settings.MIDDLEWARE` list.
Add `logging_utilities.django_middlewares.request_middleware.AddRequestToLogMiddleware` to the django `settings.MIDDLEWARE` list

#### Configure the filter `DjangoAppendRequestFilter`
For example:

```python
MIDDLEWARE = (
...,
'logging_utilities.django_middlewares.request_middleware.AddRequestToLogMiddleware',
...,
)
```

Configure the logging filter `DjangoAppendRequestFilter`:

```yaml
filters:
django_request_meta:
(): logging_utilities.filters.django_append_request.DjangoAppendRequestFilter
request_attributes:
attributes:
- path
- method
- META.QUERY_STRING
Expand All @@ -533,7 +542,7 @@ filters:

| Parameter | Type | Default | Description |
|--------------|------|---------|------------------------------------------------|
| `attributes` | list | None | All request attributes that match any of the dotted keys of the list will be added to the jsonifiable object. When `None` then no attributes are added (default behavior). |
| `attributes` | list | None | All request attributes that match any of the dotted keys of this list will be added to the log record. When `None` then no attributes are added (default behavior). |
| `always_add` | bool | False | By default empty attributes are omitted. If set, empty attributes will be added with value `-` |


Expand Down
Empty file.
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ class AddRequestToLogMiddleware():
"""Middleware that adds a logging filter *DjangoAppendRequestFilter* to the request.
"""

def __init__(self, get_response: Callable[[WSGIRequest], Any], root: str = ""):
self.root = root
def __init__(self, get_response: Callable[[WSGIRequest], Any], root_logger: str = ""):
self.root_logger = root_logger
self.get_response = get_response

def __call__(self, request: WSGIRequest) -> Any:
Expand All @@ -50,13 +50,13 @@ def _find_loggers(self) -> dict[str, logging.Logger]:
"""Return loggers part of root.
"""
result: dict[str, logging.Logger] = {}
prefix = self.root + "."
prefix = self.root_logger + "."
for name, log in logging.Logger.manager.loggerDict.items():
if not isinstance(log, logging.Logger) or not name.startswith(prefix):
continue # not under self.root
continue # not under self.root_logger
result[name] = log
# also add root logger
result[self.root] = logging.getLogger(self.root)
result[self.root_logger] = logging.getLogger(self.root_logger)
return result

def _find_handlers(self) -> list[logging.Handler]:
Expand All @@ -73,13 +73,13 @@ def _find_handlers_with_filter(self, filter_cls: type) -> dict[logging.Handler,
Only include handlers that have at least one filter of type *filter_cls*.
"""
result = {}
for logger in self._find_handlers():
for handler in self._find_handlers():
attrs = []
for f in logger.filters:
for f in handler.filters:
if isinstance(f, filter_cls):
attrs.extend(f.attributes)
if attrs:
result[logger] = attrs
result[handler] = attrs
return result

def _add_filter(self, f: DjangoAppendRequestFilter) -> None:
Expand Down
14 changes: 6 additions & 8 deletions logging_utilities/filters/django_append_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from django.core.handlers.wsgi import WSGIRequest


def r_getattr(obj, attr, *args):
def request_getattr(obj, attr, *args):

def _getattr(obj, attr):
if isinstance(obj, dict):
Expand All @@ -21,28 +21,26 @@ class DjangoAppendRequestFilter():
This filter adds Django request context attributes to the log record.
"""

def __init__(
self, request: Optional[WSGIRequest] = None, request_attributes=None, always_add=False
):
def __init__(self, request: Optional[WSGIRequest] = None, attributes=None, always_add=False):
"""Initialize the filter
Args:
request_attributes: (WSGIRequest | None)
request: (WSGIRequest | None)
Request from which to read the attributes from.
attributes: (list | None)
Request attributes that should be added to log entries
Request attributes that should be added to log entries.
always_add: bool
Always add attributes even if they are missing. Missing attributes with have the
value "-".
"""
self.request = request
self.attributes = request_attributes if request_attributes else list()
self.attributes = attributes if attributes else list()
self.always_add = always_add

def filter(self, record: LogRecord) -> bool:
request = self.request
for attr in self.attributes:
val = r_getattr(request, attr, "-")
val = request_getattr(request, attr, "-")
if self.always_add or val != "-":
setattr(record, "request." + attr, val)

Expand Down
21 changes: 19 additions & 2 deletions tests/test_django_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def test_django_request_log(self):
self._configure_django_filter(
test_logger,
DjangoAppendRequestFilter(
request, request_attributes=["path", "method", "META.QUERY_STRING"]
request, attributes=["path", "method", "META.QUERY_STRING"]
)
)

Expand Down Expand Up @@ -73,7 +73,7 @@ def test_django_request_log_always_add(self):
self._configure_django_filter(
test_logger,
DjangoAppendRequestFilter(
request, request_attributes=["does", "not", "exist"], always_add=True
request, attributes=["does", "not", "exist"], always_add=True
)
)

Expand All @@ -94,3 +94,20 @@ def test_django_request_log_always_add(self):
("message", "second message"), ("request.does", "-"), ("request.not", "-"),
("request.exist", "-")])
)

def test_django_request_log_no_request(self):
with self.assertLogs('test_formatter', level=logging.DEBUG) as ctx:
test_logger = logging.getLogger("test_formatter")
self._configure_django_filter(
test_logger,
DjangoAppendRequestFilter(request=None, attributes=["path"], always_add=True)
)

test_logger.debug("first message")

message1 = json.loads(ctx.output[0], object_pairs_hook=dictionary)
self.assertDictEqual(
message1,
dictionary([("levelname", "DEBUG"), ("name", "test_formatter"),
("message", "first message"), ("request.path", "-")])
)
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@
from django.conf import settings
from django.test import RequestFactory

from logging_utilities.django_middlewares.request_middleware import \
AddRequestToLogMiddleware
from logging_utilities.filters.django_append_request import \
DjangoAppendRequestFilter
from logging_utilities.formatters.json_formatter import JsonFormatter
from logging_utilities.request_middleware import AddRequestToLogMiddleware

# From python3.7, dict is ordered
if sys.version_info.major >= 3 and sys.version_info.minor >= 7:
Expand All @@ -34,7 +35,7 @@ def _configure_django_filter(cls, _logger):
for handler in _logger.handlers:
handler.setFormatter(JsonFormatter(add_always_extra=True))
django_filter = DjangoAppendRequestFilter(
request_attributes=["method", "path", "META.QUERY_STRING", "headers"]
attributes=["method", "path", "META.QUERY_STRING", "headers"]
)
handler.addFilter(django_filter)

Expand All @@ -48,7 +49,7 @@ def test_handler(request):
self._configure_django_filter(logger)
my_header = {"HTTP_CUSTOM_KEY": "VALUE"}
request = self.factory.get("/some_path?test=some_value", **my_header)
middleware = AddRequestToLogMiddleware(test_handler, root="tests")
middleware = AddRequestToLogMiddleware(test_handler, root_logger="tests")
middleware(request)

message1 = json.loads(ctx.output[0], object_pairs_hook=dictionary)
Expand Down

0 comments on commit 5e7a358

Please sign in to comment.