Metadata-Version: 2.4
Name: fictura
Version: 0.2.0
Summary: Backend error capture for FastAPI / any ASGI app — grouped by cause, ranked by users affected, reported to Fictura.
Project-URL: Homepage, https://fictura.co
Project-URL: Source, https://github.com/Apex-Byte-Technologies/fictura_backend
Project-URL: Issues, https://github.com/Apex-Byte-Technologies/fictura_backend/issues
Author: Apex Byte Technologies
License: MIT
Keywords: asgi,error-tracking,fastapi,fictura,monitoring,observability
Classifier: Development Status :: 4 - Beta
Classifier: Framework :: FastAPI
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: System :: Monitoring
Classifier: Typing :: Typed
Requires-Python: >=3.8
Description-Content-Type: text/markdown

# fictura — backend error capture

One stdlib-only file that reports your FastAPI backend's exceptions to
Fictura's Issues page: grouped by cause, ranked by users affected, with New /
Ongoing / Escalating status, alerts on first-seen + regressions, and an uptime
monitor next to it.

## Install

```bash
pip install fictura
```

Add `fictura` to your `requirements.txt` so it survives rebuilds. The package
has **no dependencies** — it's one stdlib-only module.

Prefer to vendor instead? Pull the same file straight from your backend:

```bash
curl -o app/fictura.py https://api.fictura.co/sdk/fictura.py
```

## Wire it (2 lines)

```python
from fastapi import FastAPI
from fictura import Fictura

fictura = Fictura(
    api_base="https://YOUR-GROWTH-AGENT-HOST",
    api_key="gk_...",                 # Dashboard → Setup → SDK app key
    release=os.getenv("GIT_SHA"),
    environment=os.getenv("ENV", "production"),
    # Optional: however your auth middleware stashes the user id.
    get_user_id=lambda scope: (scope.get("state") or {}).get("user_id"),
)

app = FastAPI()
app.add_middleware(fictura.middleware)
```

That's the whole integration. Three kinds of failure now reach the **Issues**
page, and only the third one asks anything of you:

**1. Crashes.** Unhandled exceptions, caught by the middleware. Your own error
handling is untouched — it re-raises after queueing.

**2. Errors you already log.** Constructing `Fictura()` attaches a logging
handler, so every `logger.error(...)` you already write becomes an Issue:

```python
try:
    result = await gemini.generate(prompt)
except Exception as e:
    logger.error(f"Generation failed: {e}")   # already an Issue. No new code.
    return fallback()
```

This matters more than it looks. Most real failures — a vendor API down, a
database call that fails over — are caught and logged, never re-raised, so the
middleware alone would never see them.

You get the **full traceback** even from a bare `logger.error(f"...{e}")` with
no `exc_info=`: logging handlers run on the calling thread, so the exception is
still live and gets recovered. `logger.exception(...)` is better hygiene and
works the same. A log line with no exception in flight still becomes an Issue,
grouped by the call site — so `f"failed for {user_id}"` is one issue, not one
per user.

Pass `capture_logs=False` to turn this off, or `log_level=logging.WARNING` to
widen it.

**3. Anything you want to be explicit about:**

```python
except SomeVendorError as e:
    fictura.capture_exception(e, route="/api/v1/generate", level="warning")
```

An exception that is both logged and re-raised is reported once, not twice.

## Uptime

On the Issues page, set your backend's public URL (its `/health` route). It's
pinged every 60s; 3 consecutive failures alert your Slack/email, recovery
alerts once with the downtime duration.

## Guarantees

- Never raises into your app; never blocks a response (daemon-thread batcher,
  bounded queue, drops oldest on overflow, 2s HTTP timeout, no retry storms).
- Stacks are plain Python tracebacks — readable immediately, no source maps.
- Grouping is server-side: `exception type + top app frames + route`, so
  dependency internals and line-number churn don't split issues. Log events
  with no traceback group on `logger + file:line` instead.
- One exception is one Issue, whether it was logged, raised, captured, or all
  three.
- The logging handler ignores `uvicorn`/`gunicorn` records — the server logs
  every unhandled exception the middleware already reported.
- Rate limit: 300 events/min per app at the ingest; excess returns 429 and is
  dropped client-side.

## Smoke test

```bash
GROWTH_API_KEY=gk_... GROWTH_API_BASE=http://localhost:4000 python fictura.py
```

exercises all three capture paths (and asserts the traceback recovery and
one-exception-one-issue behavior). Four events should appear on the Issues page
within seconds.
