Как построить фронтальную систему мониторинга исключений

JavaScript

что является исключением

Это означает, что пользователь не может получить ожидаемый результат при использовании приложения. Различные аномалии имеют разную степень последствий, начиная от неприятных пользователей и заканчивая непригодными для использования продуктами в тяжелых случаях, что приводит к тому, что пользователи теряют признание продукта.

Зачем обрабатывать исключения

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

Каковы исключения в интерфейсе

аномальный частота
Исключения JavaScript (синтаксические ошибки, ошибки кода) часто
Исключение загрузки статических ресурсов (img, js, css) Иногда
Исключение запроса Ajax Иногда
обещать исключение меньше
исключение iframe меньше

Как ловить исключения

try-catch

try-catchМогут быть перехвачены только синхронные ошибки времени выполнения, но не перехвачены синтаксические и асинхронные ошибки.

1. Ошибка синхронизации

try {
    kill;
} catch(err) {
    console.error('try: ', err);
}

результат:try: ReferenceError: kill is not defined

2. Невозможно отловить синтаксические ошибки

try {
    let name = '1;
} catch(err) {
    console.error('try: ', err);
}

результат:Unterminated string constant

Компилятор может предотвратить запуск синтаксических ошибок.

3. Невозможно отловить асинхронные ошибки

try {
    setTimeout(() => {
        undefined.map(v => v);
    }, 1000);
} catch(err) {
    console.error('try: ', err);
}

результат:Uncaught TypeError: Cannot read property 'map' of undefined

window.onerror

когдаJavaScriptКогда возникают ошибки времени выполнения (включая синтаксические ошибки),windowвызоветErrorEventинтерфейсerrorсобытие и выполнитьwindow.onerror()Если функция возвращаетtrue, выполнение обработчика событий по умолчанию запрещено.

1. Ошибка синхронизации

/**
* @param {String}  message   错误信息
* @param {String}  source    出错文件
* @param {Number}  lineno    行号
* @param {Number}  colno     列号
* @param {Object}  error     error对象
*/
window.onerror = (message, source, lineno, colno, error) => {
    console.error('捕获异常:', message, source, lineno, colno, error);
    return true;
};

kill;

Результат: исключение захвата:Uncaught ReferenceError: kill is not defined

2. Невозможно отловить синтаксические ошибки

/**
* @param {String}  message   错误信息
* @param {String}  source    出错文件
* @param {Number}  lineno    行号
* @param {Number}  colno     列号
* @param {Object}  error     error对象
*/
window.onerror = (message, source, lineno, colno, error) => {
    console.error('捕获异常:', message, source, lineno, colno, error);
    return true;
};

let name = '1;

результат:Unterminated string constant

Компилятор может предотвратить запуск синтаксических ошибок.

3. Асинхронная ошибка

/**
* @param {String}  message   错误信息
* @param {String}  source    出错文件
* @param {Number}  lineno    行号
* @param {Number}  colno     列号
* @param {Object}  error     error对象
*/
window.onerror = (message, source, lineno, colno, error) => {
    console.error('捕获异常:', message, source, lineno, colno, error);
    return true;
};

setTimeout(() => {
    undefined.map(v => v);
}, 1000);

