Exporters and CLI Bootstrap

js/opentelemetry/exporters.md

Exporters receive batches of spans, logs, and metrics from processors and readers and deliver them to an external collector. Fino ships one production exporter: OTLPHttpJsonExporter. InMemoryExporter is available for testing.

Both are imported from fino:opentelemetry/sdk.

OTLPHttpJsonExporter

OTLPHttpJsonExporter serializes records to OTLP/HTTP JSON and POSTs them to a collector. The wire format is JSON; there is no gRPC or protobuf option.

import { OTLPHttpJsonExporter, OtelSDK, BatchSpanProcessor } from 'fino:opentelemetry/sdk';

const exporter = new OTLPHttpJsonExporter({
  endpoint: 'http://127.0.0.1:4318',
});

const sdk = new OtelSDK({
  exporters: [exporter],
  spanProcessors: [new BatchSpanProcessor(exporter)],
}).start();

Endpoint routing

The exporter appends signal-specific paths to the base endpoint:

If the base endpoint already ends with /v1/<signal> or contains /v1/, it is used as-is for that signal.

You can also configure signal-specific endpoints independently:

const exporter = new OTLPHttpJsonExporter({
  endpoint: 'http://collector:4318',
  endpoints: {
    traces: 'http://jaeger:4318/v1/traces',
    logs: 'http://loki:3100/v1/logs',
    // metrics: falls back to endpoint/v1/metrics
  },
});

Headers and compression

const exporter = new OTLPHttpJsonExporter({
  endpoint: 'https://api.example.com/otel',
  headers: {
    'x-api-key': 'my-token',
    Authorization: 'Bearer eyJ...',
  },
  compression: 'gzip',   // 'gzip', 'deflate', or 'br'
});

Retry behavior

By default the exporter makes one attempt with no backoff. Configure retry to add resilience:

const exporter = new OTLPHttpJsonExporter({
  endpoint: 'http://127.0.0.1:4318',
  retry: {
    maxAttempts: 3,
    initialBackoffMillis: 500,  // doubles with each failed attempt
  },
});

The retry rules:

Observability hooks

The exporter supports hooks for logging or instrumentation of the export path itself:

const exporter = new OTLPHttpJsonExporter({
  endpoint: 'http://127.0.0.1:4318',
  onError(error) {
    console.error('[otel] export failed:', error.message);
  },
  onPartialSuccess(result) {
    console.warn('[otel] partial success:', result.rejectedSpans, 'spans rejected');
  },
  onRequest(info) {
    console.log('[otel] exporting', info.signal, 'to', info.url);
  },
  onResponse(info) {
    console.log('[otel] export response', info.status, 'for', info.signal);
  },
});

No recursive instrumentation

Export requests run outside active telemetry provider contexts. This means HTTP spans produced by FetchInstrumentation do not include the export request itself. The exporter deliberately suppresses provider context around each fetch call to break the feedback loop.

Timeout

const exporter = new OTLPHttpJsonExporter({
  endpoint: 'http://127.0.0.1:4318',
  timeoutMillis: 5000,  // 5 seconds per attempt
});

InMemoryExporter for testing

InMemoryExporter accumulates records in memory:

import { BatchSpanProcessor, InMemoryExporter, OtelSDK } from 'fino:opentelemetry/sdk';

const memory = new InMemoryExporter();

const sdk = new OtelSDK({
  exporters: [memory],
  spanProcessors: [new BatchSpanProcessor(memory)],
}).start();

// run your code...
await sdk.flush();

const spans = memory.getFinishedSpans();   // SpanRecord[]
const logs = memory.getFinishedLogs();     // LogRecord[]
const metrics = memory.getFinishedMetrics(); // MetricRecord[]

CLI bootstrap

The simplest way to enable OTLP export for a script is the --otlp-endpoint flag:

fino --otlp-endpoint http://127.0.0.1:4318 app.ts

This installs tracer, logger, and meter providers, starts a full instrumentation set (HTTP server, fetch, DNS, socket, TLS, trace topics), and registers a shutdown hook that flushes and stops the SDK when the script exits.

The equivalent using the OTEL_EXPORTER_OTLP_ENDPOINT environment variable:

OTEL_EXPORTER_OTLP_ENDPOINT=http://127.0.0.1:4318 fino app.ts

Supported environment variables

Variable Effect
OTEL_EXPORTER_OTLP_ENDPOINT Base OTLP endpoint
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT Signal-specific override for traces
OTEL_EXPORTER_OTLP_LOGS_ENDPOINT Signal-specific override for logs
OTEL_EXPORTER_OTLP_METRICS_ENDPOINT Signal-specific override for metrics
OTEL_EXPORTER_OTLP_HEADERS Comma-separated key=value pairs added to all requests
OTEL_EXPORTER_OTLP_COMPRESSION none or gzip
OTEL_SERVICE_NAME Overrides the service.name resource attribute
OTEL_RESOURCE_ATTRIBUTES Comma-separated key=value resource attributes
FINO_OTEL_EXPORT_INTERVAL_MS Export interval in milliseconds (default: 1000)

Resource detection

The bootstrap loader constructs the service resource by:

  1. Reading service.name and service.version from package.json in the working directory
  2. Applying OTEL_RESOURCE_ATTRIBUTES key-value pairs on top
  3. Applying OTEL_SERVICE_NAME as service.name if set
  4. Falling back to the script filename (without extension) as service.name if nothing else is found