348 lines
7.8 KiB
Markdown
348 lines
7.8 KiB
Markdown
|
|
# Logging Migration Guide
|
||
|
|
|
||
|
|
**How to migrate existing studies to use the new structured logging system**
|
||
|
|
|
||
|
|
## Overview
|
||
|
|
|
||
|
|
The new `optimization_engine.logger` module provides production-ready logging with:
|
||
|
|
- Color-coded console output
|
||
|
|
- Automatic file logging with rotation
|
||
|
|
- Structured trial logging for dashboard integration
|
||
|
|
- Zero external dependencies
|
||
|
|
|
||
|
|
## Migration Steps
|
||
|
|
|
||
|
|
### Step 1: Import the Logger
|
||
|
|
|
||
|
|
**Before:**
|
||
|
|
```python
|
||
|
|
import sys
|
||
|
|
import json
|
||
|
|
from pathlib import Path
|
||
|
|
```
|
||
|
|
|
||
|
|
**After:**
|
||
|
|
```python
|
||
|
|
import sys
|
||
|
|
import json
|
||
|
|
from pathlib import Path
|
||
|
|
from optimization_engine.logger import get_logger
|
||
|
|
```
|
||
|
|
|
||
|
|
### Step 2: Initialize Logger in main()
|
||
|
|
|
||
|
|
**Before:**
|
||
|
|
```python
|
||
|
|
def main():
|
||
|
|
study_dir = Path(__file__).parent
|
||
|
|
results_dir = study_dir / "2_results"
|
||
|
|
results_dir.mkdir(exist_ok=True)
|
||
|
|
|
||
|
|
print("=" * 80)
|
||
|
|
print("MY OPTIMIZATION STUDY")
|
||
|
|
print("=" * 80)
|
||
|
|
```
|
||
|
|
|
||
|
|
**After:**
|
||
|
|
```python
|
||
|
|
def main():
|
||
|
|
study_dir = Path(__file__).parent
|
||
|
|
results_dir = study_dir / "2_results"
|
||
|
|
results_dir.mkdir(exist_ok=True)
|
||
|
|
|
||
|
|
# Initialize logger with file logging
|
||
|
|
logger = get_logger(
|
||
|
|
"my_study_name",
|
||
|
|
study_dir=results_dir
|
||
|
|
)
|
||
|
|
|
||
|
|
logger.info("=" * 80)
|
||
|
|
logger.info("MY OPTIMIZATION STUDY")
|
||
|
|
logger.info("=" * 80)
|
||
|
|
```
|
||
|
|
|
||
|
|
### Step 3: Replace print() with logger calls
|
||
|
|
|
||
|
|
**Basic Replacements:**
|
||
|
|
```python
|
||
|
|
# Before
|
||
|
|
print("Starting optimization...")
|
||
|
|
print(f"[ERROR] Simulation failed")
|
||
|
|
print(f"[WARNING] Constraint violated")
|
||
|
|
|
||
|
|
# After
|
||
|
|
logger.info("Starting optimization...")
|
||
|
|
logger.error("Simulation failed")
|
||
|
|
logger.warning("Constraint violated")
|
||
|
|
```
|
||
|
|
|
||
|
|
### Step 4: Use Structured Trial Logging
|
||
|
|
|
||
|
|
**Trial Start - Before:**
|
||
|
|
```python
|
||
|
|
print(f"\n{'='*60}")
|
||
|
|
print(f"Trial #{trial.number}")
|
||
|
|
print(f"{'='*60}")
|
||
|
|
print(f"Design Variables:")
|
||
|
|
for name, value in design_vars.items():
|
||
|
|
print(f" {name}: {value:.3f}")
|
||
|
|
```
|
||
|
|
|
||
|
|
**Trial Start - After:**
|
||
|
|
```python
|
||
|
|
logger.trial_start(trial.number, design_vars)
|
||
|
|
```
|
||
|
|
|
||
|
|
**Trial Complete - Before:**
|
||
|
|
```python
|
||
|
|
print(f"\nTrial #{trial.number} COMPLETE")
|
||
|
|
print("Objectives:")
|
||
|
|
for name, value in objectives.items():
|
||
|
|
print(f" {name}: {value:.4f}")
|
||
|
|
print("Constraints:")
|
||
|
|
for name, value in constraints.items():
|
||
|
|
print(f" {name}: {value:.4f}")
|
||
|
|
print("[OK] Feasible" if feasible else "[WARNING] Infeasible")
|
||
|
|
```
|
||
|
|
|
||
|
|
**Trial Complete - After:**
|
||
|
|
```python
|
||
|
|
logger.trial_complete(
|
||
|
|
trial.number,
|
||
|
|
objectives=objectives,
|
||
|
|
constraints=constraints,
|
||
|
|
feasible=feasible
|
||
|
|
)
|
||
|
|
```
|
||
|
|
|
||
|
|
**Trial Failed - Before:**
|
||
|
|
```python
|
||
|
|
print(f"\n[ERROR] Trial #{trial.number} FAILED")
|
||
|
|
print(f"Error: {error_message}")
|
||
|
|
import traceback
|
||
|
|
traceback.print_exc()
|
||
|
|
```
|
||
|
|
|
||
|
|
**Trial Failed - After:**
|
||
|
|
```python
|
||
|
|
logger.trial_failed(trial.number, error_message)
|
||
|
|
logger.error("Full traceback:", exc_info=True)
|
||
|
|
```
|
||
|
|
|
||
|
|
### Step 5: Use Study Lifecycle Methods
|
||
|
|
|
||
|
|
**Study Start - Before:**
|
||
|
|
```python
|
||
|
|
print("=" * 80)
|
||
|
|
print(f"OPTIMIZATION STUDY: {study_name}")
|
||
|
|
print("=" * 80)
|
||
|
|
print(f"Trials: {n_trials}")
|
||
|
|
print(f"Sampler: {sampler}")
|
||
|
|
print("=" * 80)
|
||
|
|
```
|
||
|
|
|
||
|
|
**Study Start - After:**
|
||
|
|
```python
|
||
|
|
logger.study_start(study_name, n_trials=n_trials, sampler=sampler)
|
||
|
|
```
|
||
|
|
|
||
|
|
**Study Complete - Before:**
|
||
|
|
```python
|
||
|
|
print("=" * 80)
|
||
|
|
print(f"STUDY COMPLETE: {study_name}")
|
||
|
|
print("=" * 80)
|
||
|
|
print(f"Total trials: {n_trials}")
|
||
|
|
print(f"Successful: {n_successful}")
|
||
|
|
print(f"Failed/Pruned: {n_trials - n_successful}")
|
||
|
|
print("=" * 80)
|
||
|
|
```
|
||
|
|
|
||
|
|
**Study Complete - After:**
|
||
|
|
```python
|
||
|
|
logger.study_complete(study_name, n_trials=n_trials, n_successful=n_successful)
|
||
|
|
```
|
||
|
|
|
||
|
|
## Complete Example
|
||
|
|
|
||
|
|
### Before (Old Style)
|
||
|
|
|
||
|
|
```python
|
||
|
|
import sys
|
||
|
|
from pathlib import Path
|
||
|
|
import optuna
|
||
|
|
|
||
|
|
def main():
|
||
|
|
study_dir = Path(__file__).parent
|
||
|
|
results_dir = study_dir / "2_results"
|
||
|
|
results_dir.mkdir(exist_ok=True)
|
||
|
|
|
||
|
|
print("=" * 80)
|
||
|
|
print("MY OPTIMIZATION STUDY")
|
||
|
|
print("=" * 80)
|
||
|
|
|
||
|
|
def objective(trial):
|
||
|
|
x = trial.suggest_float("x", -10, 10)
|
||
|
|
|
||
|
|
print(f"\nTrial #{trial.number}")
|
||
|
|
print(f"x = {x:.4f}")
|
||
|
|
|
||
|
|
try:
|
||
|
|
result = x ** 2
|
||
|
|
print(f"Result: {result:.4f}")
|
||
|
|
return result
|
||
|
|
except Exception as e:
|
||
|
|
print(f"[ERROR] Trial failed: {e}")
|
||
|
|
raise
|
||
|
|
|
||
|
|
study = optuna.create_study()
|
||
|
|
study.optimize(objective, n_trials=10)
|
||
|
|
|
||
|
|
print("\nOptimization complete!")
|
||
|
|
print(f"Best value: {study.best_value:.4f}")
|
||
|
|
|
||
|
|
if __name__ == "__main__":
|
||
|
|
main()
|
||
|
|
```
|
||
|
|
|
||
|
|
### After (New Style with Logger)
|
||
|
|
|
||
|
|
```python
|
||
|
|
import sys
|
||
|
|
from pathlib import Path
|
||
|
|
import optuna
|
||
|
|
from optimization_engine.logger import get_logger
|
||
|
|
|
||
|
|
def main():
|
||
|
|
study_dir = Path(__file__).parent
|
||
|
|
results_dir = study_dir / "2_results"
|
||
|
|
results_dir.mkdir(exist_ok=True)
|
||
|
|
|
||
|
|
# Initialize logger with file logging
|
||
|
|
logger = get_logger("my_study", study_dir=results_dir)
|
||
|
|
|
||
|
|
logger.study_start("my_study", n_trials=10, sampler="TPESampler")
|
||
|
|
|
||
|
|
def objective(trial):
|
||
|
|
x = trial.suggest_float("x", -10, 10)
|
||
|
|
|
||
|
|
logger.trial_start(trial.number, {"x": x})
|
||
|
|
|
||
|
|
try:
|
||
|
|
result = x ** 2
|
||
|
|
logger.trial_complete(
|
||
|
|
trial.number,
|
||
|
|
objectives={"f(x)": result},
|
||
|
|
feasible=True
|
||
|
|
)
|
||
|
|
return result
|
||
|
|
except Exception as e:
|
||
|
|
logger.trial_failed(trial.number, str(e))
|
||
|
|
logger.error("Full traceback:", exc_info=True)
|
||
|
|
raise
|
||
|
|
|
||
|
|
study = optuna.create_study()
|
||
|
|
study.optimize(objective, n_trials=10)
|
||
|
|
|
||
|
|
logger.study_complete("my_study", n_trials=10, n_successful=len(study.trials))
|
||
|
|
logger.info(f"Best value: {study.best_value:.4f}")
|
||
|
|
|
||
|
|
if __name__ == "__main__":
|
||
|
|
main()
|
||
|
|
```
|
||
|
|
|
||
|
|
## Benefits
|
||
|
|
|
||
|
|
After migration, you'll get:
|
||
|
|
|
||
|
|
1. **Color-coded console output** - Green for INFO, Yellow for WARNING, Red for ERROR
|
||
|
|
2. **Automatic file logging** - All output saved to `2_results/optimization.log`
|
||
|
|
3. **Log rotation** - Automatic rotation at 50MB with 3 backups
|
||
|
|
4. **Structured format** - Timestamps and module names in file logs
|
||
|
|
5. **Dashboard integration** - Trial logs in structured format for future parsing
|
||
|
|
|
||
|
|
## Log File Location
|
||
|
|
|
||
|
|
After migration, logs will be automatically saved to:
|
||
|
|
```
|
||
|
|
studies/your_study/
|
||
|
|
└── 2_results/
|
||
|
|
├── optimization.log # Current log file
|
||
|
|
├── optimization.log.1 # Backup 1 (most recent)
|
||
|
|
├── optimization.log.2 # Backup 2
|
||
|
|
└── optimization.log.3 # Backup 3 (oldest)
|
||
|
|
```
|
||
|
|
|
||
|
|
## Testing Your Migration
|
||
|
|
|
||
|
|
Run your migrated study with a single trial:
|
||
|
|
```bash
|
||
|
|
cd studies/your_study
|
||
|
|
python run_optimization.py --trials 1
|
||
|
|
```
|
||
|
|
|
||
|
|
Check:
|
||
|
|
- ✅ Console output is color-coded
|
||
|
|
- ✅ File created at `2_results/optimization.log`
|
||
|
|
- ✅ Trial start/complete messages format correctly
|
||
|
|
- ✅ No errors about missing imports
|
||
|
|
|
||
|
|
## Common Patterns
|
||
|
|
|
||
|
|
### Error Handling with Context
|
||
|
|
|
||
|
|
**Before:**
|
||
|
|
```python
|
||
|
|
try:
|
||
|
|
result = run_simulation()
|
||
|
|
except Exception as e:
|
||
|
|
print(f"[ERROR] Simulation failed: {e}")
|
||
|
|
import traceback
|
||
|
|
traceback.print_exc()
|
||
|
|
raise
|
||
|
|
```
|
||
|
|
|
||
|
|
**After:**
|
||
|
|
```python
|
||
|
|
try:
|
||
|
|
result = run_simulation()
|
||
|
|
except Exception as e:
|
||
|
|
logger.error(f"Simulation failed: {e}", exc_info=True)
|
||
|
|
raise
|
||
|
|
```
|
||
|
|
|
||
|
|
### Conditional Logging
|
||
|
|
|
||
|
|
```python
|
||
|
|
# Use log levels appropriately
|
||
|
|
logger.debug("Detailed debugging info") # Only when debugging
|
||
|
|
logger.info("Starting optimization...") # General progress
|
||
|
|
logger.warning("Design var out of bounds") # Potential issues
|
||
|
|
logger.error("Simulation failed") # Actual errors
|
||
|
|
```
|
||
|
|
|
||
|
|
### Progress Messages
|
||
|
|
|
||
|
|
```python
|
||
|
|
# Before
|
||
|
|
print(f"Processing trial {i}/{total}...")
|
||
|
|
|
||
|
|
# After
|
||
|
|
logger.info(f"Processing trial {i}/{total}...")
|
||
|
|
```
|
||
|
|
|
||
|
|
## Next Steps
|
||
|
|
|
||
|
|
After migrating your study:
|
||
|
|
|
||
|
|
1. Test with a few trials
|
||
|
|
2. Check the log file in `2_results/optimization.log`
|
||
|
|
3. Verify color output in console
|
||
|
|
4. Update study documentation if needed
|
||
|
|
|
||
|
|
## Questions?
|
||
|
|
|
||
|
|
See:
|
||
|
|
- [Phase 1.3 Implementation Plan](docs/07_DEVELOPMENT/Phase_1_3_Implementation_Plan.md)
|
||
|
|
- [optimization_engine/logger.py](optimization_engine/logger.py) - Full API documentation
|
||
|
|
- [drone_gimbal_arm_optimization](studies/drone_gimbal_arm_optimization/) - Reference implementation
|