Skip to main content

A Python library for collecting and sending telemetry data to Malti server

Project description

Malti Python SDK

PyPI version Python versions License: MIT

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.muzy.dev"
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.muzy.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:

  1. x-consumer-id header
  2. x-user-id header
  3. consumer-id header
  4. user-id header

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

  1. TelemetryCollector: Collects HTTP request telemetry data
  2. BatchSender: Sends batched telemetry data to Malti server
  3. TelemetrySystem: Combines collector and sender with unified interface
  4. 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

malti_telemetry-1.0.3.tar.gz (22.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

malti_telemetry-1.0.3-py3-none-any.whl (13.7 kB view details)

Uploaded Python 3

File details

Details for the file malti_telemetry-1.0.3.tar.gz.

File metadata

  • Download URL: malti_telemetry-1.0.3.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

Hashes for malti_telemetry-1.0.3.tar.gz
Algorithm Hash digest
SHA256 f1f3a2c926a4b68ffa4118ecbabbb2ecce06bdc5d39f48d39943541f7067f456
MD5 9a4f8fe411c1d0f6e36282d343274604
BLAKE2b-256 b79744fdc84f755ade882fff732196a43bd6ac8fc4a1fa0a0e064b28e8a6b368

See more details on using hashes here.

Provenance

The following attestation bundles were made for malti_telemetry-1.0.3.tar.gz:

Publisher: publish-to-pypi.yml on muzy/malti-telemetry

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file malti_telemetry-1.0.3-py3-none-any.whl.

File metadata

File hashes

Hashes for malti_telemetry-1.0.3-py3-none-any.whl
Algorithm Hash digest
SHA256 f4a605d36b867baa57a85b1750592cde1d3f766ba1d412c59d7b2a27c2b6aeea
MD5 5b8cfdaf41f15a8364f196d24d0315bf
BLAKE2b-256 31e4f3b16023807c8d751773a3be72c78308931097be9a8b2adb8f47b6b1c6a6

See more details on using hashes here.

Provenance

The following attestation bundles were made for malti_telemetry-1.0.3-py3-none-any.whl:

Publisher: publish-to-pypi.yml on muzy/malti-telemetry

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page