diff --git a/public/app/features/explore/Logs/LogsVolumePanel.test.tsx b/public/app/features/explore/Logs/LogsVolumePanel.test.tsx
index fb973f438a3..46bb7db737f 100644
--- a/public/app/features/explore/Logs/LogsVolumePanel.test.tsx
+++ b/public/app/features/explore/Logs/LogsVolumePanel.test.tsx
@@ -25,6 +25,7 @@ function renderPanel(logsVolumeData: DataQueryResponse) {
onHiddenSeriesChanged={() => null}
eventBus={new EventBusSrv()}
allLogsVolumeMaximum={20}
+ annotations={[]}
/>
);
}
diff --git a/public/app/features/explore/Logs/LogsVolumePanel.tsx b/public/app/features/explore/Logs/LogsVolumePanel.tsx
index cd31dcce517..7e42f7dd460 100644
--- a/public/app/features/explore/Logs/LogsVolumePanel.tsx
+++ b/public/app/features/explore/Logs/LogsVolumePanel.tsx
@@ -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 &&
{extraInfoComponent}
}
diff --git a/public/app/features/explore/Logs/LogsVolumePanelList.tsx b/public/app/features/explore/Logs/LogsVolumePanelList.tsx
index 1d513abb3b1..8ca60b4ada8 100644
--- a/public/app/features/explore/Logs/LogsVolumePanelList.tsx
+++ b/public/app/features/explore/Logs/LogsVolumePanelList.tsx
@@ -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}
/>
);
})}
diff --git a/public/app/features/logs/logsModel.test.ts b/public/app/features/logs/logsModel.test.ts
index eaca7a1e9ce..58fa2ec512d 100644
--- a/public/app/features/logs/logsModel.test.ts
+++ b/public/app/features/logs/logsModel.test.ts
@@ -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', () => {
diff --git a/public/app/features/logs/logsModel.ts b/public/app/features/logs/logsModel.ts
index 468406f123d..d49dfd9a414 100644
--- a/public/app/features/logs/logsModel.ts
+++ b/public/app/features/logs/logsModel.ts
@@ -10,6 +10,7 @@ import {
DataQueryResponse,
DataSourceApi,
DataSourceJsonData,
+ DataTopic,
dateTimeFormat,
dateTimeFormatTimeAgo,
DateTimeInput,
@@ -658,6 +659,10 @@ export function queryLogsVolume {
+ // 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);
diff --git a/public/app/plugins/datasource/loki/querySplitting.ts b/public/app/plugins/datasource/loki/querySplitting.ts
index 74b7a14969a..6e00cb159c0 100644
--- a/public/app/plugins/datasource/loki/querySplitting.ts
+++ b/public/app/plugins/datasource/loki/querySplitting.ts
@@ -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,
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): 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++) {