Tempo: Show error if transformTrace() is passed bad data to parse to JSON (#44684)

* Surface errors if transformTrace() is passed bad data to parse
This commit is contained in:
Cat Perry 2022-02-01 17:39:09 -05:00 committed by GitHub
parent 1f4fdf31df
commit a86d7a5a90
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 138 additions and 19 deletions

View File

@ -1,14 +1,45 @@
import { FieldType, MutableDataFrame, PluginType, DataSourceInstanceSettings, dateTime } from '@grafana/data'; import {
ArrayVector,
FieldType,
MutableDataFrame,
PluginType,
DataSourceInstanceSettings,
dateTime,
} from '@grafana/data';
import { import {
SearchResponse, SearchResponse,
createTableFrame, createTableFrame,
transformToOTLP, transformToOTLP,
transformFromOTLP, transformFromOTLP,
transformTrace,
createTableFrameFromSearch, createTableFrameFromSearch,
} from './resultTransformer'; } from './resultTransformer';
import { otlpDataFrameToResponse, otlpDataFrameFromResponse, otlpResponse, tempoSearchResponse } from './testResponse'; import {
badOTLPResponse,
otlpDataFrameToResponse,
otlpDataFrameFromResponse,
otlpResponse,
tempoSearchResponse,
} from './testResponse';
import { collectorTypes } from '@opentelemetry/exporter-collector'; import { collectorTypes } from '@opentelemetry/exporter-collector';
const defaultSettings: DataSourceInstanceSettings = {
id: 0,
uid: '0',
type: 'tracing',
name: 'tempo',
access: 'proxy',
meta: {
id: 'tempo',
name: 'tempo',
type: PluginType.datasource,
info: {} as any,
module: '',
baseUrl: '',
},
jsonData: {},
};
describe('transformTraceList()', () => { describe('transformTraceList()', () => {
const lokiDataFrame = new MutableDataFrame({ const lokiDataFrame = new MutableDataFrame({
fields: [ fields: [
@ -84,19 +115,73 @@ describe('createTableFrameFromSearch()', () => {
}); });
}); });
const defaultSettings: DataSourceInstanceSettings = { describe('transformFromOTLP()', () => {
id: 0, // Mock the console error so that running the test suite doesnt throw the error
uid: '0', const origError = console.error;
type: 'tracing', const consoleErrorMock = jest.fn();
name: 'tempo', afterEach(() => (console.error = origError));
access: 'proxy', beforeEach(() => (console.error = consoleErrorMock));
meta: {
id: 'tempo', test('if passed bad data, will surface an error', () => {
name: 'tempo', const res = transformFromOTLP(
type: PluginType.datasource, (badOTLPResponse.batches as unknown) as collectorTypes.opentelemetryProto.trace.v1.ResourceSpans[],
info: {} as any, false
module: '', );
baseUrl: '',
}, expect(res.data[0]).toBeFalsy();
jsonData: {}, expect(res.error?.message).toBeTruthy();
}; // if it does have resources, no error will be thrown
expect({
...res.data[0],
resources: {
attributes: [
{ key: 'service.name', value: { stringValue: 'db' } },
{ key: 'job', value: { stringValue: 'tns/db' } },
{ key: 'opencensus.exporterversion', value: { stringValue: 'Jaeger-Go-2.22.1' } },
{ key: 'host.name', value: { stringValue: '63d16772b4a2' } },
{ key: 'ip', value: { stringValue: '0.0.0.0' } },
{ key: 'client-uuid', value: { stringValue: '39fb01637a579639' } },
],
},
}).not.toBeFalsy();
});
});
describe('transformTrace()', () => {
// Mock the console error so that running the test suite doesnt throw the error
const origError = console.error;
const consoleErrorMock = jest.fn();
afterEach(() => (console.error = origError));
beforeEach(() => (console.error = consoleErrorMock));
const badFrame = new MutableDataFrame({
fields: [
{
name: 'serviceTags',
values: new ArrayVector([undefined]),
},
],
});
const goodFrame = new MutableDataFrame({
fields: [
{
name: 'serviceTags',
values: new ArrayVector(),
},
],
});
test('if passed bad data, will surface an error', () => {
const response = transformTrace({ data: [badFrame] }, false);
expect(response.data[0]).toBeFalsy();
expect(response.error?.message).toBeTruthy();
});
test('if passed good data, will parse successfully', () => {
const response2 = transformTrace({ data: [goodFrame] }, false);
expect(response2.data[0]).toBeTruthy();
expect(response2.data[0]).toMatchObject(goodFrame);
expect(response2.error).toBeFalsy();
});
});

View File

@ -491,7 +491,12 @@ export function transformTrace(response: DataQueryResponse, nodeGraph = false):
return emptyDataQueryResponse; return emptyDataQueryResponse;
} }
parseJsonFields(frame); try {
parseJsonFields(frame);
} catch (error) {
console.error(error);
return { error: { message: 'Unable to parse trace response: ' + error }, data: [] };
}
let data = [...response.data]; let data = [...response.data];
if (nodeGraph) { if (nodeGraph) {

View File

@ -2223,3 +2223,32 @@ export const tempoSearchResponse = {
inspectedBytes: '83720', inspectedBytes: '83720',
}, },
}; };
export const badOTLPResponse = {
batches: [
{
resource: {},
instrumentationLibrarySpans: [
{
spans: [
{
traceId: 'AAAAAAAAAABguiq7RPE+rg==',
spanId: 'cmteMBAvwNA=',
parentSpanId: 'OY8PIaPbma4=',
name: 'HTTP GET - root',
kind: 'SPAN_KIND_CLIENT',
startTimeUnixNano: 1627471657255809000,
endTimeUnixNano: 1627471657256268000,
attributes: [
{ key: 'http.status_code', value: { intValue: 200 } },
{ key: 'http.method', value: { stringValue: 'GET' } },
{ key: 'http.url', value: { stringValue: '/' } },
{ key: 'component', value: { stringValue: 'net/http' } },
],
},
],
},
],
},
],
};