Skip to content

OpenTelemetry

Schift emits OpenTelemetry traces for retrieval and search operations. You can route these traces to any OTLP-compatible backend—such as Datadog, Honeycomb, Grafana Tempo, Jaeger, or LangSmith—by setting a few environment variables. When telemetry is not configured, the instrumentation is a no-op and adds zero overhead.

The Schift API initializes an OTLP trace exporter at boot when OTEL_EXPORTER_OTLP_ENDPOINT is set. The following traces are emitted today:

  • schift.bucket.search — the top-level bucket search span, created for every POST /v1/buckets/{bucket_id}/search request that runs the local retrieval pipeline.

Each schift.bucket.search span carries attributes that describe the request, the retrieval strategy, and the result quality.

Set these environment variables before starting the Schift API:

Terminal window
export OTEL_EXPORTER_OTLP_ENDPOINT="https://api.honeycomb.io"
export OTEL_EXPORTER_OTLP_HEADERS="x-honeycomb-team=<your-api-key>"

Restart the server. Spans are batched and exported asynchronously.

Note: OTEL_EXPORTER_OTLP_HEADERS contains credentials. Load it from a secret manager or your deployment platform’s secret storage, not from a checked-in .env file.

VariableRequiredDefaultDescription
OTEL_EXPORTER_OTLP_ENDPOINTYesThe OTLP endpoint URL.
OTEL_EXPORTER_OTLP_HEADERSUsuallyComma-separated key=value headers for authentication.
OTEL_EXPORTER_OTLP_PROTOCOLNoautogrpc or http/protobuf.
OTEL_SERVICE_NAMENoschift-apiThe service name attached to every span.
OTEL_TRACES_SAMPLERNoalways_onSampler name, e.g. parentbased_traceidratio.
OTEL_TRACES_SAMPLER_ARGNoSampler argument, e.g. 0.1 for 10%.

If OTEL_EXPORTER_OTLP_ENDPOINT is unset, init_telemetry() returns immediately and no exporter, tracer provider, or FastAPI instrumentation is installed.

Schift auto-detects the transport protocol so HTTPS endpoints work without extra configuration:

  • If OTEL_EXPORTER_OTLP_PROTOCOL is set to grpc or http/protobuf, that value is used.
  • Otherwise, HTTPS endpoints use http/protobuf.
  • Otherwise, plain HTTP or grpc:// endpoints use grpc.

Most managed vendors require http/protobuf. Set the variable explicitly only if the auto-detection does not match your collector.

The schift.bucket.search span includes these attributes:

AttributeTypeDescription
schift.bucket.idstringThe bucket ID from the request path.
schift.search.top_kintThe requested number of results.
schift.search.modestringThe search mode, such as vector or hybrid.
schift.search.rerankboolWhether reranking was requested.
schift.search.modelstringThe embedding model used for the query, if overridden.
schift.expand_neighbors.enabledboolWhether neighbor expansion was enabled.
schift.filter.keysstring[]Metadata filter keys that were applied.
schift.filter.opsstring[]Filter operators that were applied, such as eq or like.
schift.schiftql.plan_digeststringA digest of the executed SchiftQL plan, when applicable.
schift.search.methodstringThe actual retrieval method that ran.
schift.search.results.countintThe number of results returned.
schift.search.scores.topfloatThe score of the top result.
schift.search.scores.avgfloatThe average score across results.
schift.timing.total_msintTotal search latency in milliseconds.
schift.search_idstringThe internal search correlation ID.
schift.search.errorstringThe error type when the search fails.

These attributes let you build dashboards and alerts by bucket, search method, filter shape, result count, or latency without parsing unstructured logs.

When telemetry is enabled, Schift also calls FastAPIInstrumentor.instrument_app(app). This creates spans for incoming HTTP requests and propagates W3C trace context, so spans emitted by the Schift API are correlated with upstream callers.

To correlate a client request with a Schift trace, propagate the traceparent header:

from schift import Client
client = Client(
api_key="...",
headers={"traceparent": current_traceparent()},
)
import { WorkspaceClient } from "@schift-io/sdk";
const client = new WorkspaceClient({
apiKey: "...",
headers: { traceparent: currentTraceparent() },
});

By default, all traces are sampled. For high-volume deployments, reduce the sample rate:

Terminal window
export OTEL_TRACES_SAMPLER="parentbased_traceidratio"
export OTEL_TRACES_SAMPLER_ARG="0.1"

This samples 10% of traces while keeping parent-child correlation intact.

Terminal window
export OTEL_EXPORTER_OTLP_ENDPOINT="http://datadog-agent:4318"
export OTEL_EXPORTER_OTLP_PROTOCOL="http/protobuf"
Terminal window
export OTEL_EXPORTER_OTLP_ENDPOINT="https://api.honeycomb.io"
export OTEL_EXPORTER_OTLP_HEADERS="x-honeycomb-team=<your-api-key>,x-honeycomb-dataset=schift"
Terminal window
export OTEL_EXPORTER_OTLP_ENDPOINT="http://tempo:4318"
export OTEL_EXPORTER_OTLP_PROTOCOL="http/protobuf"

For LangSmith-specific instructions, see LangSmith integration.

  • No spans appear. Verify that OTEL_EXPORTER_OTLP_ENDPOINT is set before the app starts and that the endpoint is reachable from the server. init_telemetry() is called once during app creation; runtime changes after boot are not picked up.
  • Spans rejected by the collector. Check that OTEL_EXPORTER_OTLP_PROTOCOL matches the collector’s receiver. Most HTTPS vendors require http/protobuf.
  • High cardinality. Avoid unbounded values in schift.filter.keys. Some vendors charge by unique attribute combinations.