результат:捕获异常: Uncaught TypeError: Cannot read property 'map' of undefined`

window.addEventListener('error')

Когда ресурс (например,<img>или<script>) не удалось загрузить, элемент, который загружает ресурс, вызоветEventинтерфейсerrorсобытие и выполнитьonerror()функция обработчика. ЭтиerrorСобытия не всплываютwindow, но (по крайней мере, в Firefox) может использоваться однимwindow.addEventListenerзахватывать.

<script>
window.addEventListener('error', (err) => {
    console.error('捕获异常:', err);
}, true);
</script>
<img src="./test.jpg" />

результат:捕获异常:Event {isTrusted: true, type: "error", target: img, currentTarget: Window, eventPhase: 1, …}

window.addEventListener('unhandledrejection')

когдаPromiseодеялоrejectи нетrejectКогда процессор, он сработаетunhandledrejectionсобытие; это может произойти вwindowвниз, но может возникать и вWorkerсередина. Это полезно для отладки резервной обработки ошибок.

window.addEventListener("unhandledrejection", (err) => {
    err.preventDefault();
    console.error('捕获异常:', err);
});

Promise.reject('promise');

результат:捕获异常:PromiseRejectionEvent {isTrusted: true, promise: Promise, reason: "promise", type: "unhandledrejection", target: Window, …}

Vue

Vue.config.errorHandler = (err, vm, info) => {
  console.error('捕获异常:', err, vm, info);
}

React

React16, который предоставляет встроенную функциюcomponentDidCatch, используя его, можно очень просто получитьReactсообщение об ошибке ниже.

componentDidCatch(error, info) {
    console.error('捕获异常:', error, info);
}

Тем не менее, рекомендуетсяErrorBoundary

в пользовательском интерфейсеJavaScriptОшибки не должны нарушать работу всего приложения. чтобыReactпользователи решают эту проблему,React16Вводится новое понятие «границы ошибки».

новыйErrorBoundary.jsxКомпоненты:

import React from 'react';
import { Result, Button } from 'antd';

class ErrorBoundary extends React.Component {
    constructor(props) {
        super(props);
        this.state = { hasError: false, info: '' };
    }

    static getDerivedStateFromError(error) {
        return { hasError: true };
    }

    componentDidCatch(error, info) {
        this.setState({
            info: error + ''
        });
    }

    render() {
        if (this.state.hasError) {
            // 你可以渲染任何自定义的降级 UI
            return (
                <Result
                    status="500"
                    title="500"
                    subTitle={this.state.info}
                    extra={<Button type="primary">Report feedback</Button>}
                />
            );
        }

        return this.props.children;
    }
}

export default ErrorBoundary;

использовать:

<ErrorBoundary>
    <App />
</ErrorBoundary>

Уведомление

Граничная ошибка не фиксирует ошибки в следующих областях:

  • обработчик события
  • Асинхронный код (например,setTimeoutилиrequestAnimationFrameПерезвоните)
  • Рендеринг на стороне сервера
  • Ошибки, возникающие в самой границе ошибки (а не в ее дочерних элементах)

iframe

Из-за «Политики того же происхождения», установленной браузером, она не может быть обработана очень изящно.iframeИсключение, помимо основных свойств, таких как его ширина и высота, не может быть получено изiframeПолучите много информации.

<script>
    document.getElementById("myiframe").onload = () => {
        const self = document.getElementById('myiframe');

        try {
            (self.contentWindow || self.contentDocument).location.href;
        } catch(err) {
            console.log('捕获异常:' + err);
        }
    };
</script>

<iframe id="myiframe" src="https://nibuzhidao.com" frameBorder="0" />

Sentry

Очень отличный продукт в индустрии для мониторинга исключений, автор тоже использует этот продукт, и документация полная.

Какую информацию необходимо сообщить

  • неправильный идентификатор
  • ID пользователя
  • имя пользователя
  • IP-адрес пользователя
  • оборудование
  • сообщение об ошибке
  • браузер
  • версия системы
  • Версия приложения
  • модель
  • отметка времени
  • Уровень исключения (ошибка, предупреждение, информация)

отчет об исключении

1. Ajax отправляет данные

2. Динамически создавать теги img

Если количество аномальных данных велико, что приводит к высокой нагрузке на сервер, отрегулируйте частоту отправки (вы можете рассмотреть возможность хранения аномальной информации на клиенте, установить порог времени и сообщить об этом) или установить скорость сбора (скорость сбора должна быть установлен в соответствии с реальной ситуацией, случайным числом или некоторыми характеристиками пользователя.

блок-схема

异常监控流程图

использованная литература

блог

Добро пожаловать на мой блог