lyzr-audit-logger-dev 0.1.0__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.
- lyzr_audit_logger_dev-0.1.0/PKG-INFO +333 -0
- lyzr_audit_logger_dev-0.1.0/README.md +310 -0
- lyzr_audit_logger_dev-0.1.0/pyproject.toml +43 -0
- lyzr_audit_logger_dev-0.1.0/setup.cfg +4 -0
- lyzr_audit_logger_dev-0.1.0/src/audit_logger/__init__.py +208 -0
- lyzr_audit_logger_dev-0.1.0/src/audit_logger/context.py +502 -0
- lyzr_audit_logger_dev-0.1.0/src/audit_logger/enums.py +87 -0
- lyzr_audit_logger_dev-0.1.0/src/audit_logger/manager.py +913 -0
- lyzr_audit_logger_dev-0.1.0/src/audit_logger/models.py +95 -0
- lyzr_audit_logger_dev-0.1.0/src/audit_logger/py.typed +0 -0
- lyzr_audit_logger_dev-0.1.0/src/lyzr_audit_logger_dev.egg-info/PKG-INFO +333 -0
- lyzr_audit_logger_dev-0.1.0/src/lyzr_audit_logger_dev.egg-info/SOURCES.txt +13 -0
- lyzr_audit_logger_dev-0.1.0/src/lyzr_audit_logger_dev.egg-info/dependency_links.txt +1 -0
- lyzr_audit_logger_dev-0.1.0/src/lyzr_audit_logger_dev.egg-info/requires.txt +6 -0
- lyzr_audit_logger_dev-0.1.0/src/lyzr_audit_logger_dev.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: lyzr-audit-logger-dev
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Audit logging library for microservices
|
|
5
|
+
Author-email: Lyzr <support@lyzr.ai>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/lyzr/audit-logger
|
|
8
|
+
Project-URL: Documentation, https://github.com/lyzr/audit-logger#readme
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Requires-Python: >=3.10
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
Requires-Dist: motor>=3.0.0
|
|
19
|
+
Requires-Dist: pydantic>=2.0.0
|
|
20
|
+
Provides-Extra: dev
|
|
21
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
22
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
|
|
23
|
+
|
|
24
|
+
# Audit Logger
|
|
25
|
+
|
|
26
|
+
A Python package for audit logging across microservices. Provides async, non-blocking audit log capture with MongoDB storage.
|
|
27
|
+
|
|
28
|
+
## Installation
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pip install audit-logger
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Or install from source:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
# Using HTTPS
|
|
38
|
+
pip install git+https://github.com/NeuralgoLyzr/audit-logger.git
|
|
39
|
+
|
|
40
|
+
# Using SSH
|
|
41
|
+
pip install git+ssh://git@github.com/NeuralgoLyzr/audit-logger.git
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
For local development:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
cd audit_logger
|
|
48
|
+
pip install -e .
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Quick Start
|
|
52
|
+
|
|
53
|
+
### 1. Initialize at Application Startup
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
from audit_logger import init_audit_logger
|
|
57
|
+
|
|
58
|
+
# Option 1: Use environment variable AUDIT_MONGO_URL (defaults to mongodb://localhost:27017)
|
|
59
|
+
audit_manager, audit_logger = init_audit_logger()
|
|
60
|
+
|
|
61
|
+
# Option 2: Explicit configuration
|
|
62
|
+
audit_manager, audit_logger = init_audit_logger(
|
|
63
|
+
mongo_url="mongodb://audit-db:27017",
|
|
64
|
+
db_name="my_service_audit",
|
|
65
|
+
collection_name="audit_logs"
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
# Create indexes (call once on startup)
|
|
69
|
+
await audit_manager.ensure_indexes()
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### 2. Set Context in Middleware
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
from audit_logger import set_audit_context, clear_audit_context
|
|
76
|
+
|
|
77
|
+
@app.middleware("http")
|
|
78
|
+
async def audit_middleware(request: Request, call_next):
|
|
79
|
+
# Set context from auth info
|
|
80
|
+
set_audit_context(
|
|
81
|
+
org_id=request.state.auth_user.org_id,
|
|
82
|
+
user_id=request.state.auth_user.user_id,
|
|
83
|
+
api_key=request.headers.get("x-api-key"),
|
|
84
|
+
ip_address=request.client.host,
|
|
85
|
+
)
|
|
86
|
+
try:
|
|
87
|
+
return await call_next(request)
|
|
88
|
+
finally:
|
|
89
|
+
clear_audit_context()
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### 3. Log Audit Events
|
|
93
|
+
|
|
94
|
+
```python
|
|
95
|
+
from audit_logger import audit_logger, AuditResource
|
|
96
|
+
|
|
97
|
+
# Log a create action (non-blocking, returns immediately)
|
|
98
|
+
audit_logger.log_create(
|
|
99
|
+
resource_type=AuditResource.AGENT,
|
|
100
|
+
resource_id="agent_123",
|
|
101
|
+
resource_name="My Agent",
|
|
102
|
+
metadata={"tools_count": 5}
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
# Log an update action
|
|
106
|
+
from audit_logger import AuditChange
|
|
107
|
+
|
|
108
|
+
audit_logger.log_update(
|
|
109
|
+
resource_type=AuditResource.AGENT,
|
|
110
|
+
resource_id="agent_123",
|
|
111
|
+
changes=[
|
|
112
|
+
AuditChange(field="name", old_value="Old Name", new_value="New Name"),
|
|
113
|
+
AuditChange(field="description", old_value=None, new_value="New description"),
|
|
114
|
+
]
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
# Log a delete action
|
|
118
|
+
audit_logger.log_delete(
|
|
119
|
+
resource_type=AuditResource.AGENT,
|
|
120
|
+
resource_id="agent_123",
|
|
121
|
+
resource_name="My Agent"
|
|
122
|
+
)
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### 4. Shutdown Gracefully
|
|
126
|
+
|
|
127
|
+
```python
|
|
128
|
+
from audit_logger import shutdown_audit_logger
|
|
129
|
+
|
|
130
|
+
@app.on_event("shutdown")
|
|
131
|
+
async def shutdown():
|
|
132
|
+
await shutdown_audit_logger()
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Advanced Usage
|
|
136
|
+
|
|
137
|
+
### Direct Manager Access
|
|
138
|
+
|
|
139
|
+
For more control, use the `AuditLogManager` directly:
|
|
140
|
+
|
|
141
|
+
```python
|
|
142
|
+
from audit_logger import get_audit_log_manager, AuditResource, AuditResult
|
|
143
|
+
|
|
144
|
+
manager = get_audit_log_manager()
|
|
145
|
+
|
|
146
|
+
# Log with full control
|
|
147
|
+
manager.log_create(
|
|
148
|
+
org_id="org_123",
|
|
149
|
+
resource_type=AuditResource.TOOL,
|
|
150
|
+
resource_id="tool_456",
|
|
151
|
+
user_id="user_789",
|
|
152
|
+
api_key="sk-xxx",
|
|
153
|
+
ip_address="192.168.1.1",
|
|
154
|
+
resource_name="My Tool",
|
|
155
|
+
metadata={"type": "openapi"},
|
|
156
|
+
result=AuditResult.SUCCESS,
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
# Log inference events
|
|
160
|
+
manager.log_inference(
|
|
161
|
+
org_id="org_123",
|
|
162
|
+
agent_id="agent_456",
|
|
163
|
+
session_id="session_789",
|
|
164
|
+
user_id="user_123",
|
|
165
|
+
result=AuditResult.SUCCESS,
|
|
166
|
+
metadata={"model": "gpt-4", "tokens": 1500}
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
# Log guardrail violations
|
|
170
|
+
from audit_logger import GuardrailViolation, AuditSeverity
|
|
171
|
+
|
|
172
|
+
manager.log_guardrail_violation(
|
|
173
|
+
org_id="org_123",
|
|
174
|
+
session_id="session_789",
|
|
175
|
+
violations=[
|
|
176
|
+
GuardrailViolation(
|
|
177
|
+
violation_type="pii",
|
|
178
|
+
severity=AuditSeverity.MEDIUM,
|
|
179
|
+
details={"entities": ["email", "phone"]}
|
|
180
|
+
)
|
|
181
|
+
]
|
|
182
|
+
)
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### Query Audit Logs
|
|
186
|
+
|
|
187
|
+
```python
|
|
188
|
+
from audit_logger import get_audit_log_manager, AuditLogQuery, AuditResource
|
|
189
|
+
|
|
190
|
+
manager = get_audit_log_manager()
|
|
191
|
+
|
|
192
|
+
# Query with filters
|
|
193
|
+
logs = await manager.query(AuditLogQuery(
|
|
194
|
+
org_id="org_123",
|
|
195
|
+
resource_type=AuditResource.AGENT,
|
|
196
|
+
limit=50,
|
|
197
|
+
))
|
|
198
|
+
|
|
199
|
+
# Get history for a specific resource
|
|
200
|
+
history = await manager.get_by_resource(
|
|
201
|
+
org_id="org_123",
|
|
202
|
+
resource_type=AuditResource.AGENT,
|
|
203
|
+
resource_id="agent_456",
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
# Get session activity
|
|
207
|
+
session_logs = await manager.get_by_session(session_id="session_789")
|
|
208
|
+
|
|
209
|
+
# Count matching logs
|
|
210
|
+
count = await manager.count(AuditLogQuery(org_id="org_123"))
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### Using the Decorator
|
|
214
|
+
|
|
215
|
+
```python
|
|
216
|
+
from audit_logger import audit_action, AuditAction, AuditResource
|
|
217
|
+
|
|
218
|
+
@audit_action(
|
|
219
|
+
action=AuditAction.DELETE,
|
|
220
|
+
resource_type=AuditResource.AGENT,
|
|
221
|
+
resource_id_param="agent_id",
|
|
222
|
+
)
|
|
223
|
+
async def delete_agent(agent_id: str, _audit_manager=None):
|
|
224
|
+
# Delete logic here
|
|
225
|
+
pass
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### Context Manager
|
|
229
|
+
|
|
230
|
+
```python
|
|
231
|
+
from audit_logger import audit_context
|
|
232
|
+
|
|
233
|
+
async with audit_context(org_id="org_123", user_id="user_456") as request_id:
|
|
234
|
+
# All audit logs in this block will have the context set
|
|
235
|
+
audit_logger.log_create(AuditResource.AGENT, "agent_789")
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
## Configuration
|
|
239
|
+
|
|
240
|
+
### Environment Variables
|
|
241
|
+
|
|
242
|
+
| Variable | Default | Description |
|
|
243
|
+
|----------|---------|-------------|
|
|
244
|
+
| `AUDIT_MONGO_URL` | `mongodb://localhost:27017` | MongoDB connection URL |
|
|
245
|
+
|
|
246
|
+
### Programmatic Configuration
|
|
247
|
+
|
|
248
|
+
```python
|
|
249
|
+
from audit_logger import init_audit_logger
|
|
250
|
+
|
|
251
|
+
# Full configuration
|
|
252
|
+
manager, logger = init_audit_logger(
|
|
253
|
+
mongo_url="mongodb://audit-db:27017",
|
|
254
|
+
db_name="lyzr_audit_logs",
|
|
255
|
+
collection_name="audit_logs",
|
|
256
|
+
)
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### Using Existing Collection
|
|
260
|
+
|
|
261
|
+
If you already have a MongoDB collection configured:
|
|
262
|
+
|
|
263
|
+
```python
|
|
264
|
+
from motor.motor_asyncio import AsyncIOMotorClient
|
|
265
|
+
from audit_logger import init_audit_logger
|
|
266
|
+
|
|
267
|
+
client = AsyncIOMotorClient("mongodb://localhost:27017")
|
|
268
|
+
collection = client["my_db"]["audit_logs"]
|
|
269
|
+
|
|
270
|
+
manager, logger = init_audit_logger(collection=collection)
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
## Available Enums
|
|
274
|
+
|
|
275
|
+
### AuditAction
|
|
276
|
+
- `CREATE`, `READ`, `UPDATE`, `DELETE`
|
|
277
|
+
- `EXECUTE`, `LOGIN`, `LOGOUT`
|
|
278
|
+
- `ACCESS_DENIED`, `EXPORT`, `IMPORT`
|
|
279
|
+
|
|
280
|
+
### AuditResource
|
|
281
|
+
- `AGENT`, `TOOL`, `PROVIDER`, `WORKFLOW`
|
|
282
|
+
- `SESSION`, `MESSAGE`, `CONTEXT`, `MEMORY`
|
|
283
|
+
- `CREDENTIAL`, `USER`, `ORGANIZATION`, `API_KEY`
|
|
284
|
+
- `INFERENCE`, `GUARDRAIL`, `ARTIFACT`
|
|
285
|
+
|
|
286
|
+
### AuditResult
|
|
287
|
+
- `SUCCESS`, `FAILURE`, `BLOCKED`, `PARTIAL`
|
|
288
|
+
|
|
289
|
+
### AuditSeverity
|
|
290
|
+
- `LOW`, `MEDIUM`, `HIGH`, `CRITICAL`
|
|
291
|
+
|
|
292
|
+
## MongoDB Schema
|
|
293
|
+
|
|
294
|
+
```javascript
|
|
295
|
+
{
|
|
296
|
+
"_id": ObjectId,
|
|
297
|
+
"timestamp": ISODate,
|
|
298
|
+
"actor": {
|
|
299
|
+
"org_id": "org_123",
|
|
300
|
+
"user_id": "user_456",
|
|
301
|
+
"api_key_hash": "a1b2c3d4...xyz",
|
|
302
|
+
"ip_address": "192.168.1.1"
|
|
303
|
+
},
|
|
304
|
+
"action": "create",
|
|
305
|
+
"target": {
|
|
306
|
+
"resource_type": "agent",
|
|
307
|
+
"resource_id": "agent_789",
|
|
308
|
+
"resource_name": "Support Bot"
|
|
309
|
+
},
|
|
310
|
+
"result": "success",
|
|
311
|
+
"error_message": null,
|
|
312
|
+
"session_id": "session_abc",
|
|
313
|
+
"request_id": "req_def",
|
|
314
|
+
"changes": [...],
|
|
315
|
+
"guardrail_violations": [...],
|
|
316
|
+
"metadata": {...},
|
|
317
|
+
"severity": "low"
|
|
318
|
+
}
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
## Non-Blocking Writes
|
|
322
|
+
|
|
323
|
+
All logging methods are **non-blocking** by default. They use `asyncio.create_task()` to write to MongoDB in the background without blocking your request handlers.
|
|
324
|
+
|
|
325
|
+
For synchronous writes (when you need confirmation):
|
|
326
|
+
|
|
327
|
+
```python
|
|
328
|
+
log_id = await manager.log_sync(entry)
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
## License
|
|
332
|
+
|
|
333
|
+
MIT
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
# Audit Logger
|
|
2
|
+
|
|
3
|
+
A Python package for audit logging across microservices. Provides async, non-blocking audit log capture with MongoDB storage.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install audit-logger
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Or install from source:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
# Using HTTPS
|
|
15
|
+
pip install git+https://github.com/NeuralgoLyzr/audit-logger.git
|
|
16
|
+
|
|
17
|
+
# Using SSH
|
|
18
|
+
pip install git+ssh://git@github.com/NeuralgoLyzr/audit-logger.git
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
For local development:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
cd audit_logger
|
|
25
|
+
pip install -e .
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Quick Start
|
|
29
|
+
|
|
30
|
+
### 1. Initialize at Application Startup
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
from audit_logger import init_audit_logger
|
|
34
|
+
|
|
35
|
+
# Option 1: Use environment variable AUDIT_MONGO_URL (defaults to mongodb://localhost:27017)
|
|
36
|
+
audit_manager, audit_logger = init_audit_logger()
|
|
37
|
+
|
|
38
|
+
# Option 2: Explicit configuration
|
|
39
|
+
audit_manager, audit_logger = init_audit_logger(
|
|
40
|
+
mongo_url="mongodb://audit-db:27017",
|
|
41
|
+
db_name="my_service_audit",
|
|
42
|
+
collection_name="audit_logs"
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
# Create indexes (call once on startup)
|
|
46
|
+
await audit_manager.ensure_indexes()
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### 2. Set Context in Middleware
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
from audit_logger import set_audit_context, clear_audit_context
|
|
53
|
+
|
|
54
|
+
@app.middleware("http")
|
|
55
|
+
async def audit_middleware(request: Request, call_next):
|
|
56
|
+
# Set context from auth info
|
|
57
|
+
set_audit_context(
|
|
58
|
+
org_id=request.state.auth_user.org_id,
|
|
59
|
+
user_id=request.state.auth_user.user_id,
|
|
60
|
+
api_key=request.headers.get("x-api-key"),
|
|
61
|
+
ip_address=request.client.host,
|
|
62
|
+
)
|
|
63
|
+
try:
|
|
64
|
+
return await call_next(request)
|
|
65
|
+
finally:
|
|
66
|
+
clear_audit_context()
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### 3. Log Audit Events
|
|
70
|
+
|
|
71
|
+
```python
|
|
72
|
+
from audit_logger import audit_logger, AuditResource
|
|
73
|
+
|
|
74
|
+
# Log a create action (non-blocking, returns immediately)
|
|
75
|
+
audit_logger.log_create(
|
|
76
|
+
resource_type=AuditResource.AGENT,
|
|
77
|
+
resource_id="agent_123",
|
|
78
|
+
resource_name="My Agent",
|
|
79
|
+
metadata={"tools_count": 5}
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
# Log an update action
|
|
83
|
+
from audit_logger import AuditChange
|
|
84
|
+
|
|
85
|
+
audit_logger.log_update(
|
|
86
|
+
resource_type=AuditResource.AGENT,
|
|
87
|
+
resource_id="agent_123",
|
|
88
|
+
changes=[
|
|
89
|
+
AuditChange(field="name", old_value="Old Name", new_value="New Name"),
|
|
90
|
+
AuditChange(field="description", old_value=None, new_value="New description"),
|
|
91
|
+
]
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
# Log a delete action
|
|
95
|
+
audit_logger.log_delete(
|
|
96
|
+
resource_type=AuditResource.AGENT,
|
|
97
|
+
resource_id="agent_123",
|
|
98
|
+
resource_name="My Agent"
|
|
99
|
+
)
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### 4. Shutdown Gracefully
|
|
103
|
+
|
|
104
|
+
```python
|
|
105
|
+
from audit_logger import shutdown_audit_logger
|
|
106
|
+
|
|
107
|
+
@app.on_event("shutdown")
|
|
108
|
+
async def shutdown():
|
|
109
|
+
await shutdown_audit_logger()
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Advanced Usage
|
|
113
|
+
|
|
114
|
+
### Direct Manager Access
|
|
115
|
+
|
|
116
|
+
For more control, use the `AuditLogManager` directly:
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
from audit_logger import get_audit_log_manager, AuditResource, AuditResult
|
|
120
|
+
|
|
121
|
+
manager = get_audit_log_manager()
|
|
122
|
+
|
|
123
|
+
# Log with full control
|
|
124
|
+
manager.log_create(
|
|
125
|
+
org_id="org_123",
|
|
126
|
+
resource_type=AuditResource.TOOL,
|
|
127
|
+
resource_id="tool_456",
|
|
128
|
+
user_id="user_789",
|
|
129
|
+
api_key="sk-xxx",
|
|
130
|
+
ip_address="192.168.1.1",
|
|
131
|
+
resource_name="My Tool",
|
|
132
|
+
metadata={"type": "openapi"},
|
|
133
|
+
result=AuditResult.SUCCESS,
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
# Log inference events
|
|
137
|
+
manager.log_inference(
|
|
138
|
+
org_id="org_123",
|
|
139
|
+
agent_id="agent_456",
|
|
140
|
+
session_id="session_789",
|
|
141
|
+
user_id="user_123",
|
|
142
|
+
result=AuditResult.SUCCESS,
|
|
143
|
+
metadata={"model": "gpt-4", "tokens": 1500}
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
# Log guardrail violations
|
|
147
|
+
from audit_logger import GuardrailViolation, AuditSeverity
|
|
148
|
+
|
|
149
|
+
manager.log_guardrail_violation(
|
|
150
|
+
org_id="org_123",
|
|
151
|
+
session_id="session_789",
|
|
152
|
+
violations=[
|
|
153
|
+
GuardrailViolation(
|
|
154
|
+
violation_type="pii",
|
|
155
|
+
severity=AuditSeverity.MEDIUM,
|
|
156
|
+
details={"entities": ["email", "phone"]}
|
|
157
|
+
)
|
|
158
|
+
]
|
|
159
|
+
)
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### Query Audit Logs
|
|
163
|
+
|
|
164
|
+
```python
|
|
165
|
+
from audit_logger import get_audit_log_manager, AuditLogQuery, AuditResource
|
|
166
|
+
|
|
167
|
+
manager = get_audit_log_manager()
|
|
168
|
+
|
|
169
|
+
# Query with filters
|
|
170
|
+
logs = await manager.query(AuditLogQuery(
|
|
171
|
+
org_id="org_123",
|
|
172
|
+
resource_type=AuditResource.AGENT,
|
|
173
|
+
limit=50,
|
|
174
|
+
))
|
|
175
|
+
|
|
176
|
+
# Get history for a specific resource
|
|
177
|
+
history = await manager.get_by_resource(
|
|
178
|
+
org_id="org_123",
|
|
179
|
+
resource_type=AuditResource.AGENT,
|
|
180
|
+
resource_id="agent_456",
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
# Get session activity
|
|
184
|
+
session_logs = await manager.get_by_session(session_id="session_789")
|
|
185
|
+
|
|
186
|
+
# Count matching logs
|
|
187
|
+
count = await manager.count(AuditLogQuery(org_id="org_123"))
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### Using the Decorator
|
|
191
|
+
|
|
192
|
+
```python
|
|
193
|
+
from audit_logger import audit_action, AuditAction, AuditResource
|
|
194
|
+
|
|
195
|
+
@audit_action(
|
|
196
|
+
action=AuditAction.DELETE,
|
|
197
|
+
resource_type=AuditResource.AGENT,
|
|
198
|
+
resource_id_param="agent_id",
|
|
199
|
+
)
|
|
200
|
+
async def delete_agent(agent_id: str, _audit_manager=None):
|
|
201
|
+
# Delete logic here
|
|
202
|
+
pass
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### Context Manager
|
|
206
|
+
|
|
207
|
+
```python
|
|
208
|
+
from audit_logger import audit_context
|
|
209
|
+
|
|
210
|
+
async with audit_context(org_id="org_123", user_id="user_456") as request_id:
|
|
211
|
+
# All audit logs in this block will have the context set
|
|
212
|
+
audit_logger.log_create(AuditResource.AGENT, "agent_789")
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
## Configuration
|
|
216
|
+
|
|
217
|
+
### Environment Variables
|
|
218
|
+
|
|
219
|
+
| Variable | Default | Description |
|
|
220
|
+
|----------|---------|-------------|
|
|
221
|
+
| `AUDIT_MONGO_URL` | `mongodb://localhost:27017` | MongoDB connection URL |
|
|
222
|
+
|
|
223
|
+
### Programmatic Configuration
|
|
224
|
+
|
|
225
|
+
```python
|
|
226
|
+
from audit_logger import init_audit_logger
|
|
227
|
+
|
|
228
|
+
# Full configuration
|
|
229
|
+
manager, logger = init_audit_logger(
|
|
230
|
+
mongo_url="mongodb://audit-db:27017",
|
|
231
|
+
db_name="lyzr_audit_logs",
|
|
232
|
+
collection_name="audit_logs",
|
|
233
|
+
)
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### Using Existing Collection
|
|
237
|
+
|
|
238
|
+
If you already have a MongoDB collection configured:
|
|
239
|
+
|
|
240
|
+
```python
|
|
241
|
+
from motor.motor_asyncio import AsyncIOMotorClient
|
|
242
|
+
from audit_logger import init_audit_logger
|
|
243
|
+
|
|
244
|
+
client = AsyncIOMotorClient("mongodb://localhost:27017")
|
|
245
|
+
collection = client["my_db"]["audit_logs"]
|
|
246
|
+
|
|
247
|
+
manager, logger = init_audit_logger(collection=collection)
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
## Available Enums
|
|
251
|
+
|
|
252
|
+
### AuditAction
|
|
253
|
+
- `CREATE`, `READ`, `UPDATE`, `DELETE`
|
|
254
|
+
- `EXECUTE`, `LOGIN`, `LOGOUT`
|
|
255
|
+
- `ACCESS_DENIED`, `EXPORT`, `IMPORT`
|
|
256
|
+
|
|
257
|
+
### AuditResource
|
|
258
|
+
- `AGENT`, `TOOL`, `PROVIDER`, `WORKFLOW`
|
|
259
|
+
- `SESSION`, `MESSAGE`, `CONTEXT`, `MEMORY`
|
|
260
|
+
- `CREDENTIAL`, `USER`, `ORGANIZATION`, `API_KEY`
|
|
261
|
+
- `INFERENCE`, `GUARDRAIL`, `ARTIFACT`
|
|
262
|
+
|
|
263
|
+
### AuditResult
|
|
264
|
+
- `SUCCESS`, `FAILURE`, `BLOCKED`, `PARTIAL`
|
|
265
|
+
|
|
266
|
+
### AuditSeverity
|
|
267
|
+
- `LOW`, `MEDIUM`, `HIGH`, `CRITICAL`
|
|
268
|
+
|
|
269
|
+
## MongoDB Schema
|
|
270
|
+
|
|
271
|
+
```javascript
|
|
272
|
+
{
|
|
273
|
+
"_id": ObjectId,
|
|
274
|
+
"timestamp": ISODate,
|
|
275
|
+
"actor": {
|
|
276
|
+
"org_id": "org_123",
|
|
277
|
+
"user_id": "user_456",
|
|
278
|
+
"api_key_hash": "a1b2c3d4...xyz",
|
|
279
|
+
"ip_address": "192.168.1.1"
|
|
280
|
+
},
|
|
281
|
+
"action": "create",
|
|
282
|
+
"target": {
|
|
283
|
+
"resource_type": "agent",
|
|
284
|
+
"resource_id": "agent_789",
|
|
285
|
+
"resource_name": "Support Bot"
|
|
286
|
+
},
|
|
287
|
+
"result": "success",
|
|
288
|
+
"error_message": null,
|
|
289
|
+
"session_id": "session_abc",
|
|
290
|
+
"request_id": "req_def",
|
|
291
|
+
"changes": [...],
|
|
292
|
+
"guardrail_violations": [...],
|
|
293
|
+
"metadata": {...},
|
|
294
|
+
"severity": "low"
|
|
295
|
+
}
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
## Non-Blocking Writes
|
|
299
|
+
|
|
300
|
+
All logging methods are **non-blocking** by default. They use `asyncio.create_task()` to write to MongoDB in the background without blocking your request handlers.
|
|
301
|
+
|
|
302
|
+
For synchronous writes (when you need confirmation):
|
|
303
|
+
|
|
304
|
+
```python
|
|
305
|
+
log_id = await manager.log_sync(entry)
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
## License
|
|
309
|
+
|
|
310
|
+
MIT
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "lyzr-audit-logger-dev"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Audit logging library for microservices"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = {text = "MIT"}
|
|
11
|
+
requires-python = ">=3.10"
|
|
12
|
+
authors = [
|
|
13
|
+
{name = "Lyzr", email = "support@lyzr.ai"}
|
|
14
|
+
]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 4 - Beta",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"License :: OSI Approved :: MIT License",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Programming Language :: Python :: 3.10",
|
|
21
|
+
"Programming Language :: Python :: 3.11",
|
|
22
|
+
"Programming Language :: Python :: 3.12",
|
|
23
|
+
]
|
|
24
|
+
dependencies = [
|
|
25
|
+
"motor>=3.0.0",
|
|
26
|
+
"pydantic>=2.0.0",
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
[project.optional-dependencies]
|
|
30
|
+
dev = [
|
|
31
|
+
"pytest>=7.0.0",
|
|
32
|
+
"pytest-asyncio>=0.21.0",
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
[project.urls]
|
|
36
|
+
Homepage = "https://github.com/lyzr/audit-logger"
|
|
37
|
+
Documentation = "https://github.com/lyzr/audit-logger#readme"
|
|
38
|
+
|
|
39
|
+
[tool.setuptools.packages.find]
|
|
40
|
+
where = ["src"]
|
|
41
|
+
|
|
42
|
+
[tool.setuptools.package-dir]
|
|
43
|
+
"" = "src"
|