mirror of
https://github.com/grafana/grafana.git
synced 2024-11-29 12:14:08 -06:00
Tempo: Fix Spans table format (#79938)
This commit is contained in:
parent
e372b54722
commit
ed5c90a8b1
@ -15,6 +15,7 @@ import {
|
||||
transformFromOTLP,
|
||||
createTableFrameFromSearch,
|
||||
createTableFrameFromTraceQlQuery,
|
||||
createTableFrameFromTraceQlQueryAsSpans,
|
||||
} from './resultTransformer';
|
||||
import {
|
||||
badOTLPResponse,
|
||||
@ -198,6 +199,152 @@ describe('createTableFrameFromTraceQlQuery()', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('createTableFrameFromTraceQlQueryAsSpans()', () => {
|
||||
test('transforms TraceQL legacy response to DataFrame for Spans table type', () => {
|
||||
const traces = [
|
||||
{
|
||||
traceID: '1',
|
||||
rootServiceName: 'prometheus',
|
||||
rootTraceName: 'POST /api/v1/write',
|
||||
startTimeUnixNano: '1702984850354934104',
|
||||
durationMs: 1,
|
||||
spanSet: {
|
||||
spans: [
|
||||
{
|
||||
spanID: '11',
|
||||
startTimeUnixNano: '1702984850354934104',
|
||||
durationNanos: '1377608',
|
||||
},
|
||||
],
|
||||
matched: 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
traceID: '2',
|
||||
rootServiceName: 'prometheus',
|
||||
rootTraceName: 'GET /api/v1/status/config',
|
||||
startTimeUnixNano: '1702984840786143459',
|
||||
spanSet: {
|
||||
spans: [
|
||||
{
|
||||
spanID: '21',
|
||||
startTimeUnixNano: '1702984840786143459',
|
||||
durationNanos: '542316',
|
||||
},
|
||||
],
|
||||
matched: 1,
|
||||
},
|
||||
},
|
||||
];
|
||||
const frameList = createTableFrameFromTraceQlQueryAsSpans(traces, defaultSettings);
|
||||
const frame = frameList[0];
|
||||
|
||||
// Trace ID field
|
||||
expect(frame.fields[0].name).toBe('traceIdHidden');
|
||||
expect(frame.fields[0].values[0]).toBe('1');
|
||||
// Trace service field
|
||||
expect(frame.fields[1].name).toBe('traceService');
|
||||
expect(frame.fields[1].type).toBe('string');
|
||||
expect(frame.fields[1].values[0]).toBe('prometheus');
|
||||
// Trace name field
|
||||
expect(frame.fields[2].name).toBe('traceName');
|
||||
expect(frame.fields[2].type).toBe('string');
|
||||
expect(frame.fields[2].values[0]).toBe('POST /api/v1/write');
|
||||
// Span ID field
|
||||
expect(frame.fields[3].name).toBe('spanID');
|
||||
expect(frame.fields[3].type).toBe('string');
|
||||
expect(frame.fields[3].values[0]).toBe('11');
|
||||
// Time field
|
||||
expect(frame.fields[4].name).toBe('time');
|
||||
expect(frame.fields[4].type).toBe('time');
|
||||
expect(frame.fields[4].values[0]).toBe(1702984850354.934);
|
||||
// Name field
|
||||
expect(frame.fields[5].name).toBe('name');
|
||||
expect(frame.fields[5].type).toBe('string');
|
||||
expect(frame.fields[5].values[0]).toBe(undefined);
|
||||
// Duration field
|
||||
expect(frame.fields[6].name).toBe('duration');
|
||||
expect(frame.fields[6].type).toBe('number');
|
||||
expect(frame.fields[6].values[0]).toBe(1377608);
|
||||
// No more fields
|
||||
expect(frame.fields.length).toBe(7);
|
||||
});
|
||||
|
||||
test('transforms TraceQL response to DataFrame for Spans table type', () => {
|
||||
const traces = [
|
||||
{
|
||||
traceID: '1',
|
||||
rootServiceName: 'prometheus',
|
||||
rootTraceName: 'POST /api/v1/write',
|
||||
startTimeUnixNano: '1702984850354934104',
|
||||
durationMs: 1,
|
||||
spanSets: [
|
||||
{
|
||||
spans: [
|
||||
{
|
||||
spanID: '11',
|
||||
startTimeUnixNano: '1702984850354934104',
|
||||
durationNanos: '1377608',
|
||||
},
|
||||
],
|
||||
matched: 1,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
traceID: '2',
|
||||
rootServiceName: 'prometheus',
|
||||
rootTraceName: 'GET /api/v1/status/config',
|
||||
startTimeUnixNano: '1702984840786143459',
|
||||
spanSets: [
|
||||
{
|
||||
spans: [
|
||||
{
|
||||
spanID: '21',
|
||||
startTimeUnixNano: '1702984840786143459',
|
||||
durationNanos: '542316',
|
||||
},
|
||||
],
|
||||
matched: 1,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
const frameList = createTableFrameFromTraceQlQueryAsSpans(traces, defaultSettings);
|
||||
const frame = frameList[0];
|
||||
|
||||
// Trace ID field
|
||||
expect(frame.fields[0].name).toBe('traceIdHidden');
|
||||
expect(frame.fields[0].values[0]).toBe('1');
|
||||
// Trace service field
|
||||
expect(frame.fields[1].name).toBe('traceService');
|
||||
expect(frame.fields[1].type).toBe('string');
|
||||
expect(frame.fields[1].values[0]).toBe('prometheus');
|
||||
// Trace name field
|
||||
expect(frame.fields[2].name).toBe('traceName');
|
||||
expect(frame.fields[2].type).toBe('string');
|
||||
expect(frame.fields[2].values[0]).toBe('POST /api/v1/write');
|
||||
// Span ID field
|
||||
expect(frame.fields[3].name).toBe('spanID');
|
||||
expect(frame.fields[3].type).toBe('string');
|
||||
expect(frame.fields[3].values[0]).toBe('11');
|
||||
// Time field
|
||||
expect(frame.fields[4].name).toBe('time');
|
||||
expect(frame.fields[4].type).toBe('time');
|
||||
expect(frame.fields[4].values[0]).toBe(1702984850354.934);
|
||||
// Name field
|
||||
expect(frame.fields[5].name).toBe('name');
|
||||
expect(frame.fields[5].type).toBe('string');
|
||||
expect(frame.fields[5].values[0]).toBe(undefined);
|
||||
// Duration field
|
||||
expect(frame.fields[6].name).toBe('duration');
|
||||
expect(frame.fields[6].type).toBe('number');
|
||||
expect(frame.fields[6].values[0]).toBe(1377608);
|
||||
// No more fields
|
||||
expect(frame.fields.length).toBe(7);
|
||||
});
|
||||
});
|
||||
|
||||
describe('transformFromOTLP()', () => {
|
||||
// Mock the console error so that running the test suite doesnt throw the error
|
||||
const origError = console.error;
|
||||
|
@ -739,32 +739,6 @@ export function createTableFrameFromTraceQlQueryAsSpans(
|
||||
): DataFrame[] {
|
||||
const spanDynamicAttrs: Record<string, FieldDTO> = {};
|
||||
let hasNameAttribute = false;
|
||||
|
||||
data?.forEach(
|
||||
(t) =>
|
||||
t.spanSets?.forEach((ss) => {
|
||||
ss.attributes?.forEach((attr) => {
|
||||
spanDynamicAttrs[attr.key] = {
|
||||
name: attr.key,
|
||||
type: FieldType.string,
|
||||
config: { displayNameFromDS: attr.key },
|
||||
};
|
||||
});
|
||||
ss.spans.forEach((span) => {
|
||||
if (span.name) {
|
||||
hasNameAttribute = true;
|
||||
}
|
||||
span.attributes?.forEach((attr) => {
|
||||
spanDynamicAttrs[attr.key] = {
|
||||
name: attr.key,
|
||||
type: FieldType.string,
|
||||
config: { displayNameFromDS: attr.key },
|
||||
};
|
||||
});
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
const frame = new MutableDataFrame({
|
||||
name: 'Spans',
|
||||
refId: 'traces',
|
||||
@ -856,15 +830,46 @@ export function createTableFrameFromTraceQlQueryAsSpans(
|
||||
},
|
||||
});
|
||||
|
||||
if (!data?.length) {
|
||||
// According to the parameter type, `data` should never be undefined of null, but the old code had
|
||||
// entries such as `!data` or `data?`, so we keep this check just for safety
|
||||
if (data === undefined || data === null) {
|
||||
console.error(`Unexpected ${data} value for \`data\``);
|
||||
return [frame];
|
||||
}
|
||||
|
||||
if (!data.length) {
|
||||
return [frame];
|
||||
}
|
||||
|
||||
data.forEach((trace) =>
|
||||
getSpanSets(trace).forEach((ss) => {
|
||||
ss.attributes?.forEach((attr) => {
|
||||
spanDynamicAttrs[attr.key] = {
|
||||
name: attr.key,
|
||||
type: FieldType.string,
|
||||
config: { displayNameFromDS: attr.key },
|
||||
};
|
||||
});
|
||||
ss.spans.forEach((span) => {
|
||||
if (span.name) {
|
||||
hasNameAttribute = true;
|
||||
}
|
||||
span.attributes?.forEach((attr) => {
|
||||
spanDynamicAttrs[attr.key] = {
|
||||
name: attr.key,
|
||||
type: FieldType.string,
|
||||
config: { displayNameFromDS: attr.key },
|
||||
};
|
||||
});
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
data
|
||||
// Show the most recent traces
|
||||
.sort((a, b) => parseInt(b?.startTimeUnixNano!, 10) / 1000000 - parseInt(a?.startTimeUnixNano!, 10) / 1000000)
|
||||
.forEach((trace) => {
|
||||
trace.spanSets?.forEach((spanSet) => {
|
||||
getSpanSets(trace).forEach((spanSet) => {
|
||||
spanSet.spans.forEach((span) => {
|
||||
frame.add(transformSpanToTraceData(span, spanSet, trace));
|
||||
});
|
||||
@ -874,6 +879,23 @@ export function createTableFrameFromTraceQlQueryAsSpans(
|
||||
return [frame];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the spansets of a trace.
|
||||
*
|
||||
* Field `spanSets` is preferred to `spanSet` since the latter is deprecated in Tempo, but we
|
||||
* support both for backward compatibility.
|
||||
*
|
||||
* @param trace a trace
|
||||
* @returns the spansets of the trace, if existing
|
||||
*/
|
||||
const getSpanSets = (trace: TraceSearchMetadata): Spanset[] => {
|
||||
if (trace.spanSets && trace.spanSet) {
|
||||
console.warn('Both `spanSets` and `spanSet` are set. `spanSet` will be ignored');
|
||||
}
|
||||
|
||||
return trace.spanSets || (trace.spanSet ? [trace.spanSet] : []);
|
||||
};
|
||||
|
||||
const traceSubFrame = (
|
||||
trace: TraceSearchMetadata,
|
||||
spanSet: Spanset,
|
||||
|
@ -55,7 +55,7 @@ export type TraceSearchMetadata = {
|
||||
rootTraceName: string;
|
||||
startTimeUnixNano?: string;
|
||||
durationMs?: number;
|
||||
spanSet?: Spanset;
|
||||
spanSet?: Spanset; // deprecated in Tempo, https://github.com/grafana/tempo/blob/3cc44fca03ba7d676dc77da6a18b8222546ede3c/docs/sources/tempo/api_docs/_index.md?plain=1#L619
|
||||
spanSets?: Spanset[];
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user