Loki Logs volume: Added a query splitting loading indicator to the Logs Volume graph (#79681)

* Logs volume: distinguish annotations from time series frames

* Update unit tests

* Update unit tests

* Formatting
This commit is contained in:
Matias Chomicki
2023-12-19 14:33:18 +02:00
committed by GitHub
parent 8c7639738d
commit c58ed41d50
6 changed files with 100 additions and 9 deletions
@@ -25,6 +25,7 @@ function renderPanel(logsVolumeData: DataQueryResponse) {
onHiddenSeriesChanged={() => null}
eventBus={new EventBusSrv()}
allLogsVolumeMaximum={20}
annotations={[]}
/>
);
}
@@ -10,6 +10,7 @@ import {
TimeZone,
EventBus,
GrafanaTheme2,
DataFrame,
} from '@grafana/data';
import { Icon, Tooltip, TooltipDisplayMode, useStyles2, useTheme2 } from '@grafana/ui';
@@ -27,6 +28,7 @@ type Props = {
onLoadLogsVolume: () => void;
onHiddenSeriesChanged: (hiddenSeries: string[]) => void;
eventBus: EventBus;
annotations: DataFrame[];
};
export function LogsVolumePanel(props: Props) {
@@ -80,6 +82,7 @@ export function LogsVolumePanel(props: Props) {
anchorToZero
yAxisMaximum={allLogsVolumeMaximum}
eventBus={props.eventBus}
annotations={props.annotations}
/>
{extraInfoComponent && <div className={styles.extraInfoContainer}>{extraInfoComponent}</div>}
</div>
@@ -6,6 +6,7 @@ import {
AbsoluteTimeRange,
DataFrame,
DataQueryResponse,
DataTopic,
EventBus,
GrafanaTheme2,
LoadingState,
@@ -49,9 +50,13 @@ export const LogsVolumePanelList = ({
logVolumes,
maximumValue: allLogsVolumeMaximumValue,
maximumRange: allLogsVolumeMaximumRange,
annotations,
} = useMemo(() => {
let maximumValue = -Infinity;
const sorted = sortBy(logsVolumeData?.data || [], 'meta.custom.datasourceName');
const data = logsVolumeData?.data.filter((frame: DataFrame) => frame.meta?.dataTopic !== DataTopic.Annotations);
const annotations =
logsVolumeData?.data.filter((frame: DataFrame) => frame.meta?.dataTopic === DataTopic.Annotations) || [];
const sorted = sortBy(data || [], 'meta.custom.datasourceName');
const grouped = groupBy(sorted, 'meta.custom.datasourceName');
const logVolumes = mapValues(grouped, (value) => {
const mergedData = mergeLogsVolumeDataFrames(value);
@@ -63,6 +68,7 @@ export const LogsVolumePanelList = ({
maximumValue,
maximumRange,
logVolumes,
annotations,
};
}, [logsVolumeData]);
@@ -127,6 +133,7 @@ export const LogsVolumePanelList = ({
// TODO: Support filtering level from multiple log levels
onHiddenSeriesChanged={numberOfLogVolumes > 1 ? () => {} : onHiddenSeriesChanged}
eventBus={eventBus}
annotations={annotations}
/>
);
})}
@@ -1,10 +1,12 @@
import { Observable } from 'rxjs';
import {
arrayToDataFrame,
DataFrame,
DataQuery,
DataQueryRequest,
DataQueryResponse,
DataTopic,
dateTimeParse,
FieldType,
LoadingState,
@@ -1282,6 +1284,33 @@ describe('logs volume', () => {
]);
}
function setupLogsVolumeWithAnnotations() {
const resultAFrame1 = createFrame({ app: 'app01' }, [100, 200, 300], [5, 5, 5], 'A');
const loadingFrame = arrayToDataFrame([
{
time: 100,
timeEnd: 200,
isRegion: true,
color: 'rgba(120, 120, 120, 0.1)',
},
]);
loadingFrame.name = 'annotation';
loadingFrame.meta = {
dataTopic: DataTopic.Annotations,
};
datasource = new MockObservableDataSourceApi('loki', [
{
state: LoadingState.Streaming,
data: [resultAFrame1, loadingFrame],
},
{
state: LoadingState.Done,
data: [resultAFrame1],
},
]);
}
function setupErrorResponse() {
datasource = new MockObservableDataSourceApi('loki', [], undefined, 'Error message');
}
@@ -1363,6 +1392,55 @@ describe('logs volume', () => {
]);
});
});
it('handles annotations in responses', async () => {
setup(setupLogsVolumeWithAnnotations);
const logVolumeCustomMeta: LogsVolumeCustomMetaData = {
sourceQuery: { refId: 'A', target: 'volume query 1' } as DataQuery,
datasourceName: 'loki',
logsVolumeType: LogsVolumeType.FullRange,
absoluteRange: {
from: FROM.valueOf(),
to: TO.valueOf(),
},
};
await expect(volumeProvider).toEmitValuesWith((received) => {
expect(received).toContainEqual({ state: LoadingState.Loading, error: undefined, data: [] });
expect(received).toContainEqual({
state: LoadingState.Streaming,
error: undefined,
data: [
expect.objectContaining({
fields: expect.anything(),
meta: {
custom: logVolumeCustomMeta,
},
}),
expect.objectContaining({
fields: expect.anything(),
meta: {
dataTopic: DataTopic.Annotations,
},
name: 'annotation',
}),
],
});
expect(received).toContainEqual({
state: LoadingState.Done,
error: undefined,
data: [
expect.objectContaining({
fields: expect.anything(),
meta: {
custom: logVolumeCustomMeta,
},
}),
],
});
});
});
});
describe('logs sample', () => {
+5
View File
@@ -10,6 +10,7 @@ import {
DataQueryResponse,
DataSourceApi,
DataSourceJsonData,
DataTopic,
dateTimeFormat,
dateTimeFormatTimeAgo,
DateTimeInput,
@@ -658,6 +659,10 @@ export function queryLogsVolume<TQuery extends DataQuery, TOptions extends DataS
} else {
const framesByRefId = groupBy(dataQueryResponse.data, 'refId');
logsVolumeData = dataQueryResponse.data.map((dataFrame) => {
// Separate possible annotations from data frames
if (dataFrame.meta?.dataTopic === DataTopic.Annotations) {
return dataFrame;
}
let sourceRefId = dataFrame.refId || '';
if (sourceRefId.startsWith('log-volume-')) {
sourceRefId = sourceRefId.substr('log-volume-'.length);
@@ -150,17 +150,18 @@ export function runSplitGroupedQueries(datasource: LokiDatasource, requests: Lok
return response;
}
export const LOADING_FRAME_NAME = 'loki-splitting-progress';
function updateLoadingFrame(
response: DataQueryResponse,
request: DataQueryRequest<LokiQuery>,
partition: TimeRange[],
requestN: number
): DataQueryResponse {
if (isLogsQuery(request.targets[0].expr) || isLogsVolumeRequest(request)) {
if (isLogsQuery(request.targets[0].expr)) {
return response;
}
const loadingFrameName = 'loki-splitting-progress';
response.data = response.data.filter((frame) => frame.name !== loadingFrameName);
response.data = response.data.filter((frame) => frame.name !== LOADING_FRAME_NAME);
if (requestN <= 1) {
return response;
@@ -174,7 +175,7 @@ function updateLoadingFrame(
color: 'rgba(120, 120, 120, 0.1)',
},
]);
loadingFrame.name = loadingFrameName;
loadingFrame.name = LOADING_FRAME_NAME;
loadingFrame.meta = {
dataTopic: DataTopic.Annotations,
};
@@ -184,10 +185,6 @@ function updateLoadingFrame(
return response;
}
function isLogsVolumeRequest(request: DataQueryRequest<LokiQuery>): boolean {
return request.targets.some((target) => target.refId.startsWith('log-volume'));
}
function getNextRequestPointers(requests: LokiGroupedRequest[], requestGroup: number, requestN: number) {
// There's a pending request from the next group:
for (let i = requestGroup + 1; i < requests.length; i++) {