Metadata-Version: 2.5
Name: hub-error-logs-sdk
Version: 0.1.3
Summary: SDK Python para enviar logs de erro das plataformas Mupi à Caixa de Logs do mupihub.
Author: Mupi Systems
License: Proprietary
Keywords: django,error-tracking,logging,mupihub,observability
Classifier: Framework :: Django
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: System :: Logging
Requires-Python: >=3.9
Requires-Dist: django>=4.2
Provides-Extra: dev
Requires-Dist: pytest-django>=4.5; extra == 'dev'
Requires-Dist: pytest>=7.0; extra == 'dev'
Description-Content-Type: text/markdown

# hub-error-logs-sdk

SDK Python para uma plataforma Django **passar a enviar seus erros** para a
**Caixa de Logs** do Mupi Hub (o inbox de erros estilo Sentry do `mupihub`),
com esforço quase zero — sem escrever view, middleware ou `LOGGING` à mão.

Depois de instalar e configurar a chave, **erros não tratados (HTTP 500),
`DisallowedHost` e qualquer `logger.error`/`logger.exception`** passam a fluir
automaticamente para um inbox central — com stacktrace e **contexto de request
estruturado** (método, rota, headers e query sanitizados, status, usuário) —
facilitando o debug.

> Pacote de import: **`hublogs`** · Distribuição (pip): **`hub-error-logs-sdk`**

---

## Instalação

Via GitHub (recomendado por enquanto):

```bash
pip install hub-error-logs-sdk
```

Para atualizar (forçando o pip a baixar o commit novo, sem cache):

```bash
pip install --force-reinstall --no-cache-dir --no-deps \
  hub-error-logs-sdk
```

> Após atualizar, **reinicie o servidor** (`runserver`/gunicorn). O `pip install`
> não recarrega um processo que já está rodando.

---

## Uso — setup de 2 passos

### 1. Gere um token de serviço (uma vez por plataforma)

No mupihub, logado como admin → **Tokens da API** → gere um token (`user=None`,
escopo de serviço). O token aparece **uma única vez** (`mhb_...`). Guarde como
segredo, ex. numa variável de ambiente. **Nunca comite o token.**

### 2. Configure o `settings.py`

```python
import os

INSTALLED_APPS += ["hublogs"]

MUPIHUB_LOGS = {
    "token": "mhb_...",   # mhb_...
    "source": "eagenda",                         # slug curto da plataforma
    "environment": "", #producao/homolog
}
```

Pronto. No boot, o SDK liga sozinho:
- o **handler de logging** (no root logger e, quando necessário, direto em
  `django.request`/`django.security`);
- o **middleware** de contexto de request (`hublogs.middleware.RequestContext`,
  injetado em `settings.MIDDLEWARE`).

### Testando

```bash
python manage.py shell -c "
import logging
try:
    1/0
except ZeroDivisionError:
    logging.getLogger('django.request').error('teste de integração', exc_info=True)
"
```

O erro deve aparecer no inbox em segundos. Repetir o mesmo erro **não** cria itens
novos — incrementa o contador do grupo (dedup por fingerprint, feito no servidor).

---

## Ambientes (dev × prod)

O endpoint é escolhido **automaticamente** pelo `settings.DEBUG` da plataforma:

| `settings.DEBUG` | Destino dos logs |
|---|---|
| `True`  | mupihub **local**: `http://127.0.0.1:8080/moop/api/v1/logs` |
| `False` | **produção**: `https://dev.mupisystems.com.br/moop/api/v1/logs` |

- Um `endpoint` definido explicitamente (em `MUPIHUB_LOGS["endpoint"]` ou na env
  `MUPIHUB_LOG_ENDPOINT`) **sempre prevalece**, ignorando o `DEBUG`.
- O destino de debug é ajustável via `MUPIHUB_LOGS["debug_endpoint"]`
  (ou `MUPIHUB_LOG_DEBUG_ENDPOINT`), caso seu mupihub local rode em outra porta.

> Com `DEBUG=True`, confira o inbox **local** (`http://127.0.0.1:8080/moop/logs/`),
> não o de produção. Os grupos aparecem na org **dona do token**.

---

## Privacidade e dados sensíveis

Logar é "best-effort", mas **vazar segredo/PII nunca**. O que o SDK faz por padrão:

**✅ Redigido automaticamente** (vira `[Filtered]`), por nome de chave, em headers,
query string, `extra` e tags — em inglês e PT-BR:
`password`/`senha`, `authorization`, `cookie`, `token`/`access_token`/`refresh_token`,
`api_key`, `secret`, `csrf`, `sessionid`, `private_key`, `credit_card`/`cartao`/`cvv`,
`cpf`, `cnpj`, `telefone`, `ssn`. (Lista ajustável em `scrub_fields`.)

**✅ PII do usuário desligada por padrão** (`send_default_pii=False`): só o
`user.id` é enviado. **Email, username e IP** só vão se você ativar
`send_default_pii=True`.

**✅ Nunca capturado:** corpo do request (body), dados de formulário/POST, valor
do cookie (o header `Cookie` é redigido), e **variáveis locais do stacktrace**
(o stacktrace é só os frames, sem valores de variáveis).

**⚠️ Atenção — não é sanitizado:** o **texto da mensagem** (`message`), o **valor
da exceção** (`exception.value`) e a **rota** (`path` — ex.: `/reset/<token>/`)
são texto livre e **não passam pelo scrub** (só a *query string* é redigida por
chave). Não coloque segredos/PII em mensagens de erro nem em segmentos de URL
(ex.: evite `logger.error(f"senha inválida: {senha}")`). Isso vale para qualquer
ferramenta de error tracking.

