kdrantTracing
Tracing for Kdrant, on the seam every engine goes through.
kdrant-micrometer answers how slow Qdrant is. It cannot answer where a slow request sat, and in a RAG service that is usually the question worth asking: the call took 900 ms and the search was 40 of them, or the search was 700 and the embedding was 40. A metric cannot separate those. A span can, because it nests inside the caller's.
val qdrant = Kdrant(
host = "localhost",
decorateTransport = kdrantTracing(openTelemetry, serverAddress = "localhost", serverPort = 6333),
)The same call works on KdrantGrpc, and produces the same spans: the decorator sits above the wire, so one implementation covers both engines and a third would inherit it.
What the spans say
Attribute names follow OpenTelemetry's database conventions rather than an invented vocabulary, so the spans group with everything else in the trace:
| Attribute | Value |
|---|---|
db.system.name | qdrant |
db.operation.name | the client operation, e.g. query, upsert, create_collection |
db.collection.name | the collection, when the operation names one |
server.address, server.port | when you pass them |
error.type | the dev.kdrant.KdrantException subclass, on a failed span |
The span name is <operation> <collection>, or just the operation for the calls that are not about one collection.
What the spans never say
No payload value, no vector, no filter. A span attribute is exported to a backend many people can read, and the whole point of a filter is often that it names a tenant. That is also why a failed span carries error.type and no server message: Qdrant's errors quote the request back, and the request is the thing being kept out.
The attribute set is closed and asserted in the tests, so a future operation cannot quietly widen it.