Implement proper error boundaries to prevent app crashes
Frontend Error Boundaries: - Created ErrorBoundary class for centralized error handling - Wraps critical functions (transcribe, translate, TTS) with error boundaries - Global error handlers for unhandled errors and promise rejections - Component-specific error recovery with fallback functions - User-friendly error notifications with auto-dismiss - Error logging to backend for monitoring - Prevents cascading failures from component errors Backend Error Handling: - Added error boundary decorator for Flask routes - Global Flask error handlers (404, 500, generic exceptions) - Frontend error logging endpoint (/api/log-error) - Structured error responses with component information - Full traceback logging for debugging - Production vs development error message handling Features: - Graceful degradation when components fail - Automatic error recovery attempts - Error history tracking (last 50 errors) - Component-specific error handling - Production error monitoring ready - Prevents full app crashes from isolated errors 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
86
app.py
86
app.py
@@ -15,11 +15,33 @@ from cryptography.hazmat.primitives.asymmetric import ec
|
||||
from cryptography.hazmat.primitives import serialization
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
import gc # For garbage collection
|
||||
from functools import wraps
|
||||
import traceback
|
||||
|
||||
# Initialize logging
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Error boundary decorator for Flask routes
|
||||
def with_error_boundary(func):
|
||||
@wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
try:
|
||||
return func(*args, **kwargs)
|
||||
except Exception as e:
|
||||
# Log the full exception with traceback
|
||||
logger.error(f"Error in {func.__name__}: {str(e)}")
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
# Return appropriate error response
|
||||
error_message = str(e) if app.debug else "An internal error occurred"
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': error_message,
|
||||
'component': func.__name__
|
||||
}), 500
|
||||
return wrapper
|
||||
|
||||
app = Flask(__name__)
|
||||
app.config['UPLOAD_FOLDER'] = tempfile.mkdtemp()
|
||||
app.config['TTS_SERVER'] = os.environ.get('TTS_SERVER_URL', 'http://localhost:5050/v1/audio/speech')
|
||||
@@ -623,6 +645,29 @@ def get_audio(filename):
|
||||
logger.error(f"Audio retrieval error: {str(e)}")
|
||||
return jsonify({'error': f'Audio retrieval failed: {str(e)}'}), 500
|
||||
|
||||
# Error logging endpoint for frontend error reporting
|
||||
@app.route('/api/log-error', methods=['POST'])
|
||||
def log_error():
|
||||
"""Log frontend errors for monitoring"""
|
||||
try:
|
||||
error_data = request.json
|
||||
error_info = error_data.get('errorInfo', {})
|
||||
error_details = error_data.get('error', {})
|
||||
|
||||
# Log the error
|
||||
logger.error(f"Frontend error in {error_info.get('component', 'unknown')}: {error_details.get('message', 'No message')}")
|
||||
logger.error(f"Stack trace: {error_details.get('stack', 'No stack trace')}")
|
||||
logger.error(f"User agent: {error_info.get('userAgent', 'Unknown')}")
|
||||
logger.error(f"URL: {error_info.get('url', 'Unknown')}")
|
||||
|
||||
# In production, you might want to send this to a monitoring service
|
||||
# like Sentry, LogRocket, or your own analytics
|
||||
|
||||
return jsonify({'success': True})
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to log frontend error: {str(e)}")
|
||||
return jsonify({'success': False, 'error': str(e)}), 500
|
||||
|
||||
# Health check endpoints for monitoring
|
||||
@app.route('/health', methods=['GET'])
|
||||
def health_check():
|
||||
@@ -741,5 +786,46 @@ app.request_count = 0
|
||||
def before_request():
|
||||
app.request_count = getattr(app, 'request_count', 0) + 1
|
||||
|
||||
# Global error handlers
|
||||
@app.errorhandler(404)
|
||||
def not_found_error(error):
|
||||
logger.warning(f"404 error: {request.url}")
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': 'Resource not found',
|
||||
'status': 404
|
||||
}), 404
|
||||
|
||||
@app.errorhandler(500)
|
||||
def internal_error(error):
|
||||
logger.error(f"500 error: {str(error)}")
|
||||
logger.error(traceback.format_exc())
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': 'Internal server error',
|
||||
'status': 500
|
||||
}), 500
|
||||
|
||||
@app.errorhandler(Exception)
|
||||
def handle_exception(error):
|
||||
# Log the error
|
||||
logger.error(f"Unhandled exception: {str(error)}")
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
# Return JSON instead of HTML for HTTP errors
|
||||
if hasattr(error, 'code'):
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': str(error),
|
||||
'status': error.code
|
||||
}), error.code
|
||||
|
||||
# Non-HTTP exceptions
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': 'An unexpected error occurred',
|
||||
'status': 500
|
||||
}), 500
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(host='0.0.0.0', port=5005, debug=True)
|
||||
|
||||
Reference in New Issue
Block a user