# Story 12.13: Django Structured Logging Cookbook Entry

**Status:** Complete
**Priority:** Medium
**Depends on:** None
**Effort:** 1 day

---

## Problem Statement

Django developers searching for structured logging solutions find FastAPI-focused content. We're missing an SEO opportunity to reach Django users who want:

- Structured JSON logging in production
- Correlation IDs across requests
- Non-blocking logging that doesn't slow down request handling
- PII redaction for compliance

fapilog's `SyncLoggerFacade` already supports Django's synchronous request model, but we have no documentation showing how to wire it up.

### Target Search Queries

- "django structured logging"
- "django json logging"
- "django correlation id logging"
- "django request id middleware"
- "django async logging"
- "django logging best practices 2026"
- "django pii redaction logging"

### Current State

- No Django content in docs
- Django users discovering fapilog see FastAPI-only examples and assume it doesn't work for them
- `SyncLoggerFacade` exists but isn't documented for Django use

---

## Goals

1. Capture Django SEO traffic with a focused cookbook entry
2. Demonstrate fapilog works with Django (manual wiring)
3. Gauge community interest for a future `fapilog-django` plugin
4. Position fapilog as framework-agnostic (async-first, but sync-capable)

---

## User Story

**As a** Django developer
**I want** structured, non-blocking logging with correlation IDs
**So that** I can debug production issues and meet compliance requirements

---

## Scope

### In Scope

- Cookbook entry: `docs/cookbook/django-structured-logging.md`
- Working code examples for:
  - Basic Django + fapilog setup
  - Correlation ID middleware
  - Request/response logging middleware
  - Exception logging with context
  - Celery task correlation (brief mention)
- Update cookbook index

### Out of Scope

- Full `fapilog-django` plugin (future story)
- Django admin integration
- Django REST Framework specific patterns (could be follow-up)
- Channels/WebSocket support
- Comprehensive Celery integration

---

## Acceptance Criteria

### AC1: Cookbook Entry Created

- [ ] `docs/cookbook/django-structured-logging.md` exists
- [ ] Follows cookbook format (Problem → Solution → Code → Going Deeper)
- [ ] Title is SEO-optimized: "Django Structured JSON Logging with Correlation IDs"
- [ ] Includes target keywords naturally in content

### AC2: Working Code Examples

- [ ] Basic setup example works with Django 4.2+ and Django 5.x
- [ ] Correlation ID middleware example is copy-pasteable
- [ ] Request logging middleware captures method, path, status, duration
- [ ] Exception handler preserves request context
- [ ] All examples use `get_logger()` which returns `SyncLoggerFacade`

### AC3: Addresses Django-Specific Concerns

- [ ] Explains thread-local vs context vars for correlation IDs
- [ ] Shows integration with Django's `LOGGING` setting (or explains why to bypass it)
- [ ] Covers both sync views and async views (Django 4.1+)
- [ ] Mentions WSGI vs ASGI deployment differences

### AC4: Documentation Integration

- [ ] Cookbook index updated with new entry
- [ ] Cross-links to relevant existing docs (redaction, non-blocking logging)
- [ ] Documentation builds without warnings

### AC5: Honest Positioning

- [ ] Clearly states this is manual wiring, not first-class support
- [ ] Mentions FastAPI has deeper integration
- [ ] Invites feedback for potential `fapilog-django` plugin

---

## Technical Design

### Cookbook Entry Structure

```markdown
# Django Structured JSON Logging with Correlation IDs

## The Problem
- Django's default logging is unstructured
- No built-in correlation ID support
- Blocking file writes can slow requests

## The Solution
- fapilog's SyncLoggerFacade for Django's sync model
- Custom middleware for correlation IDs
- Async queue ensures non-blocking writes

## Basic Setup
[Code example]

## Adding Correlation IDs
[Middleware code]

## Request/Response Logging
[Middleware code]

## Exception Logging with Context
[Signal handler code]

## Async Views (Django 4.1+)
[Brief note on async view support]

## Production Configuration
[Settings example]

## Going Deeper
[Links to related docs]
```

### Key Code Patterns

**1. Basic Setup:**
```python
# settings.py
from fapilog import get_logger

# get_logger() returns a SyncLoggerFacade - perfect for Django's sync model
# format="json" ensures structured output; level defaults to INFO
FAPILOG_LOGGER = get_logger(format="json")

# views.py
from django.conf import settings

def my_view(request):
    settings.FAPILOG_LOGGER.info("Processing request", path=request.path)
    ...
```

**2. Correlation ID Middleware:**
```python
import threading
import uuid

_correlation_id = threading.local()

class CorrelationIdMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        correlation_id = request.headers.get("X-Correlation-ID", str(uuid.uuid4()))
        _correlation_id.value = correlation_id
        request.correlation_id = correlation_id
        response = self.get_response(request)
        response["X-Correlation-ID"] = correlation_id
        return response

def get_correlation_id() -> str:
    return getattr(_correlation_id, "value", "unknown")
```

**3. Request Logging Middleware:**
```python
import time
from django.conf import settings

class RequestLoggingMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response
        self.logger = settings.FAPILOG_LOGGER

    def __call__(self, request):
        start = time.perf_counter()
        response = self.get_response(request)
        duration_ms = (time.perf_counter() - start) * 1000

        self.logger.info(
            "HTTP request",
            method=request.method,
            path=request.path,
            status_code=response.status_code,
            duration_ms=round(duration_ms, 2),
            correlation_id=get_correlation_id(),
        )
        return response
```

