# Task ID: 29

**Title:** Implement Debug Logging

**Status:** pending

**Dependencies:** 7, 11

**Priority:** low

**Description:** Add comprehensive debug logging with --debug-log flag for API calls, timing, and troubleshooting.

**Details:**

Update `src/teams_phone/cli/main.py` and infrastructure:
```python
import logging

@app.callback()
def main(
    ...
    debug_log: Optional[Path] = typer.Option(None, "--debug-log", help="Write debug log to file"),
):
    if debug_log:
        logging.basicConfig(
            filename=debug_log,
            level=logging.DEBUG,
            format="%(asctime)s %(levelname)s %(name)s: %(message)s"
        )
        # Mask secrets in logs
        ...
```

Log in GraphClient:
- Request method, URL, sanitized headers
- Response status, timing
- Retry attempts

Never log: access tokens, client secrets, certificate contents.

**Test Strategy:**

Unit tests: verify log file created, API calls logged with timing, secrets are masked/redacted, log rotation for large files.

---

## Implementation Notes

### Session: 2026-01-30 14:45 - Agent: claude-sonnet-4-5-20250929

**Status:** Implemented

#### What Was Done
- Added `debug_log_path: Path | None` and `debug_logger: DebugLogger | None` fields to `CLIContext` dataclass
- Added `--debug-log <path>` global option to CLI main callback in `main.py`
- Created new `DebugLogger` class in `src/teams_phone/infrastructure/debug_logger.py`:
  - JSON Lines format output with timestamp, correlation_id, level, event
  - Secret masking functions (`mask_secrets`, `mask_upn`, `mask_headers`)
  - Methods for logging: `log_command_start`, `log_command_complete`, `log_graph_request`, `log_graph_response`, `log_retry`, `log_cache_hit`, `log_cache_miss`, `log_error`, `log_debug`
  - Correlation ID generated per session (first 8 chars of UUID)
  - UTF-8 file encoding for Windows compatibility
- Integrated `DebugLogger` with `GraphClient`:
  - Added `debug_logger` parameter to `GraphClient.__init__`
  - Added request/response logging to `get()`, `post()`, `post_with_response()`, `get_paginated()` methods
  - Timing information captured using `time.perf_counter()`
- Exported `DebugLogger` from `teams_phone.infrastructure` package
- Created comprehensive unit tests in `tests/unit/test_debug_logging.py` (37 tests)
- Updated CLI tests in `tests/unit/test_cli_main.py` for new option

#### Files Created/Modified
- Created: `src/teams_phone/infrastructure/debug_logger.py`
- Created: `tests/unit/test_debug_logging.py`
- Modified: `src/teams_phone/cli/context.py` (added debug_log_path, debug_logger fields)
- Modified: `src/teams_phone/cli/main.py` (added --debug-log option)
- Modified: `src/teams_phone/infrastructure/__init__.py` (exported DebugLogger)
- Modified: `src/teams_phone/infrastructure/graph_client.py` (integrated logging)
- Modified: `tests/unit/test_cli_main.py` (added tests for new option)

#### Key Decisions Made
- **JSON Lines format instead of Python logging**: Used a custom DebugLogger class that writes JSON Lines directly rather than Python's logging module. This gives more control over the output format and ensures consistent JSON structure.
- **Correlation ID scope**: Correlation ID is per-session (per DebugLogger instance), not per-request. This matches the spec requirement for "per command invocation".
- **Secret masking approach**: Used regex patterns for Bearer tokens and client secrets. Created separate `mask_upn` function for UPN truncation.
- **Logging integration point**: Added logging at the public API level (get/post methods) rather than in the retry mechanism. This provides clean request/response pairs with timing.

#### Patterns Discovered
- `CLIContext` pattern for adding global options: Add field to dataclass, add Option in callback, assign to context
- Test pattern for context inspection: Use fixture that creates test app with command that captures context values

#### Open Items for Next Agent
- [ ] Consider adding command_start/command_complete logging to actual CLI commands (currently DebugLogger is available but commands don't use it yet)
- [ ] Consider adding cache_hit/cache_miss logging to CacheManager when it reads from CSV cache
- [ ] Retry logging could be enhanced to use DebugLogger in addition to existing `logger.warning()` calls

#### Gotchas and Warnings
- The `DebugLogger` is only created when `--debug-log` is specified. Commands should check `cli_ctx.get_debug_logger()` returns non-None before calling log methods.
- Log file is opened in append mode for each write operation - this is intentional for robustness but may have minor performance impact for high-volume logging.
