Metadata-Version: 2.4
Name: flare-sdk
Version: 0.2.0
Summary: Cliente leve para enviar logs e requests ao Flare (observabilidade da lunacheckout).
Project-URL: Homepage, https://github.com/flyrecheckout/flare-sdk
Project-URL: Repository, https://github.com/flyrecheckout/flare-sdk
Author-email: Gabriel <ondarks360@gmail.com>
License: MIT
License-File: LICENSE
Keywords: fastapi,flare,logging,observability,telemetry
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: System :: Logging
Classifier: Typing :: Typed
Requires-Python: >=3.9
Provides-Extra: dev
Requires-Dist: httpx>=0.27; extra == 'dev'
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: starlette>=0.27; extra == 'dev'
Provides-Extra: fastapi
Requires-Dist: starlette>=0.27; extra == 'fastapi'
Description-Content-Type: text/markdown

# flare-sdk

Cliente leve para enviar logs e requests ao [Flare](https://github.com/flyrecheckout/Flare),
o sistema de observabilidade da lunacheckout.

Uma app instrumentada manda telemetria para `POST /ingest` com o token da sua
source; o Flare recebe, resolve `token → source` e grava. Este pacote é o lado
_cliente_ desse contrato — a parte que **envia**.

```
[sua API]  ──flare-sdk──>  POST /ingest (Bearer token)  ──>  [Flare]  ──>  dashboard
```

## Princípios

- **Nunca derruba a sua app.** Toda falha de envio (Flare fora do ar, rede caída,
  token errado) é engolida. Telemetria quebrada não pode virar um 500 na sua API.
- **Nunca bloqueia a request.** Os eventos entram numa fila e uma thread de
  background os entrega em lote. Se a fila enche, o evento é descartado — nunca se
  segura o request esperando o Flare.
- **Zero dependências no core.** O transporte usa a stdlib (`urllib`). `pip install
  flare-sdk` não puxa mais nada. O middleware de FastAPI é um extra opcional que
  nem sequer importa framework novo (fala ASGI direto).
- **Sobrevive ao fork.** uvicorn/gunicorn forkam workers; o SDK recria a thread de
  entrega por processo, sem você pensar nisso.

## Instalação

```bash
pip install flare-sdk
# com o middleware de FastAPI (opcional; não puxa dependência nova):
pip install "flare-sdk[fastapi]"
```

## Começo rápido

O jeito mais comum: pluge no `logging` que você já usa. Uma linha, e tudo que a
app já loga passa a chegar ao Flare.

```python
import logging
from flare_sdk import FlareHandler

logging.getLogger().addHandler(
    FlareHandler(
        token="seu-source-token",
        endpoint="https://flare.lunacheckout.com/ingest",
    )
)

logging.getLogger("checkout").info(
    "pagamento aprovado", extra={"order_id": 42, "gateway": "pagarme"}
)
```

`order_id` e `gateway` viram **atributos** pesquisáveis no Flare — não texto
espremido na mensagem.

### Configuração por ambiente

`token` e `endpoint` caem para as variáveis `FLARE_TOKEN` e `FLARE_INGEST_URL`
quando omitidos. Assim você liga o SDK sem tocar no código:

```bash
export FLARE_TOKEN="seu-source-token"
export FLARE_INGEST_URL="https://flare.lunacheckout.com/ingest"
```

```python
from flare_sdk import FlareHandler
logging.getLogger().addHandler(FlareHandler())  # lê do ambiente
```

## Instrumentando requests (FastAPI / Starlette)

Uma linha registra `method`, `path`, `status_code` e `duration_ms` de cada request.
As rotas são agrupadas pelo _template_ (`/orders/{id}`), não pelo id concreto —
senão a tela de métricas explodiria em cardinalidade.

```python
from fastapi import FastAPI
from flare_sdk import Flare
from flare_sdk.fastapi import FlareMiddleware

app = FastAPI()
flare = Flare()  # token/endpoint do ambiente

app.add_middleware(FlareMiddleware, client=flare)
```

Se a rota levantar, a request é registrada como `500` e a exceção é **re-levantada**
— o middleware observa, não sequestra o seu erro.

## Enviando eventos à mão

Além do handler, você pode mandar logs, requests ou qualquer evento direto:

```python
from flare_sdk import Flare

flare = Flare(token="...", endpoint="https://flare.lunacheckout.com/ingest")

flare.log("job iniciado", severity="INFO", job="reconciliação")
flare.request("POST", "/charge", 201, duration_ms=87.4, gateway="pagarme")
flare.capture({"message": "evento cru", "severity": "DEBUG", "qualquer": "coisa"})
```

Um mesmo cliente serve o handler e as chamadas manuais — passe-o ao handler para
compartilhar uma fila só:

```python
handler = FlareHandler(client=flare)
```

## Severidade

Use os nomes de sempre do `logging` (o Flare os entende todos): `DEBUG`, `INFO`,
`WARNING`, `ERROR`, `CRITICAL`. No modelo OTel do Flare, **erro** é tudo com número
`>= 17` (ERROR e CRITICAL/FATAL).

## Referência de configuração

| Parâmetro            | Default                | O que faz                                             |
| -------------------- | ---------------------- | ----------------------------------------------------- |
| `token`              | `$FLARE_TOKEN`         | Token da source (Bearer). Obrigatório.                |
| `endpoint`           | `$FLARE_INGEST_URL`    | URL completa do `/ingest`. Obrigatório.               |
| `batch_size`         | `100`                  | Máximo de eventos por POST.                           |
| `flush_interval`     | `2.0`                  | Segundos até mandar um lote parcial.                  |
| `max_queue`          | `10000`                | Teto da fila; além disso, descarta (ver `dropped`).   |
| `timeout`            | `5.0`                  | Timeout de cada POST, em segundos.                    |
| `max_retries`        | `3`                    | Retries de erro transiente (5xx/rede), com backoff.   |
| `default_attributes` | `{}`                   | Atributos anexados a todo evento (ex.: `service`).    |
| `on_error`           | `None`                 | Callback `(exc) -> None` para observar falhas de envio. |

Erros permanentes (4xx: token inválido, lote malformado, corpo grande demais) são
descartados **sem** retry — repetir mandaria o mesmo 4xx.

## Encerramento limpo

O cliente registra um `atexit` que drena a fila no fim do processo. Em jobs curtos,
force o envio com `flush()` ou use o context manager:

```python
with Flare() as flare:
    flare.log("job de 1 tiro")
# sai daqui com a fila drenada

flare.flush(timeout=5)  # ou explicitamente
```

## Observando a saúde do próprio SDK

```python
flare = Flare(..., on_error=lambda exc: logging.getLogger("flare").warning("envio falhou: %s", exc))
...
flare.dropped  # quantos eventos foram descartados por fila cheia (sinal de saturação)
```

## Desenvolvimento

```bash
pip install -e ".[dev]"
pytest
```

Nenhum teste toca a rede — o transporte é substituído por um dublê. Cobertura
mínima 85%.

## Licença

MIT.
