Tracing: Send tags, events, and references as raw json (#52245)

* Tracing: Send tags, events, and references as raw json

* Update tempo fixture
This commit is contained in:
Connor Lindsey 2022-07-21 10:00:47 -06:00 committed by GitHub
parent c68ae0b616
commit 83c1756251
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 1118 additions and 165 deletions

View File

@ -1,5 +1,4 @@
import { e2e } from '@grafana/e2e';
import { selectors } from '@grafana/e2e-selectors';
const dataSourceName = 'PromExemplar';
const addDataSource = () => {

File diff suppressed because it is too large Load Diff

View File

@ -46,12 +46,12 @@ func TraceToFrame(td pdata.Traces) (*data.Frame, error) {
data.NewField("parentSpanID", nil, []string{}),
data.NewField("operationName", nil, []string{}),
data.NewField("serviceName", nil, []string{}),
data.NewField("serviceTags", nil, []string{}),
data.NewField("serviceTags", nil, []json.RawMessage{}),
data.NewField("startTime", nil, []float64{}),
data.NewField("duration", nil, []float64{}),
data.NewField("logs", nil, []string{}),
data.NewField("references", nil, []string{}),
data.NewField("tags", nil, []string{}),
data.NewField("logs", nil, []json.RawMessage{}),
data.NewField("references", nil, []json.RawMessage{}),
data.NewField("tags", nil, []json.RawMessage{}),
},
Meta: &data.FrameMeta{
// TODO: use constant once available in the SDK
@ -147,23 +147,15 @@ func spanToSpanRow(span pdata.Span, libraryTags pdata.InstrumentationLibrary, re
parentSpanID,
span.Name(),
serviceName,
toJSONString(serviceTagsJson),
json.RawMessage(serviceTagsJson),
startTime,
float64(span.EndTimestamp()-span.StartTimestamp()) / 1_000_000,
toJSONString(logs),
toJSONString(references),
toJSONString(spanTags),
json.RawMessage(logs),
json.RawMessage(references),
json.RawMessage(spanTags),
}, nil
}
func toJSONString(json []byte) string {
s := string(json)
if s == "null" {
return ""
}
return s
}
func resourceToProcess(resource pdata.Resource) (string, []*KeyValue) {
attrs := resource.Attributes()
serviceName := tracetranslator.ResourceNoServiceName

View File

@ -1,6 +1,7 @@
package tempo
import (
"encoding/json"
"io/ioutil"
"testing"
@ -34,21 +35,21 @@ func TestTraceToFrame(t *testing.T) {
require.Equal(t, "HTTP GET - loki_api_v1_query_range", root["operationName"])
require.Equal(t, "loki-all", root["serviceName"])
require.Equal(t, "[{\"value\":\"loki-all\",\"key\":\"service.name\"},{\"value\":\"Jaeger-Go-2.25.0\",\"key\":\"opencensus.exporterversion\"},{\"value\":\"4d019a031941\",\"key\":\"host.hostname\"},{\"value\":\"172.18.0.6\",\"key\":\"ip\"},{\"value\":\"4b19ace06df8e4de\",\"key\":\"client-uuid\"}]", root["serviceTags"])
require.Equal(t, json.RawMessage("[{\"value\":\"loki-all\",\"key\":\"service.name\"},{\"value\":\"Jaeger-Go-2.25.0\",\"key\":\"opencensus.exporterversion\"},{\"value\":\"4d019a031941\",\"key\":\"host.hostname\"},{\"value\":\"172.18.0.6\",\"key\":\"ip\"},{\"value\":\"4b19ace06df8e4de\",\"key\":\"client-uuid\"}]"), root["serviceTags"])
require.Equal(t, 1616072924070.497, root["startTime"])
require.Equal(t, 8.421, root["duration"])
require.Equal(t, "", root["logs"])
require.Equal(t, "[{\"value\":\"const\",\"key\":\"sampler.type\"},{\"value\":true,\"key\":\"sampler.param\"},{\"value\":200,\"key\":\"http.status_code\"},{\"value\":\"GET\",\"key\":\"http.method\"},{\"value\":\"/loki/api/v1/query_range?direction=BACKWARD\\u0026limit=1000\\u0026query=%7Bcompose_project%3D%22devenv%22%7D%20%7C%3D%22traceID%22\\u0026start=1616070921000000000\\u0026end=1616072722000000000\\u0026step=2\",\"key\":\"http.url\"},{\"value\":\"net/http\",\"key\":\"component\"},{\"value\":\"server\",\"key\":\"span.kind\"},{\"value\":0,\"key\":\"status.code\"}]", root["tags"])
require.Equal(t, json.RawMessage("null"), root["logs"])
require.Equal(t, json.RawMessage("[{\"value\":\"const\",\"key\":\"sampler.type\"},{\"value\":true,\"key\":\"sampler.param\"},{\"value\":200,\"key\":\"http.status_code\"},{\"value\":\"GET\",\"key\":\"http.method\"},{\"value\":\"/loki/api/v1/query_range?direction=BACKWARD\\u0026limit=1000\\u0026query=%7Bcompose_project%3D%22devenv%22%7D%20%7C%3D%22traceID%22\\u0026start=1616070921000000000\\u0026end=1616072722000000000\\u0026step=2\",\"key\":\"http.url\"},{\"value\":\"net/http\",\"key\":\"component\"},{\"value\":\"server\",\"key\":\"span.kind\"},{\"value\":0,\"key\":\"status.code\"}]"), root["tags"])
span := bFrame.FindRowWithValue("spanID", "7198307df9748606")
require.Equal(t, "GetParallelChunks", span["operationName"])
require.Equal(t, "loki-all", span["serviceName"])
require.Equal(t, "[{\"value\":\"loki-all\",\"key\":\"service.name\"},{\"value\":\"Jaeger-Go-2.25.0\",\"key\":\"opencensus.exporterversion\"},{\"value\":\"4d019a031941\",\"key\":\"host.hostname\"},{\"value\":\"172.18.0.6\",\"key\":\"ip\"},{\"value\":\"4b19ace06df8e4de\",\"key\":\"client-uuid\"}]", span["serviceTags"])
require.Equal(t, json.RawMessage("[{\"value\":\"loki-all\",\"key\":\"service.name\"},{\"value\":\"Jaeger-Go-2.25.0\",\"key\":\"opencensus.exporterversion\"},{\"value\":\"4d019a031941\",\"key\":\"host.hostname\"},{\"value\":\"172.18.0.6\",\"key\":\"ip\"},{\"value\":\"4b19ace06df8e4de\",\"key\":\"client-uuid\"}]"), span["serviceTags"])
require.Equal(t, 1616072924072.852, span["startTime"])
require.Equal(t, 0.094, span["duration"])
require.Equal(t, "[{\"timestamp\":1616072924072.856,\"fields\":[{\"value\":1,\"key\":\"chunks requested\"}]},{\"timestamp\":1616072924072.9448,\"fields\":[{\"value\":1,\"key\":\"chunks fetched\"}]}]", span["logs"])
require.Equal(t, "[{\"value\":0,\"key\":\"status.code\"}]", span["tags"])
require.Equal(t, json.RawMessage("[{\"timestamp\":1616072924072.856,\"fields\":[{\"value\":1,\"key\":\"chunks requested\"}]},{\"timestamp\":1616072924072.9448,\"fields\":[{\"value\":1,\"key\":\"chunks fetched\"}]}]"), span["logs"])
require.Equal(t, json.RawMessage("[{\"value\":0,\"key\":\"status.code\"}]"), span["tags"])
})
}

View File

@ -117,9 +117,9 @@ describe('Tempo data source', () => {
{ name: 'operationName', values: ['store.validateQueryTimeRange'] },
{ name: 'startTime', values: [1619712655875.4539] },
{ name: 'duration', values: [14.984] },
{ name: 'serviceTags', values: ['{"key":"servicetag1","value":"service"}'] },
{ name: 'logs', values: ['{"timestamp":12345,"fields":[{"key":"count","value":1}]}'] },
{ name: 'tags', values: ['{"key":"tag1","value":"val1"}'] },
{ name: 'serviceTags', values: [{ key: 'servicetag1', value: 'service' }] },
{ name: 'logs', values: [{ timestamp: 12345, fields: [{ key: 'count', value: 1 }] }] },
{ name: 'tags', values: [{ key: 'tag1', value: 'val1' }] },
{ name: 'serviceName', values: ['service'] },
],
})

View File

@ -1,20 +1,12 @@
import { collectorTypes } from '@opentelemetry/exporter-collector';
import {
ArrayVector,
FieldType,
MutableDataFrame,
PluginType,
DataSourceInstanceSettings,
dateTime,
} from '@grafana/data';
import { FieldType, MutableDataFrame, PluginType, DataSourceInstanceSettings, dateTime } from '@grafana/data';
import {
SearchResponse,
createTableFrame,
transformToOTLP,
transformFromOTLP,
transformTrace,
createTableFrameFromSearch,
} from './resultTransformer';
import {
@ -154,42 +146,3 @@ describe('transformFromOTLP()', () => {
}).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

@ -5,11 +5,9 @@ import differenceInHours from 'date-fns/differenceInHours';
import formatDistance from 'date-fns/formatDistance';
import {
ArrayVector,
DataFrame,
DataQueryResponse,
DataSourceInstanceSettings,
Field,
FieldType,
MutableDataFrame,
TraceKeyValuePair,
@ -534,22 +532,12 @@ function getOTLPReferences(
}
export function transformTrace(response: DataQueryResponse, nodeGraph = false): DataQueryResponse {
// We need to parse some of the fields which contain stringified json.
// Seems like we can't just map the values as the frame we got from backend has some default processing
// and will stringify the json back when we try to set it. So we create a new field and swap it instead.
const frame: DataFrame = response.data[0];
if (!frame) {
return emptyDataQueryResponse;
}
try {
parseJsonFields(frame);
} catch (error) {
console.error(error);
return { error: { message: 'Unable to parse trace response: ' + error }, data: [] };
}
let data = [...response.data];
if (nodeGraph) {
data.push(...createGraphFrames(frame));
@ -561,30 +549,6 @@ export function transformTrace(response: DataQueryResponse, nodeGraph = false):
};
}
/**
* Change fields which are json string into JS objects. Modifies the frame in place.
*/
function parseJsonFields(frame: DataFrame) {
for (const fieldName of ['serviceTags', 'logs', 'tags', 'references']) {
const field = frame.fields.find((f) => f.name === fieldName);
if (field) {
const fieldIndex = frame.fields.indexOf(field);
const values = new ArrayVector();
const newField: Field = {
...field,
values,
type: FieldType.other,
};
for (let i = 0; i < field.values.length; i++) {
const value = field.values.get(i);
values.set(i, value === '' ? undefined : JSON.parse(value));
}
frame.fields[fieldIndex] = newField;
}
}
}
export type SearchResponse = {
traceID: string;
rootServiceName: string;