Grafana Tempo Adapter
@openplait/adapter-tempo is a generic Tempo adapter. It has no OpenLIT-specific
field assumptions; applications can add canonical attribute mappings.
Configure
import { TempoAdapter } from "@openplait/adapter-tempo";
const adapter = new TempoAdapter({
url: "https://tempo.example.com",
tenantId: "tenant-a",
bearerToken: process.env.TEMPO_TOKEN,
maxResultRows: 1_000,
maxTimeRangeMs: 7 * 24 * 60 * 60 * 1_000,
// Optional when the host already knows it. Otherwise inspect the server.
tempoVersion: "2.8.2",
attributeFields: {
"deployment.environment": {
scope: "resource",
attribute: "deployment.environment.name",
type: "string",
},
"gen_ai.request.model": {
scope: "span",
attribute: "gen_ai.request.model",
type: "string",
},
},
});
Authentication options are bearer token, a username/password pair, or custom
HTTP headers. tenantId is sent as Tempo's X-Scope-OrgID header.
Detect version-sensitive capabilities
Tempo features are not uniform across releases and deployments. In particular,
the experimental most_recent hint starts in Tempo 2.8, TraceQL metrics can be
disabled, and older servers use v1 tag discovery endpoints. Inspect the actual
server during connection health checking:
const profile = await adapter.inspectServer({ timeoutMs: 8_000 });
console.log(profile.version);
console.log(profile.features.mostRecent);
console.log(profile.features.traceqlMetrics);
console.log(profile.features.tagSearchV2);
Unknown versions are handled conservatively: semantic compilation does not add
experimental query hints. Set enableMostRecent only as an explicit operator
override. getCapabilities() also exposes the profile under the namespaced
io.openplait.tempo extension and reports only limits known from configuration.
Semantic trace search
Search uses signal traces and dataset tempo.trace_search:
const traces = {
apiVersion: "openplait.io/v1alpha1",
kind: "Query",
metadata: { name: "slow-errors" },
spec: {
mode: "semantic",
datasource: { kind: "TempoDatasource", name: "tempo-prod" },
input: { signal: "traces", entity: "tempo.trace_search" },
timeRange: { field: "timestamp", from: "${__from}", to: "${__to}" },
select: [
{ field: "trace.id" },
{ field: "timestamp" },
{ field: "duration" },
{ field: "root.service.name", as: "service" },
],
where: {
and: [
{ field: "service.name", operator: "equals", value: "checkout" },
{ field: "status.code", operator: "equals", value: "error" },
],
},
orderBy: [{ field: "timestamp", direction: "desc" }],
limit: 100,
},
};
Search returns trace summaries. Span intrinsics and attributes are filter-only; retrieve a complete OTLP trace for span rows and events:
const result = await adapter.getTrace(
"0123456789abcdef0123456789abcdef",
{ audit: { requestId: "trace-detail-31" } },
);
Tag discovery
const tags = await adapter.discoverTags("resource", {});
const services = await adapter.discoverTagValues(
"resource.service.name",
{},
{
query: '{ span:status = error }',
range: { from: fromIso, to: toIso },
},
);
Both methods prefer Tempo v2 discovery endpoints, normalize response variants,
and fall back to the legacy v1 endpoints when an unpinned server returns 400 or
404. Pinning enableTagSearchV2: true disables that compatibility retry.
TraceQL metrics
TraceQL metrics use a distinct endpoint and usually have a smaller time-window
ceiling than trace search. queryMetrics() keeps execution, authentication,
auditing, error handling, and dataframe normalization inside OpenPlait:
const metrics = await adapter.queryMetrics(
{
query: '{} | count_over_time() by (resource.service.name)',
from: fromIso,
to: toIso,
step: "5m",
},
{ audit: { requestId: "tempo-metrics-42" } },
);
The host should split ranges according to its deployed metrics limit and merge samples by label set and timestamp. OpenLIT uses 24-hour chunks by default.
Actionable errors
Non-success responses throw AdapterError. Its details contain the status,
truncated upstream body, endpoint path, query length, parameter names, and
request IDs. The complete URL and authorization headers are deliberately not
included. Log these structured details when debugging a 400; do not retry by
removing required filters.
Native TraceQL
Enable allowNativeQueries only for trusted server-side callers. Native
queries must include the io.openplait.tempo extension with explicit bounds,
limit, and spans-per-span-set settings. See Native Queries.