OpenTelemetry

Emit Vision accepts OpenTelemetry data over OTLP/HTTP JSON. If your Node.js app already uses OpenTelemetry instrumentation, you can point your exporters at Emit Vision without replacing your existing setup.

New to Emit Vision? Use the native @emit-vision/sdk-node instead. OpenTelemetry is the compatibility path for teams that already have OTel wired up and don't want to change their instrumentation layer.

What's supported

SupportedNot supported
OTLP/HTTP JSON logs (POST /otlp/v1/logs)OTLP Protobuf
OTLP/HTTP JSON traces (POST /otlp/v1/traces)OTLP/gRPC
Bearer-token auth with your Emit Vision API keyMetrics ingestion
Gantt / waterfall trace visualization

Setup

Install the OpenTelemetry Node SDK and exporters:

npm install @opentelemetry/sdk-node \
  @opentelemetry/exporter-logs-otlp-http \
  @opentelemetry/exporter-trace-otlp-http \
  @opentelemetry/sdk-logs \
  @opentelemetry/sdk-trace-base \
  @opentelemetry/auto-instrumentations-node

Configure and start the SDK:

import { NodeSDK } from "@opentelemetry/sdk-node";
import { OTLPLogExporter } from "@opentelemetry/exporter-logs-otlp-http";
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
import { SimpleLogRecordProcessor } from "@opentelemetry/sdk-logs";
import { SimpleSpanProcessor } from "@opentelemetry/sdk-trace-base";
import { getNodeAutoInstrumentations } from "@opentelemetry/auto-instrumentations-node";
 
const apiKey =
  process.env.EMIT_VISION_API_KEY ??
  "evk_local_development_seed_key_000000000000";
const endpoint = process.env.EMIT_VISION_ENDPOINT ?? "http://localhost:4301";
const baseUrl = `${endpoint}/otlp/v1`;
const headers = { Authorization: `Bearer ${apiKey}` };
 
const sdk = new NodeSDK({
  logRecordProcessors: [
    new SimpleLogRecordProcessor(
      new OTLPLogExporter({ url: `${baseUrl}/logs`, headers }),
    ),
  ],
  spanProcessors: [
    new SimpleSpanProcessor(
      new OTLPTraceExporter({ url: `${baseUrl}/traces`, headers }),
    ),
  ],
  instrumentations: [getNodeAutoInstrumentations()],
});
 
sdk.start();

Drop the trace exporter if you only want logs, or the log exporter if you only want traces.

Field mapping

OTLP data is normalized into Emit Vision's event envelope. Some fidelity is lost intentionally so traces and logs reuse the same dashboard model.

OTLP fieldEmit Vision fieldNotes
resource.attributes["service.name"]tags.serviceStored on all logs and spans
resource.attributes["service.version"]releaseFilters by deployed version
Log bodyname or properties.bodyString body → event name; structured → properties
Log severityTexttags.severityUsed for visual emphasis
Log/span traceIdcontext.traceIdPreserved for correlation
Log/span spanIdcontext.spanIdPreserved
Span namenameBecomes the dashboard event name
Span startTimeUnixNanotimestampSpan start time → event timestamp
Span durationproperties.durationMsCalculated from start/end
Span attributespropertiesSensitive keys are scrubbed
Span status.code === ERRORtype: "error"Error spans appear in the errors flow
Span status.messageerror.messageUsed as error message when present

Limitations

  • Parent-child span relationships are stored as metadata but Emit Vision does not render a waterfall or Gantt chart. Spans appear as flat events.
  • Metrics are not ingested.
  • Sampling stays within your OTel SDK — Emit Vision does not add a separate sampling layer.
  • Browser OTLP is not supported; use @emit-vision/sdk-js for browser apps.

When to use OpenTelemetry vs the native SDK

Use caseRecommendation
New project, minimal setupNative @emit-vision/sdk-node
Existing OTel instrumentation you don't want to replaceOpenTelemetry path
Need session timelines, custom events, or feature flagsNative SDK
Want to keep your existing exporter wiringOpenTelemetry path

For new projects, start with the native SDK — it's fewer packages and the integration is more direct. Point OTel at Emit Vision only when you already have OTel and don't want to maintain two instrumentation stacks.