Crux
API ReferencePlugins

@crux/otel

OpenTelemetry integration — spans for every instrumented Crux event.

import { withTelemetry } from '@crux/otel'
import { createUrlExporter, createCallbackExporter } from '@crux/otel'

For setup and usage, see the Production Telemetry guide.

withTelemetry(options?)

Creates a CruxPlugin that instruments all Crux operations with OpenTelemetry spans or lightweight structured traces.

config({
  plugins: [withTelemetry({ serviceName: 'my-app' })],
})

Options:

FieldTypeDefaultDescription
serviceNamestring'@crux/otel'Tracer name for span identification
recordContentbooleanfalseInclude prompt/input content as span attributes
attributesRecord<string, string>Custom attributes added to every span
exporterUrlExporter | CallbackExporterExport strategy. Omit for standard OTel TracerProvider path

Returns: CruxPlugin with name 'crux:otel'.

Crux emits embedding spans as crux.embedding with attributes for embedding name, kind, input count, chunk count, dense dimensions, token usage, and cost when available.

Corpus sync emits crux.corpus.sync spans and per-source crux.corpus.source spans. Namespace and source identifiers are exported as hashes so file paths, URLs, and tenant namespaces do not become raw telemetry attributes.

Ingest parsers emit crux.ingest.parse spans. These include parser name, source format, byte length, part count, warning count, and hashed namespace/source identifiers. Parser errors mark the span as ERROR.

Retrieval pipelines emit one crux.retrieval.stage span per stage. Stage spans include pipeline id, stage name, stage kind, stage phase, input/output query counts, input/output hit counts, warning count, and status. They intentionally do not include raw query text, hit content, metadata, filters, or embeddings.

UrlExporter

Configuration for HTTP POST export (ephemeral runtimes).

FieldTypeDescription
urlstringEndpoint to POST span batches to
headersRecord<string, string>?Optional headers (API keys, auth)

CallbackExporter

type CallbackExporter = (spans: ReadonlyArray<TraceSpan>) => void | Promise<void>

A function that receives completed span batches. Use for custom handling (Convex actions, PostHog, etc.).

TraceSpan

Structured span data used by the lightweight exporter path.

FieldTypeDescription
spanIdstringUnique span identifier
parentSpanIdstring?Parent span ID for nesting
traceIdstringTrace ID grouping related spans
namestringSpan name (e.g., 'crux.generate')
startTimenumberStart time (Unix ms)
endTimenumberEnd time (Unix ms)
durationMsnumberDuration in milliseconds
attributesRecord<string, string | number | boolean>Key-value attributes
statusSpanStatus{ code: 'OK' | 'ERROR' | 'UNSET', message?: string }
eventsArray<{ name, time, attributes? }>?Point-in-time events (e.g., exceptions)

createUrlExporter(options)

Creates a SpanExporter that POSTs span batches to a URL. Fire-and-forget with 5-second timeout — failures are silently ignored.

const exporter = createUrlExporter({
  url: 'https://collector.example.com/v1/traces',
  headers: { 'X-Api-Key': 'key' },
})

createCallbackExporter(callback)

Creates a SpanExporter that delivers span batches to a callback function.

const exporter = createCallbackExporter((spans) => {
  for (const span of spans) {
    console.log(`${span.name}: ${span.durationMs}ms`)
  }
})

SpanExporter

Interface for span export backends.

MethodDescription
export(spans)Export a batch of completed spans
shutdown()Flush pending spans and shut down

Span attribute constants

GenAI semantic convention constants are exported from @crux/otel:

ConstantValueConvention
GEN_AI_SYSTEMgen_ai.systemOTel GenAI
GEN_AI_REQUEST_MODELgen_ai.request.modelOTel GenAI
GEN_AI_RESPONSE_MODELgen_ai.response.modelOTel GenAI
GEN_AI_USAGE_INPUT_TOKENSgen_ai.usage.input_tokensOTel GenAI
GEN_AI_USAGE_OUTPUT_TOKENSgen_ai.usage.output_tokensOTel GenAI
CRUX_PROMPT_IDcrux.prompt.idCrux
CRUX_COSTcrux.costCrux
CRUX_EMBEDDING_NAMEcrux.embedding.nameCrux
CRUX_EMBEDDING_KINDcrux.embedding.kindCrux
CRUX_EMBEDDING_OPERATIONcrux.embedding.operationCrux
CRUX_CORPUS_IDcrux.corpus.idCrux
CRUX_CORPUS_NAMESPACE_HASHcrux.corpus.namespace_hashCrux
CRUX_CORPUS_SOURCE_ID_HASHcrux.corpus.source_id_hashCrux
CRUX_CORPUS_ACTIONcrux.corpus.actionCrux
CRUX_INGEST_PARSERcrux.ingest.parserCrux
CRUX_INGEST_FORMATcrux.ingest.formatCrux
CRUX_INGEST_SOURCE_ID_HASHcrux.ingest.source_id_hashCrux
CRUX_INGEST_PART_COUNTcrux.ingest.part_countCrux
CRUX_RETRIEVAL_PIPELINE_IDcrux.retrieval.pipeline.idCrux
CRUX_RETRIEVAL_STAGE_NAMEcrux.retrieval.stage.nameCrux
CRUX_RETRIEVAL_STAGE_KINDcrux.retrieval.stage.kindCrux
CRUX_RETRIEVAL_STAGE_PHASEcrux.retrieval.stage.phaseCrux
CRUX_RETRIEVAL_STAGE_INPUT_QUERY_COUNTcrux.retrieval.stage.input_query_countCrux
CRUX_RETRIEVAL_STAGE_OUTPUT_QUERY_COUNTcrux.retrieval.stage.output_query_countCrux
CRUX_RETRIEVAL_STAGE_INPUT_HIT_COUNTcrux.retrieval.stage.input_hit_countCrux
CRUX_RETRIEVAL_STAGE_OUTPUT_HIT_COUNTcrux.retrieval.stage.output_hit_countCrux
CRUX_RETRIEVAL_STAGE_WARNING_COUNTcrux.retrieval.stage.warning_countCrux
CRUX_TOOL_NAMEcrux.tool.nameCrux
CRUX_FLOW_IDcrux.flow.idCrux
CRUX_FLOW_NAMEcrux.flow.nameCrux
CRUX_FLOW_PARENT_IDcrux.flow.parent_idCrux
CRUX_COMPOSITION_KINDcrux.composition.kindCrux

On this page