Skip to main content

OtelExporter

Sends traces and logs to any OpenTelemetry-compatible observability platform. Traces use the standardized GenAI semantic conventions. Logs carry their original severity and message body and are correlated to traces via both the OTEL log record's native trace context and mastra.traceId / mastra.spanId attributes.

Constructor
Direct link to Constructor

new OtelExporter(config: OtelExporterConfig)

OtelExporterConfig
Direct link to otelexporterconfig

interface OtelExporterConfig {
provider?: ProviderConfig
signals?: {
traces?: boolean
logs?: boolean
}
timeout?: number
batchSize?: number
logLevel?: 'debug' | 'info' | 'warn' | 'error'
metadataMapping?: Record<string, string>
}

provider:

ProviderConfig
Provider-specific configuration (see below)

signals?:

{ traces?: boolean; logs?: boolean }
Per-signal toggle. Both default to true. Set a value to false to disable that signal. Log export also requires the matching @opentelemetry/exporter-logs-otlp-* package to be installed for the chosen protocol.

timeout?:

number
Export timeout in milliseconds (default: 30000)

batchSize?:

number
Number of spans/logs per batch (default: 512)

logLevel?:

'debug' | 'info' | 'warn' | 'error'
Level for the logger the exporter creates when no logger is passed (default: 'info'). It has no effect on a supplied logger. When the exporter is registered with Mastra, Mastra replaces this logger with the application logger. Setting debug also writes exporter diagnostics directly to the console and installs a global OpenTelemetry diagnostic logger, whichever logger is in use.

metadataMapping?:

Record<string, string>
Maps metadata keys to top-level span attribute names, for example { userId: "user.id" }. Disabled by default. Namespaced metadata is retained. See Metadata mapping below for value conversion and precedence.

resourceAttributes?:

DetectedResourceAttributes
Optional OpenTelemetry Resource Attributes (values here override any defaults)

Metadata mapping
Direct link to Metadata mapping

Use metadataMapping when your backend expects metadata such as userId under a specific span attribute name:

import { OtelExporter } from '@mastra/otel-exporter'

const exporter = new OtelExporter({
provider: {
custom: { endpoints: { traces: 'https://collector.example.com/v1/traces' } },
},
metadataMapping: { userId: 'user.id', tenantId: 'tenant.id' },
})

A span with metadata { userId: 'user-123', tenantId: 'acme' } exports both user.id and tenant.id, alongside the existing mastra.metadata.userId and mastra.metadata.tenantId attributes. Mappings apply to every span that contains the selected metadata keys, including child spans.

Mapped values are strings. Objects and arrays are JSON-encoded. Missing keys, null, undefined, and empty strings are omitted. 0 and false become "0" and "false". Keys are matched literally, so a key containing a dot doesn't select a nested property. Empty destination names are ignored.

Explicit mappings overwrite existing attributes with the same destination name. If multiple metadata keys map to the same name, the last mapping with a non-empty value wins. Mapping doesn't affect log records or resource attributes.

Provider configurations
Direct link to Provider configurations

Dash0Config
Direct link to dash0config

interface Dash0Config {
apiKey?: string
endpoint?: string
dataset?: string
}

apiKey?:

string
Dash0 API key. Falls back to DASH0_API_KEY env var.

endpoint?:

string
Dash0 ingress endpoint. Falls back to DASH0_ENDPOINT env var.

dataset?:

string
Dataset name. Falls back to DASH0_DATASET env var.

SignozConfig
Direct link to signozconfig

interface SignozConfig {
apiKey?: string
region?: 'us' | 'eu' | 'in'
endpoint?: string
}

apiKey?:

string
SigNoz ingestion key. Falls back to SIGNOZ_API_KEY env var.

region?:

'us' | 'eu' | 'in'
SigNoz cloud region. Falls back to SIGNOZ_REGION env var. (default: 'us')

endpoint?:

string
Custom endpoint. Falls back to SIGNOZ_ENDPOINT env var.

NewRelicConfig
Direct link to newrelicconfig

interface NewRelicConfig {
apiKey?: string
endpoint?: string
}

apiKey?:

string
New Relic license key. Falls back to NEW_RELIC_LICENSE_KEY env var.

endpoint?:

