# Logging Instructions

This document describes how to write and refactor logging across the AniWorld codebase to make logs **human-readable**, **debug-friendly**, and **noise-free**.

> ✅ Goal: Logs should help a developer understand what happened, why it happened, and what to inspect next — without overwhelming them with duplicates or irrelevant details.

---

## 1. Principles for Great Logs

### 1.1 Use the Right Log Level

- `DEBUG`: Detailed internal state useful when debugging a specific issue (e.g., decision points, returned values, request/response payloads). Not for normal operation.
- `INFO`: High-level events that represent what the system is doing (e.g., "Import started", "New series added", "Config reloaded"). Use sparingly.
- `WARNING`: Something unexpected happened, but the system can continue (e.g., missing optional file, fallback behavior).
- `ERROR`: An operation failed and needs attention (e.g., exception caught, failed database write).
- `CRITICAL`: The system is in an unusable state (e.g., config corruption, failed startup).

### 1.2 Keep Logs Human-Readable

- Write messages in a clear, descriptive sentence-style format.
- Avoid cryptic codes or single-word log messages.
- Prefer `logger.debug("... %s", value)`-style formatting over f-strings to avoid unnecessary work when the log level is disabled.

### 1.3 Avoid Log Spam

- Don’t log inside hot loops unless you explicitly aggregate and log a summary (e.g., "Processed 124 files, 3 failures").
- Avoid repeated/logging the same event at the same level (e.g., do not log "Retrying" 10 times at INFO; log once at INFO and then use DEBUG for each retry).
- Use rate limiting or debounce patterns for logs that can fire rapidly (e.g., external service health checks).
- Prefer a single higher-level log with context rather than many low-level logs that clutter output.

### 1.4 Log Objects Usefully

- When logging objects, log the minimal useful representation (e.g., ID, name, status) rather than the full object or its memory address.
- If an object has a `.dict()`, `.to_dict()`, or `.as_dict()` helper (common in Pydantic models), log that rather than relying on `repr()`.
- Add a `__repr__` or `__str__` implementation to domain models that returns a helpful, concise string with key identifiers.
- Use structured logging (e.g., `logger.info("Series added", extra={"series_id": series.id, "title": series.title})`) where supported.
- For exceptions, prefer `logger.exception("Failed to ...")` to capture stack traces.

---

## 2. Refactoring Existing Logs

When improving or refactoring existing log statements, aim to make them:

- **Actionable**: A developer reading the log should know what happened and what to check next.
- **Non-redundant**: Remove duplicates and ensure only one log records the same high-level event at a given level.
- **Context-rich**: Include identifiers (e.g., `series_id`, `file_path`, `user_id`) and key state that explains why a decision was made.
- **Level-appropriate**: Downgrade noisy INFO logs to DEBUG, and elevate critical failures to ERROR/CRITICAL.

### 2.1 Refactor Checklist

1. **Locate noisy logs**: Search for repeated messages (e.g., "Start", "Done") and determine whether they should be DEBUG or removed.
2. **Replace ad-hoc prints**: Remove `print()` statements or `print(obj)` and replace with `logger.*` calls.
3. **Use structured context**: If a function logs multiple related messages, include the same context in each (e.g., `extra={"series_id": series.id}`) or use a context manager that attaches it.
4. **Validate object output**: Ensure any logged object produces a useful representation (add methods or translate to dict). If not, log the key fields explicitly.
5. **Batch repetitive events**: If a loop logs per item, consider collecting stats and logging a summary at the end.

## 3. Adding New Logs

When adding logs to new code paths:

- Log **important state transitions** (e.g., "Queue started", "Download completed", "Config reloaded").
- For error paths, include what failed and why (e.g., "Could not load config from X: {exc}").
- Prefer logging at the boundaries of operations, not deep inside utility functions unless it aids debugging.
- Write logs in full sentences, with a clear subject, verb, and object.

---

## 4. Example Patterns

```python
logger.info("Import completed", extra={"series_id": series.id, "count": len(imported)})

logger.debug(
    "Fetched feed items",
    extra={"feed_url": feed.url, "item_count": len(items)},
)

try:
    result = download_episode(episode)
except Exception:
    logger.exception("Failed to download episode %s", episode.id)
```

> 💡 When in doubt, favor **fewer, richer logs** over many noisy logs.

---

## 5. Logging Audit Task List

For a guided checklist of files and logging improvements, see **`docs/tasks.md`**. This is where we track which files have been reviewed and which logging items still need attention.

> ✅ After applying the guidelines above, update `docs/tasks.md` to indicate which tasks are complete.
