9.5 KiB
9.5 KiB
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
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
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
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
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
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)
{
"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, endpointapp_error_rate: Errors per minute by error codeapp_validation_errors_total: Validation errors by fieldapp_auth_failures_total: Authentication failuresapp_database_errors_total: Database errorsapp_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
# Bad
raise Exception("User not found")
# Good
raise ResourceNotFoundError('User', user_id)
2. Provide Context
# 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
# 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
# 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
# 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
- Replace generic exceptions:
# 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
- Update validation:
# 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
- Use decorators:
# 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:
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
- Import errors: Ensure you import from
corepackage - Validation not working: Check decorator order (validate must be closest to function)
- Logs not appearing: Verify LOG_LEVEL environment variable
- Metrics missing: Ensure prometheus_client is installed
Debug Mode
In development, set:
app.config['DEBUG'] = True
This will:
- Include detailed error information
- Show stack traces
- Log to console with readable format