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

Page.js — это легковесная клиентская маршрутизирующая библиотека для JavaScript, которая позволяет управлять переходами между страницами без перезагрузки браузера. Несмотря на простоту использования, при работе с маршрутизатором могут возникать ошибки, связанные с некорректной маршрутизацией, неправильной обработкой URL или проблемами асинхронного кода. Для эффективного отлавливания таких ошибок используется логирование.

Логирование ошибок в Page.js играет ключевую роль при отладке сложных приложений и помогает выявлять источники проблем, не прерывая работу всего приложения.


Настройка глобального обработчика ошибок

Page.js предоставляет возможность определить глобальный обработчик ошибок через функцию page('*', callback), которая перехватывает все маршруты. Для логирования ошибок обычно используют блоки try...catch внутри обработчиков:

page('*', (ctx, next) => {
    try {
        next();
    } catch (error) {
        console.error('Ошибка в маршруте:', ctx.path, error);
    }
});

Пояснение:

  • ctx.path — текущий путь маршрута, где произошла ошибка.
  • next() — передает управление следующему обработчику маршрута.
  • console.error — выводит подробную информацию об ошибке в консоль.

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


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

Каждый маршрут Page.js может иметь несколько обработчиков. Для обеспечения безопасного выполнения и прозрачного логирования ошибок рекомендуется оборачивать логику маршрута в try...catch:

page('/dashboard', async (ctx, next) => {
    try {
        const data = await fetchData();
        renderDashboard(data);
    } catch (error) {
        console.error(`Ошибка при загрузке Dashboard по пути ${ctx.path}:`, error);
    }
});

Ключевые моменты:

  • Асинхронные функции обязательно оборачивать в try...catch, иначе ошибки могут остаться незамеченными.
  • Контекст маршрута ctx содержит полезную информацию: параметры (ctx.params), путь (ctx.path), query-параметры (ctx.querystring).

Разделение логирования ошибок по типу

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

  1. Ошибки сетевых запросов:
try {
    const response = await fetch('/api/data');
    if (!response.ok) throw new Error(`Сервер вернул статус ${response.status}`);
} catch (error) {
    console.error('Ошибка сетевого запроса:', error);
}
  1. Ошибки рендеринга интерфейса:
try {
    renderComponent(data);
} catch (error) {
    console.error('Ошибка рендеринга компонента:', error);
}
  1. Ошибки маршрутизации:
page('*', (ctx, next) => {
    try {
        next();
    } catch (error) {
        console.error('Ошибка маршрутизации на пути', ctx.path, error);
    }
});

Разделение ошибок позволяет быстро определить, на каком уровне возникла проблема — сервер, UI или маршрутизация.


Использование пользовательских логгеров

Помимо console.error, можно подключать сторонние сервисы логирования, например, Sentry или LogRocket:

page('*', (ctx, next) => {
    try {
        next();
    } catch (error) {
        Sentry.captureException(error, {
            extra: { path: ctx.path, params: ctx.params }
        });
        console.error('Ошибка маршрута отправлена в Sentry:', ctx.path, error);
    }
});

Преимущества:

  • Хранение истории ошибок на сервере.
  • Возможность отслеживания частоты и контекста ошибок.
  • Уведомления для команды разработчиков в реальном времени.

Асинхронные цепочки и логирование

Page.js поддерживает несколько middleware, вызываемых последовательно. В асинхронных цепочках важно корректно обрабатывать ошибки:

page('/profile', loadUserData, renderProfile);

async function loadUserData(ctx, next) {
    try {
        ctx.user = await fetchUser(ctx.params.id);
        await next();
    } catch (error) {
        console.error('Ошибка загрузки данных пользователя:', ctx.params.id, error);
    }
}

function renderProfile(ctx) {
    try {
        displayProfile(ctx.user);
    } catch (error) {
        console.error('Ошибка отображения профиля:', error);
    }
}

Рекомендации:

  • Каждая асинхронная функция должна иметь собственный блок try...catch.
  • Ошибки не должны прерывать выполнение других маршрутов, если это не критично.

Форматирование и структурирование логов

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

console.error({
    type: 'RoutingError',
    path: ctx.path,
    params: ctx.params,
    message: error.message,
    stack: error.stack
});

Преимущества такого подхода:

  • Легко фильтровать по типу ошибки.
  • Удобно отправлять данные на сервер или в сторонние сервисы.
  • Быстрее выявлять повторяющиеся проблемы в больших приложениях.

Отслеживание ошибок навигации

Иногда ошибки могут происходить не в обработчиках, а при самой навигации Page.js:

page('*', (ctx, next) => {
    try {
        if (!isValidPath(ctx.path)) throw new Error('Недопустимый путь');
        next();
    } catch (error) {
        console.error('Ошибка навигации:', ctx.path, error);
    }
});

Контроль за корректностью URL и параметров маршрута предотвращает неожиданные сбои при переходах.


Рекомендации по практическому применению

  • Логи должны содержать контекст (путь, параметры, состояние данных).
  • Асинхронные обработчики обязательно оборачивать в try...catch.
  • Для крупных проектов использовать централизованную систему логирования с отправкой данных на сервер.
  • Разделять типы ошибок (сеть, UI, маршрутизация) для удобного анализа.

Такая организация логирования позволяет Page.js работать стабильно, а разработчикам — быстро идентифицировать и исправлять ошибки без ущерба для пользовательского опыта.