mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Jaeger: upload json file (#37205)
* Dropzone component * Add file list * Add progress, error and cancelation to filelistitem * Update Dropzone component to support progress Cancelation Retry * Update file name changes * Rename to FileDropzone * FileListItem tests A11y updates for icon buttons Use value formatter from grafana/data * Add tests for FileDropzone Review comments * export FileDropzoneDefaultChildren * Jaeger: Upload trace json * Change primary text when multiple false * styling * Review comments addressed * Add test for upload trace json * Add docs for Jaeger upload * Fix strict type check error * Update docs/sources/datasources/jaeger.md Co-authored-by: achatterjee-grafana <70489351+achatterjee-grafana@users.noreply.github.com> * Minor changes Render all the options from one function. Add graph frames to the result. * Address review comments * Update docs/sources/datasources/jaeger.md Co-authored-by: achatterjee-grafana <70489351+achatterjee-grafana@users.noreply.github.com> * docs: move image Co-authored-by: achatterjee-grafana <70489351+achatterjee-grafana@users.noreply.github.com>
This commit is contained in:
parent
d9d795ef2c
commit
e3dde3510e
@ -32,7 +32,7 @@ This is a configuration for the [trace to logs feature]({{< relref "../explore/t
|
||||
|
||||
- **Data source -** Target data source.
|
||||
- **Tags -** The tags that will be used in the Loki query. Default is `'cluster', 'hostname', 'namespace', 'pod'`.
|
||||
- **Span start time shift -** Shift in the start time for the Loki query based on the span start time. In order to extend to the past, you need to use a negative value. Time units can be used here, for example, 5s, 1m, 3h. The default is 0.
|
||||
- **Span start time shift -** Shift in the start time for the Loki query based on the span start time. In order to extend to the past, you need to use a negative value. Use time interval units like 5s, 1m, 3h. The default is 0.
|
||||
- **Span end time shift -** Shift in the end time for the Loki query based on the span end time. Time units can be used here, for example, 5s, 1m, 3h. The default is 0.
|
||||
|
||||

|
||||
@ -56,6 +56,62 @@ To perform a search, set the query type selector to Search, then use the followi
|
||||
- Max Duration - Filter all traces with a duration lower than the set value. Possible values are `1.2s, 100ms, 500us`.
|
||||
- Limit - Limits the number of traces returned.
|
||||
|
||||
## Upload JSON trace file
|
||||
|
||||
You can upload a JSON file that contains a single trace to visualize it. If the file has multiple traces then the first trace is used for visualization.
|
||||
|
||||
{{< figure src="/static/img/docs/explore/jaeger-upload-json.png" class="docs-image--no-shadow" caption="Screenshot of the Jaeger data source in explore with upload selected" >}}
|
||||
|
||||
Here is an example JSON:
|
||||
|
||||
```json
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"traceID": "2ee9739529395e31",
|
||||
"spans": [
|
||||
{
|
||||
"traceID": "2ee9739529395e31",
|
||||
"spanID": "2ee9739529395e31",
|
||||
"flags": 1,
|
||||
"operationName": "CAS",
|
||||
"references": [],
|
||||
"startTime": 1616095319593196,
|
||||
"duration": 1004,
|
||||
"tags": [
|
||||
{
|
||||
"key": "sampler.type",
|
||||
"type": "string",
|
||||
"value": "const"
|
||||
}
|
||||
],
|
||||
"logs": [],
|
||||
"processID": "p1",
|
||||
"warnings": null
|
||||
}
|
||||
],
|
||||
"processes": {
|
||||
"p1": {
|
||||
"serviceName": "loki-all",
|
||||
"tags": [
|
||||
{
|
||||
"key": "jaeger.version",
|
||||
"type": "string",
|
||||
"value": "Go-2.25.0"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"warnings": null
|
||||
}
|
||||
],
|
||||
"total": 0,
|
||||
"limit": 0,
|
||||
"offset": 0,
|
||||
"errors": null
|
||||
}
|
||||
```
|
||||
|
||||
## Linking Trace ID from logs
|
||||
|
||||
You can link to Jaeger trace from logs in Loki by configuring a derived field with internal link. See the [Derived fields]({{< relref "loki.md#derived-fields" >}}) section in the [Loki data source]({{< relref "loki.md" >}}) documentation for details.
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { css } from '@emotion/css';
|
||||
import { QueryEditorProps } from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { InlineField, InlineFieldRow, Input, RadioButtonGroup } from '@grafana/ui';
|
||||
import { FileDropzone, InlineField, InlineFieldRow, Input, RadioButtonGroup, useTheme2 } from '@grafana/ui';
|
||||
import React from 'react';
|
||||
import { JaegerDatasource } from '../datasource';
|
||||
import { JaegerQuery, JaegerQueryType } from '../types';
|
||||
@ -9,46 +9,70 @@ import { SearchForm } from './SearchForm';
|
||||
|
||||
type Props = QueryEditorProps<JaegerDatasource, JaegerQuery>;
|
||||
|
||||
export function QueryEditor({ datasource, query, onChange }: Props) {
|
||||
export function QueryEditor({ datasource, query, onChange, onRunQuery }: Props) {
|
||||
const theme = useTheme2();
|
||||
|
||||
const renderEditorBody = () => {
|
||||
switch (query.queryType) {
|
||||
case 'search':
|
||||
return <SearchForm datasource={datasource} query={query} onChange={onChange} />;
|
||||
case 'upload':
|
||||
return (
|
||||
<div className={css({ padding: theme.spacing(2) })}>
|
||||
<FileDropzone
|
||||
options={{ multiple: false }}
|
||||
onLoad={(result) => {
|
||||
datasource.uploadedJson = result;
|
||||
onRunQuery();
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
default:
|
||||
return (
|
||||
<InlineFieldRow>
|
||||
<InlineField label="Trace ID" labelWidth={21} grow>
|
||||
<Input
|
||||
aria-label={selectors.components.DataSource.Jaeger.traceIDInput}
|
||||
placeholder="Eg. 4050b8060d659e52"
|
||||
value={query.query || ''}
|
||||
onChange={(v) =>
|
||||
onChange({
|
||||
...query,
|
||||
query: v.currentTarget.value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</InlineField>
|
||||
</InlineFieldRow>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={css({ width: '50%' })}>
|
||||
<InlineFieldRow>
|
||||
<InlineField label="Query type">
|
||||
<RadioButtonGroup<JaegerQueryType>
|
||||
options={[
|
||||
{ value: 'search', label: 'Search' },
|
||||
{ value: undefined, label: 'TraceID' },
|
||||
]}
|
||||
value={query.queryType}
|
||||
onChange={(v) =>
|
||||
onChange({
|
||||
...query,
|
||||
queryType: v,
|
||||
})
|
||||
}
|
||||
size="md"
|
||||
/>
|
||||
</InlineField>
|
||||
</InlineFieldRow>
|
||||
{query.queryType === 'search' ? (
|
||||
<SearchForm datasource={datasource} query={query} onChange={onChange} />
|
||||
) : (
|
||||
<>
|
||||
<div className={css({ width: query.queryType === 'upload' ? '100%' : '50%' })}>
|
||||
<InlineFieldRow>
|
||||
<InlineField label="Trace ID" labelWidth={21} grow>
|
||||
<Input
|
||||
aria-label={selectors.components.DataSource.Jaeger.traceIDInput}
|
||||
placeholder="Eg. 4050b8060d659e52"
|
||||
value={query.query || ''}
|
||||
<InlineField label="Query type">
|
||||
<RadioButtonGroup<JaegerQueryType>
|
||||
options={[
|
||||
{ value: 'search', label: 'Search' },
|
||||
{ value: undefined, label: 'TraceID' },
|
||||
{ value: 'upload', label: 'JSON file' },
|
||||
]}
|
||||
value={query.queryType}
|
||||
onChange={(v) =>
|
||||
onChange({
|
||||
...query,
|
||||
query: v.currentTarget.value,
|
||||
queryType: v,
|
||||
})
|
||||
}
|
||||
size="md"
|
||||
/>
|
||||
</InlineField>
|
||||
</InlineFieldRow>
|
||||
)}
|
||||
</div>
|
||||
{renderEditorBody()}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -4,11 +4,12 @@ import { of, throwError } from 'rxjs';
|
||||
import { createFetchResponse } from 'test/helpers/createFetchResponse';
|
||||
import { ALL_OPERATIONS_KEY } from './components/SearchForm';
|
||||
import { JaegerDatasource } from './datasource';
|
||||
import mockJson from './mockJsonResponse.json';
|
||||
import {
|
||||
testResponse,
|
||||
testResponseDataFrameFields,
|
||||
testResponseNodesFields,
|
||||
testResponseEdgesFields,
|
||||
testResponseNodesFields,
|
||||
} from './testResponse';
|
||||
import { JaegerQuery } from './types';
|
||||
|
||||
@ -72,6 +73,21 @@ describe('JaegerDatasource', () => {
|
||||
expect(field.values.length).toBe(0);
|
||||
});
|
||||
|
||||
it('should handle json file upload', async () => {
|
||||
const ds = new JaegerDatasource(defaultSettings);
|
||||
ds.uploadedJson = JSON.stringify(mockJson);
|
||||
const response = await ds
|
||||
.query({
|
||||
...defaultQuery,
|
||||
targets: [{ queryType: 'upload', refId: 'A' }],
|
||||
})
|
||||
.toPromise();
|
||||
const field = response.data[0].fields[0];
|
||||
expect(field.name).toBe('traceID');
|
||||
expect(field.type).toBe(FieldType.string);
|
||||
expect(field.values.length).toBe(2);
|
||||
});
|
||||
|
||||
it('should return search results when the query type is search', async () => {
|
||||
const mock = setupFetchMock({ data: [testResponse] });
|
||||
const ds = new JaegerDatasource(defaultSettings, timeSrvStub);
|
||||
|
@ -21,6 +21,7 @@ import { convertTagsLogfmt } from './util';
|
||||
import { ALL_OPERATIONS_KEY } from './components/SearchForm';
|
||||
|
||||
export class JaegerDatasource extends DataSourceApi<JaegerQuery> {
|
||||
uploadedJson: string | ArrayBuffer | null = null;
|
||||
constructor(private instanceSettings: DataSourceInstanceSettings, private readonly timeSrv: TimeSrv = getTimeSrv()) {
|
||||
super(instanceSettings);
|
||||
}
|
||||
@ -52,6 +53,14 @@ export class JaegerDatasource extends DataSourceApi<JaegerQuery> {
|
||||
);
|
||||
}
|
||||
|
||||
if (target.queryType === 'upload') {
|
||||
if (!this.uploadedJson) {
|
||||
return of({ data: [] });
|
||||
}
|
||||
const traceData = JSON.parse(this.uploadedJson as string).data[0];
|
||||
return of({ data: [createTraceFrame(traceData), ...createGraphFrames(traceData)] });
|
||||
}
|
||||
|
||||
let jaegerQuery = pick(target, ['operation', 'service', 'tags', 'minDuration', 'maxDuration', 'limit']);
|
||||
// remove empty properties
|
||||
jaegerQuery = pickBy(jaegerQuery, identity);
|
||||
|
105
public/app/plugins/datasource/jaeger/mockJsonResponse.json
Normal file
105
public/app/plugins/datasource/jaeger/mockJsonResponse.json
Normal file
@ -0,0 +1,105 @@
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"traceID": "2ee9739529395e31",
|
||||
"spans": [
|
||||
{
|
||||
"traceID": "2ee9739529395e31",
|
||||
"spanID": "2ee9739529395e31",
|
||||
"flags": 1,
|
||||
"operationName": "CAS",
|
||||
"references": [],
|
||||
"startTime": 1616095319593196,
|
||||
"duration": 1004,
|
||||
"tags": [
|
||||
{
|
||||
"key": "sampler.type",
|
||||
"type": "string",
|
||||
"value": "const"
|
||||
},
|
||||
{
|
||||
"key": "sampler.param",
|
||||
"type": "bool",
|
||||
"value": true
|
||||
},
|
||||
{
|
||||
"key": "span.kind",
|
||||
"type": "string",
|
||||
"value": "client"
|
||||
},
|
||||
{
|
||||
"key": "internal.span.format",
|
||||
"type": "string",
|
||||
"value": "proto"
|
||||
}
|
||||
],
|
||||
"logs": [],
|
||||
"processID": "p1",
|
||||
"warnings": null
|
||||
},
|
||||
{
|
||||
"traceID": "2ee9739529395e31",
|
||||
"spanID": "620c647812563b5a",
|
||||
"flags": 1,
|
||||
"operationName": "CAS loop",
|
||||
"references": [
|
||||
{
|
||||
"refType": "CHILD_OF",
|
||||
"traceID": "2ee9739529395e31",
|
||||
"spanID": "2ee9739529395e31"
|
||||
}
|
||||
],
|
||||
"startTime": 1616095319593344,
|
||||
"duration": 738,
|
||||
"tags": [
|
||||
{
|
||||
"key": "span.kind",
|
||||
"type": "string",
|
||||
"value": "client"
|
||||
},
|
||||
{
|
||||
"key": "internal.span.format",
|
||||
"type": "string",
|
||||
"value": "proto"
|
||||
}
|
||||
],
|
||||
"logs": [],
|
||||
"processID": "p1",
|
||||
"warnings": null
|
||||
}
|
||||
],
|
||||
"processes": {
|
||||
"p1": {
|
||||
"serviceName": "loki-all",
|
||||
"tags": [
|
||||
{
|
||||
"key": "jaeger.version",
|
||||
"type": "string",
|
||||
"value": "Go-2.25.0"
|
||||
},
|
||||
{
|
||||
"key": "hostname",
|
||||
"type": "string",
|
||||
"value": "07bb841e14c3"
|
||||
},
|
||||
{
|
||||
"key": "ip",
|
||||
"type": "string",
|
||||
"value": "172.26.0.2"
|
||||
},
|
||||
{
|
||||
"key": "client-uuid",
|
||||
"type": "string",
|
||||
"value": "44344b1d17f4ce30"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"warnings": null
|
||||
}
|
||||
],
|
||||
"total": 0,
|
||||
"limit": 0,
|
||||
"offset": 0,
|
||||
"errors": null
|
||||
}
|
@ -63,4 +63,4 @@ export type JaegerQuery = {
|
||||
limit?: number;
|
||||
} & DataQuery;
|
||||
|
||||
export type JaegerQueryType = 'search';
|
||||
export type JaegerQueryType = 'search' | 'upload';
|
||||
|
Loading…
Reference in New Issue
Block a user