A Python library for collecting and sending telemetry data to Malti server
Project description
Malti Python SDK
A Python library for collecting and sending telemetry data to Malti server using any Starlette-compatible framework.
Features
- ๐ High Performance: Asynchronous batch processing with connection pooling
- ๐ Thread-Safe: Designed for multi-worker applications
- ๐ฏ Clean Mode: Automatically filters out bot traffic (401/404 responses)
- ๐ Multi-Framework: Works with any Starlette-compatible framework (FastAPI, Starlette, Responder, etc.)
- ๐ Rich Telemetry: Collects method, endpoint, status, response time, consumer, and context
- ๐ Automatic Batching: Efficient batching with overflow protection
- โก Non-Blocking: Telemetry collection doesn't impact request performance
- ๐ก๏ธ Retry Logic: Exponential backoff for failed requests
- ๐๏ธ Configurable: Extensive environment variable configuration
- ๐ง Framework Optimized: Enhanced integrations for popular frameworks
Installation
pip install malti-telemetry
Quick Start
FastAPI Integration
from fastapi import FastAPI
from malti_telemetry.middleware import MaltiMiddleware
app = FastAPI()
# Add telemetry middleware (route patterns automatically extracted!)
app.add_middleware(MaltiMiddleware)
@app.get("/users/{user_id}")
async def get_user(user_id: int):
return {"user_id": user_id, "name": "John Doe"}
# Recorded as: method=GET, endpoint="/users/{user_id}"
if __name__ == "__main__":
import uvicorn
uvicorn.run(app)
Starlette Integration
from starlette.applications import Starlette
from starlette.middleware import Middleware
from starlette.responses import JSONResponse
from malti_telemetry.middleware import MaltiMiddleware
app = Starlette()
# Add telemetry middleware (lifespan auto-injected!)
app.add_middleware(Middleware(MaltiMiddleware))
@app.route("/users/{user_id}")
async def get_user(request):
user_id = request.path_params["user_id"]
return JSONResponse({"user_id": user_id, "name": "John Doe"})
# Recorded as: method=GET, endpoint="/users/{user_id}"
if __name__ == "__main__":
import uvicorn
uvicorn.run(app)
Responder Integration
from responder import API
from malti_telemetry.middleware import MaltiMiddleware
api = API()
# Add telemetry middleware (generic Starlette middleware works with Responder)
api.add_middleware(MaltiMiddleware)
@api.route("/users/{user_id}")
async def get_user(req, resp, *, user_id):
resp.media = {"user_id": user_id, "name": "John Doe"}
Generic Starlette Middleware
from starlette.applications import Starlette
from starlette.middleware import Middleware
from malti_telemetry.middleware import MaltiMiddleware
app = Starlette()
# Add telemetry middleware (works with any Starlette framework)
app.add_middleware(Middleware(MaltiMiddleware))
@app.route("/api/data")
async def get_data(request):
return JSONResponse({"data": "example"})
Environment Configuration
Set these environment variables before starting your application:
export MALTI_API_KEY="your-api-key-here"
export MALTI_SERVICE_NAME="my-fastapi-app"
export MALTI_URL="https://your-malti-server.com"
export MALTI_NODE="production-node-1"
Configuration
Environment Variables
| Variable | Default | Description |
|---|---|---|
MALTI_API_KEY |
(required) | Your Malti API key |
MALTI_SERVICE_NAME |
"unknown-service" |
Name of your service |
MALTI_URL |
"http://localhost:8000" |
Malti server URL |
MALTI_NODE |
"unknown-node" |
Node identifier |
MALTI_BATCH_SIZE |
500 |
Records per batch |
MALTI_BATCH_INTERVAL |
60.0 |
Seconds between batch sends |
MALTI_MAX_RETRIES |
3 |
Max retry attempts |
MALTI_RETRY_DELAY |
1.0 |
Base retry delay (seconds) |
MALTI_HTTP_TIMEOUT |
30.0 |
HTTP request timeout |
MALTI_MAX_KEEPALIVE_CONNECTIONS |
5 |
Max keepalive connections |
MALTI_MAX_CONNECTIONS |
10 |
Max total connections |
MALTI_OVERFLOW_THRESHOLD_PERCENT |
90.0 |
Buffer overflow threshold |
MALTI_CLEAN_MODE |
true |
Ignore 401/404 responses |
Programmatic Configuration
from malti_telemetry import configure_malti
configure_malti(
service_name="my-service",
api_key="your-api-key",
malti_url="https://api.malti.dev",
node="prod-web-01",
batch_size=1000,
clean_mode=True
)
Advanced Usage
Framework-Specific Features
FastAPI Features
Route Pattern Extraction: Automatic conversion of actual paths to route patterns:
/users/123โ/users/{user_id}/api/v1/posts/456/commentsโ/api/v1/posts/{post_id}/comments- Works with nested routes and mount points
Context Information: Add context using FastAPI's request state:
from fastapi import Request
@app.route("/users/{user_id}")
async def get_user(request):
user_id = request.path_params["user_id"]
if user_id < 1000:
request.state.context = "legacy"
else:
request.state.context = "current"
return JSONResponse({"user_id": user_id, "name": "John Doe"})
Starlette Features
Automatic Lifespan Management: Telemetry system starts/stops automatically:
from starlette.applications import Starlette
from starlette.middleware import Middleware
from malti_telemetry.middleware import MaltiMiddleware
app = Starlette()
app.add_middleware(Middleware(MaltiMiddleware)) # Lifespan auto-injected!
# No need for manual lifespan management!
Route Pattern Extraction: Automatically extracts route patterns from Starlette routing:
from starlette.applications import Starlette
from starlette.middleware import Middleware
from starlette.routing import Route, Mount
from malti_telemetry.middleware import MaltiMiddleware
app = Starlette()
app.add_middleware(Middleware(MaltiMiddleware))
async def user_handler(request):
return JSONResponse({"user_id": request.path_params["user_id"]})
async def post_handler(request):
return JSONResponse({"post_id": request.path_params["post_id"]})
# Works with all Starlette routing patterns
app.routes = [
Route("/api/v1/users/{user_id}", endpoint=user_handler),
Mount("/api/v2", routes=[
Route("/posts/{post_id}", endpoint=post_handler),
]),
]
# Automatically recorded as:
# method=GET, endpoint="/api/v1/users/{user_id}"
# method=GET, endpoint="/api/v2/posts/{post_id}"
Consumer Identification
Malti automatically extracts consumer information from headers:
x-consumer-idheader (highest priority)x-user-idheader (fallback)consumer-idheaderuser-idheader
Custom Consumer Extraction: Set consumer information in your framework:
# FastAPI
@app.middleware("http")
async def set_consumer(request: Request, call_next):
request.state.malti_consumer = "app"
return await call_next(request)
# Starlette
@app.middleware("http")
async def set_consumer(request, call_next):
# Add consumer to ASGI scope
request.scope["state"]["malti_consumer"] = "api"
response = await call_next(request)
return response
Manual Telemetry Recording
from malti_telemetry import get_telemetry_system
telemetry = get_telemetry_system()
# Record a custom event
telemetry.record_request(
method="GET",
endpoint="/api/custom",
status=200,
response_time=150,
consumer="custom-client",
context="manual-recording"
)
Statistics and Monitoring
from malti_telemetry import get_malti_stats
stats = get_malti_stats()
print(stats)
# {
# 'total_added': 1250,
# 'total_sent': 1200,
# 'total_failed': 50,
# 'current_size': 50,
# 'max_size': 25000,
# 'service_name': 'my-service',
# 'running': True
# }
Supported Frameworks
Malti Telemetry works with any Starlette-compatible framework:
- FastAPI: Enhanced route pattern extraction and request.state integration
- Starlette: Base middleware with full functionality and lifespan management
- Responder: Works with generic Starlette middleware
- Any ASGI framework: Generic middleware for custom implementations
Architecture
Core Components
- TelemetryCollector: Collects HTTP request telemetry data
- BatchSender: Sends batched telemetry data to Malti server
- TelemetrySystem: Combines collector and sender with unified interface
- TelemetryBuffer: Thread-safe buffer for storing records
Worker Process Model
Each FastAPI/Uvicorn worker process gets its own telemetry system instance:
โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโ
โ Worker Process โ โ Worker Process โ
โ โโโโโโโโโโโโโโโ โ โ โโโโโโโโโโโโโโโ โ
โ โTelemetrySys โ โ โ โTelemetrySys โ โ
โ โ โโโโโโโโโโโ โ โ โ โ โโโโโโโโโโโ โ โ
โ โ โBuffer โ โ โ โ โ โBuffer โ โ โ
โ โ โโโโโโโโโโโ โ โ โ โ โโโโโโโโโโโ โ โ
โ โ โโโโโโโโโโโ โ โ โ โ โโโโโโโโโโโ โ โ
โ โ โSender โ โ โ โ โ โSender โ โ โ
โ โ โโโโโโโโโโโ โ โ โ โ โโโโโโโโโโโ โ โ
โ โโโโโโโโโโโโโโโ โ โ โโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโ
Development
Setup
git clone https://github.com/muzy/malti-telemetry.git
cd python/
pip install -e ".[dev]"
Testing
pytest
Code Quality
black malti_telemetry/
isort malti_telemetry/
mypy malti_telemetry/
flake8 malti_telemetry/
License
MIT License - see LICENSE file for details.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file malti_telemetry-1.0.2.tar.gz.
File metadata
- Download URL: malti_telemetry-1.0.2.tar.gz
- Upload date:
- Size: 22.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
148d974a6042ea99b031e2c99767b5f3bbf7b9eb84a0486c70b5d80d572487cf
|
|
| MD5 |
9d0bb1571d5580545c800a7e38087dc9
|
|
| BLAKE2b-256 |
8b8c53d793682e538e45890cc69d01dd7a57fada599e881c110c3218ec3636b1
|
Provenance
The following attestation bundles were made for malti_telemetry-1.0.2.tar.gz:
Publisher:
publish-to-pypi.yml on muzy/malti-telemetry
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
malti_telemetry-1.0.2.tar.gz -
Subject digest:
148d974a6042ea99b031e2c99767b5f3bbf7b9eb84a0486c70b5d80d572487cf - Sigstore transparency entry: 507862404
- Sigstore integration time:
-
Permalink:
muzy/malti-telemetry@ea0727312cd2ba4eb638e85f9e5467a144b797b2 -
Branch / Tag:
refs/tags/v1.0.2 - Owner: https://github.com/muzy
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-to-pypi.yml@ea0727312cd2ba4eb638e85f9e5467a144b797b2 -
Trigger Event:
release
-
Statement type:
File details
Details for the file malti_telemetry-1.0.2-py3-none-any.whl.
File metadata
- Download URL: malti_telemetry-1.0.2-py3-none-any.whl
- Upload date:
- Size: 13.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
167aa05f31ab91f8c664c6e295277cc701c579fc863a1cb6bdd455b28fa2c4ce
|
|
| MD5 |
99bff8b9a7e95123ff037bb85e12ef67
|
|
| BLAKE2b-256 |
321c9fbcfc1a85bb5d224b5c2f430a901922021382b18e2ca3920c9e94e4ac49
|
Provenance
The following attestation bundles were made for malti_telemetry-1.0.2-py3-none-any.whl:
Publisher:
publish-to-pypi.yml on muzy/malti-telemetry
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
malti_telemetry-1.0.2-py3-none-any.whl -
Subject digest:
167aa05f31ab91f8c664c6e295277cc701c579fc863a1cb6bdd455b28fa2c4ce - Sigstore transparency entry: 507862423
- Sigstore integration time:
-
Permalink:
muzy/malti-telemetry@ea0727312cd2ba4eb638e85f9e5467a144b797b2 -
Branch / Tag:
refs/tags/v1.0.2 - Owner: https://github.com/muzy
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-to-pypi.yml@ea0727312cd2ba4eb638e85f9e5467a144b797b2 -
Trigger Event:
release
-
Statement type: