456 Zeilen
9.5 KiB
Markdown
456 Zeilen
9.5 KiB
Markdown
# Error Handling Guide
|
|
|
|
## Overview
|
|
|
|
This guide describes the error handling system implemented in the v2_adminpanel application. The system provides:
|
|
|
|
- Centralized error handling with custom exception hierarchy
|
|
- Input validation framework
|
|
- Structured logging
|
|
- Monitoring and alerting
|
|
- Consistent error responses
|
|
|
|
## Architecture
|
|
|
|
### 1. Custom Exception Hierarchy
|
|
|
|
```
|
|
BaseApplicationException
|
|
├── ValidationException
|
|
│ ├── InputValidationError
|
|
│ ├── BusinessRuleViolation
|
|
│ └── DataIntegrityError
|
|
├── AuthenticationException
|
|
│ ├── InvalidCredentialsError
|
|
│ ├── SessionExpiredError
|
|
│ └── InsufficientPermissionsError
|
|
├── DatabaseException
|
|
│ ├── ConnectionError
|
|
│ ├── QueryError
|
|
│ └── TransactionError
|
|
├── ExternalServiceException
|
|
│ ├── APIError
|
|
│ └── TimeoutError
|
|
└── ResourceException
|
|
├── ResourceNotFoundError
|
|
├── ResourceConflictError
|
|
└── ResourceLimitExceeded
|
|
```
|
|
|
|
### 2. Core Components
|
|
|
|
- **core/exceptions.py**: Custom exception classes
|
|
- **core/error_handlers.py**: Global error handlers and decorators
|
|
- **core/validators.py**: Input validation framework
|
|
- **core/logging_config.py**: Structured logging setup
|
|
- **core/monitoring.py**: Error metrics and alerting
|
|
- **middleware/error_middleware.py**: Request-level error handling
|
|
|
|
## Usage Examples
|
|
|
|
### 1. Raising Custom Exceptions
|
|
|
|
```python
|
|
from core.exceptions import (
|
|
InputValidationError,
|
|
ResourceNotFoundError,
|
|
BusinessRuleViolation
|
|
)
|
|
|
|
# Validation error
|
|
if not email_is_valid:
|
|
raise InputValidationError(
|
|
field='email',
|
|
message='Invalid email format',
|
|
value=email_value,
|
|
expected_type='email'
|
|
)
|
|
|
|
# Resource not found
|
|
user = db.get_user(user_id)
|
|
if not user:
|
|
raise ResourceNotFoundError(
|
|
resource_type='User',
|
|
resource_id=user_id
|
|
)
|
|
|
|
# Business rule violation
|
|
if active_licenses >= license_limit:
|
|
raise BusinessRuleViolation(
|
|
rule='license_limit',
|
|
message='License limit exceeded',
|
|
context={
|
|
'current': active_licenses,
|
|
'limit': license_limit
|
|
}
|
|
)
|
|
```
|
|
|
|
### 2. Using Error Decorators
|
|
|
|
```python
|
|
from core.error_handlers import handle_errors, validate_request
|
|
from core.validators import validate
|
|
|
|
@handle_errors(
|
|
catch=(psycopg2.Error,),
|
|
message='Database operation failed',
|
|
user_message='Datenbankfehler aufgetreten',
|
|
redirect_to='admin.dashboard'
|
|
)
|
|
def update_customer(customer_id):
|
|
# Database operations
|
|
pass
|
|
|
|
@validate_request(
|
|
required_fields={
|
|
'email': str,
|
|
'age': int,
|
|
'active': bool
|
|
}
|
|
)
|
|
def create_user():
|
|
# Request data is validated
|
|
pass
|
|
|
|
@validate({
|
|
'email': {
|
|
'type': 'email',
|
|
'required': True
|
|
},
|
|
'password': {
|
|
'type': 'password',
|
|
'required': True
|
|
},
|
|
'age': {
|
|
'type': 'integer',
|
|
'required': True,
|
|
'min_value': 18,
|
|
'max_value': 120
|
|
}
|
|
})
|
|
def register_user():
|
|
# Access validated data
|
|
data = request.validated_data
|
|
# Use data safely
|
|
```
|
|
|
|
### 3. Input Validation
|
|
|
|
```python
|
|
from core.validators import Validators
|
|
|
|
# Email validation
|
|
email = Validators.email(user_input, field_name='email')
|
|
|
|
# Phone validation
|
|
phone = Validators.phone(user_input, field_name='phone')
|
|
|
|
# License key validation
|
|
license_key = Validators.license_key(user_input)
|
|
|
|
# Integer with constraints
|
|
age = Validators.integer(
|
|
user_input,
|
|
field_name='age',
|
|
min_value=0,
|
|
max_value=150
|
|
)
|
|
|
|
# String with constraints
|
|
username = Validators.string(
|
|
user_input,
|
|
field_name='username',
|
|
min_length=3,
|
|
max_length=50,
|
|
safe_only=True
|
|
)
|
|
|
|
# Password validation
|
|
password = Validators.password(user_input)
|
|
|
|
# Custom enum validation
|
|
status = Validators.enum(
|
|
user_input,
|
|
field_name='status',
|
|
allowed_values=['active', 'inactive', 'pending']
|
|
)
|
|
```
|
|
|
|
### 4. Error Context Manager
|
|
|
|
```python
|
|
from core.error_handlers import ErrorContext
|
|
|
|
with ErrorContext(
|
|
operation='create_license',
|
|
resource_type='License',
|
|
resource_id=license_key
|
|
):
|
|
# Operations that might fail
|
|
db.insert_license(license_data)
|
|
# Errors are automatically logged with context
|
|
```
|
|
|
|
### 5. Logging
|
|
|
|
```python
|
|
from core.logging_config import get_logger, log_error, log_security_event
|
|
|
|
logger = get_logger(__name__)
|
|
|
|
# Standard logging
|
|
logger.info('User created', extra={
|
|
'user_id': user.id,
|
|
'email': user.email
|
|
})
|
|
|
|
# Error logging
|
|
try:
|
|
risky_operation()
|
|
except Exception as e:
|
|
log_error(
|
|
logger,
|
|
'Risky operation failed',
|
|
error=e,
|
|
user_id=user.id,
|
|
operation='risky_operation'
|
|
)
|
|
|
|
# Security event logging
|
|
log_security_event(
|
|
'INVALID_LOGIN_ATTEMPT',
|
|
'Multiple failed login attempts',
|
|
username=username,
|
|
ip_address=request.remote_addr,
|
|
attempt_count=5
|
|
)
|
|
```
|
|
|
|
## Error Response Format
|
|
|
|
### JSON Responses (API)
|
|
|
|
```json
|
|
{
|
|
"error": {
|
|
"code": "VALIDATION_ERROR",
|
|
"message": "Invalid input provided",
|
|
"timestamp": "2024-01-15T10:30:00Z",
|
|
"request_id": "550e8400-e29b-41d4-a716-446655440000",
|
|
"details": {
|
|
"field": "email",
|
|
"value": "invalid-email",
|
|
"expected_type": "email"
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### HTML Responses
|
|
|
|
- User-friendly error messages
|
|
- Error code and request ID for support
|
|
- Helpful suggestions for resolution
|
|
- Navigation options (back, dashboard, retry)
|
|
|
|
## Monitoring and Alerts
|
|
|
|
### Metrics Exposed
|
|
|
|
- `app_errors_total`: Total error count by code, status, endpoint
|
|
- `app_error_rate`: Errors per minute by error code
|
|
- `app_validation_errors_total`: Validation errors by field
|
|
- `app_auth_failures_total`: Authentication failures
|
|
- `app_database_errors_total`: Database errors
|
|
- `app_request_duration_seconds`: Request duration histogram
|
|
|
|
### Alert Thresholds
|
|
|
|
- Error rate > 10/min: Critical alert
|
|
- Auth failure rate > 5/min: Security alert
|
|
- DB error rate > 3/min: Infrastructure alert
|
|
- Response time 95th percentile > 2s: Performance alert
|
|
|
|
### Accessing Metrics
|
|
|
|
- Prometheus metrics: `/metrics`
|
|
- Active alerts: `/api/alerts`
|
|
|
|
## Best Practices
|
|
|
|
### 1. Always Use Specific Exceptions
|
|
|
|
```python
|
|
# Bad
|
|
raise Exception("User not found")
|
|
|
|
# Good
|
|
raise ResourceNotFoundError('User', user_id)
|
|
```
|
|
|
|
### 2. Provide Context
|
|
|
|
```python
|
|
# Bad
|
|
raise ValidationException("Invalid data")
|
|
|
|
# Good
|
|
raise InputValidationError(
|
|
field='email',
|
|
message='Email domain not allowed',
|
|
value=email,
|
|
expected_type='corporate_email'
|
|
)
|
|
```
|
|
|
|
### 3. Handle Database Errors
|
|
|
|
```python
|
|
# Bad
|
|
result = db.execute(query)
|
|
|
|
# Good
|
|
try:
|
|
result = db.execute(query)
|
|
except psycopg2.IntegrityError as e:
|
|
if e.pgcode == '23505':
|
|
raise DataIntegrityError(
|
|
entity='User',
|
|
constraint='unique_email',
|
|
message='Email already exists'
|
|
)
|
|
raise
|
|
```
|
|
|
|
### 4. Validate Early
|
|
|
|
```python
|
|
# Bad
|
|
def process_order(data):
|
|
# Process without validation
|
|
total = data['quantity'] * data['price']
|
|
|
|
# Good
|
|
@validate({
|
|
'quantity': {'type': 'integer', 'min_value': 1},
|
|
'price': {'type': 'float', 'min_value': 0}
|
|
})
|
|
def process_order():
|
|
data = request.validated_data
|
|
total = data['quantity'] * data['price']
|
|
```
|
|
|
|
### 5. Log Security Events
|
|
|
|
```python
|
|
# Failed login attempts
|
|
log_security_event(
|
|
'LOGIN_FAILURE',
|
|
f'Failed login for user {username}',
|
|
username=username,
|
|
ip_address=request.remote_addr
|
|
)
|
|
|
|
# Suspicious activity
|
|
log_security_event(
|
|
'SUSPICIOUS_ACTIVITY',
|
|
'Rapid API requests detected',
|
|
ip_address=request.remote_addr,
|
|
request_count=count,
|
|
time_window=60
|
|
)
|
|
```
|
|
|
|
## Migration Guide
|
|
|
|
### Converting Existing Error Handling
|
|
|
|
1. **Replace generic exceptions**:
|
|
```python
|
|
# Old
|
|
except Exception as e:
|
|
flash(f"Error: {str(e)}", "error")
|
|
return redirect(url_for('admin.dashboard'))
|
|
|
|
# New
|
|
except DatabaseException as e:
|
|
# Already handled by global handler
|
|
raise
|
|
```
|
|
|
|
2. **Update validation**:
|
|
```python
|
|
# Old
|
|
email = request.form.get('email')
|
|
if not email or '@' not in email:
|
|
flash("Invalid email", "error")
|
|
return redirect(request.url)
|
|
|
|
# New
|
|
from core.validators import Validators
|
|
try:
|
|
email = Validators.email(request.form.get('email'))
|
|
except InputValidationError as e:
|
|
# Handled automatically
|
|
raise
|
|
```
|
|
|
|
3. **Use decorators**:
|
|
```python
|
|
# Old
|
|
@app.route('/api/user', methods=['POST'])
|
|
def create_user():
|
|
try:
|
|
# validation code
|
|
# database operations
|
|
except Exception as e:
|
|
return jsonify({'error': str(e)}), 500
|
|
|
|
# New
|
|
@app.route('/api/user', methods=['POST'])
|
|
@validate({
|
|
'email': {'type': 'email', 'required': True},
|
|
'name': {'type': 'string', 'required': True, 'min_length': 2}
|
|
})
|
|
def create_user():
|
|
data = request.validated_data
|
|
# Use validated data directly
|
|
```
|
|
|
|
## Testing
|
|
|
|
Run the test suite:
|
|
|
|
```bash
|
|
pytest v2_adminpanel/tests/test_error_handling.py -v
|
|
```
|
|
|
|
Test coverage includes:
|
|
- Exception creation and properties
|
|
- Error handler responses (JSON/HTML)
|
|
- Validation functions
|
|
- Decorators
|
|
- Monitoring and metrics
|
|
- Alert generation
|
|
|
|
## Troubleshooting
|
|
|
|
### Common Issues
|
|
|
|
1. **Import errors**: Ensure you import from `core` package
|
|
2. **Validation not working**: Check decorator order (validate must be closest to function)
|
|
3. **Logs not appearing**: Verify LOG_LEVEL environment variable
|
|
4. **Metrics missing**: Ensure prometheus_client is installed
|
|
|
|
### Debug Mode
|
|
|
|
In development, set:
|
|
```python
|
|
app.config['DEBUG'] = True
|
|
```
|
|
|
|
This will:
|
|
- Include detailed error information
|
|
- Show stack traces
|
|
- Log to console with readable format |