django-activity-audit 1.3.0.dev9__tar.gz → 1.3.0.dev11__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {django_activity_audit-1.3.0.dev9 → django_activity_audit-1.3.0.dev11}/PKG-INFO +1 -1
- {django_activity_audit-1.3.0.dev9 → django_activity_audit-1.3.0.dev11}/activity_audit/__init__.py +8 -0
- {django_activity_audit-1.3.0.dev9 → django_activity_audit-1.3.0.dev11}/activity_audit/formatters.py +5 -0
- {django_activity_audit-1.3.0.dev9 → django_activity_audit-1.3.0.dev11}/activity_audit/middleware.py +18 -0
- {django_activity_audit-1.3.0.dev9 → django_activity_audit-1.3.0.dev11}/activity_audit/signals.py +2 -1
- {django_activity_audit-1.3.0.dev9 → django_activity_audit-1.3.0.dev11/docs}/django-activity-audit.md +83 -2
- {django_activity_audit-1.3.0.dev9 → django_activity_audit-1.3.0.dev11}/pyproject.toml +1 -1
- {django_activity_audit-1.3.0.dev9 → django_activity_audit-1.3.0.dev11}/.gitignore +0 -0
- {django_activity_audit-1.3.0.dev9 → django_activity_audit-1.3.0.dev11}/.pre-commit-config.yaml +0 -0
- {django_activity_audit-1.3.0.dev9 → django_activity_audit-1.3.0.dev11}/LICENSE +0 -0
- {django_activity_audit-1.3.0.dev9 → django_activity_audit-1.3.0.dev11}/MANIFEST.in +0 -0
- {django_activity_audit-1.3.0.dev9 → django_activity_audit-1.3.0.dev11}/README.md +0 -0
- {django_activity_audit-1.3.0.dev9 → django_activity_audit-1.3.0.dev11}/README.rst +0 -0
- {django_activity_audit-1.3.0.dev9 → django_activity_audit-1.3.0.dev11}/activity_audit/apps.py +0 -0
- {django_activity_audit-1.3.0.dev9 → django_activity_audit-1.3.0.dev11}/activity_audit/constants.py +0 -0
- {django_activity_audit-1.3.0.dev9 → django_activity_audit-1.3.0.dev11}/activity_audit/handlers.py +0 -0
- {django_activity_audit-1.3.0.dev9 → django_activity_audit-1.3.0.dev11}/activity_audit/logger_levels.py +0 -0
- {django_activity_audit-1.3.0.dev9 → django_activity_audit-1.3.0.dev11}/activity_audit/protocols.py +0 -0
- {django_activity_audit-1.3.0.dev9 → django_activity_audit-1.3.0.dev11}/activity_audit/settings.py +0 -0
- {django_activity_audit-1.3.0.dev9 → django_activity_audit-1.3.0.dev11}/activity_audit/utils.py +0 -0
- {django_activity_audit-1.3.0.dev9 → django_activity_audit-1.3.0.dev11/docs}/user-activity-feed-plan.md +0 -0
- {django_activity_audit-1.3.0.dev9 → django_activity_audit-1.3.0.dev11}/hatch +0 -0
- {django_activity_audit-1.3.0.dev9 → django_activity_audit-1.3.0.dev11}/pytest.ini +0 -0
- {django_activity_audit-1.3.0.dev9 → django_activity_audit-1.3.0.dev11}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: django-activity-audit
|
|
3
|
-
Version: 1.3.0.
|
|
3
|
+
Version: 1.3.0.dev11
|
|
4
4
|
Summary: A Django package for easy CRUD operation logging and container logs
|
|
5
5
|
Project-URL: Homepage, https://github.com/shree256/django-activity-audit
|
|
6
6
|
Project-URL: Repository, https://github.com/shree256/django-activity-audit
|
{django_activity_audit-1.3.0.dev9 → django_activity_audit-1.3.0.dev11}/activity_audit/__init__.py
RENAMED
|
@@ -2,6 +2,10 @@ default_app_config = "activity_audit.apps.AuditLoggingConfig"
|
|
|
2
2
|
|
|
3
3
|
from activity_audit.utils import (
|
|
4
4
|
get_api_handler,
|
|
5
|
+
get_async_api_handler,
|
|
6
|
+
get_async_audit_handler,
|
|
7
|
+
get_async_json_handler,
|
|
8
|
+
get_async_login_handler,
|
|
5
9
|
get_audit_handler,
|
|
6
10
|
get_console_formatter,
|
|
7
11
|
get_json_formatter,
|
|
@@ -18,4 +22,8 @@ __all__ = [
|
|
|
18
22
|
"get_api_handler",
|
|
19
23
|
"get_audit_handler",
|
|
20
24
|
"get_login_handler",
|
|
25
|
+
"get_async_json_handler",
|
|
26
|
+
"get_async_api_handler",
|
|
27
|
+
"get_async_audit_handler",
|
|
28
|
+
"get_async_login_handler",
|
|
21
29
|
]
|
{django_activity_audit-1.3.0.dev9 → django_activity_audit-1.3.0.dev11}/activity_audit/formatters.py
RENAMED
|
@@ -4,6 +4,8 @@ import json
|
|
|
4
4
|
import logging
|
|
5
5
|
import uuid
|
|
6
6
|
|
|
7
|
+
from activity_audit.middleware import get_request_id
|
|
8
|
+
|
|
7
9
|
|
|
8
10
|
def _json_default(obj):
|
|
9
11
|
"""
|
|
@@ -47,6 +49,7 @@ class JsonFormatter(logging.Formatter):
|
|
|
47
49
|
"path": record.pathname,
|
|
48
50
|
"module": record.module,
|
|
49
51
|
"function": record.funcName,
|
|
52
|
+
"request_id": get_request_id() or "",
|
|
50
53
|
"message": record.getMessage(),
|
|
51
54
|
"exception": "",
|
|
52
55
|
# "extra": {},
|
|
@@ -86,6 +89,7 @@ class APIFormatter(logging.Formatter):
|
|
|
86
89
|
"service_name",
|
|
87
90
|
"request_type",
|
|
88
91
|
"protocol",
|
|
92
|
+
"request_id",
|
|
89
93
|
"user_id",
|
|
90
94
|
"user_info",
|
|
91
95
|
"request_repr",
|
|
@@ -117,6 +121,7 @@ class AuditFormatter(logging.Formatter):
|
|
|
117
121
|
audit_fields = [
|
|
118
122
|
"model",
|
|
119
123
|
"event_type",
|
|
124
|
+
"request_id",
|
|
120
125
|
"instance_id",
|
|
121
126
|
"instance_repr",
|
|
122
127
|
"user_id",
|
{django_activity_audit-1.3.0.dev9 → django_activity_audit-1.3.0.dev11}/activity_audit/middleware.py
RENAMED
|
@@ -3,6 +3,7 @@ import json
|
|
|
3
3
|
import logging
|
|
4
4
|
import re
|
|
5
5
|
import time
|
|
6
|
+
import uuid
|
|
6
7
|
|
|
7
8
|
from asgiref.local import Local
|
|
8
9
|
from asgiref.sync import iscoroutinefunction, markcoroutinefunction, sync_to_async
|
|
@@ -32,6 +33,14 @@ def set_current_request(request):
|
|
|
32
33
|
_thread_locals.request = request
|
|
33
34
|
|
|
34
35
|
|
|
36
|
+
def get_request_id():
|
|
37
|
+
return getattr(_thread_locals, "request_id", None)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def set_request_id(request_id):
|
|
41
|
+
_thread_locals.request_id = request_id
|
|
42
|
+
|
|
43
|
+
|
|
35
44
|
def get_current_user():
|
|
36
45
|
request = get_current_request()
|
|
37
46
|
if request:
|
|
@@ -68,6 +77,8 @@ def get_user_details():
|
|
|
68
77
|
def clear_request():
|
|
69
78
|
with contextlib.suppress(AttributeError):
|
|
70
79
|
del _thread_locals.request
|
|
80
|
+
with contextlib.suppress(AttributeError):
|
|
81
|
+
del _thread_locals.request_id
|
|
71
82
|
|
|
72
83
|
|
|
73
84
|
def should_log_url(url):
|
|
@@ -119,6 +130,7 @@ class AuditLoggingMiddleware(MiddlewareMixin):
|
|
|
119
130
|
"service_name": SERVICE_NAME,
|
|
120
131
|
"request_type": REQUEST_TYPES[0],
|
|
121
132
|
"protocol": None,
|
|
133
|
+
"request_id": "",
|
|
122
134
|
"user_id": "",
|
|
123
135
|
"user_info": {},
|
|
124
136
|
"request_repr": {},
|
|
@@ -134,6 +146,8 @@ class AuditLoggingMiddleware(MiddlewareMixin):
|
|
|
134
146
|
if iscoroutinefunction(self):
|
|
135
147
|
return self.__acall__(request)
|
|
136
148
|
set_current_request(request)
|
|
149
|
+
request_id = str(uuid.uuid4())
|
|
150
|
+
set_request_id(request_id)
|
|
137
151
|
|
|
138
152
|
if not should_log_url(request.path):
|
|
139
153
|
return self.get_response(request)
|
|
@@ -184,6 +198,7 @@ class AuditLoggingMiddleware(MiddlewareMixin):
|
|
|
184
198
|
|
|
185
199
|
self.log_data["execution_time"] = end_time - start_time
|
|
186
200
|
self.log_data["protocol"] = "https" if request.is_secure() else "http"
|
|
201
|
+
self.log_data["request_id"] = request_id
|
|
187
202
|
self.log_data["request_repr"] = request_data
|
|
188
203
|
self.log_data["response_repr"] = response_data
|
|
189
204
|
|
|
@@ -195,6 +210,8 @@ class AuditLoggingMiddleware(MiddlewareMixin):
|
|
|
195
210
|
|
|
196
211
|
async def __acall__(self, request):
|
|
197
212
|
set_current_request(request)
|
|
213
|
+
request_id = str(uuid.uuid4())
|
|
214
|
+
set_request_id(request_id)
|
|
198
215
|
|
|
199
216
|
if not should_log_url(request.path):
|
|
200
217
|
return await self.get_response(request)
|
|
@@ -247,6 +264,7 @@ class AuditLoggingMiddleware(MiddlewareMixin):
|
|
|
247
264
|
|
|
248
265
|
self.log_data["execution_time"] = end_time - start_time
|
|
249
266
|
self.log_data["protocol"] = "https" if request.is_secure() else "http"
|
|
267
|
+
self.log_data["request_id"] = request_id
|
|
250
268
|
self.log_data["request_repr"] = request_data
|
|
251
269
|
self.log_data["response_repr"] = response_data
|
|
252
270
|
|
{django_activity_audit-1.3.0.dev9 → django_activity_audit-1.3.0.dev11}/activity_audit/signals.py
RENAMED
|
@@ -15,7 +15,7 @@ from django.db.models.signals import (
|
|
|
15
15
|
from django.dispatch import receiver
|
|
16
16
|
from django.forms.models import model_to_dict
|
|
17
17
|
|
|
18
|
-
from activity_audit.middleware import get_user_details
|
|
18
|
+
from activity_audit.middleware import get_request_id, get_user_details
|
|
19
19
|
from activity_audit.settings import UNREGISTERED_CLASSES
|
|
20
20
|
|
|
21
21
|
logger = logging.getLogger("audit.model")
|
|
@@ -96,6 +96,7 @@ def push_log(
|
|
|
96
96
|
"model": model,
|
|
97
97
|
"instance_id": str(instance_id),
|
|
98
98
|
"event_type": event_type,
|
|
99
|
+
"request_id": get_request_id() or "",
|
|
99
100
|
"user_id": user_id,
|
|
100
101
|
"user_info": user_info,
|
|
101
102
|
"instance_repr": instance_repr,
|
{django_activity_audit-1.3.0.dev9 → django_activity_audit-1.3.0.dev11/docs}/django-activity-audit.md
RENAMED
|
@@ -620,6 +620,87 @@ INSERT INTO rbp_stag_logs.audit FORMAT JSONEachRow
|
|
|
620
620
|
|
|
621
621
|
---
|
|
622
622
|
|
|
623
|
-
##
|
|
623
|
+
## Part E: Async Handlers
|
|
624
624
|
|
|
625
|
-
-
|
|
625
|
+
All four handler types have non-blocking async variants. Each async handler subclasses `QueueHandler` — the calling (request) thread only enqueues the log record, and a dedicated background thread owned by a `QueueListener` performs the actual file I/O.
|
|
626
|
+
|
|
627
|
+
### How It Works
|
|
628
|
+
|
|
629
|
+
```
|
|
630
|
+
Request thread Background thread
|
|
631
|
+
────────────── ─────────────────
|
|
632
|
+
emit(record)
|
|
633
|
+
└─ queue.put() ───► QueueListener.dequeue()
|
|
634
|
+
(returns immediately) └─ RotatingFileHandler.emit()
|
|
635
|
+
└─ write to file + rotate if needed
|
|
636
|
+
```
|
|
637
|
+
|
|
638
|
+
### Async Handler Classes
|
|
639
|
+
|
|
640
|
+
| Async Handler | Wraps | Formatter |
|
|
641
|
+
|---|---|---|
|
|
642
|
+
| `AsyncAPILogHandler` | `APILogHandler` | `APIFormatter` |
|
|
643
|
+
| `AsyncAuditLogHandler` | `AuditLogHandler` | `AuditFormatter` |
|
|
644
|
+
| `AsyncLoginLogHandler` | `LoginLogHandler` | `LoginFormatter` |
|
|
645
|
+
| `AsyncJsonHandler` | `RotatingFileHandler` | `JsonFormatter` |
|
|
646
|
+
|
|
647
|
+
Each handler starts its `QueueListener` thread on `__init__` and stops it cleanly on `close()` (called automatically by Django on shutdown).
|
|
648
|
+
|
|
649
|
+
### Utility Functions
|
|
650
|
+
|
|
651
|
+
Async counterparts match the signatures of their sync equivalents:
|
|
652
|
+
|
|
653
|
+
```python
|
|
654
|
+
from activity_audit import (
|
|
655
|
+
get_async_json_handler,
|
|
656
|
+
get_async_api_handler,
|
|
657
|
+
get_async_audit_handler,
|
|
658
|
+
get_async_login_handler,
|
|
659
|
+
)
|
|
660
|
+
```
|
|
661
|
+
|
|
662
|
+
#### `get_async_json_handler`
|
|
663
|
+
|
|
664
|
+
```python
|
|
665
|
+
get_async_json_handler(
|
|
666
|
+
level="DEBUG",
|
|
667
|
+
filename="audit_logs/app.log",
|
|
668
|
+
max_bytes=1024 * 1024 * 10, # 10MB
|
|
669
|
+
backup_count=5,
|
|
670
|
+
)
|
|
671
|
+
```
|
|
672
|
+
|
|
673
|
+
> Note: unlike `get_json_handler`, there is no `formatter` parameter — `JsonFormatter` is embedded directly on the inner handler so it applies on the background thread.
|
|
674
|
+
|
|
675
|
+
#### `get_async_api_handler`
|
|
676
|
+
|
|
677
|
+
```python
|
|
678
|
+
get_async_api_handler(filename="audit_logs/api.log")
|
|
679
|
+
```
|
|
680
|
+
|
|
681
|
+
#### `get_async_audit_handler`
|
|
682
|
+
|
|
683
|
+
```python
|
|
684
|
+
get_async_audit_handler(filename="audit_logs/audit.log")
|
|
685
|
+
```
|
|
686
|
+
|
|
687
|
+
#### `get_async_login_handler`
|
|
688
|
+
|
|
689
|
+
```python
|
|
690
|
+
get_async_login_handler(filename="audit_logs/login.log")
|
|
691
|
+
```
|
|
692
|
+
|
|
693
|
+
### Example Django LOGGING Configuration
|
|
694
|
+
|
|
695
|
+
```python
|
|
696
|
+
LOGGING = {
|
|
697
|
+
"version": 1,
|
|
698
|
+
"handlers": {
|
|
699
|
+
"app_file": get_async_json_handler(level="INFO", filename="audit_logs/app.log"),
|
|
700
|
+
"api_file": get_async_api_handler(filename="audit_logs/api.log"),
|
|
701
|
+
"audit_file": get_async_audit_handler(filename="audit_logs/audit.log"),
|
|
702
|
+
"login_file": get_async_login_handler(filename="audit_logs/login.log"),
|
|
703
|
+
},
|
|
704
|
+
...
|
|
705
|
+
}
|
|
706
|
+
```
|
|
File without changes
|
{django_activity_audit-1.3.0.dev9 → django_activity_audit-1.3.0.dev11}/.pre-commit-config.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{django_activity_audit-1.3.0.dev9 → django_activity_audit-1.3.0.dev11}/activity_audit/apps.py
RENAMED
|
File without changes
|
{django_activity_audit-1.3.0.dev9 → django_activity_audit-1.3.0.dev11}/activity_audit/constants.py
RENAMED
|
File without changes
|
{django_activity_audit-1.3.0.dev9 → django_activity_audit-1.3.0.dev11}/activity_audit/handlers.py
RENAMED
|
File without changes
|
|
File without changes
|
{django_activity_audit-1.3.0.dev9 → django_activity_audit-1.3.0.dev11}/activity_audit/protocols.py
RENAMED
|
File without changes
|
{django_activity_audit-1.3.0.dev9 → django_activity_audit-1.3.0.dev11}/activity_audit/settings.py
RENAMED
|
File without changes
|
{django_activity_audit-1.3.0.dev9 → django_activity_audit-1.3.0.dev11}/activity_audit/utils.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|