Hacker News Digest

27 августа 2025 г. в 12:10 • oneuptime.com • ⭐ 88 • 💬 28

OriginalHN

#opentelemetry#nodejs#typescript#express#fastify#jaeger#oneuptime#distributed-tracing

What Are Traces and Spans in OpenTelemetry?

Trace — полный путь одного запроса через все сервисы.
Span — отдельный шаг в этом пути (функция, SQL, HTTP-вызов).
Root span — первый span (обычно входящий HTTP-запрос).
Child span — вложенный шаг.
Context — передаёт trace_id и текущий span по асинхронным вызовам.
Sampler — решает, записывать ли трассировку.
Exporter — отправляет spans в OneUptime, Jaeger и т.д.


Структура span

  • name — короткое имя операции
  • start/end time — длительность
  • status — OK / ERROR
  • attributes — произвольные метки (user_id, db.table)
  • events — точки во времени (exception, cache hit)
  • links — связи с другими traces

Быстрый старт в Node.js / TypeScript

npm i @opentelemetry/api @opentelemetry/sdk-node \
      @opentelemetry/auto-instrumentations-node
// tracing.ts
import { NodeSDK } from '@opentelemetry/sdk-node';
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';

const sdk = new NodeSDK({
  serviceName: 'my-api',
  traceExporter: new OTLPTraceExporter({ url: 'https://otlp.oneuptime.com' }),
  instrumentations: [getNodeAutoInstrumentations()],
});
sdk.start();

Ручная инструментация

import { trace } from '@opentelemetry/api';
const tracer = trace.getTracer('my-service');

async function getUser(id: string) {
  return tracer.startActiveSpan('db.users.findById', async (span) => {
    span.setAttributes({ 'db.table': 'users', 'user.id': id });
    try {
      const user = await db.users.findById(id);
      span.setStatus({ code: SpanStatusCode.OK });
      return user;
    } catch (e) {
      span.recordException(e);
      span.setStatus({ code: SpanStatusCode.ERROR, message: e.message });
      throw e;
    } finally {
      span.end();
    }
  });
}

Express/Fastify middleware

import { trace } from '@opentelemetry/api';

export function traceMiddleware(req, res, next) {
  const span = trace.getTracer('http').startSpan(`${req.method} ${req.route?.path || req.url}`);
  trace.getActiveContext().with(trace.setSpan(trace.getActiveContext(), span), () => {
    res.on('finish', () => {
      span.setAttributes({
        'http.status_code': res.statusCode,
        'http.method': req.method,
        'http.route': req.route?.path,
      });
      span.end();
    });
    next();
  });
}

Практические советы

  • Именование: verb noun (GET /users, db.users.insert).
  • Атрибуты: добавляйте user.id, order.id, region.
  • Ошибки: span.recordException(err) + span.setStatus({code: ERROR}).
  • Sampling: head-based (решение на старте) или tail-based (после завершения).
  • Антипаттерны: слишком мелкие spans, отсутствие span.end(), захардкоженные ID.

Сборка всего вместе

  1. Запустите SDK на старте приложения.
  2. Добавьте auto-instrumentations (http, express, pg, redis).
  3. Дополните ручными spans для критичных операций.
  4. Отправляйте traces в OneUptime → исследуйте flame-графы, ищите узкие места.