Decorators¶
drf-commons provides a suite of function/method decorators for logging, performance monitoring, query analysis, and cache debugging.
from drf_commons.decorators import (
api_request_logger,
log_function_call,
log_exceptions,
log_db_query,
api_performance_monitor,
cache_debug,
)
api_request_logger¶
Logs incoming API requests and outgoing responses with configurable field redaction.
@api_request_logger(
log_body: bool = True,
log_headers: bool = False,
redact_fields: list = None, # Additional fields to redact
max_body_length: int = 1000, # Truncate body beyond this length
)
Default redacted fields (always sanitized regardless of configuration):
Authorization headers
password,token,api_key,secret,access_token
Usage:
from drf_commons.decorators import api_request_logger
from rest_framework.views import APIView
class PaymentView(APIView):
@api_request_logger(log_body=True, log_headers=False, redact_fields=["card_number"])
def post(self, request):
...
Log output example:
{
"event": "api_request",
"method": "POST",
"path": "/api/payments/",
"user": "john@example.com",
"body": {"amount": "99.99", "card_number": "[REDACTED]"},
"duration_ms": 145.3,
"status_code": 201
}
log_function_call¶
Logs function invocations with timing information and optional argument/result capture.
@log_function_call(
logger_name: str = "drf_commons",
log_args: bool = False,
log_result: bool = False,
category: str = None,
)
Usage:
from drf_commons.decorators import log_function_call
@log_function_call(
logger_name="billing",
log_args=True,
log_result=True,
category="BILLING",
)
def calculate_invoice_total(invoice_id: int, apply_discount: bool = False):
...
Log output (on call):
{
"event": "function_call",
"function": "calculate_invoice_total",
"args": {"invoice_id": 42, "apply_discount": false},
"duration_ms": 23.1,
"result": {"total": "149.99", "currency": "USD"}
}
log_exceptions¶
Logs exceptions raised within a function. Optionally suppresses re-raising.
@log_exceptions(
logger_name: str = "drf_commons",
reraise: bool = True,
)
Usage:
from drf_commons.decorators import log_exceptions
@log_exceptions(logger_name="payments", reraise=True)
def charge_card(amount, card_token):
# Exceptions logged with full context and re-raised
...
@log_exceptions(logger_name="notifications", reraise=False)
def send_welcome_email(user_id):
# Exceptions logged but suppressed; function returns None on failure
...
log_db_query¶
Monitors database queries executed during function execution. Logs query count, individual queries, and execution time.
@log_db_query(query_type: str = "unknown")
Usage:
from drf_commons.decorators import log_db_query
@log_db_query(query_type="read")
def get_dashboard_data(user_id: int):
articles = Article.objects.filter(created_by_id=user_id).count()
comments = Comment.objects.filter(author_id=user_id).count()
return {"articles": articles, "comments": comments}
Log output:
{
"event": "db_query",
"function": "get_dashboard_data",
"query_type": "read",
"query_count": 2,
"duration_ms": 12.4,
"queries": [
{"sql": "SELECT COUNT(*) FROM articles WHERE ...", "duration_ms": 6.1},
{"sql": "SELECT COUNT(*) FROM comments WHERE ...", "duration_ms": 5.2}
]
}
api_performance_monitor¶
Monitors API endpoint performance. Tracks request/response timing and logs a warning when execution exceeds the configured threshold.
@api_performance_monitor()
Usage:
from drf_commons.decorators import api_performance_monitor
class HeavyReportView(APIView):
@api_performance_monitor()
def get(self, request):
...
The threshold is read from COMMON["DEBUG_SLOW_REQUEST_THRESHOLD"] (default: 1.0 seconds).
cache_debug¶
Logs cache operations (get, set, delete) with timing information.
@cache_debug(cache_key_func: callable = None)
Usage:
from drf_commons.decorators import cache_debug
from django.core.cache import cache
@cache_debug(cache_key_func=lambda args, kwargs: f"user:{args[0]}")
def get_user_permissions(user_id: int):
key = f"user:{user_id}:permissions"
result = cache.get(key)
if result is None:
result = compute_permissions(user_id)
cache.set(key, result, timeout=300)
return result
Combining Decorators¶
Decorators compose naturally. Standard Python decorator stacking applies:
@api_request_logger(log_body=False)
@api_performance_monitor()
@log_db_query(query_type="read")
@log_exceptions(reraise=True)
def complex_view(request):
...
Execution order (outermost first): api_request_logger → api_performance_monitor
→ log_db_query → log_exceptions → function body.