Мониторинг ошибок в продакшне

Stimulus — это легковесный JavaScript-фреймворк, ориентированный на улучшение взаимодействия с HTML без полной перестройки клиентской логики. В продакшн-среде крайне важно отслеживать ошибки, возникающие у пользователей, чтобы быстро реагировать на сбои и предотвращать ухудшение пользовательского опыта. Stimulus предоставляет несколько возможностей для интеграции систем мониторинга и управления исключениями.


Подключение и инициализация контроллеров

Stimulus контроллеры привязываются к DOM-элементам с помощью атрибутов data-controller. Для мониторинга ошибок рекомендуется создавать отдельный контроллер, который будет отвечать за глобальную обработку событий и перехват исключений в рамках своего контекста:

// controllers/error_monitor_controller.js
import { Controller } from "@hotwired/stimulus";

export default class extends Controller {
  connect() {
    this.attachGlobalErrorHandlers();
  }

  attachGlobalErrorHandlers() {
    window.addEventListener("error", (event) => this.handleError(event));
    window.addEventListener("unhandledrejection", (event) => this.handlePromiseRejection(event));
  }

  handleError(event) {
    const errorData = {
      message: event.message,
      filename: event.filename,
      lineno: event.lineno,
      colno: event.colno,
      stack: event.error?.stack
    };
    this.sendErrorToServer(errorData);
  }

  handlePromiseRejection(event) {
    const errorData = {
      message: event.reason?.message || "Unhandled promise rejection",
      stack: event.reason?.stack
    };
    this.sendErrorToServer(errorData);
  }

  sendErrorToServer(errorData) {
    fetch("/errors", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(errorData)
    });
  }
}

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

  • Использование глобальных слушателей событий error и unhandledrejection позволяет перехватывать синхронные ошибки и ошибки промисов.
  • Метод sendErrorToServer обеспечивает централизованную отправку информации на сервер для последующего анализа.
  • Важно сериализовать объект ошибки аккуратно, так как не все свойства могут быть корректно переданы в JSON.

Интеграция с внешними системами мониторинга

Для крупных приложений часто используется интеграция с системами вроде Sentry, Rollbar или Bugsnag. Stimulus-контроллер может выступать мостом между приложением и такими сервисами:

import { Controller } from "@hotwired/stimulus";
import * as Sentry from "@sentry/browser";

export default class extends Controller {
  connect() {
    Sentry.init({ dsn: "https://examplePublicKey@o0.ingest.sentry.io/0" });
    this.attachGlobalErrorHandlers();
  }

  attachGlobalErrorHandlers() {
    window.addEventListener("error", (event) => Sentry.captureException(event.error));
    window.addEventListener("unhandledrejection", (event) => Sentry.captureException(event.reason));
  }
}

Особенности интеграции:

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

Отдельные контроллеры для локального мониторинга

В сложных интерфейсах с множеством интерактивных компонентов целесообразно создавать контроллеры для локального мониторинга:

// controllers/button_monitor_controller.js
import { Controller } from "@hotwired/stimulus";

export default class extends Controller {
  static targets = ["button"]

  connect() {
    this.buttonTarget.addEventListener("click", this.safeClick.bind(this));
  }

  safeClick(event) {
    try {
      this.performAction(event);
    } catch (error) {
      this.reportError(error);
    }
  }

  performAction(event) {
    // Логика кнопки
  }

  reportError(error) {
    fetch("/errors", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ message: error.message, stack: error.stack })
    });
  }
}

Применение локального мониторинга:

  • Позволяет точно отслеживать ошибки конкретных элементов.
  • Минимизирует ложные срабатывания, характерные для глобальных обработчиков.
  • Удобно для асинхронных операций внутри отдельных компонентов.

Сбор дополнительной информации о состоянии приложения

Для эффективного мониторинга ошибок полезно собирать не только стек-трейс, но и контекст выполнения: состояние форм, выбранные пользователем опции, загруженные данные. В Stimulus это можно реализовать через дополнительные targets или значения контроллера:

static values = { userId: Number }

reportError(error) {
  const context = {
    userId: this.userIdValue,
    componentState: this.getComponentState()
  };

  fetch("/errors", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ message: error.message, stack: error.stack, context })
  });
}

getComponentState() {
  return {
    inputValue: this.inputTarget?.value || null,
    selectedOption: this.selectTarget?.value || null
  };
}

Преимущества контекстного мониторинга:

  • Быстрая диагностика причины ошибки.
  • Возможность воспроизведения сценария пользователя.
  • Улучшение качества исправлений и релизов.

Рекомендации по организации мониторинга

  1. Разделение уровней ответственности: глобальный контроллер для общих ошибок, локальные — для отдельных элементов.
  2. Асинхронная отправка данных: ошибки не должны блокировать работу интерфейса.
  3. Минимизация накладных расходов: подключение тяжелых библиотек мониторинга только в продакшне.
  4. Обогащение данных: включение пользовательского контекста, состояния форм и активных действий.
  5. Логирование и ретрансляция: хранение ошибок локально с возможностью повторной отправки при сетевых сбоях.

Stimulus обеспечивает простую и эффективную архитектуру для мониторинга ошибок, позволяя организовать как глобальное, так и локальное отслеживание, а также легко интегрироваться с внешними сервисами и расширять функциональность по мере роста приложения.