Симптом для диагностики звучит знакомо: график ошибок показывает изменение, но инженер не может назвать запрос и этап, на котором оно возникло. В ответ часто начинают искать текст исключения во всех logs или добавляют request ID в metric labels. Первый путь тонет в несвязанных записях, второй смешивает счётчик с идентичностью одного запроса. Причина не становится ближе: у трёх источников нет договора, который превращает один сигнал в вопрос к следующему.
Цена такого разрыва — решение на основании наиболее громкой витрины. Можно увеличить timeout, включить retry или объявить downstream виновником, хотя связь между error count, span и event не подтверждена. Эта статья не расследует реальный инцидент и не собирает telemetry. Она строит безопасный diagnostic route для одного fixed synthetic сценария, чтобы показать: evidence одного отказа складывается из разных объектов, а не из максимального количества labels.
Начните не с поиска, а с вопроса
У диагностики есть три уровня. Metric помогает сформулировать, какой класс исходов стоит рассматривать: например, synthetic `outcome=synthetic-rejected` для synthetic checkout route. Trace должен показать предполагаемый причинный путь из gateway к payment шагу. Log/event record должен назвать событие на payment step и сохранить тот же correlation key. Только после этого появляется evidence-card: она говорит, какую гипотезу можно проверить и чего пока нет. Никакой из объектов по отдельности не заменяет остальные.
| Очередь | Вопрос | Нужное представление | Допустимый результат | Что не делать |
|---|---|---|---|---|
| 1 | какой класс результата разбираем? | metric labels | synthetic route + outcome | не добавлять request ID ради фильтра |
| 2 | какой путь должен ему соответствовать? | trace + span tree | один synthetic trace ID, два шага | не считать график доказательством причины |
| 3 | какое событие произошло на шаге? | log/event record | event name + trace ID + span ID | не искать по свободному тексту без correlation |
| 4 | какой вывод честен? | evidence card | not-a-production-observation | не объявлять hypothesis подтверждённой fixture-ом |
Metric даёт границу разбора, а не виновника
В учебном наборе metric record содержит имя `synthetic.checkout.authorization.rejected.total`, значение `1` и три labels. Значение `1` — не наблюденный counter, а фиксированная часть fixture. Оно нужно только чтобы показать форму: одна маленькая точка может обозначать класс outcome. По ней нельзя определить user, order, request или span. Такую границу полезно сохранять даже если UI backend позволяет кликнуть на dimensions: возможность фильтра не превращает metric в достоверный журнал событий.
Если на первом шаге неизвестно, какой вопрос нужна решать, не пополняйте labels «на всякий случай». Сначала назовите route template и outcome class, которые должны быть малым словарём. Затем спросите владельца инструмента, какая реальная единица aggregation поддержана, какие resource attributes добавляются и где будет измеряться cardinality. Без ответа status должен быть «не проверено», а не «у нас низкая cardinality». Fixture помогает удержать именно эту дисциплину: лишний `trace_id`, `request_id` или `user_id` он отвергает до того, как поле станет привычным.
Trace связывает причины, log/event фиксирует контекст
Дальше мы идём по `synthetic-trace-2023-09-A`. В trace object есть root span gateway и дочерний payment span; оба названия и состояния synthetic. Связь потомка с родителем — модель причинного маршрута, а не свидетельство выполнения вызова. Log/event record ссылается на payment span, имеет тот же trace ID и event name отказа. Если trace ID или span ID в log отличаются, fixture возвращает отказ. Это простое правило полезнее длинного списка полей: событие должно либо объяснять конкретный шаг пути, либо честно оставаться несвязанным.
Event attributes нужны для узкой диагностики события. В примере есть `failure.class=synthetic-declined` и `retry.advice=synthetic-do-not-retry`. Они не говорят, как надо обрабатывать настоящие платежи, и не являются production error message. Их роль — показать разницу между типом отказа и точной идентичностью запроса. В реальном проекте перед добавлением любых attributes нужно отдельно решить privacy, возможность redaction, retention, доступ к поиску и стабильность названий. Нельзя прятать эти решения под словом «контекст».
Прогоните одну контролируемую модель
Код ниже создаёт fixed synthetic scenario в памяти. Он не может обратиться к приложению или telemetry backend, не читает clock и не создаёт telemetry. `runTelemetryFixture()` проверяет девятнадцать assertions: общий trace ID для trace и log, правильный span, три разрешённых labels, отсутствие trace ID в labels, закрытый список входных полей, отрицательные ветки mismatch и предел rollback. Это упражнение для review контракта. Его PASS не подтверждает, что downstream отказал, что metric выросла, что span записался или что log можно найти.
import {
assembleSyntheticTelemetryScenario,
runTelemetryFixture,
} from './upgrade-2023-09.mjs';
const scenario = assembleSyntheticTelemetryScenario({
synthetic: true,
traceId: 'synthetic-trace-2023-09-A',
rootSpanId: 'synthetic-span-gateway-A',
downstreamSpanId: 'synthetic-span-payment-A',
logTraceId: 'synthetic-trace-2023-09-A',
logSpanId: 'synthetic-span-payment-A',
eventName: 'synthetic.payment.authorization-rejected',
metricLabels: {
service: 'synthetic-checkout-api',
route: 'synthetic-checkout',
outcome: 'synthetic-rejected',
},
});
const report = runTelemetryFixture();
if (!Object.values(report.assertions).every(Boolean)) throw new Error('fixture failed');
console.log(scenario.evidence.conclusion); // not-a-production-observation
// Не создаёт trace/log/metric, не запускает SDK и не отправляет данные.
node web/scripts/upgrade-2023-09.mjs --verify-fixture
# PASS проверяет только fixed synthetic records in memory.
# Не доказывает incident, production latency, trace export или cardinality.
После локального прогона полезно записать evidence без переобобщения. Корректная формулировка: «модель ожидает, что один correlation key соединяет заданный payment event и заданный trace; metric использует только три fixed dimensions». Некорректная: «причина ошибки найдена» или «metric безопасна для production». Разница кажется формальной только пока первое решение не затронуло retry, alert policy или пользовательские данные. В инженерном разборе неизвестное — это тоже результат, который должен пережить передачу задачи.
Маршрут: симптом → причина → проверка → действие
- Симптом. Есть числовой признак класса ошибок, но нет понятного перехода к одному пути запроса и событию, которое его объясняет.
- Причина. Metric, trace и log/event живут без общего contract: метрика получила per-request labels, а log не несёт trace/span correlation.
- Проверка. Выберите один synthetic outcome. Проверьте, что metric содержит только service/route/outcome, trace имеет один ID и два шага, а log/event повторяет trace ID и downstream span ID. Запустите fixture с отрицательными ветками.
- Действие. Зафиксируйте маршрут metric → trace → log/event → evidence. Поставьте trace ID в correlation fields, а не в labels метрики; detail оставьте event attributes только после отдельного policy review.
- Проверка вывода. В реальном контуре заранее назовите, какой query или безопасная выборка может подтвердить каждую стрелку. Пока она не выполнена, conclusion остаётся не подтверждённым.
- Следующий шаг. Добавьте в runbook одну ветку mismatch: что делать, если metric есть, но trace или log не correlates. Это отдельная проблема instrumentation, а не приглашение добавить новый ID в счётчик.
Когда останавливать, а когда откатывать
Если на review обнаружился high-cardinality label или несвязанный event, первое действие — остановить распространение нового контракта. Это не равно удалить все данные: нельзя обещать удаление, не зная платформы, retention, доступа и состоявшегося rollout. Затем нужно отделить два вопроса: какие новые записи могут продолжать возникать и какие потребители уже зависят от поля. Только после этого владелец выбирает обратимое действие для конкретной конфигурации.
Fixture умеет только вернуть snapshot synthetic полей и пометить `telemetry=not-created-or-deleted`. Он не выключает instrumentation, не меняет sampling, alert, dashboard или access policy. Такой rollback не декоративен: он ставит границу между проверкой модели и операционным изменением. В реальном runbook точка возврата должна быть названа точнее: версия конфигурации, набор approved fields, способ проверить отсутствие дальнейшего потока и владелец подтверждения.
Ограничения и следующий шаг
Здесь нет реальных logs, metrics, traces, latency, cardinality, traffic, backend records или incident data. Нет отправки данных, запроса, collector, exporter, storage, sampling, alerting, query, dashboard или production effect. Synthetic `value: 1` не является измерением; synthetic IDs не являются request IDs. Пакет также не утверждает, что реальные error messages, user fields или маршруты допустимы для хранения. Он только различает роли полей и показывает, какую связь надо проверить позднее.
Следующий шаг — провести ограниченное design review одного instrumentation change. Договоритесь о: одном metric question, одном route template, малом outcome vocabulary, одном correlation key и минимальном event schema. Затем выберите реальную разрешённую среду и способ проверить путь без публикации чувствительных значений. Если итогом окажется, что trace context не проходит конкретную границу, это не поражение модели: это точная задача для следующего изменения, а не основание расширять cardinality метрики.
Историческая граница сентября 2023
Материал использует только OpenTelemetry Specification v1.20.0, опубликованную 7 апреля 2023 и доступную к сентябрю того года. Она уже описывала tracing API, metrics data model и logs data model, на которые опирается различение сигналов. Статья не утверждает, что конкретная SDK, transport, collector или backend имеют одинаковую зрелость, и не переносит в 2023 год более поздние договорённости команды или инструмента.
Проверяемые источники
- OpenTelemetry Specification: Release v1.20.0, 7 апреля 2023 — официальный versioned release, доступный к сентябрю 2023. Версия фиксирует историческую рамку статьи, но не подтверждает, что конкретная система экспортирует или хранит telemetry.
- OpenTelemetry Specification v1.20.0: Overview — первичный обзор разделяет tracing, metrics, logs, resources и context propagation. Он не предписывает один backend, dashboard или retention policy.
- OpenTelemetry Specification v1.20.0: Tracing API — описывает SpanContext, TraceId и SpanId как данные, которые могут передаваться в distributed context. Само наличие поля в учебной записи не означает, что контекст дошёл через реальный transport.
- OpenTelemetry Specification v1.20.0: Metrics Data Model — различает события, streams, time series и attributes. В статье слово label применяется к маленькому договору dimension values; он не измеряет фактическую cardinality какого-либо backend.
- OpenTelemetry Specification v1.20.0: Logs Data Model — описывает LogRecord, включая TraceId, SpanId и Attributes. Он не доказывает, что event/log запись конкретного сервиса доставлена, индексирована или доступна в поиске.