mirror of
https://github.com/prometheus/prometheus
synced 2025-01-13 18:33:34 +00:00
Move /targets page discovered labels to expandable section (#12824)
* Move /targets page discovered labels to expandable section The current tooltip for showing the pre-relabeling discovered labels for a target is notoriously unreliable and can get cut off when there are many labels. This PR introduces a (hopefully unobtuse enough) expander/collapser button for the discovered labels of each target, and then the discovered labels are shown in a more persistent way underneath the final target labels, instead of using a tooltip. Fixes https://github.com/prometheus/prometheus/issues/9175#issuecomment-1713074341 Signed-off-by: Julius Volz <julius.volz@gmail.com> * Remove obsolete test snapshot Signed-off-by: Julius Volz <julius.volz@gmail.com> --------- Signed-off-by: Julius Volz <julius.volz@gmail.com>
This commit is contained in:
parent
7c75d233d0
commit
aa7bf083e9
@ -35,12 +35,7 @@ const ScrapePoolContentTable: FC<InfiniteScrollItemsProps<Target>> = ({ items })
|
|||||||
<Badge color={getColor(target.health)}>{target.health.toUpperCase()}</Badge>
|
<Badge color={getColor(target.health)}>{target.health.toUpperCase()}</Badge>
|
||||||
</td>
|
</td>
|
||||||
<td className={styles.labels}>
|
<td className={styles.labels}>
|
||||||
<TargetLabels
|
<TargetLabels discoveredLabels={target.discoveredLabels} labels={target.labels} />
|
||||||
discoveredLabels={target.discoveredLabels}
|
|
||||||
labels={target.labels}
|
|
||||||
scrapePool={target.scrapePool}
|
|
||||||
idx={index}
|
|
||||||
/>
|
|
||||||
</td>
|
</td>
|
||||||
<td className={styles['last-scrape']}>{formatRelative(target.lastScrape, now())}</td>
|
<td className={styles['last-scrape']}>{formatRelative(target.lastScrape, now())}</td>
|
||||||
<td className={styles['scrape-duration']}>
|
<td className={styles['scrape-duration']}>
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
.discovered {
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
@ -17,15 +17,12 @@ describe('targetLabels', () => {
|
|||||||
job: 'node_exporter',
|
job: 'node_exporter',
|
||||||
foo: 'bar',
|
foo: 'bar',
|
||||||
},
|
},
|
||||||
idx: 1,
|
|
||||||
scrapePool: 'cortex/node-exporter_group/0',
|
|
||||||
};
|
};
|
||||||
const targetLabels = shallow(<TargetLabels {...defaultProps} />);
|
const targetLabels = shallow(<TargetLabels {...defaultProps} />);
|
||||||
|
|
||||||
it('renders a div of series labels', () => {
|
it('renders a div of series labels', () => {
|
||||||
const div = targetLabels.find('div').filterWhere((elem) => elem.hasClass('series-labels-container'));
|
const div = targetLabels.find('div').filterWhere((elem) => elem.hasClass('series-labels-container'));
|
||||||
expect(div).toHaveLength(1);
|
expect(div).toHaveLength(1);
|
||||||
expect(div.prop('id')).toEqual('series-labels-cortex/node-exporter_group/0-1');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('wraps each label in a label badge', () => {
|
it('wraps each label in a label badge', () => {
|
||||||
@ -38,15 +35,4 @@ describe('targetLabels', () => {
|
|||||||
});
|
});
|
||||||
expect(targetLabels.find(Badge)).toHaveLength(3);
|
expect(targetLabels.find(Badge)).toHaveLength(3);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders a tooltip for discovered labels', () => {
|
|
||||||
const tooltip = targetLabels.find(Tooltip);
|
|
||||||
expect(tooltip).toHaveLength(1);
|
|
||||||
expect(tooltip.prop('isOpen')).toBe(false);
|
|
||||||
expect(tooltip.prop('target')).toEqual('series-labels-cortex\\/node-exporter_group\\/0-1');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('renders discovered labels', () => {
|
|
||||||
expect(toJson(targetLabels)).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React, { FC, Fragment, useState } from 'react';
|
import { faChevronDown, faChevronUp } from '@fortawesome/free-solid-svg-icons';
|
||||||
import { Badge, Tooltip } from 'reactstrap';
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
import 'css.escape';
|
import React, { FC, useState } from 'react';
|
||||||
import styles from './TargetLabels.module.css';
|
import { Badge, Button } from 'reactstrap';
|
||||||
|
|
||||||
interface Labels {
|
interface Labels {
|
||||||
[key: string]: string;
|
[key: string]: string;
|
||||||
@ -10,21 +10,14 @@ interface Labels {
|
|||||||
export interface TargetLabelsProps {
|
export interface TargetLabelsProps {
|
||||||
discoveredLabels: Labels;
|
discoveredLabels: Labels;
|
||||||
labels: Labels;
|
labels: Labels;
|
||||||
idx: number;
|
|
||||||
scrapePool: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const formatLabels = (labels: Labels): string[] => Object.keys(labels).map((key) => `${key}="${labels[key]}"`);
|
const TargetLabels: FC<TargetLabelsProps> = ({ discoveredLabels, labels }) => {
|
||||||
|
const [showDiscovered, setShowDiscovered] = useState(false);
|
||||||
const TargetLabels: FC<TargetLabelsProps> = ({ discoveredLabels, labels, idx, scrapePool }) => {
|
|
||||||
const [tooltipOpen, setTooltipOpen] = useState(false);
|
|
||||||
|
|
||||||
const toggle = (): void => setTooltipOpen(!tooltipOpen);
|
|
||||||
const id = `series-labels-${scrapePool}-${idx}`;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div id={id} className="series-labels-container">
|
<div className="series-labels-container">
|
||||||
{Object.keys(labels).map((labelName) => {
|
{Object.keys(labels).map((labelName) => {
|
||||||
return (
|
return (
|
||||||
<Badge color="primary" className="mr-1" key={labelName}>
|
<Badge color="primary" className="mr-1" key={labelName}>
|
||||||
@ -32,22 +25,28 @@ const TargetLabels: FC<TargetLabelsProps> = ({ discoveredLabels, labels, idx, sc
|
|||||||
</Badge>
|
</Badge>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
<Button
|
||||||
<Tooltip
|
size="sm"
|
||||||
isOpen={tooltipOpen}
|
color="link"
|
||||||
target={CSS.escape(id)}
|
title={`${showDiscovered ? 'Hide' : 'Show'} discovered (pre-relabeling) labels`}
|
||||||
toggle={toggle}
|
onClick={() => setShowDiscovered(!showDiscovered)}
|
||||||
placement={'right-end'}
|
style={{ fontSize: '0.8rem' }}
|
||||||
style={{ maxWidth: 'none', textAlign: 'left' }}
|
|
||||||
>
|
>
|
||||||
<b>Before relabeling:</b>
|
<FontAwesomeIcon icon={showDiscovered ? faChevronUp : faChevronDown} />
|
||||||
{formatLabels(discoveredLabels).map((s: string, labelIndex: number) => (
|
</Button>
|
||||||
<Fragment key={labelIndex}>
|
</div>
|
||||||
<br />
|
{showDiscovered && (
|
||||||
<span className={styles.discovered}>{s}</span>
|
<>
|
||||||
</Fragment>
|
<div className="mt-3 font-weight-bold">Discovered labels:</div>
|
||||||
|
{Object.keys(discoveredLabels).map((labelName) => (
|
||||||
|
<div key={labelName}>
|
||||||
|
<Badge color="info" className="mr-1">
|
||||||
|
{`${labelName}="${discoveredLabels[labelName]}"`}
|
||||||
|
</Badge>
|
||||||
|
</div>
|
||||||
))}
|
))}
|
||||||
</Tooltip>
|
</>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,81 +0,0 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
||||||
|
|
||||||
exports[`targetLabels renders discovered labels 1`] = `
|
|
||||||
<Fragment>
|
|
||||||
<div
|
|
||||||
className="series-labels-container"
|
|
||||||
id="series-labels-cortex/node-exporter_group/0-1"
|
|
||||||
>
|
|
||||||
<Badge
|
|
||||||
className="mr-1"
|
|
||||||
color="primary"
|
|
||||||
key="instance"
|
|
||||||
pill={false}
|
|
||||||
tag="span"
|
|
||||||
>
|
|
||||||
instance="localhost:9100"
|
|
||||||
</Badge>
|
|
||||||
<Badge
|
|
||||||
className="mr-1"
|
|
||||||
color="primary"
|
|
||||||
key="job"
|
|
||||||
pill={false}
|
|
||||||
tag="span"
|
|
||||||
>
|
|
||||||
job="node_exporter"
|
|
||||||
</Badge>
|
|
||||||
<Badge
|
|
||||||
className="mr-1"
|
|
||||||
color="primary"
|
|
||||||
key="foo"
|
|
||||||
pill={false}
|
|
||||||
tag="span"
|
|
||||||
>
|
|
||||||
foo="bar"
|
|
||||||
</Badge>
|
|
||||||
</div>
|
|
||||||
<Tooltip
|
|
||||||
autohide={true}
|
|
||||||
isOpen={false}
|
|
||||||
placement="right-end"
|
|
||||||
placementPrefix="bs-tooltip"
|
|
||||||
style={
|
|
||||||
Object {
|
|
||||||
"maxWidth": "none",
|
|
||||||
"textAlign": "left",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
target="series-labels-cortex\\\\/node-exporter_group\\\\/0-1"
|
|
||||||
toggle={[Function]}
|
|
||||||
trigger="hover focus"
|
|
||||||
>
|
|
||||||
<b>
|
|
||||||
Before relabeling:
|
|
||||||
</b>
|
|
||||||
<br />
|
|
||||||
<span
|
|
||||||
className="discovered"
|
|
||||||
>
|
|
||||||
__address__="localhost:9100"
|
|
||||||
</span>
|
|
||||||
<br />
|
|
||||||
<span
|
|
||||||
className="discovered"
|
|
||||||
>
|
|
||||||
__metrics_path__="/metrics"
|
|
||||||
</span>
|
|
||||||
<br />
|
|
||||||
<span
|
|
||||||
className="discovered"
|
|
||||||
>
|
|
||||||
__scheme__="http"
|
|
||||||
</span>
|
|
||||||
<br />
|
|
||||||
<span
|
|
||||||
className="discovered"
|
|
||||||
>
|
|
||||||
job="node_exporter"
|
|
||||||
</span>
|
|
||||||
</Tooltip>
|
|
||||||
</Fragment>
|
|
||||||
`;
|
|
Loading…
Reference in New Issue
Block a user