string
Custom endpoint. Falls back to NEW_RELIC_ENDPOINT env var. (default: https://otlp.nr-data.net:443/v1/traces)

TraceloopConfig
Direct link to traceloopconfig

interface TraceloopConfig {
apiKey?: string
destinationId?: string
endpoint?: string
}

apiKey?:

string
Traceloop API key. Falls back to TRACELOOP_API_KEY env var.

destinationId?:

string
Destination identifier. Falls back to TRACELOOP_DESTINATION_ID env var.

endpoint?:

string
Custom endpoint. Falls back to TRACELOOP_ENDPOINT env var. (default: https://api.traceloop.com/v1/traces)

LaminarConfig
Direct link to laminarconfig

interface LaminarConfig {
apiKey?: string
endpoint?: string
}

apiKey?:

string
Laminar project API key. Falls back to LMNR_PROJECT_API_KEY env var.

endpoint?:

string
Custom endpoint. Falls back to LAMINAR_ENDPOINT env var. (default: https://api.lmnr.ai/v1/traces)

CustomConfig
Direct link to customconfig

type CustomConfig = {
protocol?: 'http/json' | 'http/protobuf' | 'grpc' | 'zipkin'
headers?: Record<string, string>
} & (
| { endpoint?: string; endpoints?: never }
| {
endpoint?: never
endpoints:
| { traces: string | undefined; logs?: string | undefined }
| { traces?: string | undefined; logs: string | undefined }
}
)

endpoint?:

string
Legacy collector endpoint. Cannot be combined with endpoints. Uses the existing signal-path derivation for OTLP.

endpoints?:

{ traces: string | undefined; logs?: string | undefined } | { traces?: string | undefined; logs: string | undefined }
Exact per-signal URLs. Supply at least one endpoint. Omitted signals are not exported. Cannot be combined with endpoint.

protocol?:

'http/json' | 'http/protobuf' | 'grpc' | 'zipkin'
Export protocol (default: 'http/json')

headers?:

Record<string, string>
Custom headers for authentication

Custom signal endpoints
Direct link to Custom signal endpoints

Supply either endpoint or endpoints, never both. TypeScript rejects configurations with both options. Runtime validation also rejects them and disables the exporter with an error message.

Use endpoints to supply the exact URL for each signal. URLs are passed to the selected exporter without adding, removing, or replacing path segments. Trace and log endpoints can use different hosts and unrelated paths:

import { OtelExporter } from '@mastra/otel-exporter'

const exporter = new OtelExporter({
provider: {
custom: {
protocol: 'http/protobuf',
endpoints: {
traces: 'https://traces.example.com/api/ingest/?tenant=acme',
logs: 'https://logs.example.com/events',
},
},
},
})

Supply at least one signal endpoint. Omit logs for trace-only export or traces for log-only export. The signals toggles can still disable a configured signal. Protocol and headers apply to both endpoints. The selected protocol must support the signal. A custom exporter instance overrides trace transport as before.

You can pass environment-variable values explicitly:

const exporter = new OtelExporter({
provider: {
custom: {
endpoints: {
traces: process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT,
logs: process.env.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT,
},
},
},
})

The type requires at least one signal key and allows undefined values for environment-variable configuration. At runtime, at least one URL must be non-empty. A missing value disables that signal. Mastra doesn't load endpoint environment variables automatically.

The singular endpoint option retains its existing behavior for compatibility: OTLP signal paths are derived from the collector URL, and Zipkin uses the configured URL directly. For custom paths, use endpoints instead.

Methods
Direct link to Methods

exportTracingEvent
Direct link to exporttracingevent

async exportTracingEvent(event: TracingEvent): Promise<void>

Exports a tracing event to the configured OTEL backend.

flush
Direct link to flush

async flush(): Promise<void>

Force flushes any buffered spans to the OTEL backend without shutting down the exporter. Useful in serverless environments where you need to ensure spans are exported before the runtime terminates.

shutdown
Direct link to shutdown

async shutdown(): Promise<void>

Flushes pending traces and shuts down the exporter.

Usage examples
Direct link to Usage examples

Zero-Config (using environment variables)
Direct link to Zero-Config (using environment variables)

import { OtelExporter } from '@mastra/otel-exporter'

// Set SIGNOZ_API_KEY, SIGNOZ_REGION environment variables
const exporter = new OtelExporter({ provider: { signoz: {} } })

// Or for other providers:
// Set DASH0_API_KEY, DASH0_ENDPOINT for Dash0
// Set NEW_RELIC_LICENSE_KEY for New Relic
// Set TRACELOOP_API_KEY for Traceloop
// Set LMNR_PROJECT_API_KEY for Laminar

Explicit Configuration
Direct link to Explicit Configuration

import { OtelExporter } from '@mastra/otel-exporter'

const exporter = new OtelExporter({
provider: {
signoz: {
apiKey: process.env.SIGNOZ_API_KEY,
region: 'us',
},
},
})

With Custom Endpoint
Direct link to With Custom Endpoint

const exporter = new OtelExporter({
provider: {
custom: {
endpoint: 'https://my-collector.example.com/v1/traces',
protocol: 'http/protobuf',
headers: {
'x-api-key': process.env.API_KEY,
},
},
},
timeout: 60000,
logLevel: 'debug',
})

Protocol requirements
Direct link to Protocol requirements

Different providers require different OTEL exporter packages. Trace and log export are independent: install only the trace package if you only need traces, or install both the trace and log packages for the chosen protocol if you also want log export. Zipkin doesn't support OTLP logs.

ProtocolTrace packageLog package
gRPC@opentelemetry/exporter-trace-otlp-grpc (+ @grpc/grpc-js)@opentelemetry/exporter-logs-otlp-grpc (+ @grpc/grpc-js)
HTTP/Protobuf@opentelemetry/exporter-trace-otlp-proto@opentelemetry/exporter-logs-otlp-proto
HTTP/JSON@opentelemetry/exporter-trace-otlp-http@opentelemetry/exporter-logs-otlp-http
Zipkin@opentelemetry/exporter-zipkinnot supported

Tags support
Direct link to Tags support

The OtelExporter supports trace tagging for categorization and filtering. Tags are only applied to root spans and are stored as the mastra.tags attribute.

Usage
Direct link to Usage

const result = await agent.generate('Hello', {
tracingOptions: {
tags: ['production', 'experiment-v2', 'user-request'],
},
})

How Tags Are Stored
Direct link to How Tags Are Stored

Tags are stored as a JSON-stringified array in the mastra.tags span attribute for maximum backend compatibility:

{
"mastra.tags": "[\"production\",\"experiment-v2\",\"user-request\"]"
}
note

While the OpenTelemetry specification supports native array attributes, many backends (Jaeger, Zipkin, Tempo) have limited array support. JSON strings ensure consistent behavior across all observability platforms.