django-activity-audit 1.3.0.dev8__tar.gz → 1.3.0.dev10__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.
Files changed (25) hide show
  1. {django_activity_audit-1.3.0.dev8 → django_activity_audit-1.3.0.dev10}/PKG-INFO +1 -1
  2. {django_activity_audit-1.3.0.dev8 → django_activity_audit-1.3.0.dev10}/activity_audit/__init__.py +8 -0
  3. django_activity_audit-1.3.0.dev10/activity_audit/handlers.py +166 -0
  4. {django_activity_audit-1.3.0.dev8 → django_activity_audit-1.3.0.dev10}/activity_audit/utils.py +52 -0
  5. django_activity_audit-1.3.0.dev10/django-activity-audit.md +625 -0
  6. {django_activity_audit-1.3.0.dev8 → django_activity_audit-1.3.0.dev10}/pyproject.toml +1 -1
  7. django_activity_audit-1.3.0.dev10/user-activity-feed-plan.md +416 -0
  8. django_activity_audit-1.3.0.dev8/activity_audit/handlers.py +0 -74
  9. {django_activity_audit-1.3.0.dev8 → django_activity_audit-1.3.0.dev10}/.gitignore +0 -0
  10. {django_activity_audit-1.3.0.dev8 → django_activity_audit-1.3.0.dev10}/.pre-commit-config.yaml +0 -0
  11. {django_activity_audit-1.3.0.dev8 → django_activity_audit-1.3.0.dev10}/LICENSE +0 -0
  12. {django_activity_audit-1.3.0.dev8 → django_activity_audit-1.3.0.dev10}/MANIFEST.in +0 -0
  13. {django_activity_audit-1.3.0.dev8 → django_activity_audit-1.3.0.dev10}/README.md +0 -0
  14. {django_activity_audit-1.3.0.dev8 → django_activity_audit-1.3.0.dev10}/README.rst +0 -0
  15. {django_activity_audit-1.3.0.dev8 → django_activity_audit-1.3.0.dev10}/activity_audit/apps.py +0 -0
  16. {django_activity_audit-1.3.0.dev8 → django_activity_audit-1.3.0.dev10}/activity_audit/constants.py +0 -0
  17. {django_activity_audit-1.3.0.dev8 → django_activity_audit-1.3.0.dev10}/activity_audit/formatters.py +0 -0
  18. {django_activity_audit-1.3.0.dev8 → django_activity_audit-1.3.0.dev10}/activity_audit/logger_levels.py +0 -0
  19. {django_activity_audit-1.3.0.dev8 → django_activity_audit-1.3.0.dev10}/activity_audit/middleware.py +0 -0
  20. {django_activity_audit-1.3.0.dev8 → django_activity_audit-1.3.0.dev10}/activity_audit/protocols.py +0 -0
  21. {django_activity_audit-1.3.0.dev8 → django_activity_audit-1.3.0.dev10}/activity_audit/settings.py +0 -0
  22. {django_activity_audit-1.3.0.dev8 → django_activity_audit-1.3.0.dev10}/activity_audit/signals.py +0 -0
  23. {django_activity_audit-1.3.0.dev8 → django_activity_audit-1.3.0.dev10}/hatch +0 -0
  24. {django_activity_audit-1.3.0.dev8 → django_activity_audit-1.3.0.dev10}/pytest.ini +0 -0
  25. {django_activity_audit-1.3.0.dev8 → django_activity_audit-1.3.0.dev10}/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.dev8
3
+ Version: 1.3.0.dev10
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
@@ -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
  ]
