Metadata-Version: 2.4
Name: django-logpilot
Version: 0.1.0
Summary: Production-ready logging and alerting for Django with minimal setup.
Author: django-logpilot contributors
License-Expression: MIT
Project-URL: Homepage, https://github.com/django-logpilot/django-logpilot
Keywords: django,logging,observability,structured-logging,alerts
Classifier: Development Status :: 3 - Alpha
Classifier: Framework :: Django
Classifier: Framework :: Django :: 4.2
Classifier: Framework :: Django :: 5.0
Classifier: Framework :: Django :: 5.1
Classifier: Framework :: Django :: 5.2
Classifier: Framework :: Django :: 6.0
Classifier: Intended Audience :: Developers
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: Topic :: System :: Logging
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: Django>=4.2
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == "dev"
Requires-Dist: pytest-django>=4.8; extra == "dev"
Requires-Dist: build>=1.2; extra == "dev"
Requires-Dist: twine>=5.0; extra == "dev"
Dynamic: license-file

# django-logpilot

**Production-ready logging for Django with minimal setup.**

Logpilot configures sensible defaults so you get readable logs in development, structured JSON in production, optional rotating files, and a **request ID** on every response—without spending an afternoon on `LOGGING` dictionaries.

Alerting hooks exist in settings for upcoming releases; today the focus is **great logging out of the box**.

---

## What you get

- **Console and optional file** output with rotation  
- **Text or JSON** format (switch per environment)  
- **Request ID** middleware (`X-Request-ID` in / out) wired into log lines  
- **Root logger** configured so your app’s `logging.getLogger(__name__)` calls show up alongside Django’s logs  
- **Sensitive key redaction** helper for dict-shaped data you choose to log  
- **Parent directories** for `file_path` created automatically when possible  

---

## Requirements

- Python 3.10+
- Django 4.2+

---

## Install

From PyPI:

```bash
pip install django-logpilot
```

To try a **local checkout** of this repo inside another project:

```bash
pip install -e /absolute/path/to/django-logpilot
```

---

## Example settings (try the full stack)

Add this to your Django `settings.py` (adjust `INSTALLED_APPS` / `MIDDLEWARE` if you already have entries—merge lists, don’t duplicate).

```python
INSTALLED_APPS = [
    "django.contrib.admin",
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.messages",
    "django.contrib.staticfiles",
    "django_logpilot",
]

MIDDLEWARE = [
    "django_logpilot.middleware.RequestIdMiddleware",
    "django.middleware.security.SecurityMiddleware",
    "django.contrib.sessions.middleware.SessionMiddleware",
    "django.middleware.common.CommonMiddleware",
    "django.middleware.csrf.CsrfViewMiddleware",
    "django.contrib.auth.middleware.AuthenticationMiddleware",
    "django.contrib.messages.middleware.MessageMiddleware",
    "django.middleware.clickjacking.XFrameOptionsMiddleware",
]

# Leave handlers empty so Logpilot can apply its own config at startup.
# (See “Already using LOGGING?” below if you have a custom dict.)
LOGGING = {}

DJANGO_LOGPILOT = {
    "format": "text",
    "level": "DEBUG",
    "outputs": ["console", "file"],
    "file_path": str(BASE_DIR / "logs" / "app.log"),
    "file_max_bytes": 5 * 1024 * 1024,
    "file_backup_count": 3,
    "request_id_header": "X-Request-ID",
    "request_id_response_header": "X-Request-ID",
    "redact_keys": (
        "password",
        "secret",
        "token",
        "authorization",
        "cookie",
        "api_key",
    ),
    "alerts": {
        "enabled": False,
        "min_level": "ERROR",
        "deduplicate_for": 300,
        "channels": [],
        "slack_webhook": None,
        "email_backend": None,
    },
}
```

Then run the dev server and hit any page. Check:

- **Terminal** — log lines with your chosen format  
- **`logs/app.log`** — same records (after the first write)  
- **Response headers** — `X-Request-ID` present  

To exercise **your own** log lines in a view or signal:

```python
import logging

logger = logging.getLogger(__name__)

def some_view(request):
    logger.info("Hello from the app")
    ...
```

For **JSON** lines (typical in production), set `"format": "json"`.

---

## What actually gets logged?

Logpilot attaches handlers to the **root** logger (and to key `django` / `django.request` loggers). Any code that uses the standard library `logging` and **propagates** to the root—almost all app code with `logging.getLogger(__name__)`—will appear in console and file, subject to the configured **level** (`DEBUG`, `INFO`, etc.).

---

## Settings reference (`DJANGO_LOGPILOT`)

| Key | Default | Description |
|-----|---------|-------------|
| `format` | `"text"` | `"text"` (human-readable) or `"json"` (one JSON object per line). |
| `level` | `"INFO"` | Level for the root and attached Django loggers. |
| `outputs` | `["console"]` | `"console"`, `"file"`, or both. |
| `file_path` | `None` | Required when `"file"` is in `outputs`. Parent dirs are created if missing. |
| `file_max_bytes` | `10485760` | Rotate after this many bytes (default 10 MiB). |
| `file_backup_count` | `5` | Rotated files to keep. |
| `request_id_header` | `"X-Request-ID"` | Incoming header to reuse a trace id. |
| `request_id_response_header` | `"X-Request-ID"` | Outgoing header with the active id. |
| `redact_keys` | built-in list | Substrings matched against dict **keys** (case-insensitive) in `redact_mapping()`. |
| `alerts` | see defaults | Reserved for Slack/email and deduplication; delivery is not active in early releases. |

Built-in `redact_keys` include: `password`, `secret`, `token`, `authorization`, `cookie`, `set-cookie`, `api_key`, `apikey`, `credit_card`.

---

## Already using Django `LOGGING`?

- If **`LOGGING["handlers"]` is non-empty**, Logpilot **does not** overwrite your configuration (so existing projects stay safe).
- To **add** Logpilot’s handlers and formatters on top of your dict:

```python
DJANGO_LOGPILOT_MERGE_LOGGING = True
```

If you define handlers manually, every name in `"filters": [...]` must exist under `LOGGING["filters"]`. Logpilot’s own handlers do **not** rely on a custom filter for request IDs (formatters read context set by middleware), which avoids common `KeyError` mistakes when merging configs.

---

## Redacting structured data

Logpilot does not automatically scan request bodies. When **you** log a dict, wrap it:

```python
from django_logpilot.redaction import redact_mapping

logger.info("signup payload: %s", redact_mapping({"email": "a@b.com", "password": "x"}))
```

---

## Troubleshooting

| Issue | What to do |
|-------|------------|
| No Logpilot output | You likely have custom `LOGGING` handlers. Use `DJANGO_LOGPILOT_MERGE_LOGGING = True` or simplify `LOGGING` for a test. |
| `KeyError: 'logpilot_request_id'` | Remove old copy-pasted `"filters": ["logpilot_request_id"]` from custom handlers, or define that filter under `LOGGING["filters"]`. |
| Cannot create log file | The process needs write permission to the parent path (e.g. not `/var/log/...` without the right user). |

---

## Package status

**0.1.x — early.** Defaults and APIs may still move; pin a version in production until 1.0.

---

## Developing django-logpilot itself

```bash
git clone <your-repo-url>
cd django-logpilot
python -m venv .venv
source .venv/bin/activate   # Windows: .venv\Scripts\activate
pip install -e ".[dev]"
pytest
```


## License

MIT — see [LICENSE](LICENSE).
