Разработчик подключил сбор ошибок, обрадовался первому графику и через неделю перестал открывать панель. Уведомлений приходило по двести в день, среди них были те же самые сбои, известные проблемы сторонних библиотек и сообщения от старых версий, которые уже никого не волновали. Инструмент работал, а пользы не приносил. Проблема не в нём, а в том, что между сбором данных и решением стоит работа, которую никто не сделал.
Наблюдаемость описывает способность понять состояние системы
Разговоры о мониторинге упираются в путаницу терминов. Сбор данных и понимание происходящего это разные вещи.
В теории управления наблюдаемость означает, насколько состояние системы определяется по её выходам. В программной инженерии смысл похож: способность понять, что происходит внутри, по данным, которые система производит.
Практическое различие формулируется просто. Мониторинг отвечает на заранее известные вопросы: работает ли служба, сколько запросов в секунду, не превышена ли задержка. Наблюдаемость позволяет отвечать на вопросы, которых заранее не было: почему у части пользователей из одного региона выросло время ответа именно в эти часы.
Вывод простой: панель с десятком графиков это мониторинг, а наблюдаемость появляется, когда к цифрам можно задать произвольный вопрос и получить ответ без правки кода.
Четыре вида данных образуют основу: метрики это числовые показатели во времени, журналы записи о событиях, трассировки связывают операции в цепочку, профили показывают, где тратится время внутри процесса.
Сбор ошибок начинается с правильной группировки
Первое, что превращает поток сообщений в полезный инструмент, это объединение одинаковых сбоев.
Без группировки ошибки одного дефекта приходят тысячами записей, с группировкой они превращаются в одну проблему с числом случаев.
Основа группировки это отпечаток. Он строится из типа ошибки, места возникновения и так называемого стабильного фрагмента стека, то есть части вызовов без номеров строк и случайных значений.
import hashlib
import re
def fingerprint(error):
frames = []
for frame in error.get("stack", [])[:5]:
module = re.sub(r"\d+", "N", frame.get("module", ""))
function = frame.get("function", "")
frames.append(module + ":" + function)
raw = "|".join([error.get("type", "Error"), *frames])
return hashlib.sha1(raw.encode("utf-8")).hexdigest()[:16]
Второй приём это определение причины. Если сбой вызван известной проблемой во внешней библиотеке, такую группу стоит помечать отдельно и исключать из оповещений.
Третий приём это привязка к версии. Ошибка, которая перестала появляться в новой версии, не требует внимания. Привязка к релизу позволяет отсеять устаревшие проблемы автоматически.
Четвёртый приём это учёт окружения. Сбои на локальных машинах разработчиков и на рабочем контуре смешивать нельзя, иначе статистика теряет смысл.
Приоритеты расставляются по частоте, охвату и влиянию
Второй шаг это понимание, что исправлять первым, здесь помогает оценка по трём величинам.
Первая величина это частота, причём важен не счётчик, а темп: растёт, падает, держится на месте.
Вторая величина это число затронутых пользователей. Один человек, столкнувшийся с ошибкой сто раз, это не то же самое, что сто человек, столкнувшихся по разу.
Третья величина это влияние на главный сценарий. Ошибка при оформлении заказа важнее ошибки при открытии страницы с редко используемой настройкой.
def priority(issue):
frequency = min(issue["events"] / 100, 1.0)
reach = min(issue["users"] / 50, 1.0)
critical = 1.0 if issue["affects_checkout"] else 0.4
score = frequency * 0.3 + reach * 0.3 + critical * 0.4
return round(score * 100, 1)
queue = [
{"events": 400, "users": 30, "affects_checkout": False},
{"events": 40, "users": 40, "affects_checkout": True},
]
print([priority(issue) for issue in queue])
На примере видно, как редкая ошибка обгоняет частую: сорок случаев у сорока людей в главном сценарии важнее четырёхсот у тридцати человек в настройках.
Второй приём это пороги. Разные сценарии имеют разные допустимые уровни. Ошибка оплаты недопустима ни в каком количестве, ошибка загрузки аватара может подождать неделю.
Третий приём это отслеживание регрессий. Если проблема, закрытая месяц назад, снова появилась, это отдельный сигнал. Автоматическая проверка по отпечатку ловит такие случаи.
Оповещения настраиваются только на то, что требует действия
Третий шаг решает, не забросят ли инструмент. Оповещение должно означать, что кто-то обязан встать и что-то сделать.
Первое правило это оповещать о новом, а не о каждом: первое появление проблемы заслуживает внимания, повторы уже статистика.
Второе правило это ограничивать частоту: уведомление приходит один раз, а не каждые пять минут, повтор допустим при ухудшении.
Третье правило это настраивать пороги по критичности. Ошибка в оформлении заказа будит ночью. Рост числа предупреждений в журнале ждёт утра.
Четвёртое правило это разделять каналы: срочное идёт туда, где нужна немедленная реакция, остальное в канал планового разбора.
Пятое правило это проверять состав. Раз в месяц стоит просмотреть, какие оповещения срабатывали, и убрать те, на которые никто не реагировал.
const RULES = [
{ match: /checkout|payment/i, channel: "oncall", threshold: 1 },
{ match: /телефон/i, channel: "analytics", threshold: 1 },
{ match: /.*/, channel: "digest", threshold: 50 },
];
function route(issue) {
const hit = RULES.find((rule) => rule.match.test(issue.title));
return hit || RULES[RULES.length - 1];
}
Такая таблица заменяет десяток ручных настроек и делает поведение предсказуемым. Новый сотрудник смотрит на список и понимает правила.
Контекст в отчёте об ошибке определяет скорость разбора
Четвёртый шаг это сбор контекста: отчёт без подробностей требует воспроизведения, а оно занимает часы.
Первое, что нужно приложить, это сведения о пользователе. Не имя и адрес, а идентификатор и признак авторизации. Персональные данные в отчётах об ошибках не нужны и создают риск.
Второе это сведения об окружении. Версия приложения, браузер, операционная система, тип устройства. Без этого невозможно понять, повторяется ли проблема у всех.
Третье это действия до сбоя. Цепочка шагов, приведшая к ошибке. Автоматический сбор последних действий удобнее ручных пометок.
Четвёртое это состояние данных. Какие параметры были в запросе, какой ответ пришёл, сколько времени заняла операция.
Пятое это скриншот или запись сессии. Для интерфейсных сбоев это часто единственный способ понять, что видел человек, не запрашивая у него подробности.
function buildContext(event) {
return {
release: event.release,
page: location.pathname,
viewport: window.innerWidth + "x" + window.innerHeight,
online: navigator.onLine,
lastActions: actionBuffer.slice(-5),
requestId: event.requestId,
};
}
Скриншот стоит делать с осторожностью. На нём может оказаться чужая личная информация. Маскирование полей ввода и удаление чувствительных блоков обязательны.
Приватность и объём данных требуют осознанных ограничений
Пятый шаг это ограничения: инструмент легко превращается в хранилище лишних сведений.
Первое ограничение это персональные данные. Почта, телефон, адрес, номер карты не должны попадать в отчёты. Фильтрация выполняется до отправки.
Второе ограничение это срок хранения: данные об ошибках нужны месяцами, а не годами, старые записи удаляются автоматически.
Третье ограничение это объём: сбор всех событий подряд растит расходы и замедляет интерфейс, часть событий отбрасывают на стороне клиента.
Четвёртое ограничение это доступ: отчёты раскрывают внутреннее устройство системы, круг людей с доступом ограничивают.
Пятое ограничение это согласие. Требования к обработке данных различаются по странам, и это учитывается при настройке.
Замыкание цикла отличает рабочий процесс от красивой панели
Шестой шаг это связь между ошибкой и исправлением, именно здесь инструмент превращается в рабочий процесс.
Первое звено это создание задачи. Из отчёта формируется обращение в системе учёта с ссылкой на группу ошибок.
Второе звено это привязка к выпуску. Обращение связывается с версией, в которой исправление попадёт к людям.
Третье звено это проверка. После выпуска отслеживается, исчезли ли новые случаи. Автоматическое закрытие проблемы при отсутствии новых событий экономит время.
Четвёртое звено это анализ после разбора. Почему ошибка не была поймана тестами, что стоит изменить в проверках. Запись вывода предотвращает повторение.
Пятое звено это метрика. Доля проблем, разобранных за неделю, показывает, работает ли процесс.
def close_if_resolved(issue, releases):
if issue["status"] != "исправление выпущено":
return issue
last_seen = issue.get("last_seen_version")
if last_seen and releases.index(last_seen) < releases.index(issue["fixed_in"]):
issue["status"] = "закрыто"
issue["resolved_at"] = issue.get("checked_at")
return issue
Шестое звено это итог месяца. Число разобранных проблем, время до первого ответа, число повторных появлений. Эти цифры показывают состояние работы без иллюзий.
Сравнение двух подходов к разбору показывает разницу в скорости
Полезно сопоставить, как проходит разбор одной проблемы в двух командах.
Первая команда собирает всё. Панель пестрит событиями, оповещения приходят на каждый случай, задачи создаются вручную из самых громких. Разбор начинается с поиска нужной группы среди сотни, на это уходит час, потом выясняется, что проблема известна и помечена как незначительная.
Вторая команда настроила группировку, пороги и маршрутизацию. Событие попадает в нужную группу автоматически, из группы создаётся задача, к ней приложены версия, окружение и цепочка действий. Разбор начинается сразу с причины. Время от жалобы до понимания проблемы сокращается в разы.
Разница не в инструменте, возможности одинаковые, разница в объёме работы, проделанной до первого сбоя.
Посчитаем трудозатраты: настройка группировки, порогов и маршрутизации занимает два-три дня, а экономия на каждом разборе час-два. При десяти проблемах в месяц настройка окупается сразу, а дальше экономит время постоянно.
Ошибки внедрения обходятся дороже, чем отсутствие инструмента
Разберём частые провалы, чтобы не повторять их.
Первая ошибка это оповещение о каждом сбое: люди перестают реагировать на поток, и настоящая проблема тонет среди известных.
Вторая ошибка это отсутствие версии в отчёте. Без неё невозможно понять, исправлено ли уже, и приходится проверять вручную.
Третья ошибка это сбор персональных данных. Через месяц выясняется, что в отчётах лежат адреса и телефоны клиентов, а это уже нарушение, а не небрежность.
Четвёртая ошибка это отсутствие владельца. Проблемы копятся, потому что непонятно, кто отвечает за разбор конкретной области.
Пятая ошибка это игнорирование ложных срабатываний. Если половина оповещений оказывается пустыми, доверие к инструменту падает и его перестают замечать.
Шестая ошибка это бесконечное хранение. Объём растёт, стоимость тоже, а старые данные всё равно никто не открывает.
Порядок внедрения занимает неделю и даёт результат сразу
Работу стоит вести по шагам, начиная с самого простого.
Первый шаг это сбор данных с минимальным набором полей: тип ошибки, стек, версия, идентификатор пользователя без личных сведений.
Второй шаг это группировка и проверка, что одинаковые сбои объединяются в одну группу.
Третий шаг это приоритеты. Настраивается оценка по частоте, охвату и влиянию на главные сценарии.
Четвёртый шаг это оповещения. Сначала только на критичное, остальное в ежедневную сводку.
Пятый шаг это контекст. Добавляются окружение, цепочка действий и сведения о запросе.
Шестой шаг это связь с задачами. Из группы ошибок создаётся обращение в системе учёта.
Седьмой шаг это проверка результата. Через месяц считаются разобранные проблемы и время до первого ответа.
Такой порядок занимает неделю и не требует сложных решений. Главное в нём то, что каждый шаг приносит пользу отдельно, и останавливаться можно на любом.
Мониторинг ошибок получился инструментом, который сам по себе не решает ничего. Он собирает данные и показывает картину. Польза появляется на следующих шагах: отсеять шум, расставить приоритеты, настроить осмысленные оповещения, собрать контекст и довести каждую проблему до исправления и проверки. Пропуск любого звена превращает панель в красивую картинку, которую через месяц перестают открывать.