121 lines
3.7 KiB
Python
121 lines
3.7 KiB
Python
"""
|
|
logger decorator to enable logging of infos from django's HttpRequest object.
|
|
|
|
Usage:
|
|
|
|
Code:
|
|
|
|
@log.logger(__name__)
|
|
def home(request, logger=None):
|
|
logger.info('user performed some action')
|
|
|
|
Class based views work the same way, just decorate/modify the view's METHOD(s).
|
|
|
|
Logging formatter configuration:
|
|
|
|
'format': '[%(asctime)s] %(levelname)s %(message)s ' \
|
|
'[ip: %(request.META.REMOTE_ADDR)s, ua: "%(request.META.HTTP_USER_AGENT)s"]'
|
|
|
|
Based on code from (but heavily modified/refactored):
|
|
https://derrickpetzold.com/p/django-requst-logging-json/
|
|
which is (c) Derrick Petzold - with a Creative Commons BY-SA license.
|
|
"""
|
|
|
|
import logging
|
|
from collections import defaultdict
|
|
|
|
from django.http.request import HttpRequest
|
|
|
|
|
|
def _get_attrdict(obj, basename, excluded=None):
|
|
"""
|
|
get a dictionary of (basename-prefixed) attribute names/values,
|
|
excluding the excluded names, internal stuff and callables.
|
|
|
|
:param obj: the object to inspect
|
|
:param basename: the prefix for the names in the result dictionary
|
|
:param excluded: excluded attribute names, do not even touch [set or list]
|
|
:return: dict names: values
|
|
"""
|
|
if excluded is None:
|
|
excluded = set()
|
|
d = {}
|
|
names = set(dir(obj)) - set(excluded)
|
|
for name in names:
|
|
if not name.startswith('_'):
|
|
try:
|
|
attr = getattr(obj, name)
|
|
if not callable(attr):
|
|
d[basename + name] = attr
|
|
except AttributeError:
|
|
pass
|
|
return d
|
|
|
|
|
|
def _get_elementdict(dct, basename, excluded=None):
|
|
"""
|
|
get a dictionary of (basename-prefixed) dictionary elements,
|
|
excluding the excluded names.
|
|
|
|
:param dct: the dict to inspect
|
|
:param basename: the prefix for the names in the result dictionary
|
|
:param excluded: excluded dictionary keys [set or list]
|
|
:return: dict names: values
|
|
"""
|
|
if excluded is None:
|
|
excluded = set()
|
|
names = set(dct) - set(excluded)
|
|
return {basename + name: dct[name] for name in names}
|
|
|
|
|
|
def _build_request_info(request):
|
|
"""
|
|
build a dictionary with extra information extracted from request object
|
|
|
|
:param request: django HttpRequest object or None
|
|
:return: dict names: values
|
|
"""
|
|
# we avoid KeyErrors in case we have a logging format string using
|
|
# placeholders that are not available (either because they are not in
|
|
# request or because we have no request)
|
|
d = defaultdict(lambda: None)
|
|
if request:
|
|
d.update(_get_elementdict(request.META, "request.META."))
|
|
d.update(_get_attrdict(request, "request.", ['raw_post_data', ]))
|
|
d.update(_get_attrdict(request.session, "request.session."))
|
|
d.update(_get_attrdict(request.user, "request.user."))
|
|
return d
|
|
|
|
|
|
def get_logger(name, request=None):
|
|
"""
|
|
get a logger providing extra information from request,
|
|
use this if the decorator is not practicable.
|
|
|
|
:param name: name of the logger
|
|
:param request: django's HttpRequest object
|
|
:return: logger instance
|
|
"""
|
|
return logging.LoggerAdapter(logging.getLogger(name), _build_request_info(request))
|
|
|
|
|
|
def logger(name):
|
|
"""
|
|
decorator to provide extra information from request to logging
|
|
|
|
:param name: name of the logger
|
|
:return: decorated function/method
|
|
"""
|
|
def wrap(func):
|
|
def caller(*args, **kwargs):
|
|
request = None
|
|
for arg in args:
|
|
if isinstance(arg, HttpRequest):
|
|
request = arg
|
|
break
|
|
if 'logger' not in kwargs:
|
|
kwargs['logger'] = get_logger(name, request)
|
|
return func(*args, **kwargs)
|
|
return caller
|
|
return wrap
|