Files
progcode/editorial/agent-rewrites/265.json
T

8 lines
18 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
"index": 265,
"slug": "editorial-2020-08-field-metrics-basics",
"title": "Метрики приложения: как превратить график в проверяемое действие",
"excerpt": "Пользователь видит ошибку, а общий график запросов не объясняет причину. Разбираем counter, окно, labels и проверку учебного порога без выдуманных production-выводов.",
"contentHtml": "<p>Рассмотрим дежурный сценарий: пользователь сообщает, что checkout иногда завершается ошибкой. На dashboard линия <code>requests_total</code> растёт ровно, аварий в логах нет. Команда смотрит на общий график и не понимает, что проверять дальше. Цена ошибки — не только пропущенный сбой. Можно поднять шумный alert, увеличить timeout без причины или потратить час на чтение логов, которые не связаны с нужной операцией.</p>\n<p>Вопрос здесь не в количестве линий на графике. Метрика помогает принять решение, когда её границы совпадают с вопросом: названы операция, событие, окно и следующий шаг. Один общий счётчик показывает, что система что-то делала. Он не показывает, сколько ошибок произошло в checkout и можно ли связать их с конкретным запросом.</p>\n<h2>Сначала зафиксируйте вопрос</h2>\n<p>До выбора типа метрики запишите наблюдаемый симптом без диагноза: какой пользовательский путь нарушен, в какое время это происходит и какое действие нельзя делать вслепую. В нашем примере нужно ответить на узкий вопрос: сколько завершённых запросов checkout закончилось ошибкой за последние десять минут?</p>\n<p>Такой вопрос сразу задаёт границы. Событие — завершённый запрос с известным исходом. Операция — checkout. Окно — десять минут. Следующий шаг после сигнала — открыть журнал или trace, а не объявить причину найденной. Если поменять любую из этих границ, число перестанет отвечать на исходный вопрос.</p>\n<h2>Отделите событие от состояния</h2>\n<p>Для завершённых запросов подходит <em>counter</em> — накопительная метрика событий. Она растёт и может сброситься при перезапуске процесса. Для вопроса «сколько ошибок checkout появилось за десять минут» нужен прирост counter, а не его последняя сырая величина.</p>\n<p>Для вопроса «сколько запросов выполняется сейчас» нужен <em>gauge</em>: он описывает текущее состояние и может расти или уменьшаться. Для вопроса «как распределилась длительность» нужна отдельная метрика наблюдений, например <em>histogram</em>. Не называйте один тип другим только потому, что все они отображаются линиями.</p>\n<p>Последнее значение <code>store_http_requests_total</code> зависит от времени жизни процесса. Оно не равно числу ошибок за выбранное окно. В PromQL такой вопрос выражает <code>increase()</code>:</p>\n<pre><code># Учебный запрос. Не production-alert без проверки окружения.\nsum(increase(store_http_requests_total{operation=&quot;checkout&quot;, outcome=&quot;error&quot;}[10m])) &gt;= 2</code></pre>\n<p>Выражение выбирает series с двумя ограниченными labels, считает прирост за десять минут и сравнивает его с учебной границей. Оно не сообщает причину ошибки. После срабатывания нужно открыть связанный журнал, trace или изолированный сценарий.</p>\n<p><code>increase()</code> учитывает reset counter после перезапуска target и рассчитывает прирост на указанном диапазоне. Поэтому ручная разность двух крайних snapshots полезна для объяснения идеи, но не должна подменять выполнение PromQL. На результат влияют время samples, scrape interval, пропуски scrape и границы range vector.</p>\n<h2>Labels должны сужать вопрос</h2>\n<p><code>operation=checkout</code> отделяет логическую операцию. <code>outcome=error</code> отделяет ошибку от успешного завершения. Набор значений ограничен заранее: операции и исходы берутся из небольшого списка. Такой label позволяет агрегировать данные и сравнивать ветки, не создавая отдельное имя метрики для каждой ветки.</p>\n<p>Не добавляйте в labels <code>request_id</code>, email, полный URL или другой идентификатор, который почти уникален для каждого события. Каждая комбинация labels создаёт отдельную time series. Уникальный идентификатор превратит counter в поток почти одноразовых рядов. График потеряет агрегирование, а Prometheus получит дополнительные затраты памяти, CPU, диска и сети. Конкретный запрос ищут в журнале по корреляционному идентификатору.</p>\n<pre><code># Хорошая граница метрики\nstore_http_requests_total{operation=&quot;checkout&quot;, outcome=&quot;error&quot;}\n\n# Плохая граница: уникальный label разрушает агрегацию\nstore_http_requests_total{operation=&quot;checkout&quot;, request_id=&quot;8f2d...&quot;}</code></pre>\n<p>Если нужна доля ошибок, знаменатель должен описывать ту же границу. Ошибки, посчитанные внутри приложения, нельзя без проверки делить на все попытки, посчитанные на proxy. Сначала убедитесь, что обе величины относятся к одной операции, одному времени и одной точке завершения.</p>\n<h2>Воспроизводимый учебный пример</h2>\n<p>Ниже приведены выдуманные snapshots. Они нужны только для проверки арифметики. В этом примере counter ошибки checkout равен нулю в 10:00, единице в 10:05 и двум в 10:10:</p>\n<pre><code># Не production telemetry. Значения заданы для учебного примера.\nstore_http_requests_total{operation=&quot;checkout&quot;,outcome=&quot;error&quot;}\n2020-08-01T10:00:00Z 0\n2020-08-01T10:05:00Z 1\n2020-08-01T10:10:00Z 2\n\n# В упрощённой модели: 2 - 0 = 2\n# Учебная граница &gt;= 2 пересечена.</code></pre>\n<p>Повторить разбор можно в таком порядке: зафиксировать ту же series, подставить три значения в выбранное окно, проверить арифметику <code>2 - 0 = 2</code>, затем выполнить PromQL на доступном сервере метрик. Отдельно повторите пример с последним значением <code>1</code>: он не должен пересекать границу <code>&gt;= 2</code>. Это проверяет учебную логику, но не проверяет exporter, scrape или alert delivery.</p>\n<p>В настоящем Prometheus <code>increase()</code> работает по samples range vector, а не по обещанию ровно трёх точек. Она корректирует reset counter и экстраполирует расчёт на границы окна. Поэтому простая разность полезна как контрольный пример, но не как формула для ручного production-расчёта.</p>\n<figure><img src='/assets/editorial/2020/metrics-diagnosis-2020.svg' alt='Схема диагностики метрики: от общего графика к counter ошибок checkout, проверке окна и связанному журналу' loading='lazy' /><figcaption>Порог переводит общий симптом в ограниченную проверку. Он не называет причину и не заменяет журнал.</figcaption></figure>\n<h2>Симптом → причина → проверка → действие</h2>\n<div class='table-scroll'><table><caption>Как не сделать из одной линии графика ложный диагноз</caption><thead><tr><th scope='col'>Симптом</th><th scope='col'>Возможная причина</th><th scope='col'>Проверка</th><th scope='col'>Действие</th></tr></thead><tbody><tr><td>Пользователь сообщает об ошибке, а общий <code>requests_total</code> растёт</td><td>Счётчик смешивает операции и исходы</td><td>Проверить metric name и доступные labels</td><td>Выбрать bounded <code>operation</code> и <code>outcome</code>; не менять timeout</td></tr><tr><td><code>increase(...[10m]) = 0</code></td><td>В series не было прироста или путь не инструментирован</td><td>Сверить operation с жалобой, время scrape и target</td><td>Не объявлять систему здоровой; проверить журнал и покрытие</td></tr><tr><td>Последняя величина counter большая</td><td>Процесс давно работает</td><td>Сравнить прирост за окно, а не абсолютное значение</td><td>Использовать <code>increase()</code></td></tr><tr><td>У каждой ошибки отдельная series</td><td>В label попал request ID или полный URL</td><td>Найти динамические значения и посчитать series</td><td>Убрать уникальное поле из metric contract; оставить его в логе</td></tr><tr><td>Порог срабатывает после редкого scrape</td><td>Окно и частота сбора не согласованы</td><td>Сопоставить range vector, scrape interval и пропуски</td><td>Изменить окно или сбор после проверки владельца alert</td></tr><tr><td>Две ошибки есть, но причина неизвестна</td><td>Метрика агрегирует событие без контекста</td><td>Найти trace или журнал по времени и correlation ID</td><td>Открыть ограниченную ветку расследования</td></tr></tbody></table></div>\n<p>Таблица разделяет наблюдение и действие. Значение два в учебном примере — не универсальный порог. В production порог зависит от объёма трафика, допустимой доли ошибок, стоимости ложного сигнала, времени реакции и владельца. Если эти условия не названы, число выглядит точным, но не является рабочим правилом.</p>\n<h2>Порядок проверки</h2>\n<ol><li>Запишите симптом без диагноза: какой пользовательский путь нарушен, когда это происходит и чем опасна ошибка.</li><li>Назовите событие, которое увеличивает counter. Для checkout это завершённый запрос с известным исходом, а не начало попытки.</li><li>Выберите тип метрики. Counter отвечает за накопленные события, gauge — за текущее состояние, histogram — за распределение наблюдений.</li><li>Проверьте labels. Оставьте ограниченные значения операции и исхода. Уникальные идентификаторы отправьте в журнал или trace context.</li><li>Согласуйте окно с частотой scrape. Укажите, что считается приростом и на какой границе он измеряется.</li><li>Проверьте учебные snapshots арифметикой. Отдельно выполните PromQL на сервере метрик и проверьте результат на реальном наборе series.</li><li>Если порог пересечён, найдите один связанный журнал или trace. Сравните время, operation, outcome и correlation ID.</li><li>Только после этого меняйте код, timeout, retry или правило alert. Зафиксируйте критерий отката.</li></ol>\n<h2>Ограничения и отрицательный путь</h2>\n<p>Метрика не видит событие, которое не дошло до точки instrumentation. Ошибка в браузере может произойти до приложения. Пропущенный scrape оставит окно неполным. Перезапуск процесса изменит сырое значение counter, хотя функция запроса умеет учитывать reset. Неправильный label может спрятать нужную операцию в общей series.</p>\n<p>Поэтому ноль ошибок не равен нулю пользовательских проблем. Проверьте, что target жив, series существует, timestamp попадает в окно, а журнал содержит тот же путь. Если условия не выполняются, честный результат — «сигнал недостаточен», а не «система исправна». Это отрицательное решение экономит время и не создаёт ложной уверенности.</p>\n<p>Учебные snapshots и код выше не измеряют память Prometheus, latency, нагрузку, доставку alert или поведение exporter. Они не подтверждают production-результаты. Их задача — показать связь между операционным вопросом, типом метрики, labels, окном и проверкой. Для реального внедрения нужен отдельный стенд с известным scrape, контролируемым запросом и сохранённым ответом PromQL.</p>\n<h2>Проверяемый критерий готовности</h2>\n<p>Разбор готов, если другой инженер может повторить его без устной подсказки: назвать series и её labels, объяснить момент увеличения counter, выполнить запрос с указанным окном, увидеть результат на доступном наборе данных и перейти от срабатывания к одному связанному журналу или trace. Дополнительно он должен показать отрицательный путь: почему ноль не закрывает жалобу, если metric не покрывает endpoint или scrape пропущен.</p>\n<h2>Проверяемые источники</h2>\n<ul><li><a href='https://prometheus.io/docs/practices/instrumentation/' target='_blank' rel='noopener noreferrer'>Prometheus: Instrumentation</a> — официальные рекомендации по счётчикам ошибок, общему числу попыток, выбору типа метрики и осторожности с labels.</li><li><a href='https://prometheus.io/docs/prometheus/latest/querying/functions/' target='_blank' rel='noopener noreferrer'>Prometheus: Query functions</a> — официальное описание <code>increase()</code>, counter reset и расчёта прироста в range vector.</li><li><a href='https://prometheus.io/docs/practices/naming/' target='_blank' rel='noopener noreferrer'>Prometheus: Metric and label naming</a> — официальные правила именования, единиц и ограничений для metric dimensions.</li><li><a href='https://prometheus.io/docs/concepts/metric_types/' target='_blank' rel='noopener noreferrer'>Prometheus: Metric types</a> — определения counter, gauge и histogram, использованные в сравнении типов метрик.</li></ul>"
}