# otel _(no submodules)_ ## Purpose `OtelExporter` converts `ProfileEntry` records to OTLP-compatible spans and optionally exports them to a collector endpoint over HTTP. Both the `fetch` implementation and the random-bytes producer used for trace/span IDs are injectable so test runs can assert exact payloads and produce deterministic IDs. ## Features - Zod-validated config (`endpoint` MUST be a valid URL when supplied; `serviceName` non-empty; `batchSize > 0`). - Defaults: `http://localhost:4318/v1/traces`, `serviceName: "profiler"`, `batchSize: 100`. - `entryToSpan(entry, traceId?, parentSpanId?)` — single conversion; defaults trace/span IDs from the injected RNG. - `convertEntries(entries, traceId?)` — batch conversion under a single shared trace. - `addToBatch(spans)` / `flush()` / `getPendingCount()` — pending-batch buffer. - `export(spans)` — POSTs an OTLP JSON payload; returns `true` on `response.ok`, `false` on non-ok responses or thrown errors (no exceptions escape). - `exportBatch()` — drains pending in `batchSize` slices, returning the count of successfully exported spans. - Attribute encoding: strings → `stringValue`, integers → `intValue`, non-integer numbers → `doubleValue`, booleans → `boolValue`. ## Usage: Basic ```ts import { OtelExporter } from "@suaveplan/profiler"; const exporter = new OtelExporter({ serviceName: "my-app" }); const spans = exporter.convertEntries(profiler.getEntries()); await exporter.export(spans); ``` ## Usage: Advanced ```ts import { OtelExporter } from "@suaveplan/profiler"; const seed = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]); let cursor = 0; const exporter = new OtelExporter({ endpoint: "https://collector.internal/v1/traces", headers: { "x-api-key": process.env.OTLP_KEY ?? "" }, batchSize: 25, randomBytes: (n) => seed.slice(cursor, (cursor += n)), fetchFn: async (url, init) => { const res = await myInstrumentedFetch(url, init); return { ok: res.ok }; }, }); exporter.addToBatch(exporter.convertEntries(profiler.getEntries())); const ok = await exporter.exportBatch(); ``` ## API Reference - `class OtelExporter` — `new OtelExporter(config?: OtelExporterConfig)`. - `interface OtelExporterConfig { endpoint?, serviceName?, batchSize?, headers?, fetchFn?, randomBytes? }`. - Methods: `generateTraceId`, `generateSpanId`, `entryToSpan`, `convertEntries`, `addToBatch`, `getPendingCount`, `flush`, `export`, `exportBatch`. - Errors: `ProfilerError` with `PROFILER.INVALID_OPTIONS` on bad config. ## Implementation Notes The payload structure follows the OTLP/HTTP JSON encoding: a single `resourceSpans` entry with one `scopeSpans` group, and per-span attributes encoded as `{ key, value }` records where `value` is a discriminated union by primitive type. `metadata` fields that are objects/arrays are silently dropped — OTLP only allows primitive attribute values, and reshaping nested metadata is the caller's responsibility. Export failures are swallowed and surfaced via the boolean return so exporters can be used inside fire-and-forget profiling loops without crashing the host.