Впервые вам действительно нужна наблюдаемость, когда вы спокойно смотрите на приборную панель. Это когда пользователь пишет "оформление заказа медленно", график ошибок выглядит нормально, а в логах только ряд отключенных сообщений.
OpenTelemetry был создан, чтобы избежать этого момента: не для того, чтобы было больше графики, а для того, чтобы соединить части. Запрос поступает в API, вызывает базу данных, проходит через внешнего поставщика, отправляет задание в очередь и, возможно, завершается сбоем трех служб позже. Без распределенной трассировки вы восстанавливаете эту историю вручную. По крайней мере, с OpenTelemetry у вас есть карта.
Дело не в trace, а в истории
trace — это последовательность span. В таком случае это звучит холодно. На практике каждый span — это часть истории: POST /checkout, SELECT inventory, call payment provider, publish order.created.
Ценность появляется, когда вы начинаете отвечать на реальные вопросы:
- какой внешний сервис тормозит?
- ошибки исходят от конкретной версии?
- проблема касается всех или только одного арендатора?
- повторная попытка скрывает тайм-аут?
- асинхронное задание запускается, но затем умирает где-то еще?
Эти вопросы не могут быть решены с помощью console.log, брошенного в спешке. Действительно, часто журнал, добавленный в экстренной ситуации, помогает вам сегодня, а завтра становится шумом.
Как бы это поместить в приложение Node.js
Самая здоровая настройка проста: приложение производит телеметрию, а Collector решает, куда ее отправить.
Node.js app -> OpenTelemetry Collector -> backend di observability
Почему бы не экспортировать напрямую поставщику? Поскольку на первый взгляд кажется, что это быстрее, затем вы понимаете, что каждая служба имеет разные конфигурации, разные повторы, разные фильтры и не имеет центральной точки для удаления конфиденциальных данных или изменения места назначения.
Collector скучно во всех смыслах. Он получает OTLP, выполняет пакетную обработку, может фильтровать, выполнять выборку, добавлять общие атрибуты и экспортировать в несколько систем.
Самоинструмент: хорошо, но недостаточно
В Node.js я бы начал с автоинструментации. Это дает вам немедленную информацию об HTTP, поддерживаемых платформах, базах данных и общих библиотеках.
npm install @opentelemetry/sdk-node \ @opentelemetry/auto-instrumentations-node \ @opentelemetry/exporter-trace-otlp-http
Затем вы инициализируете SDK перед остальной частью приложения:
import { NodeSDK } from '@opentelemetry/sdk-node'; import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'; import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node'; const sdk = new NodeSDK({ traceExporter: new OTLPTraceExporter({ url: process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT, }), instrumentations: [getNodeAutoInstrumentations()], }); sdk.start();
Однако здесь рассматривается структура, а не ваш продукт. Он знает, что вы сделали запрос, но не знает, что запрос был в режиме «создать заказ» или «продлить подписку». Для этого вам понадобится span руководств в тех точках, где имеет значение доминирование.
const span = tracer.startSpan('checkout.create_order'); try { span.setAttribute('cart.items_count', input.items.length); const order = await createOrder(input); span.setAttribute('order.id', order.id); return order; } catch (error) { span.recordException(error as Error); throw error; } finally { span.end(); }
Я бы не стал никуда выкладывать мануалы по span. Я бы поставил их туда, где в три ночи хотелось бы понять, что произошло, не читая половину кодовой базы.
Три правила, которые помогут избежать хаоса
Первое правило: каждый сервис должен иметь service.name, среду и версию. Это кажется тривиальным, но без этих атрибутов trace гораздо менее полезен. Когда при развертывании что-то ломается, вам нужно отфильтровать по версии за две секунды.
Второе правило: не помещайте в атрибуты конфиденциальные данные. Электронные письма, токены, целочисленные полезные данные и адреса не должны случайно попасть в серверную часть наблюдаемости. Если вам нужно идентифицировать пользователя, рассмотрите внутренние идентификаторы, хеширование или менее конфиденциальные поля.
Третье правило: обратите внимание на мощность. user.id как атрибут trace может иметь смысл. Будучи метрической меткой, он может разрушить ваши затраты и производительность.
Метрики: мало, но хорошо
Я бы начал с очень практичных показателей:
- темпы, ошибки и продолжительность запросов;
- латентность внешних зависимостей;
- количество таймаутов и повторов;
- глубина хвостов;
- продолжительность работы;
- процент ошибок на версию.
Остальное добавляется по мере необходимости. Дашборды, полные графиков, на которые никто не смотрит, — это мебель, а не наблюдаемость.
Журналы: все еще полезны, но связаны
Логи не исчезают. Они просто становятся намного полезнее, когда несут trace_id и span_id. Таким образом, вы можете начать с журнала ошибок и открыть trace или начать с медленного trace и читать только журналы, созданные на этом пути.
Без корреляции вы ищете иголки. Благодаря корреляции вы, по крайней мере, знаете, в каком ящике искать.
Контрольный список, который я бы использовал, прежде чем сказать: «Мы застрахованы»
- trace фактически объединяет несколько служб.
- Журналы включают
trace_idиspan_id. - Collector настроен с ограничениями пакетной обработки и памяти.
- Ошибки записываются в span.
- Существует политика отбора проб.
- Метрики имеют контролируемую кардинальность.
- Конфиденциальные данные фильтруются.
- Оповещения начинаются с симптомов пользователя, а не случайных графиков.
Заключение
OpenTelemetry не решает производственные проблемы самостоятельно. Но то, как вы с ними справляетесь, меняется. Вместо того, чтобы слепо добавлять логи, вы начинаете следовать фактическому пути запроса.
Для меня признак того, что это работает, прост: когда что-то происходит, команда перестает спрашивать: «Куда мы смотрим?» и начинает спрашивать: «Почему этот кусок медленный?». Именно здесь наблюдаемость становится инструментом, а не набором информационных панелей.