Metadata-Version: 2.5
Name: maping-client
Version: 0.1.1
Summary: Zero-config RED-metrics client for FastAPI/Starlette, reporting to a mAPI-ng collector
Project-URL: Homepage, https://github.com/arhuman/maping-python
Project-URL: mAPI-ng core, https://github.com/arhuman/maping
Author: Arnaud ASSAD
License-Expression: MIT
License-File: LICENSE
Requires-Python: >=3.10
Requires-Dist: httpx>=0.27
Requires-Dist: protobuf>=5.0
Requires-Dist: psutil>=5.9
Provides-Extra: dev
Requires-Dist: fastapi>=0.110; extra == 'dev'
Requires-Dist: mypy>=1.11; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: ruff>=0.6; extra == 'dev'
Requires-Dist: uvicorn>=0.30; extra == 'dev'
Provides-Extra: zstd
Requires-Dist: zstandard>=0.22; extra == 'zstd'
Description-Content-Type: text/markdown

# maping-client

Zero-config RED-metrics client for FastAPI/Starlette services, reporting to a
[mAPI-ng](https://github.com/arhuman/maping) collector.

Open source. Start free on the [hosted service](https://www.mapi-ng.com) (no
card), or self-host the complete MIT stack.

Aggregates per-endpoint rate/errors/duration into a DDSketch client-side and
ships batched summaries over the Connect unary protocol, matching the wire
contract used by mAPI-ng's Go clients (`maping/client`). No `MAPING_KEY` set
means the middleware is a no-op: safe to add to any app.

## Hosted or self-hosted

This client behaves identically either way: only the env vars you set differ.

- **Hosted, forever free, no card.** Sign up at
  [mapi-ng.com](https://www.mapi-ng.com), create a key, set `MAPING_KEY`.
  That is the whole setup: the client already defaults to the hosted ingest
  endpoint, so `MAPING_ENDPOINT` is not needed.
- **Self-hosted.** Run the complete MIT stack yourself
  (`make local` for dev, `make up` for prod, see
  [arhuman/maping](https://github.com/arhuman/maping)). Set `MAPING_KEY` for
  your own instance and `MAPING_ENDPOINT` to point at it.

Nothing here is held back to push you toward the hosted plan: the wire
contract, the server, and this client are all MIT. Start hosted and move to
self-hosting later, or the reverse, at any time. Convenience, not lock-in.

## Status

Early development. Wire contract is pinned from
`arhuman/maping`'s `proto/maping/v1/maping.proto` via `scripts/sync-proto.sh`;
see that repo's `docs/adr/` for the design rationale behind the wire format
(DDSketch, ADR-0001; Connect protocol, ADR-0002).

## Quickstart

Set your credentials (pick one; see "Hosted or self-hosted" above):

```bash
# Hosted (forever free, no card)
export MAPING_KEY="mk_live_..."

# Self-hosted
export MAPING_KEY="mk_live_..."
export MAPING_ENDPOINT="https://your-collector.internal"
```

Then the same code either way:

```python
from contextlib import asynccontextmanager

from fastapi import FastAPI
from maping import Recorder
from maping.asgi import MapingMiddleware

recorder = Recorder()  # reads MAPING_KEY, and MAPING_ENDPOINT if self-hosting, from env


@asynccontextmanager
async def lifespan(_: FastAPI):
    await recorder.start()
    yield
    await recorder.shutdown()  # must run AFTER the server stops accepting requests


fastapi_app = FastAPI(lifespan=lifespan)

# Wrap the WHOLE app -- not app.add_middleware() -- so this sits outside
# Starlette's ServerErrorMiddleware and observes the real final status even
# after an unhandled exception. See src/maping/asgi.py for why.
app = MapingMiddleware(fastapi_app, recorder=recorder)
```

See `examples/fastapi_app.py` for a fuller quickstart, including labelled
errors, aborted requests, and downstream-call timing.

## Running tests

```bash
git clone https://github.com/arhuman/maping-python.git
cd maping-python
pip install -e ".[dev,zstd]"

ruff check src/ tests/ examples/
mypy src/
pytest tests/ -v
```

Same three commands the CI workflow runs on every push and PR (see
`.github/workflows/_test.yml`, the reusable workflow both `ci.yml` and
`release.yml` call).

## Contributing

- Open an issue or PR against
  [arhuman/maping-python](https://github.com/arhuman/maping-python).
- Keep changes scoped: one concern per PR, with tests. `ruff check`, `mypy`,
  and `pytest` all have to pass before review; CI enforces this on every PR.
- The wire contract under `src/maping/proto/v1/` is pinned from the core
  `arhuman/maping` repo via `scripts/sync-proto.sh` (see `PINNED_REF` for the
  exact pin). Don't hand-edit the generated `maping_pb2.py`/`maping_pb2.pyi`;
  change the pin and re-run the sync script instead.

## Fidelity notes

- RED metrics (rate, errors, duration, DDSketch percentiles) match the Go
  client's wire contract exactly.
- InstanceWindow/USE gauges (CPU, memory, GC, goroutine-equivalents) are
  **best-effort approximations** of the Go runtime stats the field names were
  originally defined for; Python's GC/concurrency model has no exact
  equivalent to Go's runtime introspection. See `src/maping/_sampler.py`.
