bozo 0.1.0
Production-grade structured logging with optional real-time web viewer
Requires Python: >=3.13

# bozo

**Production-grade structured logging with optional real-time web viewer.**

[![Python 3.13+](https://img.shields.io/badge/python-3.13+-blue.svg)](https://www.python.org/downloads/)
[![MIT License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
[![Tests Passing](https://img.shields.io/badge/tests-346%20passing-brightgreen)]()
[![Code Coverage](https://img.shields.io/badge/coverage-81%25-brightgreen)]()

> **Simple.** One function to set up structured logging.
> **Powerful.** File, JSON, HTTP, console—all at once.
> **Developer-Friendly.** Type-safe, zero-config defaults.

---

## Features

✨ **Structured Logging**
Context-aware, nested logging with built-in support for sensitive data redaction.

📊 **Multiple Outputs**
Simultaneously log to console (colored), text files, JSON files, and HTTP endpoints.

🌐 **Web Viewer** *(Optional)*
Real-time log streaming dashboard. Perfect for development and debugging.

🛠 **Production-Ready**
Automatic log rotation, third-party library suppression, and thread-safe operations.

🔍 **Full Type Safety**
Fully typed with Python 3.13+ support. Works great with type checkers.

⚙️ **Smart Defaults**
Intelligent log file management, automatic cleanup, and sensible configuration presets.

---

## Quick Start

### Installation

```bash
# Core library
pip install bozo

# With web viewer support
pip install bozo[viewer]
```

### Basic Usage

```python
import bozo

# Initialize once at startup
bozo.setup("myapp")
log = bozo.get(__name__)

# Log with structured data
log.info("user_login", user_id=42, email="user@example.com")
log.warning("slow_query", query_time=1.5, threshold=1.0)
log.error("payment_failed", amount=99.99, reason="insufficient_funds")
```

**Output** (console, file, JSON—simultaneously):

```
2026-02-04 10:23:45 | INFO     | myapp.main | user_login
2026-02-04 10:23:46 | WARNING  | myapp.main | slow_query
2026-02-04 10:23:47 | ERROR    | myapp.main | payment_failed
```

### Sensitive Data

Automatically redact passwords, tokens, and secrets:

```python
log.info(
    "auth_attempt",
    username="alice",
    password=bozo.redact("secret123")  # Will log as [REDACTED]
)
```

### Context Management

Attach request context that persists across async/threaded operations:

```python
with bozo.log_context(request_id="abc-123", user_id=42):
    log.info("processing")  # request_id and user_id added automatically
    await some_async_operation()
    log.info("completed")  # context still present
```

### Web Viewer

View logs in real-time from a beautiful dashboard:

```python
# Enable during setup
bozo.setup(
    "myapp",
    enable_viewer=True,
    viewer_port=8080
)

# Or start it manually
bozo.start_viewer(open_browser=True)  # Opens http://127.0.0.1:8080
```

---

## Configuration

### Development vs. Production

```python
# Development: Pretty-printed, colored console output
bozo.setup("myapp", environment=bozo.Environment.DEVELOPMENT)

# Production: Plain text, structured JSON, minimal console
bozo.setup("myapp", environment=bozo.Environment.PRODUCTION)
```

### Output Handlers

Control what gets logged and where:

```python
bozo.setup(
    "myapp",
    # Console
    enable_console=True,
    console_level=bozo.LogLevel.INFO,
    # Text files
    enable_file=True,
    file_level=bozo.LogLevel.DEBUG,
    enable_error_file=True,  # Separate ERROR+ logs
    # JSON files (for log aggregation)
    enable_json=True,
    json_level=bozo.LogLevel.DEBUG,
    # HTTP shipping (for external platforms)
    enable_http=True,
)
```

### Log Directory

Logs are organized by run:

```bash
~/.local/share/bozo/myapp/          # macOS/Linux (or platform-specific)
├── current/                         # Latest run
│   ├── 2026-02-04_10-23-45.log
│   ├── 2026-02-04_10-23-45.error
│   └── 2026-02-04_10-23-45.json
├── 2026-02-03_18-15-22/             # Previous runs
│   ├── 2026-02-03_18-15-22.log
│   ├── 2026-02-03_18-15-22.error
│   └── 2026-02-03_18-15-22.json
└── ...
```

Automatically cleaned up (configurable):

```python
bozo.setup("myapp", max_runs_to_keep=10)  # Keep only 10 most recent runs
```

---

## Advanced Usage

### Capturing Python Warnings

Route Python warnings through bozo:

```python
import warnings

bozo.setup("myapp", capture_warnings=True)

warnings.warn("deprecated feature")  # Will be logged
```

### Third-Party Library Suppression

Reduce noise from verbose libraries:

```python
bozo.setup("myapp", suppress_third_party=True)
# Automatically reduces verbosity for: urllib3, httpx, httpcore, etc.
```

### Programmatic Context

```python
# Set context that persists
bozo.set_context(request_id="req-123")
log.info("operation_started")  # Has request_id

# Clear specific context
bozo.clear_context()
```

### Logging Levels

All standard levels are supported:

```python
log.debug("verbose_info")
log.info("important_event")
log.warning("potential_issue")
log.error("something_failed", error=str(exception))
```

---

## Type Safety

Full IDE autocomplete and type checking:

```python
import bozo
from bozo import BoundLogger, LogLevel, Environment

log: BoundLogger = bozo.get(__name__)
env: Environment = bozo.Environment.PRODUCTION
level: LogLevel = bozo.LogLevel.DEBUG
```

---

## Common Patterns

### Web Framework Integration

**FastAPI:**
```python
from contextlib import asynccontextmanager
from fastapi import FastAPI, Request
import bozo

bozo.setup("api", environment=bozo.Environment.PRODUCTION)
log = bozo.get(__name__)

@app.middleware("http")
async def log_requests(request: Request, call_next):
    with bozo.log_context(
        method=request.method,
        path=request.url.path,
        client=request.client.host
    ):
        response = await call_next(request)
        log.info("request", status_code=response.status_code)
        return response
```

**Django:**
```python
# settings.py
import bozo

bozo.setup(
    "myproject",
    environment=bozo.Environment.PRODUCTION,
    enable_viewer=False,
)

LOGGING = {"version": 1, "disable_existing_loggers": False}
```

### Database Operations

```python
with bozo.log_context(operation="user_insert"):
    try:
        db.insert_user(user_data)
        log.info("user_created", user_id=user_data["id"])
    except Exception as e:
        log.error("insert_failed", error=str(e), data=user_data)
        raise
```

### Background Tasks

```python
import asyncio

async def process_queue():
    while True:
        item = await queue.get()
        with bozo.log_context(job_id=item["id"]):
            try:
                await process_item(item)
                log.info("job_completed")
            except Exception as e:
                log.error("job_failed", error=str(e))
                await queue.put(item)  # Retry
            await asyncio.sleep(1)
```

---

## Performance

- **Minimal overhead**: Structured logging is negligible vs. application logic
- **Thread-safe**: All operations are safe for concurrent use
- **Bounded memory**: Web viewer queue has configurable limits
- **Efficient rotation**: Automatic file rotation prevents disk bloat

---

## Troubleshooting

**Q: Where are my log files?**

A: Check `~/.local/share/bozo/yourapp/current/` (or the path printed at startup)

**Q: Web viewer not working?**

A: Install viewer dependencies: `pip install bozo[viewer]`

**Q: How do I disable file logging?**

A: `bozo.setup("app", enable_file=False, enable_json=False)`

**Q: Can I use bozo with existing logging setup?**

A: bozo replaces your logging setup. Call `bozo.setup()` once at startup.

---

## Contributing

Contributions welcome! See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.

---

## License

MIT License - see [LICENSE](LICENSE) for details.

---

## Support

- 📖 [Documentation](https://bozo.tejusgupta.dev/)
- 🐛 [Issue Tracker](https://github.com/tejus3131/bozo/issues)
- 💬 [Discussions](https://github.com/tejus3131/bozo/discussions)

**Made with ❤️ by [Tejus Gupta](https://tejusgupta.dev/)**