# 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.