Node.js Internals
Diagnostics Channel: Трейсинг и мониторинг
Как понять, почему production-сервер тормозит? Diagnostics Channel позволяет видеть ВСЕ: HTTP-запросы, DB-запросы, утечки памяти - без изменения кода приложения.
- **DataDog APM** трейсит миллионы Node.js приложений через Diagnostics Channel, автоматически находя N+1 queries и медленные эндпоинты
- **AWS X-Ray** использует каналы для distributed tracing в Lambda - связывает запросы через микросервисы в единый trace
- **Sentry** подписывается на http.client.request для автоматического breadcrumb логирования - видно все API-вызовы перед ошибкой
- **Netflix** использует каналы для A/B тестирования - перехватывает запросы и рандомно меняет поведение без деплоя
Зачем нужен Diagnostics Channel
**Diagnostics Channel** (Node.js 15+) - встроенная pub/sub система для инструментации приложения. В отличие от EventEmitter, это не бизнес-логика, а инфраструктурный слой для мониторинга: трейсинг HTTP-запросов, подключений к БД, работы с файлами.
**Ключевая идея:** Diagnostics Channel позволяет библиотекам публиковать диагностические события, а APM-инструментам (DataDog, New Relic) подписываться на них **без изменения кода приложения**.
Отличие от EventEmitter
| Критерий | EventEmitter | Diagnostics Channel |
|---|---|---|
| Назначение | Бизнес-логика (user.created, order.paid) | Инфраструктура (http.request, db.query) |
| Жизненный цикл | Создается явно (new EventEmitter) | Глобальные каналы (channel('http.server.request')) |
| Производительность | Overhead при каждом emit | Zero-cost если нет подписчиков |
| Use Case | Связь между модулями | Мониторинг, трейсинг, профилирование |
Zero-cost abstraction
Если на канал никто не подписан, `channel.publish()` не делает ничего - нет overhead на сериализацию данных, создание объектов событий. Это позволяет библиотекам добавлять диагностику без влияния на производительность.
**Важно:** Diagnostics Channel не заменяет логирование (Winston, Pino) - это низкоуровневый механизм для APM. Логи пишутся в файлы, каналы отправляют метрики в реальном времени.
В чем главное отличие Diagnostics Channel от EventEmitter?
Создание и публикация каналов
Каналы создаются через `diagnostics_channel.channel(name)` - это глобальный реестр, вызовы с одинаковым name возвращают один и тот же канал. Публикуйте события через `channel.publish(message)`, подписывайтесь через `channel.subscribe(callback)`.
Naming convention
Используйте namespace pattern: `module.operation.phase` - например, `http.server.request.start`, `db.postgres.query.end`. Это позволяет APM-инструментам фильтровать события по префиксам.
Структура сообщений
**Best Practice:** Передавайте объекты с минимальным набором полей - подписчики могут обогащать данные. Избегайте сериализации больших payload (req.body), передавайте только метаданные.
Проверка подписчиков
**Ошибки в подписчиках:** Если callback кидает exception, он не прерывает publish() - остальные подписчики всё равно вызовутся. Но ошибка выведется в stderr.
Зачем проверять channel.hasSubscribers перед publish()?
Подписка на системные каналы
Node.js публикует события для встроенных модулей: `http`, `net`, `dns`, `child_process`. Подписываясь на них, можно собирать метрики без изменения кода приложения.
Системные каналы HTTP
| Канал | Когда публикуется | Данные |
|---|---|---|
| http.client.request.start | Начало исходящего HTTP-запроса | { request } |
| http.client.response.finish | Получен полный ответ | { request, response } |
| http.server.request.start | Входящий запрос на сервер | { request, response, socket } |
| http.server.response.finish | Отправлен ответ клиенту | { request, response, socket } |
Трейсинг исходящих запросов
Каналы net и dns
**Use Case:** Audit логирование - записывайте все исходящие соединения для compliance (SOC 2, GDPR). Diagnostics Channel позволяет сделать это централизованно, без изменения кода модулей.
**Осторожно с утечками:** Не сохраняйте ссылки на `request`/`response` в Map без очистки - это приведет к memory leak. Используйте WeakMap или удаляйте в finish-событиях.
Какое преимущество дает подписка на http.client.request.start вместо обертки над http.request?
Интеграция с OpenTelemetry
**OpenTelemetry** - стандарт для distributed tracing, метрик и логов. Diagnostics Channel - основной механизм, через который OTel собирает трейсы из Node.js приложений без изменения кода.
Как работает OTel автоинструментация
Context Propagation: связь между сервисами
OpenTelemetry передает `traceparent` header через сервисы, чтобы связать запросы в единый trace. Diagnostics Channel автоматически добавляет этот header к исходящим запросам.
**Формат traceparent:** `00-{traceId}-{spanId}-{flags}`. Например: `00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01`.
Кастомные spans и атрибуты
Как OpenTelemetry инструментирует Node.js приложение без изменения кода?
APM инструментация: DataDog интеграция
**APM (Application Performance Monitoring)** - инструменты для мониторинга production: DataDog, New Relic, Dynatrace. Они используют Diagnostics Channel для автоматического сбора метрик, трейсов, профилирования.
DataDog APM: автоматическая инструментация
Что собирает DataDog через Diagnostics Channel
- **HTTP трейсы:** входящие и исходящие запросы (метод, URL, статус, latency)
- **Database queries:** автоинструментация для pg, mysql, mongodb, redis
- **Message queues:** RabbitMQ, Kafka, SQS
- **External APIs:** fetch, axios, node-fetch
- **DNS lookups и TCP соединения**
Кастомные метрики и теги
Профилирование: CPU и Memory
DataDog собирает CPU profiles (flamegraphs) и heap snapshots через Diagnostics Channel. Это помогает найти bottlenecks в production без влияния на производительность.
**Zero-overhead profiling:** DataDog использует sampling - снимает stack trace раз в 10ms, это дает <1% CPU overhead.
Интеграция с логами
**Production considerations:** - Не трейсите PII (emails, credit cards) - используйте фильтры - Сэмплинг трейсов: 100% в dev, 1-10% в production - Лимит spans на trace: 1000 (иначе trace обрежется)
Diagnostics Channel нужен только для APM-инструментов, в обычном приложении он бесполезен
Diagnostics Channel полезен для любого мониторинга: аудит логов, rate limiting, A/B тестирование
Через каналы можно: 1. Логировать все исходящие API-вызовы для compliance 2. Считать rate limits по IP (подписка на http.server.request.start) 3. A/B тесты: перехватывать запросы и рандомно менять поведение 4. Audit trail: записывать кто, когда, к какому ресурсу обращался
Какое преимущество дает continuous profiling через DataDog?
Ключевые идеи
- **Diagnostics Channel** - pub/sub для мониторинга, не для бизнес-логики (используйте EventEmitter для последней)
- **Zero-cost overhead:** если нет подписчиков, publish() не делает ничего - проверяйте channel.hasSubscribers
- **Системные каналы** (http, net, dns) позволяют мониторить Node.js без изменения кода
- **OpenTelemetry** автоинструментация работает через Diagnostics Channel - автоматический distributed tracing
- **APM инструменты** (DataDog, New Relic) используют каналы для сбора метрик, профилирования, error tracking
Связанные темы
Diagnostics Channel - часть экосистемы observability в Node.js:
- Performance Hooks — Измерение производительности (performance.now, PerformanceObserver) дополняет трейсинг через каналы
- Async Hooks — Низкоуровневый API для трейсинга async контекста - основа для AsyncLocalStorage и OTel context propagation
- Worker Threads — Трейсинг воркеров требует передачи traceparent через workerData или MessagePort
Вопросы для размышления
- Какие части типичного приложения нуждаются в мониторинге? HTTP API, DB queries, external integrations?
- Можно ли заменить middleware-логирование на подписку через Diagnostics Channel?
- В случае microservices, как трейсить запросы между сервисами? Применяются ли traceparent headers?