mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Tempo: Fix Spans table format (#79938)
This commit is contained in:
parent
e372b54722
commit
ed5c90a8b1
@ -15,6 +15,7 @@ import {
|
|||||||
transformFromOTLP,
|
transformFromOTLP,
|
||||||
createTableFrameFromSearch,
|
createTableFrameFromSearch,
|
||||||
createTableFrameFromTraceQlQuery,
|
createTableFrameFromTraceQlQuery,
|
||||||
|
createTableFrameFromTraceQlQueryAsSpans,
|
||||||
} from './resultTransformer';
|
} from './resultTransformer';
|
||||||
import {
|
import {
|
||||||
badOTLPResponse,
|
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()', () => {
|
describe('transformFromOTLP()', () => {
|
||||||
// Mock the console error so that running the test suite doesnt throw the error
|
// Mock the console error so that running the test suite doesnt throw the error
|
||||||
const origError = console.error;
|
const origError = console.error;
|
||||||
|
@ -739,32 +739,6 @@ export function createTableFrameFromTraceQlQueryAsSpans(
|
|||||||
): DataFrame[] {
|
): DataFrame[] {
|
||||||
const spanDynamicAttrs: Record<string, FieldDTO> = {};
|
const spanDynamicAttrs: Record<string, FieldDTO> = {};
|
||||||
let hasNameAttribute = false;
|
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({
|
const frame = new MutableDataFrame({
|
||||||
name: 'Spans',
|
name: 'Spans',
|
||||||
refId: 'traces',
|
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];
|
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
|
data
|
||||||
// Show the most recent traces
|
// Show the most recent traces
|
||||||
.sort((a, b) => parseInt(b?.startTimeUnixNano!, 10) / 1000000 - parseInt(a?.startTimeUnixNano!, 10) / 1000000)
|
.sort((a, b) => parseInt(b?.startTimeUnixNano!, 10) / 1000000 - parseInt(a?.startTimeUnixNano!, 10) / 1000000)
|
||||||
.forEach((trace) => {
|
.forEach((trace) => {
|
||||||
trace.spanSets?.forEach((spanSet) => {
|
getSpanSets(trace).forEach((spanSet) => {
|
||||||
spanSet.spans.forEach((span) => {
|
spanSet.spans.forEach((span) => {
|
||||||
frame.add(transformSpanToTraceData(span, spanSet, trace));
|
frame.add(transformSpanToTraceData(span, spanSet, trace));
|
||||||
});
|
});
|
||||||
@ -874,6 +879,23 @@ export function createTableFrameFromTraceQlQueryAsSpans(
|
|||||||
return [frame];
|
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 = (
|
const traceSubFrame = (
|
||||||
trace: TraceSearchMetadata,
|
trace: TraceSearchMetadata,
|
||||||
spanSet: Spanset,
|
spanSet: Spanset,
|
||||||
|
@ -55,7 +55,7 @@ export type TraceSearchMetadata = {
|
|||||||
rootTraceName: string;
|
rootTraceName: string;
|
||||||
startTimeUnixNano?: string;
|
startTimeUnixNano?: string;
|
||||||
durationMs?: number;
|
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[];
|
spanSets?: Spanset[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user