### Personalizar

```python
MUPIHUB_LOGS = {
    "token": os.environ["MUPIHUB_LOG_TOKEN"],
    "source": "eagenda",

    # adiciona termos à lista padrão de redação (substring, case-insensitive)
    "scrub_fields": ("password", "senha", "token", "cpf", "cnpj", "rg_numero", "pin"),

    # ative só se realmente precisar de email/username/IP do usuário no inbox
    "send_default_pii": False,
}
```

> `scrub_fields` **substitui** a lista padrão — inclua os termos padrão que quiser
> manter. Casamento é por substring no nome da chave (normalizado), então prefira
> termos específicos (`cpf`, `senha`) a curtos demais (`rg` casaria "o**rg**anization").

---

## Captura manual (opcional)

Além da captura automática, há uma API explícita:

```python
import hublogs

hublogs.set_user({"id": 7})                 # contexto do request atual
hublogs.set_tag("feature", "checkout")
hublogs.add_context("order_id", 1042)

try:
    processa()
except Exception:
    hublogs.capture_exception()             # envia a exceção atual + stacktrace

hublogs.capture_message("algo estranho", level="warning")
hublogs.flush(timeout=5)                     # força o envio do que está na fila
```

---

## Configuração

`MUPIHUB_LOGS` (dict no `settings.py`). Token e alguns campos também aceitam
variável de ambiente (a env preenche quando o campo não está no dict).

| Chave | Default | Env | Descrição |
|---|---|---|---|
| `token` | — (obrigatório) | `MUPIHUB_LOG_TOKEN` | Token de serviço (`mhb_...`). |
| `source` | — (obrigatório) | `MUPIHUB_LOG_SOURCE` | Slug curto da plataforma. |
| `environment` | `""` | `MUPIHUB_LOG_ENVIRONMENT` / `ENV` | Ex.: `production`, `staging`. |
| `release` | `""` | `MUPIHUB_LOG_RELEASE` / `RELEASE` | Versão/commit. |
| `endpoint` | auto (prod) | `MUPIHUB_LOG_ENDPOINT` | Sobrepõe o `DEBUG`. |
| `debug_endpoint` | `http://127.0.0.1:8080/moop/api/v1/logs` | `MUPIHUB_LOG_DEBUG_ENDPOINT` | Usado quando `DEBUG=True`. |
| `level` | `ERROR` | — | Nível mínimo capturado. |
| `send_default_pii` | `False` | — | Inclui email/username/IP do usuário. |
| `scrub_fields` | lista padrão | — | Termos sensíveis a redigir. |
| `capture_loggers` | `("django.request", "django.security")` | — | Loggers anexados direto quando não propagam ao root. |
| `auto_middleware` | `True` | — | Injeta o `RequestContext` no `MIDDLEWARE`. |
| `verify_token` | `True` | — | Ping em `/auth/me/` no boot (avisa token inválido). |
| `enabled` | `True` | — | Desliga o SDK quando `False`. |
| `batch_size` | `50` | — | Eventos por requisição (cap do servidor: 200). |
| `flush_interval` | `2.0` | — | Segundos entre flushes da fila. |
| `max_queue` | `1000` | — | Tamanho da fila em memória (descarta quando cheia). |
| `timeout` | `5.0` | — | Timeout de rede (s). |

---

## Garantias de robustez

- **Nunca derruba nem bloqueia o app host.** Envio é em lote, numa thread daemon;
  fila cheia → descarta; erro de rede → engole (sem retry-storm).
- **Funciona com `LOGGING` customizado.** Mesmo que o projeto tenha
  `django.request`/`django.security` com `propagate=False`, o handler é anexado
  direto neles. E o SDK se **re-anexa** caso o Django reconfigure o logging (ex.:
  o `runserver`), então a captura não "some".
- **Respeita os limites do servidor:** ≤ 200 eventos e ≤ 64 KB por requisição
  (eventos grandes são truncados); back-off em `429`.
- **Dedup é no servidor:** o SDK só encaminha; você pode mandar um `fingerprint`
  próprio via `capture_*`.

---

## Troubleshooting

| Sintoma | Causa provável / solução |
|---|---|
| Nada chega no inbox | Confira a versão instalada: `python -c "import hublogs; print(hublogs.__version__)"`. Atualize com `--force-reinstall --no-cache-dir` e **reinicie o servidor**. |
| Chega no shell mas não no `runserver` | Servidor rodando código antigo em memória → **reinicie** o `runserver`. |
| `202 ok:true` mas não aparece | Você está no inbox **errado** (prod × local) ou em outra **organização**. Com `DEBUG=True`, veja o `:8080`, na org do token. |
| Grupos não aparecem na aba "Aberto" | Podem estar **Ignorados** — troque o filtro de status. |
| Token rejeitado (boot loga aviso) | Token ausente/errado/revogado — gere outro em **Tokens da API**. |

---

## Desenvolvimento

Python 3.12, Django 4.2 (alvo das plataformas), em `.venv/`.

```bash
source .venv/bin/activate
pip install -e ".[dev]"
python -m pytest -q
```

Contrato de ingestão e arquitetura: veja `.claude/CLAUDE.md` e
`.claude/INTEGRACAO_PLATAFORMAS_LOGS.md`.
