mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Made really good progress on loki support in dashboards
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
import _ from 'lodash';
|
||||
import { colors } from '@grafana/ui';
|
||||
|
||||
import { TimeSeries } from 'app/core/core';
|
||||
import { colors, TimeSeries } from '@grafana/ui';
|
||||
import { getThemeColor } from 'app/core/utils/colors';
|
||||
|
||||
/**
|
||||
@@ -341,6 +340,6 @@ export function makeSeriesForLogs(rows: LogRowModel[], intervalMs: number): Time
|
||||
return a[1] - b[1];
|
||||
});
|
||||
|
||||
return new TimeSeries(series);
|
||||
return { datapoints: series.datapoints, target: series.alias, color: series.color };
|
||||
});
|
||||
}
|
||||
|
||||
@@ -216,7 +216,7 @@ export class Explore extends React.PureComponent<ExploreProps> {
|
||||
{showingStartPage && <StartPage onClickExample={this.onClickExample} />}
|
||||
{!showingStartPage && (
|
||||
<>
|
||||
{supportsGraph && <GraphContainer exploreId={exploreId} />}
|
||||
{supportsGraph && !supportsLogs && <GraphContainer exploreId={exploreId} />}
|
||||
{supportsTable && <TableContainer exploreId={exploreId} onClickCell={this.onClickLabel} />}
|
||||
{supportsLogs && (
|
||||
<LogsContainer
|
||||
|
||||
@@ -3,6 +3,8 @@ import React, { PureComponent } from 'react';
|
||||
|
||||
import * as rangeUtil from 'app/core/utils/rangeutil';
|
||||
import { RawTimeRange, Switch } from '@grafana/ui';
|
||||
import TimeSeries from 'app/core/time_series2';
|
||||
|
||||
import {
|
||||
LogsDedupDescription,
|
||||
LogsDedupStrategy,
|
||||
@@ -205,12 +207,13 @@ export default class Logs extends PureComponent<Props, State> {
|
||||
|
||||
// React profiler becomes unusable if we pass all rows to all rows and their labels, using getter instead
|
||||
const getRows = () => processedRows;
|
||||
const timeSeries = data.series.map(series => new TimeSeries(series));
|
||||
|
||||
return (
|
||||
<div className="logs-panel">
|
||||
<div className="logs-panel-graph">
|
||||
<Graph
|
||||
data={data.series}
|
||||
data={timeSeries}
|
||||
height="100px"
|
||||
range={range}
|
||||
id={`explore-logs-graph-${exploreId}`}
|
||||
|
||||
@@ -7,6 +7,17 @@ describe('LokiDatasource', () => {
|
||||
url: 'myloggingurl',
|
||||
};
|
||||
|
||||
const testResp = {
|
||||
data: {
|
||||
streams: [
|
||||
{
|
||||
entries: [{ ts: '2019-02-01T10:27:37.498180581Z', line: 'hello' }],
|
||||
labels: '{}',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
describe('when querying', () => {
|
||||
const backendSrvMock = { datasourceRequest: jest.fn() };
|
||||
|
||||
@@ -17,7 +28,7 @@ describe('LokiDatasource', () => {
|
||||
|
||||
test('should use default max lines when no limit given', () => {
|
||||
const ds = new LokiDatasource(instanceSettings, backendSrvMock, templateSrvMock);
|
||||
backendSrvMock.datasourceRequest = jest.fn();
|
||||
backendSrvMock.datasourceRequest = jest.fn(() => Promise.resolve(testResp));
|
||||
const options = getQueryOptions<LokiQuery>({ targets: [{ expr: 'foo', refId: 'B' }] });
|
||||
|
||||
ds.query(options);
|
||||
@@ -30,7 +41,7 @@ describe('LokiDatasource', () => {
|
||||
const customData = { ...(instanceSettings.jsonData || {}), maxLines: 20 };
|
||||
const customSettings = { ...instanceSettings, jsonData: customData };
|
||||
const ds = new LokiDatasource(customSettings, backendSrvMock, templateSrvMock);
|
||||
backendSrvMock.datasourceRequest = jest.fn();
|
||||
backendSrvMock.datasourceRequest = jest.fn(() => Promise.resolve(testResp));
|
||||
|
||||
const options = getQueryOptions<LokiQuery>({ targets: [{ expr: 'foo', refId: 'B' }] });
|
||||
ds.query(options);
|
||||
@@ -38,6 +49,34 @@ describe('LokiDatasource', () => {
|
||||
expect(backendSrvMock.datasourceRequest.mock.calls.length).toBe(1);
|
||||
expect(backendSrvMock.datasourceRequest.mock.calls[0][0].url).toContain('limit=20');
|
||||
});
|
||||
|
||||
test('should return log streams when resultFormat is undefined', async done => {
|
||||
const ds = new LokiDatasource(instanceSettings, backendSrvMock, templateSrvMock);
|
||||
backendSrvMock.datasourceRequest = jest.fn(() => Promise.resolve(testResp));
|
||||
|
||||
const options = getQueryOptions<LokiQuery>({
|
||||
targets: [{ expr: 'foo', refId: 'B' }],
|
||||
});
|
||||
|
||||
const res = await ds.query(options);
|
||||
|
||||
expect(res.data[0].entries[0].line).toBe('hello');
|
||||
done();
|
||||
});
|
||||
|
||||
test('should return time series when resultFormat is time_series', async done => {
|
||||
const ds = new LokiDatasource(instanceSettings, backendSrvMock, templateSrvMock);
|
||||
backendSrvMock.datasourceRequest = jest.fn(() => Promise.resolve(testResp));
|
||||
|
||||
const options = getQueryOptions<LokiQuery>({
|
||||
targets: [{ expr: 'foo', refId: 'B', resultFormat: 'time_series' }],
|
||||
});
|
||||
|
||||
const res = await ds.query(options);
|
||||
|
||||
expect(res.data[0].datapoints).toBeDefined();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when performing testDataSource', () => {
|
||||
|
||||
@@ -73,10 +73,11 @@ export class LokiDatasource {
|
||||
};
|
||||
}
|
||||
|
||||
query(options: DataQueryOptions<LokiQuery>): Promise<{ data: LogsStream[] }> {
|
||||
async query(options: DataQueryOptions<LokiQuery>) {
|
||||
const queryTargets = options.targets
|
||||
.filter(target => target.expr)
|
||||
.filter(target => target.expr && !target.hide)
|
||||
.map(target => this.prepareQueryTarget(target, options));
|
||||
|
||||
if (queryTargets.length === 0) {
|
||||
return Promise.resolve({ data: [] });
|
||||
}
|
||||
@@ -84,20 +85,29 @@ export class LokiDatasource {
|
||||
const queries = queryTargets.map(target => this._request('/api/prom/query', target));
|
||||
|
||||
return Promise.all(queries).then((results: any[]) => {
|
||||
// Flatten streams from multiple queries
|
||||
const allStreams: LogsStream[] = results.reduce((acc, response, i) => {
|
||||
if (!response) {
|
||||
return acc;
|
||||
const allStreams: LogsStream[] = [];
|
||||
|
||||
for (let i = 0; i < results.length; i++) {
|
||||
const result = results[i];
|
||||
const query = queryTargets[i];
|
||||
|
||||
// add search term to stream & add to array
|
||||
if (result.data) {
|
||||
for (const stream of (result.data.streams || [])) {
|
||||
stream.search = query.regexp;
|
||||
allStreams.push(stream);
|
||||
}
|
||||
const streams: LogsStream[] = response.data.streams || [];
|
||||
// Inject search for match highlighting
|
||||
const search: string = queryTargets[i].regexp;
|
||||
streams.forEach(s => {
|
||||
s.search = search;
|
||||
});
|
||||
return [...acc, ...streams];
|
||||
}, []);
|
||||
}
|
||||
}
|
||||
|
||||
// check resultType
|
||||
if (options.targets[0].resultFormat === 'time_series') {
|
||||
const logs = mergeStreamsToLogs(allStreams, this.maxLines);
|
||||
logs.series = makeSeriesForLogs(logs.rows, options.intervalMs);
|
||||
return { data: logs.series };
|
||||
} else {
|
||||
return { data: allStreams };
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user