mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Explore: Add trace UI to show traces from tracing datasources (#23047)
* Add integration with Jeager Add Jaeger datasource and modify derived fields in loki to allow for opening a trace in Jager in separate split. Modifies build so that this branch docker images are pushed to docker hub Add a traceui dir with docker-compose and provision files for demoing.:wq * Enable docker logger plugin to send logs to loki * Add placeholder zipkin datasource * Fixed rebase issues, added enhanceDataFrame to non-legacy code path * Trace selector for jaeger query field * Fix logs default mode for Loki * Fix loading jaeger query field services on split * Updated grafana image in traceui/compose file * Fix prettier error * Hide behind feature flag, clean up unused code. * Fix tests * Fix tests * Cleanup code and review feedback * Remove traceui directory * Remove circle build changes * Fix feature toggles object * Fix merge issues * Add trace ui in Explore * WIP * WIP * WIP * Make jaeger datasource return trace data instead of link * Allow js in jest tests * Return data from Jaeger datasource * Take yarn.lock from master * Fix missing component * Update yarn lock * Fix some ts and lint errors * Fix merge * Fix type errors * Make tests pass again * Add tests * Fix es5 compatibility Co-authored-by: David Kaltschmidt <david.kaltschmidt@gmail.com>
This commit is contained in:
88
public/app/plugins/datasource/jaeger/datasource.test.ts
Normal file
88
public/app/plugins/datasource/jaeger/datasource.test.ts
Normal file
@@ -0,0 +1,88 @@
|
||||
import { JaegerDatasource, JaegerQuery } from './datasource';
|
||||
import { DataQueryRequest, DataSourceInstanceSettings, FieldType, PluginType } from '@grafana/data';
|
||||
import { BackendSrv, BackendSrvRequest, getBackendSrv, setBackendSrv } from '@grafana/runtime';
|
||||
|
||||
describe('JaegerDatasource', () => {
|
||||
it('returns trace when queried', async () => {
|
||||
await withMockedBackendSrv(makeBackendSrvMock('12345'), async () => {
|
||||
const ds = new JaegerDatasource(defaultSettings);
|
||||
const response = await ds.query(defaultQuery).toPromise();
|
||||
const field = response.data[0].fields[0];
|
||||
expect(field.name).toBe('trace');
|
||||
expect(field.type).toBe(FieldType.trace);
|
||||
expect(field.values.get(0)).toEqual({
|
||||
traceId: '12345',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('returns empty response if trace id is not specified', async () => {
|
||||
const ds = new JaegerDatasource(defaultSettings);
|
||||
const response = await ds
|
||||
.query({
|
||||
...defaultQuery,
|
||||
targets: [],
|
||||
})
|
||||
.toPromise();
|
||||
const field = response.data[0].fields[0];
|
||||
expect(field.name).toBe('trace');
|
||||
expect(field.type).toBe(FieldType.trace);
|
||||
expect(field.values.length).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
function makeBackendSrvMock(traceId: string) {
|
||||
return {
|
||||
datasourceRequest(options: BackendSrvRequest): Promise<any> {
|
||||
expect(options.url.substr(options.url.length - 17, options.url.length)).toBe(`/api/traces/${traceId}`);
|
||||
return Promise.resolve({
|
||||
data: {
|
||||
data: [
|
||||
{
|
||||
traceId,
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
},
|
||||
} as any;
|
||||
}
|
||||
|
||||
async function withMockedBackendSrv(srv: BackendSrv, fn: () => Promise<void>) {
|
||||
const oldSrv = getBackendSrv();
|
||||
setBackendSrv(srv);
|
||||
await fn();
|
||||
setBackendSrv(oldSrv);
|
||||
}
|
||||
|
||||
const defaultSettings: DataSourceInstanceSettings = {
|
||||
id: 0,
|
||||
type: 'tracing',
|
||||
name: 'jaeger',
|
||||
meta: {
|
||||
id: 'jaeger',
|
||||
name: 'jaeger',
|
||||
type: PluginType.datasource,
|
||||
info: {} as any,
|
||||
module: '',
|
||||
baseUrl: '',
|
||||
},
|
||||
jsonData: {},
|
||||
};
|
||||
|
||||
const defaultQuery: DataQueryRequest<JaegerQuery> = {
|
||||
requestId: '1',
|
||||
dashboardId: 0,
|
||||
interval: '0',
|
||||
panelId: 0,
|
||||
scopedVars: {},
|
||||
timezone: '',
|
||||
app: 'explore',
|
||||
startTime: 0,
|
||||
targets: [
|
||||
{
|
||||
query: '12345',
|
||||
refId: '1',
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -7,14 +7,15 @@ import {
|
||||
DataQueryRequest,
|
||||
DataQueryResponse,
|
||||
DataQuery,
|
||||
FieldType,
|
||||
} from '@grafana/data';
|
||||
import { getBackendSrv } from '@grafana/runtime';
|
||||
import { Observable, from, of } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
|
||||
import { getTimeSrv } from 'app/features/dashboard/services/TimeSrv';
|
||||
import { DatasourceRequestOptions } from 'app/core/services/backend_srv';
|
||||
import { serializeParams } from '../../../core/utils/fetch';
|
||||
|
||||
import { Observable, from, of } from 'rxjs';
|
||||
import { serializeParams } from 'app/core/utils/fetch';
|
||||
|
||||
export type JaegerQuery = {
|
||||
query: string;
|
||||
@@ -25,7 +26,64 @@ export class JaegerDatasource extends DataSourceApi<JaegerQuery> {
|
||||
super(instanceSettings);
|
||||
}
|
||||
|
||||
_request(apiUrl: string, data?: any, options?: DatasourceRequestOptions): Observable<Record<string, any>> {
|
||||
async metadataRequest(url: string, params?: Record<string, any>) {
|
||||
const res = await this._request(url, params, { silent: true }).toPromise();
|
||||
return res.data.data;
|
||||
}
|
||||
|
||||
query(options: DataQueryRequest<JaegerQuery>): Observable<DataQueryResponse> {
|
||||
// At this moment we expect only one target. In case we somehow change the UI to be able to show multiple
|
||||
// traces at one we need to change this.
|
||||
const id = options.targets[0]?.query;
|
||||
if (id) {
|
||||
// TODO: this api is internal, used in jaeger ui. Officially they have gRPC api that should be used.
|
||||
return this._request(`/api/traces/${id}`).pipe(
|
||||
map(response => {
|
||||
return {
|
||||
data: [
|
||||
new MutableDataFrame({
|
||||
fields: [
|
||||
{
|
||||
name: 'trace',
|
||||
type: FieldType.trace,
|
||||
values: response?.data?.data || [],
|
||||
},
|
||||
],
|
||||
}),
|
||||
],
|
||||
};
|
||||
})
|
||||
);
|
||||
} else {
|
||||
return of({
|
||||
data: [
|
||||
new MutableDataFrame({
|
||||
fields: [
|
||||
{
|
||||
name: 'trace',
|
||||
type: FieldType.trace,
|
||||
values: [],
|
||||
},
|
||||
],
|
||||
}),
|
||||
],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async testDatasource(): Promise<any> {
|
||||
return true;
|
||||
}
|
||||
|
||||
getTimeRange(): { start: number; end: number } {
|
||||
const range = getTimeSrv().timeRange();
|
||||
return {
|
||||
start: getTime(range.from, false),
|
||||
end: getTime(range.to, true),
|
||||
};
|
||||
}
|
||||
|
||||
private _request(apiUrl: string, data?: any, options?: DatasourceRequestOptions): Observable<Record<string, any>> {
|
||||
// Hack for proxying metadata requests
|
||||
const baseUrl = `/api/datasources/proxy/${this.instanceSettings.id}`;
|
||||
const params = data ? serializeParams(data) : '';
|
||||
@@ -37,49 +95,11 @@ export class JaegerDatasource extends DataSourceApi<JaegerQuery> {
|
||||
|
||||
return from(getBackendSrv().datasourceRequest(req));
|
||||
}
|
||||
|
||||
async metadataRequest(url: string, params?: Record<string, any>) {
|
||||
const res = await this._request(url, params, { silent: true }).toPromise();
|
||||
return res.data.data;
|
||||
}
|
||||
|
||||
query(options: DataQueryRequest<JaegerQuery>): Observable<DataQueryResponse> {
|
||||
//http://localhost:16686/search?end=1573338717880000&limit=20&lookback=6h&maxDuration&minDuration&service=app&start=1573317117880000
|
||||
const url =
|
||||
options.targets.length && options.targets[0].query
|
||||
? `${this.instanceSettings.url}/trace/${options.targets[0].query}?uiEmbed=v0`
|
||||
: '';
|
||||
|
||||
return of({
|
||||
data: [
|
||||
new MutableDataFrame({
|
||||
fields: [
|
||||
{
|
||||
name: 'url',
|
||||
values: [url],
|
||||
},
|
||||
],
|
||||
}),
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
async testDatasource(): Promise<any> {
|
||||
return true;
|
||||
}
|
||||
|
||||
getTime(date: string | DateTime, roundUp: boolean) {
|
||||
if (typeof date === 'string') {
|
||||
date = dateMath.parse(date, roundUp);
|
||||
}
|
||||
return date.valueOf() * 1000;
|
||||
}
|
||||
|
||||
getTimeRange(): { start: number; end: number } {
|
||||
const range = getTimeSrv().timeRange();
|
||||
return {
|
||||
start: this.getTime(range.from, false),
|
||||
end: this.getTime(range.to, true),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function getTime(date: string | DateTime, roundUp: boolean) {
|
||||
if (typeof date === 'string') {
|
||||
date = dateMath.parse(date, roundUp);
|
||||
}
|
||||
return date.valueOf() * 1000;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user