logtide-sdk 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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Polliog
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,615 @@
1
+ Metadata-Version: 2.4
2
+ Name: logtide-sdk
3
+ Version: 0.1.0
4
+ Summary: Official Python SDK for LogTide - Self-hosted log management with batching, retry logic, circuit breaker, query API, and middleware support
5
+ Author-email: Polliog <giuseppe@solture.it>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/logtide/python-sdk
8
+ Project-URL: Repository, https://github.com/logtide/python-sdk
9
+ Project-URL: Documentation, https://logtide.dev/docs
10
+ Project-URL: Issues, https://github.com/logtide/python-sdk/issues
11
+ Keywords: logtide,logging,logs,log-management,monitoring,observability,self-hosted,tracing,circuit-breaker,flask,django,fastapi
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.8
17
+ Classifier: Programming Language :: Python :: 3.9
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Topic :: System :: Logging
22
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
23
+ Requires-Python: >=3.8
24
+ Description-Content-Type: text/markdown
25
+ License-File: LICENSE
26
+ Requires-Dist: requests>=2.31.0
27
+ Provides-Extra: async
28
+ Requires-Dist: aiohttp>=3.9.0; extra == "async"
29
+ Provides-Extra: dev
30
+ Requires-Dist: pytest>=7.4.0; extra == "dev"
31
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
32
+ Requires-Dist: black>=23.0.0; extra == "dev"
33
+ Requires-Dist: mypy>=1.5.0; extra == "dev"
34
+ Requires-Dist: ruff>=0.1.0; extra == "dev"
35
+ Requires-Dist: types-requests>=2.31.0; extra == "dev"
36
+ Provides-Extra: flask
37
+ Requires-Dist: flask>=2.0.0; extra == "flask"
38
+ Provides-Extra: django
39
+ Requires-Dist: django>=3.2.0; extra == "django"
40
+ Provides-Extra: fastapi
41
+ Requires-Dist: fastapi>=0.100.0; extra == "fastapi"
42
+ Requires-Dist: starlette>=0.27.0; extra == "fastapi"
43
+ Dynamic: license-file
44
+
45
+ # LogTide Python SDK
46
+
47
+ Official Python SDK for LogTide with advanced features: automatic batching, retry logic, circuit breaker, query API, live streaming, and middleware support.
48
+
49
+ ## Features
50
+
51
+ - ✅ **Automatic batching** with configurable size and interval
52
+ - ✅ **Retry logic** with exponential backoff
53
+ - ✅ **Circuit breaker** pattern for fault tolerance
54
+ - ✅ **Max buffer size** with drop policy to prevent memory leaks
55
+ - ✅ **Query API** for searching and filtering logs
56
+ - ✅ **Live tail** with Server-Sent Events (SSE)
57
+ - ✅ **Trace ID context** for distributed tracing
58
+ - ✅ **Global metadata** added to all logs
59
+ - ✅ **Structured error serialization**
60
+ - ✅ **Internal metrics** (logs sent, errors, latency, etc.)
61
+ - ✅ **Flask, Django & FastAPI middleware** for auto-logging HTTP requests
62
+ - ✅ **Full Python 3.8+ support** with type hints
63
+
64
+ ## Requirements
65
+
66
+ - Python 3.8 or higher
67
+ - pip or poetry
68
+
69
+ ## Installation
70
+
71
+ ```bash
72
+ pip install logtide-sdk
73
+ ```
74
+
75
+ ### Optional dependencies
76
+
77
+ ```bash
78
+ # For async support
79
+ pip install logtide-sdk[async]
80
+
81
+ # For Flask middleware
82
+ pip install logtide-sdk[flask]
83
+
84
+ # For Django middleware
85
+ pip install logtide-sdk[django]
86
+
87
+ # For FastAPI middleware
88
+ pip install logtide-sdk[fastapi]
89
+
90
+ # Install all extras
91
+ pip install logtide-sdk[async,flask,django,fastapi]
92
+ ```
93
+
94
+ ## Quick Start
95
+
96
+ ```python
97
+ from logtide_sdk import LogTideClient, ClientOptions
98
+
99
+ client = LogTideClient(
100
+ ClientOptions(
101
+ api_url='http://localhost:8080',
102
+ api_key='lp_your_api_key_here',
103
+ )
104
+ )
105
+
106
+ # Send logs
107
+ client.info('api-gateway', 'Server started', {'port': 3000})
108
+ client.error('database', 'Connection failed', Exception('Timeout'))
109
+
110
+ # Graceful shutdown (automatic via atexit, but can be called manually)
111
+ client.close()
112
+ ```
113
+
114
+ ---
115
+
116
+ ## Configuration Options
117
+
118
+ ### Basic Options
119
+
120
+ | Option | Type | Default | Description |
121
+ |--------|------|---------|-------------|
122
+ | `api_url` | `str` | **required** | Base URL of your LogTide instance |
123
+ | `api_key` | `str` | **required** | Project API key (starts with `lp_`) |
124
+ | `batch_size` | `int` | `100` | Number of logs to batch before sending |
125
+ | `flush_interval` | `int` | `5000` | Interval in ms to auto-flush logs |
126
+
127
+ ### Advanced Options
128
+
129
+ | Option | Type | Default | Description |
130
+ |--------|------|---------|-------------|
131
+ | `max_buffer_size` | `int` | `10000` | Max logs in buffer (prevents memory leak) |
132
+ | `max_retries` | `int` | `3` | Max retry attempts on failure |
133
+ | `retry_delay_ms` | `int` | `1000` | Initial retry delay (exponential backoff) |
134
+ | `circuit_breaker_threshold` | `int` | `5` | Failures before opening circuit |
135
+ | `circuit_breaker_reset_ms` | `int` | `30000` | Time before retrying after circuit opens |
136
+ | `enable_metrics` | `bool` | `True` | Track internal metrics |
137
+ | `debug` | `bool` | `False` | Enable debug logging to console |
138
+ | `global_metadata` | `dict` | `{}` | Metadata added to all logs |
139
+ | `auto_trace_id` | `bool` | `False` | Auto-generate trace IDs for logs |
140
+
141
+ ### Example: Full Configuration
142
+
143
+ ```python
144
+ import os
145
+
146
+ client = LogTideClient(
147
+ ClientOptions(
148
+ api_url='http://localhost:8080',
149
+ api_key='lp_your_api_key_here',
150
+
151
+ # Batching
152
+ batch_size=100,
153
+ flush_interval=5000,
154
+
155
+ # Buffer management
156
+ max_buffer_size=10000,
157
+
158
+ # Retry with exponential backoff (1s → 2s → 4s)
159
+ max_retries=3,
160
+ retry_delay_ms=1000,
161
+
162
+ # Circuit breaker
163
+ circuit_breaker_threshold=5,
164
+ circuit_breaker_reset_ms=30000,
165
+
166
+ # Metrics & debugging
167
+ enable_metrics=True,
168
+ debug=True,
169
+
170
+ # Global context
171
+ global_metadata={
172
+ 'env': os.getenv('APP_ENV'),
173
+ 'version': '1.0.0',
174
+ 'hostname': os.uname().nodename,
175
+ },
176
+
177
+ # Auto trace IDs
178
+ auto_trace_id=False,
179
+ )
180
+ )
181
+ ```
182
+
183
+ ---
184
+
185
+ ## Logging Methods
186
+
187
+ ### Basic Logging
188
+
189
+ ```python
190
+ from logtide_sdk import LogLevel
191
+
192
+ client.debug('service-name', 'Debug message')
193
+ client.info('service-name', 'Info message', {'userId': 123})
194
+ client.warn('service-name', 'Warning message')
195
+ client.error('service-name', 'Error message', {'custom': 'data'})
196
+ client.critical('service-name', 'Critical message')
197
+ ```
198
+
199
+ ### Error Logging with Auto-Serialization
200
+
201
+ The SDK automatically serializes `Exception` objects:
202
+
203
+ ```python
204
+ try:
205
+ raise RuntimeError('Database timeout')
206
+ except Exception as e:
207
+ # Automatically serializes error with stack trace
208
+ client.error('database', 'Query failed', e)
209
+ ```
210
+
211
+ Generated log metadata:
212
+ ```json
213
+ {
214
+ "error": {
215
+ "name": "RuntimeError",
216
+ "message": "Database timeout",
217
+ "stack": "Traceback (most recent call last):\n ..."
218
+ }
219
+ }
220
+ ```
221
+
222
+ ---
223
+
224
+ ## Trace ID Context
225
+
226
+ Track requests across services with trace IDs.
227
+
228
+ ### Manual Trace ID
229
+
230
+ ```python
231
+ client.set_trace_id('request-123')
232
+
233
+ client.info('api', 'Request received')
234
+ client.info('database', 'Querying users')
235
+ client.info('api', 'Response sent')
236
+
237
+ client.set_trace_id(None) # Clear context
238
+ ```
239
+
240
+ ### Scoped Trace ID (Context Manager)
241
+
242
+ ```python
243
+ with client.with_trace_id('request-456'):
244
+ client.info('api', 'Processing in context')
245
+ client.warn('cache', 'Cache miss')
246
+ # Trace ID automatically restored after context
247
+ ```
248
+
249
+ ### Auto-Generated Trace ID
250
+
251
+ ```python
252
+ import uuid
253
+
254
+ with client.with_new_trace_id():
255
+ client.info('worker', 'Background job started')
256
+ client.info('worker', 'Job completed')
257
+ ```
258
+
259
+ ---
260
+
261
+ ## Query API
262
+
263
+ Search and retrieve logs programmatically.
264
+
265
+ ### Basic Query
266
+
267
+ ```python
268
+ from datetime import datetime, timedelta
269
+ from logtide_sdk import QueryOptions, LogLevel
270
+
271
+ result = client.query(
272
+ QueryOptions(
273
+ service='api-gateway',
274
+ level=LogLevel.ERROR,
275
+ from_time=datetime.now() - timedelta(hours=24),
276
+ to_time=datetime.now(),
277
+ limit=100,
278
+ offset=0,
279
+ )
280
+ )
281
+
282
+ print(f"Found {result.total} logs")
283
+ for log in result.logs:
284
+ print(log)
285
+ ```
286
+
287
+ ### Full-Text Search
288
+
289
+ ```python
290
+ result = client.query(QueryOptions(q='timeout', limit=50))
291
+ ```
292
+
293
+ ### Get Logs by Trace ID
294
+
295
+ ```python
296
+ logs = client.get_by_trace_id('trace-123')
297
+ print(f"Trace has {len(logs)} logs")
298
+ ```
299
+
300
+ ### Aggregated Statistics
301
+
302
+ ```python
303
+ from datetime import datetime, timedelta
304
+ from logtide_sdk import AggregatedStatsOptions
305
+
306
+ stats = client.get_aggregated_stats(
307
+ AggregatedStatsOptions(
308
+ from_time=datetime.now() - timedelta(days=7),
309
+ to_time=datetime.now(),
310
+ interval='1h',
311
+ )
312
+ )
313
+
314
+ for service in stats.top_services:
315
+ print(f"{service['service']}: {service['count']} logs")
316
+ ```
317
+
318
+ ---
319
+
320
+ ## Live Streaming (SSE)
321
+
322
+ Stream logs in real-time using Server-Sent Events.
323
+
324
+ ```python
325
+ def handle_log(log):
326
+ print(f"[{log['time']}] {log['level']}: {log['message']}")
327
+
328
+ def handle_error(error):
329
+ print(f"Stream error: {error}")
330
+
331
+ client.stream(
332
+ on_log=handle_log,
333
+ on_error=handle_error,
334
+ filters={
335
+ 'service': 'api-gateway',
336
+ 'level': 'error',
337
+ }
338
+ )
339
+
340
+ # Note: This blocks. Run in separate thread for production.
341
+ ```
342
+
343
+ ---
344
+
345
+ ## Metrics
346
+
347
+ Track SDK performance and health.
348
+
349
+ ```python
350
+ metrics = client.get_metrics()
351
+
352
+ print(f"Logs sent: {metrics.logs_sent}")
353
+ print(f"Logs dropped: {metrics.logs_dropped}")
354
+ print(f"Errors: {metrics.errors}")
355
+ print(f"Retries: {metrics.retries}")
356
+ print(f"Avg latency: {metrics.avg_latency_ms}ms")
357
+ print(f"Circuit breaker trips: {metrics.circuit_breaker_trips}")
358
+
359
+ # Get circuit breaker state
360
+ print(client.get_circuit_breaker_state()) # CLOSED|OPEN|HALF_OPEN
361
+
362
+ # Reset metrics
363
+ client.reset_metrics()
364
+ ```
365
+
366
+ ---
367
+
368
+ ## Middleware
369
+
370
+ ### Flask Middleware
371
+
372
+ Auto-log all HTTP requests and responses.
373
+
374
+ ```python
375
+ from flask import Flask
376
+ from logtide_sdk import LogTideClient, ClientOptions
377
+ from logtide_sdk.middleware import LogTideFlaskMiddleware
378
+
379
+ app = Flask(__name__)
380
+
381
+ client = LogTideClient(
382
+ ClientOptions(
383
+ api_url='http://localhost:8080',
384
+ api_key='lp_your_api_key_here',
385
+ )
386
+ )
387
+
388
+ LogTideFlaskMiddleware(
389
+ app,
390
+ client=client,
391
+ service_name='flask-api',
392
+ log_requests=True,
393
+ log_responses=True,
394
+ skip_paths=['/metrics'],
395
+ )
396
+ ```
397
+
398
+ **Logged automatically:**
399
+ - Request: `GET /api/users`
400
+ - Response: `GET /api/users 200 (45ms)`
401
+ - Errors: `Request error: Internal Server Error`
402
+
403
+ ### Django Middleware
404
+
405
+ ```python
406
+ # settings.py
407
+ MIDDLEWARE = [
408
+ 'logtide_sdk.middleware.LogTideDjangoMiddleware',
409
+ ]
410
+
411
+ from logtide_sdk import LogTideClient, ClientOptions
412
+
413
+ LOGTIDE_CLIENT = LogTideClient(
414
+ ClientOptions(
415
+ api_url='http://localhost:8080',
416
+ api_key='lp_your_api_key_here',
417
+ )
418
+ )
419
+ LOGTIDE_SERVICE_NAME = 'django-api'
420
+ ```
421
+
422
+ ### FastAPI Middleware
423
+
424
+ ```python
425
+ from fastapi import FastAPI
426
+ from logtide_sdk import LogTideClient, ClientOptions
427
+ from logtide_sdk.middleware import LogTideFastAPIMiddleware
428
+
429
+ app = FastAPI()
430
+
431
+ client = LogTideClient(
432
+ ClientOptions(
433
+ api_url='http://localhost:8080',
434
+ api_key='lp_your_api_key_here',
435
+ )
436
+ )
437
+
438
+ app.add_middleware(
439
+ LogTideFastAPIMiddleware,
440
+ client=client,
441
+ service_name='fastapi-api',
442
+ )
443
+ ```
444
+
445
+ ---
446
+
447
+ ## Examples
448
+
449
+ See the [examples/](./examples) directory for complete working examples:
450
+
451
+ - **[basic.py](./examples/basic.py)** - Simple usage
452
+ - **[advanced.py](./examples/advanced.py)** - All advanced features
453
+ - **[flask_example.py](./examples/flask_example.py)** - Flask integration
454
+ - **[fastapi_example.py](./examples/fastapi_example.py)** - FastAPI integration
455
+
456
+ ---
457
+
458
+ ## API Reference
459
+
460
+ ### LogTideClient
461
+
462
+ #### Constructor
463
+ ```python
464
+ client = LogTideClient(options: ClientOptions)
465
+ ```
466
+
467
+ #### Logging Methods
468
+ - `log(entry: LogEntry) -> None`
469
+ - `debug(service: str, message: str, metadata: dict = None) -> None`
470
+ - `info(service: str, message: str, metadata: dict = None) -> None`
471
+ - `warn(service: str, message: str, metadata: dict = None) -> None`
472
+ - `error(service: str, message: str, metadata_or_error: dict | Exception = None) -> None`
473
+ - `critical(service: str, message: str, metadata_or_error: dict | Exception = None) -> None`
474
+
475
+ #### Context Methods
476
+ - `set_trace_id(trace_id: str | None) -> None`
477
+ - `get_trace_id() -> str | None`
478
+ - `with_trace_id(trace_id: str)` → context manager
479
+ - `with_new_trace_id()` → context manager
480
+
481
+ #### Query Methods
482
+ - `query(options: QueryOptions) -> LogsResponse`
483
+ - `get_by_trace_id(trace_id: str) -> list[dict]`
484
+ - `get_aggregated_stats(options: AggregatedStatsOptions) -> AggregatedStatsResponse`
485
+
486
+ #### Streaming
487
+ - `stream(on_log: callable, on_error: callable = None, filters: dict = None) -> None`
488
+
489
+ #### Metrics
490
+ - `get_metrics() -> ClientMetrics`
491
+ - `reset_metrics() -> None`
492
+ - `get_circuit_breaker_state() -> CircuitState`
493
+
494
+ #### Lifecycle
495
+ - `flush() -> None`
496
+ - `close() -> None`
497
+
498
+ ---
499
+
500
+ ## Best Practices
501
+
502
+ ### 1. Always Close on Shutdown
503
+
504
+ ```python
505
+ import atexit
506
+
507
+ # Automatic cleanup (already registered by client)
508
+ # Or manually:
509
+ atexit.register(client.close)
510
+ ```
511
+
512
+ ### 2. Use Global Metadata
513
+
514
+ ```python
515
+ client = LogTideClient(
516
+ ClientOptions(
517
+ api_url='http://localhost:8080',
518
+ api_key='lp_your_api_key_here',
519
+ global_metadata={
520
+ 'env': os.getenv('ENV'),
521
+ 'version': '1.0.0',
522
+ 'region': 'us-east-1',
523
+ },
524
+ )
525
+ )
526
+ ```
527
+
528
+ ### 3. Enable Debug Mode in Development
529
+
530
+ ```python
531
+ client = LogTideClient(
532
+ ClientOptions(
533
+ api_url='http://localhost:8080',
534
+ api_key='lp_your_api_key_here',
535
+ debug=os.getenv('ENV') == 'development',
536
+ )
537
+ )
538
+ ```
539
+
540
+ ### 4. Monitor Metrics in Production
541
+
542
+ ```python
543
+ import time
544
+ import threading
545
+
546
+ def monitor_metrics():
547
+ while True:
548
+ metrics = client.get_metrics()
549
+
550
+ if metrics.logs_dropped > 0:
551
+ print(f"⚠️ Logs dropped: {metrics.logs_dropped}")
552
+
553
+ if metrics.circuit_breaker_trips > 0:
554
+ print("🔴 Circuit breaker is OPEN!")
555
+
556
+ time.sleep(60)
557
+
558
+ # Run in background thread
559
+ monitor_thread = threading.Thread(target=monitor_metrics, daemon=True)
560
+ monitor_thread.start()
561
+ ```
562
+
563
+ ---
564
+
565
+ ## Development
566
+
567
+ ### Setup
568
+
569
+ ```bash
570
+ # Clone repository
571
+ git clone https://github.com/logtide/python-sdk.git
572
+ cd logtide-sdk-python
573
+
574
+ # Create virtual environment
575
+ python -m venv venv
576
+ source venv/bin/activate # On Windows: venv\Scripts\activate
577
+
578
+ # Install dev dependencies
579
+ pip install -e ".[dev]"
580
+ ```
581
+
582
+ ### Testing
583
+
584
+ ```bash
585
+ # Run tests
586
+ pytest tests/
587
+
588
+ # Type checking
589
+ mypy logtide_sdk/
590
+
591
+ # Code formatting
592
+ black logtide_sdk/ tests/ examples/
593
+
594
+ # Linting
595
+ ruff check logtide_sdk/
596
+ ```
597
+
598
+ ---
599
+
600
+ ## License
601
+
602
+ MIT
603
+
604
+ ---
605
+
606
+ ## Contributing
607
+
608
+ Contributions are welcome! Please open an issue or PR on [GitHub](https://github.com/logtide/python-sdk).
609
+
610
+ ---
611
+
612
+ ## Support
613
+
614
+ - **Documentation**: [https://logtide.dev/docs](https://logtide.dev/docs)
615
+ - **Issues**: [GitHub Issues](https://github.com/logtide/python-sdk/issues)