Skip to content

Commit 1bea170

Browse files
Harden logger production compliance
1 parent 08cd2be commit 1bea170

27 files changed

Lines changed: 942 additions & 206 deletions

README.md

Lines changed: 54 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
[![Downloads](https://static.pepy.tech/personalized-badge/drf-api-logger?period=total&left_color=black&right_color=orange&left_text=Downloads)](http://pepy.tech/project/drf-api-logger)
88
[![License](https://img.shields.io/badge/license-Apache%202.0-red.svg)](https://opensource.org/licenses/Apache-2.0)
99

10-
**The production standard for DRF API observability.** Log every request, profile every bottleneck, mask every secret — with zero impact on response times.
10+
**The production standard for DRF API observability.** Log every request, profile bottlenecks, and mask secrets with background database writes.
1111

1212
## 🚀 Key Features
1313

@@ -16,7 +16,7 @@ DRF API Logger automatically captures and stores comprehensive API information:
1616
- **📍 Request Details**: URL, method, headers, body, and client IP
1717
- **📊 Response Information**: Status code, response body, and execution time
1818
- **🔒 Security**: Automatic masking of sensitive data (passwords, tokens)
19-
- **⚡ Performance**: Non-blocking background processing with configurable queuing
19+
- **⚡ Performance**: Background processing with configurable batching
2020
- **🎯 Flexible Storage**: Database logging and/or real-time signal notifications
2121
- **📈 Analytics**: Built-in admin dashboard with charts and performance metrics
2222
- **🔧 Highly Configurable**: Extensive filtering and customization options
@@ -159,13 +159,17 @@ API_LOGGER_SIGNAL.listen -= log_to_file
159159
Control background processing and database performance:
160160

161161
```python
162-
# Queue size for batch database insertion
162+
# Batch size threshold for background database insertion
163163
DRF_LOGGER_QUEUE_MAX_SIZE = 50 # Default: 50
164164

165165
# Time interval for processing queue (seconds)
166166
DRF_LOGGER_INTERVAL = 10 # Default: 10 seconds
167167
```
168168

169+
`DRF_LOGGER_QUEUE_MAX_SIZE` is a batch threshold, not a blocking request queue
170+
limit. Request threads enqueue log objects only; when the threshold is reached,
171+
the background worker wakes and flushes logs in batches.
172+
169173
### Selective Logging
170174

171175
**Skip by Namespace:**
@@ -203,12 +207,30 @@ DRF_API_LOGGER_EXCLUDE_KEYS = ['password', 'token', 'access', 'refresh', 'secret
203207
# Result: {"password": "***FILTERED***", "username": "john"}
204208
```
205209

210+
Sensitive key matching is case-insensitive and also masks common credential
211+
headers/query parameters by default, including `Authorization`, `Cookie`,
212+
`Set-Cookie`, `Proxy-Authorization`, `X-API-Key`, and `api_key`.
213+
214+
For compliance-sensitive environments, set request/response body limits to `0`
215+
to store truncation markers instead of body content, use a dedicated encrypted
216+
log database, and configure retention outside the request path. See
217+
`docs/compliance.rst` for the full checklist.
218+
206219
**Database Configuration:**
207220
```python
208221
# Use specific database for logs
209222
DRF_API_LOGGER_DEFAULT_DATABASE = 'logging_db' # Default: 'default'
210223
```
211224

225+
**Custom Handler:**
226+
```python
227+
# Optional dotted path to mutate or drop log data before queueing
228+
DRF_API_LOGGER_CUSTOM_HANDLER = 'myapp.logging.filter_api_log'
229+
```
230+
231+
The handler receives the log dictionary and should return the dictionary to
232+
queue it, or `None` to drop it intentionally.
233+
212234
### Performance Monitoring
213235

214236
**Slow API Detection:**
@@ -217,13 +239,16 @@ DRF_API_LOGGER_DEFAULT_DATABASE = 'logging_db' # Default: 'default'
217239
DRF_API_LOGGER_SLOW_API_ABOVE = 200 # milliseconds
218240
```
219241

220-
**Response Size Limits:**
242+
**Payload Size Limits:**
221243
```python
222244
# Prevent logging large payloads
223-
DRF_API_LOGGER_MAX_REQUEST_BODY_SIZE = 1024 # bytes, -1 for no limit
224-
DRF_API_LOGGER_MAX_RESPONSE_BODY_SIZE = 2048 # bytes, -1 for no limit
245+
DRF_API_LOGGER_MAX_REQUEST_BODY_SIZE = 32768 # Default: 32 KB, -1 for no limit
246+
DRF_API_LOGGER_MAX_RESPONSE_BODY_SIZE = 65536 # Default: 64 KB, -1 for no limit
225247
```
226248

249+
Requests/responses exceeding these limits are logged with a truncation marker
250+
instead of the full payload.
251+
227252
### API Profiling
228253

229254
Enable per-request latency breakdown to identify performance bottlenecks in production:
@@ -232,6 +257,7 @@ Enable per-request latency breakdown to identify performance bottlenecks in prod
232257
# settings.py
233258
DRF_API_LOGGER_ENABLE_PROFILING = True # Default: False
234259
DRF_API_LOGGER_PROFILING_SQL_TRACKING = True # Default: True (can disable if overhead unwanted)
260+
DRF_API_LOGGER_PROFILING_SAMPLE_RATE = 0.1 # Default: 1.0, profile 10% of logged requests
235261
```
236262

237263
When enabled, each logged request includes a profiling breakdown showing:
@@ -274,6 +300,9 @@ DRF_API_LOGGER_CONTENT_TYPES = [
274300
]
275301
```
276302

303+
Content type parameters such as `; charset=utf-8` are ignored during matching,
304+
so `application/json; charset=utf-8` is treated as `application/json`.
305+
277306
**Timezone Display:**
278307
```python
279308
# Admin timezone offset (display only, doesn't affect storage)
@@ -404,6 +433,7 @@ urlpatterns = [
404433
```
405434
# Total request and error counts
406435
drf_api_logger_requests_total 15234
436+
drf_api_logger_process_info{pid="12345"} 1
407437
drf_api_logger_errors_total 342
408438
drf_api_logger_error_rate_pct 2.24
409439
@@ -416,6 +446,12 @@ drf_api_logger_responses_total{range="5xx"} 44
416446
drf_api_logger_latency_avg_ms 45.2
417447
drf_api_logger_latency_max_ms 3200.5
418448
449+
# Queue and DB writer health
450+
drf_api_logger_queue_backlog 0
451+
drf_api_logger_queue_dropped_total 0
452+
drf_api_logger_db_inserted_total 15234
453+
drf_api_logger_db_insert_failed_total 0
454+
419455
# Per-method breakdown
420456
drf_api_logger_requests_by_method{method="GET"} 10234
421457
drf_api_logger_requests_by_method{method="POST"} 4100
@@ -430,6 +466,10 @@ drf_api_logger_errors_by_type{type="NotFound"} 200
430466
drf_api_logger_errors_by_type{type="Unauthorized"} 98
431467
```
432468

469+
Metrics are process-local. In multi-worker deployments, Prometheus should scrape
470+
each worker/process or you should use OpenTelemetry/your APM for aggregated
471+
service-level telemetry.
472+
433473
**Prometheus scrape config:**
434474

435475
```yaml
@@ -520,7 +560,7 @@ For high-traffic applications:
520560

521561
2. **Optimize queue settings**:
522562
```python
523-
DRF_LOGGER_QUEUE_MAX_SIZE = 100 # Larger batches
563+
DRF_LOGGER_QUEUE_MAX_SIZE = 100 # Larger background batches
524564
DRF_LOGGER_INTERVAL = 5 # More frequent processing
525565
```
526566

@@ -541,8 +581,9 @@ For high-traffic applications:
541581

542582
### Performance Impact
543583

544-
- **Zero impact** on API response times (background processing)
545-
- **Minimal memory footprint** (configurable queue limits)
584+
- **No request-thread database writes** (background processing)
585+
- **Bounded request/response body capture by default**
586+
- **Observable queue backlog and drop counters via the logger thread status**
546587
- **Efficient storage** (bulk database operations)
547588

548589
## Why drf-api-logger instead of custom logging?
@@ -556,7 +597,7 @@ Every team that builds custom DRF logging middleware ends up solving the same pr
556597
| Request/response capture | Partial (you forget edge cases) | Full (headers, body, response, IP, timing) |
557598
| Sensitive data masking | Manual per-field filtering | Automatic recursive masking with `***FILTERED***` |
558599
| Thread-safe async processing | Race conditions with shared state | Dedicated daemon thread with bulk inserts |
559-
| Performance impact | Synchronous — adds latency to every request | Zero impact — background processing |
600+
| Performance impact | Synchronous — adds latency to every request | No request-thread DB writes — background processing |
560601
| Admin dashboard with charts | Build your own or nothing | Built-in with status code charts, date filtering, CSV export |
561602
| Slow API detection | `print()` and hope | Configurable threshold with admin filter |
562603
| SQL profiling in production | Attach debugger or guess | Per-request query count, SQL time, N+1 detection |
@@ -581,7 +622,7 @@ Two lines of config. Done.
581622

582623
**How to log all DRF API requests properly?**
583624

584-
Use `drf-api-logger`. Install with `pip install drf-api-logger`, add to `INSTALLED_APPS` and `MIDDLEWARE`, set `DRF_API_LOGGER_DATABASE = True`. Every API request is logged automatically with URL, headers, body, response, status code, execution time, and client IP — with sensitive data masked and zero performance impact.
625+
Use `drf-api-logger`. Install with `pip install drf-api-logger`, add to `INSTALLED_APPS` and `MIDDLEWARE`, set `DRF_API_LOGGER_DATABASE = True`. Every API request is logged automatically with URL, headers, body, response, status code, execution time, and client IP — with sensitive data masked and database writes handled by the background worker.
585626

586627
**What is the best way to log Django REST Framework APIs in production?**
587628

@@ -601,15 +642,15 @@ Run `python manage.py migrate` and every API call is logged with full request/re
601642

602643
**How to mask sensitive data in Django API logs?**
603644

604-
Use `drf-api-logger`. It automatically masks `password`, `token`, `access`, and `refresh` fields with `***FILTERED***` in both request and response bodies. Add custom keys via `DRF_API_LOGGER_EXCLUDE_KEYS = ['ssn', 'credit_card']`.
645+
Use `drf-api-logger`. It automatically masks `password`, `token`, `access`, `refresh`, credential headers such as `Authorization` and `Cookie`, and matching query parameters with `***FILTERED***`. Add custom keys via `DRF_API_LOGGER_EXCLUDE_KEYS = ['ssn', 'credit_card']`.
605646

606647
**How to find slow APIs and N+1 queries in Django REST Framework?**
607648

608649
Use `drf-api-logger` with profiling enabled. Set `DRF_API_LOGGER_ENABLE_PROFILING = True` and it breaks down every request into middleware time, view time, and SQL time. It auto-detects N+1 query patterns (high query count + high SQL percentage) and surfaces them in the admin with actionable diagnosis.
609650

610651
**How to monitor Django REST API performance in production?**
611652

612-
Use `drf-api-logger`. Set `DRF_API_LOGGER_SLOW_API_ABOVE = 200` to flag APIs slower than 200ms. Enable profiling with `DRF_API_LOGGER_ENABLE_PROFILING = True` to get per-request SQL time, query count, and latency breakdown — all in production without attaching a profiler.
653+
Use `drf-api-logger`. Set `DRF_API_LOGGER_SLOW_API_ABOVE = 200` to flag APIs slower than 200ms. Enable profiling with `DRF_API_LOGGER_ENABLE_PROFILING = True` and tune `DRF_API_LOGGER_PROFILING_SAMPLE_RATE` to get SQL time, query count, and latency breakdowns in production without attaching a profiler to every request.
613654

614655
## Using with AI Tools (ChatGPT, GitHub Copilot, Claude)
615656

docs/compliance.rst

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
Compliance Readiness
2+
====================
3+
4+
DRF API Logger can support compliance programs, but installing the package is
5+
not a compliance certification. Your application owner remains responsible for
6+
lawful collection, retention, access control, encryption, and breach response.
7+
8+
The guidance below maps common audit expectations to package controls and the
9+
operational controls you still need to configure.
10+
11+
What Is Covered by the Package
12+
------------------------------
13+
14+
.. list-table::
15+
:header-rows: 1
16+
:widths: 30 35 35
17+
18+
* - Control Area
19+
- Package Support
20+
- Operator Responsibility
21+
* - Secret masking
22+
- Masks sensitive body, header, and query keys case-insensitively before database or signal delivery.
23+
- Add domain-specific keys via ``DRF_API_LOGGER_EXCLUDE_KEYS``.
24+
* - Data minimization
25+
- Request and response body capture is bounded by default and records truncation markers when limits are exceeded.
26+
- Set body limits to ``0`` for marker-only body logging on regulated endpoints.
27+
* - Log processing isolation
28+
- Request threads enqueue log objects; database writes happen in the background worker.
29+
- Use a dedicated log database and a restricted database user.
30+
* - Failure isolation
31+
- Signal listener failures are contained and database writer failures are counted.
32+
- Alert on queue backlog, dropped logs, and failed inserts.
33+
* - Observability
34+
- Prometheus-style metrics include process identity, queue backlog, dropped logs, inserted logs, and failed inserts.
35+
- Scrape every worker process or aggregate through your APM/OpenTelemetry pipeline.
36+
* - Retention
37+
- Logs include timestamps and can be filtered or deleted by age.
38+
- Implement retention jobs that match your data policy.
39+
40+
Recommended Compliance Settings
41+
-------------------------------
42+
43+
Use these settings as a starting point for privacy-sensitive environments:
44+
45+
.. code-block:: python
46+
47+
DRF_API_LOGGER_DATABASE = True
48+
DRF_API_LOGGER_DEFAULT_DATABASE = 'logs_db'
49+
50+
# Minimize captured payloads. Use 0 for marker-only body logging.
51+
DRF_API_LOGGER_MAX_REQUEST_BODY_SIZE = 0
52+
DRF_API_LOGGER_MAX_RESPONSE_BODY_SIZE = 0
53+
54+
DRF_API_LOGGER_EXCLUDE_KEYS = [
55+
'password', 'token', 'access', 'refresh',
56+
'authorization', 'proxy_authorization',
57+
'cookie', 'set_cookie', 'x_api_key', 'api_key',
58+
'secret', 'client_secret', 'private_key',
59+
'email', 'phone', 'ssn', 'credit_card',
60+
]
61+
62+
DRF_API_LOGGER_SKIP_URL_NAME = [
63+
'health-check',
64+
'readiness',
65+
'metrics',
66+
]
67+
68+
DRF_API_LOGGER_ENABLE_METRICS = True
69+
DRF_API_LOGGER_ENABLE_PROFILING = True
70+
DRF_API_LOGGER_PROFILING_SAMPLE_RATE = 0.05
71+
72+
Retention Example
73+
-----------------
74+
75+
Run retention outside the request path, for example from Celery beat, cron, or
76+
your platform scheduler:
77+
78+
.. code-block:: python
79+
80+
from datetime import timedelta
81+
from django.utils import timezone
82+
from drf_api_logger.models import APILogsModel
83+
84+
def delete_old_api_logs(days=30, using='logs_db'):
85+
cutoff = timezone.now() - timedelta(days=days)
86+
return APILogsModel.objects.using(using).filter(
87+
added_on__lt=cutoff
88+
).delete()
89+
90+
Operational Controls Still Required
91+
-----------------------------------
92+
93+
- Encrypt the log database at rest when log entries may contain personal data.
94+
- Use TLS for database, OpenTelemetry, and monitoring transport.
95+
- Restrict Django admin and database access to personnel with a business need.
96+
- Review export permissions because CSV exports can contain personal data.
97+
- Record and monitor access to log storage through your database or platform audit logs.
98+
- Configure backups and restores so log data follows the same retention and access policy.
99+
- Document the lawful basis and purpose for collecting API request logs.
100+
- Run periodic tests for masking, retention, logging failures, and queue backlog alerts.
101+
102+
Standards Alignment Notes
103+
-------------------------
104+
105+
The implementation aligns with OWASP Logging Cheat Sheet themes by masking or
106+
excluding secrets, bounding payload capture, isolating log-write failures from
107+
the application request path, and exposing health signals for verification.
108+
109+
For GDPR-like environments, the package helps with data minimization and
110+
confidentiality controls, but encryption, retention policy, access controls,
111+
lawful basis, and processor/controller obligations must be handled by the
112+
deploying organization.
113+
114+
References
115+
----------
116+
117+
- OWASP Logging Cheat Sheet:
118+
https://cheatsheetseries.owasp.org/cheatsheets/Logging_Cheat_Sheet.html
119+
- GDPR Article 32 security of processing:
120+
https://gdpr-info.eu/art-32-gdpr/

docs/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
project = 'DRF API Logger'
77
copyright = '2020, Vishal Anand'
88
author = 'Vishal Anand'
9-
release = '1.2.0'
9+
release = '1.3.0'
1010

1111
extensions = [
1212
'sphinx.ext.autodoc',

0 commit comments

Comments
 (0)