mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-12-01 14:47:38 +00:00
Merge branch 'public-single-node' into pmm-6401-read-prometheus-data-files
This commit is contained in:
commit
0821298471
14 changed files with 96 additions and 62 deletions
|
@ -1,12 +1,12 @@
|
||||||
{
|
{
|
||||||
"files": {
|
"files": {
|
||||||
"main.css": "./static/css/main.07bcc4ad.css",
|
"main.css": "./static/css/main.07bcc4ad.css",
|
||||||
"main.js": "./static/js/main.3e8347de.js",
|
"main.js": "./static/js/main.1293a99e.js",
|
||||||
"static/js/27.939f971b.chunk.js": "./static/js/27.939f971b.chunk.js",
|
"static/js/27.939f971b.chunk.js": "./static/js/27.939f971b.chunk.js",
|
||||||
"index.html": "./index.html"
|
"index.html": "./index.html"
|
||||||
},
|
},
|
||||||
"entrypoints": [
|
"entrypoints": [
|
||||||
"static/css/main.07bcc4ad.css",
|
"static/css/main.07bcc4ad.css",
|
||||||
"static/js/main.3e8347de.js"
|
"static/js/main.1293a99e.js"
|
||||||
]
|
]
|
||||||
}
|
}
|
|
@ -1 +1 @@
|
||||||
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="./favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="VM-UI is a metric explorer for Victoria Metrics"/><link rel="apple-touch-icon" href="./apple-touch-icon.png"/><link rel="icon" type="image/png" sizes="32x32" href="./favicon-32x32.png"><link rel="manifest" href="./manifest.json"/><title>VM UI</title><link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link href="https://fonts.googleapis.com/css2?family=Lato:ital,wght@0,300;0,400;0,700;1,300;1,400;1,700&display=swap" rel="stylesheet"><script src="./dashboards/index.js" type="module"></script><script defer="defer" src="./static/js/main.3e8347de.js"></script><link href="./static/css/main.07bcc4ad.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
|
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="./favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="VM-UI is a metric explorer for Victoria Metrics"/><link rel="apple-touch-icon" href="./apple-touch-icon.png"/><link rel="icon" type="image/png" sizes="32x32" href="./favicon-32x32.png"><link rel="manifest" href="./manifest.json"/><title>VM UI</title><link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link href="https://fonts.googleapis.com/css2?family=Lato:ital,wght@0,300;0,400;0,700;1,300;1,400;1,700&display=swap" rel="stylesheet"><script src="./dashboards/index.js" type="module"></script><script defer="defer" src="./static/js/main.1293a99e.js"></script><link href="./static/css/main.07bcc4ad.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
|
File diff suppressed because one or more lines are too long
|
@ -30,8 +30,8 @@ const GlobalSettings: FC = () => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const [changedServerUrl, setChangedServerUrl] = useState(serverUrl);
|
const [changedServerUrl, setChangedServerUrl] = useState(serverUrl);
|
||||||
|
|
||||||
const setServer = () => {
|
const setServer = (url?: string) => {
|
||||||
dispatch({type: "SET_SERVER", payload: changedServerUrl});
|
dispatch({type: "SET_SERVER", payload: url || changedServerUrl});
|
||||||
handleClose();
|
handleClose();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -63,12 +63,12 @@ const GlobalSettings: FC = () => {
|
||||||
<CloseIcon/>
|
<CloseIcon/>
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</Box>
|
</Box>
|
||||||
<ServerConfigurator setServer={setChangedServerUrl}/>
|
<ServerConfigurator setServer={setChangedServerUrl} onEnter={setServer}/>
|
||||||
<Box display="grid" gridTemplateColumns="auto auto" gap={1} justifyContent="end" mt={4}>
|
<Box display="grid" gridTemplateColumns="auto auto" gap={1} justifyContent="end" mt={4}>
|
||||||
<Button variant="outlined" onClick={handleClose}>
|
<Button variant="outlined" onClick={handleClose}>
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
<Button variant="contained" onClick={setServer}>
|
<Button variant="contained" onClick={() => setServer()}>
|
||||||
apply
|
apply
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
|
@ -2,14 +2,15 @@ import React, {FC, useState} from "preact/compat";
|
||||||
import TextField from "@mui/material/TextField";
|
import TextField from "@mui/material/TextField";
|
||||||
import {useAppState} from "../../../../state/common/StateContext";
|
import {useAppState} from "../../../../state/common/StateContext";
|
||||||
import {ErrorTypes} from "../../../../types";
|
import {ErrorTypes} from "../../../../types";
|
||||||
import {ChangeEvent} from "react";
|
import {ChangeEvent, KeyboardEvent} from "react";
|
||||||
|
|
||||||
export interface ServerConfiguratorProps {
|
export interface ServerConfiguratorProps {
|
||||||
error?: ErrorTypes | string;
|
error?: ErrorTypes | string;
|
||||||
setServer: (url: string) => void
|
setServer: (url: string) => void
|
||||||
|
onEnter: (url: string) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const ServerConfigurator: FC<ServerConfiguratorProps> = ({error, setServer}) => {
|
const ServerConfigurator: FC<ServerConfiguratorProps> = ({error, setServer, onEnter}) => {
|
||||||
|
|
||||||
const {serverUrl} = useAppState();
|
const {serverUrl} = useAppState();
|
||||||
const [changedServerUrl, setChangedServerUrl] = useState(serverUrl);
|
const [changedServerUrl, setChangedServerUrl] = useState(serverUrl);
|
||||||
|
@ -20,10 +21,24 @@ const ServerConfigurator: FC<ServerConfiguratorProps> = ({error, setServer}) =>
|
||||||
setServer(value);
|
setServer(value);
|
||||||
};
|
};
|
||||||
|
|
||||||
return <TextField variant="outlined" fullWidth label="Server URL" value={changedServerUrl || ""}
|
const onKeyDown = (e: KeyboardEvent) => {
|
||||||
|
if (e.key === "Enter") {
|
||||||
|
e.preventDefault();
|
||||||
|
onEnter(changedServerUrl);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return <TextField
|
||||||
|
autoFocus
|
||||||
|
fullWidth
|
||||||
|
variant="outlined"
|
||||||
|
label="Server URL"
|
||||||
|
value={changedServerUrl || ""}
|
||||||
error={error === ErrorTypes.validServer || error === ErrorTypes.emptyServer}
|
error={error === ErrorTypes.validServer || error === ErrorTypes.emptyServer}
|
||||||
inputProps={{style: {fontFamily: "Monospace"}}}
|
inputProps={{style: {fontFamily: "Monospace"}}}
|
||||||
onChange={onChangeServer}/>;
|
onChange={onChangeServer}
|
||||||
|
onKeyDown={onKeyDown}
|
||||||
|
/>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ServerConfigurator;
|
export default ServerConfigurator;
|
||||||
|
|
|
@ -12,6 +12,7 @@ import ListItem from "@mui/material/ListItem";
|
||||||
import ListItemText from "@mui/material/ListItemText";
|
import ListItemText from "@mui/material/ListItemText";
|
||||||
import {useLocation} from "react-router-dom";
|
import {useLocation} from "react-router-dom";
|
||||||
import {getAppModeEnable} from "../../../../utils/app-mode";
|
import {getAppModeEnable} from "../../../../utils/app-mode";
|
||||||
|
import Box from "@mui/material/Box";
|
||||||
|
|
||||||
interface AutoRefreshOption {
|
interface AutoRefreshOption {
|
||||||
seconds: number
|
seconds: number
|
||||||
|
@ -55,12 +56,16 @@ export const ExecutionControls: FC = () => {
|
||||||
setAnchorEl(null);
|
setAnchorEl(null);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleUpdate = () => {
|
||||||
|
dispatch({type: "RUN_QUERY"});
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const delay = selectedDelay.seconds;
|
const delay = selectedDelay.seconds;
|
||||||
let timer: number;
|
let timer: number;
|
||||||
if (autoRefresh) {
|
if (autoRefresh) {
|
||||||
timer = setInterval(() => {
|
timer = setInterval(() => {
|
||||||
dispatch({type: "RUN_QUERY_TO_NOW"});
|
dispatch({type: "RUN_QUERY"});
|
||||||
}, delay * 1000) as unknown as number;
|
}, delay * 1000) as unknown as number;
|
||||||
} else {
|
} else {
|
||||||
setSelectedDelay(delayOptions[0]);
|
setSelectedDelay(delayOptions[0]);
|
||||||
|
@ -74,22 +79,33 @@ export const ExecutionControls: FC = () => {
|
||||||
const open = Boolean(anchorEl);
|
const open = Boolean(anchorEl);
|
||||||
|
|
||||||
return <>
|
return <>
|
||||||
<Tooltip title="Auto-refresh control">
|
<Box sx={{
|
||||||
<Button variant="contained" color="primary"
|
|
||||||
sx={{
|
|
||||||
minWidth: "110px",
|
minWidth: "110px",
|
||||||
color: "white",
|
color: "white",
|
||||||
border: appModeEnable ? "none" : "1px solid rgba(0, 0, 0, 0.2)",
|
border: appModeEnable ? "none" : "1px solid rgba(0, 0, 0, 0.2)",
|
||||||
justifyContent: "space-between",
|
justifyContent: "space-between",
|
||||||
boxShadow: "none",
|
boxShadow: "none",
|
||||||
}}
|
borderRadius: "4px",
|
||||||
startIcon={<AutorenewIcon/>}
|
display: "grid",
|
||||||
|
gridTemplateColumns: "auto 1fr"
|
||||||
|
}}>
|
||||||
|
<Tooltip title="Refresh dashboard">
|
||||||
|
<Button variant="contained" color="primary"
|
||||||
|
sx={{color: "white", minWidth: "34px", boxShadow: "none", borderRadius: "3px 0 0 3px", p: "6px 6px"}}
|
||||||
|
startIcon={<AutorenewIcon fontSize={"small"} style={{marginRight: "-8px", marginLeft: "4px"}}/>}
|
||||||
|
onClick={handleUpdate}
|
||||||
|
>
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip title="Auto-refresh control">
|
||||||
|
<Button variant="contained" color="primary" sx={{boxShadow: "none", borderRadius: "0 3px 3px 0"}} fullWidth
|
||||||
endIcon={<KeyboardArrowDownIcon sx={{transform: open ? "rotate(180deg)" : "none"}}/>}
|
endIcon={<KeyboardArrowDownIcon sx={{transform: open ? "rotate(180deg)" : "none"}}/>}
|
||||||
onClick={(e) => setAnchorEl(e.currentTarget)}
|
onClick={(e) => setAnchorEl(e.currentTarget)}
|
||||||
>
|
>
|
||||||
{selectedDelay.title}
|
{selectedDelay.title}
|
||||||
</Button>
|
</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
</Box>
|
||||||
<Popper
|
<Popper
|
||||||
open={open}
|
open={open}
|
||||||
anchorEl={anchorEl}
|
anchorEl={anchorEl}
|
||||||
|
@ -97,7 +113,7 @@ export const ExecutionControls: FC = () => {
|
||||||
modifiers={[{name: "offset", options: {offset: [0, 6]}}]}>
|
modifiers={[{name: "offset", options: {offset: [0, 6]}}]}>
|
||||||
<ClickAwayListener onClickAway={() => setAnchorEl(null)}>
|
<ClickAwayListener onClickAway={() => setAnchorEl(null)}>
|
||||||
<Paper elevation={3}>
|
<Paper elevation={3}>
|
||||||
<List style={{minWidth: "110px",maxHeight: "208px", overflow: "auto", padding: "20px 0"}}>
|
<List style={{minWidth: "110px", maxHeight: "208px", overflow: "auto", padding: "20px 0"}}>
|
||||||
{delayOptions.map(d =>
|
{delayOptions.map(d =>
|
||||||
<ListItem key={d.seconds} button onClick={() => handleChange(d)}>
|
<ListItem key={d.seconds} button onClick={() => handleChange(d)}>
|
||||||
<ListItemText primary={d.title}/>
|
<ListItemText primary={d.title}/>
|
||||||
|
|
|
@ -73,11 +73,8 @@ export const TimeSelector: FC = () => {
|
||||||
|
|
||||||
const open = Boolean(anchorEl);
|
const open = Boolean(anchorEl);
|
||||||
const setTimeAndClosePicker = () => {
|
const setTimeAndClosePicker = () => {
|
||||||
if (from) {
|
if (from && until) {
|
||||||
dispatch({type: "SET_FROM", payload: new Date(from)});
|
dispatch({type: "SET_PERIOD", payload: {from: new Date(from), to: new Date(until)}});
|
||||||
}
|
|
||||||
if (until) {
|
|
||||||
dispatch({type: "SET_UNTIL", payload: new Date(until)});
|
|
||||||
}
|
}
|
||||||
setAnchorEl(null);
|
setAnchorEl(null);
|
||||||
};
|
};
|
||||||
|
|
|
@ -6,8 +6,6 @@ import {isValidHttpUrl} from "../utils/url";
|
||||||
import {ErrorTypes} from "../types";
|
import {ErrorTypes} from "../types";
|
||||||
import debounce from "lodash.debounce";
|
import debounce from "lodash.debounce";
|
||||||
import {DisplayType} from "../components/CustomPanel/Configurator/DisplayTypeSwitch";
|
import {DisplayType} from "../components/CustomPanel/Configurator/DisplayTypeSwitch";
|
||||||
import usePrevious from "./usePrevious";
|
|
||||||
import {arrayEquals} from "../utils/array";
|
|
||||||
import Trace from "../components/CustomPanel/Trace/Trace";
|
import Trace from "../components/CustomPanel/Trace/Trace";
|
||||||
import {MAX_SERIES} from "../config";
|
import {MAX_SERIES} from "../config";
|
||||||
|
|
||||||
|
@ -94,7 +92,7 @@ export const useFetchQuery = ({predefinedQuery, visible, display, customStep}: F
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
const throttledFetchData = useCallback(debounce(fetchData, 600), []);
|
const throttledFetchData = useCallback(debounce(fetchData, 800), []);
|
||||||
|
|
||||||
const fetchUrl = useMemo(() => {
|
const fetchUrl = useMemo(() => {
|
||||||
const expr = predefinedQuery ?? query;
|
const expr = predefinedQuery ?? query;
|
||||||
|
@ -116,13 +114,8 @@ export const useFetchQuery = ({predefinedQuery, visible, display, customStep}: F
|
||||||
},
|
},
|
||||||
[serverUrl, period, displayType, customStep]);
|
[serverUrl, period, displayType, customStep]);
|
||||||
|
|
||||||
const prevFetchUrl = usePrevious(fetchUrl);
|
|
||||||
const prevDisplayType = usePrevious(displayType);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const equalFetchUrl = fetchUrl && prevFetchUrl && arrayEquals(fetchUrl, prevFetchUrl);
|
if (!visible || !fetchUrl?.length) return;
|
||||||
const changedDisplayType = displayType !== prevDisplayType;
|
|
||||||
if (!visible || (equalFetchUrl && !changedDisplayType) || !fetchUrl?.length) return;
|
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
const expr = predefinedQuery ?? query;
|
const expr = predefinedQuery ?? query;
|
||||||
throttledFetchData(fetchUrl, fetchQueue, (display || displayType), expr);
|
throttledFetchData(fetchUrl, fetchQueue, (display || displayType), expr);
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import {ErrorTypes} from "../types";
|
import {ErrorTypes} from "../types";
|
||||||
import {getAppModeParams} from "../utils/app-mode";
|
|
||||||
import {useAppState} from "../state/common/StateContext";
|
import {useAppState} from "../state/common/StateContext";
|
||||||
import {useMemo} from "preact/compat";
|
import {useMemo} from "preact/compat";
|
||||||
import {getTopQueries} from "../api/top-queries";
|
import {getTopQueries} from "../api/top-queries";
|
||||||
|
|
|
@ -62,6 +62,7 @@ The following tip changes can be tested by building VictoriaMetrics components f
|
||||||
* BUGFIX: [vmalert](https://docs.victoriametrics.com/vmalert.html): fix panic if `vmalert` runs with `-clusterMode` command-line flag in [multitenant mode](https://docs.victoriametrics.com/vmalert.html#multitenancy). The issue has been introduced in [v1.82.0](https://docs.victoriametrics.com/CHANGELOG.html#v1820).
|
* BUGFIX: [vmalert](https://docs.victoriametrics.com/vmalert.html): fix panic if `vmalert` runs with `-clusterMode` command-line flag in [multitenant mode](https://docs.victoriametrics.com/vmalert.html#multitenancy). The issue has been introduced in [v1.82.0](https://docs.victoriametrics.com/CHANGELOG.html#v1820).
|
||||||
* BUGFIX: [vmalert](https://docs.victoriametrics.com/vmalert.html): properly escape string passed to `quotesEscape` [template function](https://docs.victoriametrics.com/vmalert.html#template-functions), so it can be safely embedded into JSON string. This makes obsolete the `crlfEscape` function. See [this](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3139) and [this](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/890) issue.
|
* BUGFIX: [vmalert](https://docs.victoriametrics.com/vmalert.html): properly escape string passed to `quotesEscape` [template function](https://docs.victoriametrics.com/vmalert.html#template-functions), so it can be safely embedded into JSON string. This makes obsolete the `crlfEscape` function. See [this](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3139) and [this](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/890) issue.
|
||||||
* BUGFIX: [vmagent](https://docs.victoriametrics.com/vmagent.html): do not show invalid error message in Kubernetes service discovery: `cannot parse WatchEvent json response: EOF`. The invalid error message has been appeared in [v1.82.0](https://docs.victoriametrics.com/CHANGELOG.html#v1820).
|
* BUGFIX: [vmagent](https://docs.victoriametrics.com/vmagent.html): do not show invalid error message in Kubernetes service discovery: `cannot parse WatchEvent json response: EOF`. The invalid error message has been appeared in [v1.82.0](https://docs.victoriametrics.com/CHANGELOG.html#v1820).
|
||||||
|
* BUGFIX: [vmagent](https://docs.victoriametrics.com/vmagent.html): properly add `exported_` prefix to metric labels, which clashing with scrape target labels if `honor_labels: true` option isn't set in [scrape_config](https://docs.victoriametrics.com/sd_configs.html#scrape_configs). Previously some `exported_` prefixes were missing in the resulting metric labels. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3278). The issue has been introduced in [v1.82.0](https://docs.victoriametrics.com/CHANGELOG.html#v1820).
|
||||||
* BUGFIX: `vmselect`: expose missing metric `vm_cache_size_max_bytes{type="promql/rollupResult"}` . This metric is used for monitoring rollup cache usage with the query `vm_cache_size_bytes{type="promql/rollupResult"} / vm_cache_size_max_bytes{type="promql/rollupResult"}` in the same way as this is done for other cache types.
|
* BUGFIX: `vmselect`: expose missing metric `vm_cache_size_max_bytes{type="promql/rollupResult"}` . This metric is used for monitoring rollup cache usage with the query `vm_cache_size_bytes{type="promql/rollupResult"} / vm_cache_size_max_bytes{type="promql/rollupResult"}` in the same way as this is done for other cache types.
|
||||||
|
|
||||||
## [v1.82.1](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.82.1)
|
## [v1.82.1](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.82.1)
|
||||||
|
|
|
@ -24,7 +24,7 @@ type SDConfig struct {
|
||||||
Role string `yaml:"role"`
|
Role string `yaml:"role"`
|
||||||
// The filepath to kube config.
|
// The filepath to kube config.
|
||||||
// If defined any cluster connection information from HTTPClientConfig is ignored.
|
// If defined any cluster connection information from HTTPClientConfig is ignored.
|
||||||
KubeConfigFile string `yaml:"kubeconfig_file"`
|
KubeConfigFile string `yaml:"kubeconfig_file,omitempty"`
|
||||||
|
|
||||||
HTTPClientConfig promauth.HTTPClientConfig `yaml:",inline"`
|
HTTPClientConfig promauth.HTTPClientConfig `yaml:",inline"`
|
||||||
ProxyURL *proxy.URL `yaml:"proxy_url,omitempty"`
|
ProxyURL *proxy.URL `yaml:"proxy_url,omitempty"`
|
||||||
|
|
|
@ -704,7 +704,15 @@ func (wc *writeRequestCtx) reset() {
|
||||||
|
|
||||||
func (wc *writeRequestCtx) resetNoRows() {
|
func (wc *writeRequestCtx) resetNoRows() {
|
||||||
prompbmarshal.ResetWriteRequest(&wc.writeRequest)
|
prompbmarshal.ResetWriteRequest(&wc.writeRequest)
|
||||||
|
|
||||||
|
labels := wc.labels
|
||||||
|
for i := range labels {
|
||||||
|
label := &labels[i]
|
||||||
|
label.Name = ""
|
||||||
|
label.Value = ""
|
||||||
|
}
|
||||||
wc.labels = wc.labels[:0]
|
wc.labels = wc.labels[:0]
|
||||||
|
|
||||||
wc.samples = wc.samples[:0]
|
wc.samples = wc.samples[:0]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -740,6 +748,7 @@ func (sw *scrapeWork) applySeriesLimit(wc *writeRequestCtx) int {
|
||||||
}
|
}
|
||||||
dstSeries = append(dstSeries, ts)
|
dstSeries = append(dstSeries, ts)
|
||||||
}
|
}
|
||||||
|
prompbmarshal.ResetTimeSeries(wc.writeRequest.Timeseries[len(dstSeries):])
|
||||||
wc.writeRequest.Timeseries = dstSeries
|
wc.writeRequest.Timeseries = dstSeries
|
||||||
return samplesDropped
|
return samplesDropped
|
||||||
}
|
}
|
||||||
|
@ -880,16 +889,14 @@ func appendLabels(dst []prompbmarshal.Label, metric string, src []parser.Tag, ex
|
||||||
func appendExtraLabels(dst, extraLabels []prompbmarshal.Label, offset int, honorLabels bool) []prompbmarshal.Label {
|
func appendExtraLabels(dst, extraLabels []prompbmarshal.Label, offset int, honorLabels bool) []prompbmarshal.Label {
|
||||||
// Add extraLabels to labels.
|
// Add extraLabels to labels.
|
||||||
// Handle duplicates in the same way as Prometheus does.
|
// Handle duplicates in the same way as Prometheus does.
|
||||||
if len(dst) > offset && dst[offset].Name == "__name__" {
|
if len(dst) == offset {
|
||||||
offset++
|
|
||||||
}
|
|
||||||
labels := dst[offset:]
|
|
||||||
if len(labels) == 0 {
|
|
||||||
// Fast path - add extraLabels to dst without the need to de-duplicate.
|
// Fast path - add extraLabels to dst without the need to de-duplicate.
|
||||||
dst = append(dst, extraLabels...)
|
dst = append(dst, extraLabels...)
|
||||||
return dst
|
return dst
|
||||||
}
|
}
|
||||||
|
offsetEnd := len(dst)
|
||||||
for _, label := range extraLabels {
|
for _, label := range extraLabels {
|
||||||
|
labels := dst[offset:offsetEnd]
|
||||||
prevLabel := promrelabel.GetLabelByName(labels, label.Name)
|
prevLabel := promrelabel.GetLabelByName(labels, label.Name)
|
||||||
if prevLabel == nil {
|
if prevLabel == nil {
|
||||||
// Fast path - the label doesn't exist in labels, so just add it to dst.
|
// Fast path - the label doesn't exist in labels, so just add it to dst.
|
||||||
|
@ -904,13 +911,13 @@ func appendExtraLabels(dst, extraLabels []prompbmarshal.Label, offset int, honor
|
||||||
// See https://prometheus.io/docs/prometheus/latest/configuration/configuration/#scrape_config
|
// See https://prometheus.io/docs/prometheus/latest/configuration/configuration/#scrape_config
|
||||||
exportedName := "exported_" + label.Name
|
exportedName := "exported_" + label.Name
|
||||||
exportedLabel := promrelabel.GetLabelByName(labels, exportedName)
|
exportedLabel := promrelabel.GetLabelByName(labels, exportedName)
|
||||||
if exportedLabel == nil {
|
if exportedLabel != nil {
|
||||||
|
// The label with the name exported_<label.Name> already exists.
|
||||||
|
// Add yet another 'exported_' prefix to it.
|
||||||
|
exportedLabel.Name = "exported_" + exportedName
|
||||||
|
}
|
||||||
prevLabel.Name = exportedName
|
prevLabel.Name = exportedName
|
||||||
dst = append(dst, label)
|
dst = append(dst, label)
|
||||||
} else {
|
|
||||||
exportedLabel.Value = prevLabel.Value
|
|
||||||
prevLabel.Value = label.Value
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return dst
|
return dst
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,16 +27,22 @@ func TestAppendExtraLabels(t *testing.T) {
|
||||||
f("{}", "{}", false, "{}")
|
f("{}", "{}", false, "{}")
|
||||||
f("foo", "{}", true, `{__name__="foo"}`)
|
f("foo", "{}", true, `{__name__="foo"}`)
|
||||||
f("foo", "{}", false, `{__name__="foo"}`)
|
f("foo", "{}", false, `{__name__="foo"}`)
|
||||||
f("foo", "bar", true, `{__name__="foo",__name__="bar"}`)
|
f("foo", "bar", true, `{__name__="foo"}`)
|
||||||
f("foo", "bar", false, `{__name__="foo",__name__="bar"}`)
|
f("foo", "bar", false, `{exported___name__="foo",__name__="bar"}`)
|
||||||
f(`{a="b"}`, `{c="d"}`, true, `{a="b",c="d"}`)
|
f(`{a="b"}`, `{c="d"}`, true, `{a="b",c="d"}`)
|
||||||
f(`{a="b"}`, `{c="d"}`, false, `{a="b",c="d"}`)
|
f(`{a="b"}`, `{c="d"}`, false, `{a="b",c="d"}`)
|
||||||
f(`{a="b"}`, `{a="d"}`, true, `{a="b"}`)
|
f(`{a="b"}`, `{a="d"}`, true, `{a="b"}`)
|
||||||
f(`{a="b"}`, `{a="d"}`, false, `{exported_a="b",a="d"}`)
|
f(`{a="b"}`, `{a="d"}`, false, `{exported_a="b",a="d"}`)
|
||||||
f(`{a="b",exported_a="x"}`, `{a="d"}`, true, `{a="b",exported_a="x"}`)
|
f(`{a="b",exported_a="x"}`, `{a="d"}`, true, `{a="b",exported_a="x"}`)
|
||||||
f(`{a="b",exported_a="x"}`, `{a="d"}`, false, `{a="d",exported_a="b"}`)
|
f(`{a="b",exported_a="x"}`, `{a="d"}`, false, `{exported_a="b",exported_exported_a="x",a="d"}`)
|
||||||
f(`{a="b"}`, `{a="d",exported_a="x"}`, true, `{a="b",exported_a="x"}`)
|
f(`{a="b"}`, `{a="d",exported_a="x"}`, true, `{a="b",exported_a="x"}`)
|
||||||
f(`{a="b"}`, `{a="d",exported_a="x"}`, false, `{exported_a="b",a="d",exported_a="x"}`)
|
f(`{a="b"}`, `{a="d",exported_a="x"}`, false, `{exported_exported_a="b",a="d",exported_a="x"}`)
|
||||||
|
f(`{foo="a",exported_foo="b"}`, `{exported_foo="c"}`, true, `{foo="a",exported_foo="b"}`)
|
||||||
|
f(`{foo="a",exported_foo="b"}`, `{exported_foo="c"}`, false, `{foo="a",exported_exported_foo="b",exported_foo="c"}`)
|
||||||
|
f(`{foo="a",exported_foo="b"}`, `{exported_foo="c",bar="x"}`, true, `{foo="a",exported_foo="b",bar="x"}`)
|
||||||
|
f(`{foo="a",exported_foo="b"}`, `{exported_foo="c",bar="x"}`, false, `{foo="a",exported_exported_foo="b",exported_foo="c",bar="x"}`)
|
||||||
|
f(`{foo="a",exported_foo="b"}`, `{exported_foo="c",foo="d"}`, true, `{foo="a",exported_foo="b"}`)
|
||||||
|
f(`{foo="a",exported_foo="b"}`, `{exported_foo="c",foo="d"}`, false, `{exported_foo="a",exported_exported_foo="b",exported_foo="c",foo="d"}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPromLabelsString(t *testing.T) {
|
func TestPromLabelsString(t *testing.T) {
|
||||||
|
|
Loading…
Reference in a new issue