Source Map Resolution для OpenTelemetry Traces: как smapped-traces решает проблему

#opentelemetry#sourcemaps#debugging#nextjs#javascript

TL;DR

smapped-traces — это open-source решение для резолва source maps в OpenTelemetry трассировках, использующее debug ID вместо хрупких path-based подходов. Поддерживает Next.js 15+, работает с Turbopack и webpack, предлагает multiple storage backends (SQLite, S3, HTTP).

Введение: проблема source maps в production

Когда мы переходим с Sentry на OpenTelemetry, одна из ключевых проблем — потеря читаемости stack traces в production. Вместо понятных:

Error: Cannot read properties of undefined (reading 'id')
  at DashboardComponent (./src/components/Dashboard.tsx:42:15)

Мы видим минифицированный ад:

Error: Cannot read properties of undefined (reading 'id')
  at t (/_next/static/chunks/pages/dashboard-abc123.js:1:23847)

Традиционные подходы к резолву source maps через path matching хрупки и требуют:

smapped-traces предлагает альтернативу через стандартизированные debug ID.

Как работают debug ID

Концепция основана на TC39 proposal и реализована в Turbopack нативно, для webpack требуется plugin.

Build-time:

  1. Каждому бандлу присваивается UUID (debug ID)
  2. Этот же ID записывается в соответствующий .map файл
  3. Создается mapping вида { "file.js": "debug-id" }

Runtime:

  1. При ошибке в коде мы получаем stack trace с путями файлов
  2. Специальный exporter ищет debug ID для каждого файла
  3. По ID загружается соответствующий source map

Архитектура smapped-traces

Решение состоит из трех ключевых компонентов:

1. Build Plugin (Next.js)

// next.config.js
const { withSmappedTraces } = require('@smapped-traces/nextjs');

module.exports = withSmappedTraces({
  // Конфигурация хранилища
  storage: {
    type: 's3',
    bucket: 'my-sourcemaps-bucket'
  },
  // Дополнительные опции
  purgeSourceMaps: true // удаляем .map файлы из output
});

Плагин выполняет:

2. SourceMappedSpanExporter

import { SourceMappedSpanExporter } from 'smapped-traces';
import { ConsoleSpanExporter } from '@opentelemetry/sdk-trace-base';

const exporter = new SourceMappedSpanExporter({
  // Делегируем экспорт основному экспортеру
  delegate: new ConsoleSpanExporter(),
  // Конфигурация хранилища
  storage: {
    type: 'http',
    endpoint: 'https://sourcemaps.internal'
  }
});

Экспортер:

3. Traces Handler (для OTLP)

import { createTracesHandler } from 'smapped-traces';

export default createTracesHandler({
  storage: { type: 'sqlite', path: '/data/sourcemaps.db' },
  collector: {
    url: 'https://otel-collector.internal'
  }
});

Обработчик:

Практическое применение

Интеграция с OpenTelemetry SDK

import { NodeSDK } from '@opentelemetry/sdk-node';
import { SourceMappedSpanExporter } from 'smapped-traces';
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';

const sdk = new NodeSDK({
  traceExporter: new SourceMappedSpanExporter({
    delegate: new OTLPTraceExporter(),
    storage: { type: 's3' }
  }),
  instrumentations: [getNodeAutoInstrumentations()]
});

sdk.start();

Поддерживаемые storage backends

  1. SQLite (для standalone приложений):
{
  type: 'sqlite',
  path: '/path/to/database.db'
}
  1. S3-совместимые (AWS, Cloudflare R2, Google Cloud Storage):
{
  type: 's3',
  bucket: 'my-sourcemaps',
  region: 'us-east-1'
}
  1. HTTP endpoint (для собственных решений):
{
  type: 'http',
  endpoint: 'https://sourcemaps-api.internal'
}

Кастомизация через интерфейсы

Вы можете реализовать собственное хранилище:

interface SourceMapStore {
  getSourceMap(debugId: string): Promise<RawSourceMap | null>;
  getDebugId(sourceUrl: string): Promise<string | null>;
}

class MyCustomStore implements SourceMapStore {
  // ... ваша реализация
}

Ограничения и roadmap

Планируемые фичи:

Заключение

smapped-traces предлагает production-ready решение для source map резолва в OpenTelemetry, устраняя главные боли:

Для проектов на Next.js с Turbopack/webpack — это наиболее надежный способ получить читаемые stack traces в production без головной боли с path mapping.

Ресурсы:


Источник: https://github.com/jrandolf/smapped-traces