### Thread-Local vs Context Vars

Django's synchronous middleware uses thread-locals for request-scoped data. This differs from FastAPI's async context vars but achieves the same goal. The cookbook should explain:

- Thread-locals work for sync views (one thread per request)
- Async views need `contextvars` (show pattern if supporting async views)
- The `SyncLoggerFacade` handles the bridge to fapilog's async internals

---

## Tasks

### Phase 1: Write Cookbook Entry
- [ ] Create `docs/cookbook/django-structured-logging.md`
- [ ] Write Problem section with Django-specific pain points
- [ ] Write Solution section explaining fapilog's approach
- [ ] Add basic setup code example

### Phase 2: Add Middleware Examples
- [ ] Write correlation ID middleware example
- [ ] Write request logging middleware example
- [ ] Add exception logging with Django signals
- [ ] Test all code examples work with Django 4.2+

### Phase 3: Production Guidance
- [ ] Add production configuration section
- [ ] Cover WSGI deployment (Gunicorn)
- [ ] Briefly mention ASGI deployment (Daphne/Uvicorn)
- [ ] Add Celery correlation ID propagation note

### Phase 4: Integration
- [ ] Update `docs/cookbook/index.md`
- [ ] Add cross-links to existing cookbook entries
- [ ] Verify documentation builds
- [ ] Review for SEO keyword placement

---

## Tests

### Manual Verification

- [ ] Create minimal Django project with fapilog
- [ ] Verify correlation ID flows through request lifecycle
- [ ] Verify JSON output to stdout
- [ ] Verify non-blocking behavior under load
- [ ] Test with Django 4.2 and Django 5.0

### Documentation Build

- [ ] `make docs` succeeds without warnings
- [ ] New page appears in cookbook index
- [ ] All code blocks render correctly

---

## Documentation Updates

- [ ] Create `docs/cookbook/django-structured-logging.md`
- [ ] Update `docs/cookbook/index.md`
- [ ] No CHANGELOG update needed (documentation only)

---

## Definition of Done

### Content Complete
- [ ] Cookbook entry addresses all target search queries
- [ ] Code examples are tested and working
- [ ] Django-specific concerns addressed (thread-locals, WSGI/ASGI)
- [ ] Honest about manual wiring vs first-class support

### Quality
- [ ] Follows cookbook format consistently
- [ ] No spelling or grammar errors
- [ ] Code examples follow Django best practices
- [ ] Links to related docs work

### SEO
- [ ] Title includes primary keywords
- [ ] H2/H3 headings include secondary keywords
- [ ] Content naturally incorporates search terms
- [ ] Meta description suitable for search results

---

## Risks

### Risk 1: Code examples don't work with all Django versions
- **Mitigation:** Test with Django 4.2 LTS and Django 5.x; note version requirements

### Risk 2: Users expect full plugin, disappointed by manual wiring
- **Mitigation:** Set expectations upfront; frame as "getting started" with invitation to request more

### Risk 3: Thread-local correlation IDs break with async views
- **Mitigation:** Document the limitation; show contextvars pattern for async views

---

## Success Metrics

- Organic search traffic to Django cookbook page
- GitHub issues/discussions requesting `fapilog-django` plugin
- Community contributions extending Django patterns

---

## Related Documents

- [Cookbook Index](../cookbook/index.md)
- [Non-blocking Async Logging](../cookbook/non-blocking-async-logging.md)
- [FastAPI JSON Logging](../cookbook/fastapi-json-logging.md) (reference for structure)

---

## Open Questions

1. **Should we include Django REST Framework patterns?**
   - Recommendation: Brief mention, defer to follow-up story if interest

2. **How much Celery detail?**
   - Recommendation: Show correlation ID propagation concept, link to future guide

3. **Should we create a minimal example repo?**
   - Recommendation: Not for initial cookbook; consider if demand warrants

---

## Code Review

**Date:** 2026-01-22
**Reviewer:** Claude
**Verdict:** OK to PR

### Summary

Reviewed Django cookbook entry with structured JSON logging, correlation ID middleware, request logging, exception handling, and async view support.

### AC Verification

| Criterion | Evidence |
|-----------|----------|
| AC1: Cookbook entry created | `django-structured-logging.md:1` - File exists with SEO title |
| AC2: Working code examples | `django-structured-logging.md:26-47,60-94,126-158,179-221` - All patterns covered |
| AC3: Django-specific concerns | `django-structured-logging.md:58,225-270,323-366` - Thread-local, contextvars, LOGGING bypass |
| AC4: Documentation integration | `index.md:12,27` - Toctree and Quick Links updated |
| AC5: Honest positioning | `django-structured-logging.md:54` - Manual wiring note with plugin invitation |

### Quality Gates

- [x] Documentation builds without warnings (`sphinx-build -W`)
- [x] Cookbook format followed
- [x] Cross-links verified

---

## Change Log

| Date       | Change               | Author |
| ---------- | -------------------- | ------ |
| 2026-01-22 | Initial story creation | Claude |
| 2026-01-22 | Fixed API: get_sync_logger→get_logger, removed invalid level param | Claude |
