mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Explore: show meta data when available (#25798)
* Return valid metadata even if data is empty * Make sure fields is not accessed when there is no data * Meta Info should depend only on meta data * Refactor condition for better reading * Change data to empty array for better condition handling * Remove unecessary checks * Add test for showing metaData even when data is empty
This commit is contained in:
parent
7a11ed91cb
commit
373e869652
@ -8,7 +8,7 @@ import {
|
||||
MutableDataFrame,
|
||||
toDataFrame,
|
||||
} from '@grafana/data';
|
||||
import { dataFrameToLogsModel, dedupLogRows, getSeriesProperties } from './logs_model';
|
||||
import { dataFrameToLogsModel, dedupLogRows, getSeriesProperties, logSeriesToLogsModel } from './logs_model';
|
||||
|
||||
describe('dedupLogRows()', () => {
|
||||
test('should return rows as is when dedup is set to none', () => {
|
||||
@ -596,6 +596,37 @@ describe('dataFrameToLogsModel', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('logSeriesToLogsModel', () => {
|
||||
it('should return correct metaData even if the data is empty', () => {
|
||||
const logSeries: DataFrame[] = [
|
||||
{
|
||||
fields: [],
|
||||
length: 0,
|
||||
refId: 'A',
|
||||
|
||||
meta: {
|
||||
searchWords: ['test'],
|
||||
limit: 1000,
|
||||
stats: [{ displayName: 'Summary: total bytes processed', value: 97048, unit: 'decbytes' }],
|
||||
custom: { lokiQueryStatKey: 'Summary: total bytes processed' },
|
||||
preferredVisualisationType: 'logs',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const metaData = {
|
||||
hasUniqueLabels: false,
|
||||
meta: [
|
||||
{ label: 'Limit', value: '1000 (0 returned)', kind: 1 },
|
||||
{ label: 'Total bytes processed', value: '97 kB', kind: 1 },
|
||||
],
|
||||
rows: [],
|
||||
};
|
||||
|
||||
expect(logSeriesToLogsModel(logSeries)).toMatchObject(metaData);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getSeriesProperties()', () => {
|
||||
it('sets a minimum bucket size', () => {
|
||||
const result = getSeriesProperties([], 2, undefined, 3, 123);
|
||||
|
@ -269,7 +269,7 @@ function separateLogsAndMetrics(dataFrames: DataFrame[]) {
|
||||
const logSeries: DataFrame[] = [];
|
||||
|
||||
for (const dataFrame of dataFrames) {
|
||||
if (isLogsData(dataFrame)) {
|
||||
if (isLogsData(dataFrame) || !dataFrame.fields.length) {
|
||||
logSeries.push(dataFrame);
|
||||
continue;
|
||||
}
|
||||
@ -303,23 +303,29 @@ export function logSeriesToLogsModel(logSeries: DataFrame[]): LogsModel | undefi
|
||||
const allLabels: Labels[] = [];
|
||||
|
||||
// Find the fields we care about and collect all labels
|
||||
const allSeries: LogFields[] = logSeries.map(series => {
|
||||
const fieldCache = new FieldCache(series);
|
||||
const stringField = fieldCache.getFirstFieldOfType(FieldType.string);
|
||||
if (stringField?.labels) {
|
||||
allLabels.push(stringField.labels);
|
||||
}
|
||||
return {
|
||||
series,
|
||||
timeField: fieldCache.getFirstFieldOfType(FieldType.time),
|
||||
timeNanosecondField: fieldCache.hasFieldWithNameAndType('tsNs', FieldType.time)
|
||||
? fieldCache.getFieldByName('tsNs')
|
||||
: undefined,
|
||||
stringField,
|
||||
logLevelField: fieldCache.getFieldByName('level'),
|
||||
idField: getIdField(fieldCache),
|
||||
} as LogFields;
|
||||
});
|
||||
let allSeries: LogFields[] = [];
|
||||
|
||||
if (hasFields(logSeries)) {
|
||||
allSeries = logSeries.map(series => {
|
||||
const fieldCache = new FieldCache(series);
|
||||
const stringField = fieldCache.getFirstFieldOfType(FieldType.string);
|
||||
|
||||
if (stringField?.labels) {
|
||||
allLabels.push(stringField.labels);
|
||||
}
|
||||
|
||||
return {
|
||||
series,
|
||||
timeField: fieldCache.getFirstFieldOfType(FieldType.time),
|
||||
timeNanosecondField: fieldCache.hasFieldWithNameAndType('tsNs', FieldType.time)
|
||||
? fieldCache.getFieldByName('tsNs')
|
||||
: undefined,
|
||||
stringField,
|
||||
logLevelField: fieldCache.getFieldByName('level'),
|
||||
idField: getIdField(fieldCache),
|
||||
} as LogFields;
|
||||
});
|
||||
}
|
||||
|
||||
const commonLabels = allLabels.length > 0 ? findCommonLabels(allLabels) : {};
|
||||
|
||||
@ -445,6 +451,10 @@ export function logSeriesToLogsModel(logSeries: DataFrame[]): LogsModel | undefi
|
||||
};
|
||||
}
|
||||
|
||||
function hasFields(logSeries: DataFrame[]): boolean {
|
||||
return logSeries.some(series => series.fields.length);
|
||||
}
|
||||
|
||||
function getIdField(fieldCache: FieldCache): FieldWithIndex | undefined {
|
||||
const idFieldNames = ['id'];
|
||||
for (const fieldName of idFieldNames) {
|
||||
|
@ -171,9 +171,11 @@ export function getProcessedDataFrames(results?: DataQueryResponseData[]): DataF
|
||||
for (const result of results) {
|
||||
const dataFrame = guessFieldTypes(toDataFrame(result));
|
||||
|
||||
// clear out the cached info
|
||||
for (const field of dataFrame.fields) {
|
||||
field.state = null;
|
||||
if (dataFrame.fields && dataFrame.fields.length) {
|
||||
// clear out the cached info
|
||||
for (const field of dataFrame.fields) {
|
||||
field.state = null;
|
||||
}
|
||||
}
|
||||
|
||||
dataFrames.push(dataFrame);
|
||||
|
@ -225,7 +225,7 @@ export class Logs extends PureComponent<Props, State> {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{hasData && meta && (
|
||||
{meta && (
|
||||
<MetaInfoText
|
||||
metaItems={meta.map(item => {
|
||||
return {
|
||||
|
@ -312,22 +312,36 @@ export function lokiStreamsToDataframes(
|
||||
lokiQueryStatKey: 'Summary: total bytes processed',
|
||||
};
|
||||
|
||||
const meta: QueryResultMeta = {
|
||||
searchWords: getHighlighterExpressionsFromQuery(formatQuery(target.expr)),
|
||||
limit,
|
||||
stats,
|
||||
custom,
|
||||
preferredVisualisationType: 'logs',
|
||||
};
|
||||
|
||||
const series: DataFrame[] = data.map(stream => {
|
||||
const dataFrame = lokiStreamResultToDataFrame(stream, reverse);
|
||||
enhanceDataFrame(dataFrame, config);
|
||||
|
||||
return {
|
||||
...dataFrame,
|
||||
refId: target.refId,
|
||||
meta: {
|
||||
searchWords: getHighlighterExpressionsFromQuery(formatQuery(target.expr)),
|
||||
limit,
|
||||
stats,
|
||||
custom,
|
||||
preferredVisualisationType: 'logs',
|
||||
},
|
||||
meta,
|
||||
};
|
||||
});
|
||||
|
||||
if (stats.length && !data.length) {
|
||||
return [
|
||||
{
|
||||
fields: [],
|
||||
length: 0,
|
||||
refId: target.refId,
|
||||
meta,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
return series;
|
||||
}
|
||||
|
||||
|
@ -107,8 +107,9 @@ export class TablePanel extends Component<Props> {
|
||||
const { data, height, width } = this.props;
|
||||
|
||||
const count = data.series?.length;
|
||||
const hasFields = data.series[0]?.fields.length;
|
||||
|
||||
if (!count || count < 1) {
|
||||
if (!count || !hasFields) {
|
||||
return <div>No data</div>;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user