mirror of
https://github.com/prometheus/prometheus.git
synced 2025-08-06 22:27:17 +02:00
UI: More inclusive metadata handling for _count/_sum/_bucket suffixes (#16910)
Although these suffixes always need to be removed before querying metadata for metrics that follow the Prometheus naming best practices, there can also be metrics that don't follow these naming practices and have these suffixes without being part of either a histogram or a summary metric. Fixes https://github.com/prometheus/prometheus/issues/16907 Signed-off-by: Julius Volz <julius.volz@gmail.com>
This commit is contained in:
parent
f8b3fce845
commit
13b55ffc81
@ -128,13 +128,28 @@ const matchingCriteriaList = (
|
|||||||
const SelectorExplainView: FC<SelectorExplainViewProps> = ({ node }) => {
|
const SelectorExplainView: FC<SelectorExplainViewProps> = ({ node }) => {
|
||||||
const baseMetricName = node.name.replace(/(_count|_sum|_bucket)$/, "");
|
const baseMetricName = node.name.replace(/(_count|_sum|_bucket)$/, "");
|
||||||
const { lookbackDelta } = useSettings();
|
const { lookbackDelta } = useSettings();
|
||||||
const { data: metricMeta } = useSuspenseAPIQuery<MetadataResult>({
|
|
||||||
|
// Try to get metadata for the full unchanged metric name first.
|
||||||
|
const { data: fullMetricMeta } = useSuspenseAPIQuery<MetadataResult>({
|
||||||
|
path: `/metadata`,
|
||||||
|
params: {
|
||||||
|
metric: node.name,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Also get prefix-stripped metric metadata in case the metadata only exists for
|
||||||
|
// the histogram / summary base metric name.
|
||||||
|
const { data: baseMetricMeta } = useSuspenseAPIQuery<MetadataResult>({
|
||||||
path: `/metadata`,
|
path: `/metadata`,
|
||||||
params: {
|
params: {
|
||||||
metric: baseMetricName,
|
metric: baseMetricName,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Determine which metadata to use.
|
||||||
|
const metricMeta =
|
||||||
|
fullMetricMeta.data[node.name] ?? baseMetricMeta.data[baseMetricName];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card withBorder>
|
<Card withBorder>
|
||||||
<Text fz="lg" fw={600} mb="md">
|
<Text fz="lg" fw={600} mb="md">
|
||||||
@ -142,17 +157,13 @@ const SelectorExplainView: FC<SelectorExplainViewProps> = ({ node }) => {
|
|||||||
selector
|
selector
|
||||||
</Text>
|
</Text>
|
||||||
<Text fz="sm">
|
<Text fz="sm">
|
||||||
{metricMeta.data === undefined ||
|
{metricMeta === undefined || metricMeta.length < 1 ? (
|
||||||
metricMeta.data[baseMetricName] === undefined ||
|
|
||||||
metricMeta.data[baseMetricName].length < 1 ? (
|
|
||||||
<>No metric metadata found.</>
|
<>No metric metadata found.</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<strong>Metric help</strong>:{" "}
|
<strong>Metric help</strong>: {metricMeta[0].help}
|
||||||
{metricMeta.data[baseMetricName][0].help}
|
|
||||||
<br />
|
<br />
|
||||||
<strong>Metric type</strong>:{" "}
|
<strong>Metric type</strong>: {metricMeta[0].type}
|
||||||
{metricMeta.data[baseMetricName][0].type}
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Text>
|
</Text>
|
||||||
@ -161,7 +172,8 @@ const SelectorExplainView: FC<SelectorExplainViewProps> = ({ node }) => {
|
|||||||
{node.type === nodeType.vectorSelector ? (
|
{node.type === nodeType.vectorSelector ? (
|
||||||
<>
|
<>
|
||||||
This node selects the latest (non-stale) sample value within the
|
This node selects the latest (non-stale) sample value within the
|
||||||
last <span className="promql-code promql-duration">{lookbackDelta}</span>
|
last{" "}
|
||||||
|
<span className="promql-code promql-duration">{lookbackDelta}</span>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
|
@ -1,11 +1,23 @@
|
|||||||
import { FC, useMemo, useState } from "react";
|
import { FC, useMemo, useState } from "react";
|
||||||
import { useSuspenseAPIQuery } from "../../../api/api";
|
import { useSuspenseAPIQuery } from "../../../api/api";
|
||||||
import { MetadataResult } from "../../../api/responseTypes/metadata";
|
import { MetadataResult } from "../../../api/responseTypes/metadata";
|
||||||
import { ActionIcon, CopyButton, Group, Stack, Table, TextInput } from "@mantine/core";
|
import {
|
||||||
|
ActionIcon,
|
||||||
|
CopyButton,
|
||||||
|
Group,
|
||||||
|
Stack,
|
||||||
|
Table,
|
||||||
|
TextInput,
|
||||||
|
} from "@mantine/core";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { Fuzzy } from "@nexucis/fuzzy";
|
import { Fuzzy } from "@nexucis/fuzzy";
|
||||||
import sanitizeHTML from "sanitize-html";
|
import sanitizeHTML from "sanitize-html";
|
||||||
import { IconCheck, IconCodePlus, IconCopy, IconZoomCode } from "@tabler/icons-react";
|
import {
|
||||||
|
IconCheck,
|
||||||
|
IconCodePlus,
|
||||||
|
IconCopy,
|
||||||
|
IconZoomCode,
|
||||||
|
} from "@tabler/icons-react";
|
||||||
import LabelsExplorer from "./LabelsExplorer";
|
import LabelsExplorer from "./LabelsExplorer";
|
||||||
import { useDebouncedValue } from "@mantine/hooks";
|
import { useDebouncedValue } from "@mantine/hooks";
|
||||||
import classes from "./MetricsExplorer.module.css";
|
import classes from "./MetricsExplorer.module.css";
|
||||||
@ -55,10 +67,17 @@ const MetricsExplorer: FC<MetricsExplorerProps> = ({
|
|||||||
return getSearchMatches(debouncedFilterText, metricNames);
|
return getSearchMatches(debouncedFilterText, metricNames);
|
||||||
}, [debouncedFilterText, metricNames]);
|
}, [debouncedFilterText, metricNames]);
|
||||||
|
|
||||||
const getMeta = (m: string) =>
|
const getMeta = (m: string) => {
|
||||||
data.data[m.replace(/(_count|_sum|_bucket)$/, "")] || [
|
return (
|
||||||
|
// First check if the full metric name has metadata (even if it has one of the
|
||||||
|
// histogram/summary suffixes, it may be a metric that is not following naming
|
||||||
|
// conventions, see https://github.com/prometheus/prometheus/issues/16907).
|
||||||
|
data.data[m] ??
|
||||||
|
data.data[m.replace(/(_count|_sum|_bucket)$/, "")] ?? [
|
||||||
{ help: "unknown", type: "unknown", unit: "unknown" },
|
{ help: "unknown", type: "unknown", unit: "unknown" },
|
||||||
];
|
]
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
if (selectedMetric !== null) {
|
if (selectedMetric !== null) {
|
||||||
return (
|
return (
|
||||||
|
@ -664,9 +664,11 @@ export class HybridComplete implements CompleteStrategy {
|
|||||||
.then((metricMetadata) => {
|
.then((metricMetadata) => {
|
||||||
if (metricMetadata) {
|
if (metricMetadata) {
|
||||||
for (const [metricName, node] of metricCompletion) {
|
for (const [metricName, node] of metricCompletion) {
|
||||||
// For histograms and summaries, the metadata is only exposed for the base metric name,
|
// First check if the full metric name has metadata (even if it has one of the
|
||||||
// not separately for the _count, _sum, and _bucket time series.
|
// histogram/summary suffixes, it may be a metric that is not following naming
|
||||||
const metadata = metricMetadata[metricName.replace(/(_count|_sum|_bucket)$/, '')];
|
// conventions, see https://github.com/prometheus/prometheus/issues/16907).
|
||||||
|
// Then fall back to the base metric name if full metadata doesn't exist.
|
||||||
|
const metadata = metricMetadata[metricName] ?? metricMetadata[metricName.replace(/(_count|_sum|_bucket)$/, '')];
|
||||||
if (metadata) {
|
if (metadata) {
|
||||||
if (metadata.length > 1) {
|
if (metadata.length > 1) {
|
||||||
// it means the metricName has different possible helper and type
|
// it means the metricName has different possible helper and type
|
||||||
|
Loading…
Reference in New Issue
Block a user