malti-telemetry


Namemalti-telemetry JSON
Version 1.0.4 PyPI version JSON
download
home_pageNone
SummaryA Python library for collecting and sending telemetry data to Malti server
upload_time2025-10-19 09:51:45
maintainerNone
docs_urlNone
authorNone
requires_python>=3.11
licenseNone
keywords telemetry monitoring fastapi analytics api self-hosted starlette
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # Malti Python SDK

[![PyPI version](https://badge.fury.io/py/malti-telemetry.svg)](https://pypi.org/project/malti-telemetry/)
[![Python versions](https://img.shields.io/pypi/pyversions/malti-telemetry.svg)](https://pypi.org/project/malti-telemetry/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/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
- 🌐 **IP Consumer Extraction**: Optional IP address extraction from X-Forwarded-For with anonymization

## Installation

```bash
pip install malti-telemetry
```

## Quick Start

### FastAPI Integration

```python
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

```python
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

```python
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

```python
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:

```bash
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"

# Optional: Enable IP address consumer extraction
export MALTI_USE_IP_AS_CONSUMER=true
export MALTI_IP_ANONYMIZE=true
```

## 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 |
| `MALTI_USE_IP_AS_CONSUMER` | `false` | Use IP address as consumer fallback |
| `MALTI_IP_ANONYMIZE` | `false` | Anonymize IP addresses (simple octet masking) |

### Programmatic Configuration

```python
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,
    use_ip_as_consumer=True,
    ip_anonymize=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:

```python
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:

```python
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:

```python
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

**IP Address as Consumer**: When no consumer headers are found, you can configure Malti to use IP addresses as a fallback:

```python
# Enable IP address extraction from X-Forwarded-For header
configure_malti(
    use_ip_as_consumer=True,
    ip_anonymize=True  # Optional: anonymize IPs for privacy
)

# Or via environment variables
export MALTI_USE_IP_AS_CONSUMER=true
export MALTI_IP_ANONYMIZE=true
```

**IP Anonymization**: When enabled, IP addresses are anonymized using simple octet masking:
- IPv4: `192.168.1.100` β†’ `192.168.1.xxx`
- IPv6: `2001:db8:85a3:8d3:1319:8a2e:370:7348` β†’ `2001:db8:85a3:8d3:xxxx:xxxx:xxxx:xxxx`

**IP Extraction Priority**:
1. X-Forwarded-For header (uses first/leftmost IP)
2. Direct client IP (fallback)

**Custom Consumer Extraction**: Set consumer information in your framework:

```python
# 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

```python
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

```python
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

```bash
git clone https://github.com/muzy/malti-telemetry.git
cd python/
pip install -e ".[dev]"
```

### Testing

```bash
pytest
```

### Code Quality

```bash
black malti_telemetry/
isort malti_telemetry/
mypy malti_telemetry/
flake8 malti_telemetry/
```

## License

MIT License - see [LICENSE](LICENSE) file for details.

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "malti-telemetry",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.11",
    "maintainer_email": "smuzy <malti@muzy.dev>",
    "keywords": "telemetry, monitoring, fastapi, analytics, api, self-hosted, starlette",
    "author": null,
    "author_email": null,
    "download_url": "https://files.pythonhosted.org/packages/ca/89/81d70e54fc2657183f6bd5ec0e4b4436158d559373ec09c84b23d4ee6301/malti_telemetry-1.0.4.tar.gz",
    "platform": null,
    "description": "# Malti Python SDK\n\n[![PyPI version](https://badge.fury.io/py/malti-telemetry.svg)](https://pypi.org/project/malti-telemetry/)\n[![Python versions](https://img.shields.io/pypi/pyversions/malti-telemetry.svg)](https://pypi.org/project/malti-telemetry/)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nA Python library for collecting and sending telemetry data to Malti server using any Starlette-compatible framework.\n\n## Features\n\n- \ud83d\ude80 **High Performance**: Asynchronous batch processing with connection pooling\n- \ud83d\udd12 **Thread-Safe**: Designed for multi-worker applications\n- \ud83c\udfaf **Clean Mode**: Automatically filters out bot traffic (401/404 responses)\n- \ud83c\udf1f **Multi-Framework**: Works with any Starlette-compatible framework (FastAPI, Starlette, Responder, etc.)\n- \ud83d\udcca **Rich Telemetry**: Collects method, endpoint, status, response time, consumer, and context\n- \ud83d\udd04 **Automatic Batching**: Efficient batching with overflow protection\n- \u26a1 **Non-Blocking**: Telemetry collection doesn't impact request performance\n- \ud83d\udee1\ufe0f **Retry Logic**: Exponential backoff for failed requests\n- \ud83c\udf9b\ufe0f **Configurable**: Extensive environment variable configuration\n- \ud83d\udd27 **Framework Optimized**: Enhanced integrations for popular frameworks\n- \ud83c\udf10 **IP Consumer Extraction**: Optional IP address extraction from X-Forwarded-For with anonymization\n\n## Installation\n\n```bash\npip install malti-telemetry\n```\n\n## Quick Start\n\n### FastAPI Integration\n\n```python\nfrom fastapi import FastAPI\nfrom malti_telemetry.middleware import MaltiMiddleware\n\napp = FastAPI()\n\n# Add telemetry middleware (route patterns automatically extracted!)\napp.add_middleware(MaltiMiddleware)\n\n@app.get(\"/users/{user_id}\")\nasync def get_user(user_id: int):\n    return {\"user_id\": user_id, \"name\": \"John Doe\"}\n\n# Recorded as: method=GET, endpoint=\"/users/{user_id}\"\n\nif __name__ == \"__main__\":\n    import uvicorn\n    uvicorn.run(app)\n```\n\n### Starlette Integration\n\n```python\nfrom starlette.applications import Starlette\nfrom starlette.middleware import Middleware\nfrom starlette.responses import JSONResponse\nfrom malti_telemetry.middleware import MaltiMiddleware\n\napp = Starlette()\n\n# Add telemetry middleware (lifespan auto-injected!)\napp.add_middleware(Middleware(MaltiMiddleware))\n\n@app.route(\"/users/{user_id}\")\nasync def get_user(request):\n    user_id = request.path_params[\"user_id\"]\n    return JSONResponse({\"user_id\": user_id, \"name\": \"John Doe\"})\n\n# Recorded as: method=GET, endpoint=\"/users/{user_id}\"\n\nif __name__ == \"__main__\":\n    import uvicorn\n    uvicorn.run(app)\n```\n\n### Responder Integration\n\n```python\nfrom responder import API\nfrom malti_telemetry.middleware import MaltiMiddleware\n\napi = API()\n\n# Add telemetry middleware (generic Starlette middleware works with Responder)\napi.add_middleware(MaltiMiddleware)\n\n@api.route(\"/users/{user_id}\")\nasync def get_user(req, resp, *, user_id):\n    resp.media = {\"user_id\": user_id, \"name\": \"John Doe\"}\n```\n\n### Generic Starlette Middleware\n\n```python\nfrom starlette.applications import Starlette\nfrom starlette.middleware import Middleware\nfrom malti_telemetry.middleware import MaltiMiddleware\n\napp = Starlette()\n\n# Add telemetry middleware (works with any Starlette framework)\napp.add_middleware(Middleware(MaltiMiddleware))\n\n@app.route(\"/api/data\")\nasync def get_data(request):\n    return JSONResponse({\"data\": \"example\"})\n```\n\n### Environment Configuration\n\nSet these environment variables before starting your application:\n\n```bash\nexport MALTI_API_KEY=\"your-api-key-here\"\nexport MALTI_SERVICE_NAME=\"my-fastapi-app\"\nexport MALTI_URL=\"https://your-malti-server.muzy.dev\"\nexport MALTI_NODE=\"production-node-1\"\n\n# Optional: Enable IP address consumer extraction\nexport MALTI_USE_IP_AS_CONSUMER=true\nexport MALTI_IP_ANONYMIZE=true\n```\n\n## Configuration\n\n### Environment Variables\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| `MALTI_API_KEY` | *(required)* | Your Malti API key |\n| `MALTI_SERVICE_NAME` | `\"unknown-service\"` | Name of your service |\n| `MALTI_URL` | `\"http://localhost:8000\"` | Malti server URL |\n| `MALTI_NODE` | `\"unknown-node\"` | Node identifier |\n| `MALTI_BATCH_SIZE` | `500` | Records per batch |\n| `MALTI_BATCH_INTERVAL` | `60.0` | Seconds between batch sends |\n| `MALTI_MAX_RETRIES` | `3` | Max retry attempts |\n| `MALTI_RETRY_DELAY` | `1.0` | Base retry delay (seconds) |\n| `MALTI_HTTP_TIMEOUT` | `30.0` | HTTP request timeout |\n| `MALTI_MAX_KEEPALIVE_CONNECTIONS` | `5` | Max keepalive connections |\n| `MALTI_MAX_CONNECTIONS` | `10` | Max total connections |\n| `MALTI_OVERFLOW_THRESHOLD_PERCENT` | `90.0` | Buffer overflow threshold |\n| `MALTI_CLEAN_MODE` | `true` | Ignore 401/404 responses |\n| `MALTI_USE_IP_AS_CONSUMER` | `false` | Use IP address as consumer fallback |\n| `MALTI_IP_ANONYMIZE` | `false` | Anonymize IP addresses (simple octet masking) |\n\n### Programmatic Configuration\n\n```python\nfrom malti_telemetry import configure_malti\n\nconfigure_malti(\n    service_name=\"my-service\",\n    api_key=\"your-api-key\",\n    malti_url=\"https://api.malti.muzy.dev\",\n    node=\"prod-web-01\",\n    batch_size=1000,\n    clean_mode=True,\n    use_ip_as_consumer=True,\n    ip_anonymize=True\n)\n```\n\n## Advanced Usage\n\n### Framework-Specific Features\n\n#### FastAPI Features\n\n**Route Pattern Extraction**: Automatic conversion of actual paths to route patterns:\n- `/users/123` \u2192 `/users/{user_id}`\n- `/api/v1/posts/456/comments` \u2192 `/api/v1/posts/{post_id}/comments`\n- Works with nested routes and mount points\n\n**Context Information**: Add context using FastAPI's request state:\n\n```python\nfrom fastapi import Request\n\n@app.route(\"/users/{user_id}\")\nasync def get_user(request):\n    user_id = request.path_params[\"user_id\"]\n    if user_id < 1000:\n        request.state.context = \"legacy\"\n    else:\n        request.state.context = \"current\"\n    return JSONResponse({\"user_id\": user_id, \"name\": \"John Doe\"})\n```\n\n#### Starlette Features\n\n**Automatic Lifespan Management**: Telemetry system starts/stops automatically:\n\n```python\nfrom starlette.applications import Starlette\nfrom starlette.middleware import Middleware\nfrom malti_telemetry.middleware import MaltiMiddleware\n\napp = Starlette()\napp.add_middleware(Middleware(MaltiMiddleware))  # Lifespan auto-injected!\n\n# No need for manual lifespan management!\n```\n\n**Route Pattern Extraction**: Automatically extracts route patterns from Starlette routing:\n\n```python\nfrom starlette.applications import Starlette\nfrom starlette.middleware import Middleware\nfrom starlette.routing import Route, Mount\nfrom malti_telemetry.middleware import MaltiMiddleware\n\napp = Starlette()\napp.add_middleware(Middleware(MaltiMiddleware))\n\nasync def user_handler(request):\n    return JSONResponse({\"user_id\": request.path_params[\"user_id\"]})\n\nasync def post_handler(request):\n    return JSONResponse({\"post_id\": request.path_params[\"post_id\"]})\n\n# Works with all Starlette routing patterns\napp.routes = [\n    Route(\"/api/v1/users/{user_id}\", endpoint=user_handler),\n    Mount(\"/api/v2\", routes=[\n        Route(\"/posts/{post_id}\", endpoint=post_handler),\n    ]),\n]\n\n# Automatically recorded as:\n# method=GET, endpoint=\"/api/v1/users/{user_id}\"\n# method=GET, endpoint=\"/api/v2/posts/{post_id}\"\n```\n\n### Consumer Identification\n\nMalti automatically extracts consumer information from headers:\n\n1. `x-consumer-id` header \n2. `x-user-id` header \n3. `consumer-id` header\n4. `user-id` header\n\n**IP Address as Consumer**: When no consumer headers are found, you can configure Malti to use IP addresses as a fallback:\n\n```python\n# Enable IP address extraction from X-Forwarded-For header\nconfigure_malti(\n    use_ip_as_consumer=True,\n    ip_anonymize=True  # Optional: anonymize IPs for privacy\n)\n\n# Or via environment variables\nexport MALTI_USE_IP_AS_CONSUMER=true\nexport MALTI_IP_ANONYMIZE=true\n```\n\n**IP Anonymization**: When enabled, IP addresses are anonymized using simple octet masking:\n- IPv4: `192.168.1.100` \u2192 `192.168.1.xxx`\n- IPv6: `2001:db8:85a3:8d3:1319:8a2e:370:7348` \u2192 `2001:db8:85a3:8d3:xxxx:xxxx:xxxx:xxxx`\n\n**IP Extraction Priority**:\n1. X-Forwarded-For header (uses first/leftmost IP)\n2. Direct client IP (fallback)\n\n**Custom Consumer Extraction**: Set consumer information in your framework:\n\n```python\n# FastAPI\n@app.middleware(\"http\")\nasync def set_consumer(request: Request, call_next):\n    request.state.malti_consumer = \"app\"\n    return await call_next(request)\n\n# Starlette\n@app.middleware(\"http\")\nasync def set_consumer(request, call_next):\n    # Add consumer to ASGI scope\n    request.scope[\"state\"][\"malti_consumer\"] = \"api\"\n    response = await call_next(request)\n    return response\n```\n\n### Manual Telemetry Recording\n\n```python\nfrom malti_telemetry import get_telemetry_system\n\ntelemetry = get_telemetry_system()\n\n# Record a custom event\ntelemetry.record_request(\n    method=\"GET\",\n    endpoint=\"/api/custom\",\n    status=200,\n    response_time=150,\n    consumer=\"custom-client\",\n    context=\"manual-recording\"\n)\n```\n\n### Statistics and Monitoring\n\n```python\nfrom malti_telemetry import get_malti_stats\n\nstats = get_malti_stats()\nprint(stats)\n# {\n#     'total_added': 1250,\n#     'total_sent': 1200,\n#     'total_failed': 50,\n#     'current_size': 50,\n#     'max_size': 25000,\n#     'service_name': 'my-service',\n#     'running': True\n# }\n```\n\n## Supported Frameworks\n\nMalti Telemetry works with any Starlette-compatible framework:\n\n- **FastAPI**: Enhanced route pattern extraction and request.state integration\n- **Starlette**: Base middleware with full functionality and lifespan management\n- **Responder**: Works with generic Starlette middleware\n- **Any ASGI framework**: Generic middleware for custom implementations\n\n### Architecture\n\n#### Core Components\n\n1. **TelemetryCollector**: Collects HTTP request telemetry data\n2. **BatchSender**: Sends batched telemetry data to Malti server\n3. **TelemetrySystem**: Combines collector and sender with unified interface\n4. **TelemetryBuffer**: Thread-safe buffer for storing records\n\n### Worker Process Model\n\nEach FastAPI/Uvicorn worker process gets its own telemetry system instance:\n\n```\n\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510    \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502 Worker Process  \u2502    \u2502 Worker Process  \u2502\n\u2502 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u2502    \u2502 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u2502\n\u2502 \u2502TelemetrySys \u2502 \u2502    \u2502 \u2502TelemetrySys \u2502 \u2502\n\u2502 \u2502 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u2502 \u2502    \u2502 \u2502 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u2502 \u2502\n\u2502 \u2502 \u2502Buffer   \u2502 \u2502 \u2502    \u2502 \u2502 \u2502Buffer   \u2502 \u2502 \u2502\n\u2502 \u2502 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2502 \u2502    \u2502 \u2502 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2502 \u2502\n\u2502 \u2502 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u2502 \u2502    \u2502 \u2502 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u2502 \u2502\n\u2502 \u2502 \u2502Sender   \u2502 \u2502 \u2502    \u2502 \u2502 \u2502Sender   \u2502 \u2502 \u2502\n\u2502 \u2502 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2502 \u2502    \u2502 \u2502 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2502 \u2502\n\u2502 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2502    \u2502 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518    \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n```\n\n## Development\n\n### Setup\n\n```bash\ngit clone https://github.com/muzy/malti-telemetry.git\ncd python/\npip install -e \".[dev]\"\n```\n\n### Testing\n\n```bash\npytest\n```\n\n### Code Quality\n\n```bash\nblack malti_telemetry/\nisort malti_telemetry/\nmypy malti_telemetry/\nflake8 malti_telemetry/\n```\n\n## License\n\nMIT License - see [LICENSE](LICENSE) file for details.\n",
    "bugtrack_url": null,
    "license": null,
    "summary": "A Python library for collecting and sending telemetry data to Malti server",
    "version": "1.0.4",
    "project_urls": {
        "Changelog": "https://github.com/muzy/malti-telemetry/releases",
        "Homepage": "https://github.com/muzy/malti-telemetry/",
        "Issues": "https://github.com/muzy/malti-telemetry/issues",
        "Repository": "https://github.com/muzy/malti-telemetry/"
    },
    "split_keywords": [
        "telemetry",
        " monitoring",
        " fastapi",
        " analytics",
        " api",
        " self-hosted",
        " starlette"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "ba935c43dbd8636310da15ade683b5d0a5d0e59f097d01720aa99844398de752",
                "md5": "64a9e74a4bf73d7d3435e77aa5844532",
                "sha256": "84bb140cd1ba2cb8982dfc26ef590901deb72ed03e6da5503ae6107c15538915"
            },
            "downloads": -1,
            "filename": "malti_telemetry-1.0.4-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "64a9e74a4bf73d7d3435e77aa5844532",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.11",
            "size": 15301,
            "upload_time": "2025-10-19T09:51:44",
            "upload_time_iso_8601": "2025-10-19T09:51:44.293679Z",
            "url": "https://files.pythonhosted.org/packages/ba/93/5c43dbd8636310da15ade683b5d0a5d0e59f097d01720aa99844398de752/malti_telemetry-1.0.4-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "ca8981d70e54fc2657183f6bd5ec0e4b4436158d559373ec09c84b23d4ee6301",
                "md5": "3097f868c9cb5bad1cf3ad46171d6735",
                "sha256": "c0b09e7914c71e90e6aa1e7ee0315023db0ab9b5b499dd70efacb04a5cdfb0d7"
            },
            "downloads": -1,
            "filename": "malti_telemetry-1.0.4.tar.gz",
            "has_sig": false,
            "md5_digest": "3097f868c9cb5bad1cf3ad46171d6735",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.11",
            "size": 26034,
            "upload_time": "2025-10-19T09:51:45",
            "upload_time_iso_8601": "2025-10-19T09:51:45.451348Z",
            "url": "https://files.pythonhosted.org/packages/ca/89/81d70e54fc2657183f6bd5ec0e4b4436158d559373ec09c84b23d4ee6301/malti_telemetry-1.0.4.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-10-19 09:51:45",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "muzy",
    "github_project": "malti-telemetry",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "malti-telemetry"
}
        
Elapsed time: 1.95034s