Metadata-Version: 2.4
Name: loguru-kit
Version: 0.6.0
Summary: Dead simple loguru setup
Author-email: JunSeok Kim <infend@gmail.com>
License: MIT
Project-URL: Homepage, https://github.com/bestend/loguru-kit
Project-URL: Documentation, https://github.com/bestend/loguru-kit#readme
Project-URL: Repository, https://github.com/bestend/loguru-kit.git
Project-URL: Issues, https://github.com/bestend/loguru-kit/issues
Keywords: loguru,logging,fastapi,opentelemetry
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: System :: Logging
Requires-Python: >=3.12
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: loguru>=0.7.0
Provides-Extra: fastapi
Requires-Dist: starlette>=0.27.0; extra == "fastapi"
Provides-Extra: otel
Requires-Dist: opentelemetry-api>=1.20.0; extra == "otel"
Provides-Extra: all
Requires-Dist: starlette>=0.27.0; extra == "all"
Requires-Dist: opentelemetry-api>=1.20.0; extra == "all"
Provides-Extra: dev
Requires-Dist: pytest>=7.0.0; extra == "dev"
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
Requires-Dist: ruff>=0.1.0; extra == "dev"
Requires-Dist: mypy>=1.0.0; extra == "dev"
Requires-Dist: httpx>=0.24.0; extra == "dev"
Dynamic: license-file

# loguru-kit

Safe & extensible loguru setup with conflict-free isolation.

**Language:** [한국어](./README.ko.md) | English

[![Python Version](https://img.shields.io/badge/python-3.12%2B-blue)](https://www.python.org/downloads/)
[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/licenses/MIT)
[![Tests](https://github.com/bestend/loguru-kit/actions/workflows/tests.yml/badge.svg)](https://github.com/bestend/loguru-kit/actions/workflows/tests.yml)

## Features

- **Conflict-free**: No `logger.remove()` - safe to use alongside other loguru users
- **Isolated loggers**: Each module gets its own context via `logger.bind()`
- **Extensible**: Handler, Formatter, ContextProvider protocols
- **Async-safe**: contextvars-based context propagation
- **Integrations**: FastAPI middleware, OpenTelemetry, stdlib intercept

## Installation

```bash
pip install loguru-kit
```

## Quick Start

```python
from loguru_kit import setup, get_logger

# Initialize once at app startup
setup()

# Get isolated logger for your module
logger = get_logger(__name__)
logger.info("Hello, world!")
```

## Configuration

### Via Code

```python
from loguru_kit import setup, ConsoleHandler, JsonFormatter

setup(
    level="DEBUG",
    handlers=[
        ConsoleHandler(level="INFO"),
    ],
    formatters=[
        JsonFormatter(),
    ],
)
```

### Via Environment Variables

| Variable | Default | Description |
|----------|---------|-------------|
| `LOGURU_KIT_LEVEL` | `INFO` | Log level |
| `LOGURU_KIT_JSON` | `false` | JSON output |

Priority: code args > env vars > defaults

## Extension System

### Custom Handler

```python
from loguru_kit import Handler, setup

class SlackHandler:
    """Send critical logs to Slack."""
    
    def __call__(self, message) -> None:
        if message.record["level"].name == "CRITICAL":
            send_to_slack(str(message))

setup(handlers=[SlackHandler()])
```

### Custom Formatter

```python
from loguru_kit import Formatter, setup

class CompactFormatter:
    """Minimal log format."""
    
    def __call__(self, record: dict) -> str:
        return "{level}: {message}\n"

setup(formatters=[CompactFormatter()])
```

### Custom Context Provider

```python
from loguru_kit import ContextProvider, setup

class UserProvider:
    """Inject current user into logs."""
    
    def __call__(self) -> dict:
        return {"user_id": get_current_user_id()}

setup(context_providers=[UserProvider()])
```

## Integrations

### FastAPI

```bash
pip install loguru-kit[fastapi]
```

```python
from fastapi import FastAPI
from loguru_kit import setup, get_logger
from loguru_kit.integrations.fastapi import LoggingMiddleware

setup()
logger = get_logger(__name__)

app = FastAPI()
app.add_middleware(LoggingMiddleware)

@app.get("/")
async def root():
    logger.info("processing request")
    return {"status": "ok"}
```

Request ID is automatically injected into all logs within a request context.

### OpenTelemetry

```bash
pip install loguru-kit[otel]
```

```python
from loguru_kit import setup
from loguru_kit.integrations.otel import patch_loguru

setup()
patch_loguru()
# trace_id, span_id automatically injected into logs
```

### stdlib Logging Intercept

Selectively intercept stdlib loggers (e.g., from third-party libraries):

```python
from loguru_kit import setup
from loguru_kit.integrations.stdlib import intercept_stdlib

setup()
intercept_stdlib(loggers=["uvicorn", "sqlalchemy"])
# Only specified loggers are redirected to loguru
```

## Migration from v1

### Breaking Changes

| v1 | v2 |
|----|-----|
| `from loguru_kit import logger` | `from loguru_kit import get_logger; logger = get_logger(__name__)` |
| `LOGURU_*` env vars | `LOGURU_KIT_*` env vars |
| `setup(intercept=True)` | `intercept_stdlib(loggers=[...])` (explicit) |
| `setup(otel=True)` | `patch_loguru()` (explicit) |

### Migration Steps

1. Replace `logger` import with `get_logger(__name__)`
2. Update environment variables from `LOGURU_*` to `LOGURU_KIT_*`
3. Use explicit `intercept_stdlib()` instead of `intercept=True`
4. Use explicit `patch_loguru()` instead of `otel=True`

### Before (v1)

```python
from loguru_kit import setup, logger

setup(level="DEBUG", intercept=True, otel=True)
logger.info("Hello!")
```

### After (v2)

```python
from loguru_kit import setup, get_logger
from loguru_kit.integrations.stdlib import intercept_stdlib
from loguru_kit.integrations.otel import patch_loguru

setup(level="DEBUG")
intercept_stdlib(loggers=["uvicorn"])
patch_loguru()

logger = get_logger(__name__)
logger.info("Hello!")
```

## Why v2?

v1 had a critical flaw: calling `logger.remove()` which pollutes global loguru state. This caused conflicts when:
- Multiple packages use loguru-kit
- Other packages use raw loguru
- stdlib logging handlers get hijacked

v2 uses `logger.bind()` for isolation - your loguru-kit usage never affects other loguru users.

## License

MIT License - See [LICENSE](./LICENSE) for details.
