○ 1. Фон
Болевые точки
Однодневный рекламодатель product:xxx сообщил, что наша страница не может быть зарегистрирована! Однодневная операция: Это событие зависает на ххх носителях!
В нашей компании почти 100 миллионов рекламных страниц, работающих в Интернете, поэтому, если вы работаете в Интернете голым, вы не знаете, в чем проблема, и вы найдете ее на стороне бизнеса, и бизнес-сторона спросит вас. сцена очень неприятная.
выберите
В компании четыре бизнес-подразделения, в каждом из которых не менее 3-х проектов, здесь не менее 12. Предварительно много бизнес-направлений.
Мы выбираем делать это сами или выбираем третью сторону. Мы сравниваем несколько общих третьих лиц.
- Fundebug: платная версия стоит от 159 юаней в месяц, данные существуют у третьих лиц, а самосохранение данных требует 300 000 юаней в год. Все равно очень дорого.
- FrontJS, FrontJS Premium — 899 в месяц, Pro — 2999 в месяц.
- Часовой, 80 долларов в месяц.
Используя Sentry в качестве счета, рассчитайте эти 12 предметов. 12 проектов — это почти 100 000 в год. Приблизительно подсчитано, что на выполнение версии MVP у 2 человек уйдет 1,5 месяца или 90 человеко-дней.Исходя из зарплаты 15 000 на человека в месяц, общая стоимость составляет 45 000, и это делается один раз и навсегда. .
Поэтому с точки зрения стоимости мы выберем самостоятельные исследования, но помимо стоимости есть и другие причины. Например, мы выполним некоторые пользовательские функции на основе этой системы, пройдем через систему авторизованных пользователей компании, а затем будем управлять Todo для пользователей и ранжировать пользователей по ошибке.
Существует также безопасность, основанная на бизнес-данных, мы надеемся построить систему самостоятельно.
Поэтому с точки зрения стоимости, безопасности и масштабируемости мы решили разработать его самостоятельно.
○ Два, дизайн продукта
Какой продукт мы хотим?В соответствии с первыми принципами решается ключевая проблема «как локализовать проблему». Какую информацию мы хотим узнать с помощью анализа метода 5W1H?
сообщение об ошибке
На самом деле мониторинг ошибок можно описать просто одним предложением, собирать ошибки страниц, сообщать о них, а затем анализировать симптомы.
Анализируя это предложение по правилу 5W1H, мы можем обнаружить, что есть несколько моментов, требующих нашего внимания.
- Что, какая ошибка произошла: логическая ошибка, ошибка данных, сетевая ошибка, синтаксическая ошибка и т.д.
- Когда, период времени, в котором это происходит, например, отметка времени.
- Кто, сколько пользователей затронут, включая количество событий, IP, информацию об устройстве.
- Где и какие страницы появляются, в том числе страницы, рекламные площадки (наша компания) и СМИ (наша компания).
- Почему, почему причина ошибки, включая стек ошибки, ⾏ столбец, SOURCEMAP.
- Как, как найти и решить проблему, нам также необходимо собрать системную и другую информацию.
Уровень архитектуры
Во-первых, нам нужно разобраться, какие функции нам нужны.
Итак, как мы можем получить приведенную выше информацию, чтобы найти последнюю ошибку?
В первую очередь нам обязательно нужно собирать ошибки, а потом как мы можем воспринимать ошибки на стороне страницы пользовательского устройства, о которых нужно сообщать. Затем отображается первый слой, и нам нужен терминал сбора и отчетности.
Как можно сообщать сообщать, а бэкенд такой длинный, и ты его узнаешь, нужен интерфейс. Затем нужно, чтобы сервер собирал ошибку, чтобы сообщить, фильтруя агрегаты для ошибок. Так что второй этаж тоже знает, нам нужна коллекция агрегатов.
Мы собрали достаточно информации о наших материалах, как их использовать дальше, нам нужно систематизировать их по нашим правилам. Если эффективность запросов очень низкая из-за того, что каждый раз пишут SQL-подобные, нам нужна визуальная платформа для отображения. Поэтому есть третий слой, сторона визуального анализа.
Вроде сделано, все должны так думать, платформа мониторинга ошибок сделана, 🙅 . Если это так, вы обнаружите явление.Каждый раз, когда вы подключаетесь к сети и в течение определенного периода времени после выхода в сеть, разработчики продолжают смотреть на экран.Что это делает, гуманоидный режим наблюдателя с отслеживанием глаз? Поэтому надо решать через код Естественно появился четвертый уровень, терминал мониторинга и сигнализации.
Поэтому, пожалуйста, скажите вслух, что нам нужно🙈 , сбор и отчет завершен, сбор и объединение завершен, визуальный анализ завершен, мониторинг и сигнализация завершены.
○ 3. Дизайн системы
Подобно функциям, определите ввод и вывод каждой ссылки, а также функции, которые должно обрабатывать ядро.
Давайте посмотрим, как реализуются упомянутые выше четыре конца.
Терминал сбора и отчетности (SDK)
Основной ввод этой ссылки — все ошибки, а вывод — сбор и отчет об ошибках. Ядро состоит в том, чтобы иметь дело со сбором различных типов ошибок. Остальные - какие-то непрофильные, но необходимые работы.
тип ошибки
Давайте сначала посмотрим, какие типы ошибок нам нужно обрабатывать.
Распространенные ошибки выполнения JS
- SyntaxError
Синтаксическая ошибка при синтаксическом анализе
// 控制台运行
const xx,
SyntxError не может быть перехвачен с помощью window.onerror, как правило, SyntaxError обнаруживается на этапе построения или даже на этапе локальной разработки.
- TypeError
значение не соответствует ожидаемому типу
// 控制台运行
const person = void 0
person.name
- ReferenceError
ссылка на необъявленную переменную
// 控制台运行
nodefined
- RangeError
Когда значение не входит в допустимый диапазон или не установлено
(function fn ( ) { fn() })()
Ошибка сети
- ResourceError
ошибка загрузки ресурса
new Image().src = '/remote/image/notdeinfed.png'
- HttpError
Ошибка HTTP-запроса
// 控制台运行
fetch('/remote/notdefined', {})
ошибка сбора
Из всех причин ошибок, то как мы фиксируем ошибку.
try/catch
Может обнаруживать обычные ошибки времени выполнения, а не синтаксические ошибки и асинхронные ошибки.
// 常规运行时错误,可以捕获 ✅
try {
console.log(notdefined);
} catch(e) {
console.log('捕获到异常:', e);
}
// 语法错误,不能捕获 ❌
try {
const notdefined,
} catch(e) {
console.log('捕获到异常:', e);
}
// 异步错误,不能捕获 ❌
try {
setTimeout(() => {
console.log(notdefined);
}, 0)
} catch(e) {
console.log('捕获到异常:',e);
}
У try/catch есть свои преимущества осторожного обращения, но его недостатки также более очевидны.
window.onerror
Чистая коллекция ошибок js, window.onerror, когда возникает ошибка времени выполнения JS, окно вызывает событие ошибки интерфейса ErrorEvent.
/**
* @param {String} message 错误信息
* @param {String} source 出错文件
* @param {Number} lineno 行号
* @param {Number} colno 列号
* @param {Object} error Error对象
*/
window.onerror = function(message, source, lineno, colno, error) {
console.log('捕获到异常:', {message, source, lineno, colno, error});
}
Убедитесь, что следующие несколько ошибок могут быть обнаружены в первую очередь.
// 常规运行时错误,可以捕获 ✅
window.onerror = function(message, source, lineno, colno, error) {
console.log('捕获到异常:',{message, source, lineno, colno, error});
}
console.log(notdefined);
// 语法错误,不能捕获 ❌
window.onerror = function(message, source, lineno, colno, error) {
console.log('捕获到异常:',{message, source, lineno, colno, error});
}
const notdefined,
// 异步错误,可以捕获 ✅
window.onerror = function(message, source, lineno, colno, error) {
console.log('捕获到异常:',{message, source, lineno, colno, error});
}
setTimeout(() => {
console.log(notdefined);
}, 0)
// 资源错误,不能捕获 ❌
<script>
window.onerror = function(message, source, lineno, colno, error) {
console.log('捕获到异常:',{message, source, lineno, colno, error});
return true;
}
</script>
<img src="https://yun.tuia.cn/image/kkk.png">
Что делать, если window.onerror не может отлавливать ошибки ресурсов?
window.addEventListener
Когда ресурс (например, изображение или сценарий) не загружается, элемент, который загружает ресурс, вызывает событие ошибки интерфейса Event.Эти события ошибки не всплывают в окне, но могут быть перехвачены. И window.onerror не может контролировать захват.
// 图片、script、css加载错误,都能被捕获 ✅
<script>
window.addEventListener('error', (error) => {
console.log('捕获到异常:', error);
}, true)
</script>
<img src="https://yun.tuia.cn/image/kkk.png">
<script src="https://yun.tuia.cn/foundnull.js"></script>
<link href="https://yun.tuia.cn/foundnull.css" rel="stylesheet"/>
// new Image错误,不能捕获 ❌
<script>
window.addEventListener('error', (error) => {
console.log('捕获到异常:', error);
}, true)
</script>
<script>
new Image().src = 'https://yun.tuia.cn/image/lll.png'
</script>
// fetch错误,不能捕获 ❌
<script>
window.addEventListener('error', (error) => {
console.log('捕获到异常:', error);
}, true)
</script>
<script>
fetch('https://tuia.cn/test')
</script>
Использование нового изображения относительно невелико, и вы можете самостоятельно справиться со своими ошибками.
А как насчет общей выборки, выборка возвращает промис, но ошибка промиса не может быть поймана, что мне делать?
Ошибка обещания
- Обычная ошибка обещания
try/catch не может отлавливать ошибки в Promise
// try/catch 不能处理 JSON.parse 的错误,因为它在 Promise 中
try {
new Promise((resolve,reject) => {
JSON.parse('')
resolve();
})
} catch(err) {
console.error('in try catch', err)
}
// 需要使用catch方法
new Promise((resolve,reject) => {
JSON.parse('')
resolve();
}).catch(err => {
console.log('in catch fn', err)
})
- асинхронная ошибка
try/catch не может перехватывать асинхронные обернутые ошибки
const getJSON = async () => {
throw new Error('inner error')
}
// 通过try/catch处理
const makeRequest = async () => {
try {
// 捕获不到
JSON.parse(getJSON());
} catch (err) {
console.log('outer', err);
}
};
try {
// try/catch不到
makeRequest()
} catch(err) {
console.error('in try catch', err)
}
try {
// 需要await,才能捕获到
await makeRequest()
} catch(err) {
console.error('in try catch', err)
}
- Импортная ошибка кусочки
import на самом деле возвращает обещание, поэтому используйте следующие два метода для отлова ошибок.
// Promise catch方法
import(/* webpackChunkName: "incentive" */'./index').then(module => {
module.default()
}).catch((err) => {
console.error('in catch fn', err)
})
// await 方法,try catch
try {
const module = await import(/* webpackChunkName: "incentive" */'./index');
module.default()
} catch(err) {
console.error('in try catch', err)
}
Резюме: глобально перехватывать ошибки в промисах
Вышеупомянутые три на самом деле сводятся к ошибкам типа Promise, которые могут быть перехвачены необработанным отклонением.
// 全局统一处理Promise
window.addEventListener("unhandledrejection", function(e){
console.log('捕获到异常:', e);
});
fetch('https://tuia.cn/test')
Чтобы предотвратить отсутствующие исключения Promise, необработанное отклонение можно использовать для глобального мониторинга Uncaught Promise Error.
ошибка vue
Поскольку Vue будет захватывать все однофайловые компоненты Vue или код, унаследованный от Vue.extend, ошибки в Vue не будут напрямую перехватываться window.onerror, а будут переданы Vue.config.errorHandler.
/**
* 全局捕获Vue错误,直接扔出给onerror处理
*/
Vue.config.errorHandler = function (err) {
setTimeout(() => {
throw err
})
}
Ошибка реагирования
React объявляет компонент с границей ошибки через componentDidCatch
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
// 更新 state 使下一次渲染能够显示降级后的 UI
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
// 你同样可以将错误日志上报给服务器
logErrorToMyService(error, errorInfo);
}
render() {
if (this.state.hasError) {
// 你可以自定义降级后的 UI 并渲染
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
class App extends React.Component {
render() {
return (
<ErrorBoundary>
<MyWidget />
</ErrorBoundary>
)
}
}
Но границы ошибок не улавливают следующие ошибки: обработка событий React, асинхронный код, ошибки, генерируемые самими границами ошибок.
междоменная проблема
В общем случае, если возникает ошибка, такая как ошибка сценария, в основном наверняка возникает междоменная проблема.
Если текущая страница обслуживания и облачный JS находятся в разных доменных именах, если облачный JS имеет ошибку, в window.onerror появится ошибка сценария. Ее можно решить следующими двумя способами.
- Серверная часть настроена с помощью Access-Control-Allow-Origin, внешнего сценария и перекрестного происхождения.
<script src="http://yun.tuia.cn/test.js" crossorigin></script>
const script = document.createElement('script');
script.crossOrigin = 'anonymous';
script.src = 'http://yun.tuia.cn/test.js';
document.body.appendChild(script);
- Если вы не можете изменить заголовок запроса сервера, вы можете считать, что ошибка выдает ошибку, обходя ее с помощью try/catch.
<!doctype html>
<html>
<head>
<title>Test page in http://test.com</title>
</head>
<body>
<script src="https://yun.dui88.com/tuia/cdn/remote/testerror.js"></script>
<script>
window.onerror = function (message, url, line, column, error) {
console.log(message, url, line, column, error);
}
try {
foo(); // 调用testerror.js中定义的foo方法
} catch (e) {
throw e;
}
</script>
</body>
</html>
Вы обнаружите, что если вы не добавите try catch, console.log напечатает ошибку сценария. Добавьте попытку поймать, чтобы поймать его.
Давайте посмотрим на сцену, обычно вызывающую удаленный js, есть следующие три типичные ситуации.
- Ошибка при вызове удаленного JS-метода
- Проблема с событием внутри удаленного JS
- Либо ошибка внутри обратных вызовов, таких как setTimeout
сцена вызова метода
Инкапсулируя функцию, исходный метод можно украсить так, чтобы его можно было попробовать/перехватить.
<!doctype html>
<html>
<head>
<title>Test page in http://test.com</title>
</head>
<body>
<script src="https://yun.dui88.com/tuia/cdn/remote/testerror.js"></script>
<script>
window.onerror = function (message, url, line, column, error) {
console.log(message, url, line, column, error);
}
function wrapErrors(fn) {
// don't wrap function more than once
if (!fn.__wrapped__) {
fn.__wrapped__ = function () {
try {
return fn.apply(this, arguments);
} catch (e) {
throw e; // re-throw the error
}
};
}
return fn.__wrapped__;
}
wrapErrors(foo)()
</script>
</body>
</html>
Можно попробовать избавиться от wrapErrors и пощупать.
сцена события
Нативные методы могут быть взломаны.
<!doctype html>
<html>
<head>
<title>Test page in http://test.com</title>
</head>
<body>
<script>
const originAddEventListener = EventTarget.prototype.addEventListener;
EventTarget.prototype.addEventListener = function (type, listener, options) {
const wrappedListener = function (...args) {
try {
return listener.apply(this, args);
}
catch (err) {
throw err;
}
}
return originAddEventListener.call(this, type, wrappedListener, options);
}
</script>
<div style="height: 9999px;">http://test.com</div>
<script src="https://yun.dui88.com/tuia/cdn/remote/error_scroll.js"></script>
<script>
window.onerror = function (message, url, line, column, error) {
console.log(message, url, line, column, error);
}
</script>
</body>
</html>
Можно попробовать удалить код, который инкапсулирует EventTarget.prototype.addEventListener и пощупать его.
интерфейс отчетности
为什么不能直接用GET/POST/HEAD请求接口进行上报?
Легче подумать, почему. Вообще говоря, доменное имя с точкой не является текущим доменным именем, поэтому все запросы к интерфейсу будут представлять собой междоменные запросы.
Почему нельзя сообщить об этом, запросив другие файловые ресурсы (js/css/ttf)?
После создания узла ресурса браузер фактически не будет отправлять запрос ресурса, пока объект не будет внедрен в дерево DOM браузера. Более того, загрузка ресурсов js/css заблокирует отрисовку страницы и повлияет на взаимодействие с пользователем.
Построение управления изображениями не только не требует вставки DOM, поскольку новый объект изображения создается в js, запрос может быть инициирован, и нет проблем с блокировкой.В среде браузера без js можно использовать тег img. для нормального управления.
Используйте новое изображение для отчетов по интерфейсу. В последнем вопросе тоже картинка.При репортаже используется прозрачный GIF 1х1 вместо других файлов PNG/JEPG/BMP.
Во-первых, 1x1 пиксель — это самое маленькое разрешенное изображение. Кроме того, поскольку изображение прочерчено точками, изображение предпочтительно должно быть прозрачным, чтобы оно не влияло на эффект отображения самой страницы.Двойка указывает на то, что изображение прозрачно, если для маркировки используется двоичный бит. изображение как прозрачный цвет, и нет необходимости хранить данные цветового пространства, что может сэкономить объем. Поскольку требуется прозрачный цвет, JPEG можно исключить напрямую.
Для одного и того же ответа GIF может сэкономить 41% трафика по сравнению с BMP и 35% трафика по сравнению с PNG. GIF — лучший выбор.
- междоменный
- Не содержит куки
- Не нужно ждать, пока сервер вернет данные
неблокирующая загрузка
Старайтесь избегать влияния загрузки ресурсов SDK js.
Сначала кэшируя запись об ошибке window.onerror, затем асинхронно загружая SDK, а затем обрабатывая отчет об ошибке в SDK.
<!DOCTYPE html>
<html lang="en">
<head>
<script>
(function(w) {
w._error_storage_ = [];
function errorhandler(){
// 用于记录当前的错误
w._error_storage_&&w._error_storage_.push([].slice.call(arguments));
}
w.addEventListener && w.addEventListener("error", errorhandler, true);
var times = 3,
appendScript = function appendScript() {
var sc = document.createElement("script");
sc.async = !0,
sc.src = './build/skyeye.js', // 取决于你存放的位置
sc.crossOrigin = "anonymous",
sc.onerror = function() {
times--,
times > 0 && setTimeout(appendScript, 1500)
},
document.head && document.head.appendChild(sc);
};
setTimeout(appendScript, 1500);
})(window);
</script>
</head>
<body>
<h1>这是一个测试页面(new)</h1>
</body>
</html>
Агрегатор коллекций (сервер журналов)
В этой ссылке входом является запись об ошибке, полученная по уважительной причине, а выходом является действительное хранилище данных. Основная функция должна очищать данные, что, кстати, устраняет чрезмерное давление обслуживания. Еще одна основная функция — хранение данных.
Общий процесс можно рассматривать как идентификацию ошибок -> фильтрация ошибок -> прием ошибок -> сохранение ошибок.
Идентификация ошибок (сотрудничество с SDK)
Перед агрегированием нам нужна возможность идентифицировать ошибки в разных измерениях, что можно понимать как способность находить одну запись об ошибке и одно событие ошибки.
запись с одной ошибкой
Соответствующий идентификатор записи об ошибке генерируется DATE и случайными значениями.
const errorKey = `${+new Date()}@${randomString(8)}`
function randomString(len) {
len = len || 32;
let chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678';
let maxPos = chars.length;
let pwd = '';
for (let i = 0; i < len; i++) {
pwd += chars.charAt(Math.floor(Math.random() * maxPos));
}
return pwd;
}
Одиночное событие ошибки
Во-первых, требуется возможность обнаружения одного и того же события ошибки (разные пользователи, один и тот же тип ошибки, сообщение об ошибке).
При добавлении message, colno и lineno для вычисления значения кода Ask может быть сгенерирован неверный errorKey.
const eventKey = compressString(String(e.message), String(e.colno) + String(e.lineno))
function compressString(str, key) {
let chars = 'ABCDEFGHJKMNPQRSTWXYZ';
if (!str || !key) {
return 'null';
}
let n = 0,
m = 0;
for (let i = 0; i < str.length; i++) {
n += str[i].charCodeAt();
}
for (let j = 0; j < key.length; j++) {
m += key[j].charCodeAt();
}
let num = n + '' + key[key.length - 1].charCodeAt() + m + str[str.length - 1].charCodeAt();
if(num) {
num = num + chars[num[num.length - 1]];
}
return num;
}
Как показано на рисунке ниже, событие ошибки (список событий), каждое подчиненное является фактической записью об ошибке.
Фильтрация ошибок (сотрудничество SDK)
Фильтрация доменов
Отфильтруйте ошибки сценария на этой странице, которые могут быть вставлены в другие js с помощью веб-просмотра.
Мы заботимся только о наших собственных проблемах удаленного JS, поэтому мы фильтруем на основе доменного имени нашей компании.
// 伪代码
if(!e.filename || !e.filename.match(/^(http|https):\/\/yun./)) return true
Повторить отчет
Как избежать дублирования отчетности? Кэширование выполняется в соответствии с errorKey, и предотвращается сообщение о повторяющихся ошибках, превышающих пороговое значение.
// 伪代码
const localStorage = window.localStorage;
const TIMES = 6; // 缓存条数
export function setItem(key, repeat) {
if(!key) {
key = 'unknow';
}
if (has(key)) {
const value = getItem(key);
// 核心代码,超过条数,跳出
if (value >= repeat) {
return true;
}
storeStorage[key] = {
value: value + 1,
time: Date.now()
}
} else {
storeStorage[key] = {
value: 1,
time: Date.now()
}
}
return false;
}
прием ошибок
Обрабатывая принимающий интерфейс, обратите внимание на управление потоком, так как именно здесь разработка бэкенда должна вкладывать больше всего энергии для обработки большого количества параллельного трафика.
журнал ошибок
Принимающая сторона использует Koa, который просто реализует прием и печать на диск.
// 伪代码
module.exports = async ctx => {
const { query } = ctx.request;
// 对于字段进行简单check
check([ 'mobile', 'network', 'ip', 'system', 'ua', ......], query);
ctx.type = 'application/json';
ctx.body = { code: '1', msg: '数据上报成功' };
// 进行日志记录到磁盘的代码,根据自己的日志库选择
};
Механизм пикового бритья
Например, установите пороговое значение 2000 года в секунду, а затем уменьшите верхний предел в соответствии с количеством запросов, и верхний предел сбрасывается.
// 伪代码
// 1000ms
const TICK = 1000;
// 1秒上限为2000
const MAX_LIMIT = 2000;
// 每台服务器请求上限值
let maxLimit = MAX_LIMIT;
/**
* 启动重置函数
*/
const task = () => {
setTimeout(() => {
maxLimit = MAX_LIMIT;
task();
}, TICK);
};
task();
const check = () => {
if (maxLimit <= 0) {
throw new Error('超过上报次数');
}
maxLimit--;
// 执行业务代码。。。
};
Обработка выборки
За пределами порога также возможен отбор проб.
// 只采集 20%
if(Math.random() < 0.2) {
collect(data) // 记录错误信息
}
хранилище ошибок
Для журналов, напечатанных на диске, как мы можем их агрегировать?Здесь мы должны рассмотреть использование решений для хранения.
Как правило, после выбора схемы хранения и настройки конфигурации схема хранения может периодически получать данные через диск. Итак, нам нужно выбрать решение для хранения.
Что касается решений для хранения, мы сравнили распространенные ежедневные решения, такие как Alibaba Cloud Log Service — Log Service (SLS), ELK (Elastic, Logstash, Kibana), решения класса Hadoop/Hive (хранение данных в Hadoop и использование Hive для запросов).
Были сопоставлены следующие аспекты, и, наконец, был выбран Log Service, в основном с учетом того, что его не нужно создавать, низкая стоимость и удовлетворительная функция запроса.
функциональный элемент | ELK-подобная система | Hadoop + Hive | служба журнала |
---|---|---|---|
задержка журнала | 1~60 секунд | минуты в часы | в реальном времени |
задержка запроса | менее 1 секунды | минутный уровень | менее 1 секунды |
возможность запроса | Хорошо | Хорошо | Хорошо |
Расширяемость | Заранее подготовьте машину | Заранее подготовьте машину | 10-кратное увеличение емкости за секунды |
Стоимость | выше | ниже | очень низкий |
Задержка журнала: как долго после создания журнала он может быть запрошен. Задержка запроса: количество данных, сканируемых в единицу времени. Возможность запроса: запрос по ключевому слову, запрос комбинации условий, нечеткий запрос, числовое сравнение, контекстный запрос. Масштабируемость: Быстро реагируйте на стократное увеличение трафика. Стоимость: Плата за ГБ.
Для конкретного использования API вы можете просмотретьслужба журнала.
Клемма визуального анализа (визуализация платформы)
В этой ссылке входом является запись об ошибке, полученная по уважительной причине, а выходом является действительное хранилище данных. Основная функция должна очищать данные, что, кстати, устраняет чрезмерное давление обслуживания. Еще одна основная функция — хранение данных.
основная функция
- На диаграмме домашней страницы вы можете выбрать 1 день, 4 часа, 1 час и т. д., количество ошибок агрегации, а агрегация делится на 24 части в день.
- Список домашней страницы собирает данные за выбранное время и отображает файл ошибки, ключ ошибки, количество событий, тип ошибки, время и информацию об ошибке.
- Подробная информация об ошибке, список событий, основная информация, информация об устройстве, диаграмма соотношения устройств (см. приведенную выше диаграмму списка событий).
Таблица лидеров
В начале я составил список незавершенных ошибок, свой список ошибок и список исправленных ошибок.Ошибки не привязаны к людям, и они слишком зависят от человеческой инициативы.Каждый должен проявлять инициативу в их устранении. на платформе, и эффект не очень хороший.
Позже, через список неправильных авторов, и через Dingding Daily, чтобы напомнить соответствующему персоналу, чтобы справиться с этим. За срочные ошибки ответственность будет возложена на человека через сигналы тревоги в реальном времени, а сигналы тревоги скажут позже.
Конкретный принцип:
- Пакет webpack использует команду git для упаковки автора, адреса электронной почты и времени автора в заголовке.
- В службе визуализации запросите соответствующий URL-адрес отчета об ошибке, чтобы он соответствовал соответствующему автору, и верните его на сторону отображения.
SourceMap
Сборка с использованием карты скрытого источника webpack. По сравнению с source-map комментариев в конце меньше, но index.js.map в выходной директории не меньше. Онлайн-среда позволяет избежать утечки исходной карты.
webpackJsonp([1],[
function(e,t,i){...},
function(e,t,i){...},
function(e,t,i){...},
function(e,t,i){...},
...
])
// 这里没有生成source-map的链接地址
По URL-адресу файла отчета об ошибке, согласно каталогу и правилам, согласованным в команде, найдите адрес sourceMap, который был упакован и загружен ранее.
const sourcemapUrl = ('xxxfolder/' + url + 'xxxHash' +'.map')
Получите сообщаемую строку, столбец и источник и используйте стороннюю библиотеку sourceMap для позиционирования.
const sourceMap = require('source-map')
// 根据行数获取源文件行数
const getPosition = async(map, rolno, colno) => {
const consumer = await new sourceMap.SourceMapConsumer(map)
const position = consumer.originalPositionFor({
line: rolno,
column: colno
})
position.content = consumer.sourceContentFor(position.source)
return position
}
Если вам интересен принцип SourceMap, вы можете продолжить углубляться.SourceMap и внешний мониторинг исключений.
Сигнал ошибки
Аварийная система
- Каждая бизнес-линия устанавливает свой собственный порог, интервал времени ошибки и интервал опроса сигналов тревоги.
- Тревога в соответствующую группу через крючок Dingding
- Сообщите список неправильных авторов в виде ежедневного отчета
○ Четыре, расширение
поведенческая коллекция
Собирая действия пользователя, становится очевидным, почему произошла ошибка.
Классификация
- Поведение пользовательского интерфейса: нажатие, прокрутка, фокусировка/расфокусировка, долгое нажатие
- Поведение браузера: запрос, вперед / назад, прыжка, новая страница, закрыть
- Поведение консоли: журнал, предупреждение, ошибка
Метод сбора
- поведение кликов
Используйте addEventListener для прослушивания события щелчка на глобальном уровне и сбора имени события и элемента DOM. Отчет с сообщением об ошибке.
- послать запрос
Слушайте функцию обратного вызова onreadystatechange XMLHttpRequest
- прыжок страницы
Слушая window.onpopstate, он будет срабатывать при переходе на страницу.
- поведение консоли
Переопределите информацию и другие методы объекта консоли.
Заинтересованные могут обратиться кмониторинг поведения.
возникшие проблемы
Из-за некоторой вовлеченности в конфиденциальность следующее будет десенсибилизировано.
Проблема с пустым логом
После онлайн-операции в градациях серого мы обнаружили, что в журнале SLS есть несколько пустых логов 😢 , 🦢, что случилось?
Во-первых, давайте вспомним, какие ссылки на этой ссылке могут иметь проблемы.
Проверяем ссылку.Перед ссылкой сбора SLS логи дисков собираются, принимаются сервером и сообщаются SDK.Потом проверяем по очереди.
На шаг вперед обнаруживается, что в логе диска уже есть пустой лог, тогда остальным приходится смотреть на принимающей стороне и на стороне SDK.
Начните использовать метод управляющих переменных и сначала сделайте пустой вывод на стороне SDK, чтобы предотвратить создание отчетов о пустых журналах. Результат: признан недействительным 😅.
Продолжайте обрабатывать принимающую сторону Node и оцените полученные данные как пустые.Если они пустые, не печатайте журнал, результат: все еще недействителен😳.
Итак, начинаем обнаруживать, что что-то не так с самой печатью журнала? Я исследовал API сторонней библиотеки логов для логирования и делал разные попытки, но обнаружил, что это все равно бесполезно, а лицо у меня черное🌚.
Что за ситуация, "нерешительность" смотрите в исходниках. Проверьте исходный код библиотеки журналов на наличие проблем. Я прошел основной процесс вызова исходного кода и не нашел никаких проблем, поэтому я был сбит с толку 🙃.
Логика всего кода нормальная, что заставило нас задуматься, не проблема ли это с данными, поэтому мы начали сокращать сообщаемые поля и, наконец, определили его как поле. Обнаружил, что после выхода в интернет проблем нет😢.
Это потому, что данные, хранящиеся в некоторых полях, слишком длинные? Однако возможность этой ошибки не отражена в логике кода и журналах процессов.
Поэтому мы используем метод дихотомии, чтобы добавить поля из двух частей и, наконец, найти определенное поле. Если есть поле для отчета, возникнет проблема. Это было неожиданно.
Мы снова подумали о ссылке.За исключением библиотеки журналов, другие коды в основном являются нашей собственной логикой, поэтому мы проверили библиотеку журналов и заподозрили, что она сделала с полем.
Итак, поиском было обнаружено, что библиотека логов будет использовать определенное поле в подчиненном режиме (вы можете понять режим master-slave Node), что вызвало конфликт с полем, о котором мы сообщили, поэтому оно было потеряно 🤪.
проблема с потерей журнала
Я решил последнюю проблему, я был счастлив, и чувство выполненного долга пришло в мое сердце. Но меня сразу же ударили по голове, и я слишком рано понял, что был слишком счастлив 🤮.
Когда одноклассник команды тестировал локально, потому что он хорошо провел время, он постоянно обновлял страницу, чтобы сообщить об ошибке текущей страницы. Однако он обнаружил, что количество записей, сообщаемых локально, не соответствует количеству записей в фактической службе журнала, а количество записей в службе журнала было намного меньше.
Поскольку я занимался бэкенд-разработкой более 2 лет, когда только закончил учебу, я все еще немного чувствителен к потере данных в операциях ввода-вывода. Интуитивно кажется, что это может быть проблема многопроцессного направления. Есть подозрение, что это проблема взаимоблокировки файлов, вызванная несколькими процессами.
Затем мы удаляем многопоточность и через однопоточность повторяем исходные шаги, чтобы воспроизвести проблему. Ничего не пропало 🤭.
Мы обнаружили, что есть два места для настройки кластера (режим master-slave), библиотеки журналов и инструмента развертывания.
Обратите внимание на режим процесса «ведущий-ведомый», используемый библиотекой журналов по умолчанию, а в средстве развертывания нет концепции режима «ведущий-ведомый», что неизбежно приведет к проблеме взаимоблокировки записи ввода-вывода, что приведет к потере журнала. Поэтому мне интересно, есть ли в сообществе сторонняя поддержка, которая может решить эту проблему.
Затем с помощью поиска в Google я быстро нашел соответствующую стороннюю библиотеку, которая может обеспечить обмен сообщениями между главным процессом и подчиненным процессом. Принцип заключается в том, что главный процесс отвечает за запись всех сообщений в журнал, а подчиненный процесс передает сообщения главному процессу.
○ V. Рекомендуемая литература и цитаты
Обработка исключений
source-map
Реагировать на ошибку
Script Error
Захватывайте и сообщайте об ошибках JavaScript с помощью window.onerror | Блог о продукте • Sentry
Что, черт возьми, такое «Ошибка скрипта»? | Блог о продукте • Sentry
в общем и целом
Фронтенд-мониторинг | Allan - Как реализовать многотерминальную платформу мониторинга ошибок
Шаг за шагом создайте систему мониторинга внешнего интерфейса: мониторинг ошибок JS
Выступление на Дне открытых дверей