You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
131 lines
3.0 KiB
131 lines
3.0 KiB
---
|
|
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])
|
|
```
|
|
|