Metadata-Version: 2.4
Name: fastapi-logkit
Version: 0.1.2
Summary: FastAPI logging bootstrap: dev-friendly console, production structlog JSON
Project-URL: Homepage, https://pypi.org/project/fastapi-logkit/
Project-URL: Repository, https://github.com/RevolutionaryWarrior/fastapi-logkit
Project-URL: Issues, https://github.com/RevolutionaryWarrior/fastapi-logkit/issues
Author: Byuckchon
License: MIT
Keywords: asgi,fastapi,logging,starlette,structlog,uvicorn
Classifier: Development Status :: 4 - Beta
Classifier: Framework :: FastAPI
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.10
Requires-Dist: fastapi>=0.100.0
Requires-Dist: starlette>=0.27.0
Requires-Dist: structlog>=24.1.0
Provides-Extra: dev
Requires-Dist: httpx>=0.27.0; extra == 'dev'
Requires-Dist: pytest>=8.0.0; extra == 'dev'
Requires-Dist: uvicorn>=0.23.0; extra == 'dev'
Provides-Extra: uvicorn
Requires-Dist: uvicorn>=0.23.0; extra == 'uvicorn'
Description-Content-Type: text/markdown

# fastapi-logkit

FastAPI logging bootstrap: readable console logs in development, structured structlog JSON to `stdout` in production-minded environments.

- Prefer turning off Uvicorn/Gunicorn access logs and emitting HTTP access as structured middleware logs instead.
- The global exception handler logs unhandled request errors; process-level logs are normalized via Uvicorn/Gunicorn logger wiring.

## Installation

```bash
pip install fastapi-logkit
```

