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 обеспечивает централизованную
отправку информации на сервер для последующего анализа.Для крупных приложений часто используется интеграция с системами вроде 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));
}
}
Особенности интеграции:
В сложных интерфейсах с множеством интерактивных компонентов целесообразно создавать контроллеры для локального мониторинга:
// 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
};
}
Преимущества контекстного мониторинга:
Stimulus обеспечивает простую и эффективную архитектуру для мониторинга ошибок, позволяя организовать как глобальное, так и локальное отслеживание, а также легко интегрироваться с внешними сервисами и расширять функциональность по мере роста приложения.