Initial commit
Dieser Commit ist enthalten in:
456
v2_adminpanel/ERROR_HANDLING_GUIDE.md
Normale Datei
456
v2_adminpanel/ERROR_HANDLING_GUIDE.md
Normale Datei
@ -0,0 +1,456 @@
|
||||
# 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
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren