о
- Публичный аккаунт WeChat: внешний обруч (Love-FED)
- мой блог:Блог Рауба
- Знать столбец:передний обруч
предисловие
Привет всем, я снова встречаюсь с вами, на этот раз я поделюсь с вами некоторыми ключевыми моментами механизма захвата исключений и отчетности, которые вам нужно знать во внешнем мониторинге исключений, а также эталонных кодов и процессов реального боевого характера. .
Во-первых, зачем нам перехватывать и сообщать об исключениях?
Как говорится, в проекте, прошедшем большое тестирование и совместную отладку, иногда могут быть очень скрытые ошибки, такие сложные и непредсказуемые проблемы можно эффективно уменьшить только с помощью совершенного механизма мониторинга, поэтому для фронтенд-пользователей , захват исключений и отчетность имеют решающее значение.
Хотя на рынке есть несколько очень сложных систем внешнего мониторинга, таких какsentry,bugsnagИ т. д., но знание себя и своего врага может помочь вам выиграть сотню сражений.Только понимая принципы и логику, вы можете быть удобны, когда используете их.
метод отлова исключений
1. try catch
Обычно для того, чтобы судить, есть ли исключение в куске кода, напишем так:
try {
var a = 1;
var b = a + c;
} catch (e) {
// 捕获处理
console.log(e); // ReferenceError: c is not defined
}
Использование try catch может хорошо перехватывать исключения и обрабатывать их соответствующим образом, чтобы не привести к зависанию страницы, но у него есть некоторые недостатки, такие как необходимость оборачивать код, который перехватывает исключение, что приведет к раздуванию страницы. и не подходит для всего проекта.
2. window.onerror
По сравнению с try catch, window.onerror обеспечивает глобальную функцию прослушивания исключений:
window.onerror = function(errorMessage, scriptURI, lineNo, columnNo, error) {
console.log('errorMessage: ' + errorMessage); // 异常信息
console.log('scriptURI: ' + scriptURI); // 异常文件路径
console.log('lineNo: ' + lineNo); // 异常行号
console.log('columnNo: ' + columnNo); // 异常列号
console.log('error: ' + error); // 异常堆栈信息
};
console.log(a);
Как показано на рисунке:
window.onerror не только предоставляет нам неправильную информацию, но также предоставляет номер строки и столбца с ошибкой, которые могут быть точно расположены.Это, кажется, именно то, что мы хотим, но следующим шагом является процесс заполнения ямы.
проблема с перехватом исключений
1. Script error.
Мы разумно пытаемся перехватывать исключения на локальных страницах, например:
<!-- http://localhost:3031/ -->
<script>
window.onerror = function() {
console.log(arguments);
};
</script>
<script src="http://cdn.xxx.com/index.js"></script>
Здесь мы размещаем статические ресурсы во внешнем домене для оптимизации загрузки, но захваченная информация об исключении:
После анализа Window.onError не может захватить аномальную информацию после междоменного, поэтому решением является свойство Script для настройки crossorigin = «anonymous», а сервер добавляет Access-Control-Allow-Origin.<script src="http://cdn.xxx.com/index.js" crossorigin="anonymous"></script>
Общие веб-сайты CDN настроят Access-Control-Allow-Origin на *, что означает, что все домены могут быть доступны.
2. sourceMap
После решения междоменных задач или сохранения скриптов в одном домене вы можете сжать код перед публикацией.В настоящее время существует проблема, заключающаяся в том, что сжатый код не может найти исходное местоположение ошибки. Как показано на рисунке, мы используем webpack для упаковки и сжатия кода в bundle.js:
// webpack.config.js
var path = require('path');
// webpack 4.1.1
module.exports = {
mode: 'development',
entry: './client/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'client')
}
}
Наконец, файл сценария, представленный нашей страницей, выглядит следующим образом:
!function(e){var o={};function n(r){if(o[r])return o[r].exports;var t=o[r]={i:r,l:!1,exports:{}}...;
Итак, информация об исключении, которую мы видим, такова:
lineNo может быть очень маленьким числом, обычно 1, тогда как columnNo может быть очень большим числом, в данном случае 730, потому что весь код сжат в одну строку.Так как ты это решаешь? Умные детские туфли могут уже иметь возможность включить исходную карту, она права, мы можем использовать сжатие упаковки WebPack, чтобы создать файл карты для соответствующего скрипта для отслеживания, и открыть функцию Source-Map в WebPack:
module.exports = {
...
devtool: '#source-map',
...
}
Сжатый файл будет иметь этот комментарий в конце:
!function(e){var o={};function n(r){if(o[r])return o[r].exports;var t=o[r]={i:r,l:!1,exports:{}}...;
//# sourceMappingURL=bundle.js.map
Это означает, что файл карты, соответствующий этому файлу, — bundle.js.map. Ниже приведено содержимое файла исходной карты, который является объектом JSON:
version: 3, // Source map的版本
sources: ["webpack:///webpack/bootstrap", ...], // 转换前的文件
names: ["installedModules", "__webpack_require__", ...], // 转换前的所有变量名和属性名
mappings: "aACA,IAAAA,KAGA,SAAAC...", // 记录位置信息的字符串
file: "bundle.js", // 转换后的文件名
sourcesContent: ["// The module cache var installedModules = {};..."], // 源代码
sourceRoot: "" // 转换前的文件所在的目录
Если вы хотите узнать больше о sourceMap, вы можете перейти по ссылке:Подробная карта исходного кода JavaScript
Итак, поскольку мы получили файл карты соответствующего скрипта, как нам разобрать и получить ненормальную информацию о файле перед сжатием? Я представлю это, когда об исключении будет сообщено ниже.
3. Фреймворк MVVM
Сейчас все больше и больше проектов начинают использовать интерфейсные фреймворки.В MVVM фреймворке, если вы всегда хотите использовать window.onerror для отлова исключений, вас, скорее всего, сметет, или вы вообще не сможете их отловить, потому что ваша информация об исключении перехвачена собственным механизмом исключений фреймворка. НапримерВ Vue 2.x мы должны перехватывать такие глобальные исключения:
Vue.config.errorHandler = function (err, vm, info) {
let {
message, // 异常信息
name, // 异常名称
script, // 异常脚本url
line, // 异常行号
column, // 异常列号
stack // 异常堆栈信息
} = err;
// vm为抛出异常的 Vue 实例
// info为 Vue 特定的错误信息,比如错误所在的生命周期钩子
}
В настоящее время три информации о скрипте, строке и столбце печатаются как неопределенные, но эту информацию можно найти в стеке, которую можно получить путем обычного сопоставления, а затем сообщить.
Такой же способ обработки исключений предусмотрен и в React, а Error Boundary был представлен в версии React 16.x:
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
componentDidCatch(error, info) {
this.setState({ hasError: true });
// 将异常信息上报给服务器
logErrorToMyService(error, info);
}
render() {
if (this.state.hasError) {
return '出错了';
}
return this.props.children;
}
}
Затем мы можем использовать компонент следующим образом:
<ErrorBoundary>
<MyWidget />
</ErrorBoundary>
Подробности смотрите в официальной документации:Error Handling in React 16
отчет об исключении
Вышеприведенное вводит соответствующие точки знаний о захвате исключений переднего плана.Затем, поскольку мы успешно перехватили исключение, как мы должны сообщить об этом?
Если код скрипта не сжат, вы можете напрямую захватить и загрузить соответствующую информацию об исключении, которая здесь не будет представлена.Далее в основном объясняются общие методы обработки отчетов о сжатых файлах.
1. Отправить исключение
Когда исключение перехвачено, мы можем передать информацию об исключении в интерфейс, взяв в качестве примера window.onerror:
window.onerror = function(errorMessage, scriptURI, lineNo, columnNo, error) {
// 构建错误对象
var errorObj = {
errorMessage: errorMessage || null,
scriptURI: scriptURI || null,
lineNo: lineNo || null,
columnNo: columnNo || null,
stack: error && error.stack ? error.stack : null
};
if (XMLHttpRequest) {
var xhr = new XMLHttpRequest();
xhr.open('post', '/middleware/errorMsg', true); // 上报给node中间层处理
xhr.setRequestHeader('Content-Type', 'application/json'); // 设置请求头
xhr.send(JSON.stringify(errorObj)); // 发送参数
}
}
2. Анализ исходной карты
По сути, файл в формате source-map является типом данных. Раз это тип данных, то должен быть способ его парсинга. В настоящее время на рынке есть соответствующие инструменты для его парсинга. среда браузера или среда узла.Плагин под названием «source-map».
Требуя этот подключаемый модуль, внешний браузер может анализировать файл карты, но поскольку скорость обработки внешнего интерфейса низкая, здесь это не рекомендуется, мы по-прежнему используем серверный анализ. Если в вашем приложении есть средний уровень узла, вы можете отправить информацию об исключении на средний уровень, а затем проанализировать файл карты и передать данные на внутренний сервер.Код среднего уровня выглядит следующим образом:
const express = require('express');
const fs = require('fs');
const router = express.Router();
const fetch = require('node-fetch');
const sourceMap = require('source-map');
const path = require('path');
const resolve = file => path.resolve(__dirname, file);
// 定义post接口
router.post('/errorMsg/', function(req, res) {
let error = req.body; // 获取前端传过来的报错对象
let url = error.scriptURI; // 压缩文件路径
if (url) {
let fileUrl = url.slice(url.indexOf('client/')) + '.map'; // map文件路径
// 解析sourceMap
let smc = new sourceMap.SourceMapConsumer(fs.readFileSync(resolve('../' + fileUrl), 'utf8')); // 返回一个promise对象
smc.then(function(result) {
// 解析原始报错数据
let ret = result.originalPositionFor({
line: error.lineNo, // 压缩后的行号
column: error.columnNo // 压缩后的列号
});
let url = ''; // 上报地址
// 将异常上报至后台
fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
errorMessage: error.errorMessage, // 报错信息
source: ret.source, // 报错文件路径
line: ret.line, // 报错文件行号
column: ret.column, // 报错文件列号
stack: error.stack // 报错堆栈
})
}).then(function(response) {
return response.json();
}).then(function(json) {
res.json(json);
});
})
}
});
module.exports = router;
Здесь мы получаем адрес файла карты на стороне сервера через ненормальный путь к файлу, переданный из внешнего интерфейса, а затем передаем сжатый номер строки и столбца объекту обещания, возвращенному sourceMap для анализа.С помощью метода originalPositionFor мы можем получить исходная строка ошибки и номер столбца и адрес файла Наконец, необходимая информация об исключении равномерно передается в фоновое хранилище через ajax для завершения отчета об исключении. Как видно из рисунка ниже, консоль выводит реальное местоположение ошибки и файл после синтаксического анализа:
Прикрепил:source-map API
3. На заметку
Выше приведены основные точки знаний и процессы захвата исключений и отчетов.Есть также некоторые моменты, на которые необходимо обратить внимание.Например, если ваше приложение имеет большой доступ, небольшое исключение может привести к зависанию вашего сервера. , так что вы можете сделать это при составлении отчетов.Фильтрация и выборка информации и т. д., установите переключатель управления, сервер также может фильтровать похожие аномалии и не хранить несколько раз в течение определенного периода времени. Кроме того, захват исключения, такой как window.onerror, не может захватить информацию об ошибке исключения обещания, которая требует внимания.
Окончательная грубая блок-схема выглядит следующим образом:
Эпилог
Фронтенд-фиксация исключений и составление отчетов являются предпосылкой внешнего мониторинга исключений.Только понимание и хорошая работа по сбору и анализу аномальных данных могут реализовать полный механизм реагирования на ошибки и обработки, и, наконец, визуализацию данных. достигнуто. Подробный адрес кода примера этой статьи:GitHub.com/Ло Чжихао/О…