Логирование ошибок маршрутизации

Universal Router — это гибкая библиотека маршрутизации для JavaScript, которая позволяет создавать маршруты с динамическими параметрами, асинхронной обработкой и вложенной структурой. При работе с Universal Router ошибки маршрутизации неизбежны: пользователь может перейти по несуществующему пути, сервер может вернуть некорректные данные, а хэндлер маршрута может завершиться с исключением. Для отслеживания таких событий используется логирование ошибок.

Логирование ошибок в Universal Router строится на обработке исключений в промисах, так как метод router.resolve() всегда возвращает промис, который может завершиться успешно или с ошибкой.

import UniversalRouter from 'universal-router';

const routes = [
  {
    path: '/user/:id',
    action: async ({ params }) => {
      if (!params.id) throw new Error('ID пользователя не указан');
      return `Пользователь ${params.id}`;
    }
  }
];

const router = new UniversalRouter(routes);

router.resolve('/user/')
  .then(result => console.log(result))
  .catch(error => console.error('Ошибка маршрутизации:', error));

В примере выше, если путь /user/ не содержит параметр id, произойдет выброс ошибки, которая будет поймана методом .catch(). Это основной механизм первичного логирования.

Расширенное логирование с контекстом

Universal Router позволяет передавать объект context в resolve(). Внутри маршрутов этот объект доступен и может использоваться для хранения контекста запроса, токенов аутентификации и функций логирования. Такой подход упрощает централизованное логирование ошибок.

const context = {
  logger: {
    error: (message, details) => {
      console.error(message, details);
    }
  }
};

const routes = [
  {
    path: '/profile/:id',
    action: async ({ params, context }) => {
      if (!params.id) {
        context.logger.error('Маршрут profile вызван без id', { path: '/profile/:id' });
        throw new Error('ID профиля отсутствует');
      }
      return `Профиль ${params.id}`;
    }
  }
];

router.resolve('/profile/', context)
  .catch(err => {
    context.logger.error('Глобальная ошибка маршрутизации', { error: err });
  });

Использование контекста позволяет интегрировать Universal Router с любыми системами логирования: от console до внешних сервисов вроде Sentry или LogRocket.

Логирование асинхронных ошибок

Маршруты Universal Router могут быть асинхронными. Исключения, возникающие внутри async функций или промисов, автоматически передаются в .catch() после вызова resolve(). Для более детального анализа ошибок важно сохранять информацию о маршруте и параметрах запроса.

const routes = [
  {
    path: '/data/:id',
    action: async ({ params, context }) => {
      try {
        const data = await fetchData(params.id);
        if (!data) throw new Error('Данные не найдены');
        return data;
      } catch (err) {
        context.logger.error('Ошибка загрузки данных', { params, error: err });
        throw err; // обязательно проброс ошибки дальше
      }
    }
  }
];

Пример показывает два ключевых момента: использование try/catch внутри маршрута для локального логирования и повторный выброс ошибки, чтобы глобальный обработчик также мог зафиксировать её.

Централизованная обработка ошибок

Для крупных приложений целесообразно создать глобальный обработчик ошибок маршрутизации, который будет собирать все исключения, возникшие при разрешении маршрутов:

async function handleRoute(path) {
  try {
    const result = await router.resolve(path, { logger });
    console.log('Маршрут выполнен успешно:', result);
  } catch (err) {
    logger.error('Ошибка маршрутизации', {
      path,
      stack: err.stack,
      message: err.message
    });
    renderErrorPage(err); // функция рендеринга страницы ошибки
  }
}

Такой подход гарантирует, что любые ошибки — как синхронные, так и асинхронные — будут зафиксированы и обработаны одинаково, что облегчает отладку и мониторинг.

Логирование 404 и неопределённых маршрутов

Universal Router позволяет использовать маршрут с path: '(.*)' для перехвата всех неопределённых путей. Это удобно для логирования 404 ошибок.

const routes = [
  { path: '/home', action: () => 'Главная' },
  { path: '/about', action: () => 'О сайте' },
  { path: '(.*)', action: ({ path, context }) => {
    context.logger.error('Не найден маршрут', { path });
    return 'Страница не найдена';
  }}
];

Такой паттерн позволяет отлавливать все неопределённые маршруты и централизованно логировать их, обеспечивая полный контроль над ошибками навигации.

Советы по организации логирования

  • Использовать объект context для передачи логгера в маршруты.
  • Локально логировать ошибки там, где они возникают, и при необходимости пробрасывать их глобальному обработчику.
  • Сохранять путь запроса, параметры и стек вызовов для точного анализа.
  • Разграничивать ошибки навигации и ошибки данных, чтобы проще отслеживать причину сбоя.
  • Для асинхронных маршрутов всегда использовать try/catch и повторный выброс ошибки.

Эффективная система логирования в Universal Router повышает стабильность приложения, упрощает отладку и обеспечивает прозрачность всех ошибок маршрутизации.