Skip to content

Conversation

@jonastemplestein
Copy link

Summary

Exposes the internal OTLP exporter as OtlpExporter.make() to allow building custom multi-endpoint tracers or composing multiple exporters without copying internal code.

Motivation

When building applications that need to send traces to multiple backends (e.g., Honeycomb + Axiom + Sentry), the current options are:

  1. Use NodeSdk with multiple SpanProcessors (requires @opentelemetry/sdk-trace-base)
  2. Copy the internal exporter code

Exposing the exporter allows Effect-native multi-endpoint tracing without any OpenTelemetry SDK dependencies:

import { OtlpExporter, OtlpResource } from "@effect/opentelemetry"

const makeMultiExporter = (endpoints: Array<EndpointConfig>) =>
  Effect.gen(function*() {
    const exporters = yield* Effect.forEach(endpoints, e =>
      OtlpExporter.make({
        url: e.url,
        headers: e.headers,
        label: e.name,
        exportInterval: "5 seconds",
        maxBatchSize: 100,
        body: (spans) => ({ resourceSpans: [{ scopeSpans: [{ spans }] }] }),
        shutdownTimeout: "3 seconds"
      })
    )
    return {
      push: (data: unknown) => exporters.forEach(e => e.push(data))
    }
  })

Changes

  • Added src/OtlpExporter.ts - public module re-exporting the internal exporter with proper JSDoc documentation
  • Updated src/index.ts - added OtlpExporter export
  • Added changeset

Test Plan

  • Existing tests pass (no behavioral changes)
  • Would be good to add a test for the new public API

🤖 Generated with Claude Code

Exposes the internal OTLP exporter as OtlpExporter.make() to allow building
custom multi-endpoint tracers or composing multiple exporters.
@changeset-bot
Copy link

changeset-bot bot commented Dec 3, 2025

🦋 Changeset detected

Latest commit: 41e5623

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@effect/opentelemetry Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

*/
export const make: (
options: MakeOptions
) => Effect.Effect<Exporter, never, HttpClient.HttpClient | Scope.Scope> = internal.make as any
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we need as any?

import type * as Duration from "effect/Duration"
import type * as Effect from "effect/Effect"
import type * as Scope from "effect/Scope"
import * as internal from "./internal/otlpExporter.js"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would just move the entire module up instead of re-exporting internal

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Discussion Ongoing

Development

Successfully merging this pull request may close these issues.

3 participants