Наблюдаемость
Авто-инструментирование и ручные спаны: правило 80/20 в OTel
Авто-инструментирование говорит вам: “этот HTTP-запрос занял 800 мс и сделал 5 запросов в БД.” Ручное инструментирование говорит: “медленной частью был цикл проверки мошенничества, который разветвился на 12 вызовов движка правил, из которых 3 завершились таймаутом.” Оба необходимы. Ни одного в отдельности недостаточно.
Авто-инструментирование: 70-80%, которые вы получаете бесплатно
Большая часть production-трафика проходит через небольшой набор фреймворков — HTTP-серверы, HTTP-клиенты, драйверы БД, клиенты очередей — которые contrib-библиотеки OTel инструментируют из коробки.
Java: OTel Java Agent (-javaagent JAR) перезаписывает байткод при старте JVM и автоматически инструментирует ~200 библиотек — никаких изменений кода не требуется. Добавьте один флаг -javaagent к JVM-аргументам, задайте переменные окружения OTEL_SERVICE_NAME и OTEL_EXPORTER_OTLP_ENDPOINT, и каждый HTTP-запрос, JDBC-запрос и Kafka-сообщение будет трассирован.
Python: opentelemetry-instrument оборачивает процесс и monkey-patching применяет изменения к популярным библиотекам (Flask, Django, FastAPI, SQLAlchemy, Redis, Celery и др.).
Node.js: @opentelemetry/sdk-node плюс набор инструментаций (express, http, pg, redis, kafkajs), зарегистрированных до любых других импортов через require hook.
Go: Авто-инструментирование менее зрелое, поскольку в Go нет monkey-patching. Авто-инструментирование на основе eBPF (через uprobes) — развивающийся подход; contrib-библиотеки OTel для Go требуют явной настройки middleware для каждого фреймворка.
| Язык | Механизм авто-инструментирования | Покрытие |
|---|---|---|
| Java | -javaagent JAR, перезапись байткода при старте | ~200 библиотек: HTTP + БД + очереди + кэши |
| Python | opentelemetry-instrument, monkey-patching | Flask, Django, FastAPI, SQLAlchemy, Redis, Celery |
| Node.js | require hook до других импортов | express, http, pg, redis, kafkajs, grpc |
| Go | Настройка middleware вручную (нет monkey-patching) | net/http, gRPC, database/sql — требует ручной интеграции |
70-80% — простая часть: HTTP-маршруты, запросы в БД, вызовы кэша, взаимодействие с очередями. Авто-инструментирование говорит вам что произошло. Ручное инструментирование говорит почему.
Ручное инструментирование: мост к диагностической ценности
Оставшиеся 20-30% — бизнес-специфичные спаны, кастомные атрибуты и сквозные концерны — здесь живёт настоящая диагностическая ценность, потому что именно здесь живут бизнес-вопросы.
Паттерн: откройте спан вокруг бизнес-операции, прикрепите атрибуты, описывающие операцию, и генерируйте span events для значимых моментов.
const span = tracer.startSpan("fraud.check", {
attributes: {
"customer.segment": customer.segment,
"order.value_cents": order.valueCents,
"feature_flag.fraud_model": flags.fraudModel,
},
});
try {
for (const rule of rules) {
const result = await rules.engine.evaluate(rule, order);
span.addEvent("rule.evaluated", {
"rule.id": rule.id,
"rule.result": result.outcome,
"rule.latency_ms": result.latencyMs,
});
}
} catch (err) {
span.recordException(err);
span.setStatus({ code: SpanStatusCode.ERROR });
} finally {
span.end();
}Этот спан сообщает о сегменте клиента, стоимости заказа, состоянии фича-флага модели проверки мошенничества и результате каждой оценки правила — данные, которые авто-инструментирование не может видеть, поскольку не знает вашего бизнес-домена.
Паттерн платформенной обёртки
Ручное инструментирование — это один и тот же паттерн во всех сервисах, поэтому платформенные команды публикуют тонкую вспомогательную библиотеку, оборачивающую OTel API с организационными конвенциями:
- Типизированные бизнес-атрибуты — предопределённые имена атрибутов для общих концептов (сегмент клиента, состояние фича-флага, размер батча), чтобы команды не могли сделать опечатки.
- Хелперы редактирования — удаление PII перед прикреплением к спанам (email → хэш, SSN → маска).
- Хелперы span events — типизированные хелперы для типовых событий (cache miss, retry, fallback).
- Предварительная конфигурация Resource — имя сервиса, версия и окружение развёртывания из стандартизированного набора переменных окружения.
Новые сервисы импортируют обёртку; CI-линтер отклоняет прямое использование OTel SDK в новом коде (для предотвращения неконтролируемых имён атрибутов). Хуки переопределения позволяют командам добавлять специфичные для сервиса атрибуты без изменения обёртки.
Почему это работает
Почему не один спан на функцию? Каждый спан имеет накладные расходы: SDK выделяет структуру спана, записывает атрибуты, запускает семплер, а при завершении спана — запускает batch processor, сериализует в OTLP и, возможно, помещает в очередь экспортёра. При ~1-5 мкс на спан, 100 спанов на запрос при 10к RPS — это 1-5 мс накладных расходов CPU на ядро в секунду. Что важнее, плоский список из 100 спанов на уровне функций читается хуже, чем 8 спанов на правильном уровне абстракции. Рекомендация: один спан на пересечение границы сервиса плюс один на значимый вызов подсистемы (один спан для проверки мошенничества, один для запроса в БД внутри неё, но не по спану на каждый метод внутри проверки).
Что покрывает авто-инструментирование, чего не может ручное, и что покрывает ручное, чего не может авто?
Почему платформенная команда публикует языково-специфичную обёртку вокруг OTel SDK вместо того, чтобы позволить продуктовым командам вызывать OTel SDK напрямую?
Упорядочите шаги добавления хорошо инструментированного бизнес-спана в сервис:
- 1 Откройте спан с описательным именем операции (например, fraud.check)
- 2 Прикрепите структурированные атрибуты: сегмент клиента, стоимость заказа, состояние фича-флага
- 3 Выполните бизнес-логику
- 4 Генерируйте span events для значимых моментов: cache miss, retry, результат оценки правила
- 5 При ошибке: вызовите span.recordException(err) и установите статус ERROR
- 6 Всегда вызывайте span.end() в блоке finally для гарантированного сброса спана
- 01Почему OTel Java Agent — наиболее мощный вариант авто-инструментирования среди языков?
- 02Какова правильная гранулярность ручных спанов и почему?
- 03Что решает паттерн платформенной обёртки и что он стоит?
Авто-инструментирование инструментирует 70-80% production-трафика бесплатно: OTel Java Agent перезаписывает байткод для покрытия ~200 библиотек; opentelemetry-instrument в Python применяет monkey-patching к популярным фреймворкам; Node.js использует require-hook до других импортов. Go требует ручной настройки middleware (нет monkey-patching). Оставшиеся 20-30% — ручные спаны: откройте спан вокруг бизнес-операции, прикрепите типизированные атрибуты (сегмент клиента, состояние фича-флага, размер батча), генерируйте span events для значимых моментов, записывайте исключения через span.recordException(). Накладные расходы — ~1-5 мкс на спан; сохраняйте гранулярность на уровне бизнес-операций, а не функций. Платформенные команды публикуют языково-специфичную обёртку, обеспечивающую соблюдение Semantic Conventions, редактирование и конфигурацию resource, чтобы продуктовые команды могли добавлять бизнес-спаны без изобретения новых имён атрибутов.
- Vendor-нейтральность, eBPF-инструментирование, Operator и OTel в браузере и serverlesssenior
- Эксплуатация OTel Collector: надёжность, version skew, режимы отказа и управлениеsenior
- OTel: построй vendor-нейтральный пайплайнsenior
- OTel: тест с множественным выборомsenior
- OTel: чтение конфигов и трейсовsenior
- OTel: тест на свободное припоминаниеsenior
встречается в167
- Путь запроса: семь остановок от сокета до ответаjunior
- Accept и парсинг: от очереди ядра до типизированного запросаmiddle
- Маршрутизация и middleware: что выполняется и в каком порядкеmiddle
- Обработчик и ответ: от бизнес-логики до байтов на проводеmiddle
- Стриминг и backpressure: когда клиент читает медленнее, чем вы пишетеsenior
- Таймауты и хвостовая задержка: бюджеты, дедлайны и ловушка fan-outsenior
- Middleware и DI: два паттерна, формирующие любой backendjunior
- Пишем middleware: сигнатуры, next() и три модели фреймворковmiddle
- Инверсия управления: как зависимости добираются до классаmiddle
- Скоупы и время жизни DI: singleton, request, transientmiddle
- DI как шов для тестов: фейки, моки и граница, которая важнаsenior
- DI-контейнеры в продакшене: графы разрешения, циклы и когда не стоитsenior
- Блокирующий vs неблокирующий I/O: два способа ждатьjunior
- Event loop: один поток, упорядоченные фазыmiddle
- Что блокирует цикл: CPU-работа и синхронные вызовыmiddle
- Вынос CPU-работы: worker threads и пул libuvmiddle
- Backpressure и ограниченная конкурентностьsenior
- Пропускная способность под нагрузкой: хвостовая задержка и насыщениеsenior
- Зачем пул: цена создания соединенияjunior
- Размер пула: почему больше не значит быстрееmiddle
- Взятие и таймауты: очередь ожидания — настоящий дроссель задержкиmiddle
- Стратегии retry: backoff, jitter и thundering herdmiddle
- Наблюдаемость, production-инциденты и дизайн для глобального масштабаsenior
- Задачи, микрозадачи и scheduler.yield()middle
- Точность таймеров, троттлинг и фоновая работаmiddle
- Event loop Node.js: фазы, nextTick и задержка циклаsenior
- Стратегии рендеринга: SSG, SSR, ISR, streaming и гидратацияjunior
- SSG, SSR, ISR, streaming и RSC — как работает каждая стратегияmiddle
- Цена гидратации: selective, progressive, острова, resumabilitymiddle
- Core Web Vitals: что измеряют LCP, INP и CLSjunior
- LCP: четыре фазы, одна доминирующая стоимостьmiddle
- INP: input delay, processing, presentationmiddle
- Lab vs field: почему они расходятся и как использовать каждыйmiddle
- Трейдоффы метрик, RUM-атрибуция и цикл CI+полеsenior
- Общая картина: от URL до LCP до INP как эстафетаjunior
- Восемь слоёв трассировки: от service worker до второй навигацииmiddle
- Пять канонических поломок: где производство стабильно ломаетсяsenior
- Метод трёх треков: чтение трасс и построение системы мониторингаsenior
- Что такое индекс и как он ускоряет запросыjunior
- Leading-column rule: почему порядок столбцов в composite-индексе важенmiddle
- Partial, expression и covering-индексыmiddle
- Типы индексов: GIN, GiST, BRIN, Hash, Bloom и HOT-обновленияmiddle
- Index-only scan, Visibility Map и INCLUDEsenior
- Типичные сбои в продакшне и аудит индексовsenior
- Упражнение по проектированию индексов: стратегия полнотекстового поискаsenior
- EXPLAIN и планы выполнения: что решает планировщик и почемуjunior
- Типы сканирования: Seq, Index, Bitmap, Index-Onlymiddle
- Алгоритмы соединения и каскад ошибок оценки строкmiddle
- pg_statistic, ANALYZE и производственная наблюдаемостьmiddle
- Расширенная статистика: исправление ошибок оценки для коррелированных колонокsenior
- Кеш планов, настройка константных стоимостей и внутренности планировщикаsenior
- Производственные режимы отказа и стабильность плановsenior
- Connection pool: зачем амортизировать стоимость backend Postgresjunior
- Режимы PgBouncer: session, transaction и statementmiddle
- Размер пула: формула (ядра × 2) + шпинделей и двухуровневый стекmiddle
- Исчерпание пула и idle-in-transaction: сценарий отказа в 3 ночиmiddle
- Миграция на transaction mode: план развёртывания и prepared statements в PgBouncer 1.21middle
- Процессная модель Postgres и почему увеличение max_connections снижает производительностьsenior
- Ландшафт пулеров 2026, serverless connection storms и полная таксономия отказовsenior
- ADD COLUMN: мгновенно в PG 11+ против перезаписи в старом Postgresjunior
- Режим отказа очереди блокировок: почему мгновенный DDL может заморозить базуmiddle
- Безопасные DDL-паттерны: NOT VALID, CONCURRENTLY и исправления небезопасных операцийmiddle
- Таксономия сбоев миграций и дисциплина продакшнаsenior
- Выбор ключа шарда: стратегии hash, range, list и directorymiddle
- Ко-локация и Citus: инвариант, делающий шардирование пригодным к использованиюmiddle
- Режим отказа hot shard: обнаружение, изоляция и долгосрочная политикаmiddle
- Онлайн-решардинг, 2PC и операционная стоимость шардированияsenior
- Семь актов: от CREATE TABLE до Citusjunior
- Акты 1–3 в глубину: схема, индексы и статистика планировщикаmiddle
- Акты 4–6 в глубину: MVCC bloat, connection pooling и безопасные миграцииmiddle
- Акт 7 в глубину: шардинг, co-location и семиуровневый каскад трейдоффовmiddle
- Наблюдаемость, антипаттерны и производственный триажsenior
- Биты в проводеjunior
- Математика задержкиmiddle
- Bufferbloat и перегрузкаsenior
- Граница физического уровняsenior
- Номера последовательности и состояние соединенияmiddle
- Управление потоком и перегрузкойmiddle
- BBR, производственная наблюдаемость и за пределами TCPsenior
- CDN: контент по соседствуjunior
- Anycast и GeoDNS: маршрутизация к ближайшему edgemiddle
- Многоуровневый кеш и Cache-Controlmiddle
- Заголовок Vary и cache keysmiddle
- Stale-while-revalidate и cache stampedesenior
- Edge workers и edge-side compositionsenior
- CDN: операции и observabilitysenior
- WebSocket: HTTP-апгрейд до постоянного соединенияjunior
- WebSocket vs SSE vs long-polling: выбор правильного транспортаmiddle
- Backpressure в WebSocket: когда клиенты не успеваютmiddle
- Реконнект: jittered backoff, thundering herd, восстановление сообщенийsenior
- WebSocket в масштабе: HTTP/2 мультиплексирование, permessage-deflate, C10Msenior
- WebSocket в production: прокси, безопасность и распределённая архитектураsenior
- Что делают обратные проксиjunior
- Алгоритмы балансировки: от round-robin до power-of-two-choicesmiddle
- L4 vs L7 балансировка и сохранение IP клиентаmiddle
- Health checks, connection draining и slow startmiddle
- Retry-бури, circuit breakers и load sheddingsenior
- Устойчивая архитектура LB: anycast, zone-aware маршрутизация и observabilitysenior
- Почему QUIC, а не TCP+TLSjunior
- QUIC-потоки и head-of-line blockingjunior
- Объединённое рукопожатие и 1-RTTmiddle
- Connection ID и миграция сетиmiddle
- Обнаружение потерь и управление перегрузкойmiddle
- Возобновление 0-RTT и шифрование пакетовsenior
- Развёртывание и стоимость CPUsenior
- DDoS: что это и почему работаетjunior
- Атаки усиления и истощение состоянияmiddle
- Ограничение скорости: алгоритмы и архитектураmiddle
- WAF, межсетевые экраны, mTLS и HSTSmiddle
- Отравление DNS-кэша и BGP-перехватsenior
- Эшелонированная защита и экономика атакsenior
- Двенадцать слоёв: один URL, семь действующих лицjunior
- DNS, TCP, TLS по очереди: куда уходят миллисекундыmiddle
- Критический путь рендеринга и Core Web Vitalsmiddle
- Перехват прокси и шлюзы безопасности: rate limiter, WAF, mTLSmiddle
- Альтернативные пути: QUIC 0-RTT, WebSocket upgrade, миграция соединенияmiddle
- Наблюдаемость: распределённые трейсы, USE/RED и семплированиеsenior
- Устойчивость: каскадные повторы, circuit breakers и error budgetsenior
- Сначала профиль: измерь куда реально уходит времяjunior
- Закон Амдала и self-time: потолок любого ускорения, которое ты можешь выпуститьmiddle
- Измерительный цикл: микробенч, макробенч, prod-профиль, эффект наблюдателяmiddle
- Чтение флейм-графов: формы, профайлеры по языкам и 60-секундный сканmiddle
- Статистические baseline''''ы: почему один запуск — не измерениеmiddle
- История профайлеров и ловушки микробенчей: от Кнута до GWPsenior
- Hardware counters, профили холодного старта и безопасность профилейsenior
- Непрерывное профилирование в масштабе: затраты, CI-гейты, корреляция с трейсами и антипаттерныsenior
- Что делает путь горячим: симптом против причиныjunior
- Пять форм hotspot''''а: CPU, аллокации, кэш, лок, syscallmiddle
- Чтение parent и child chains: где применять правкуmiddle
- JIT deopt, цикл fix-and-verify и PR-time профилированиеmiddle
- Аппаратные счётчики и Intel TMA: диагностика подкатегорийsenior
- False sharing и горячие пути нативных мостовsenior
- Горячие пути в production: безопасность, хвостовая латентность и происхождение инструментовsenior
- Иерархия памяти: почему расстояние важнее числа операцийjunior
- Row-major vs column-major: порядок доступа и разрыв в 9xjunior
- Branch prediction: 10–30 циклов штрафа за неожиданный ifmiddle
- Hardware prefetcher, TLB и memory-level parallelismsenior
- Основы GC: за что рантайм берёт налогjunior
- Алгоритмы GC: поколенческая гипотеза, concurrent marking и write barriermiddle
- GC tradeoffs: пауза, throughput, память и давление аллокацийmiddle
- Настройка GC: пейсинг, форма кучи и наблюдаемость аллокацийmiddle
- Внутреннее устройство GC: tri-color инвариант, write barriers и глубокое погружение в рантаймыsenior
- GC в production: наблюдаемость, безопасность, edge cases и управление флотомsenior
- N+1: одна логическая операция, много round-trip''''овjunior
- Семейства фиксов: JOIN, IN, preload и DataLoadermiddle
- Обнаружение N+1: query logs, APM traces и CI gatesmiddle
- DataLoader: батчинг по дереву резолверовmiddle
- Кросс-протокольный N+1: HTTP fan-out и Redis MGETmiddle
- N+1 в масштабе: исчерпание пула, изменения планов и денормализацияsenior
- Batching: амортизируй фиксированную цену каждой операцииjunior
- Окно батчинга: размер и время ожиданияmiddle
- Batching в Kafka и Postgresmiddle
- io_uring и наблюдаемость пакетированияmiddle
- От Nagle до io_uring: эволюция пакетированияmiddle
- Backpressure, изоляция сбоев и безопасность батчей в продакшенеsenior
- Что на самом деле стоит bundle: download, parse, compile, executejunior
- Core Web Vitals: LCP, INP и CLSmiddle
- Code splitting: route-level, component-level, vendor splittingmiddle
- Tree shaking и compression: удаляем то, что не используемmiddle
- Third-party scripts: тихий убийца бюджетаmiddle
- CI enforcement и RUM: делаем бюджеты рабочимиmiddle
- V8 JIT-пайплайн, HTTP-приоритеты и безопасность bundlesenior
- Цикл performance: дисциплина, а не проектjunior
- Классификация и исправление: сопоставление family bottleneck с методамиmiddle
- Observability-стек и CI gates: ловить регрессии до выпускаmiddle
- От инцидента к enforcement: SLO burn до верифицированного исправления за 35 минутmiddle
- Культура, экономика и масштаб performancesenior