diff --git a/.betterer.results b/.betterer.results
index e861dbe1415..4018a113db2 100644
--- a/.betterer.results
+++ b/.betterer.results
@@ -3276,8 +3276,7 @@ exports[`better eslint`] = {
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
],
"public/app/features/explore/Logs/LogsMetaRow.tsx:5381": [
- [0, 0, 0, "Styles should be written using objects.", "0"],
- [0, 0, 0, "Unexpected any. Specify a different type.", "1"]
+ [0, 0, 0, "Styles should be written using objects.", "0"]
],
"public/app/features/explore/Logs/LogsNavigation.tsx:5381": [
[0, 0, 0, "Styles should be written using objects.", "0"],
@@ -6019,9 +6018,6 @@ exports[`better eslint`] = {
[0, 0, 0, "Styles should be written using objects.", "6"],
[0, 0, 0, "Styles should be written using objects.", "7"]
],
- "public/app/plugins/panel/logs/LogsPanel.tsx:5381": [
- [0, 0, 0, "Do not use any type assertions.", "0"]
- ],
"public/app/plugins/panel/logs/types.ts:5381": [
[0, 0, 0, "Do not re-export imported variable (\`./panelcfg.gen\`)", "0"]
],
diff --git a/public/app/features/explore/Logs/LogsMetaRow.tsx b/public/app/features/explore/Logs/LogsMetaRow.tsx
index 55f5bdacc02..b6608f69b20 100644
--- a/public/app/features/explore/Logs/LogsMetaRow.tsx
+++ b/public/app/features/explore/Logs/LogsMetaRow.tsx
@@ -13,13 +13,14 @@ import {
transformDataFrame,
DataTransformerConfig,
CustomTransformOperator,
+ Labels,
} from '@grafana/data';
import { DataFrame } from '@grafana/data/';
import { reportInteraction } from '@grafana/runtime';
import { Button, Dropdown, Menu, ToolbarButton, Tooltip, useStyles2 } from '@grafana/ui';
import { downloadDataFrameAsCsv, downloadLogsModelAsTxt } from '../../inspector/utils/download';
-import { LogLabels } from '../../logs/components/LogLabels';
+import { LogLabels, LogLabelsList } from '../../logs/components/LogLabels';
import { MAX_CHARACTERS } from '../../logs/components/LogRowMessage';
import { logRowsToReadableJson } from '../../logs/utils';
import { MetaInfoText, MetaItemProps } from '../MetaInfoText';
@@ -133,7 +134,7 @@ export const LogsMetaRow = React.memo(
logsMetaItem.push(
{
label: 'Showing only selected fields',
- value: renderMetaItem(displayedFields, LogsMetaKind.LabelsMap),
+ value: ,
},
{
label: '',
@@ -195,11 +196,16 @@ export const LogsMetaRow = React.memo(
LogsMetaRow.displayName = 'LogsMetaRow';
-function renderMetaItem(value: any, kind: LogsMetaKind) {
+function renderMetaItem(value: string | number | Labels, kind: LogsMetaKind) {
+ if (typeof value === 'string' || typeof value === 'number') {
+ return <>{value}>;
+ }
if (kind === LogsMetaKind.LabelsMap) {
return ;
- } else if (kind === LogsMetaKind.Error) {
- return {value};
}
- return value;
+ if (kind === LogsMetaKind.Error) {
+ return {value.toString()};
+ }
+ console.error(`Meta type ${typeof value} ${value} not recognized.`);
+ return <>>;
}
diff --git a/public/app/features/logs/components/LogLabels.test.tsx b/public/app/features/logs/components/LogLabels.test.tsx
index e76459f0b65..9bfd070d9a7 100644
--- a/public/app/features/logs/components/LogLabels.test.tsx
+++ b/public/app/features/logs/components/LogLabels.test.tsx
@@ -1,27 +1,35 @@
import { render, screen } from '@testing-library/react';
import React from 'react';
-import { LogLabels } from './LogLabels';
+import { LogLabels, LogLabelsList } from './LogLabels';
describe('', () => {
it('renders notice when no labels are found', () => {
- render();
+ render();
expect(screen.queryByText('(no unique labels)')).toBeInTheDocument();
});
it('renders labels', () => {
render();
- expect(screen.queryByText('bar')).toBeInTheDocument();
- expect(screen.queryByText('42')).toBeInTheDocument();
+ expect(screen.queryByText('foo=bar')).toBeInTheDocument();
+ expect(screen.queryByText('baz=42')).toBeInTheDocument();
});
it('excludes labels with certain names or labels starting with underscore', () => {
render();
- expect(screen.queryByText('bar')).toBeInTheDocument();
- expect(screen.queryByText('42')).not.toBeInTheDocument();
+ expect(screen.queryByText('foo=bar')).toBeInTheDocument();
+ expect(screen.queryByText('level=42')).not.toBeInTheDocument();
expect(screen.queryByText('13')).not.toBeInTheDocument();
});
it('excludes labels with empty string values', () => {
render();
- expect(screen.queryByText('bar')).toBeInTheDocument();
- expect(screen.queryByText('baz')).not.toBeInTheDocument();
+ expect(screen.queryByText('foo=bar')).toBeInTheDocument();
+ expect(screen.queryByText(/baz/)).not.toBeInTheDocument();
+ });
+});
+
+describe('', () => {
+ it('renders labels', () => {
+ render();
+ expect(screen.queryByText('bar')).toBeInTheDocument();
+ expect(screen.queryByText('42')).toBeInTheDocument();
});
});
diff --git a/public/app/features/logs/components/LogLabels.tsx b/public/app/features/logs/components/LogLabels.tsx
index 3f76fda46dd..2305ad7e866 100644
--- a/public/app/features/logs/components/LogLabels.tsx
+++ b/public/app/features/logs/components/LogLabels.tsx
@@ -1,47 +1,90 @@
import { css, cx } from '@emotion/css';
-import React from 'react';
+import React, { useMemo } from 'react';
import { GrafanaTheme2, Labels } from '@grafana/data';
-import { useStyles2 } from '@grafana/ui';
+import { Tooltip, useStyles2 } from '@grafana/ui';
// Levels are already encoded in color, filename is a Loki-ism
const HIDDEN_LABELS = ['level', 'lvl', 'filename'];
interface Props {
labels: Labels;
+ emptyMessage?: string;
}
-export const LogLabels = ({ labels }: Props) => {
+export const LogLabels = React.memo(({ labels, emptyMessage }: Props) => {
const styles = useStyles2(getStyles);
- const displayLabels = Object.keys(labels).filter((label) => !label.startsWith('_') && !HIDDEN_LABELS.includes(label));
+ const displayLabels = useMemo(
+ () =>
+ Object.keys(labels)
+ .filter((label) => !label.startsWith('_') && !HIDDEN_LABELS.includes(label))
+ .sort(),
+ [labels]
+ );
- if (displayLabels.length === 0) {
+ if (displayLabels.length === 0 && emptyMessage) {
return (
- (no unique labels)
+ {emptyMessage}
);
}
return (
- {displayLabels.sort().map((label) => {
+ {displayLabels.map((label) => {
const value = labels[label];
if (!value) {
return;
}
- const tooltip = `${label}: ${value}`;
+ const labelValue = `${label}=${value}`;
return (
-
-
- {value}
-
-
+
+ {labelValue}
+
);
})}
);
-};
+});
+LogLabels.displayName = 'LogLabels';
+
+interface LogLabelsArrayProps {
+ labels: string[];
+}
+
+export const LogLabelsList = React.memo(({ labels }: LogLabelsArrayProps) => {
+ const styles = useStyles2(getStyles);
+ return (
+
+ {labels.map((label) => (
+
+ {label}
+
+ ))}
+
+ );
+});
+LogLabelsList.displayName = 'LogLabelsList';
+
+interface LogLabelProps {
+ styles: Record;
+ tooltip?: string;
+ children: JSX.Element | string;
+}
+
+const LogLabel = React.forwardRef(
+ ({ styles, tooltip, children }: LogLabelProps, ref) => {
+ return (
+
+
+ {children}
+
+
+ );
+ }
+);
+LogLabel.displayName = 'LogLabel';
const getStyles = (theme: GrafanaTheme2) => {
return {
diff --git a/public/app/plugins/panel/logs/LogsPanel.test.tsx b/public/app/plugins/panel/logs/LogsPanel.test.tsx
index 7c3fdbf0726..2230f70dae1 100644
--- a/public/app/plugins/panel/logs/LogsPanel.test.tsx
+++ b/public/app/plugins/panel/logs/LogsPanel.test.tsx
@@ -87,7 +87,7 @@ describe('LogsPanel', () => {
options: { showCommonLabels: true, sortOrder: LogsSortOrder.Descending },
});
expect(await screen.findByText(/common labels:/i)).toBeInTheDocument();
- expect(container.firstChild?.childNodes[0].textContent).toMatch(/^Common labels:common_appcommon_job/);
+ expect(container.firstChild?.childNodes[0].textContent).toMatch(/^Common labels:app=common_appjob=common_job/);
});
it('shows common labels on bottom when ascending sort order', async () => {
const { container } = setup({
@@ -95,7 +95,7 @@ describe('LogsPanel', () => {
options: { showCommonLabels: true, sortOrder: LogsSortOrder.Ascending },
});
expect(await screen.findByText(/common labels:/i)).toBeInTheDocument();
- expect(container.firstChild?.childNodes[0].textContent).toMatch(/Common labels:common_appcommon_job$/);
+ expect(container.firstChild?.childNodes[0].textContent).toMatch(/Common labels:app=common_appjob=common_job$/);
});
it('does not show common labels when showCommonLabels is set to false', async () => {
setup({ data: { series: seriesWithCommonLabels }, options: { showCommonLabels: false } });
diff --git a/public/app/plugins/panel/logs/LogsPanel.tsx b/public/app/plugins/panel/logs/LogsPanel.tsx
index 8069088e533..40aa8dab3f0 100644
--- a/public/app/plugins/panel/logs/LogsPanel.tsx
+++ b/public/app/plugins/panel/logs/LogsPanel.tsx
@@ -39,6 +39,8 @@ interface LogsPermalinkUrlState {
};
}
+const noCommonLabels: Labels = {};
+
export const LogsPanel = ({
data,
timeZone,
@@ -225,7 +227,10 @@ export const LogsPanel = ({
const renderCommonLabels = () => (
Common labels:
-
+
);