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.
ConstructorDirect link to Constructor
new OtelExporter(config: OtelExporterConfig)
OtelExporterConfigDirect 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:
signals?:
@opentelemetry/exporter-logs-otlp-* package to be installed for the chosen protocol.timeout?:
batchSize?:
logLevel?:
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?:
{ userId: "user.id" }. Disabled by default. Namespaced metadata is retained. See Metadata mapping below for value conversion and precedence.resourceAttributes?:
Metadata mappingDirect 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 configurationsDirect link to Provider configurations
Dash0ConfigDirect link to dash0config
interface Dash0Config {
apiKey?: string
endpoint?: string
dataset?: string
}
apiKey?:
endpoint?:
dataset?:
SignozConfigDirect link to signozconfig
interface SignozConfig {
apiKey?: string
region?: 'us' | 'eu' | 'in'
endpoint?: string
}
apiKey?:
region?:
endpoint?:
NewRelicConfigDirect link to newrelicconfig
interface NewRelicConfig {
apiKey?: string
endpoint?: string
}
apiKey?:
endpoint?:
TraceloopConfigDirect link to traceloopconfig
interface TraceloopConfig {
apiKey?: string
destinationId?: string
endpoint?: string
}
apiKey?:
destinationId?:
endpoint?:
LaminarConfigDirect link to laminarconfig
interface LaminarConfig {
apiKey?: string
endpoint?: string
}
apiKey?:
endpoint?:
CustomConfigDirect 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?:
endpoints?:
protocol?:
headers?:
Custom signal endpointsDirect 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.
MethodsDirect link to Methods
exportTracingEventDirect link to exporttracingevent
async exportTracingEvent(event: TracingEvent): Promise<void>
Exports a tracing event to the configured OTEL backend.
flushDirect 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.
shutdownDirect link to shutdown
async shutdown(): Promise<void>
Flushes pending traces and shuts down the exporter.
Usage examplesDirect 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 ConfigurationDirect 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 EndpointDirect 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 requirementsDirect 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.
| Protocol | Trace package | Log 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-zipkin | not supported |
Tags supportDirect 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.
UsageDirect link to Usage
const result = await agent.generate('Hello', {
tracingOptions: {
tags: ['production', 'experiment-v2', 'user-request'],
},
})
How Tags Are StoredDirect 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\"]"
}
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.
RelatedDirect link to Related
- OtelExporter Guide: Setup guide with provider configurations
- OtelBridge: For bidirectional OTEL context integration
- Tracing Overview: General tracing concepts