diff --git a/public/app/plugins/datasource/tempo/datasource.test.ts b/public/app/plugins/datasource/tempo/datasource.test.ts index 8a950bd62c3..eaad824a6e7 100644 --- a/public/app/plugins/datasource/tempo/datasource.test.ts +++ b/public/app/plugins/datasource/tempo/datasource.test.ts @@ -9,6 +9,12 @@ describe('Tempo data source', () => { setupBackendSrv( new MutableDataFrame({ fields: [ + { name: 'traceID', values: ['04450900759028499335'] }, + { name: 'spanID', values: ['4322526419282105830'] }, + { name: 'parentSpanID', values: [''] }, + { 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"}'] }, @@ -17,20 +23,50 @@ describe('Tempo data source', () => { }) ); const ds = new TempoDatasource(defaultSettings); - await expect(ds.query({ targets: [{ refId: 'refid1' }] } as any)).toEmitValuesWith((response) => { - const fields = (response[0].data[0] as DataFrame).fields; - expect( - fields.map((f) => ({ - name: f.name, - values: f.values.toArray(), - })) - ).toMatchObject([ - { 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'] }, - ]); - }); + const response = await ds.query({ targets: [{ refId: 'refid1' }] } as any).toPromise(); + + expect( + (response.data[0] as DataFrame).fields.map((f) => ({ + name: f.name, + values: f.values.toArray(), + })) + ).toMatchObject([ + { name: 'traceID', values: ['04450900759028499335'] }, + { name: 'spanID', values: ['4322526419282105830'] }, + { name: 'parentSpanID', values: [''] }, + { 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: 'serviceName', values: ['service'] }, + ]); + + expect( + (response.data[1] as DataFrame).fields.map((f) => ({ + name: f.name, + values: f.values.toArray(), + })) + ).toMatchObject([ + { name: 'id', values: ['4322526419282105830'] }, + { name: 'title', values: ['service'] }, + { name: 'subTitle', values: ['store.validateQueryTimeRange'] }, + { name: 'mainStat', values: ['total: 14.98ms (100%)'] }, + { name: 'secondaryStat', values: ['self: 14.98ms (100%)'] }, + { name: 'color', values: [1.000007560204647] }, + ]); + + expect( + (response.data[2] as DataFrame).fields.map((f) => ({ + name: f.name, + values: f.values.toArray(), + })) + ).toMatchObject([ + { name: 'id', values: [] }, + { name: 'target', values: [] }, + { name: 'source', values: [] }, + ]); }); }); diff --git a/public/app/plugins/datasource/tempo/datasource.ts b/public/app/plugins/datasource/tempo/datasource.ts index 665eae1b07b..30905380449 100644 --- a/public/app/plugins/datasource/tempo/datasource.ts +++ b/public/app/plugins/datasource/tempo/datasource.ts @@ -12,6 +12,7 @@ import { import { DataSourceWithBackend } from '@grafana/runtime'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; +import { createGraphFrames } from './graphTransform'; export type TempoQuery = { query: string; @@ -28,7 +29,6 @@ export class TempoDatasource extends DataSourceWithBackend { if (response.error) { return response; } - // 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. @@ -38,26 +38,12 @@ export class TempoDatasource extends DataSourceWithBackend { return emptyDataQueryResponse; } - for (const fieldName of ['serviceTags', 'logs', 'tags']) { - 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, - }; + parseJsonFields(frame); - 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; - } - } - - return response; + return { + ...response, + data: [...response.data, ...createGraphFrames(frame)], + }; }) ); } @@ -77,6 +63,30 @@ export class TempoDatasource extends DataSourceWithBackend { } } +/** + * 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']) { + 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; + } + } +} + const emptyDataQueryResponse = { data: [ new MutableDataFrame({ diff --git a/public/app/plugins/datasource/tempo/graphTransform.test.ts b/public/app/plugins/datasource/tempo/graphTransform.test.ts new file mode 100644 index 00000000000..d10637183f3 --- /dev/null +++ b/public/app/plugins/datasource/tempo/graphTransform.test.ts @@ -0,0 +1,64 @@ +import { createGraphFrames } from './graphTransform'; +import { bigResponse } from './testResponse'; +import { DataFrameView, MutableDataFrame } from '@grafana/data'; + +describe('createGraphFrames', () => { + it('transforms basic response into nodes and edges frame', async () => { + const frames = createGraphFrames(bigResponse); + expect(frames.length).toBe(2); + expect(frames[0].length).toBe(30); + expect(frames[1].length).toBe(29); + + let view = new DataFrameView(frames[0]); + expect(view.get(0)).toMatchObject({ + id: '4322526419282105830', + title: 'loki-all', + subTitle: 'store.validateQueryTimeRange', + mainStat: 'total: 0ms (0.02%)', + secondaryStat: 'self: 0ms (100%)', + color: 0.00021968356127648162, + }); + + expect(view.get(29)).toMatchObject({ + id: '4450900759028499335', + title: 'loki-all', + subTitle: 'HTTP GET - loki_api_v1_query_range', + mainStat: 'total: 18.21ms (100%)', + secondaryStat: 'self: 3.22ms (17.71%)', + color: 0.17707117189595056, + }); + + view = new DataFrameView(frames[1]); + expect(view.get(28)).toMatchObject({ + id: '4450900759028499335--4790760741274015949', + }); + }); + + it('handles single span response', async () => { + const frames = createGraphFrames(singleSpanResponse); + expect(frames.length).toBe(2); + expect(frames[0].length).toBe(1); + + const view = new DataFrameView(frames[0]); + expect(view.get(0)).toMatchObject({ + id: '4322526419282105830', + title: 'loki-all', + subTitle: 'store.validateQueryTimeRange', + mainStat: 'total: 14.98ms (100%)', + secondaryStat: 'self: 14.98ms (100%)', + color: 1.000007560204647, + }); + }); +}); + +const singleSpanResponse = new MutableDataFrame({ + fields: [ + { name: 'traceID', values: ['04450900759028499335'] }, + { name: 'spanID', values: ['4322526419282105830'] }, + { name: 'parentSpanID', values: [''] }, + { name: 'operationName', values: ['store.validateQueryTimeRange'] }, + { name: 'serviceName', values: ['loki-all'] }, + { name: 'startTime', values: [1619712655875.4539] }, + { name: 'duration', values: [14.984] }, + ], +}); diff --git a/public/app/plugins/datasource/tempo/graphTransform.ts b/public/app/plugins/datasource/tempo/graphTransform.ts new file mode 100644 index 00000000000..a44f2b78df7 --- /dev/null +++ b/public/app/plugins/datasource/tempo/graphTransform.ts @@ -0,0 +1,198 @@ +import { DataFrame, DataFrameView, FieldType, MutableDataFrame } from '@grafana/data'; +import { NodeGraphDataFrameFieldNames as Fields } from '@grafana/ui'; + +interface Row { + traceID: string; + spanID: string; + parentSpanID: string; + operationName: string; + serviceName: string; + serviceTags: string; + startTime: number; + duration: number; + logs: string; + tags: string; +} + +interface Node { + [Fields.id]: string; + [Fields.title]: string; + [Fields.subTitle]: string; + [Fields.mainStat]: string; + [Fields.secondaryStat]: string; + [Fields.color]: number; +} + +interface Edge { + [Fields.id]: string; + [Fields.target]: string; + [Fields.source]: string; +} + +export function createGraphFrames(data: DataFrame): DataFrame[] { + const { nodes, edges } = convertTraceToGraph(data); + + const nodesFrame = new MutableDataFrame({ + fields: [ + { name: Fields.id, type: FieldType.string }, + { name: Fields.title, type: FieldType.string }, + { name: Fields.subTitle, type: FieldType.string }, + { name: Fields.mainStat, type: FieldType.string }, + { name: Fields.secondaryStat, type: FieldType.string }, + { name: Fields.color, type: FieldType.number, config: { color: { mode: 'continuous-GrYlRd' } } }, + ], + meta: { + preferredVisualisationType: 'nodeGraph', + }, + }); + + for (const node of nodes) { + nodesFrame.add(node); + } + + const edgesFrame = new MutableDataFrame({ + fields: [ + { name: Fields.id, type: FieldType.string }, + { name: Fields.target, type: FieldType.string }, + { name: Fields.source, type: FieldType.string }, + ], + meta: { + preferredVisualisationType: 'nodeGraph', + }, + }); + + for (const edge of edges) { + edgesFrame.add(edge); + } + + return [nodesFrame, edgesFrame]; +} + +function convertTraceToGraph(data: DataFrame): { nodes: Node[]; edges: Edge[] } { + const nodes: Node[] = []; + const edges: Edge[] = []; + + const view = new DataFrameView(data); + + const traceDuration = findTraceDuration(view); + const spanMap = makeSpanMap(view); + + for (let i = 0; i < view.length; i++) { + const row = view.get(i); + + const childrenDuration = getDuration(spanMap[row.spanID].children.map((c) => spanMap[c].span)); + const selfDuration = row.duration - childrenDuration; + + nodes.push({ + [Fields.id]: row.spanID, + [Fields.title]: row.serviceName ?? '', + [Fields.subTitle]: row.operationName, + [Fields.mainStat]: `total: ${toFixedNoTrailingZeros(row.duration)}ms (${toFixedNoTrailingZeros( + (row.duration / traceDuration) * 100 + )}%)`, + [Fields.secondaryStat]: `self: ${toFixedNoTrailingZeros(selfDuration)}ms (${toFixedNoTrailingZeros( + (selfDuration / row.duration) * 100 + )}%)`, + [Fields.color]: selfDuration / traceDuration, + }); + + if (row.parentSpanID) { + edges.push({ + [Fields.id]: row.parentSpanID + '--' + row.spanID, + [Fields.target]: row.spanID, + [Fields.source]: row.parentSpanID, + }); + } + } + + return { nodes, edges }; +} + +function toFixedNoTrailingZeros(n: number) { + return parseFloat(n.toFixed(2)); +} + +/** + * Get the duration of the whole trace as it isn't a part of the response data. + * Note: Seems like this should be the same as just longest span, but this is probably safer. + */ +function findTraceDuration(view: DataFrameView): number { + let traceEndTime = 0; + let traceStartTime = Infinity; + + for (let i = 0; i < view.length; i++) { + const row = view.get(i); + + if (row.startTime < traceStartTime) { + traceStartTime = row.startTime; + } + + if (row.startTime + row.duration > traceEndTime) { + traceEndTime = row.startTime + row.duration; + } + } + + return traceEndTime - traceStartTime; +} + +/** + * Returns a map of the spans with children array for easier processing. + */ +function makeSpanMap(view: DataFrameView): { [id: string]: { span: Row; children: string[] } } { + const spanMap: { [id: string]: { span?: Row; children: string[] } } = {}; + + for (let i = 0; i < view.length; i++) { + const row = view.get(i); + + if (!spanMap[row.spanID]) { + spanMap[row.spanID] = { + // Need copy because of how the view works + span: { ...row }, + children: [], + }; + } else { + spanMap[row.spanID].span = { ...row }; + } + if (!spanMap[row.parentSpanID]) { + spanMap[row.parentSpanID] = { + span: undefined, + children: [row.spanID], + }; + } else { + spanMap[row.parentSpanID].children.push(row.spanID); + } + } + return spanMap as { [id: string]: { span: Row; children: string[] } }; +} + +/** + * Get non overlapping duration of the spans. + */ +function getDuration(rows: Row[]): number { + const ranges = rows.map<[number, number]>((r) => [r.startTime, r.startTime + r.duration]); + ranges.sort((a, b) => a[0] - b[0]); + const mergedRanges = ranges.reduce((acc, range) => { + if (!acc.length) { + return [range]; + } + const tail = acc.slice(-1)[0]; + const [prevStart, prevEnd] = tail; + const [start, end] = range; + if (end < prevEnd) { + // In this case the range is completely inside the prev range so we can just ignore it. + return acc; + } + + if (start > prevEnd) { + // There is no overlap so we can just add it to stack + return [...acc, range]; + } + + // We know there is overlap and current range ends later than previous so we can just extend the range + return [...acc.slice(0, -1), [prevStart, end]] as Array<[number, number]>; + }, [] as Array<[number, number]>); + + return mergedRanges.reduce((acc, range) => { + return acc + (range[1] - range[0]); + }, 0); +} diff --git a/public/app/plugins/datasource/tempo/testResponse.ts b/public/app/plugins/datasource/tempo/testResponse.ts new file mode 100644 index 00000000000..adc86bec8a5 --- /dev/null +++ b/public/app/plugins/datasource/tempo/testResponse.ts @@ -0,0 +1,1850 @@ +import { MutableDataFrame } from '@grafana/data'; + +export const bigResponse = new MutableDataFrame({ + fields: [ + { + name: 'traceID', + values: [ + '04450900759028499335', + '04450900759028499335', + '04450900759028499335', + '04450900759028499335', + '04450900759028499335', + '04450900759028499335', + '04450900759028499335', + '04450900759028499335', + '04450900759028499335', + '04450900759028499335', + '04450900759028499335', + '04450900759028499335', + '04450900759028499335', + '04450900759028499335', + '04450900759028499335', + '04450900759028499335', + '04450900759028499335', + '04450900759028499335', + '04450900759028499335', + '04450900759028499335', + '04450900759028499335', + '04450900759028499335', + '04450900759028499335', + '04450900759028499335', + '04450900759028499335', + '04450900759028499335', + '04450900759028499335', + '04450900759028499335', + '04450900759028499335', + '04450900759028499335', + ], + }, + { + name: 'spanID', + values: [ + '4322526419282105830', + '3095626263385822295', + '6397320272727147889', + '1853508259384889601', + '835290848278351608', + '5850531882244692375', + '663601651643245598', + '7501257416198979329', + '197793019475688138', + '1615811285828848458', + '4574624098918415850', + '7514953483465123028', + '8952478937948910109', + '4807320274580307678', + '3568196851335088191', + '8875894577931816561', + '797350946023907600', + '3198122728676260175', + '2528859115168229623', + '2108752984455624810', + '4343095775387093037', + '8751049139444653283', + '7753188085660872705', + '879502345235818407', + '3139747016493009985', + '7783241608507301178', + '1799838932837912875', + '4045260340056550643', + '4790760741274015949', + '4450900759028499335', + ], + }, + { + name: 'parentSpanID', + values: [ + '3095626263385822295', + '3198122728676260175', + '7501257416198979329', + '663601651643245598', + '5850531882244692375', + '663601651643245598', + '7501257416198979329', + '197793019475688138', + '1615811285828848458', + '3198122728676260175', + '8875894577931816561', + '3568196851335088191', + '4807320274580307678', + '3568196851335088191', + '8875894577931816561', + '797350946023907600', + '3198122728676260175', + '1799838932837912875', + '4343095775387093037', + '4343095775387093037', + '8751049139444653283', + '1799838932837912875', + '3139747016493009985', + '3139747016493009985', + '7783241608507301178', + '1799838932837912875', + '4045260340056550643', + '4790760741274015949', + '4450900759028499335', + '', + ], + }, + { + name: 'operationName', + values: [ + 'store.validateQueryTimeRange', + 'store.validateQuery', + 'cachingIndexClient.cacheFetch', + 'Shipper.Uploads.Query', + 'Shipper.Downloads.Table.MultiQueries', + 'Shipper.Downloads.Query', + 'QUERY', + 'store.lookupEntriesByQueries', + 'Store.lookupIdsByMetricNameMatcher', + 'SeriesStore.lookupSeriesByMetricNameMatchers', + 'cachingIndexClient.cacheFetch', + 'Shipper.Uploads.Query', + 'Shipper.Downloads.Table.MultiQueries', + 'Shipper.Downloads.Query', + 'QUERY', + 'store.lookupEntriesByQueries', + 'SeriesStore.lookupChunksBySeries', + 'SeriesStore.GetChunkRefs', + 'Fetcher.processCacheResponse', + 'GetParallelChunks', + 'ChunkStore.FetchChunks', + 'LokiStore.fetchLazyChunks', + 'Fetcher.processCacheResponse', + 'GetParallelChunks', + 'ChunkStore.FetchChunks', + 'LokiStore.fetchLazyChunks', + '/logproto.Querier/Query', + '/logproto.Querier/Query', + 'query.Exec', + 'HTTP GET - loki_api_v1_query_range', + ], + }, + { + name: 'serviceName', + values: [ + 'loki-all', + 'loki-all', + 'loki-all', + 'loki-all', + 'loki-all', + 'loki-all', + 'loki-all', + 'loki-all', + 'loki-all', + 'loki-all', + 'loki-all', + 'loki-all', + 'loki-all', + 'loki-all', + 'loki-all', + 'loki-all', + 'loki-all', + 'loki-all', + 'loki-all', + 'loki-all', + 'loki-all', + 'loki-all', + 'loki-all', + 'loki-all', + 'loki-all', + 'loki-all', + 'loki-all', + 'loki-all', + 'loki-all', + 'loki-all', + ], + }, + { + name: 'serviceTags', + values: [ + [ + { + value: 'loki-all', + key: 'service.name', + }, + { + value: 'Jaeger-Go-2.25.0', + key: 'opencensus.exporterversion', + }, + { + value: '708c78ea08c1', + key: 'host.hostname', + }, + { + value: '172.18.0.3', + key: 'ip', + }, + { + value: '632583de9a4a497b', + key: 'client-uuid', + }, + ], + [ + { + value: 'loki-all', + key: 'service.name', + }, + { + value: 'Jaeger-Go-2.25.0', + key: 'opencensus.exporterversion', + }, + { + value: '708c78ea08c1', + key: 'host.hostname', + }, + { + value: '172.18.0.3', + key: 'ip', + }, + { + value: '632583de9a4a497b', + key: 'client-uuid', + }, + ], + [ + { + value: 'loki-all', + key: 'service.name', + }, + { + value: 'Jaeger-Go-2.25.0', + key: 'opencensus.exporterversion', + }, + { + value: '708c78ea08c1', + key: 'host.hostname', + }, + { + value: '172.18.0.3', + key: 'ip', + }, + { + value: '632583de9a4a497b', + key: 'client-uuid', + }, + ], + [ + { + value: 'loki-all', + key: 'service.name', + }, + { + value: 'Jaeger-Go-2.25.0', + key: 'opencensus.exporterversion', + }, + { + value: '708c78ea08c1', + key: 'host.hostname', + }, + { + value: '172.18.0.3', + key: 'ip', + }, + { + value: '632583de9a4a497b', + key: 'client-uuid', + }, + ], + [ + { + value: 'loki-all', + key: 'service.name', + }, + { + value: 'Jaeger-Go-2.25.0', + key: 'opencensus.exporterversion', + }, + { + value: '708c78ea08c1', + key: 'host.hostname', + }, + { + value: '172.18.0.3', + key: 'ip', + }, + { + value: '632583de9a4a497b', + key: 'client-uuid', + }, + ], + [ + { + value: 'loki-all', + key: 'service.name', + }, + { + value: 'Jaeger-Go-2.25.0', + key: 'opencensus.exporterversion', + }, + { + value: '708c78ea08c1', + key: 'host.hostname', + }, + { + value: '172.18.0.3', + key: 'ip', + }, + { + value: '632583de9a4a497b', + key: 'client-uuid', + }, + ], + [ + { + value: 'loki-all', + key: 'service.name', + }, + { + value: 'Jaeger-Go-2.25.0', + key: 'opencensus.exporterversion', + }, + { + value: '708c78ea08c1', + key: 'host.hostname', + }, + { + value: '172.18.0.3', + key: 'ip', + }, + { + value: '632583de9a4a497b', + key: 'client-uuid', + }, + ], + [ + { + value: 'loki-all', + key: 'service.name', + }, + { + value: 'Jaeger-Go-2.25.0', + key: 'opencensus.exporterversion', + }, + { + value: '708c78ea08c1', + key: 'host.hostname', + }, + { + value: '172.18.0.3', + key: 'ip', + }, + { + value: '632583de9a4a497b', + key: 'client-uuid', + }, + ], + [ + { + value: 'loki-all', + key: 'service.name', + }, + { + value: 'Jaeger-Go-2.25.0', + key: 'opencensus.exporterversion', + }, + { + value: '708c78ea08c1', + key: 'host.hostname', + }, + { + value: '172.18.0.3', + key: 'ip', + }, + { + value: '632583de9a4a497b', + key: 'client-uuid', + }, + ], + [ + { + value: 'loki-all', + key: 'service.name', + }, + { + value: 'Jaeger-Go-2.25.0', + key: 'opencensus.exporterversion', + }, + { + value: '708c78ea08c1', + key: 'host.hostname', + }, + { + value: '172.18.0.3', + key: 'ip', + }, + { + value: '632583de9a4a497b', + key: 'client-uuid', + }, + ], + [ + { + value: 'loki-all', + key: 'service.name', + }, + { + value: 'Jaeger-Go-2.25.0', + key: 'opencensus.exporterversion', + }, + { + value: '708c78ea08c1', + key: 'host.hostname', + }, + { + value: '172.18.0.3', + key: 'ip', + }, + { + value: '632583de9a4a497b', + key: 'client-uuid', + }, + ], + [ + { + value: 'loki-all', + key: 'service.name', + }, + { + value: 'Jaeger-Go-2.25.0', + key: 'opencensus.exporterversion', + }, + { + value: '708c78ea08c1', + key: 'host.hostname', + }, + { + value: '172.18.0.3', + key: 'ip', + }, + { + value: '632583de9a4a497b', + key: 'client-uuid', + }, + ], + [ + { + value: 'loki-all', + key: 'service.name', + }, + { + value: 'Jaeger-Go-2.25.0', + key: 'opencensus.exporterversion', + }, + { + value: '708c78ea08c1', + key: 'host.hostname', + }, + { + value: '172.18.0.3', + key: 'ip', + }, + { + value: '632583de9a4a497b', + key: 'client-uuid', + }, + ], + [ + { + value: 'loki-all', + key: 'service.name', + }, + { + value: 'Jaeger-Go-2.25.0', + key: 'opencensus.exporterversion', + }, + { + value: '708c78ea08c1', + key: 'host.hostname', + }, + { + value: '172.18.0.3', + key: 'ip', + }, + { + value: '632583de9a4a497b', + key: 'client-uuid', + }, + ], + [ + { + value: 'loki-all', + key: 'service.name', + }, + { + value: 'Jaeger-Go-2.25.0', + key: 'opencensus.exporterversion', + }, + { + value: '708c78ea08c1', + key: 'host.hostname', + }, + { + value: '172.18.0.3', + key: 'ip', + }, + { + value: '632583de9a4a497b', + key: 'client-uuid', + }, + ], + [ + { + value: 'loki-all', + key: 'service.name', + }, + { + value: 'Jaeger-Go-2.25.0', + key: 'opencensus.exporterversion', + }, + { + value: '708c78ea08c1', + key: 'host.hostname', + }, + { + value: '172.18.0.3', + key: 'ip', + }, + { + value: '632583de9a4a497b', + key: 'client-uuid', + }, + ], + [ + { + value: 'loki-all', + key: 'service.name', + }, + { + value: 'Jaeger-Go-2.25.0', + key: 'opencensus.exporterversion', + }, + { + value: '708c78ea08c1', + key: 'host.hostname', + }, + { + value: '172.18.0.3', + key: 'ip', + }, + { + value: '632583de9a4a497b', + key: 'client-uuid', + }, + ], + [ + { + value: 'loki-all', + key: 'service.name', + }, + { + value: 'Jaeger-Go-2.25.0', + key: 'opencensus.exporterversion', + }, + { + value: '708c78ea08c1', + key: 'host.hostname', + }, + { + value: '172.18.0.3', + key: 'ip', + }, + { + value: '632583de9a4a497b', + key: 'client-uuid', + }, + ], + [ + { + value: 'loki-all', + key: 'service.name', + }, + { + value: 'Jaeger-Go-2.25.0', + key: 'opencensus.exporterversion', + }, + { + value: '708c78ea08c1', + key: 'host.hostname', + }, + { + value: '172.18.0.3', + key: 'ip', + }, + { + value: '632583de9a4a497b', + key: 'client-uuid', + }, + ], + [ + { + value: 'loki-all', + key: 'service.name', + }, + { + value: 'Jaeger-Go-2.25.0', + key: 'opencensus.exporterversion', + }, + { + value: '708c78ea08c1', + key: 'host.hostname', + }, + { + value: '172.18.0.3', + key: 'ip', + }, + { + value: '632583de9a4a497b', + key: 'client-uuid', + }, + ], + [ + { + value: 'loki-all', + key: 'service.name', + }, + { + value: 'Jaeger-Go-2.25.0', + key: 'opencensus.exporterversion', + }, + { + value: '708c78ea08c1', + key: 'host.hostname', + }, + { + value: '172.18.0.3', + key: 'ip', + }, + { + value: '632583de9a4a497b', + key: 'client-uuid', + }, + ], + [ + { + value: 'loki-all', + key: 'service.name', + }, + { + value: 'Jaeger-Go-2.25.0', + key: 'opencensus.exporterversion', + }, + { + value: '708c78ea08c1', + key: 'host.hostname', + }, + { + value: '172.18.0.3', + key: 'ip', + }, + { + value: '632583de9a4a497b', + key: 'client-uuid', + }, + ], + [ + { + value: 'loki-all', + key: 'service.name', + }, + { + value: 'Jaeger-Go-2.25.0', + key: 'opencensus.exporterversion', + }, + { + value: '708c78ea08c1', + key: 'host.hostname', + }, + { + value: '172.18.0.3', + key: 'ip', + }, + { + value: '632583de9a4a497b', + key: 'client-uuid', + }, + ], + [ + { + value: 'loki-all', + key: 'service.name', + }, + { + value: 'Jaeger-Go-2.25.0', + key: 'opencensus.exporterversion', + }, + { + value: '708c78ea08c1', + key: 'host.hostname', + }, + { + value: '172.18.0.3', + key: 'ip', + }, + { + value: '632583de9a4a497b', + key: 'client-uuid', + }, + ], + [ + { + value: 'loki-all', + key: 'service.name', + }, + { + value: 'Jaeger-Go-2.25.0', + key: 'opencensus.exporterversion', + }, + { + value: '708c78ea08c1', + key: 'host.hostname', + }, + { + value: '172.18.0.3', + key: 'ip', + }, + { + value: '632583de9a4a497b', + key: 'client-uuid', + }, + ], + [ + { + value: 'loki-all', + key: 'service.name', + }, + { + value: 'Jaeger-Go-2.25.0', + key: 'opencensus.exporterversion', + }, + { + value: '708c78ea08c1', + key: 'host.hostname', + }, + { + value: '172.18.0.3', + key: 'ip', + }, + { + value: '632583de9a4a497b', + key: 'client-uuid', + }, + ], + [ + { + value: 'loki-all', + key: 'service.name', + }, + { + value: 'Jaeger-Go-2.25.0', + key: 'opencensus.exporterversion', + }, + { + value: '708c78ea08c1', + key: 'host.hostname', + }, + { + value: '172.18.0.3', + key: 'ip', + }, + { + value: '632583de9a4a497b', + key: 'client-uuid', + }, + ], + [ + { + value: 'loki-all', + key: 'service.name', + }, + { + value: 'Jaeger-Go-2.25.0', + key: 'opencensus.exporterversion', + }, + { + value: '708c78ea08c1', + key: 'host.hostname', + }, + { + value: '172.18.0.3', + key: 'ip', + }, + { + value: '632583de9a4a497b', + key: 'client-uuid', + }, + ], + [ + { + value: 'loki-all', + key: 'service.name', + }, + { + value: 'Jaeger-Go-2.25.0', + key: 'opencensus.exporterversion', + }, + { + value: '708c78ea08c1', + key: 'host.hostname', + }, + { + value: '172.18.0.3', + key: 'ip', + }, + { + value: '632583de9a4a497b', + key: 'client-uuid', + }, + ], + [ + { + value: 'loki-all', + key: 'service.name', + }, + { + value: 'Jaeger-Go-2.25.0', + key: 'opencensus.exporterversion', + }, + { + value: '708c78ea08c1', + key: 'host.hostname', + }, + { + value: '172.18.0.3', + key: 'ip', + }, + { + value: '632583de9a4a497b', + key: 'client-uuid', + }, + ], + ], + }, + { + name: 'startTime', + values: [ + 1619712655875.4539, + 1619712655875.4502, + 1619712655875.592, + 1619712655875.653, + 1619712655875.731, + 1619712655875.712, + 1619712655875.6428, + 1619712655875.5771, + 1619712655875.5168, + 1619712655875.488, + 1619712655875.939, + 1619712655875.959, + 1619712655876.0051, + 1619712655875.991, + 1619712655875.9539, + 1619712655875.9338, + 1619712655875.917, + 1619712655875.442, + 1619712655876.365, + 1619712655876.3809, + 1619712655876.359, + 1619712655876.331, + 1619712655876.62, + 1619712655876.629, + 1619712655876.616, + 1619712655876.592, + 1619712655875.052, + 1619712655874.819, + 1619712655874.7021, + 1619712655874.591, + ], + }, + { + name: 'duration', + values: [ + 0.004, + 0.016, + 0.039, + 0.047, + 0.063, + 0.087, + 0.163, + 0.303, + 0.384, + 0.421, + 0.012, + 0.021, + 0.033, + 0.048, + 0.092, + 0.169, + 0.197, + 0.689, + 0.012, + 0.196, + 0.225, + 0.255, + 0.007, + 0.167, + 0.189, + 0.217, + 13.918, + 14.723, + 14.984, + 18.208, + ], + }, + { + name: 'logs', + values: [ + null, + null, + [ + { + timestamp: 1619712655875.631, + fields: [ + { + value: 'debug', + key: 'level', + }, + { + value: 0, + key: 'hits', + }, + { + value: 16, + key: 'misses', + }, + ], + }, + ], + null, + [ + { + timestamp: 1619712655875.738, + fields: [ + { + value: 'debug', + key: 'level', + }, + { + value: 'index_18746', + key: 'table-name', + }, + { + value: 16, + key: 'query-count', + }, + ], + }, + { + timestamp: 1619712655875.773, + fields: [ + { + value: 'debug', + key: 'level', + }, + { + value: 'compactor-1619711145.gz', + key: 'queried-db', + }, + ], + }, + { + timestamp: 1619712655875.794, + fields: [ + { + value: 'debug', + key: 'level', + }, + { + value: '708c78ea08c1-1619516350042748959-1619711100.gz', + key: 'queried-db', + }, + ], + }, + ], + [ + { + timestamp: 1619712655875.719, + fields: [ + { + value: 'debug', + key: 'level', + }, + { + value: 'index_18746', + key: 'table-name', + }, + ], + }, + ], + [ + { + timestamp: 1619712655875.7068, + fields: [ + { + value: 'debug', + key: 'level', + }, + { + value: 'uploads-manager', + key: 'queried', + }, + ], + }, + { + timestamp: 1619712655875.803, + fields: [ + { + value: 'debug', + key: 'level', + }, + { + value: 'downloads-manager', + key: 'queried', + }, + ], + }, + ], + null, + [ + { + timestamp: 1619712655875.536, + fields: [ + { + value: 'debug', + key: 'level', + }, + { + value: 'logs', + key: 'metricName', + }, + { + value: 'compose_project="devenv"', + key: 'matcher', + }, + ], + }, + { + timestamp: 1619712655875.568, + fields: [ + { + value: 'debug', + key: 'level', + }, + { + value: 'compose_project="devenv"', + key: 'matcher', + }, + { + value: 16, + key: 'queries', + }, + ], + }, + { + timestamp: 1619712655875.5762, + fields: [ + { + value: 'debug', + key: 'level', + }, + { + value: 'compose_project="devenv"', + key: 'matcher', + }, + { + value: 16, + key: 'filteredQueries', + }, + ], + }, + { + timestamp: 1619712655875.892, + fields: [ + { + value: 'debug', + key: 'level', + }, + { + value: 'compose_project="devenv"', + key: 'matcher', + }, + { + value: 2, + key: 'entries', + }, + ], + }, + { + timestamp: 1619712655875.9019, + fields: [ + { + value: 'debug', + key: 'level', + }, + { + value: 'compose_project="devenv"', + key: 'matcher', + }, + { + value: 1, + key: 'ids', + }, + ], + }, + ], + [ + { + timestamp: 1619712655875.4958, + fields: [ + { + value: 'debug', + key: 'level', + }, + { + value: 'logs', + key: 'metricName', + }, + { + value: 1, + key: 'matchers', + }, + ], + }, + { + timestamp: 1619712655875.9092, + fields: [ + { + value: 'debug', + key: 'level', + }, + { + value: 'post intersection', + key: 'msg', + }, + { + value: 1, + key: 'ids', + }, + ], + }, + ], + [ + { + timestamp: 1619712655875.9512, + fields: [ + { + value: 'debug', + key: 'level', + }, + { + value: 0, + key: 'hits', + }, + { + value: 1, + key: 'misses', + }, + ], + }, + ], + null, + [ + { + timestamp: 1619712655876.012, + fields: [ + { + value: 'debug', + key: 'level', + }, + { + value: 'index_18746', + key: 'table-name', + }, + { + value: 1, + key: 'query-count', + }, + ], + }, + { + timestamp: 1619712655876.031, + fields: [ + { + value: 'debug', + key: 'level', + }, + { + value: 'compactor-1619711145.gz', + key: 'queried-db', + }, + ], + }, + { + timestamp: 1619712655876.0378, + fields: [ + { + value: 'debug', + key: 'level', + }, + { + value: '708c78ea08c1-1619516350042748959-1619711100.gz', + key: 'queried-db', + }, + ], + }, + ], + [ + { + timestamp: 1619712655875.999, + fields: [ + { + value: 'debug', + key: 'level', + }, + { + value: 'index_18746', + key: 'table-name', + }, + ], + }, + ], + [ + { + timestamp: 1619712655875.988, + fields: [ + { + value: 'debug', + key: 'level', + }, + { + value: 'uploads-manager', + key: 'queried', + }, + ], + }, + { + timestamp: 1619712655876.0452, + fields: [ + { + value: 'debug', + key: 'level', + }, + { + value: 'downloads-manager', + key: 'queried', + }, + ], + }, + ], + null, + [ + { + timestamp: 1619712655875.925, + fields: [ + { + value: 'debug', + key: 'level', + }, + { + value: 1, + key: 'seriesIDs', + }, + ], + }, + { + timestamp: 1619712655875.9329, + fields: [ + { + value: 'debug', + key: 'level', + }, + { + value: 1, + key: 'queries', + }, + ], + }, + { + timestamp: 1619712655876.1118, + fields: [ + { + value: 'debug', + key: 'level', + }, + { + value: 2, + key: 'entries', + }, + ], + }, + ], + [ + { + timestamp: 1619712655875.4849, + fields: [ + { + value: 'debug', + key: 'level', + }, + { + value: 'logs', + key: 'metric', + }, + ], + }, + { + timestamp: 1619712655875.915, + fields: [ + { + value: 'debug', + key: 'level', + }, + { + value: 1, + key: 'series-ids', + }, + ], + }, + { + timestamp: 1619712655876.12, + fields: [ + { + value: 'debug', + key: 'level', + }, + { + value: 2, + key: 'chunk-ids', + }, + ], + }, + { + timestamp: 1619712655876.131, + fields: [ + { + value: 'debug', + key: 'level', + }, + { + value: 2, + key: 'chunks-post-filtering', + }, + ], + }, + ], + [ + { + timestamp: 1619712655876.375, + fields: [ + { + value: 'debug', + key: 'level', + }, + { + value: 1, + key: 'chunks', + }, + { + value: 0, + key: 'decodeRequests', + }, + { + value: 1, + key: 'missing', + }, + ], + }, + ], + [ + { + timestamp: 1619712655876.384, + fields: [ + { + value: 1, + key: 'chunks requested', + }, + ], + }, + { + timestamp: 1619712655876.577, + fields: [ + { + value: 1, + key: 'chunks fetched', + }, + ], + }, + ], + null, + [ + { + timestamp: 1619712655876.342, + fields: [ + { + value: 'debug', + key: 'level', + }, + { + value: 'loading lazy chunks', + key: 'msg', + }, + { + value: 1, + key: 'chunks', + }, + ], + }, + ], + [ + { + timestamp: 1619712655876.627, + fields: [ + { + value: 'debug', + key: 'level', + }, + { + value: 1, + key: 'chunks', + }, + { + value: 0, + key: 'decodeRequests', + }, + { + value: 1, + key: 'missing', + }, + ], + }, + ], + [ + { + timestamp: 1619712655876.631, + fields: [ + { + value: 1, + key: 'chunks requested', + }, + ], + }, + { + timestamp: 1619712655876.795, + fields: [ + { + value: 1, + key: 'chunks fetched', + }, + ], + }, + ], + null, + [ + { + timestamp: 1619712655876.604, + fields: [ + { + value: 'debug', + key: 'level', + }, + { + value: 'loading lazy chunks', + key: 'msg', + }, + { + value: 1, + key: 'chunks', + }, + ], + }, + ], + null, + null, + [ + { + timestamp: 1619712655889.606, + fields: [ + { + value: 'debug', + key: 'level', + }, + { + value: 1, + key: 'Ingester.TotalReached', + }, + { + value: 1, + key: 'Ingester.TotalChunksMatched', + }, + { + value: 0, + key: 'Ingester.TotalBatches', + }, + { + value: 0, + key: 'Ingester.TotalLinesSent', + }, + { + value: '47 kB', + key: 'Ingester.HeadChunkBytes', + }, + { + value: 424, + key: 'Ingester.HeadChunkLines', + }, + { + value: '219 kB', + key: 'Ingester.DecompressedBytes', + }, + { + value: 1679, + key: 'Ingester.DecompressedLines', + }, + { + value: '124 kB', + key: 'Ingester.CompressedBytes', + }, + { + value: 0, + key: 'Ingester.TotalDuplicates', + }, + { + value: 0, + key: 'Store.TotalChunksRef', + }, + { + value: 0, + key: 'Store.TotalChunksDownloaded', + }, + { + value: '0s', + key: 'Store.ChunksDownloadTime', + }, + { + value: '0 B', + key: 'Store.HeadChunkBytes', + }, + { + value: 0, + key: 'Store.HeadChunkLines', + }, + { + value: '0 B', + key: 'Store.DecompressedBytes', + }, + { + value: 0, + key: 'Store.DecompressedLines', + }, + { + value: '0 B', + key: 'Store.CompressedBytes', + }, + { + value: 0, + key: 'Store.TotalDuplicates', + }, + ], + }, + { + timestamp: 1619712655889.617, + fields: [ + { + value: 'debug', + key: 'level', + }, + { + value: '18 MB', + key: 'Summary.BytesProcessedPerSecond', + }, + { + value: 141753, + key: 'Summary.LinesProcessedPerSecond', + }, + { + value: '266 kB', + key: 'Summary.TotalBytesProcessed', + }, + { + value: 2103, + key: 'Summary.TotalLinesProcessed', + }, + { + value: '14.835651ms', + key: 'Summary.ExecTime', + }, + ], + }, + ], + null, + ], + }, + { + name: 'tags', + values: [ + [ + { + value: 0, + key: 'status.code', + }, + ], + [ + { + value: 0, + key: 'status.code', + }, + ], + [ + { + value: 0, + key: 'status.code', + }, + ], + [ + { + value: 0, + key: 'status.code', + }, + ], + [ + { + value: 0, + key: 'status.code', + }, + ], + [ + { + value: 0, + key: 'status.code', + }, + ], + [ + { + value: 'fake', + key: 'organization', + }, + { + value: 'client', + key: 'span.kind', + }, + { + value: 0, + key: 'status.code', + }, + ], + [ + { + value: 0, + key: 'status.code', + }, + ], + [ + { + value: 0, + key: 'status.code', + }, + ], + [ + { + value: 0, + key: 'status.code', + }, + ], + [ + { + value: 0, + key: 'status.code', + }, + ], + [ + { + value: 0, + key: 'status.code', + }, + ], + [ + { + value: 0, + key: 'status.code', + }, + ], + [ + { + value: 0, + key: 'status.code', + }, + ], + [ + { + value: 'fake', + key: 'organization', + }, + { + value: 'client', + key: 'span.kind', + }, + { + value: 0, + key: 'status.code', + }, + ], + [ + { + value: 0, + key: 'status.code', + }, + ], + [ + { + value: 0, + key: 'status.code', + }, + ], + [ + { + value: 0, + key: 'status.code', + }, + ], + [ + { + value: 0, + key: 'status.code', + }, + ], + [ + { + value: 0, + key: 'status.code', + }, + ], + [ + { + value: 0, + key: 'status.code', + }, + ], + [ + { + value: 0, + key: 'status.code', + }, + ], + [ + { + value: 0, + key: 'status.code', + }, + ], + [ + { + value: 0, + key: 'status.code', + }, + ], + [ + { + value: 0, + key: 'status.code', + }, + ], + [ + { + value: 0, + key: 'status.code', + }, + ], + [ + { + value: 'gRPC', + key: 'component', + }, + { + value: 'server', + key: 'span.kind', + }, + { + value: 0, + key: 'status.code', + }, + ], + [ + { + value: 'gRPC', + key: 'component', + }, + { + value: 'client', + key: 'span.kind', + }, + { + value: 0, + key: 'status.code', + }, + ], + [ + { + value: 0, + key: 'status.code', + }, + ], + [ + { + 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&limit=1000&query=%7Bcompose_project%3D%22devenv%22%7D&start=1619709055000000000&end=1619712656000000000&step=2', + key: 'http.url', + }, + { + value: 'net/http', + key: 'component', + }, + { + value: 'server', + key: 'span.kind', + }, + { + value: 0, + key: 'status.code', + }, + ], + ], + }, + ], +});