# Документация по обработке ошибок ## Архитектура обработки ошибок Система использует многоуровневую обработку ошибок: 1. **Error Boundary** - для ошибок React компонентов 2. **API Error Handler** - для ошибок API routes 3. **User-Friendly Messages** - понятные сообщения для пользователей 4. **Sentry Integration** - мониторинг критических ошибок 5. **Logging** - логирование всех ошибок --- ## Error Boundary ### Использование Error Boundary автоматически обернут вокруг всего приложения в `app/layout.tsx`. Для обертки отдельных компонентов: ```typescript import { ErrorBoundary } from '@/components/ErrorBoundary'; function MyComponent() { return ( Произошла ошибка} onError={(error, errorInfo) => { console.error('Component error:', error); }} > ); } ``` ### Кастомный fallback ```typescript

Ошибка в компоненте

} >
``` --- ## API Error Handler ### Использование в API routes ```typescript import { handleError, AppError, Errors } from '@/lib/error-handler'; export async function POST(request: Request) { try { // Ваш код if (!authorized) { throw Errors.UNAUTHORIZED; } } catch (error) { return handleError(error, { path: '/api/endpoint', method: 'POST', userId: 'user-id', }); } } ``` ### Создание кастомных ошибок ```typescript throw new AppError( 'Сообщение об ошибке', 'ERROR_CODE', 400, { additional: 'data' } ); ``` --- ## Понятные сообщения для пользователей ### Автоматическое преобразование Система автоматически преобразует технические ошибки в понятные сообщения: ```typescript import { getUserFriendlyError } from '@/lib/errors/user-friendly-messages'; try { // Код, который может выбросить ошибку } catch (error) { const friendly = getUserFriendlyError(error); // friendly.title - заголовок // friendly.message - понятное сообщение // friendly.type - тип (error/warning/info) // friendly.action - рекомендуемое действие } ``` ### С контекстом ```typescript import { getContextualErrorMessage } from '@/lib/errors/user-friendly-messages'; const friendly = getContextualErrorMessage(error, { action: 'сохранении данных', resource: 'воздушное судно', operation: 'create', }); ``` --- ## Компоненты для отображения ошибок ### ErrorDisplay ```typescript import ErrorDisplay from '@/components/ErrorDisplay'; refetch()} onClose={() => setError(null)} showDetails={true} details={error.stack} /> ``` ### ErrorAlert (улучшенный) ```typescript import ErrorAlert from '@/components/ErrorAlert'; retry()} onClose={() => clearError()} /> ``` --- ## Хук useErrorHandler ```typescript import { useErrorHandler } from '@/hooks/useErrorHandler'; function MyComponent() { const { error, userFriendlyError, handleError, clearError, hasError } = useErrorHandler({ onError: (error) => { // Дополнительная обработка }, logError: true, sendToSentry: true, }); const fetchData = async () => { try { // Загрузка данных } catch (err) { handleError(err, { action: 'загрузке данных', resource: 'воздушные суда', }); } }; if (hasError && userFriendlyError) { return ( ); } return
...
; } ``` --- ## Интеграция с Sentry ### Настройка 1. Создайте аккаунт на [sentry.io](https://sentry.io) 2. Создайте проект и получите DSN 3. Добавьте в `.env.local`: ```env NEXT_PUBLIC_SENTRY_DSN=your-dsn-here SENTRY_ORG=your-org SENTRY_PROJECT=your-project ``` ### Использование ```typescript import { captureException, captureMessage, setUserContext } from '@/lib/monitoring/sentry'; // Отправка исключения try { // Код } catch (error) { captureException(error, { component: 'MyComponent', action: 'fetchData', }); } // Отправка сообщения captureMessage('Важное событие', 'warning', { userId: '123', }); // Установка контекста пользователя setUserContext('user-id', 'user@example.com', 'username'); ``` ### Автоматическая отправка Критические ошибки автоматически отправляются в Sentry: - Ошибки базы данных - Ошибки подключения - Ошибки таймаута - Ошибки 500+ --- ## Логирование ошибок Все ошибки логируются через Winston: ```typescript import { logError, logSecurity, logAudit } from '@/lib/logger'; // Обычная ошибка logError('Ошибка загрузки данных', error, { userId: '123', component: 'AircraftList', }); // Ошибка безопасности logSecurity('Попытка несанкционированного доступа', { ip: '192.168.1.1', path: '/api/admin', }); // Аудит logAudit('CREATE_AIRCRAFT', 'aircraft', { userId: '123', aircraftId: 'RA-12345', }); ``` Логи сохраняются в: - `logs/error.log` - только ошибки - `logs/combined.log` - все логи --- ## Коды ошибок Используйте предопределенные коды ошибок: ```typescript import { ErrorCode } from '@/lib/errors/error-codes'; throw new AppError('Сообщение', ErrorCode.NETWORK_ERROR, 500); ``` Доступные коды: - `NETWORK_ERROR` - ошибка сети - `VALIDATION_ERROR` - ошибка валидации - `UNAUTHORIZED` - не авторизован - `FORBIDDEN` - доступ запрещен - `NOT_FOUND` - не найдено - `DATABASE_ERROR` - ошибка БД - `RATE_LIMIT_EXCEEDED` - превышен лимит - И другие... --- ## Best Practices 1. **Всегда обрабатывайте ошибки** - используйте try/catch 2. **Логируйте с контекстом** - добавляйте информацию о действии 3. **Показывайте понятные сообщения** - используйте `getUserFriendlyError` 4. **Используйте Error Boundary** - для изоляции ошибок компонентов 5. **Отправляйте критические ошибки в Sentry** - для мониторинга 6. **Не показывайте технические детали** - только в режиме разработки --- ## Примеры ### Полный пример компонента с обработкой ошибок ```typescript 'use client'; import { useState } from 'react'; import { useErrorHandler } from '@/hooks/useErrorHandler'; import ErrorDisplay from '@/components/ErrorDisplay'; import { aircraftApi } from '@/lib/api/api-client'; export default function AircraftList() { const { error, userFriendlyError, handleError, clearError, hasError } = useErrorHandler(); const [aircraft, setAircraft] = useState([]); const [loading, setLoading] = useState(false); const fetchAircraft = async () => { try { setLoading(true); clearError(); const data = await aircraftApi.list(); setAircraft(data.items ?? []); } catch (err) { handleError(err, { action: 'загрузке списка воздушных судов', resource: 'aircraft', }); } finally { setLoading(false); } }; if (hasError && userFriendlyError) { return ( ); } if (loading) return
Загрузка...
; return (
{aircraft.map(a =>
{a.registrationNumber}
)}
); } ``` --- **Дата создания:** 2025-01-21 **Версия:** 1.0