PyPI: [fastapi-logkit](https://pypi.org/project/fastapi-logkit/)

## Usage

`fastapi_logkit` does **not read OS environment variables.** Read deploy env, log level, etc. in your app and pass values into helpers.

```python
import os
from fastapi import FastAPI
from fastapi_logkit import setup_logging
from fastapi_logkit.fastapi import install_logkit

def app_env() -> str:
    return (os.getenv("APP_ENV") or os.getenv("ENVIRONMENT") or "development").strip()

cfg = setup_logging(
    env=app_env(),
    level=os.getenv("LOG_LEVEL", "INFO").strip().upper(),
)

app = FastAPI()
install_logkit(app, config=cfg)
```

- With `install_logkit(..., config=cfg)`, the exception handler mirrors `cfg.json_logs` (structured JSON vs `app.error` text).
- If `access_log` is omitted, it defaults from `is_production_environment(cfg.env)`.

When running Uvicorn, pass `log_config` from `get_uvicorn_log_config_for_run`. After `setup_logging` / `configure_logging`, the returned `LogKitConfig` is kept as the **active** config; `get_uvicorn_log_config_for_run()` reads it (via `get_active_logkit_config`) so **`silence_uvicorn` matches bootstrap without repeating `config=`**:

```python
from fastapi_logkit.integrations.uvicorn import get_uvicorn_log_config_for_run

cfg = setup_logging(env=app_env(), level=os.getenv("LOG_LEVEL", "INFO").strip().upper())
uvicorn.run(
    "main:app",
    log_config=get_uvicorn_log_config_for_run(),
)
```

Pass **`config=`** or **`silence_uvicorn=`** / **`env=`** when you need a one-off that differs from the last `setup_logging` result. If nothing has called `setup_logging` yet, omitting `config` falls back to resolving `env` (default `"development"`) and `is_json_logging_environment` for silence, same as before.

For isolated tests, `clear_active_logkit_config()` (in `fastapi_logkit.bootstrap`) clears the stored config.

Production-style run: `uvicorn main:app --host 0.0.0.0 --port 8000 --no-access-log`

---

## Behaviour summary (arguments / helpers)

| API / helper | Description |
|----------------|-------------|
| `setup_logging(env=..., level="INFO", json_logs=None, ...)` | Omit `json_logs` to derive it from `is_json_logging_environment(env)` (`production` / `prod` / `staging` → JSON pipeline). Stores the resulting `LogKitConfig` as the **active** config for helpers below. |
| `get_active_logkit_config()` | The `LogKitConfig` from the last successful `setup_logging` / `configure_logging`, or `None`. Also exported from `fastapi_logkit`. |
| `clear_active_logkit_config()` | Clears the active config (`fastapi_logkit.bootstrap`; useful between test cases). |
| `get_uvicorn_log_config_for_run(...)` | Uvicorn `log_config` dict. With no args after `setup_logging`, uses active `silence_uvicorn`; otherwise `config=` / `silence_uvicorn=` / `env=` as documented in the integration module. |
| `is_json_logging_environment(env)` | Whether `env` is treated as a JSON-logging env; **string only** (does not touch `os`). |
| `is_production_environment(env)` | True only for `production` / `prod` — default access middleware install in `install_logkit`. |
| `resolve_env_name(env)` | If `env` is `None`, returns `"development"` (no env var lookup). |
| `configure_logging(...)` | Alias of `setup_logging`. |

Body redaction, proxy IP trust, and **which access lines are emitted** are configured via **`AccessLogOptions`** + `install_logkit(..., access_log_options=...)`.

### Proxy headers and `originIP`

Access logs include **`clientIP`** (the immediate TCP peer from ASGI `scope["client"]`) and **`originIP`** (intended “client behind proxies” when enabled).

- **`AccessLogOptions.trust_proxy_headers`** defaults to **`False`**. In that case **`originIP` matches `clientIP`** (forwarded headers are ignored), which avoids spoofed `X-Forwarded-For` when the app is not behind a trusted edge proxy.
- With **`trust_proxy_headers=True`**, **`originIP`** is resolved in order: first hop of **`X-Forwarded-For`** (comma-separated list), else **`X-Real-IP`**, else the same value as **`clientIP`**. Header names are compared case-insensitively as received in the ASGI scope.

Only set **`trust_proxy_headers=True`** when a **trusted** reverse proxy (nginx, a load balancer, etc.) terminates TLS and **sets or overwrites** these headers for you; otherwise clients can forge them.

Pass the flag via `install_logkit(..., access_log_options=AccessLogOptions(trust_proxy_headers=True))` and/or `setup_logging(..., access_log_options=AccessLogOptions(trust_proxy_headers=True))` so it lives on `LogKitConfig.access_log` when you reuse `config=`.

### HTTP access line threshold

Each access line is classified with a stdlib severity: **2xx/3xx → `INFO`**, **4xx → `WARNING`**, **5xx → `ERROR`**. A line is emitted only if that severity is **≥** the configured minimum.

- By default, `install_logkit(..., config=cfg)` sets the access minimum from **`cfg.level`** (same string as `setup_logging(..., level=...)`). For example, `level="WARNING"` suppresses successful **2xx/3xx** access lines but still records **4xx** and **5xx**.
- Set **`AccessLogOptions(access_min_level=logging.WARNING)`** (or another numeric level) to override the threshold without changing the global structlog floor.

---

## JSON line fields (JSON mode)

Each `stdout` line is produced by `structlog.processors.JSONRenderer()`, with fields merged as follows:

| Category | Fields |
|----------|--------|
| Common (processors) | `env` — name passed into `setup_logging`. `logLevel` — numeric Pino-like level (e.g. info=30). The `event` key is stripped right before rendering. |
| Request context (`RequestContextMiddleware`) | `request_id`, `method`, `path` merged from structlog contextvars. |
| Access log (`AccessLogMiddleware`) | `statusCode`, `userAgent`, `clientIP`, `originIP`, `dateTime` (ms timestamp), `endPoint`, `pathParams`, `method`, `traceBack` when present. For 4xx/5xx with body logging enabled, `requestBody`. |
| JSON `unhandled_exception_handler` without access middleware on the stack | `traceBack`, `endPoint`, `method`, `statusCode` (500). May skip when access middleware already logged the same failure. |
| App kwargs | Any keys passed to `logger.info("name", **fields)` are included verbatim. |

---

See the `fastapi_logkit/` source for processors and middleware details.
