---
title: Logging Constraints
category: constraints
severity: critical
---

# Logging Constraints

## Core Rule

Use `logging.getLogger(__name__)` - never use `print()`

**CRITICAL ANTI-PATTERN**: `api_client.py` uses `print()` instead of logging (11 instances).

## CRITICAL Anti-Pattern: print() Instead of Logging

**File**: `api_client.py`
**Evidence**: Lines with `print(f"...")` instead of `_logger.info(...)`

**Broken code**:
```python
def get_motions(self, ...):
    try:
        # ...
        print(f"Fetched {len(voting_records)} voting records from API")  # BAD
        print(f"Processed into {len(motions)} unique motions")  # BAD
    except Exception as e:
        print(f"Error fetching motions from API: {e}")  # BAD - no traceback
```

**Fix**:
```python
import logging

_logger = logging.getLogger(__name__)

def get_motions(self, ...):
    try:
        _logger.info("Fetched %d voting records from API", len(voting_records))
        _logger.info("Processed into %d unique motions", len(motions))
    except Exception as e:
        _logger.exception("Error fetching motions from API: %s", e)
        return []
```

## Logger Initialization

Get logger at module level:

```python
# GOOD: Use logging.getLogger(__name__)
import logging

_logger = logging.getLogger(__name__)

def some_function():
    _logger.info("Processing started")
    _logger.debug("Detail: %s", detail)
```

## Logger Naming

Use `__name__` for automatic module path:

```python
# In database.py - logger will be "database"
_logger = logging.getLogger(__name__)

# In pipeline/svd_pipeline.py - logger will be "pipeline.svd_pipeline"
_logger = logging.getLogger(__name__)
```

**INCONSISTENCY WARNING**: 16 files use `logger`, 17 files use `_logger`. Choose one convention.

**Recommendation**: Use `_logger` (with underscore) for module-level loggers to distinguish from class-level loggers.

## Log Levels

| Level | When to Use |
|-------|-------------|
| DEBUG | Detailed diagnostic info (dev only) |
| INFO | Normal operation milestones |
| WARNING | Unexpected but handled (fallbacks) |
| ERROR | Operation failed, may need attention |
| CRITICAL | Fatal error, program may crash |

## Exception Logging

Use `_logger.exception()` for caught exceptions (includes traceback):

```python
try:
    result = risky_operation()
except Exception as exc:
    _logger.exception("Operation failed: %s", exc)
    return fallback_value
```

## Anti-Patterns

### Debug Prints in Production Code
```python
# BAD
print(f"[TRAJ DEBUG] processing window {wid}")

# GOOD
_logger.debug("Processing window %s", wid)
```

### Inconsistent Logger Names
```python
# BAD - mixing _logger and logger
_logger = logging.getLogger(__name__)
logger = logging.getLogger("other")  # Inconsistent
```

## Sensitive Data

Never log sensitive information:
- API keys
- User votes
- Session IDs (if tied to user data)
- Personal information

```python
# BAD
_logger.info("User %s voted %s", user_id, vote)

# GOOD - log aggregates, not individual votes
_logger.info("Vote recorded for session %s", session_id[:8])
```