@@ -0,0 +1,166 @@
1
+ import logging
2
+ import queue
3
+
4
+ from logging.handlers import QueueHandler, QueueListener, RotatingFileHandler
5
+
6
+ from .formatters import APIFormatter, AuditFormatter, JsonFormatter, LoginFormatter
7
+
8
+
9
+ class BaseAuditHandler(RotatingFileHandler):
10
+ """Base handler for all audit logs with common emit logic."""
11
+
12
+ def emit(self, record):
13
+ """
14
+ Emit a record with additional values for audit-specific fields.
15
+ """
16
+ try:
17
+ # Handle extra if present
18
+ if hasattr(record, "extra"):
19
+ for key, value in record.extra.items():
20
+ setattr(record, key, value)
21
+
22
+ super().emit(record)
23
+
24
+ except Exception as e:
25
+ self.handleError(record)
26
+ # Log the error to the root logger
27
+ logging.getLogger().error(f"Error in AuditLogHandler: {str(e)}")
28
+
29
+
30
+ class APILogHandler(BaseAuditHandler):
31
+ """Handler for API audit logs with default APIFormatter."""
32
+
33
+ def __init__(
34
+ self,
35
+ filename,
36
+ mode="a",
37
+ maxBytes=0,
38
+ backupCount=0,
39
+ encoding=None,
40
+ delay=False,
41
+ ):
42
+ super().__init__(filename, mode, maxBytes, backupCount, encoding, delay)
43
+ self.setFormatter(APIFormatter())
44
+
45
+
46
+ class AuditLogHandler(BaseAuditHandler):
47
+ """Handler for model audit logs with default AuditFormatter."""
48
+
49
+ def __init__(
50
+ self,
51
+ filename,
52
+ mode="a",
53
+ maxBytes=0,
54
+ backupCount=0,
55
+ encoding=None,
56
+ delay=False,
57
+ ):
58
+ super().__init__(filename, mode, maxBytes, backupCount, encoding, delay)
59
+ self.setFormatter(AuditFormatter())
60
+
61
+
62
+ class LoginLogHandler(BaseAuditHandler):
63
+ """Handler for login audit logs with default LoginFormatter."""
64
+
65
+ def __init__(
66
+ self,
67
+ filename,
68
+ mode="a",
69
+ maxBytes=0,
70
+ backupCount=0,
71
+ encoding=None,
72
+ delay=False,
73
+ ):
74
+ super().__init__(filename, mode, maxBytes, backupCount, encoding, delay)
75
+ self.setFormatter(LoginFormatter())
76
+
77
+
78
+ # ASYNC ------------------------------------------------------
79
+ class AsyncBaseAuditHandler(QueueHandler):
80
+ """
81
+ Base async handler. Enqueues records on the calling thread; a background
82
+ QueueListener thread does the actual file write via the wrapped sync handler.
83
+
84
+ Subclasses pass the concrete sync handler class and formatter via
85
+ _sync_handler_class and _formatter_class.
86
+ """
87
+
88
+ _sync_handler_class = None
89
+ _formatter_class = None
90
+
91
+ def __init__(
92
+ self,
93
+ filename,
94
+ mode="a",
95
+ maxBytes=0,
96
+ backupCount=0,
97
+ encoding=None,
98
+ delay=False,
99
+ ):
100
+ log_queue = queue.Queue(-1)
101
+ super().__init__(log_queue)
102
+
103
+ sync_handler = self._sync_handler_class(
104
+ filename, mode, maxBytes, backupCount, encoding, delay
105
+ )
106
+ self._listener = QueueListener(
107
+ log_queue, sync_handler, respect_handler_level=True
108
+ )
109
+ self._listener.start()
110
+
111
+ def close(self):
112
+ self._listener.stop()
113
+ super().close()
114
+
115
+
116
+ class AsyncAPILogHandler(AsyncBaseAuditHandler):
117
+ """Non-blocking handler for API audit logs."""
118
+
119
+ _sync_handler_class = APILogHandler
120
+ _formatter_class = APIFormatter
121
+
122
+
123
+ class AsyncAuditLogHandler(AsyncBaseAuditHandler):
124
+ """Non-blocking handler for model audit logs."""
125
+
126
+ _sync_handler_class = AuditLogHandler
127
+ _formatter_class = AuditFormatter
128
+
129
+
130
+ class AsyncLoginLogHandler(AsyncBaseAuditHandler):
131
+ """Non-blocking handler for login audit logs."""
132
+
133
+ _sync_handler_class = LoginLogHandler
134
+ _formatter_class = LoginFormatter
135
+
136
+
137
+ class AsyncJsonHandler(QueueHandler):
138
+ """
139
+ Non-blocking handler for general JSON logs. Wraps a RotatingFileHandler
140
+ with JsonFormatter on the background thread.
141
+ """
142
+
143
+ def __init__(
144
+ self,
145
+ filename,
146
+ mode="a",
147
+ maxBytes=0,
148
+ backupCount=0,
149
+ encoding=None,
150
+ delay=False,
151
+ ):
152
+ log_queue = queue.Queue(-1)
153
+ super().__init__(log_queue)
154
+
155
+ sync_handler = RotatingFileHandler(
156
+ filename, mode, maxBytes, backupCount, encoding, delay
157
+ )
158
+ sync_handler.setFormatter(JsonFormatter())
159
+ self._listener = QueueListener(
160
+ log_queue, sync_handler, respect_handler_level=True
161
+ )
162
+ self._listener.start()
163
+
164
+ def close(self):
165
+ self._listener.stop()
166
+ super().close()
@@ -63,6 +63,58 @@ def get_login_handler(
63
63
  }
64
64
 
65
65
 
66
+ # ASYNC ------------------------------------------------------
67
+ def get_async_json_handler(
68
+ level: str = "DEBUG",
69
+ filename: str = "audit_logs/app.log",
70
+ max_bytes: int = 1024 * 1024 * 10,
71
+ backup_count: int = 5,
72
+ ) -> dict:
73
+ return {
74
+ "level": level,
75
+ "class": "activity_audit.handlers.AsyncJsonHandler",
76
+ "filename": filename,
77
+ "maxBytes": max_bytes,
78
+ "backupCount": backup_count,
79
+ }
80
+
81
+
82
+ def get_async_api_handler(
83
+ filename: str = "audit_logs/api.log",
84
+ ) -> dict:
85
+ return {
86
+ "class": "activity_audit.handlers.AsyncAPILogHandler",
87
+ "filename": filename,
88
+ "maxBytes": 1024 * 1024 * 10, # 10MB
89
+ "backupCount": 5,
90
+ }
91
+
92
+
93
+ def get_async_audit_handler(
94
+ filename: str = "audit_logs/audit.log",
95
+ ) -> dict:
96
+ return {
97
+ "class": "activity_audit.handlers.AsyncAuditLogHandler",
98
+ "filename": filename,
99
+ "maxBytes": 1024 * 1024 * 10, # 10MB
100
+ "backupCount": 5,
101
+ }
102
+
103
+
104
+ def get_async_login_handler(
105
+ filename: str = "audit_logs/login.log",
106
+ ) -> dict:
107
+ return {
108
+ "class": "activity_audit.handlers.AsyncLoginLogHandler",
109
+ "filename": filename,
110
+ "maxBytes": 1024 * 1024 * 5, # 5MB
111
+ "backupCount": 5,
112
+ }
113
+
114
+
115
+ # ----------------------------------------------------------------
116
+
117
+
66
118
  def push_usage_log(
67
119
  message: str,
68
120
  event: str,