docs
0
# Observability & Analytics
1
2
Comprehensive logging, request tracing, analytics, and monitoring capabilities with custom metadata and performance metrics.
3
4
## Capabilities
5
6
### Request Logging
7
8
Track and analyze all API requests with detailed logging capabilities including request/response data, performance metrics, and custom metadata.
9
10
```python { .api }
11
class Logs:
12
"""Request logging and retrieval"""
13
14
def list(
15
self,
16
*,
17
limit: Optional[int] = None,
18
after: Optional[str] = None,
19
before: Optional[str] = None,
20
order: Optional[str] = None,
21
filters: Optional[dict] = None,
22
**kwargs
23
) -> dict:
24
"""
25
List request logs with filtering and pagination.
26
27
Parameters:
28
- limit: Maximum number of logs to retrieve
29
- after: Cursor for pagination (logs after this ID)
30
- before: Cursor for pagination (logs before this ID)
31
- order: Sort order ('asc' or 'desc')
32
- filters: Filter criteria (date range, model, provider, etc.)
33
34
Returns:
35
Dictionary containing log entries and pagination info
36
"""
37
38
def retrieve(
39
self,
40
log_id: str,
41
**kwargs
42
) -> dict:
43
"""
44
Retrieve a specific log entry by ID.
45
46
Parameters:
47
- log_id: Unique identifier for the log entry
48
49
Returns:
50
Detailed log entry with request/response data
51
"""
52
53
class AsyncLogs:
54
"""Async request logging and retrieval"""
55
56
async def list(self, **kwargs) -> dict: ...
57
async def retrieve(self, log_id: str, **kwargs) -> dict: ...
58
```
59
60
### Response Analysis
61
62
Analyze API responses with detailed breakdowns of input/output tokens, performance metrics, and content analysis.
63
64
```python { .api }
65
class Responses:
66
"""Response analysis and metrics"""
67
68
def list(
69
self,
70
*,
71
limit: Optional[int] = None,
72
after: Optional[str] = None,
73
before: Optional[str] = None,
74
filters: Optional[dict] = None,
75
**kwargs
76
) -> dict:
77
"""List and analyze API responses"""
78
79
def retrieve(
80
self,
81
response_id: str,
82
**kwargs
83
) -> dict:
84
"""Retrieve detailed response analysis"""
85
86
input_items: InputItems
87
output_items: OutputItems
88
89
class InputItems:
90
"""Input content analysis"""
91
92
def list(self, **kwargs) -> dict: ...
93
def retrieve(self, item_id: str, **kwargs) -> dict: ...
94
95
class OutputItems:
96
"""Output content analysis"""
97
98
def list(self, **kwargs) -> dict: ...
99
def retrieve(self, item_id: str, **kwargs) -> dict: ...
100
101
class AsyncResponses:
102
"""Async response analysis"""
103
104
async def list(self, **kwargs) -> dict: ...
105
async def retrieve(self, response_id: str, **kwargs) -> dict: ...
106
input_items: AsyncInputItems
107
output_items: AsyncOutputItems
108
```
109
110
### Request Labeling
111
112
Categorize and tag API requests for better organization, analysis, and filtering.
113
114
```python { .api }
115
class Labels:
116
"""Request labeling and categorization"""
117
118
def create(
119
self,
120
*,
121
log_id: str,
122
label: str,
123
value: Optional[str] = None,
124
**kwargs
125
) -> dict:
126
"""
127
Create a label for a request log.
128
129
Parameters:
130
- log_id: ID of the log entry to label
131
- label: Label name/category
132
- value: Optional label value
133
134
Returns:
135
Created label object
136
"""
137
138
def list(
139
self,
140
*,
141
log_id: Optional[str] = None,
142
label: Optional[str] = None,
143
**kwargs
144
) -> dict:
145
"""List labels with optional filtering"""
146
147
def update(
148
self,
149
label_id: str,
150
*,
151
value: Optional[str] = None,
152
**kwargs
153
) -> dict:
154
"""Update an existing label"""
155
156
def delete(
157
self,
158
label_id: str,
159
**kwargs
160
) -> dict:
161
"""Delete a label"""
162
163
class AsyncLabels:
164
"""Async request labeling"""
165
166
async def create(self, **kwargs) -> dict: ...
167
async def list(self, **kwargs) -> dict: ...
168
async def update(self, label_id: str, **kwargs) -> dict: ...
169
async def delete(self, label_id: str, **kwargs) -> dict: ...
170
```
171
172
## Usage Examples
173
174
### Basic Request Logging
175
176
```python
177
from portkey_ai import Portkey
178
179
# Initialize with observability enabled
180
portkey = Portkey(
181
api_key="PORTKEY_API_KEY",
182
virtual_key="VIRTUAL_KEY",
183
debug=True, # Enable detailed logging
184
metadata={
185
"environment": "production",
186
"user_id": "user123",
187
"session_id": "session456"
188
}
189
)
190
191
# Make a request (automatically logged)
192
response = portkey.chat.completions.create(
193
messages=[{"role": "user", "content": "Hello"}],
194
model="gpt-4",
195
metadata={
196
"request_type": "greeting",
197
"category": "customer_support"
198
}
199
)
200
201
# Retrieve recent logs
202
logs = portkey.logs.list(limit=10, order="desc")
203
print(f"Found {len(logs['data'])} recent requests")
204
205
for log in logs['data']:
206
print(f"Request {log['id']}: {log['model']} - {log['status']}")
207
print(f" Tokens: {log['usage']['total_tokens']}")
208
print(f" Duration: {log['duration_ms']}ms")
209
```
210
211
### Advanced Log Filtering
212
213
```python
214
from datetime import datetime, timedelta
215
216
# Filter logs by date range and criteria
217
yesterday = datetime.now() - timedelta(days=1)
218
logs = portkey.logs.list(
219
filters={
220
"date": {
221
"gte": yesterday.isoformat(),
222
"lte": datetime.now().isoformat()
223
},
224
"model": "gpt-4",
225
"status": "success",
226
"provider": "openai",
227
"metadata.environment": "production"
228
},
229
limit=50
230
)
231
232
# Analyze request patterns
233
total_requests = len(logs['data'])
234
total_tokens = sum(log['usage']['total_tokens'] for log in logs['data'])
235
avg_latency = sum(log['duration_ms'] for log in logs['data']) / total_requests
236
237
print(f"Analysis for last 24 hours:")
238
print(f" Total requests: {total_requests}")
239
print(f" Total tokens: {total_tokens}")
240
print(f" Average latency: {avg_latency:.2f}ms")
241
```
242
243
### Request Tracing
244
245
```python
246
import uuid
247
248
# Use trace IDs to track request flows
249
trace_id = str(uuid.uuid4())
250
251
# First request in trace
252
response1 = portkey.chat.completions.create(
253
messages=[{"role": "user", "content": "What is Python?"}],
254
model="gpt-4",
255
trace_id=trace_id,
256
metadata={"step": "initial_query"}
257
)
258
259
# Follow-up request in same trace
260
response2 = portkey.chat.completions.create(
261
messages=[
262
{"role": "user", "content": "What is Python?"},
263
{"role": "assistant", "content": response1.choices[0].message.content},
264
{"role": "user", "content": "Give me a code example"}
265
],
266
model="gpt-4",
267
trace_id=trace_id,
268
metadata={"step": "follow_up"}
269
)
270
271
# Retrieve all logs for this trace
272
trace_logs = portkey.logs.list(
273
filters={"trace_id": trace_id}
274
)
275
276
print(f"Trace {trace_id} has {len(trace_logs['data'])} requests")
277
```
278
279
### Response Analysis
280
281
```python
282
# Analyze response patterns and content
283
responses = portkey.responses.list(
284
filters={
285
"model": "gpt-4",
286
"date": {"gte": "2024-01-01"}
287
},
288
limit=100
289
)
290
291
# Analyze input/output patterns
292
for response in responses['data']:
293
response_detail = portkey.responses.retrieve(response['id'])
294
295
print(f"Response {response['id']}:")
296
print(f" Input tokens: {response_detail['input_tokens']}")
297
print(f" Output tokens: {response_detail['output_tokens']}")
298
print(f" Cost: ${response_detail['cost']:.4f}")
299
300
# Analyze input items
301
input_items = portkey.responses.input_items.list(
302
response_id=response['id']
303
)
304
305
# Analyze output items
306
output_items = portkey.responses.output_items.list(
307
response_id=response['id']
308
)
309
```
310
311
### Request Labeling and Categorization
312
313
```python
314
# Create labels for request categorization
315
recent_logs = portkey.logs.list(limit=20)
316
317
for log in recent_logs['data']:
318
# Auto-label based on content
319
if 'translate' in log['request']['messages'][0]['content'].lower():
320
portkey.labels.create(
321
log_id=log['id'],
322
label="category",
323
value="translation"
324
)
325
elif 'code' in log['request']['messages'][0]['content'].lower():
326
portkey.labels.create(
327
log_id=log['id'],
328
label="category",
329
value="coding"
330
)
331
332
# Label by performance
333
if log['duration_ms'] > 5000:
334
portkey.labels.create(
335
log_id=log['id'],
336
label="performance",
337
value="slow"
338
)
339
340
# Query logs by labels
341
coding_requests = portkey.logs.list(
342
filters={"labels.category": "coding"}
343
)
344
345
slow_requests = portkey.logs.list(
346
filters={"labels.performance": "slow"}
347
)
348
349
print(f"Found {len(coding_requests['data'])} coding requests")
350
print(f"Found {len(slow_requests['data'])} slow requests")
351
```
352
353
### Real-time Monitoring
354
355
```python
356
import time
357
358
def monitor_requests():
359
"""Monitor requests in real-time"""
360
last_check = datetime.now()
361
362
while True:
363
# Get new logs since last check
364
new_logs = portkey.logs.list(
365
filters={
366
"date": {"gte": last_check.isoformat()}
367
},
368
order="asc"
369
)
370
371
for log in new_logs['data']:
372
print(f"New request: {log['id']}")
373
print(f" Model: {log['model']}")
374
print(f" Status: {log['status']}")
375
print(f" Duration: {log['duration_ms']}ms")
376
print(f" Tokens: {log['usage']['total_tokens']}")
377
378
# Alert on errors
379
if log['status'] == 'error':
380
print(f" ERROR: {log['error']['message']}")
381
382
# Alert on slow requests
383
if log['duration_ms'] > 10000:
384
print(f" WARNING: Slow request ({log['duration_ms']}ms)")
385
386
last_check = datetime.now()
387
time.sleep(30) # Check every 30 seconds
388
389
# Run monitoring (in production, use proper async/threading)
390
# monitor_requests()
391
```
392
393
### Custom Analytics Dashboard
394
395
```python
396
from collections import defaultdict
397
398
def generate_analytics_report(days=7):
399
"""Generate comprehensive analytics report"""
400
401
# Get logs for specified period
402
start_date = datetime.now() - timedelta(days=days)
403
logs = portkey.logs.list(
404
filters={
405
"date": {"gte": start_date.isoformat()}
406
},
407
limit=1000
408
)
409
410
# Aggregate metrics
411
metrics = {
412
'total_requests': len(logs['data']),
413
'by_model': defaultdict(int),
414
'by_provider': defaultdict(int),
415
'by_status': defaultdict(int),
416
'total_tokens': 0,
417
'total_cost': 0,
418
'avg_latency': 0,
419
'error_rate': 0
420
}
421
422
latencies = []
423
errors = 0
424
425
for log in logs['data']:
426
metrics['by_model'][log['model']] += 1
427
metrics['by_provider'][log['provider']] += 1
428
metrics['by_status'][log['status']] += 1
429
metrics['total_tokens'] += log['usage']['total_tokens']
430
metrics['total_cost'] += log['cost']
431
latencies.append(log['duration_ms'])
432
433
if log['status'] == 'error':
434
errors += 1
435
436
metrics['avg_latency'] = sum(latencies) / len(latencies) if latencies else 0
437
metrics['error_rate'] = (errors / len(logs['data'])) * 100 if logs['data'] else 0
438
439
# Print report
440
print(f"Analytics Report ({days} days)")
441
print("=" * 40)
442
print(f"Total Requests: {metrics['total_requests']}")
443
print(f"Total Tokens: {metrics['total_tokens']:,}")
444
print(f"Total Cost: ${metrics['total_cost']:.2f}")
445
print(f"Average Latency: {metrics['avg_latency']:.0f}ms")
446
print(f"Error Rate: {metrics['error_rate']:.2f}%")
447
448
print("\nTop Models:")
449
for model, count in sorted(metrics['by_model'].items(), key=lambda x: x[1], reverse=True)[:5]:
450
print(f" {model}: {count} requests")
451
452
print("\nProviders:")
453
for provider, count in metrics['by_provider'].items():
454
print(f" {provider}: {count} requests")
455
456
return metrics
457
458
# Generate report
459
analytics = generate_analytics_report(days=30)
460
```
461
462
### Async Observability Operations
463
464
```python
465
import asyncio
466
from portkey_ai import AsyncPortkey
467
468
async def async_observability_example():
469
portkey = AsyncPortkey(
470
api_key="PORTKEY_API_KEY",
471
virtual_key="VIRTUAL_KEY"
472
)
473
474
# Make async request with metadata
475
response = await portkey.chat.completions.create(
476
messages=[{"role": "user", "content": "Hello async world"}],
477
model="gpt-4",
478
metadata={"async": "true", "test": "observability"}
479
)
480
481
# Async log retrieval
482
logs = await portkey.logs.list(limit=5)
483
484
# Async labeling
485
if logs['data']:
486
await portkey.labels.create(
487
log_id=logs['data'][0]['id'],
488
label="async_test",
489
value="true"
490
)
491
492
# Async response analysis
493
responses = await portkey.responses.list(limit=5)
494
for resp in responses['data']:
495
detail = await portkey.responses.retrieve(resp['id'])
496
print(f"Response {resp['id']}: {detail['input_tokens']} → {detail['output_tokens']} tokens")
497
498
asyncio.run(async_observability_example())
499
```
500
501
## Analytics Metrics
502
503
Portkey automatically tracks 40+ production-critical metrics including:
504
505
### Performance Metrics
506
- Request latency (p50, p95, p99)
507
- Throughput (requests per minute/hour)
508
- Error rates by provider/model
509
- Token usage and costs
510
511
### Usage Metrics
512
- Requests by model, provider, user
513
- Token consumption patterns
514
- Cost analysis and optimization
515
- Geographic distribution
516
517
### Quality Metrics
518
- Response quality scores
519
- User feedback integration
520
- A/B test results
521
- Conversion tracking
522
523
### Infrastructure Metrics
524
- Cache hit rates
525
- Fallback trigger frequency
526
- Load balancing distribution
527
- Provider availability