mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2026-07-22 00:30:22 +03:00
Compare commits
1 Commits
master
...
vmui/auto-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
510b026525 |
8
.github/workflows/vmui.yml
vendored
8
.github/workflows/vmui.yml
vendored
@@ -26,7 +26,9 @@ jobs:
|
||||
vmui-checks:
|
||||
name: VMUI Checks (lint, test, typecheck)
|
||||
permissions:
|
||||
checks: write
|
||||
contents: read
|
||||
pull-requests: read
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Code checkout
|
||||
@@ -68,6 +70,12 @@ jobs:
|
||||
env:
|
||||
VMUI_SKIP_INSTALL: true
|
||||
|
||||
- name: Annotate Code Linting Results
|
||||
uses: ataylorme/eslint-annotate-action@d57a1193d4c59cbfbf3f86c271f42612f9dbd9e9 # 3.0.0
|
||||
with:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
report-json: app/vmui/packages/vmui/vmui-lint-report.json
|
||||
|
||||
- name: Check overall status
|
||||
run: |
|
||||
echo "Lint status: ${{ steps.lint.outcome }}"
|
||||
|
||||
@@ -462,9 +462,7 @@ func requestHandler(w http.ResponseWriter, r *http.Request) bool {
|
||||
return true
|
||||
case "/prometheus/metric-relabel-debug", "/metric-relabel-debug":
|
||||
promscrapeMetricRelabelDebugRequests.Inc()
|
||||
rwGlobalRelabelConfigs := remotewrite.GetRemoteWriteRelabelConfigString()
|
||||
rwURLRelabelConfigss := remotewrite.GetURLRelabelConfigString()
|
||||
promscrape.WriteMetricRelabelDebug(w, r, rwGlobalRelabelConfigs, rwURLRelabelConfigss)
|
||||
promscrape.WriteMetricRelabelDebug(w, r)
|
||||
return true
|
||||
case "/prometheus/target-relabel-debug", "/target-relabel-debug":
|
||||
promscrapeTargetRelabelDebugRequests.Inc()
|
||||
|
||||
@@ -12,7 +12,6 @@ import (
|
||||
"github.com/VictoriaMetrics/metrics"
|
||||
"gopkg.in/yaml.v2"
|
||||
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/bytesutil"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/fasttime"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/flagutil"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
|
||||
@@ -83,16 +82,6 @@ func WriteRelabelConfigData(w io.Writer) {
|
||||
_, _ = w.Write(*p)
|
||||
}
|
||||
|
||||
// GetRemoteWriteRelabelConfigString returns -remoteWrite.relabelConfig contents in string
|
||||
func GetRemoteWriteRelabelConfigString() string {
|
||||
var bb bytesutil.ByteBuffer
|
||||
WriteRelabelConfigData(&bb)
|
||||
if bb.Len() == 0 {
|
||||
return ""
|
||||
}
|
||||
return string(bb.B)
|
||||
}
|
||||
|
||||
// WriteURLRelabelConfigData writes -remoteWrite.urlRelabelConfig contents to w
|
||||
func WriteURLRelabelConfigData(w io.Writer) {
|
||||
p := remoteWriteURLRelabelConfigData.Load()
|
||||
@@ -119,24 +108,6 @@ func WriteURLRelabelConfigData(w io.Writer) {
|
||||
_, _ = w.Write(d)
|
||||
}
|
||||
|
||||
// GetURLRelabelConfigString returns -remoteWrite.urlRelabelConfig contents in []string
|
||||
func GetURLRelabelConfigString() []string {
|
||||
p := remoteWriteURLRelabelConfigData.Load()
|
||||
if p == nil {
|
||||
return nil
|
||||
}
|
||||
var ss []string
|
||||
for i := range *remoteWriteURLs {
|
||||
cfgData := (*p)[i]
|
||||
var cfgDataBytes []byte
|
||||
if cfgData != nil {
|
||||
cfgDataBytes, _ = yaml.Marshal(cfgData)
|
||||
}
|
||||
ss = append(ss, string(cfgDataBytes))
|
||||
}
|
||||
return ss
|
||||
}
|
||||
|
||||
func reloadRelabelConfigs() {
|
||||
rcs := allRelabelConfigs.Load()
|
||||
if !rcs.isSet() {
|
||||
|
||||
@@ -386,7 +386,7 @@ func bufferRequestBody(ctx context.Context, r io.ReadCloser, userName string) (i
|
||||
return nil, &httpserver.ErrorWithStatusCode{
|
||||
Err: fmt.Errorf("reject request from the user %s because the request body couldn't be read in -maxQueueDuration=%s; read %d bytes in %s",
|
||||
userName, *maxQueueDuration, len(buf), d.Truncate(time.Second)),
|
||||
StatusCode: http.StatusRequestTimeout,
|
||||
StatusCode: http.StatusBadRequest,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
//go:build synctest
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"testing"
|
||||
"testing/synctest"
|
||||
"time"
|
||||
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/httpserver"
|
||||
)
|
||||
|
||||
func TestBufferRequestBody_Timeout(t *testing.T) {
|
||||
synctest.Test(t, func(t *testing.T) {
|
||||
|
||||
defaultMaxQueueDuration := *maxQueueDuration
|
||||
defer func() {
|
||||
*maxQueueDuration = defaultMaxQueueDuration
|
||||
}()
|
||||
|
||||
*maxQueueDuration = 100 * time.Millisecond
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), *maxQueueDuration)
|
||||
defer cancel()
|
||||
|
||||
_, err := bufferRequestBody(ctx, &timeoutBody{delay: *maxQueueDuration + 100*time.Millisecond}, "foo")
|
||||
if err == nil {
|
||||
t.Fatalf("expecting non-nil error")
|
||||
}
|
||||
|
||||
esc, ok := err.(*httpserver.ErrorWithStatusCode)
|
||||
if !ok {
|
||||
t.Fatalf("unexpected error type: %s", err)
|
||||
}
|
||||
if esc.StatusCode != http.StatusRequestTimeout {
|
||||
t.Fatalf("read request body timeout meet unexpected status code; got %d; want %d", esc.StatusCode, http.StatusRequestTimeout)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
type timeoutBody struct {
|
||||
delay time.Duration
|
||||
}
|
||||
|
||||
func (r *timeoutBody) Read(_ []byte) (int, error) {
|
||||
time.Sleep(r.delay)
|
||||
return 0, context.DeadlineExceeded
|
||||
}
|
||||
|
||||
func (r *timeoutBody) Close() error {
|
||||
return nil
|
||||
}
|
||||
@@ -541,7 +541,7 @@ func handleStaticAndSimpleRequests(w http.ResponseWriter, r *http.Request, path
|
||||
return true
|
||||
case "/metric-relabel-debug":
|
||||
promscrapeMetricRelabelDebugRequests.Inc()
|
||||
promscrape.WriteMetricRelabelDebug(w, r, "", nil)
|
||||
promscrape.WriteMetricRelabelDebug(w, r)
|
||||
return true
|
||||
case "/target-relabel-debug":
|
||||
promscrapeTargetRelabelDebugRequests.Inc()
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
197
app/vmselect/vmui/assets/index-xYKUiOTH.js
Normal file
197
app/vmselect/vmui/assets/index-xYKUiOTH.js
Normal file
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,t,n)=>()=>{if(n)throw n[0];try{return e&&(t=e(e=0)),t}catch(e){throw n=[e],e}},s=(e,t)=>()=>(t||(e((t={exports:{}}).exports,t),e=null),t.exports),c=(e,n)=>{let r={};for(var i in e)t(r,i,{get:e[i],enumerable:!0});return n||t(r,Symbol.toStringTag,{value:`Module`}),r},l=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},u=(n,r,a)=>(a=n==null?{}:e(i(n)),l(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n)),d=e=>a.call(e,`module.exports`)?e[`module.exports`]:l(t({},`__esModule`,{value:!0}),e);export{u as a,d as i,o as n,c as r,s as t};
|
||||
1
app/vmselect/vmui/assets/rolldown-runtime-Cyuzqnbw.js
Normal file
1
app/vmselect/vmui/assets/rolldown-runtime-Cyuzqnbw.js
Normal file
@@ -0,0 +1 @@
|
||||
var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,t)=>()=>(e&&(t=e(e=0)),t),s=(e,t)=>()=>(t||(e((t={exports:{}}).exports,t),e=null),t.exports),c=(e,n)=>{let r={};for(var i in e)t(r,i,{get:e[i],enumerable:!0});return n||t(r,Symbol.toStringTag,{value:`Module`}),r},l=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},u=(n,r,a)=>(a=n==null?{}:e(i(n)),l(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n)),d=e=>a.call(e,`module.exports`)?e[`module.exports`]:l(t({},`__esModule`,{value:!0}),e);export{u as a,d as i,o as n,c as r,s as t};
|
||||
78
app/vmselect/vmui/assets/vendor-B83wxFqK.js
Normal file
78
app/vmselect/vmui/assets/vendor-B83wxFqK.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -37,11 +37,11 @@
|
||||
<meta property="og:title" content="UI for VictoriaMetrics">
|
||||
<meta property="og:url" content="https://victoriametrics.com/">
|
||||
<meta property="og:description" content="Explore and troubleshoot your VictoriaMetrics data">
|
||||
<script type="module" crossorigin src="./assets/index-D5egN2id.js"></script>
|
||||
<link rel="modulepreload" crossorigin href="./assets/rolldown-runtime-CNC7AqOf.js">
|
||||
<link rel="modulepreload" crossorigin href="./assets/vendor-DwJYpOdw.js">
|
||||
<script type="module" crossorigin src="./assets/index-xYKUiOTH.js"></script>
|
||||
<link rel="modulepreload" crossorigin href="./assets/rolldown-runtime-Cyuzqnbw.js">
|
||||
<link rel="modulepreload" crossorigin href="./assets/vendor-B83wxFqK.js">
|
||||
<link rel="stylesheet" crossorigin href="./assets/vendor-CnsZ1jie.css">
|
||||
<link rel="stylesheet" crossorigin href="./assets/index-BJqoElx2.css">
|
||||
<link rel="stylesheet" crossorigin href="./assets/index-BBUnmLOr.css">
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
|
||||
@@ -35,10 +35,7 @@ var (
|
||||
"By default, or when set to 0, -maxBackfillAge equals to -retentionPeriod, e.g. it is unlimited within the configured retention. "+
|
||||
"This can be useful for limiting ingestion of historical samples, for example, when older data has been moved to another storage tier. "+
|
||||
"See https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/#retention")
|
||||
vmselectAddr = flag.String("vmselectAddr", "", "TCP address to listen for incoming connections from vmselect. "+
|
||||
"When set, the node will be able to accept cluster-native vmselect RPC requests as if it were vmstorage. "+
|
||||
"The tenant ID assigned to this node's data is controlled by -accountID and -projectID flags. "+
|
||||
"See https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/#multi-tenancy")
|
||||
vmselectAddr = flag.String("vmselectAddr", "", "TCP address to accept connections from vmselect services")
|
||||
vmselectDisableRPCCompression = flag.Bool("rpc.disableCompression", false, "Whether to disable compression of the data sent from vmstorage to vmselect. "+
|
||||
"This reduces CPU usage at the cost of higher network bandwidth usage")
|
||||
snapshotAuthKey = flagutil.NewPassword("snapshotAuthKey", "authKey, which must be passed in query string to /snapshot* pages. It overrides -httpAuth.*")
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM golang:1.26.5 AS build-web-stage
|
||||
FROM golang:1.26.4 AS build-web-stage
|
||||
COPY build /build
|
||||
|
||||
WORKDIR /build
|
||||
|
||||
1124
app/vmui/packages/vmui/package-lock.json
generated
1124
app/vmui/packages/vmui/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -9,7 +9,7 @@
|
||||
"start": "vite",
|
||||
"start:playground": "cross-env PLAYGROUND=true npm run start",
|
||||
"build": "vite build",
|
||||
"lint": "eslint --format stylish 'src/**/*.{ts,tsx}'",
|
||||
"lint": "eslint --output-file vmui-lint-report.json --format json 'src/**/*.{ts,tsx}'",
|
||||
"lint:local": "eslint --ext .ts,.tsx -f stylish src",
|
||||
"lint:fix": "eslint 'src/**/*.{ts,tsx}' --fix",
|
||||
"copy-metricsql-docs": "cp ../../../../docs/MetricsQL.md src/assets/MetricsQL.md || true",
|
||||
@@ -23,40 +23,40 @@
|
||||
"classnames": "^2.5.1",
|
||||
"dayjs": "^1.11.21",
|
||||
"lodash.debounce": "^4.0.8",
|
||||
"marked": "^18.0.6",
|
||||
"preact": "^10.29.7",
|
||||
"qs": "^6.15.3",
|
||||
"marked": "^18.0.5",
|
||||
"preact": "^10.29.2",
|
||||
"qs": "^6.15.2",
|
||||
"react-input-mask": "^2.0.4",
|
||||
"react-router-dom": "^7.18.1",
|
||||
"react-router-dom": "^7.17.0",
|
||||
"uplot": "^1.6.32",
|
||||
"vite": "^8.1.4",
|
||||
"vite": "^8.0.16",
|
||||
"web-vitals": "^5.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/eslintrc": "^3.3.6",
|
||||
"@eslint/eslintrc": "^3.3.5",
|
||||
"@eslint/js": "^9.39.2",
|
||||
"@preact/preset-vite": "^2.10.5",
|
||||
"@testing-library/jest-dom": "^6.9.1",
|
||||
"@testing-library/preact": "^3.2.4",
|
||||
"@types/lodash.debounce": "^4.0.9",
|
||||
"@types/node": "^26.1.1",
|
||||
"@types/node": "^25.9.2",
|
||||
"@types/qs": "^6.15.1",
|
||||
"@types/react": "^19.2.17",
|
||||
"@types/react-input-mask": "^3.0.6",
|
||||
"@types/react-router-dom": "^5.3.3",
|
||||
"@typescript-eslint/eslint-plugin": "^8.64.0",
|
||||
"@typescript-eslint/parser": "^8.64.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.61.0",
|
||||
"@typescript-eslint/parser": "^8.61.0",
|
||||
"cross-env": "^10.1.0",
|
||||
"eslint": "^9.39.2",
|
||||
"eslint-plugin-react": "^7.37.5",
|
||||
"eslint-plugin-unused-imports": "^4.4.1",
|
||||
"globals": "^17.7.0",
|
||||
"http-proxy-middleware": "^4.2.0",
|
||||
"globals": "^17.6.0",
|
||||
"http-proxy-middleware": "^4.1.0",
|
||||
"jsdom": "^29.1.1",
|
||||
"postcss": "^8.5.19",
|
||||
"postcss": "^8.5.15",
|
||||
"sass-embedded": "^1.100.0",
|
||||
"typescript": "^6.0.3",
|
||||
"vitest": "^4.1.10"
|
||||
"vitest": "^4.1.8"
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { FC, useEffect, useRef, useState } from "preact/compat";
|
||||
import { FC, useEffect, useRef } from "preact/compat";
|
||||
import { useTimeDispatch } from "../../../../state/time/TimeStateContext";
|
||||
import { getAppModeEnable } from "../../../../utils/app-mode";
|
||||
import Button from "../../../Main/Button/Button";
|
||||
@@ -9,27 +9,44 @@ import classNames from "classnames";
|
||||
import Tooltip from "../../../Main/Tooltip/Tooltip";
|
||||
import useDeviceDetect from "../../../../hooks/useDeviceDetect";
|
||||
import useBoolean from "../../../../hooks/useBoolean";
|
||||
import { getMillisecondsFromDuration } from "../../../../utils/time";
|
||||
import { useSearchParams } from "react-router";
|
||||
|
||||
interface AutoRefreshOption {
|
||||
seconds: number
|
||||
title: string
|
||||
}
|
||||
|
||||
const delayOptions: AutoRefreshOption[] = [
|
||||
{ seconds: 0, title: "Off" },
|
||||
{ seconds: 1, title: "1s" },
|
||||
{ seconds: 2, title: "2s" },
|
||||
{ seconds: 5, title: "5s" },
|
||||
{ seconds: 10, title: "10s" },
|
||||
{ seconds: 30, title: "30s" },
|
||||
{ seconds: 60, title: "1m" },
|
||||
{ seconds: 300, title: "5m" },
|
||||
{ seconds: 900, title: "15m" },
|
||||
{ seconds: 1800, title: "30m" },
|
||||
{ seconds: 3600, title: "1h" },
|
||||
{ seconds: 7200, title: "2h" }
|
||||
const delayOptions = [
|
||||
"Off",
|
||||
"1s",
|
||||
"2s",
|
||||
"5s",
|
||||
"10s",
|
||||
"30s",
|
||||
"1m",
|
||||
"5m",
|
||||
"15m",
|
||||
"30m",
|
||||
"1h",
|
||||
"2h"
|
||||
];
|
||||
|
||||
const DEFAULT_OPTION = delayOptions[0];
|
||||
|
||||
const MIN_REFRESH_MS = 1000;
|
||||
const MAX_REFRESH_MS = getMillisecondsFromDuration(delayOptions[delayOptions.length - 1]);
|
||||
const REFRESH_URL_PARAM = "refresh";
|
||||
|
||||
const durationToMs = (dur: string | null) => {
|
||||
if (!dur) return 0;
|
||||
|
||||
try {
|
||||
return getMillisecondsFromDuration(dur);
|
||||
} catch (_e) {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
const isValidDelay = (ms: number) => {
|
||||
return ms >= MIN_REFRESH_MS && ms <= MAX_REFRESH_MS;
|
||||
};
|
||||
|
||||
interface ExecutionControlsProps {
|
||||
tooltip: string;
|
||||
useAutorefresh?: boolean;
|
||||
@@ -38,12 +55,14 @@ interface ExecutionControlsProps {
|
||||
|
||||
export const ExecutionControls: FC<ExecutionControlsProps> = ({ tooltip, useAutorefresh, closeModal }) => {
|
||||
const { isMobile } = useDeviceDetect();
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
|
||||
const dispatch = useTimeDispatch();
|
||||
const appModeEnable = getAppModeEnable();
|
||||
const [autoRefresh, setAutoRefresh] = useState(false);
|
||||
|
||||
const [selectedDelay, setSelectedDelay] = useState<AutoRefreshOption>(delayOptions[0]);
|
||||
const rawDelay = searchParams.get(REFRESH_URL_PARAM);
|
||||
const msDelay = durationToMs(rawDelay);
|
||||
const selectedDelay = isValidDelay(msDelay) ? rawDelay : DEFAULT_OPTION;
|
||||
|
||||
const {
|
||||
value: openOptions,
|
||||
@@ -52,11 +71,20 @@ export const ExecutionControls: FC<ExecutionControlsProps> = ({ tooltip, useAuto
|
||||
} = useBoolean(false);
|
||||
const optionsButtonRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const handleChange = (d: AutoRefreshOption) => {
|
||||
if ((autoRefresh && !d.seconds) || (!autoRefresh && d.seconds)) {
|
||||
setAutoRefresh(prev => !prev);
|
||||
}
|
||||
setSelectedDelay(d);
|
||||
const handleChange = (dur: string) => () => {
|
||||
setSearchParams(prev => {
|
||||
const nextParams = new URLSearchParams(prev);
|
||||
const ms = durationToMs(dur);
|
||||
|
||||
if (ms) {
|
||||
nextParams.set(REFRESH_URL_PARAM, `${dur}`);
|
||||
} else {
|
||||
nextParams.delete(REFRESH_URL_PARAM);
|
||||
}
|
||||
|
||||
return nextParams;
|
||||
});
|
||||
|
||||
handleCloseOptions();
|
||||
};
|
||||
|
||||
@@ -68,23 +96,19 @@ export const ExecutionControls: FC<ExecutionControlsProps> = ({ tooltip, useAuto
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const delay = selectedDelay.seconds;
|
||||
const ms = durationToMs(selectedDelay);
|
||||
let timer: number;
|
||||
if (autoRefresh) {
|
||||
|
||||
if (useAutorefresh && isValidDelay(ms)) {
|
||||
timer = setInterval(() => {
|
||||
dispatch({ type: "RUN_QUERY" });
|
||||
}, delay * 1000) as unknown as number;
|
||||
} else {
|
||||
setSelectedDelay(delayOptions[0]);
|
||||
}, ms) as unknown as number;
|
||||
}
|
||||
return () => {
|
||||
timer && clearInterval(timer);
|
||||
};
|
||||
}, [selectedDelay, autoRefresh]);
|
||||
|
||||
const createHandlerChange = (d: AutoRefreshOption) => () => {
|
||||
handleChange(d);
|
||||
};
|
||||
return () => {
|
||||
clearInterval(timer);
|
||||
};
|
||||
}, [selectedDelay, useAutorefresh]);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -106,7 +130,7 @@ export const ExecutionControls: FC<ExecutionControlsProps> = ({ tooltip, useAuto
|
||||
<span className="vm-mobile-option__icon"><RestartIcon/></span>
|
||||
<div className="vm-mobile-option-text">
|
||||
<span className="vm-mobile-option-text__label">Auto-refresh</span>
|
||||
<span className="vm-mobile-option-text__value">{selectedDelay.title}</span>
|
||||
<span className="vm-mobile-option-text__value">{selectedDelay}</span>
|
||||
</div>
|
||||
<span className="vm-mobile-option__arrow"><ArrowDownIcon/></span>
|
||||
</div>
|
||||
@@ -139,7 +163,7 @@ export const ExecutionControls: FC<ExecutionControlsProps> = ({ tooltip, useAuto
|
||||
)}
|
||||
onClick={toggleOpenOptions}
|
||||
>
|
||||
{selectedDelay.title}
|
||||
{selectedDelay}
|
||||
</Button>
|
||||
</div>
|
||||
</Tooltip>
|
||||
@@ -187,12 +211,12 @@ export const ExecutionControls: FC<ExecutionControlsProps> = ({ tooltip, useAuto
|
||||
className={classNames({
|
||||
"vm-list-item": true,
|
||||
"vm-list-item_mobile": isMobile,
|
||||
"vm-list-item_active": d.seconds === selectedDelay.seconds
|
||||
"vm-list-item_active": d === selectedDelay
|
||||
})}
|
||||
key={d.seconds}
|
||||
onClick={createHandlerChange(d)}
|
||||
key={d}
|
||||
onClick={handleChange(d)}
|
||||
>
|
||||
{d.title}
|
||||
{d}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
import dayjs from "dayjs";
|
||||
import { getNanoTimestamp, parseSupportedDuration } from "./time";
|
||||
import { getMillisecondsFromDuration, getNanoTimestamp, parseSupportedDuration } from "./time";
|
||||
|
||||
describe("Time utils", () => {
|
||||
describe("getNanoTimestamp", () => {
|
||||
@@ -82,4 +82,19 @@ describe("Time utils", () => {
|
||||
expect(parseSupportedDuration(" ")).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("getMillisecondsFromDuration", () => {
|
||||
it("should convert valid durations to milliseconds", () => {
|
||||
expect(getMillisecondsFromDuration("1s")).toBe(1000);
|
||||
expect(getMillisecondsFromDuration("1.5s")).toBe(1500);
|
||||
expect(getMillisecondsFromDuration("1m30s")).toBe(90000);
|
||||
expect(getMillisecondsFromDuration("500ms")).toBe(500);
|
||||
});
|
||||
|
||||
it("should reject invalid durations", () => {
|
||||
expect(() => getMillisecondsFromDuration("garbage1s")).toThrow("Invalid duration");
|
||||
expect(() => getMillisecondsFromDuration("1second")).toThrow("Invalid duration");
|
||||
expect(() => getMillisecondsFromDuration("")).toThrow("Invalid duration");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -29,6 +29,8 @@ export const supportedDurations = [
|
||||
|
||||
const shortDurations = supportedDurations.map(d => d.short);
|
||||
|
||||
const durationUnits = [...shortDurations].sort((a, b) => b.length - a.length).join("|");
|
||||
|
||||
export const sameTs = (a: number, b: number) => {
|
||||
return roundToThousandths(a) === roundToThousandths(b);
|
||||
};
|
||||
@@ -100,6 +102,16 @@ export const getSecondsFromDuration = (dur: string) => {
|
||||
return dayjs.duration(durObject).asSeconds();
|
||||
};
|
||||
|
||||
export const getMillisecondsFromDuration = (dur: string): number => {
|
||||
const fullRegexp = new RegExp(`^(?:\\d+(?:\\.\\d+)?(?:${durationUnits}))+$`);
|
||||
|
||||
if (!fullRegexp.test(dur)) {
|
||||
throw new Error(`Invalid duration: "${dur}"`);
|
||||
}
|
||||
|
||||
return getSecondsFromDuration(dur) * 1000;
|
||||
};
|
||||
|
||||
const instantQueryViews = [DisplayType.table, DisplayType.code];
|
||||
export const getStepFromDuration = (dur: number, histogram?: boolean, displayType?: DisplayType): string => {
|
||||
if (displayType && instantQueryViews.includes(displayType)) return roundStep(dur);
|
||||
@@ -276,4 +288,3 @@ export const getNanoTimestamp = (dateStr: string): bigint => {
|
||||
// Return the full timestamp in nanoseconds as a BigInt
|
||||
return BigInt(baseMs) * 1000000n + BigInt(extraNano);
|
||||
};
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ ROOT_IMAGE ?= alpine:3.24.1
|
||||
ROOT_IMAGE_SCRATCH ?= scratch
|
||||
CERTS_IMAGE := alpine:3.24.1
|
||||
|
||||
GO_BUILDER_IMAGE := golang:1.26.5
|
||||
GO_BUILDER_IMAGE := golang:1.26.4
|
||||
|
||||
BUILDER_IMAGE := local/builder:2.0.0-$(shell echo $(GO_BUILDER_IMAGE) | tr :/ __)-1
|
||||
BASE_IMAGE := local/base:1.1.4-$(shell echo $(ROOT_IMAGE) | tr :/ __)-$(shell echo $(CERTS_IMAGE) | tr :/ __)
|
||||
|
||||
@@ -3,7 +3,7 @@ services:
|
||||
# It scrapes targets defined in --promscrape.config
|
||||
# And forward them to --remoteWrite.url
|
||||
vmagent:
|
||||
image: victoriametrics/vmagent:v1.148.0
|
||||
image: victoriametrics/vmagent:v1.147.0
|
||||
depends_on:
|
||||
- "vmauth"
|
||||
ports:
|
||||
@@ -42,14 +42,14 @@ services:
|
||||
# vmstorage shards. Each shard receives 1/N of all metrics sent to vminserts,
|
||||
# where N is number of vmstorages (2 in this case).
|
||||
vmstorage-1:
|
||||
image: victoriametrics/vmstorage:v1.148.0-cluster
|
||||
image: victoriametrics/vmstorage:v1.147.0-cluster
|
||||
volumes:
|
||||
- strgdata-1:/storage
|
||||
command:
|
||||
- "--storageDataPath=/storage"
|
||||
restart: always
|
||||
vmstorage-2:
|
||||
image: victoriametrics/vmstorage:v1.148.0-cluster
|
||||
image: victoriametrics/vmstorage:v1.147.0-cluster
|
||||
volumes:
|
||||
- strgdata-2:/storage
|
||||
command:
|
||||
@@ -59,7 +59,7 @@ services:
|
||||
# vminsert is ingestion frontend. It receives metrics pushed by vmagent,
|
||||
# pre-process them and distributes across configured vmstorage shards.
|
||||
vminsert-1:
|
||||
image: victoriametrics/vminsert:v1.148.0-cluster
|
||||
image: victoriametrics/vminsert:v1.147.0-cluster
|
||||
depends_on:
|
||||
- "vmstorage-1"
|
||||
- "vmstorage-2"
|
||||
@@ -68,7 +68,7 @@ services:
|
||||
- "--storageNode=vmstorage-2:8400"
|
||||
restart: always
|
||||
vminsert-2:
|
||||
image: victoriametrics/vminsert:v1.148.0-cluster
|
||||
image: victoriametrics/vminsert:v1.147.0-cluster
|
||||
depends_on:
|
||||
- "vmstorage-1"
|
||||
- "vmstorage-2"
|
||||
@@ -80,7 +80,7 @@ services:
|
||||
# vmselect is a query fronted. It serves read queries in MetricsQL or PromQL.
|
||||
# vmselect collects results from configured `--storageNode` shards.
|
||||
vmselect-1:
|
||||
image: victoriametrics/vmselect:v1.148.0-cluster
|
||||
image: victoriametrics/vmselect:v1.147.0-cluster
|
||||
depends_on:
|
||||
- "vmstorage-1"
|
||||
- "vmstorage-2"
|
||||
@@ -90,7 +90,7 @@ services:
|
||||
- "--vmalert.proxyURL=http://vmalert:8880"
|
||||
restart: always
|
||||
vmselect-2:
|
||||
image: victoriametrics/vmselect:v1.148.0-cluster
|
||||
image: victoriametrics/vmselect:v1.147.0-cluster
|
||||
depends_on:
|
||||
- "vmstorage-1"
|
||||
- "vmstorage-2"
|
||||
@@ -105,7 +105,7 @@ services:
|
||||
# read requests from Grafana, vmui, vmalert among vmselects.
|
||||
# It can be used as an authentication proxy.
|
||||
vmauth:
|
||||
image: victoriametrics/vmauth:v1.148.0
|
||||
image: victoriametrics/vmauth:v1.147.0
|
||||
depends_on:
|
||||
- "vmselect-1"
|
||||
- "vmselect-2"
|
||||
@@ -119,7 +119,7 @@ services:
|
||||
|
||||
# vmalert executes alerting and recording rules
|
||||
vmalert:
|
||||
image: victoriametrics/vmalert:v1.148.0
|
||||
image: victoriametrics/vmalert:v1.147.0
|
||||
depends_on:
|
||||
- "vmauth"
|
||||
ports:
|
||||
|
||||
@@ -3,7 +3,7 @@ services:
|
||||
# It scrapes targets defined in --promscrape.config
|
||||
# And forward them to --remoteWrite.url
|
||||
vmagent:
|
||||
image: victoriametrics/vmagent:v1.148.0
|
||||
image: victoriametrics/vmagent:v1.147.0
|
||||
depends_on:
|
||||
- "victoriametrics"
|
||||
ports:
|
||||
@@ -18,7 +18,7 @@ services:
|
||||
# VictoriaMetrics instance, a single process responsible for
|
||||
# storing metrics and serve read requests.
|
||||
victoriametrics:
|
||||
image: victoriametrics/victoria-metrics:v1.148.0
|
||||
image: victoriametrics/victoria-metrics:v1.147.0
|
||||
ports:
|
||||
- 8428:8428
|
||||
- 8089:8089
|
||||
@@ -59,7 +59,7 @@ services:
|
||||
|
||||
# vmalert executes alerting and recording rules
|
||||
vmalert:
|
||||
image: victoriametrics/vmalert:v1.148.0
|
||||
image: victoriametrics/vmalert:v1.147.0
|
||||
depends_on:
|
||||
- "victoriametrics"
|
||||
- "alertmanager"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
services:
|
||||
vmagent:
|
||||
image: victoriametrics/vmagent:v1.148.0
|
||||
image: victoriametrics/vmagent:v1.147.0
|
||||
depends_on:
|
||||
- "victoriametrics"
|
||||
ports:
|
||||
@@ -14,7 +14,7 @@ services:
|
||||
restart: always
|
||||
|
||||
victoriametrics:
|
||||
image: victoriametrics/victoria-metrics:v1.148.0
|
||||
image: victoriametrics/victoria-metrics:v1.147.0
|
||||
ports:
|
||||
- 8428:8428
|
||||
volumes:
|
||||
@@ -40,7 +40,7 @@ services:
|
||||
restart: always
|
||||
|
||||
vmalert:
|
||||
image: victoriametrics/vmalert:v1.148.0
|
||||
image: victoriametrics/vmalert:v1.147.0
|
||||
depends_on:
|
||||
- "victoriametrics"
|
||||
ports:
|
||||
|
||||
@@ -10,9 +10,9 @@ sitemap:
|
||||
|
||||
- To use *vmanomaly*, part of the enterprise package, a license key is required. Obtain your key [here](https://victoriametrics.com/products/enterprise/trial/) for this tutorial or for enterprise use.
|
||||
- In the tutorial, we'll be using the following VictoriaMetrics components:
|
||||
- [VictoriaMetrics Single-Node](https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/) (v1.148.0)
|
||||
- [vmalert](https://docs.victoriametrics.com/victoriametrics/vmalert/) (v1.148.0)
|
||||
- [vmagent](https://docs.victoriametrics.com/victoriametrics/vmagent/) (v1.148.0)
|
||||
- [VictoriaMetrics Single-Node](https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/) (v1.147.0)
|
||||
- [vmalert](https://docs.victoriametrics.com/victoriametrics/vmalert/) (v1.147.0)
|
||||
- [vmagent](https://docs.victoriametrics.com/victoriametrics/vmagent/) (v1.147.0)
|
||||
- [Grafana](https://grafana.com/) (v12.2.0)
|
||||
- [Docker](https://docs.docker.com/get-docker/) and [Docker Compose](https://docs.docker.com/compose/)
|
||||
- [Node exporter](https://github.com/prometheus/node_exporter#node-exporter) (v1.9.1) and [Alertmanager](https://prometheus.io/docs/alerting/latest/alertmanager/) (v0.28.1)
|
||||
@@ -323,7 +323,7 @@ Let's wrap it all up together into the `docker-compose.yml` file.
|
||||
services:
|
||||
vmagent:
|
||||
container_name: vmagent
|
||||
image: victoriametrics/vmagent:v1.148.0
|
||||
image: victoriametrics/vmagent:v1.147.0
|
||||
depends_on:
|
||||
- "victoriametrics"
|
||||
ports:
|
||||
@@ -340,7 +340,7 @@ services:
|
||||
|
||||
victoriametrics:
|
||||
container_name: victoriametrics
|
||||
image: victoriametrics/victoria-metrics:v1.148.0
|
||||
image: victoriametrics/victoria-metrics:v1.147.0
|
||||
ports:
|
||||
- 8428:8428
|
||||
volumes:
|
||||
@@ -373,7 +373,7 @@ services:
|
||||
|
||||
vmalert:
|
||||
container_name: vmalert
|
||||
image: victoriametrics/vmalert:v1.148.0
|
||||
image: victoriametrics/vmalert:v1.147.0
|
||||
depends_on:
|
||||
- "victoriametrics"
|
||||
ports:
|
||||
|
||||
@@ -1,426 +0,0 @@
|
||||
Several VictoriaMetrics components can connect to cloud storage to read or write object data.
|
||||
|
||||
The following table shows the supported types of storage for each component:
|
||||
|
||||
| Component | AWS S3 and S3-compatible | Google Cloud Storage | Azure Blob Storage |
|
||||
|-----------|----|----------------------|--------------------|
|
||||
| [vmbackup](https://docs.victoriametrics.com/victoriametrics/vmbackup/) | ✅ | ✅ | ✅ |
|
||||
| [vmrestore](https://docs.victoriametrics.com/victoriametrics/vmrestore/) | ✅ | ✅ | ✅ |
|
||||
| [vmbackupmanager](https://docs.victoriametrics.com/victoriametrics/vmbackupmanager/) | ✅ | ✅ | ✅ |
|
||||
| [vmalert](https://docs.victoriametrics.com/victoriametrics/vmalert/) | ✅ | ✅ | ❌ |
|
||||
|
||||
All these components use the same underlying libraries, so the authentication setup is largely the same. The main difference is in the command-line flags:
|
||||
|
||||
- vmalert uses `-s3.*` prefixed flags (e.g., `-s3.credsFilePath`)
|
||||
- backup and restore tools use unprefixed flags (e.g., `-credsFilePath`)
|
||||
|
||||
See the [component reference](https://docs.victoriametrics.com/guides/connecting-vm-components-to-cloud-storage/#per-component-flag-reference) for details.
|
||||
|
||||
## Obtaining credentials
|
||||
|
||||
You need to supply credentials so the component can connect to the cloud storage. The setup differs by provider; the sections below cover AWS S3, S3‑compatible systems, GCS, and Azure Blob Storage.
|
||||
|
||||
### AWS S3
|
||||
|
||||
1. In AWS, [create an IAM user](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_users_create.html) or role with the minimum permissions for the target bucket (see table below).
|
||||
1. [Create an access key](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html) for that IAM identity.
|
||||
1. Copy the **Access key ID** and **Secret access key** values. You will use them in the credentials file or environment variables.
|
||||
|
||||
| Component | S3 usage | Minimum S3 permissions |
|
||||
|-----------------|-------------------------------------------------------------|------------------------|
|
||||
| vmalert (Enterprise) | Reads alerting/recording rules from bucket. | `s3:GetObject`, `s3:ListBucket` on the rules bucket/prefix.|
|
||||
| vmrestore | Restores backups from S3 buckets | `s3:GetObject`, `s3:ListBucket` on the backup bucket/prefix.|
|
||||
| vmbackup | Uploads backups to S3 and may delete old backup objects during maintenance.| `s3:PutObject`, `s3:GetObject`, `s3:ListBucket`, `s3:DeleteObject` on the backup bucket/prefix.|
|
||||
| vmbackupmanager (Enterprise) | Automates backups using vmbackup behavior. | Same as vmbackup. |
|
||||
|
||||
|
||||
### S3-compatible storage (MinIO, Ceph)
|
||||
|
||||
Generate access keys using your storage system's admin interface or CLI. The credentials follow the same format as AWS S3.
|
||||
|
||||
### Google Cloud Storage
|
||||
|
||||
1. Open the Google Cloud Console and go to **IAM & Admin > Service Accounts**.
|
||||
1. Click **Create service account**, enter a name, and assign a Storage role (for example, Storage Object Admin).
|
||||
1. Open the service account, go to **Keys**, then click **Add key > Create new key**.
|
||||
1. Choose **JSON** as the key type and click **Create**.
|
||||
1. Store the JSON file on the machine running the VictoriaMetrics component.
|
||||
|
||||
### Azure Blob Storage
|
||||
|
||||
> Azure does not use credential files.
|
||||
|
||||
1. Log in to the Azure Portal.
|
||||
1. Search for and select **Storage accounts**.
|
||||
1. Click on your specific storage account name. (This is your `AZURE_STORAGE_ACCOUNT_NAME`).
|
||||
1. In the left menu under **Security + networking**, click **Access keys**.
|
||||
1. Copy the key value from either **key1** or **key2** (this is your `AZURE_STORAGE_ACCOUNT_KEY`).
|
||||
1. Define the access keys as environment variables.
|
||||
```sh
|
||||
export AZURE_STORAGE_ACCOUNT_NAME=mystorageaccount
|
||||
export AZURE_STORAGE_ACCOUNT_KEY=myaccountkey
|
||||
```
|
||||
|
||||
## Authenticating with the cloud provider
|
||||
|
||||
Provide the credentials as a file or with environment variables, along with the path to the cloud storage bucket. The syntax for the bucket name depends on the cloud provider:
|
||||
|
||||
- `s3://`: for AWS S3 and self-hosted S3-compatible storage (MinIO, Ceph)
|
||||
- `gs://`: Google Cloud Storage
|
||||
- `azblob://`: Azure Blob Storage
|
||||
|
||||
### vmbackup and vmrestore
|
||||
|
||||
The following example backups to an AWS S3 bucket using a credentials file:
|
||||
|
||||
```sh
|
||||
vmbackup \
|
||||
-storageDataPath=/data \
|
||||
-snapshot.createURL=http://localhost:8428/snapshot/create \
|
||||
-dst=s3://victoriametrics-backup/backup01 \
|
||||
-credsFilePath=/etc/credentials
|
||||
```
|
||||
|
||||
In order to restore from the same backup from AWS S3:
|
||||
|
||||
```sh
|
||||
vmrestore \
|
||||
-src=s3://victoriametrics-backup/backup01 \
|
||||
-storageDataPath=/data \
|
||||
-credsFilePath=/etc/credentials
|
||||
|
||||
```
|
||||
|
||||
> To use non-AWS S3 buckets such as MinIO or Ceph, you must [supply the `-customS3Endpoint` argument](https://docs.victoriametrics.com/guides/connecting-vm-components-to-cloud-storage/#s3-compatible).
|
||||
|
||||
Alternatively, you can set the access keys as environment variables instead of using a credential file:
|
||||
|
||||
```sh
|
||||
export AWS_ACCESS_KEY_ID=YOUR_AWS_ACCESS_KEY
|
||||
export AWS_SECRET_ACCESS_KEY=YOUR_SECRET_AWS_ACCESS_KEY
|
||||
|
||||
vmbackup \
|
||||
-storageDataPath=/data \
|
||||
-snapshot.createURL=http://localhost:8428/snapshot/create \
|
||||
-dst=s3://victoriametrics-backup/backup01
|
||||
|
||||
vmrestore \
|
||||
-src=s3://victoriametrics-backup/backup01 \
|
||||
-storageDataPath=/data
|
||||
```
|
||||
|
||||
|
||||
Backups on Google Cloud Storage use the `gs://` prefix in the destination:
|
||||
|
||||
```sh
|
||||
vmbackup \
|
||||
-storageDataPath=/data \
|
||||
-snapshot.createURL=http://localhost:8428/snapshot/create \
|
||||
-dst=gs://victoriametrics-backup/backup01 \
|
||||
-credsFilePath=/etc/credentials
|
||||
```
|
||||
|
||||
You can restore this backup with:
|
||||
|
||||
```sh
|
||||
vmrestore \
|
||||
-src=gs://victoriametrics-backup/backup01 \
|
||||
-storageDataPath=/data \
|
||||
-credsFilePath=/etc/credentials
|
||||
```
|
||||
|
||||
On Google Cloud, you can define the path to the JSON credential file with `GOOGLE_APPLICATION_CREDENTIALS`. For example:
|
||||
|
||||
```sh
|
||||
export GOOGLE_APPLICATION_CREDENTIALS=/etc/credentials
|
||||
|
||||
vmbackup \
|
||||
-storageDataPath=/data \
|
||||
-snapshot.createURL=http://localhost:8428/snapshot/create \
|
||||
-dst=gs://victoriametrics-backup/backup01
|
||||
|
||||
vmrestore \
|
||||
-src=gs://victoriametrics-backup/backup01 \
|
||||
-storageDataPath=/data
|
||||
```
|
||||
|
||||
For Azure Blob Storage, use the `azblob://` prefix and rely on environment variables instead of `-credsFilePath`.
|
||||
|
||||
```sh
|
||||
export AZURE_STORAGE_ACCOUNT_NAME=myaccount
|
||||
export AZURE_STORAGE_ACCOUNT_KEY=mykey
|
||||
|
||||
vmbackup \
|
||||
-storageDataPath=/data \
|
||||
-snapshot.createURL=http://localhost:8428/snapshot/create \
|
||||
-dst=azblob://victoriametrics-backup/backup01
|
||||
|
||||
vmrestore \
|
||||
-src=azblob://victoriametrics-backup/backup01 \
|
||||
-storageDataPath=/data
|
||||
```
|
||||
|
||||
### vmbackupmanager
|
||||
|
||||
> vmbackupmanager only works in the [Enterprise](https://docs.victoriametrics.com/victoriametrics/enterprise/) edition.
|
||||
|
||||
To manage backups with vmbackupmanager on AWS S3, add the credentials with the `-credsFilePath` flag:
|
||||
|
||||
```sh
|
||||
vmbackupmanager \
|
||||
-dst=s3://vmstorage-data/backups \
|
||||
-credsFilePath=/etc/credentials \
|
||||
-storageDataPath=/vmstorage-data \
|
||||
-snapshot.createURL=http://vmstorage:8482/snapshot/create \
|
||||
-licenseFile=/etc/vm-license
|
||||
```
|
||||
|
||||
Or define the access keys using environment variables:
|
||||
|
||||
```sh
|
||||
export AWS_ACCESS_KEY_ID=YOUR_AWS_ACCESS_KEY
|
||||
export AWS_SECRET_ACCESS_KEY=YOUR_SECRET_AWS_ACCESS_KEY
|
||||
|
||||
vmbackupmanager \
|
||||
-dst=s3://vmstorage-data/backups \
|
||||
-storageDataPath=/vmstorage-data \
|
||||
-snapshot.createURL=http://vmstorage:8482/snapshot/create \
|
||||
-licenseFile=/etc/vm-license
|
||||
```
|
||||
|
||||
> To use non-AWS S3 buckets, you must [supply the `-customS3Endpoint` argument](#s3-compatible).
|
||||
|
||||
Automated backups on Google Cloud Storage take the following form:
|
||||
|
||||
```sh
|
||||
vmbackupmanager \
|
||||
-dst=gs://vmstorage-data/backups \
|
||||
-credsFilePath=/etc/credentials \
|
||||
-storageDataPath=/vmstorage-data \
|
||||
-snapshot.createURL=http://vmstorage:8482/snapshot/create \
|
||||
-licenseFile=/etc/vm-license
|
||||
```
|
||||
|
||||
As with vmbackup and vmrestore, you can also define the path to the JSON credential file with `GOOGLE_APPLICATION_CREDENTIALS`:
|
||||
|
||||
```sh
|
||||
export GOOGLE_APPLICATION_CREDENTIALS=/etc/credentials
|
||||
|
||||
vmbackupmanager \
|
||||
-dst=gs://vmstorage-data/backups \
|
||||
-storageDataPath=/vmstorage-data \
|
||||
-snapshot.createURL=http://vmstorage:8482/snapshot/create \
|
||||
-licenseFile=/etc/vm-license
|
||||
```
|
||||
|
||||
vmbackupmanager can also use Azure Blob Storage by defining environment variables:
|
||||
|
||||
```sh
|
||||
export AZURE_STORAGE_ACCOUNT_NAME=mystorageaccount
|
||||
export AZURE_STORAGE_ACCOUNT_KEY=myaccountkey
|
||||
|
||||
vmbackupmanager \
|
||||
-dst=azblob://vmstorage-data/backups \
|
||||
-storageDataPath=/vmstorage-data \
|
||||
-snapshot.createURL=http://vmstorage:8482/snapshot/create \
|
||||
-licenseFile=/etc/vm-license
|
||||
```
|
||||
|
||||
### vmalert
|
||||
|
||||
> - vmalert cloud storage command line flags are prefixed with `-s3.` for S3 buckets *and* Google Cloud Storage.
|
||||
> - Cloud storage only works in the [Enterprise](https://docs.victoriametrics.com/victoriametrics/enterprise/) edition.
|
||||
|
||||
Read alerting rules from an S3 bucket. The `-rule` flag accepts a prefix, so it matches all files starting with `alerts_` in the `rules` folder:
|
||||
|
||||
```sh
|
||||
vmalert \
|
||||
-rule=s3://my-alert-bucket/rules/alerts_ \
|
||||
-s3.credsFilePath=/etc/vmalert/aws-credentials \
|
||||
-datasource.url=http://vmselect:8481/select/0/prometheus \
|
||||
-notifier.url=http://alertmanager:9093 \
|
||||
-licenseFile=/etc/vm-license
|
||||
```
|
||||
|
||||
Instead of a credential file, you can supply the access keys using environment variables:
|
||||
|
||||
```sh
|
||||
export AWS_ACCESS_KEY_ID=YOUR_AWS_ACCESS_KEY
|
||||
export AWS_SECRET_ACCESS_KEY=YOUR_SECRET_AWS_ACCESS_KEY
|
||||
|
||||
vmalert \
|
||||
-rule=s3://my-alert-bucket/rules/alerts_ \
|
||||
-datasource.url=http://vmselect:8481/select/0/prometheus \
|
||||
-notifier.url=http://alertmanager:9093 \
|
||||
-licenseFile=/etc/vm-license
|
||||
```
|
||||
|
||||
> To use non-AWS S3 buckets, you must [supply the `-s3.customEndpoint` argument](#s3-compatible).
|
||||
|
||||
To read rules from Google Cloud Storage:
|
||||
|
||||
```sh
|
||||
vmalert \
|
||||
-rule=gs://my-alert-bucket/rules/alerts_ \
|
||||
-s3.credsFilePath=/etc/vmalert/gcp-service-account.json \
|
||||
-datasource.url=http://vmselect:8481/select/0/prometheus \
|
||||
-notifier.url=http://alertmanager:9093 \
|
||||
-licenseFile=/etc/vm-license
|
||||
```
|
||||
|
||||
If you prefer, you can supply the path to the JSON credential file with the `GOOGLE_APPLICATION_CREDENTIALS` environment variable:
|
||||
|
||||
```sh
|
||||
export GOOGLE_APPLICATION_CREDENTIALS=/etc/credentials
|
||||
|
||||
vmalert \
|
||||
-rule=gs://my-alert-bucket/rules/alerts_ \
|
||||
-datasource.url=http://vmselect:8481/select/0/prometheus \
|
||||
-notifier.url=http://alertmanager:9093 \
|
||||
-licenseFile=/etc/vm-license
|
||||
```
|
||||
|
||||
## Credentials files format
|
||||
|
||||
The file format depends on the storage provider.
|
||||
|
||||
### S3 credentials
|
||||
|
||||
The file uses the standard AWS shared credentials format used by the [AWS CLI](https://docs.aws.amazon.com/cli/v1/userguide/cli-configure-files.html) and [AWS SDKs](https://docs.aws.amazon.com/sdkref/latest/guide/file-format.html):
|
||||
|
||||
```ini
|
||||
[default]
|
||||
aws_access_key_id=YOUR_AWS_ACCESS_KEY
|
||||
aws_secret_access_key=YOUR_AWS_SECRET_ACCESS_KEY
|
||||
```
|
||||
|
||||
You can define multiple profiles in a single file:
|
||||
|
||||
```ini
|
||||
[default]
|
||||
aws_access_key_id=DEFAULT_ACCESS_KEY
|
||||
aws_secret_access_key=DEFAULT_SECRET_KEY
|
||||
|
||||
[monitoring]
|
||||
aws_access_key_id=MONITORING_ACCESS_KEY
|
||||
aws_secret_access_key=MONITORING_SECRET_KEY
|
||||
|
||||
[alerts]
|
||||
aws_access_key_id=ALERTS_ACCESS_KEY
|
||||
aws_secret_access_key=ALERTS_SECRET_KEY
|
||||
```
|
||||
|
||||
Use the `-configProfile` flag (or `-s3.configProfile` in vmalert) to select a non-default profile:
|
||||
|
||||
```sh
|
||||
-configProfile=alerts
|
||||
```
|
||||
|
||||
You can separate credentials from other configuration settings. Put credentials in one file:
|
||||
|
||||
```ini
|
||||
[default]
|
||||
aws_access_key_id=DEFAULT_ACCESS_KEY
|
||||
aws_secret_access_key=DEFAULT_SECRET_KEY
|
||||
```
|
||||
|
||||
And non-sensitive settings in another:
|
||||
|
||||
```ini
|
||||
[default]
|
||||
region=us-east-1
|
||||
```
|
||||
|
||||
Then pass both files:
|
||||
|
||||
```sh
|
||||
-configFilePath=/etc/aws-config \
|
||||
-credsFilePath=/etc/credentials
|
||||
```
|
||||
|
||||
### GCS credentials file format
|
||||
|
||||
The file is the JSON key downloaded from Google Cloud Console. Its content looks like this:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "service_account",
|
||||
"project_id": "project-id",
|
||||
"private_key_id": "key-id",
|
||||
"private_key": "-----BEGIN PRIVATE KEY-----\nprivate-key\n-----END PRIVATE KEY-----\n",
|
||||
"client_email": "service-account-email",
|
||||
"client_id": "client-id",
|
||||
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
|
||||
"token_uri": "https://accounts.google.com/o/oauth2/token",
|
||||
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
|
||||
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/service-account-email"
|
||||
}
|
||||
```
|
||||
|
||||
This is the standard service account key format defined by [Google Cloud IAM](https://developers.google.com/workspace/guides/create-credentials).
|
||||
|
||||
### Azure Blob Storage
|
||||
|
||||
Azure does not support credentials via file. Use environment variables instead.
|
||||
|
||||
```sh
|
||||
export AZURE_STORAGE_ACCOUNT_NAME=mystorageaccount
|
||||
export AZURE_STORAGE_ACCOUNT_KEY=myaccountkey
|
||||
```
|
||||
|
||||
## Self-hosted and S3-compatible endpoints
|
||||
|
||||
For S3-compatible storage such as MinIO or Ceph, set a custom endpoint with the `-customS3Endpoint` flag for vmbackup, vmrestore, and vmbackupmanager. For example:
|
||||
|
||||
```sh
|
||||
vmbackup \
|
||||
-storageDataPath=/data \
|
||||
-snapshot.createURL=http://localhost:8428/snapshot/create \
|
||||
-dst=s3://victoriametrics-backup/backup01 \
|
||||
-customS3Endpoint=http://minio.example.local:9000
|
||||
```
|
||||
|
||||
On vmalert, use the `-s3.customEndpoint` flag instead:
|
||||
|
||||
```sh
|
||||
vmalert \
|
||||
-rule=s3://my-alert-bucket/rules/alerts_ \
|
||||
-s3.customEndpoint=http://minio.example.local:9000 \
|
||||
-s3.credsFilePath=/etc/vmalert/aws-credentials \
|
||||
-datasource.url=http://vmselect:8481/select/0/prometheus \
|
||||
-notifier.url=http://alertmanager:9093 \
|
||||
-licenseFile=/etc/vm-license
|
||||
```
|
||||
|
||||
### Addressing S3-compatible buckets {#s3-compatible}
|
||||
|
||||
When connecting to non-AWS S3-compatible buckets, there is an additional flag you might need to configure:
|
||||
|
||||
- `-s3ForcePathStyle`: on vmbackupmanager, vmbackup, and vmrestore.
|
||||
- `-s3.forcePathStyle`: on vmalert.
|
||||
|
||||
The flag changes the expected URL pattern for a bucket.
|
||||
|
||||
| Flag value | Address-style | Example | Use with |
|
||||
|------------|---------------|---------|----------|
|
||||
| `true` (default) | Path-style | `https://endpoint/bucket/key` | MinIO, Ceph, most S3-compatible storages |
|
||||
| `false` | Virtual host-style | `https://bucket.endpoint/key` | [Aliyun OSS](https://www.aliyun.com/product/oss) and other endpoints that require it |
|
||||
|
||||
> The flag only takes effect when you use a custom endpoint (`-customS3Endpoint` or `-s3.customEndpoint` on vmalert). When connecting to real AWS S3, the SDK handles addressing automatically.
|
||||
|
||||
## Per-component flag reference
|
||||
|
||||
The table below shows how the same concept maps to different flag names across components.
|
||||
|
||||
| Concept | vmalert | vmbackup, vmrestore, and vmbackupmanager |
|
||||
|---|---|---|---|---|
|
||||
| Credentials file | `-s3.credsFilePath` | `-credsFilePath` |
|
||||
| Config file | `-s3.configFilePath` | `-configFilePath` |
|
||||
| Profile selection | `-s3.configProfile` | `-configProfile` |
|
||||
| Custom endpoint | `-s3.customEndpoint` | `-customS3Endpoint` |
|
||||
| Force path style | `-s3.forcePathStyle` | `-s3ForcePathStyle` |
|
||||
| TLS insecure | N/A | `-s3TLSInsecureSkipVerify` |
|
||||
| Storage class | N/A | `-s3StorageClass` |
|
||||
|
||||
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
---
|
||||
weight: 5
|
||||
title: Connecting VictoriaMetrics components to cloud storage
|
||||
menu:
|
||||
docs:
|
||||
parent: guides
|
||||
weight: 5
|
||||
tags:
|
||||
- metrics
|
||||
- guide
|
||||
---
|
||||
{{% content "README.md" %}}
|
||||
@@ -240,23 +240,23 @@ vmagent will write data into VictoriaMetrics single-node and cluster (with tenan
|
||||
# compose.yaml
|
||||
services:
|
||||
vmsingle:
|
||||
image: victoriametrics/victoria-metrics:v1.148.0
|
||||
image: victoriametrics/victoria-metrics:v1.147.0
|
||||
|
||||
vmstorage:
|
||||
image: victoriametrics/vmstorage:v1.148.0-cluster
|
||||
image: victoriametrics/vmstorage:v1.147.0-cluster
|
||||
|
||||
vminsert:
|
||||
image: victoriametrics/vminsert:v1.148.0-cluster
|
||||
image: victoriametrics/vminsert:v1.147.0-cluster
|
||||
command:
|
||||
- -storageNode=vmstorage:8400
|
||||
|
||||
vmselect:
|
||||
image: victoriametrics/vmselect:v1.148.0-cluster
|
||||
image: victoriametrics/vmselect:v1.147.0-cluster
|
||||
command:
|
||||
- -storageNode=vmstorage:8401
|
||||
|
||||
vmagent:
|
||||
image: victoriametrics/vmagent:v1.148.0
|
||||
image: victoriametrics/vmagent:v1.147.0
|
||||
volumes:
|
||||
- ./scrape.yaml:/etc/vmagent/config.yaml
|
||||
command:
|
||||
@@ -308,7 +308,7 @@ Now add the vmauth service to `compose.yaml`:
|
||||
# compose.yaml
|
||||
services:
|
||||
vmauth:
|
||||
image: docker.io/victoriametrics/vmauth:v1.148.0
|
||||
image: docker.io/victoriametrics/vmauth:v1.147.0
|
||||
ports:
|
||||
- 8427:8427
|
||||
volumes:
|
||||
|
||||
@@ -155,15 +155,15 @@ These services will store and query the metrics scraped by vmagent.
|
||||
# compose.yaml
|
||||
services:
|
||||
vmstorage:
|
||||
image: victoriametrics/vmstorage:v1.148.0-cluster
|
||||
image: victoriametrics/vmstorage:v1.147.0-cluster
|
||||
|
||||
vminsert:
|
||||
image: victoriametrics/vminsert:v1.148.0-cluster
|
||||
image: victoriametrics/vminsert:v1.147.0-cluster
|
||||
command:
|
||||
- -storageNode=vmstorage:8400
|
||||
|
||||
vmselect:
|
||||
image: victoriametrics/vmselect:v1.148.0-cluster
|
||||
image: victoriametrics/vmselect:v1.147.0-cluster
|
||||
command:
|
||||
- -storageNode=vmstorage:8401
|
||||
ports:
|
||||
@@ -196,7 +196,7 @@ Add the vmauth service to `compose.yaml`:
|
||||
# compose.yaml
|
||||
services:
|
||||
vmauth:
|
||||
image: victoriametrics/vmauth:v1.148.0-enterprise
|
||||
image: victoriametrics/vmauth:v1.147.0-enterprise
|
||||
ports:
|
||||
- 8427:8427
|
||||
volumes:
|
||||
@@ -251,7 +251,7 @@ Add the vmagent service to `compose.yaml` with OAuth2 configuration:
|
||||
# compose.yaml
|
||||
services:
|
||||
vmagent:
|
||||
image: victoriametrics/vmagent:v1.148.0
|
||||
image: victoriametrics/vmagent:v1.147.0
|
||||
volumes:
|
||||
- ./scrape.yaml:/etc/vmagent/config.yaml
|
||||
command:
|
||||
|
||||
@@ -107,7 +107,7 @@ The final piece is the Docker Compose file. This ties all the services together
|
||||
# compose.yml
|
||||
services:
|
||||
victoriametrics:
|
||||
image: victoriametrics/victoria-metrics:v1.148.0
|
||||
image: victoriametrics/victoria-metrics:v1.147.0
|
||||
command:
|
||||
- "--storageDataPath=/victoria-metrics-data"
|
||||
- "--selfScrapeInterval=10s"
|
||||
@@ -128,7 +128,7 @@ services:
|
||||
- ./alertmanager.yml:/etc/alertmanager/alertmanager.yml:ro
|
||||
|
||||
vmalert:
|
||||
image: victoriametrics/vmalert:v1.148.0
|
||||
image: victoriametrics/vmalert:v1.147.0
|
||||
depends_on:
|
||||
- victoriametrics
|
||||
- alertmanager
|
||||
|
||||
@@ -27,5 +27,5 @@ to [the latest available releases](https://docs.victoriametrics.com/victoriametr
|
||||
|
||||
## Currently supported LTS release lines
|
||||
|
||||
- v1.136.x - the latest one is [v1.136.14 LTS release](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.136.14)
|
||||
- v1.122.x - the latest one is [v1.122.27 LTS release](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.122.27)
|
||||
- v1.136.x - the latest one is [v1.136.4 LTS release](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.136.4)
|
||||
- v1.122.x - the latest one is [v1.122.19 LTS release](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.122.19)
|
||||
|
||||
@@ -53,8 +53,8 @@ and unpack it. It contains a single `victoria-metrics-prod` binary.
|
||||
For example, on Linux with `amd64` architecture:
|
||||
|
||||
```sh
|
||||
wget https://github.com/VictoriaMetrics/VictoriaMetrics/releases/download/v1.148.0/victoria-metrics-linux-amd64-v1.148.0.tar.gz
|
||||
tar xzf victoria-metrics-linux-amd64-v1.148.0.tar.gz
|
||||
wget https://github.com/VictoriaMetrics/VictoriaMetrics/releases/download/v1.147.0/victoria-metrics-linux-amd64-v1.147.0.tar.gz
|
||||
tar xzf victoria-metrics-linux-amd64-v1.147.0.tar.gz
|
||||
```
|
||||
|
||||
The binary is self-contained and requires no installation - it is ready to run as is.
|
||||
@@ -229,9 +229,9 @@ Download the newest available [VictoriaMetrics release](https://docs.victoriamet
|
||||
from [DockerHub](https://hub.docker.com/r/victoriametrics/victoria-metrics) or [Quay](https://quay.io/repository/victoriametrics/victoria-metrics?tab=tags):
|
||||
|
||||
```sh
|
||||
docker pull victoriametrics/victoria-metrics:v1.148.0
|
||||
docker pull victoriametrics/victoria-metrics:v1.147.0
|
||||
docker run -it --rm -v `pwd`/victoria-metrics-data:/victoria-metrics-data -p 8428:8428 \
|
||||
victoriametrics/victoria-metrics:v1.148.0 --selfScrapeInterval=5s -storageDataPath=victoria-metrics-data
|
||||
victoriametrics/victoria-metrics:v1.147.0 --selfScrapeInterval=5s -storageDataPath=victoria-metrics-data
|
||||
```
|
||||
|
||||
_For Enterprise images, see [this link](https://docs.victoriametrics.com/victoriametrics/enterprise/#docker-images)._
|
||||
|
||||
@@ -26,36 +26,26 @@ See also [LTS releases](https://docs.victoriametrics.com/victoriametrics/lts-rel
|
||||
|
||||
## tip
|
||||
|
||||
## [v1.148.0](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.148.0)
|
||||
|
||||
Released at 2026-07-20
|
||||
|
||||
* SECURITY: upgrade Go builder from Go1.26.4 to Go1.26.5. See [the list of issues addressed in Go1.26.5](https://github.com/golang/go/issues?q=milestone%3AGo1.26.5%20label%3ACherryPickApproved).
|
||||
|
||||
* FEATURE: [MetricsQL](https://docs.victoriametrics.com/victoriametrics/metricsql/): support `fill` modifiers to allow missing series on either side of a binary operation to be filled with a provided default value. See [#10598](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/10598).
|
||||
* FEATURE: [vmagent](https://docs.victoriametrics.com/victoriametrics/vmagent/) and [vmsingle](https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/): support scraping metrics over Unix domain sockets. The socket path can be configured via the `__unix_socket__` target label. See [#11156](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/11156). Thanks to @vinyas-bharadwaj for contribution.
|
||||
* FEATURE: [vmagent](https://docs.victoriametrics.com/victoriametrics/vmagent/) and [vmsingle](https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/): Improve background discovery performance for [http_sd](https://docs.victoriametrics.com/victoriametrics/sd_configs/#http_sd_configs) discovery. See [#8838](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/8838).
|
||||
* FEATURE: [vmagent](https://docs.victoriametrics.com/victoriametrics/vmagent/) and [vmsingle](https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/): allow overriding `max_scrape_size` on a per-target basis via the `__max_scrape_size__` label during target relabeling. See [#11188](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/11188).
|
||||
* FEATURE: [vmstorage](https://docs.victoriametrics.com/victoriametrics/cluster-victoriametrics/) and [vmsingle](https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/): add `-maxBackfillAge` command-line flag for limiting ingestion of samples with historical timestamps, for example, when older data has been moved between storage tiers (nvme/hdd, hot/cold). See [#11199](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/11199). Thanks to @AshwinRamaniPsg for contribution.
|
||||
* FEATURE: [vmagent](https://docs.victoriametrics.com/victoriametrics/vmagent/): automatically preload relabeling rules configured via `-remoteWrite.relabelConfig` and `-remoteWrite.urlRelabelConfig` in the [metrics relabel debug UI](https://docs.victoriametrics.com/victoriametrics/relabeling/#relabel-debugging). See [#9918](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/9918).
|
||||
* FEATURE: [vmui](https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/#vmui): persist the selected auto-refresh interval in the URL. See [#1310](https://github.com/VictoriaMetrics/VictoriaLogs/issues/1310).
|
||||
|
||||
* BUGFIX: `vminsert` in [VictoriaMetrics cluster](https://docs.victoriametrics.com/victoriametrics/cluster-victoriametrics/): Now drops metadata blocks when communicating with vmstorage nodes over the legacy RPC protocol. To avoid this limitation, upgrade `vmstorage` to a version that supports the new RPC protocol (>= [v1.137.0](https://github.com/VictoriaMetrics/VictoriaMetrics/blob/master/docs/victoriametrics/changelog/CHANGELOG.md#v11370)). See [#11146](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/11146).
|
||||
* BUGFIX: [vmbackup](https://docs.victoriametrics.com/victoriametrics/vmbackup/) and [vmbackupmanager](https://docs.victoriametrics.com/victoriametrics/vmbackupmanager/): retry S3 requests failing with `HTTP 429` status code or `TooManyRequests` error code. Previously such requests were not retried, so a short burst of rate limiting would fail the whole backup. See [#11218](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/11218). Thanks to @gautamrizwani for contribution.
|
||||
* BUGFIX: `vmselect` in [VictoriaMetrics cluster](https://docs.victoriametrics.com/victoriametrics/cluster-victoriametrics/): properly apply limit to metrics metadata response. See [#11139](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/11139).
|
||||
* BUGFIX: [vmagent](https://docs.victoriametrics.com/victoriametrics/vmagent/): fix a possible data race when processing OpenTelemetry metadata. See [#11238](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/11238). Thanks to @nevgeny for contribution.
|
||||
* BUGFIX: [vmagent](https://docs.victoriametrics.com/victoriametrics/vmagent/): flush pending persistent queue data to chunk file before updating the metadata. This prevents the metadata writer offset from getting ahead of the chunk file size and avoids losing the persistent queue after an unclean shutdown. See [#11192](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/11192).
|
||||
* BUGFIX: [vmagent](https://docs.victoriametrics.com/victoriametrics/vmagent/): atomically write persistent queue metainfo to prevent possible file corruption on ungraceful shutdown. See [#11192](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/11192).
|
||||
* BUGFIX: [vmagent](https://docs.victoriametrics.com/victoriametrics/vmagent/): fix increased CPU and memory usage when `-remoteWrite.urlRelabelConfig` or `-remoteWrite.streamAggr.config` flags are used. The bug was introduced in [#10854](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/10854) and existed since [v1.147.0](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.147.0). See [#11250](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/11250).
|
||||
* BUGFIX: [vmui](https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/#vmui): preserve newline formatting in alert and rule annotations on the Alerting page. See [#11171](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/11171).
|
||||
* BUGFIX: [vmui](https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/#vmui): hide `Total metric names` stats on [Cardinality Explorer](https://docs.victoriametrics.com/victoriametrics/#cardinality-explorer) page when user selects a specific metric or label to focus. See [#11154](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/11154) for details. Thanks to @lghuy05 for the contribution.
|
||||
* BUGFIX: [vmui](https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/#vmui): keep only one header navigation dropdown (`Explore`, `Tools`) open at a time. Previously, hovering across two dropdowns could briefly leave both open due to the close delay. See [#11224](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/11224). Thanks to @antedotee for contribution.
|
||||
* BUGFIX: [vmauth](https://docs.victoriametrics.com/victoriametrics/vmauth/): return `408 Request Timeout` instead of `400 Bad Request` when the request body isn't received within `-maxQueueDuration`. This prevents `vmagent` from incorrectly downgrading the remote write protocol and dropping data when `vmauth` is used as a proxy for a remote write endpoint. See [#11272](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/11272).
|
||||
|
||||
## [v1.147.0](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.147.0)
|
||||
|
||||
Released at 2026-07-06
|
||||
|
||||
**Update Note 1:** [vmagent](https://docs.victoriametrics.com/victoriametrics/vmagent/): contains a bug that causes increased CPU and memory usage when `-remoteWrite.urlRelabelConfig` or `-remoteWrite.streamAggr.config` flags are used. The bug was introduced in [#10854](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/10854). Upgrade to v1.148.0 or rollback to v1.146.0. See [#11250](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/11250).
|
||||
|
||||
* SECURITY: upgrade base docker image (Alpine) from 3.23.4 to 3.24.1. See [Alpine 3.24.1 release notes](https://www.alpinelinux.org/posts/Alpine-3.24.1-released.html).
|
||||
|
||||
* FEATURE: [vmauth](https://docs.victoriametrics.com/victoriametrics/vmauth/): add `default_vm_access_claim` field into `jwt` section of auth config. It could be used at [JWT claim placeholders](https://docs.victoriametrics.com/victoriametrics/vmauth/#jwt-claim-based-request-templating), if `JWT` token doesn't have `vm_access` claim. See [#11054](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/11054).
|
||||
@@ -65,7 +55,7 @@ Released at 2026-07-06
|
||||
* FEATURE: [vmagent](https://docs.victoriametrics.com/victoriametrics/vmagent/): add support for [Monitoring Data eXchange (MDX)](https://docs.victoriametrics.com/victoriametrics/vmagent/#monitoring-data-exchange): the ability to route only metrics from VictoriaMetrics services to a specific `-remoteWrite.url`. MDX is useful for building monitoring-of-monitoring where one remote storage should receive the full metric stream and another should receive only VictoriaMetrics metrics. Enable per destination with `-remoteWrite.mdx.enable=true`. See [#10600](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/10600).
|
||||
* FEATURE: [vmsingle](https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/) and `vmstorage` in [VictoriaMetrics cluster](https://docs.victoriametrics.com/victoriametrics/cluster-victoriametrics/): expose `vm_data_size_bytes{type="storage/metaindex"}` and `vm_data_size_bytes{type="indexdb/metaindex"}` metrics for tracking memory occupied by metaindex data. See [#11204](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/11204). Thanks to @SamarthBagga for contribution.
|
||||
* FEATURE: [vmsingle](https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/) and `vmselect` in [VictoriaMetrics cluster](https://docs.victoriametrics.com/victoriametrics/cluster-victoriametrics/): add `optimize_repeated_binary_op_subexprs=1` query arg to [/api/v1/query_range](https://docs.victoriametrics.com/victoriametrics/keyconcepts/#range-query) for executing binary operator sides sequentially when they share the same optimized aggregate rollup result expression. This allows the second side to reuse rollup result cache populated by the first side. See [#10575](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/10575). Thanks to @xhebox for the contribution.
|
||||
* FEATURE: [vmsingle](https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/): Add the support of vmselect RPC to vmsingle so that single node can be queried by a vmselect from a vmcluster deployment. See [#4328](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/4328), [#10926](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/10926), and the [documentation](https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/#multi-tenancy).
|
||||
* FEATURE: [vmsingle](https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/): Add the support of vmselect RPC to vmsingle so that single node can be queried by a vmselect from a vmcluster deployment. See [4328](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/4328), [10926](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/10926), and the [documentation](https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/#multi-tenancy).
|
||||
* FEATURE: [alerts](https://github.com/VictoriaMetrics/VictoriaMetrics/blob/master/deployment/docker/rules): add `InvalidAuthTokenRequestErrors` alerting rule to [vmauth alerts](https://github.com/VictoriaMetrics/VictoriaMetrics/blob/master/deployment/docker/rules/alerts-vmauth.yml). The new rule notifies when vmauth receives requests with invalid or missing auth tokens, which may indicate a client misconfiguration, expired token use, or brute-force attack. See [#11180](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/11180).
|
||||
* FEATURE: [alerts](https://github.com/VictoriaMetrics/VictoriaMetrics/blob/master/deployment/docker/rules): add `AlertingRuleResultsApproachingLimit` and `RecordingRuleResultsApproachingLimit` alerting rules to [vmalert alerts](https://github.com/VictoriaMetrics/VictoriaMetrics/blob/master/deployment/docker/rules/alerts-vmalert.yml). These alerts notify when a rule's last evaluation samples exceed 90% of the configured group results limit. See [#11179](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/11179). Thanks to @vinyas-bharadwaj for the contribution.
|
||||
* FEATURE: [vmauth](https://docs.victoriametrics.com/victoriametrics/vmauth/): prevent possible password brute-force attacks with an artificial 2-3 second delay as recommended by [OWASP](https://owasp.org/Top10/2025/A07_2025-Authentication_Failures). See [#11180](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/11180).
|
||||
@@ -347,27 +337,6 @@ It enables back `Discovered targets` debug UI by default.
|
||||
* BUGFIX: `vmstorage` in [VictoriaMetrics cluster](https://docs.victoriametrics.com/victoriametrics/cluster-victoriametrics/): properly apply `extra_filters[]` filter when querying `vm_account_id` or `vm_project_id` labels via [multitenant](https://docs.victoriametrics.com/victoriametrics/cluster-victoriametrics/#multitenancy) request for `/api/v1/label/…/values` API. Before, `extra_filters` was ignored. See [#10503](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/10503).
|
||||
* BUGFIX: [vmsingle](https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/) and `vmselect` in [VictoriaMetrics cluster](https://docs.victoriametrics.com/victoriametrics/cluster-victoriametrics/): revert the use of rollup result cache for [instant queries](https://docs.victoriametrics.com/keyConcepts.html#instant-query) that contain [`rate`](https://docs.victoriametrics.com/MetricsQL.html#rate) function with a lookbehind window larger than `-search.minWindowForInstantRollupOptimization`. The cache usage was removed since [v1.132.0](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.132.0). See [#10098](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/10098#issuecomment-3895011084) for more details.
|
||||
|
||||
## [v1.136.14](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.136.14)
|
||||
|
||||
Released at 2026-07-17
|
||||
|
||||
**v1.136.x is a line of [LTS releases](https://docs.victoriametrics.com/victoriametrics/lts-releases/). It contains important up-to-date bugfixes for [VictoriaMetrics enterprise](https://docs.victoriametrics.com/victoriametrics/enterprise/).
|
||||
All these fixes are also included in [the latest community release](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/latest).
|
||||
The v1.136.x line will be supported for at least 12 months since [v1.136.0](https://docs.victoriametrics.com/victoriametrics/changelog/#v11360) release**
|
||||
|
||||
* SECURITY: upgrade Go builder from Go1.26.4 to Go1.26.5. See [the list of issues addressed in Go1.26.5](https://github.com/golang/go/issues?q=milestone%3AGo1.26.5%20label%3ACherryPickApproved).
|
||||
|
||||
* BUGFIX: [vmagent](https://docs.victoriametrics.com/victoriametrics/vmagent/): flush pending persistent queue data to chunk file before updating the metadata. This prevents the metadata writer offset from getting ahead of the chunk file size and avoids losing the persistent queue after an unclean shutdown. See [#11192](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/11192).
|
||||
* BUGFIX: [vmagent](https://docs.victoriametrics.com/victoriametrics/vmagent/): fix a possible data race when processing OpenTelemetry metadata. See [#11238](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/11238). Thanks to @nevgeny for contribution.
|
||||
* BUGFIX: [vmagent](https://docs.victoriametrics.com/victoriametrics/vmagent/): atomically write persistent queue metainfo to prevent possible file corruption on ungraceful shutdown. See [#11192](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/11192).
|
||||
* BUGFIX: [vmbackup](https://docs.victoriametrics.com/victoriametrics/vmbackup/) and [vmbackupmanager](https://docs.victoriametrics.com/victoriametrics/vmbackupmanager/): retry S3 requests failing with `HTTP 429` status code or `TooManyRequests` error code. Previously such requests were not retried, so a short burst of rate limiting would fail the whole backup. See [#11218](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/11218). Thanks to @gautamrizwani for contribution.
|
||||
* BUGFIX: `vmselect` in [VictoriaMetrics cluster](https://docs.victoriametrics.com/victoriametrics/cluster-victoriametrics/): properly apply limit to metrics metadata response. See [#11139](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/11139).
|
||||
* BUGFIX: `vminsert` in [VictoriaMetrics cluster](https://docs.victoriametrics.com/victoriametrics/cluster-victoriametrics/): Now drops metadata blocks when communicating with vmstorage nodes over the legacy RPC protocol. To avoid this limitation, upgrade `vmstorage` to a version that supports the new RPC protocol (>= [v1.137.0](https://github.com/VictoriaMetrics/VictoriaMetrics/blob/master/docs/victoriametrics/changelog/CHANGELOG.md#v11370)). See [#11146](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/11146).
|
||||
* BUGFIX: [vmui](https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/#vmui): keep only one header navigation dropdown (`Explore`, `Tools`) open at a time. Previously, hovering across two dropdowns could briefly leave both open due to the close delay. See [#11224](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/11224). Thanks to @antedotee for contribution.
|
||||
* BUGFIX: [vmui](https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/#vmui): preserve newline formatting in alert and rule annotations on the Alerting page. See [#11171](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/11171).
|
||||
* BUGFIX: [vmui](https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/#vmui): hide `Total metric names` stats on [Cardinality Explorer](https://docs.victoriametrics.com/victoriametrics/#cardinality-explorer) page when user selects a specific metric or label to focus. See [#11154](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/11154) for details. Thanks to @lghuy05 for the contribution.
|
||||
* BUGFIX: [vmauth](https://docs.victoriametrics.com/victoriametrics/vmauth/): return `408 Request Timeout` instead of `400 Bad Request` when the request body isn't received within `-maxQueueDuration`. This prevents `vmagent` from incorrectly downgrading the remote write protocol and dropping data when `vmauth` is used as a proxy for a remote write endpoint. See [#11272](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/11272).
|
||||
|
||||
## [v1.136.13](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.136.13)
|
||||
|
||||
Released at 2026-07-03
|
||||
@@ -766,18 +735,6 @@ See changes [here](https://docs.victoriametrics.com/victoriametrics/changelog/ch
|
||||
|
||||
See changes [here](https://docs.victoriametrics.com/victoriametrics/changelog/changelog_2025/#v11230)
|
||||
|
||||
## [v1.122.27](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.122.27)
|
||||
|
||||
Released at 2026-07-17
|
||||
|
||||
**v1.122.x is a line of [LTS releases](https://docs.victoriametrics.com/victoriametrics/lts-releases/). It contains important up-to-date bugfixes for [VictoriaMetrics enterprise](https://docs.victoriametrics.com/victoriametrics/enterprise/).
|
||||
All these fixes are also included in [the latest community release](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/latest).
|
||||
The v1.122.x line will be supported for at least 12 months since [v1.122.0](https://docs.victoriametrics.com/victoriametrics/changelog/#v11220) release**
|
||||
|
||||
* BUGFIX: [vmagent](https://docs.victoriametrics.com/victoriametrics/vmagent/): flush pending persistent queue data to chunk file before updating the metadata. This prevents the metadata writer offset from getting ahead of the chunk file size and avoids losing the persistent queue after an unclean shutdown. See [#11192](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/11192).
|
||||
* BUGFIX: [vmbackup](https://docs.victoriametrics.com/victoriametrics/vmbackup/) and [vmbackupmanager](https://docs.victoriametrics.com/victoriametrics/vmbackupmanager/): retry S3 requests failing with `HTTP 429` status code or `TooManyRequests` error code. Previously such requests were not retried, so a short burst of rate limiting would fail the whole backup. See [#11218](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/11218). Thanks to @gautamrizwani for contribution.
|
||||
* BUGFIX: [vmui](https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/#vmui): hide `Total metric names` stats on [Cardinality Explorer](https://docs.victoriametrics.com/victoriametrics/#cardinality-explorer) page when user selects a specific metric or label to focus. See [#11154](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/11154) for details. Thanks to @lghuy05 for the contribution.
|
||||
|
||||
## [v1.122.26](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.122.26)
|
||||
|
||||
Released at 2026-07-03
|
||||
|
||||
@@ -121,7 +121,7 @@ It is allowed to run Enterprise components in [cases listed here](https://docs.v
|
||||
Binary releases of Enterprise components are available at [the releases page for VictoriaMetrics](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/latest),
|
||||
[the releases page for VictoriaLogs](https://github.com/VictoriaMetrics/VictoriaLogs/releases/latest)
|
||||
and [the releases page for VictoriaTraces](https://github.com/VictoriaMetrics/VictoriaTraces/releases/latest).
|
||||
Enterprise binaries and packages have `enterprise` suffix in their names. For example, `victoria-metrics-linux-amd64-v1.148.0-enterprise.tar.gz`.
|
||||
Enterprise binaries and packages have `enterprise` suffix in their names. For example, `victoria-metrics-linux-amd64-v1.147.0-enterprise.tar.gz`.
|
||||
|
||||
In order to run binary release of Enterprise component, please download the `*-enterprise.tar.gz` archive for your OS and architecture
|
||||
from the corresponding releases page and unpack it. Then run the unpacked binary.
|
||||
@@ -139,8 +139,8 @@ For example, the following command runs VictoriaMetrics Enterprise binary with t
|
||||
obtained at [this page](https://victoriametrics.com/products/enterprise/trial/):
|
||||
|
||||
```sh
|
||||
wget https://github.com/VictoriaMetrics/VictoriaMetrics/releases/download/v1.148.0/victoria-metrics-linux-amd64-v1.148.0-enterprise.tar.gz
|
||||
tar -xzf victoria-metrics-linux-amd64-v1.148.0-enterprise.tar.gz
|
||||
wget https://github.com/VictoriaMetrics/VictoriaMetrics/releases/download/v1.147.0/victoria-metrics-linux-amd64-v1.147.0-enterprise.tar.gz
|
||||
tar -xzf victoria-metrics-linux-amd64-v1.147.0-enterprise.tar.gz
|
||||
./victoria-metrics-prod -license=BASE64_ENCODED_LICENSE_KEY
|
||||
```
|
||||
|
||||
@@ -155,7 +155,7 @@ Alternatively, VictoriaMetrics Enterprise license can be stored in the file and
|
||||
It is allowed to run Enterprise components in [cases listed here](https://docs.victoriametrics.com/victoriametrics/enterprise/#valid-cases-for-victoriametrics-enterprise).
|
||||
|
||||
Docker images for Enterprise components are available at [VictoriaMetrics Docker Hub](https://hub.docker.com/u/victoriametrics) and [VictoriaMetrics Quay](https://quay.io/organization/victoriametrics).
|
||||
Enterprise docker images have `enterprise` suffix in their names. For example, `victoriametrics/victoria-metrics:v1.148.0-enterprise`.
|
||||
Enterprise docker images have `enterprise` suffix in their names. For example, `victoriametrics/victoria-metrics:v1.147.0-enterprise`.
|
||||
|
||||
In order to run Docker image of VictoriaMetrics Enterprise component, it is required to provide the license key via the command-line
|
||||
flag as described in the [binary-releases](https://docs.victoriametrics.com/victoriametrics/enterprise/#binary-releases) section.
|
||||
@@ -165,13 +165,13 @@ Enterprise license key can be obtained at [this page](https://victoriametrics.co
|
||||
For example, the following command runs VictoriaMetrics Enterprise Docker image with the specified license key:
|
||||
|
||||
```sh
|
||||
docker run --name=victoria-metrics victoriametrics/victoria-metrics:v1.148.0-enterprise -license=BASE64_ENCODED_LICENSE_KEY
|
||||
docker run --name=victoria-metrics victoriametrics/victoria-metrics:v1.147.0-enterprise -license=BASE64_ENCODED_LICENSE_KEY
|
||||
```
|
||||
|
||||
Alternatively, the license code can be stored in the file and then referred via `-licenseFile` command-line flag:
|
||||
|
||||
```sh
|
||||
docker run --name=victoria-metrics -v /vm-license:/vm-license victoriametrics/victoria-metrics:v1.148.0-enterprise -licenseFile=/path/to/vm-license
|
||||
docker run --name=victoria-metrics -v /vm-license:/vm-license victoriametrics/victoria-metrics:v1.147.0-enterprise -licenseFile=/path/to/vm-license
|
||||
```
|
||||
|
||||
Example docker-compose configuration:
|
||||
@@ -181,7 +181,7 @@ version: "3.5"
|
||||
services:
|
||||
victoriametrics:
|
||||
container_name: victoriametrics
|
||||
image: victoriametrics/victoria-metrics:v1.148.0
|
||||
image: victoriametrics/victoria-metrics:v1.147.0
|
||||
ports:
|
||||
- 8428:8428
|
||||
volumes:
|
||||
@@ -213,7 +213,7 @@ is used to provide the license key in plain-text:
|
||||
```yaml
|
||||
server:
|
||||
image:
|
||||
tag: v1.148.0-enterprise
|
||||
tag: v1.147.0-enterprise
|
||||
|
||||
license:
|
||||
key: {BASE64_ENCODED_LICENSE_KEY}
|
||||
@@ -224,7 +224,7 @@ In order to provide the license key via existing secret, the following values fi
|
||||
```yaml
|
||||
server:
|
||||
image:
|
||||
tag: v1.148.0-enterprise
|
||||
tag: v1.147.0-enterprise
|
||||
|
||||
license:
|
||||
secret:
|
||||
@@ -274,7 +274,7 @@ spec:
|
||||
license:
|
||||
key: {BASE64_ENCODED_LICENSE_KEY}
|
||||
image:
|
||||
tag: v1.148.0-enterprise
|
||||
tag: v1.147.0-enterprise
|
||||
```
|
||||
|
||||
In order to provide the license key via an existing secret, the following custom resource is used:
|
||||
@@ -291,7 +291,7 @@ spec:
|
||||
name: vm-license
|
||||
key: license
|
||||
image:
|
||||
tag: v1.148.0-enterprise
|
||||
tag: v1.147.0-enterprise
|
||||
```
|
||||
|
||||
Example secret with license key:
|
||||
@@ -342,7 +342,7 @@ Builds are available for amd64 and arm64 architectures.
|
||||
|
||||
Example archive:
|
||||
|
||||
`victoria-metrics-linux-amd64-v1.148.0-enterprise.tar.gz`
|
||||
`victoria-metrics-linux-amd64-v1.147.0-enterprise.tar.gz`
|
||||
|
||||
Includes:
|
||||
|
||||
@@ -351,7 +351,7 @@ Includes:
|
||||
|
||||
Example Docker image:
|
||||
|
||||
`victoriametrics/victoria-metrics:v1.148.0-enterprise-fips` – uses the FIPS-compatible binary and based on `scratch` image.
|
||||
`victoriametrics/victoria-metrics:v1.147.0-enterprise-fips` – uses the FIPS-compatible binary and based on `scratch` image.
|
||||
|
||||
## What Happens to Licensed Components When a License Expires
|
||||
|
||||
|
||||
@@ -513,7 +513,6 @@ URLs for scrape targets are composed of the following parts:
|
||||
[scrape_config](https://docs.victoriametrics.com/victoriametrics/sd_configs/#scrape_configs)
|
||||
or by updating/setting the corresponding `__param_*` labels during
|
||||
relabeling.
|
||||
- Unix domain socket path (e.g. `/var/run/node_exporter.sock`) can be configured during target relabeling via a special label - `__unix_socket__`. If this label is set, `vmagent` tunnels the scrape request through the specified Unix domain socket instead of connecting to a TCP socket. The `__address__` label is still used to populate the `instance` label and construct the HTTP request URL, but the actual network connection goes through the Unix socket.
|
||||
|
||||
The resulting scrape URL looks like the following:
|
||||
|
||||
|
||||
@@ -35,8 +35,8 @@ scrape_configs:
|
||||
After you created the `scrape.yaml` file, download and unpack [single-node VictoriaMetrics](https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/) to the same directory:
|
||||
|
||||
```sh
|
||||
wget https://github.com/VictoriaMetrics/VictoriaMetrics/releases/download/v1.148.0/victoria-metrics-linux-amd64-v1.148.0.tar.gz
|
||||
tar xzf victoria-metrics-linux-amd64-v1.148.0.tar.gz
|
||||
wget https://github.com/VictoriaMetrics/VictoriaMetrics/releases/download/v1.147.0/victoria-metrics-linux-amd64-v1.147.0.tar.gz
|
||||
tar xzf victoria-metrics-linux-amd64-v1.147.0.tar.gz
|
||||
```
|
||||
|
||||
Then start VictoriaMetrics and instruct it to scrape targets defined in `scrape.yaml` and save scraped metrics
|
||||
@@ -150,8 +150,8 @@ Then start [single-node VictoriaMetrics](https://docs.victoriametrics.com/victor
|
||||
|
||||
```yaml
|
||||
# Download and unpack single-node VictoriaMetrics
|
||||
wget https://github.com/VictoriaMetrics/VictoriaMetrics/releases/download/v1.148.0/victoria-metrics-linux-amd64-v1.148.0.tar.gz
|
||||
tar xzf victoria-metrics-linux-amd64-v1.148.0.tar.gz
|
||||
wget https://github.com/VictoriaMetrics/VictoriaMetrics/releases/download/v1.147.0/victoria-metrics-linux-amd64-v1.147.0.tar.gz
|
||||
tar xzf victoria-metrics-linux-amd64-v1.147.0.tar.gz
|
||||
|
||||
# Run single-node VictoriaMetrics with the given scrape.yaml
|
||||
./victoria-metrics-prod -promscrape.config=scrape.yaml
|
||||
|
||||
@@ -188,9 +188,6 @@ See the docs at https://docs.victoriametrics.com/victoriametrics/
|
||||
Timezone to use for timestamps in logs. Timezone must be a valid IANA Time Zone. For example: America/New_York, Europe/Berlin, Etc/GMT+3 or Local (default "UTC")
|
||||
-loggerWarnsPerSecondLimit int
|
||||
Per-second limit on the number of WARN messages. If more than the given number of warns are emitted per second, then the remaining warns are suppressed. Zero values disable the rate limit
|
||||
-maxBackfillAge value
|
||||
The maximum allowed age for the ingested samples with historical timestamps. Samples with timestamps older than now-maxBackfillAge are rejected during data ingestion. By default, or when set to 0, -maxBackfillAge equals to -retentionPeriod, e.g. it is unlimited within the configured retention. This can be useful for limiting ingestion of historical samples, for example, when older data has been moved to another storage tier. See https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/#retention
|
||||
The following optional suffixes are supported: s (second), h (hour), d (day), w (week), M (month), y (year). If suffix isn't set, then the duration is counted in months (default 0)
|
||||
-maxConcurrentInserts int
|
||||
The maximum number of concurrent insert requests. Set higher value when clients send data over slow networks. Default value depends on the number of available CPU cores. It should work fine in most cases since it minimizes resource usage. See also -insert.maxQueueDuration (default 2*cgroup.AvailableCPUs())
|
||||
-maxIngestionRate int
|
||||
|
||||
@@ -546,7 +546,7 @@ tags at [Docker Hub](https://hub.docker.com/r/victoriametrics/vmalert/tags) and
|
||||
|
||||
## Reading rules from object storage
|
||||
|
||||
The [Enterprise version](https://docs.victoriametrics.com/victoriametrics/enterprise/) of `vmalert` may read alerting and recording rules
|
||||
[Enterprise version](https://docs.victoriametrics.com/victoriametrics/enterprise/) of `vmalert` may read alerting and recording rules
|
||||
from object storage:
|
||||
|
||||
* `./bin/vmalert -rule=s3://bucket/dir/alert.rules` would read rules from the given path at S3 bucket
|
||||
@@ -563,48 +563,6 @@ The following [command-line flags](#flags) can be used for fine-tuning access to
|
||||
* `-s3.customEndpoint` - custom S3 endpoint for use with S3-compatible storages (e.g. MinIO). S3 is used if not set.
|
||||
* `-s3.forcePathStyle` - prefixing endpoint with bucket name when set false, true by default.
|
||||
|
||||
### S3 (AWS and S3-compatible)
|
||||
|
||||
The following example reads rules from an S3 bucket:
|
||||
|
||||
```sh
|
||||
./bin/vmalert \
|
||||
-rule=s3://my-alert-bucket/rules/alerts_ \
|
||||
-s3.credsFilePath=/etc/vmalert/aws-credentials \
|
||||
-datasource.url=http://vmselect:8481/select/0/prometheus \
|
||||
-notifier.url=http://alertmanager:9093 \
|
||||
-licenseFile=/etc/vm-license
|
||||
```
|
||||
|
||||
For S3-compatible backends such as [MinIO](https://www.min.io/) or [Ceph](https://ceph.io/), add `-s3.customEndpoint`:
|
||||
|
||||
```sh
|
||||
./bin/vmalert \
|
||||
-rule=s3://victoriametrics-alert-rules/rules_ \
|
||||
-s3.credsFilePath=/etc/vmalert/aws-credentials \
|
||||
-s3.customEndpoint=http://minio.example.local:9000 \
|
||||
-datasource.url=http://vmselect:8481/select/0/prometheus \
|
||||
-notifier.url=http://alertmanager:9093 \
|
||||
-licenseFile=/etc/vm-license
|
||||
```
|
||||
|
||||
See [Connecting VM components to cloud storage](https://docs.victoriametrics.com/guides/connecting-vm-components-to-cloud-storage/) for details on creating IAM users, credentials file format, environment variables, and IAM roles.
|
||||
|
||||
### Google Cloud Storage (GCS)
|
||||
|
||||
The following example reads rules from a GCS bucket:
|
||||
|
||||
```sh
|
||||
./bin/vmalert \
|
||||
-rule=gs://my-alert-bucket/rules/alerts_ \
|
||||
-s3.credsFilePath=/etc/vmalert/gcp-service-account.json \
|
||||
-datasource.url=http://vmselect:8481/select/0/prometheus \
|
||||
-notifier.url=http://alertmanager:9093 \
|
||||
-licenseFile=/etc/vm-license
|
||||
```
|
||||
|
||||
See [Connecting VM components to cloud storage](https://docs.victoriametrics.com/guides/connecting-vm-components-to-cloud-storage/) for details on creating service accounts and downloading JSON keys.
|
||||
|
||||
## Topology examples
|
||||
|
||||
The following sections are showing how `vmalert` may be used and configured
|
||||
|
||||
@@ -204,45 +204,61 @@ See [this article](https://medium.com/@valyala/speeding-up-backups-for-big-time-
|
||||
|
||||
### Providing credentials as a file
|
||||
|
||||
See [Connecting VM components to cloud storage](https://docs.victoriametrics.com/guides/connecting-vm-components-to-cloud-storage/) for instructions on obtaining credentials and providing them via credential files, environment variables, cloud provider metadata service, or Kubernetes secrets and IAM roles.
|
||||
Obtaining credentials from a file.
|
||||
|
||||
The following examples show the most common authentication patterns for each storage provider.
|
||||
Add flag `-credsFilePath=/etc/credentials` with the following content:
|
||||
|
||||
#### S3 (AWS and S3-compatible)
|
||||
* for S3 (AWS, MinIO or other S3 compatible storages):
|
||||
|
||||
```sh
|
||||
vmbackup \
|
||||
-storageDataPath=/data \
|
||||
-snapshot.createURL=http://localhost:8428/snapshot/create \
|
||||
-dst=s3://victoriametrics-backup/backup01 \
|
||||
-credsFilePath=/etc/credentials
|
||||
```
|
||||
```sh
|
||||
[default]
|
||||
aws_access_key_id=theaccesskey
|
||||
aws_secret_access_key=thesecretaccesskeyvalue
|
||||
```
|
||||
|
||||
#### Google Cloud Storage (GCS)
|
||||
* for GCP cloud storage:
|
||||
|
||||
```sh
|
||||
vmbackup \
|
||||
-storageDataPath=/data \
|
||||
-snapshot.createURL=http://localhost:8428/snapshot/create \
|
||||
-dst=gs://victoriametrics-backup/backup01 \
|
||||
-credsFilePath=/etc/credentials
|
||||
```
|
||||
```json
|
||||
{
|
||||
"type": "service_account",
|
||||
"project_id": "project-id",
|
||||
"private_key_id": "key-id",
|
||||
"private_key": "-----BEGIN PRIVATE KEY-----\nprivate-key\n-----END PRIVATE KEY-----\n",
|
||||
"client_email": "service-account-email",
|
||||
"client_id": "client-id",
|
||||
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
|
||||
"token_uri": "https://accounts.google.com/o/oauth2/token",
|
||||
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
|
||||
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/service-account-email"
|
||||
}
|
||||
```
|
||||
|
||||
#### Azure Blob Storage
|
||||
### Providing credentials via env variables
|
||||
|
||||
```sh
|
||||
export AZURE_STORAGE_ACCOUNT_NAME=mystorageaccount
|
||||
export AZURE_STORAGE_ACCOUNT_KEY=myaccountkey
|
||||
Obtaining credentials from env variables.
|
||||
|
||||
vmbackup \
|
||||
-storageDataPath=/data \
|
||||
-snapshot.createURL=http://localhost:8428/snapshot/create \
|
||||
-dst=azblob://victoriametrics-backup/backup01
|
||||
```
|
||||
* For AWS S3 compatible storages set env variable `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`.
|
||||
Also you can set env variable `AWS_SHARED_CREDENTIALS_FILE` with path to credentials file.
|
||||
* For GCE cloud storage set env variable `GOOGLE_APPLICATION_CREDENTIALS` with path to credentials file.
|
||||
* For Azure storage use one of these env variables:
|
||||
* `AZURE_STORAGE_ACCOUNT_CONNECTION_STRING`: use a connection string (must be either SAS Token or Account/Key)
|
||||
* `AZURE_STORAGE_ACCOUNT_NAME` and `AZURE_STORAGE_ACCOUNT_KEY`: use a specific account name and key (either primary or secondary)
|
||||
* `AZURE_USE_DEFAULT_CREDENTIAL` and `AZURE_STORAGE_ACCOUNT_NAME`: use the `DefaultAzureCredential` to allow the Azure library
|
||||
to search for multiple options (for example, managed identity related variables). Note that if multiple credentials are available,
|
||||
it is required to specify the `AZURE_CLIENT_ID` to select specific credentials.
|
||||
|
||||
The `AZURE_STORAGE_DOMAIN` can be used for optionally overriding the default domain for the Azure storage service.
|
||||
|
||||
Please, note that `vmbackup` will use credentials provided by cloud providers metadata service [when applicable](https://docs.victoriametrics.com/victoriametrics/vmbackup/#using-cloud-providers-metadata-service).
|
||||
|
||||
### Using cloud providers metadata service
|
||||
|
||||
`vmbackup` and `vmbackupmanager` will automatically use cloud providers metadata service in order to obtain credentials if they are running in cloud environment and credentials are not explicitly provided via flags or env variables.
|
||||
|
||||
### Providing credentials in Kubernetes
|
||||
|
||||
The simplest way to provide credentials in Kubernetes is to use [Secrets](https://kubernetes.io/docs/concepts/configuration/secret/) and inject them into the pod as environment variables. For example, the following secret can be used for AWS S3 credentials:
|
||||
The simplest way to provide credentials in Kubernetes is to use [Secrets](https://kubernetes.io/docs/concepts/configuration/secret/)
|
||||
and inject them into the pod as environment variables. For example, the following secret can be used for AWS S3 credentials:
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
@@ -250,13 +266,14 @@ kind: Secret
|
||||
metadata:
|
||||
name: vmbackup-credentials
|
||||
data:
|
||||
access_key: <base64-encoded-key>
|
||||
secret_key: <base64-encoded-secret>
|
||||
access_key: key
|
||||
secret_key: secret
|
||||
```
|
||||
|
||||
And then it can be injected into the pod as environment variables:
|
||||
|
||||
```yaml
|
||||
...
|
||||
env:
|
||||
- name: AWS_ACCESS_KEY_ID
|
||||
valueFrom:
|
||||
@@ -268,6 +285,7 @@ env:
|
||||
secretKeyRef:
|
||||
key: secret_key
|
||||
name: vmbackup-credentials
|
||||
...
|
||||
```
|
||||
|
||||
A more secure way is to use IAM roles to provide tokens for pods instead of managing credentials manually.
|
||||
|
||||
@@ -88,21 +88,34 @@ There are two flags which could help with performance tuning:
|
||||
|
||||
### Example of Usage
|
||||
|
||||
The following command backs up data to a GCS bucket:
|
||||
GCS and cluster version. You need to have a credentials file in json format with following structure:
|
||||
|
||||
credentials.json
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "service_account",
|
||||
"project_id": "<project>",
|
||||
"private_key_id": "",
|
||||
"private_key": "-----BEGIN PRIVATE KEY-----\-----END PRIVATE KEY-----\n",
|
||||
"client_email": "test@<project>.iam.gserviceaccount.com",
|
||||
"client_id": "",
|
||||
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
|
||||
"token_uri": "https://oauth2.googleapis.com/token",
|
||||
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
|
||||
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/test%40<project>.iam.gserviceaccount.com"
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
Backup manager launched with the following configuration:
|
||||
|
||||
```sh
|
||||
export NODE_IP=192.168.0.10
|
||||
export VMSTORAGE_ENDPOINT=http://127.0.0.1:8428
|
||||
./vmbackupmanager \
|
||||
-dst=gs://vmstorage-data/$NODE_IP \
|
||||
-credsFilePath=credentials.json \
|
||||
-storageDataPath=/vmstorage-data \
|
||||
-snapshot.createURL=$VMSTORAGE_ENDPOINT/snapshot/create \
|
||||
-licenseFile=/path/to/vm-license
|
||||
./vmbackupmanager -dst=gs://vmstorage-data/$NODE_IP -credsFilePath=credentials.json -storageDataPath=/vmstorage-data -snapshot.createURL=$VMSTORAGE_ENDPOINT/snapshot/create -licenseFile=/path/to/vm-license
|
||||
```
|
||||
|
||||
See [Connecting VM components to cloud storage](https://docs.victoriametrics.com/guides/connecting-vm-components-to-cloud-storage/) for instructions on obtaining credentials and configuring access to S3, GCS, and Azure Blob Storage.
|
||||
|
||||
Expected logs in vmbackupmanager:
|
||||
|
||||
```sh
|
||||
@@ -134,7 +147,8 @@ objects. Typical object storage systems implement server-side copy by creating n
|
||||
This is very fast and efficient. Unfortunately there are systems such as [S3 Glacier](https://aws.amazon.com/s3/storage-classes/glacier/),
|
||||
which perform full object copy during server-side copying. This may be slow and expensive.
|
||||
|
||||
See [Connecting VM components to cloud storage](https://docs.victoriametrics.com/guides/connecting-vm-components-to-cloud-storage/) for more examples of authentication with different storage types.
|
||||
Please, see [vmbackup docs](https://docs.victoriametrics.com/victoriametrics/vmbackup/#advanced-usage) for more examples of authentication with different
|
||||
storage types.
|
||||
|
||||
### Backup Retention Policy
|
||||
|
||||
|
||||
@@ -34,9 +34,9 @@ vmctl command-line tool is available as:
|
||||
|
||||
Download and unpack vmctl:
|
||||
```sh
|
||||
wget https://github.com/VictoriaMetrics/VictoriaMetrics/releases/download/v1.148.0/vmutils-darwin-arm64-v1.148.0.tar.gz
|
||||
wget https://github.com/VictoriaMetrics/VictoriaMetrics/releases/download/v1.147.0/vmutils-darwin-arm64-v1.147.0.tar.gz
|
||||
|
||||
tar xzf vmutils-darwin-arm64-v1.148.0.tar.gz
|
||||
tar xzf vmutils-darwin-arm64-v1.147.0.tar.gz
|
||||
```
|
||||
|
||||
Once binary is unpacked, see the full list of supported modes by running the following command:
|
||||
|
||||
@@ -47,13 +47,13 @@ i.e. the end result would be similar to [rsync --delete](https://askubuntu.com/q
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
* See [Connecting VM components to cloud storage](https://docs.victoriametrics.com/guides/connecting-vm-components-to-cloud-storage/) for instructions on configuring credentials.
|
||||
* See [how to setup credentials via environment variables](https://docs.victoriametrics.com/victoriametrics/vmbackup/#providing-credentials-via-env-variables).
|
||||
* If `vmrestore` consumes all the network bandwidth, then set `-maxBytesPerSecond` to the desired value.
|
||||
* If `vmrestore` has been interrupted due to temporary error, then just restart it with the same args. It will resume the restore process.
|
||||
|
||||
## Advanced usage
|
||||
|
||||
See [Connecting VM components to cloud storage](https://docs.victoriametrics.com/guides/connecting-vm-components-to-cloud-storage/) for examples of authentication
|
||||
Please, see [vmbackup docs](https://docs.victoriametrics.com/victoriametrics/vmbackup/#advanced-usage) for examples of authentication
|
||||
with different storage types.
|
||||
|
||||
Run `vmrestore -help` in order to see all the available options:
|
||||
|
||||
@@ -41,8 +41,6 @@ See the docs at https://docs.victoriametrics.com/victoriametrics/cluster-victori
|
||||
Flag value can be read from the given http/https url when using -deleteAuthKey=http://host/path or -deleteAuthKey=https://host/path
|
||||
-denyQueryTracing
|
||||
Whether to disable the ability to trace queries. See https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/#query-tracing
|
||||
-enableMetadata
|
||||
Whether to enable metadata processing for metrics scraped from targets, received via VictoriaMetrics remote write, Prometheus remote write v1 or OpenTelemetry protocol. See also remoteWrite.maxMetadataPerBlock (default true)
|
||||
-enableMultitenancyViaHeaders
|
||||
Enables multitenancy via HTTP headers. See https://docs.victoriametrics.com/victoriametrics/cluster-victoriametrics/#multitenancy-via-headers
|
||||
-enableTCP6
|
||||
@@ -105,8 +103,6 @@ See the docs at https://docs.victoriametrics.com/victoriametrics/cluster-victori
|
||||
Whether to use proxy protocol for connections accepted at the given -httpListenAddr . See https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt . With enabled proxy protocol http server cannot serve regular /metrics endpoint. Use -pushmetrics.url for metrics pushing
|
||||
Supports array of values separated by comma or specified via multiple flags.
|
||||
Empty values are set to false.
|
||||
-insert.maxQueueDuration duration
|
||||
The maximum duration to wait in the queue when -maxConcurrentInserts concurrent insert requests are executed (default 1m0s)
|
||||
-internStringCacheExpireDuration duration
|
||||
The expiry duration for caches for interned strings. See https://en.wikipedia.org/wiki/String_interning . See also -internStringMaxLen and -internStringDisableCache (default 6m0s)
|
||||
-internStringDisableCache
|
||||
@@ -131,8 +127,6 @@ See the docs at https://docs.victoriametrics.com/victoriametrics/cluster-victori
|
||||
Timezone to use for timestamps in logs. Timezone must be a valid IANA Time Zone. For example: America/New_York, Europe/Berlin, Etc/GMT+3 or Local (default "UTC")
|
||||
-loggerWarnsPerSecondLimit int
|
||||
Per-second limit on the number of WARN messages. If more than the given number of warns are emitted per second, then the remaining warns are suppressed. Zero values disable the rate limit
|
||||
-maxConcurrentInserts int
|
||||
The maximum number of concurrent insert requests. Set higher value when clients send data over slow networks. Default value depends on the number of available CPU cores. It should work fine in most cases since it minimizes resource usage. See also -insert.maxQueueDuration (default 24)
|
||||
-memory.allowedBytes size
|
||||
Allowed size of system memory VictoriaMetrics caches may occupy. This option overrides -memory.allowedPercent if set to a non-zero value. Too low a value may increase the cache miss rate usually resulting in higher CPU and disk IO usage. Too high a value may evict too much data from the OS page cache resulting in higher disk IO usage. The process may behave unexpectedly if this flag is set too small (e.g., 1 byte).
|
||||
Supports the following optional suffixes for size values: KB, MB, GB, TB, KiB, MiB, GiB, TiB (default 0)
|
||||
@@ -154,119 +148,6 @@ See the docs at https://docs.victoriametrics.com/victoriametrics/cluster-victori
|
||||
Flag value can be read from the given http/https url when using -pprofAuthKey=http://host/path or -pprofAuthKey=https://host/path
|
||||
-prevCacheRemovalPercent float
|
||||
Items in the previous caches are removed when the percent of requests it serves becomes lower than this value. Higher values reduce memory usage at the cost of higher CPU usage. See also -cacheExpireDuration (default 0.1)
|
||||
-promscrape.azureSDCheckInterval duration
|
||||
Interval for checking for changes in Azure. This works only if azure_sd_configs is configured in '-promscrape.config' file. See https://docs.victoriametrics.com/victoriametrics/sd_configs/#azure_sd_configs for details (default 1m0s)
|
||||
-promscrape.cluster.memberLabel string
|
||||
If non-empty, then the label with this name and the -promscrape.cluster.memberNum value is added to all the scraped metrics. See https://docs.victoriametrics.com/victoriametrics/vmagent/#scraping-big-number-of-targets for more info
|
||||
-promscrape.cluster.memberNum string
|
||||
The number of vmagent instance in the cluster of scrapers. It must be a unique value in the range 0 ... promscrape.cluster.membersCount-1 across scrapers in the cluster. Can be specified as pod name of Kubernetes StatefulSet - pod-name-Num, where Num is a numeric part of pod name. See also -promscrape.cluster.memberLabel . See https://docs.victoriametrics.com/victoriametrics/vmagent/#scraping-big-number-of-targets for more info (default "0")
|
||||
-promscrape.cluster.memberURLTemplate string
|
||||
An optional template for URL to access vmagent instance with the given -promscrape.cluster.memberNum value. Every %d occurrence in the template is substituted with -promscrape.cluster.memberNum at urls to vmagent instances responsible for scraping the given target at /service-discovery page. For example -promscrape.cluster.memberURLTemplate='http://vmagent-%d:8429/targets'. See https://docs.victoriametrics.com/victoriametrics/vmagent/#scraping-big-number-of-targets for more details
|
||||
-promscrape.cluster.membersCount int
|
||||
The number of members in a cluster of scrapers. Each member must have a unique -promscrape.cluster.memberNum in the range 0 ... promscrape.cluster.membersCount-1 . Each member then scrapes roughly 1/N of all the targets. By default, cluster scraping is disabled, i.e. a single scraper scrapes all the targets. See https://docs.victoriametrics.com/victoriametrics/vmagent/#scraping-big-number-of-targets for more info (default 1)
|
||||
-promscrape.cluster.name string
|
||||
Optional name of the cluster. If multiple vmagent clusters scrape the same targets, then each cluster must have unique name in order to properly de-duplicate samples received from these clusters. See https://docs.victoriametrics.com/victoriametrics/vmagent/#scraping-big-number-of-targets for more info
|
||||
-promscrape.cluster.replicationFactor int
|
||||
The number of members in the cluster, which scrape the same targets. If the replication factor is greater than 1, then the deduplication must be enabled at remote storage side. See https://docs.victoriametrics.com/victoriametrics/vmagent/#scraping-big-number-of-targets for more info (default 1)
|
||||
-promscrape.cluster.shardByLabels array
|
||||
Optional list of target labels, which will be used for sharding targets among cluster members if -promscrape.cluster.membersCount is greater than 1. If none of the specified labels are found in a target, then all the target labels will be used for sharding. See https://docs.victoriametrics.com/victoriametrics/vmagent/#scraping-big-number-of-targets for more info
|
||||
Supports an array of values separated by comma or specified via multiple flags.
|
||||
Each array item can contain comma inside single-quoted or double-quoted string, {}, [] and () braces.
|
||||
-promscrape.config string
|
||||
Optional path to Prometheus config file with 'scrape_configs' section containing targets to scrape. The path can point to local file and to http url. See https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/#how-to-scrape-prometheus-exporters-such-as-node-exporter for details
|
||||
-promscrape.config.dryRun
|
||||
Checks -promscrape.config file for errors and unsupported fields and then exits. Returns non-zero exit code on parsing errors and emits these errors to stderr. See also -promscrape.config.strictParse command-line flag. Pass -loggerLevel=ERROR if you don't need to see info messages in the output.
|
||||
-promscrape.config.strictParse
|
||||
Whether to deny unsupported fields in -promscrape.config . Set to false in order to silently skip unsupported fields (default true)
|
||||
-promscrape.configCheckInterval duration
|
||||
Interval for checking for changes in -promscrape.config file. By default, the checking is disabled. See how to reload -promscrape.config file at https://docs.victoriametrics.com/victoriametrics/vmagent/#configuration-update
|
||||
-promscrape.consul.waitTime duration
|
||||
Wait time used by Consul service discovery. Default value is used if not set
|
||||
-promscrape.consulSDCheckInterval duration
|
||||
Interval for checking for changes in Consul. This works only if consul_sd_configs is configured in '-promscrape.config' file. See https://docs.victoriametrics.com/victoriametrics/sd_configs/#consul_sd_configs for details (default 30s)
|
||||
-promscrape.consulagentSDCheckInterval duration
|
||||
Interval for checking for changes in Consul Agent. This works only if consulagent_sd_configs is configured in '-promscrape.config' file. See https://docs.victoriametrics.com/victoriametrics/sd_configs/#consulagent_sd_configs for details (default 30s)
|
||||
-promscrape.digitaloceanSDCheckInterval duration
|
||||
Interval for checking for changes in digital ocean. This works only if digitalocean_sd_configs is configured in '-promscrape.config' file. See https://docs.victoriametrics.com/victoriametrics/sd_configs/#digitalocean_sd_configs for details (default 1m0s)
|
||||
-promscrape.disableCompression
|
||||
Whether to disable sending 'Accept-Encoding: gzip' request headers to all the scrape targets. This may reduce CPU usage on scrape targets at the cost of higher network bandwidth utilization. It is possible to set 'disable_compression: true' individually per each 'scrape_config' section in '-promscrape.config' for fine-grained control
|
||||
-promscrape.disableKeepAlive
|
||||
Whether to disable HTTP keep-alive connections when scraping all the targets. This may be useful when targets has no support for HTTP keep-alive connection. It is possible to set 'disable_keepalive: true' individually per each 'scrape_config' section in '-promscrape.config' for fine-grained control. Note that disabling HTTP keep-alive may increase load on both vmagent and scrape targets
|
||||
-promscrape.discovery.concurrency int
|
||||
The maximum number of concurrent requests to Prometheus autodiscovery API (Consul, Kubernetes, etc.) (default 100)
|
||||
-promscrape.discovery.concurrentWaitTime duration
|
||||
The maximum duration for waiting to perform API requests if more than -promscrape.discovery.concurrency requests are simultaneously performed (default 1m0s)
|
||||
-promscrape.dnsSDCheckInterval duration
|
||||
Interval for checking for changes in dns. This works only if dns_sd_configs is configured in '-promscrape.config' file. See https://docs.victoriametrics.com/victoriametrics/sd_configs/#dns_sd_configs for details (default 30s)
|
||||
-promscrape.dockerSDCheckInterval duration
|
||||
Interval for checking for changes in docker. This works only if docker_sd_configs is configured in '-promscrape.config' file. See https://docs.victoriametrics.com/victoriametrics/sd_configs/#docker_sd_configs for details (default 30s)
|
||||
-promscrape.dockerswarmSDCheckInterval duration
|
||||
Interval for checking for changes in dockerswarm. This works only if dockerswarm_sd_configs is configured in '-promscrape.config' file. See https://docs.victoriametrics.com/victoriametrics/sd_configs/#dockerswarm_sd_configs for details (default 30s)
|
||||
-promscrape.dropOriginalLabels
|
||||
Whether to drop original labels for scrape targets at /targets and /api/v1/targets pages. This may be needed for reducing memory usage when original labels for big number of scrape targets occupy big amounts of memory. Note that this reduces debuggability for improper per-target relabeling configs
|
||||
-promscrape.ec2SDCheckInterval duration
|
||||
Interval for checking for changes in ec2. This works only if ec2_sd_configs is configured in '-promscrape.config' file. See https://docs.victoriametrics.com/victoriametrics/sd_configs/#ec2_sd_configs for details (default 1m0s)
|
||||
-promscrape.eurekaSDCheckInterval duration
|
||||
Interval for checking for changes in eureka. This works only if eureka_sd_configs is configured in '-promscrape.config' file. See https://docs.victoriametrics.com/victoriametrics/sd_configs/#eureka_sd_configs for details (default 30s)
|
||||
-promscrape.fileSDCheckInterval duration
|
||||
Interval for checking for changes in 'file_sd_config'. See https://docs.victoriametrics.com/victoriametrics/sd_configs/#file_sd_configs for details (default 1m0s)
|
||||
-promscrape.gceSDCheckInterval duration
|
||||
Interval for checking for changes in gce. This works only if gce_sd_configs is configured in '-promscrape.config' file. See https://docs.victoriametrics.com/victoriametrics/sd_configs/#gce_sd_configs for details (default 1m0s)
|
||||
-promscrape.hetznerSDCheckInterval duration
|
||||
Interval for checking for changes in Hetzner API. This works only if hetzner_sd_configs is configured in '-promscrape.config' file. See https://docs.victoriametrics.com/victoriametrics/sd_configs/#hetzner_sd_configs for details (default 1m0s)
|
||||
-promscrape.httpSDCheckInterval duration
|
||||
Interval for checking for changes in http endpoint service discovery. This works only if http_sd_configs is configured in '-promscrape.config' file. See https://docs.victoriametrics.com/victoriametrics/sd_configs/#http_sd_configs for details (default 1m0s)
|
||||
-promscrape.kubernetes.apiServerTimeout duration
|
||||
How frequently to reload the full state from Kubernetes API server (default 30m0s)
|
||||
-promscrape.kubernetes.attachNamespaceMetadataAll
|
||||
Whether to set attach_metadata.namespace=true for all the kubernetes_sd_configs at -promscrape.config . It is possible to set attach_metadata.namespace=false individually per each kubernetes_sd_configs . See https://docs.victoriametrics.com/victoriametrics/sd_configs/#kubernetes_sd_configs
|
||||
-promscrape.kubernetes.attachNodeMetadataAll
|
||||
Whether to set attach_metadata.node=true for all the kubernetes_sd_configs at -promscrape.config . It is possible to set attach_metadata.node=false individually per each kubernetes_sd_configs . See https://docs.victoriametrics.com/victoriametrics/sd_configs/#kubernetes_sd_configs
|
||||
-promscrape.kubernetes.useHTTP2Client
|
||||
Whether to use HTTP/2 client for connection to Kubernetes API server. This may reduce amount of concurrent connections to API server when watching for a big number of Kubernetes objects.
|
||||
-promscrape.kubernetesSDCheckInterval duration
|
||||
Interval for checking for changes in Kubernetes API server. This works only if kubernetes_sd_configs is configured in '-promscrape.config' file. See https://docs.victoriametrics.com/victoriametrics/sd_configs/#kubernetes_sd_configs for details (default 30s)
|
||||
-promscrape.kumaSDCheckInterval duration
|
||||
Interval for checking for changes in kuma service discovery. This works only if kuma_sd_configs is configured in '-promscrape.config' file. See https://docs.victoriametrics.com/victoriametrics/sd_configs/#kuma_sd_configs for details (default 30s)
|
||||
-promscrape.marathonSDCheckInterval duration
|
||||
Interval for checking for changes in Marathon REST API. This works only if marathon_sd_configs is configured in '-promscrape.config' file. See https://docs.victoriametrics.com/victoriametrics/sd_configs/#marathon_sd_configs for details (default 30s)
|
||||
-promscrape.maxDroppedTargets int
|
||||
The maximum number of droppedTargets to show at /api/v1/targets page. Increase this value if your setup drops more scrape targets during relabeling and you need investigating labels for all the dropped targets. Note that the increased number of tracked dropped targets may result in increased memory usage (default 10000)
|
||||
-promscrape.maxResponseHeadersSize size
|
||||
The maximum size of http response headers from Prometheus scrape targets
|
||||
Supports the following optional suffixes for size values: KB, MB, GB, TB, KiB, MiB, GiB, TiB (default 4096)
|
||||
-promscrape.maxScrapeSize size
|
||||
The maximum size of uncompressed scrape response in bytes to process from Prometheus targets. Bigger uncompressed responses are rejected. See also max_scrape_size option at https://docs.victoriametrics.com/victoriametrics/sd_configs/#scrape_configs
|
||||
Supports the following optional suffixes for size values: KB, MB, GB, TB, KiB, MiB, GiB, TiB (default 16777216)
|
||||
-promscrape.minResponseSizeForStreamParse size
|
||||
The minimum target response size for automatic switching to stream parsing mode, which can reduce memory usage. See https://docs.victoriametrics.com/victoriametrics/vmagent/#stream-parsing-mode
|
||||
Supports the following optional suffixes for size values: KB, MB, GB, TB, KiB, MiB, GiB, TiB (default 1000000)
|
||||
-promscrape.noStaleMarkers
|
||||
Whether to disable sending Prometheus stale markers for metrics when scrape target disappears. This option may reduce memory usage if stale markers aren't needed for your setup. This option also disables populating the scrape_series_added metric. See https://prometheus.io/docs/concepts/jobs_instances/#automatically-generated-labels-and-time-series
|
||||
-promscrape.nomad.waitTime duration
|
||||
Wait time used by Nomad service discovery. Default value is used if not set
|
||||
-promscrape.nomadSDCheckInterval duration
|
||||
Interval for checking for changes in Nomad. This works only if nomad_sd_configs is configured in '-promscrape.config' file. See https://docs.victoriametrics.com/victoriametrics/sd_configs/#nomad_sd_configs for details (default 30s)
|
||||
-promscrape.openstackSDCheckInterval duration
|
||||
Interval for checking for changes in openstack API server. This works only if openstack_sd_configs is configured in '-promscrape.config' file. See https://docs.victoriametrics.com/victoriametrics/sd_configs/#openstack_sd_configs for details (default 30s)
|
||||
-promscrape.ovhcloudSDCheckInterval duration
|
||||
Interval for checking for changes in OVH Cloud API. This works only if ovhcloud_sd_configs is configured in '-promscrape.config' file. See https://docs.victoriametrics.com/victoriametrics/sd_configs/#ovhcloud_sd_configs for details (default 30s)
|
||||
-promscrape.puppetdbSDCheckInterval duration
|
||||
Interval for checking for changes in PuppetDB API. This works only if puppetdb_sd_configs is configured in '-promscrape.config' file. See https://docs.victoriametrics.com/victoriametrics/sd_configs/#puppetdb_sd_configs for details (default 30s)
|
||||
-promscrape.seriesLimitPerTarget int
|
||||
Optional limit on the number of unique time series a single scrape target can expose. See https://docs.victoriametrics.com/victoriametrics/vmagent/#cardinality-limiter for more info
|
||||
-promscrape.streamParse
|
||||
Whether to enable stream parsing for metrics obtained from scrape targets. This may be useful for reducing memory usage when millions of metrics are exposed per each scrape target. It is possible to set 'stream_parse: true' individually per each 'scrape_config' section in '-promscrape.config' for fine-grained control
|
||||
-promscrape.suppressDuplicateScrapeTargetErrors
|
||||
Whether to suppress 'duplicate scrape target' errors; see https://docs.victoriametrics.com/victoriametrics/vmagent/#troubleshooting for details
|
||||
-promscrape.suppressScrapeErrors
|
||||
Whether to suppress scrape errors logging. The last error for each target is always available at '/targets' page even if scrape errors logging is suppressed. See also -promscrape.suppressScrapeErrorsDelay
|
||||
-promscrape.suppressScrapeErrorsDelay duration
|
||||
The delay for suppressing repeated scrape errors logging per each scrape targets. This may be used for reducing the number of log lines related to scrape errors. See also -promscrape.suppressScrapeErrors
|
||||
-promscrape.vultrSDCheckInterval duration
|
||||
Interval for checking for changes in Vultr. This works only if vultr_sd_configs is configured in '-promscrape.config' file. See https://docs.victoriametrics.com/victoriametrics/sd_configs/#vultr_sd_configs for details (default 30s)
|
||||
-promscrape.yandexcloudSDCheckInterval duration
|
||||
Interval for checking for changes in Yandex Cloud API. This works only if yandexcloud_sd_configs is configured in '-promscrape.config' file. See https://docs.victoriametrics.com/victoriametrics/sd_configs/#yandexcloud_sd_configs for details (default 30s)
|
||||
-pushmetrics.disableCompression
|
||||
Whether to disable request body compression when pushing metrics to every -pushmetrics.url
|
||||
-pushmetrics.extraLabel array
|
||||
|
||||
@@ -130,9 +130,6 @@ See the docs at https://docs.victoriametrics.com/victoriametrics/cluster-victori
|
||||
Timezone to use for timestamps in logs. Timezone must be a valid IANA Time Zone. For example: America/New_York, Europe/Berlin, Etc/GMT+3 or Local (default "UTC")
|
||||
-loggerWarnsPerSecondLimit int
|
||||
Per-second limit on the number of WARN messages. If more than the given number of warns are emitted per second, then the remaining warns are suppressed. Zero values disable the rate limit
|
||||
-maxBackfillAge value
|
||||
The maximum allowed age for the ingested samples with historical timestamps. Samples with timestamps older than now-maxBackfillAge are rejected during data ingestion. By default, or when set to 0, -maxBackfillAge equals to -retentionPeriod, e.g. it is unlimited within the configured retention. This can be useful for limiting ingestion of historical samples, for example, when older data has been moved to another storage tier. See https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/#retention
|
||||
The following optional suffixes are supported: s (second), h (hour), d (day), w (week), M (month), y (year). If suffix isn't set, then the duration is counted in months (default 0)
|
||||
-maxConcurrentInserts int
|
||||
The maximum number of concurrent insert requests. Set higher value when clients send data over slow networks. Default value depends on the number of available CPU cores. It should work fine in most cases since it minimizes resource usage. See also -insert.maxQueueDuration (default 2*cgroup.AvailableCPUs())
|
||||
-memory.allowedBytes size
|
||||
|
||||
2
go.mod
2
go.mod
@@ -1,6 +1,6 @@
|
||||
module github.com/VictoriaMetrics/VictoriaMetrics
|
||||
|
||||
go 1.26.5
|
||||
go 1.26.4
|
||||
|
||||
require (
|
||||
cloud.google.com/go/storage v1.62.3
|
||||
|
||||
@@ -648,7 +648,7 @@ func (mi *metainfo) WriteToFile(path string) error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot marshal persistent queue metainfo %#v: %w", mi, err)
|
||||
}
|
||||
fs.MustWriteAtomic(path, data, true)
|
||||
fs.MustWriteSync(path, data)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -9,48 +9,47 @@ import (
|
||||
)
|
||||
|
||||
// WriteMetricRelabelDebug writes /metric-relabel-debug page to w with the corresponding args.
|
||||
func WriteMetricRelabelDebug(w io.Writer, targetID, metric, relabelConfigs string, urlRelabelIndexLength, urlRelabelIndexCurrent int, format string, err error) {
|
||||
writeRelabelDebug(w, false, targetID, metric, relabelConfigs, urlRelabelIndexLength, urlRelabelIndexCurrent, format, err)
|
||||
func WriteMetricRelabelDebug(w io.Writer, targetID, metric, relabelConfigs, format string, err error) {
|
||||
writeRelabelDebug(w, false, targetID, metric, relabelConfigs, format, err)
|
||||
}
|
||||
|
||||
// WriteTargetRelabelDebug writes /target-relabel-debug page to w with the corresponding args.
|
||||
func WriteTargetRelabelDebug(w io.Writer, targetID, metric, relabelConfigs, format string, err error) {
|
||||
writeRelabelDebug(w, true, targetID, metric, relabelConfigs, 0, 0, format, err)
|
||||
writeRelabelDebug(w, true, targetID, metric, relabelConfigs, format, err)
|
||||
}
|
||||
|
||||
func writeRelabelDebug(w io.Writer, isTargetRelabel bool, targetID, metric, relabelConfigs string, urlRelabelIndexLength, urlRelabelIndexCurrent int, format string, err error) {
|
||||
func writeRelabelDebug(w io.Writer, isTargetRelabel bool, targetID, metric, relabelConfigs, format string, err error) {
|
||||
if metric == "" {
|
||||
metric = "{}"
|
||||
}
|
||||
targetURL := ""
|
||||
if err != nil {
|
||||
WriteRelabelDebugSteps(w, targetURL, targetID, format, nil, metric, relabelConfigs, urlRelabelIndexLength, urlRelabelIndexCurrent, isTargetRelabel, err)
|
||||
WriteRelabelDebugSteps(w, targetURL, targetID, format, nil, metric, relabelConfigs, err)
|
||||
return
|
||||
}
|
||||
|
||||
metric, err = normalizeInputLabels(metric)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("cannot parse metric: %w", err)
|
||||
WriteRelabelDebugSteps(w, targetURL, targetID, format, nil, metric, relabelConfigs, urlRelabelIndexLength, urlRelabelIndexCurrent, isTargetRelabel, err)
|
||||
WriteRelabelDebugSteps(w, targetURL, targetID, format, nil, metric, relabelConfigs, err)
|
||||
return
|
||||
}
|
||||
|
||||
labels, err := promutil.NewLabelsFromString(metric)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("cannot parse metric: %w", err)
|
||||
WriteRelabelDebugSteps(w, targetURL, targetID, format, nil, metric, relabelConfigs, urlRelabelIndexLength, urlRelabelIndexCurrent, isTargetRelabel, err)
|
||||
WriteRelabelDebugSteps(w, targetURL, targetID, format, nil, metric, relabelConfigs, err)
|
||||
return
|
||||
}
|
||||
|
||||
pcs, err := ParseRelabelConfigsData([]byte(relabelConfigs))
|
||||
if err != nil {
|
||||
err = fmt.Errorf("cannot parse relabel configs: %w", err)
|
||||
WriteRelabelDebugSteps(w, targetURL, targetID, format, nil, metric, relabelConfigs, urlRelabelIndexLength, urlRelabelIndexCurrent, isTargetRelabel, err)
|
||||
WriteRelabelDebugSteps(w, targetURL, targetID, format, nil, metric, relabelConfigs, err)
|
||||
return
|
||||
}
|
||||
|
||||
dss, targetURL := newDebugRelabelSteps(pcs, labels, isTargetRelabel)
|
||||
WriteRelabelDebugSteps(w, targetURL, targetID, format, dss, metric, relabelConfigs, urlRelabelIndexLength, urlRelabelIndexCurrent, isTargetRelabel, nil)
|
||||
WriteRelabelDebugSteps(w, targetURL, targetID, format, dss, metric, relabelConfigs, nil)
|
||||
}
|
||||
|
||||
func newDebugRelabelSteps(pcs *ParsedConfigs, labels *promutil.Labels, isTargetRelabel bool) ([]DebugStep, string) {
|
||||
@@ -141,10 +140,6 @@ func getChangedLabelNames(in, out *promutil.Labels) map[string]struct{} {
|
||||
func normalizeInputLabels(metric string) (string, error) {
|
||||
metric = strings.TrimSpace(metric)
|
||||
|
||||
if strings.ContainsRune(metric, '\n') {
|
||||
return metric, fmt.Errorf("only one time series is allowed; got multiple lines")
|
||||
}
|
||||
|
||||
openBrace := strings.Contains(metric, `{`)
|
||||
closeBrace := strings.Contains(metric, `}`)
|
||||
|
||||
|
||||
@@ -6,66 +6,37 @@
|
||||
|
||||
{% stripspace %}
|
||||
|
||||
{% func RelabelDebugSteps(targetURL, targetID, format string, dss []DebugStep, metric, relabelConfigs string, urlRelabelIndexLength, urlRelabelIndexCurrent int, isTargetRelabel bool, err error) %}
|
||||
{% func RelabelDebugSteps(targetURL, targetID, format string, dss []DebugStep, metric, relabelConfigs string, err error) %}
|
||||
{% if format == "json" %}
|
||||
{%= RelabelDebugStepsJSON(targetURL, targetID, dss, metric, relabelConfigs, urlRelabelIndexLength, urlRelabelIndexCurrent, isTargetRelabel, err) %}
|
||||
{%= RelabelDebugStepsJSON(targetURL, targetID, dss, metric, relabelConfigs, err) %}
|
||||
{% else %}
|
||||
{%= RelabelDebugStepsHTML(targetURL, targetID, dss, metric, relabelConfigs, urlRelabelIndexLength, urlRelabelIndexCurrent, isTargetRelabel, err) %}
|
||||
{%= RelabelDebugStepsHTML(targetURL, targetID, dss, metric, relabelConfigs, err) %}
|
||||
{% endif %}
|
||||
{% endfunc %}
|
||||
|
||||
{% func RelabelDebugStepsHTML(targetURL, targetID string, dss []DebugStep, metric, relabelConfigs string, urlRelabelIndexLength, urlRelabelIndexCurrent int, isTargetRelabel bool, err error) %}
|
||||
{% func RelabelDebugStepsHTML(targetURL, targetID string, dss []DebugStep, metric, relabelConfigs string, err error) %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
{%= htmlcomponents.CommonHeader() %}
|
||||
<title>Metric relabel debug</title>
|
||||
<script>
|
||||
function setRelabelDebugFormMethod(form) {
|
||||
form.method = (form.elements["relabel_configs"].value.length + form.elements["metric"].value.length > 1000) ? "POST" : "GET";
|
||||
}
|
||||
|
||||
function reloadRelabelConfigs(select) {
|
||||
if (!confirm('Reload will discard all modifications to the current configuration. Continue?')) {
|
||||
select.value = select.prevValue;
|
||||
return;
|
||||
function submitRelabelDebugForm(e) {
|
||||
var form = e.target;
|
||||
var method = "GET";
|
||||
if (form.elements["relabel_configs"].value.length + form.elements["metric"].value.length > 1000) {
|
||||
method = "POST";
|
||||
}
|
||||
document.getElementById('reload_url_relabel_configs').value = '1';
|
||||
setRelabelDebugFormMethod(select.form);
|
||||
select.form.submit();
|
||||
form.method = method;
|
||||
}
|
||||
|
||||
function initRelabelConfigsHighlight() {
|
||||
var ta = document.getElementById('relabel-configs-input');
|
||||
var bd = document.getElementById('relabel-configs-backdrop');
|
||||
if (!ta || !bd) return;
|
||||
function escapeHtml(s) {
|
||||
return s.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');
|
||||
}
|
||||
function highlight(text) {
|
||||
return text.split('\n').map(function(line) {
|
||||
var e = escapeHtml(line);
|
||||
return /^\s*#/.test(line)
|
||||
? '<span style="color:#999">'+e+'</span>'
|
||||
: '<span style="color:#212529">'+e+'</span>';
|
||||
}).join('\n');
|
||||
}
|
||||
function update() { bd.innerHTML = highlight(ta.value)+'\n'; }
|
||||
ta.addEventListener('input', update);
|
||||
ta.addEventListener('scroll', function() { bd.scrollTop = ta.scrollTop; });
|
||||
update();
|
||||
}
|
||||
document.addEventListener('DOMContentLoaded', initRelabelConfigsHighlight);
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
{%= htmlcomponents.Navbar() %}
|
||||
<div class="container-fluid">
|
||||
<a href="https://docs.victoriametrics.com/victoriametrics/relabeling/" target="_blank">Relabeling Cookbook</a>{% space %}|{% space %}
|
||||
<a href="https://docs.victoriametrics.com/victoriametrics/relabeling/#relabeling-stages" target="_blank">Relabeling Stages</a>
|
||||
<a href="https://docs.victoriametrics.com/victoriametrics/relabeling/" target="_blank">Relabeling docs</a>{% space %}
|
||||
|
||||
{% if targetID != "" %}
|
||||
{% space %}|{% space %}
|
||||
{% if targetURL != "" %}
|
||||
<a href="metric-relabel-debug?id={%s targetID %}">Metric relabel debug</a>
|
||||
{% else %}
|
||||
@@ -79,14 +50,14 @@ document.addEventListener('DOMContentLoaded', initRelabelConfigsHighlight);
|
||||
{% endif %}
|
||||
|
||||
<div class="m-3">
|
||||
<form method="POST" onsubmit="setRelabelDebugFormMethod(this)">
|
||||
{%= relabelDebugFormInputs(metric, relabelConfigs, urlRelabelIndexLength, urlRelabelIndexCurrent, isTargetRelabel, targetID) %}
|
||||
<form method="POST" onsubmit="submitRelabelDebugForm(event)">
|
||||
{%= relabelDebugFormInputs(metric, relabelConfigs) %}
|
||||
{% if targetID != "" %}
|
||||
<input type="hidden" name="id" value="{%s targetID %}" />
|
||||
{% endif %}
|
||||
<input type="submit" value="Submit" class="btn btn-primary m-1" />
|
||||
{% if targetID != "" %}
|
||||
<button type="button" onclick="location.href='?id={%s targetID %}'" class="btn btn-secondary m-1">Reset All</button>
|
||||
<button type="button" onclick="location.href='?id={%s targetID %}'" class="btn btn-secondary m-1">Reset</button>
|
||||
{% endif %}
|
||||
</form>
|
||||
</div>
|
||||
@@ -101,44 +72,15 @@ document.addEventListener('DOMContentLoaded', initRelabelConfigsHighlight);
|
||||
</html>
|
||||
{% endfunc %}
|
||||
|
||||
{% func relabelDebugFormInputs(metric, relabelConfigs string, urlRelabelIndexLength, urlRelabelIndexCurrent int, isTargetRelabel bool, targetID string) %}
|
||||
{% func relabelDebugFormInputs(metric, relabelConfigs string) %}
|
||||
<div>
|
||||
<!-- show remote write relabel reload only for scrape metric relabel debug and pure relabel debug. discovery debug should not display this section -->
|
||||
{% if !isTargetRelabel %}
|
||||
<div>
|
||||
<div class="m-1">
|
||||
<div class="d-flex align-items-center gap-2 mt-1">
|
||||
Configs:
|
||||
{% if urlRelabelIndexLength > 0 %}
|
||||
<input type="hidden" name="reload_url_relabel_configs" id="reload_url_relabel_configs" value="" />
|
||||
<select name="url_relabel_configs_index" class="form-select form-select-sm w-auto" onfocus="this.prevValue=this.value" onchange="reloadRelabelConfigs(this)">
|
||||
{% for i := range urlRelabelIndexLength %}
|
||||
{% if urlRelabelIndexCurrent == i %}
|
||||
<option value="{%d i %}" selected="selected">remote-write-url-{%d i %}</option>
|
||||
{% else %}
|
||||
<option value="{%d i %}">remote-write-url-{%d i %}</option>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</select>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
Configs:
|
||||
{% endif %}
|
||||
|
||||
<!-- the following text area css was generated with the help of AI to display yaml comments (starting with #) in gray. it could be rewritten in the future -->
|
||||
<div class="m-1" style="position:relative;height:15em;">
|
||||
<div id="relabel-configs-backdrop" style="position:absolute;top:0;left:0;right:0;bottom:0;pointer-events:none;overflow:hidden;font-family:monospace;white-space:pre-wrap;padding:0.375rem 0.75rem;border:1px solid transparent;"></div>
|
||||
<textarea id="relabel-configs-input" name="relabel_configs" class="form-control" style="position:absolute;top:0;left:0;height:100%;font-family:monospace;color:transparent;caret-color:#212529;background:transparent;resize:none;overflow-y:scroll;">
|
||||
{%s relabelConfigs %}
|
||||
</textarea>
|
||||
</div>
|
||||
Relabel configs:<br/>
|
||||
<textarea name="relabel_configs" style="width: 100%; height: 15em; font-family: monospace" class="m-1">{%s relabelConfigs %}</textarea>
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
A Time Series:<br/>
|
||||
<textarea name="metric" style="width: 100%; height: 5em; font-family: monospace" class="m-1" placeholder="up{job="job_name",instance="host:port"}">{%s metric %}</textarea>
|
||||
|
||||
<div>
|
||||
Labels:<br/>
|
||||
<textarea name="metric" style="width: 100%; height: 5em; font-family: monospace" class="m-1">{%s metric %}</textarea>
|
||||
</div>
|
||||
{% endfunc %}
|
||||
|
||||
@@ -211,7 +153,7 @@ document.addEventListener('DOMContentLoaded', initRelabelConfigsHighlight);
|
||||
{% endif %}
|
||||
{% endfunc %}
|
||||
|
||||
{% func RelabelDebugStepsJSON(targetURL, targetID string, dss []DebugStep, metric, relabelConfigs string, urlRelabelIndexLength, urlRelabelIndexCurrent int, isTargetRelabel bool, err error) %}
|
||||
{% func RelabelDebugStepsJSON(targetURL, targetID string, dss []DebugStep, metric, relabelConfigs string, err error) %}
|
||||
{
|
||||
{% if err != nil %}
|
||||
"status": "error",
|
||||
|
||||
@@ -25,37 +25,37 @@ var (
|
||||
)
|
||||
|
||||
//line lib/promrelabel/debug.qtpl:9
|
||||
func StreamRelabelDebugSteps(qw422016 *qt422016.Writer, targetURL, targetID, format string, dss []DebugStep, metric, relabelConfigs string, urlRelabelIndexLength, urlRelabelIndexCurrent int, isTargetRelabel bool, err error) {
|
||||
func StreamRelabelDebugSteps(qw422016 *qt422016.Writer, targetURL, targetID, format string, dss []DebugStep, metric, relabelConfigs string, err error) {
|
||||
//line lib/promrelabel/debug.qtpl:10
|
||||
if format == "json" {
|
||||
//line lib/promrelabel/debug.qtpl:11
|
||||
StreamRelabelDebugStepsJSON(qw422016, targetURL, targetID, dss, metric, relabelConfigs, urlRelabelIndexLength, urlRelabelIndexCurrent, isTargetRelabel, err)
|
||||
StreamRelabelDebugStepsJSON(qw422016, targetURL, targetID, dss, metric, relabelConfigs, err)
|
||||
//line lib/promrelabel/debug.qtpl:12
|
||||
} else {
|
||||
//line lib/promrelabel/debug.qtpl:13
|
||||
StreamRelabelDebugStepsHTML(qw422016, targetURL, targetID, dss, metric, relabelConfigs, urlRelabelIndexLength, urlRelabelIndexCurrent, isTargetRelabel, err)
|
||||
StreamRelabelDebugStepsHTML(qw422016, targetURL, targetID, dss, metric, relabelConfigs, err)
|
||||
//line lib/promrelabel/debug.qtpl:14
|
||||
}
|
||||
//line lib/promrelabel/debug.qtpl:15
|
||||
}
|
||||
|
||||
//line lib/promrelabel/debug.qtpl:15
|
||||
func WriteRelabelDebugSteps(qq422016 qtio422016.Writer, targetURL, targetID, format string, dss []DebugStep, metric, relabelConfigs string, urlRelabelIndexLength, urlRelabelIndexCurrent int, isTargetRelabel bool, err error) {
|
||||
func WriteRelabelDebugSteps(qq422016 qtio422016.Writer, targetURL, targetID, format string, dss []DebugStep, metric, relabelConfigs string, err error) {
|
||||
//line lib/promrelabel/debug.qtpl:15
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line lib/promrelabel/debug.qtpl:15
|
||||
StreamRelabelDebugSteps(qw422016, targetURL, targetID, format, dss, metric, relabelConfigs, urlRelabelIndexLength, urlRelabelIndexCurrent, isTargetRelabel, err)
|
||||
StreamRelabelDebugSteps(qw422016, targetURL, targetID, format, dss, metric, relabelConfigs, err)
|
||||
//line lib/promrelabel/debug.qtpl:15
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line lib/promrelabel/debug.qtpl:15
|
||||
}
|
||||
|
||||
//line lib/promrelabel/debug.qtpl:15
|
||||
func RelabelDebugSteps(targetURL, targetID, format string, dss []DebugStep, metric, relabelConfigs string, urlRelabelIndexLength, urlRelabelIndexCurrent int, isTargetRelabel bool, err error) string {
|
||||
func RelabelDebugSteps(targetURL, targetID, format string, dss []DebugStep, metric, relabelConfigs string, err error) string {
|
||||
//line lib/promrelabel/debug.qtpl:15
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line lib/promrelabel/debug.qtpl:15
|
||||
WriteRelabelDebugSteps(qb422016, targetURL, targetID, format, dss, metric, relabelConfigs, urlRelabelIndexLength, urlRelabelIndexCurrent, isTargetRelabel, err)
|
||||
WriteRelabelDebugSteps(qb422016, targetURL, targetID, format, dss, metric, relabelConfigs, err)
|
||||
//line lib/promrelabel/debug.qtpl:15
|
||||
qs422016 := string(qb422016.B)
|
||||
//line lib/promrelabel/debug.qtpl:15
|
||||
@@ -66,495 +66,431 @@ func RelabelDebugSteps(targetURL, targetID, format string, dss []DebugStep, metr
|
||||
}
|
||||
|
||||
//line lib/promrelabel/debug.qtpl:17
|
||||
func StreamRelabelDebugStepsHTML(qw422016 *qt422016.Writer, targetURL, targetID string, dss []DebugStep, metric, relabelConfigs string, urlRelabelIndexLength, urlRelabelIndexCurrent int, isTargetRelabel bool, err error) {
|
||||
func StreamRelabelDebugStepsHTML(qw422016 *qt422016.Writer, targetURL, targetID string, dss []DebugStep, metric, relabelConfigs string, err error) {
|
||||
//line lib/promrelabel/debug.qtpl:17
|
||||
qw422016.N().S(`<!DOCTYPE html><html lang="en"><head>`)
|
||||
//line lib/promrelabel/debug.qtpl:21
|
||||
htmlcomponents.StreamCommonHeader(qw422016)
|
||||
//line lib/promrelabel/debug.qtpl:21
|
||||
qw422016.N().S(`<title>Metric relabel debug</title><script>function setRelabelDebugFormMethod(form) {form.method = (form.elements["relabel_configs"].value.length + form.elements["metric"].value.length > 1000) ? "POST" : "GET";}function reloadRelabelConfigs(select) {if (!confirm('Reload will discard all modifications to the current configuration. Continue?')) {select.value = select.prevValue;return;}document.getElementById('reload_url_relabel_configs').value = '1';setRelabelDebugFormMethod(select.form);select.form.submit();}function initRelabelConfigsHighlight() {var ta = document.getElementById('relabel-configs-input');var bd = document.getElementById('relabel-configs-backdrop');if (!ta || !bd) return;function escapeHtml(s) {return s.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');}function highlight(text) {return text.split('\n').map(function(line) {var e = escapeHtml(line);return /^\s*#/.test(line)? '<span style="color:#999">'+e+'</span>': '<span style="color:#212529">'+e+'</span>';}).join('\n');}function update() { bd.innerHTML = highlight(ta.value)+'\n'; }ta.addEventListener('input', update);ta.addEventListener('scroll', function() { bd.scrollTop = ta.scrollTop; });update();}document.addEventListener('DOMContentLoaded', initRelabelConfigsHighlight);</script></head><body>`)
|
||||
//line lib/promrelabel/debug.qtpl:62
|
||||
qw422016.N().S(`<title>Metric relabel debug</title><script>function submitRelabelDebugForm(e) {var form = e.target;var method = "GET";if (form.elements["relabel_configs"].value.length + form.elements["metric"].value.length > 1000) {method = "POST";}form.method = method;}</script></head><body>`)
|
||||
//line lib/promrelabel/debug.qtpl:35
|
||||
htmlcomponents.StreamNavbar(qw422016)
|
||||
//line lib/promrelabel/debug.qtpl:62
|
||||
qw422016.N().S(`<div class="container-fluid"><a href="https://docs.victoriametrics.com/victoriametrics/relabeling/" target="_blank">Relabeling Cookbook</a>`)
|
||||
//line lib/promrelabel/debug.qtpl:64
|
||||
//line lib/promrelabel/debug.qtpl:35
|
||||
qw422016.N().S(`<div class="container-fluid"><a href="https://docs.victoriametrics.com/victoriametrics/relabeling/" target="_blank">Relabeling docs</a>`)
|
||||
//line lib/promrelabel/debug.qtpl:37
|
||||
qw422016.N().S(` `)
|
||||
//line lib/promrelabel/debug.qtpl:64
|
||||
qw422016.N().S(`|`)
|
||||
//line lib/promrelabel/debug.qtpl:64
|
||||
qw422016.N().S(` `)
|
||||
//line lib/promrelabel/debug.qtpl:64
|
||||
qw422016.N().S(`<a href="https://docs.victoriametrics.com/victoriametrics/relabeling/#relabeling-stages" target="_blank">Relabeling Stages</a>`)
|
||||
//line lib/promrelabel/debug.qtpl:67
|
||||
//line lib/promrelabel/debug.qtpl:39
|
||||
if targetID != "" {
|
||||
//line lib/promrelabel/debug.qtpl:68
|
||||
qw422016.N().S(` `)
|
||||
//line lib/promrelabel/debug.qtpl:68
|
||||
qw422016.N().S(`|`)
|
||||
//line lib/promrelabel/debug.qtpl:68
|
||||
qw422016.N().S(` `)
|
||||
//line lib/promrelabel/debug.qtpl:69
|
||||
//line lib/promrelabel/debug.qtpl:40
|
||||
if targetURL != "" {
|
||||
//line lib/promrelabel/debug.qtpl:69
|
||||
//line lib/promrelabel/debug.qtpl:40
|
||||
qw422016.N().S(`<a href="metric-relabel-debug?id=`)
|
||||
//line lib/promrelabel/debug.qtpl:70
|
||||
//line lib/promrelabel/debug.qtpl:41
|
||||
qw422016.E().S(targetID)
|
||||
//line lib/promrelabel/debug.qtpl:70
|
||||
//line lib/promrelabel/debug.qtpl:41
|
||||
qw422016.N().S(`">Metric relabel debug</a>`)
|
||||
//line lib/promrelabel/debug.qtpl:71
|
||||
//line lib/promrelabel/debug.qtpl:42
|
||||
} else {
|
||||
//line lib/promrelabel/debug.qtpl:71
|
||||
//line lib/promrelabel/debug.qtpl:42
|
||||
qw422016.N().S(`<a href="target-relabel-debug?id=`)
|
||||
//line lib/promrelabel/debug.qtpl:72
|
||||
//line lib/promrelabel/debug.qtpl:43
|
||||
qw422016.E().S(targetID)
|
||||
//line lib/promrelabel/debug.qtpl:72
|
||||
//line lib/promrelabel/debug.qtpl:43
|
||||
qw422016.N().S(`">Target relabel debug</a>`)
|
||||
//line lib/promrelabel/debug.qtpl:73
|
||||
//line lib/promrelabel/debug.qtpl:44
|
||||
}
|
||||
//line lib/promrelabel/debug.qtpl:74
|
||||
//line lib/promrelabel/debug.qtpl:45
|
||||
}
|
||||
//line lib/promrelabel/debug.qtpl:74
|
||||
//line lib/promrelabel/debug.qtpl:45
|
||||
qw422016.N().S(`<br>`)
|
||||
//line lib/promrelabel/debug.qtpl:77
|
||||
//line lib/promrelabel/debug.qtpl:48
|
||||
if err != nil {
|
||||
//line lib/promrelabel/debug.qtpl:78
|
||||
//line lib/promrelabel/debug.qtpl:49
|
||||
htmlcomponents.StreamErrorNotification(qw422016, err)
|
||||
//line lib/promrelabel/debug.qtpl:79
|
||||
//line lib/promrelabel/debug.qtpl:50
|
||||
}
|
||||
//line lib/promrelabel/debug.qtpl:79
|
||||
qw422016.N().S(`<div class="m-3"><form method="POST" onsubmit="setRelabelDebugFormMethod(this)">`)
|
||||
//line lib/promrelabel/debug.qtpl:83
|
||||
streamrelabelDebugFormInputs(qw422016, metric, relabelConfigs, urlRelabelIndexLength, urlRelabelIndexCurrent, isTargetRelabel, targetID)
|
||||
//line lib/promrelabel/debug.qtpl:84
|
||||
//line lib/promrelabel/debug.qtpl:50
|
||||
qw422016.N().S(`<div class="m-3"><form method="POST" onsubmit="submitRelabelDebugForm(event)">`)
|
||||
//line lib/promrelabel/debug.qtpl:54
|
||||
streamrelabelDebugFormInputs(qw422016, metric, relabelConfigs)
|
||||
//line lib/promrelabel/debug.qtpl:55
|
||||
if targetID != "" {
|
||||
//line lib/promrelabel/debug.qtpl:84
|
||||
//line lib/promrelabel/debug.qtpl:55
|
||||
qw422016.N().S(`<input type="hidden" name="id" value="`)
|
||||
//line lib/promrelabel/debug.qtpl:85
|
||||
//line lib/promrelabel/debug.qtpl:56
|
||||
qw422016.E().S(targetID)
|
||||
//line lib/promrelabel/debug.qtpl:85
|
||||
//line lib/promrelabel/debug.qtpl:56
|
||||
qw422016.N().S(`" />`)
|
||||
//line lib/promrelabel/debug.qtpl:86
|
||||
//line lib/promrelabel/debug.qtpl:57
|
||||
}
|
||||
//line lib/promrelabel/debug.qtpl:86
|
||||
//line lib/promrelabel/debug.qtpl:57
|
||||
qw422016.N().S(`<input type="submit" value="Submit" class="btn btn-primary m-1" />`)
|
||||
//line lib/promrelabel/debug.qtpl:88
|
||||
//line lib/promrelabel/debug.qtpl:59
|
||||
if targetID != "" {
|
||||
//line lib/promrelabel/debug.qtpl:88
|
||||
//line lib/promrelabel/debug.qtpl:59
|
||||
qw422016.N().S(`<button type="button" onclick="location.href='?id=`)
|
||||
//line lib/promrelabel/debug.qtpl:89
|
||||
//line lib/promrelabel/debug.qtpl:60
|
||||
qw422016.E().S(targetID)
|
||||
//line lib/promrelabel/debug.qtpl:89
|
||||
qw422016.N().S(`'" class="btn btn-secondary m-1">Reset All</button>`)
|
||||
//line lib/promrelabel/debug.qtpl:90
|
||||
//line lib/promrelabel/debug.qtpl:60
|
||||
qw422016.N().S(`'" class="btn btn-secondary m-1">Reset</button>`)
|
||||
//line lib/promrelabel/debug.qtpl:61
|
||||
}
|
||||
//line lib/promrelabel/debug.qtpl:90
|
||||
//line lib/promrelabel/debug.qtpl:61
|
||||
qw422016.N().S(`</form></div><div class="row"><main class="col-12">`)
|
||||
//line lib/promrelabel/debug.qtpl:96
|
||||
//line lib/promrelabel/debug.qtpl:67
|
||||
streamrelabelDebugSteps(qw422016, dss, targetURL, targetID)
|
||||
//line lib/promrelabel/debug.qtpl:96
|
||||
//line lib/promrelabel/debug.qtpl:67
|
||||
qw422016.N().S(`</main></div></div></body></html>`)
|
||||
//line lib/promrelabel/debug.qtpl:102
|
||||
//line lib/promrelabel/debug.qtpl:73
|
||||
}
|
||||
|
||||
//line lib/promrelabel/debug.qtpl:102
|
||||
func WriteRelabelDebugStepsHTML(qq422016 qtio422016.Writer, targetURL, targetID string, dss []DebugStep, metric, relabelConfigs string, urlRelabelIndexLength, urlRelabelIndexCurrent int, isTargetRelabel bool, err error) {
|
||||
//line lib/promrelabel/debug.qtpl:102
|
||||
//line lib/promrelabel/debug.qtpl:73
|
||||
func WriteRelabelDebugStepsHTML(qq422016 qtio422016.Writer, targetURL, targetID string, dss []DebugStep, metric, relabelConfigs string, err error) {
|
||||
//line lib/promrelabel/debug.qtpl:73
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line lib/promrelabel/debug.qtpl:102
|
||||
StreamRelabelDebugStepsHTML(qw422016, targetURL, targetID, dss, metric, relabelConfigs, urlRelabelIndexLength, urlRelabelIndexCurrent, isTargetRelabel, err)
|
||||
//line lib/promrelabel/debug.qtpl:102
|
||||
//line lib/promrelabel/debug.qtpl:73
|
||||
StreamRelabelDebugStepsHTML(qw422016, targetURL, targetID, dss, metric, relabelConfigs, err)
|
||||
//line lib/promrelabel/debug.qtpl:73
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line lib/promrelabel/debug.qtpl:102
|
||||
//line lib/promrelabel/debug.qtpl:73
|
||||
}
|
||||
|
||||
//line lib/promrelabel/debug.qtpl:102
|
||||
func RelabelDebugStepsHTML(targetURL, targetID string, dss []DebugStep, metric, relabelConfigs string, urlRelabelIndexLength, urlRelabelIndexCurrent int, isTargetRelabel bool, err error) string {
|
||||
//line lib/promrelabel/debug.qtpl:102
|
||||
//line lib/promrelabel/debug.qtpl:73
|
||||
func RelabelDebugStepsHTML(targetURL, targetID string, dss []DebugStep, metric, relabelConfigs string, err error) string {
|
||||
//line lib/promrelabel/debug.qtpl:73
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line lib/promrelabel/debug.qtpl:102
|
||||
WriteRelabelDebugStepsHTML(qb422016, targetURL, targetID, dss, metric, relabelConfigs, urlRelabelIndexLength, urlRelabelIndexCurrent, isTargetRelabel, err)
|
||||
//line lib/promrelabel/debug.qtpl:102
|
||||
//line lib/promrelabel/debug.qtpl:73
|
||||
WriteRelabelDebugStepsHTML(qb422016, targetURL, targetID, dss, metric, relabelConfigs, err)
|
||||
//line lib/promrelabel/debug.qtpl:73
|
||||
qs422016 := string(qb422016.B)
|
||||
//line lib/promrelabel/debug.qtpl:102
|
||||
//line lib/promrelabel/debug.qtpl:73
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line lib/promrelabel/debug.qtpl:102
|
||||
//line lib/promrelabel/debug.qtpl:73
|
||||
return qs422016
|
||||
//line lib/promrelabel/debug.qtpl:102
|
||||
//line lib/promrelabel/debug.qtpl:73
|
||||
}
|
||||
|
||||
//line lib/promrelabel/debug.qtpl:104
|
||||
func streamrelabelDebugFormInputs(qw422016 *qt422016.Writer, metric, relabelConfigs string, urlRelabelIndexLength, urlRelabelIndexCurrent int, isTargetRelabel bool, targetID string) {
|
||||
//line lib/promrelabel/debug.qtpl:104
|
||||
qw422016.N().S(`<div><!-- show remote write relabel reload only for scrape metric relabel debug and pure relabel debug. discovery debug should not display this section -->`)
|
||||
//line lib/promrelabel/debug.qtpl:107
|
||||
if !isTargetRelabel {
|
||||
//line lib/promrelabel/debug.qtpl:107
|
||||
qw422016.N().S(`<div><div class="m-1"><div class="d-flex align-items-center gap-2 mt-1">Configs:`)
|
||||
//line lib/promrelabel/debug.qtpl:112
|
||||
if urlRelabelIndexLength > 0 {
|
||||
//line lib/promrelabel/debug.qtpl:112
|
||||
qw422016.N().S(`<input type="hidden" name="reload_url_relabel_configs" id="reload_url_relabel_configs" value="" /><select name="url_relabel_configs_index" class="form-select form-select-sm w-auto" onfocus="this.prevValue=this.value" onchange="reloadRelabelConfigs(this)">`)
|
||||
//line lib/promrelabel/debug.qtpl:115
|
||||
for i := range urlRelabelIndexLength {
|
||||
//line lib/promrelabel/debug.qtpl:116
|
||||
if urlRelabelIndexCurrent == i {
|
||||
//line lib/promrelabel/debug.qtpl:116
|
||||
qw422016.N().S(`<option value="`)
|
||||
//line lib/promrelabel/debug.qtpl:117
|
||||
qw422016.N().D(i)
|
||||
//line lib/promrelabel/debug.qtpl:117
|
||||
qw422016.N().S(`" selected="selected">remote-write-url-`)
|
||||
//line lib/promrelabel/debug.qtpl:117
|
||||
qw422016.N().D(i)
|
||||
//line lib/promrelabel/debug.qtpl:117
|
||||
qw422016.N().S(`</option>`)
|
||||
//line lib/promrelabel/debug.qtpl:118
|
||||
} else {
|
||||
//line lib/promrelabel/debug.qtpl:118
|
||||
qw422016.N().S(`<option value="`)
|
||||
//line lib/promrelabel/debug.qtpl:119
|
||||
qw422016.N().D(i)
|
||||
//line lib/promrelabel/debug.qtpl:119
|
||||
qw422016.N().S(`">remote-write-url-`)
|
||||
//line lib/promrelabel/debug.qtpl:119
|
||||
qw422016.N().D(i)
|
||||
//line lib/promrelabel/debug.qtpl:119
|
||||
qw422016.N().S(`</option>`)
|
||||
//line lib/promrelabel/debug.qtpl:120
|
||||
}
|
||||
//line lib/promrelabel/debug.qtpl:121
|
||||
}
|
||||
//line lib/promrelabel/debug.qtpl:121
|
||||
qw422016.N().S(`</select>`)
|
||||
//line lib/promrelabel/debug.qtpl:123
|
||||
}
|
||||
//line lib/promrelabel/debug.qtpl:123
|
||||
qw422016.N().S(`</div></div></div>`)
|
||||
//line lib/promrelabel/debug.qtpl:127
|
||||
} else {
|
||||
//line lib/promrelabel/debug.qtpl:127
|
||||
qw422016.N().S(`Configs:`)
|
||||
//line lib/promrelabel/debug.qtpl:129
|
||||
}
|
||||
//line lib/promrelabel/debug.qtpl:129
|
||||
qw422016.N().S(`<!-- the following text area css was generated with the help of AI to display yaml comments (starting with #) in gray. it could be rewritten in the future --><div class="m-1" style="position:relative;height:15em;"><div id="relabel-configs-backdrop" style="position:absolute;top:0;left:0;right:0;bottom:0;pointer-events:none;overflow:hidden;font-family:monospace;white-space:pre-wrap;padding:0.375rem 0.75rem;border:1px solid transparent;"></div><textarea id="relabel-configs-input" name="relabel_configs" class="form-control" style="position:absolute;top:0;left:0;height:100%;font-family:monospace;color:transparent;caret-color:#212529;background:transparent;resize:none;overflow-y:scroll;">`)
|
||||
//line lib/promrelabel/debug.qtpl:135
|
||||
//line lib/promrelabel/debug.qtpl:75
|
||||
func streamrelabelDebugFormInputs(qw422016 *qt422016.Writer, metric, relabelConfigs string) {
|
||||
//line lib/promrelabel/debug.qtpl:75
|
||||
qw422016.N().S(`<div>Relabel configs:<br/><textarea name="relabel_configs" style="width: 100%; height: 15em; font-family: monospace" class="m-1">`)
|
||||
//line lib/promrelabel/debug.qtpl:78
|
||||
qw422016.E().S(relabelConfigs)
|
||||
//line lib/promrelabel/debug.qtpl:135
|
||||
qw422016.N().S(`</textarea></div></div><div class="mt-2">A Time Series:<br/><textarea name="metric" style="width: 100%; height: 5em; font-family: monospace" class="m-1" placeholder="up{job="job_name",instance="host:port"}">`)
|
||||
//line lib/promrelabel/debug.qtpl:141
|
||||
//line lib/promrelabel/debug.qtpl:78
|
||||
qw422016.N().S(`</textarea></div><div>Labels:<br/><textarea name="metric" style="width: 100%; height: 5em; font-family: monospace" class="m-1">`)
|
||||
//line lib/promrelabel/debug.qtpl:83
|
||||
qw422016.E().S(metric)
|
||||
//line lib/promrelabel/debug.qtpl:141
|
||||
//line lib/promrelabel/debug.qtpl:83
|
||||
qw422016.N().S(`</textarea></div>`)
|
||||
//line lib/promrelabel/debug.qtpl:143
|
||||
//line lib/promrelabel/debug.qtpl:85
|
||||
}
|
||||
|
||||
//line lib/promrelabel/debug.qtpl:143
|
||||
func writerelabelDebugFormInputs(qq422016 qtio422016.Writer, metric, relabelConfigs string, urlRelabelIndexLength, urlRelabelIndexCurrent int, isTargetRelabel bool, targetID string) {
|
||||
//line lib/promrelabel/debug.qtpl:143
|
||||
//line lib/promrelabel/debug.qtpl:85
|
||||
func writerelabelDebugFormInputs(qq422016 qtio422016.Writer, metric, relabelConfigs string) {
|
||||
//line lib/promrelabel/debug.qtpl:85
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line lib/promrelabel/debug.qtpl:143
|
||||
streamrelabelDebugFormInputs(qw422016, metric, relabelConfigs, urlRelabelIndexLength, urlRelabelIndexCurrent, isTargetRelabel, targetID)
|
||||
//line lib/promrelabel/debug.qtpl:143
|
||||
//line lib/promrelabel/debug.qtpl:85
|
||||
streamrelabelDebugFormInputs(qw422016, metric, relabelConfigs)
|
||||
//line lib/promrelabel/debug.qtpl:85
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line lib/promrelabel/debug.qtpl:143
|
||||
//line lib/promrelabel/debug.qtpl:85
|
||||
}
|
||||
|
||||
//line lib/promrelabel/debug.qtpl:143
|
||||
func relabelDebugFormInputs(metric, relabelConfigs string, urlRelabelIndexLength, urlRelabelIndexCurrent int, isTargetRelabel bool, targetID string) string {
|
||||
//line lib/promrelabel/debug.qtpl:143
|
||||
//line lib/promrelabel/debug.qtpl:85
|
||||
func relabelDebugFormInputs(metric, relabelConfigs string) string {
|
||||
//line lib/promrelabel/debug.qtpl:85
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line lib/promrelabel/debug.qtpl:143
|
||||
writerelabelDebugFormInputs(qb422016, metric, relabelConfigs, urlRelabelIndexLength, urlRelabelIndexCurrent, isTargetRelabel, targetID)
|
||||
//line lib/promrelabel/debug.qtpl:143
|
||||
//line lib/promrelabel/debug.qtpl:85
|
||||
writerelabelDebugFormInputs(qb422016, metric, relabelConfigs)
|
||||
//line lib/promrelabel/debug.qtpl:85
|
||||
qs422016 := string(qb422016.B)
|
||||
//line lib/promrelabel/debug.qtpl:143
|
||||
//line lib/promrelabel/debug.qtpl:85
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line lib/promrelabel/debug.qtpl:143
|
||||
//line lib/promrelabel/debug.qtpl:85
|
||||
return qs422016
|
||||
//line lib/promrelabel/debug.qtpl:143
|
||||
//line lib/promrelabel/debug.qtpl:85
|
||||
}
|
||||
|
||||
//line lib/promrelabel/debug.qtpl:145
|
||||
//line lib/promrelabel/debug.qtpl:87
|
||||
func streamrelabelDebugSteps(qw422016 *qt422016.Writer, dss []DebugStep, targetURL, targetID string) {
|
||||
//line lib/promrelabel/debug.qtpl:146
|
||||
//line lib/promrelabel/debug.qtpl:88
|
||||
if len(dss) > 0 {
|
||||
//line lib/promrelabel/debug.qtpl:146
|
||||
//line lib/promrelabel/debug.qtpl:88
|
||||
qw422016.N().S(`<div class="m-3"><b>Original labels:</b> <samp>`)
|
||||
//line lib/promrelabel/debug.qtpl:148
|
||||
//line lib/promrelabel/debug.qtpl:90
|
||||
streammustFormatLabels(qw422016, dss[0].In)
|
||||
//line lib/promrelabel/debug.qtpl:148
|
||||
//line lib/promrelabel/debug.qtpl:90
|
||||
qw422016.N().S(`</samp></div>`)
|
||||
//line lib/promrelabel/debug.qtpl:150
|
||||
//line lib/promrelabel/debug.qtpl:92
|
||||
}
|
||||
//line lib/promrelabel/debug.qtpl:150
|
||||
//line lib/promrelabel/debug.qtpl:92
|
||||
qw422016.N().S(`<table class="table table-striped table-hover table-bordered table-sm"><thead><tr><th scope="col" style="width: 5%">Step</th><th scope="col" style="width: 25%">Relabeling Rule</th><th scope="col" style="width: 35%">Input Labels</th><th scope="col" stile="width: 35%">Output labels</a></tr></thead><tbody>`)
|
||||
//line lib/promrelabel/debug.qtpl:161
|
||||
//line lib/promrelabel/debug.qtpl:103
|
||||
for i, ds := range dss {
|
||||
//line lib/promrelabel/debug.qtpl:163
|
||||
//line lib/promrelabel/debug.qtpl:105
|
||||
inLabels, inErr := promutil.NewLabelsFromString(ds.In)
|
||||
outLabels, outErr := promutil.NewLabelsFromString(ds.Out)
|
||||
changedLabels := getChangedLabelNames(inLabels, outLabels)
|
||||
|
||||
//line lib/promrelabel/debug.qtpl:166
|
||||
//line lib/promrelabel/debug.qtpl:108
|
||||
qw422016.N().S(`<tr><td>`)
|
||||
//line lib/promrelabel/debug.qtpl:168
|
||||
//line lib/promrelabel/debug.qtpl:110
|
||||
qw422016.N().D(i)
|
||||
//line lib/promrelabel/debug.qtpl:168
|
||||
//line lib/promrelabel/debug.qtpl:110
|
||||
qw422016.N().S(`</td><td><b><pre class="m-2">`)
|
||||
//line lib/promrelabel/debug.qtpl:169
|
||||
//line lib/promrelabel/debug.qtpl:111
|
||||
qw422016.E().S(ds.Rule)
|
||||
//line lib/promrelabel/debug.qtpl:169
|
||||
//line lib/promrelabel/debug.qtpl:111
|
||||
qw422016.N().S(`</pre></b></td><td>`)
|
||||
//line lib/promrelabel/debug.qtpl:171
|
||||
//line lib/promrelabel/debug.qtpl:113
|
||||
if inErr == nil {
|
||||
//line lib/promrelabel/debug.qtpl:171
|
||||
//line lib/promrelabel/debug.qtpl:113
|
||||
qw422016.N().S(`<div class="m-2" style="font-size: 0.9em" title="deleted and updated labels highlighted in red">`)
|
||||
//line lib/promrelabel/debug.qtpl:173
|
||||
//line lib/promrelabel/debug.qtpl:115
|
||||
streamlabelsWithHighlight(qw422016, inLabels, changedLabels, "#D15757")
|
||||
//line lib/promrelabel/debug.qtpl:173
|
||||
//line lib/promrelabel/debug.qtpl:115
|
||||
qw422016.N().S(`</div>`)
|
||||
//line lib/promrelabel/debug.qtpl:175
|
||||
//line lib/promrelabel/debug.qtpl:117
|
||||
} else {
|
||||
//line lib/promrelabel/debug.qtpl:175
|
||||
//line lib/promrelabel/debug.qtpl:117
|
||||
qw422016.N().S(`<div class="m-2" style="font-size: 0.9em; color: red" title="error parsing input labels"><pre>`)
|
||||
//line lib/promrelabel/debug.qtpl:177
|
||||
//line lib/promrelabel/debug.qtpl:119
|
||||
qw422016.E().S(inErr.Error())
|
||||
//line lib/promrelabel/debug.qtpl:177
|
||||
//line lib/promrelabel/debug.qtpl:119
|
||||
qw422016.N().S(`</pre></div>`)
|
||||
//line lib/promrelabel/debug.qtpl:179
|
||||
//line lib/promrelabel/debug.qtpl:121
|
||||
break
|
||||
//line lib/promrelabel/debug.qtpl:180
|
||||
//line lib/promrelabel/debug.qtpl:122
|
||||
}
|
||||
//line lib/promrelabel/debug.qtpl:180
|
||||
//line lib/promrelabel/debug.qtpl:122
|
||||
qw422016.N().S(`</td><td>`)
|
||||
//line lib/promrelabel/debug.qtpl:183
|
||||
//line lib/promrelabel/debug.qtpl:125
|
||||
if outErr == nil {
|
||||
//line lib/promrelabel/debug.qtpl:183
|
||||
//line lib/promrelabel/debug.qtpl:125
|
||||
qw422016.N().S(`<div class="m-2" style="font-size: 0.9em" title="added and updated labels highlighted in blue">`)
|
||||
//line lib/promrelabel/debug.qtpl:185
|
||||
//line lib/promrelabel/debug.qtpl:127
|
||||
streamlabelsWithHighlight(qw422016, outLabels, changedLabels, "#4495e0")
|
||||
//line lib/promrelabel/debug.qtpl:185
|
||||
//line lib/promrelabel/debug.qtpl:127
|
||||
qw422016.N().S(`</div>`)
|
||||
//line lib/promrelabel/debug.qtpl:187
|
||||
//line lib/promrelabel/debug.qtpl:129
|
||||
} else {
|
||||
//line lib/promrelabel/debug.qtpl:187
|
||||
//line lib/promrelabel/debug.qtpl:129
|
||||
qw422016.N().S(`<div class="m-2" style="font-size: 0.9em; color: red" title="error parsing output labels"><pre>`)
|
||||
//line lib/promrelabel/debug.qtpl:189
|
||||
//line lib/promrelabel/debug.qtpl:131
|
||||
qw422016.E().S(outErr.Error())
|
||||
//line lib/promrelabel/debug.qtpl:189
|
||||
//line lib/promrelabel/debug.qtpl:131
|
||||
qw422016.N().S(`</pre></div>`)
|
||||
//line lib/promrelabel/debug.qtpl:191
|
||||
//line lib/promrelabel/debug.qtpl:133
|
||||
break
|
||||
//line lib/promrelabel/debug.qtpl:192
|
||||
//line lib/promrelabel/debug.qtpl:134
|
||||
}
|
||||
//line lib/promrelabel/debug.qtpl:192
|
||||
//line lib/promrelabel/debug.qtpl:134
|
||||
qw422016.N().S(`</td></tr>`)
|
||||
//line lib/promrelabel/debug.qtpl:195
|
||||
//line lib/promrelabel/debug.qtpl:137
|
||||
}
|
||||
//line lib/promrelabel/debug.qtpl:195
|
||||
//line lib/promrelabel/debug.qtpl:137
|
||||
qw422016.N().S(`</tbody></table>`)
|
||||
//line lib/promrelabel/debug.qtpl:198
|
||||
//line lib/promrelabel/debug.qtpl:140
|
||||
if len(dss) > 0 {
|
||||
//line lib/promrelabel/debug.qtpl:198
|
||||
//line lib/promrelabel/debug.qtpl:140
|
||||
qw422016.N().S(`<div class="m-3"><b>Resulting labels:</b> <samp>`)
|
||||
//line lib/promrelabel/debug.qtpl:200
|
||||
//line lib/promrelabel/debug.qtpl:142
|
||||
streammustFormatLabels(qw422016, dss[len(dss)-1].Out)
|
||||
//line lib/promrelabel/debug.qtpl:200
|
||||
//line lib/promrelabel/debug.qtpl:142
|
||||
qw422016.N().S(`</samp>`)
|
||||
//line lib/promrelabel/debug.qtpl:201
|
||||
//line lib/promrelabel/debug.qtpl:143
|
||||
if targetURL != "" {
|
||||
//line lib/promrelabel/debug.qtpl:201
|
||||
//line lib/promrelabel/debug.qtpl:143
|
||||
qw422016.N().S(`<div><b>Target URL:</b>`)
|
||||
//line lib/promrelabel/debug.qtpl:203
|
||||
//line lib/promrelabel/debug.qtpl:145
|
||||
qw422016.N().S(` `)
|
||||
//line lib/promrelabel/debug.qtpl:203
|
||||
//line lib/promrelabel/debug.qtpl:145
|
||||
qw422016.N().S(`<a href="`)
|
||||
//line lib/promrelabel/debug.qtpl:203
|
||||
//line lib/promrelabel/debug.qtpl:145
|
||||
qw422016.E().S(targetURL)
|
||||
//line lib/promrelabel/debug.qtpl:203
|
||||
//line lib/promrelabel/debug.qtpl:145
|
||||
qw422016.N().S(`" target="_blank">`)
|
||||
//line lib/promrelabel/debug.qtpl:203
|
||||
//line lib/promrelabel/debug.qtpl:145
|
||||
qw422016.E().S(targetURL)
|
||||
//line lib/promrelabel/debug.qtpl:203
|
||||
//line lib/promrelabel/debug.qtpl:145
|
||||
qw422016.N().S(`</a>`)
|
||||
//line lib/promrelabel/debug.qtpl:204
|
||||
//line lib/promrelabel/debug.qtpl:146
|
||||
if targetID != "" {
|
||||
//line lib/promrelabel/debug.qtpl:205
|
||||
//line lib/promrelabel/debug.qtpl:147
|
||||
qw422016.N().S(` `)
|
||||
//line lib/promrelabel/debug.qtpl:205
|
||||
//line lib/promrelabel/debug.qtpl:147
|
||||
qw422016.N().S(`(<a href="target_response?id=`)
|
||||
//line lib/promrelabel/debug.qtpl:206
|
||||
//line lib/promrelabel/debug.qtpl:148
|
||||
qw422016.E().S(targetID)
|
||||
//line lib/promrelabel/debug.qtpl:206
|
||||
//line lib/promrelabel/debug.qtpl:148
|
||||
qw422016.N().S(`" target="_blank" title="click to fetch target response on behalf of the scraper">response</a>)`)
|
||||
//line lib/promrelabel/debug.qtpl:207
|
||||
//line lib/promrelabel/debug.qtpl:149
|
||||
}
|
||||
//line lib/promrelabel/debug.qtpl:207
|
||||
//line lib/promrelabel/debug.qtpl:149
|
||||
qw422016.N().S(`</div>`)
|
||||
//line lib/promrelabel/debug.qtpl:209
|
||||
//line lib/promrelabel/debug.qtpl:151
|
||||
}
|
||||
//line lib/promrelabel/debug.qtpl:209
|
||||
//line lib/promrelabel/debug.qtpl:151
|
||||
qw422016.N().S(`</div>`)
|
||||
//line lib/promrelabel/debug.qtpl:211
|
||||
//line lib/promrelabel/debug.qtpl:153
|
||||
}
|
||||
//line lib/promrelabel/debug.qtpl:212
|
||||
//line lib/promrelabel/debug.qtpl:154
|
||||
}
|
||||
|
||||
//line lib/promrelabel/debug.qtpl:212
|
||||
//line lib/promrelabel/debug.qtpl:154
|
||||
func writerelabelDebugSteps(qq422016 qtio422016.Writer, dss []DebugStep, targetURL, targetID string) {
|
||||
//line lib/promrelabel/debug.qtpl:212
|
||||
//line lib/promrelabel/debug.qtpl:154
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line lib/promrelabel/debug.qtpl:212
|
||||
//line lib/promrelabel/debug.qtpl:154
|
||||
streamrelabelDebugSteps(qw422016, dss, targetURL, targetID)
|
||||
//line lib/promrelabel/debug.qtpl:212
|
||||
//line lib/promrelabel/debug.qtpl:154
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line lib/promrelabel/debug.qtpl:212
|
||||
//line lib/promrelabel/debug.qtpl:154
|
||||
}
|
||||
|
||||
//line lib/promrelabel/debug.qtpl:212
|
||||
//line lib/promrelabel/debug.qtpl:154
|
||||
func relabelDebugSteps(dss []DebugStep, targetURL, targetID string) string {
|
||||
//line lib/promrelabel/debug.qtpl:212
|
||||
//line lib/promrelabel/debug.qtpl:154
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line lib/promrelabel/debug.qtpl:212
|
||||
//line lib/promrelabel/debug.qtpl:154
|
||||
writerelabelDebugSteps(qb422016, dss, targetURL, targetID)
|
||||
//line lib/promrelabel/debug.qtpl:212
|
||||
//line lib/promrelabel/debug.qtpl:154
|
||||
qs422016 := string(qb422016.B)
|
||||
//line lib/promrelabel/debug.qtpl:212
|
||||
//line lib/promrelabel/debug.qtpl:154
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line lib/promrelabel/debug.qtpl:212
|
||||
//line lib/promrelabel/debug.qtpl:154
|
||||
return qs422016
|
||||
//line lib/promrelabel/debug.qtpl:212
|
||||
//line lib/promrelabel/debug.qtpl:154
|
||||
}
|
||||
|
||||
//line lib/promrelabel/debug.qtpl:214
|
||||
func StreamRelabelDebugStepsJSON(qw422016 *qt422016.Writer, targetURL, targetID string, dss []DebugStep, metric, relabelConfigs string, urlRelabelIndexLength, urlRelabelIndexCurrent int, isTargetRelabel bool, err error) {
|
||||
//line lib/promrelabel/debug.qtpl:214
|
||||
//line lib/promrelabel/debug.qtpl:156
|
||||
func StreamRelabelDebugStepsJSON(qw422016 *qt422016.Writer, targetURL, targetID string, dss []DebugStep, metric, relabelConfigs string, err error) {
|
||||
//line lib/promrelabel/debug.qtpl:156
|
||||
qw422016.N().S(`{`)
|
||||
//line lib/promrelabel/debug.qtpl:216
|
||||
//line lib/promrelabel/debug.qtpl:158
|
||||
if err != nil {
|
||||
//line lib/promrelabel/debug.qtpl:216
|
||||
//line lib/promrelabel/debug.qtpl:158
|
||||
qw422016.N().S(`"status": "error","error":`)
|
||||
//line lib/promrelabel/debug.qtpl:218
|
||||
//line lib/promrelabel/debug.qtpl:160
|
||||
qw422016.N().Q(fmt.Sprintf("Error: %s", err))
|
||||
//line lib/promrelabel/debug.qtpl:219
|
||||
//line lib/promrelabel/debug.qtpl:161
|
||||
} else {
|
||||
//line lib/promrelabel/debug.qtpl:220
|
||||
//line lib/promrelabel/debug.qtpl:162
|
||||
var hasError bool
|
||||
|
||||
//line lib/promrelabel/debug.qtpl:220
|
||||
//line lib/promrelabel/debug.qtpl:162
|
||||
qw422016.N().S(`"status": "success","steps": [`)
|
||||
//line lib/promrelabel/debug.qtpl:223
|
||||
//line lib/promrelabel/debug.qtpl:165
|
||||
for i, ds := range dss {
|
||||
//line lib/promrelabel/debug.qtpl:225
|
||||
//line lib/promrelabel/debug.qtpl:167
|
||||
inLabels, inErr := promutil.NewLabelsFromString(ds.In)
|
||||
outLabels, outErr := promutil.NewLabelsFromString(ds.Out)
|
||||
changedLabels := getChangedLabelNames(inLabels, outLabels)
|
||||
|
||||
//line lib/promrelabel/debug.qtpl:228
|
||||
//line lib/promrelabel/debug.qtpl:170
|
||||
qw422016.N().S(`{"inLabels":`)
|
||||
//line lib/promrelabel/debug.qtpl:230
|
||||
//line lib/promrelabel/debug.qtpl:172
|
||||
qw422016.N().Q(labelsWithHighlight(inLabels, changedLabels, "#D15757"))
|
||||
//line lib/promrelabel/debug.qtpl:230
|
||||
//line lib/promrelabel/debug.qtpl:172
|
||||
qw422016.N().S(`,"outLabels":`)
|
||||
//line lib/promrelabel/debug.qtpl:231
|
||||
//line lib/promrelabel/debug.qtpl:173
|
||||
qw422016.N().Q(labelsWithHighlight(outLabels, changedLabels, "#4495e0"))
|
||||
//line lib/promrelabel/debug.qtpl:231
|
||||
//line lib/promrelabel/debug.qtpl:173
|
||||
qw422016.N().S(`,"rule":`)
|
||||
//line lib/promrelabel/debug.qtpl:232
|
||||
//line lib/promrelabel/debug.qtpl:174
|
||||
qw422016.N().Q(ds.Rule)
|
||||
//line lib/promrelabel/debug.qtpl:232
|
||||
//line lib/promrelabel/debug.qtpl:174
|
||||
qw422016.N().S(`,"errors": {`)
|
||||
//line lib/promrelabel/debug.qtpl:234
|
||||
//line lib/promrelabel/debug.qtpl:176
|
||||
if inErr != nil {
|
||||
//line lib/promrelabel/debug.qtpl:234
|
||||
//line lib/promrelabel/debug.qtpl:176
|
||||
qw422016.N().S(`"inLabels":`)
|
||||
//line lib/promrelabel/debug.qtpl:235
|
||||
//line lib/promrelabel/debug.qtpl:177
|
||||
qw422016.N().Q(`<span style="color: #D15757">` + inErr.Error() + `</span>`)
|
||||
//line lib/promrelabel/debug.qtpl:235
|
||||
//line lib/promrelabel/debug.qtpl:177
|
||||
if outErr != nil {
|
||||
//line lib/promrelabel/debug.qtpl:235
|
||||
//line lib/promrelabel/debug.qtpl:177
|
||||
qw422016.N().S(`,`)
|
||||
//line lib/promrelabel/debug.qtpl:235
|
||||
//line lib/promrelabel/debug.qtpl:177
|
||||
}
|
||||
//line lib/promrelabel/debug.qtpl:236
|
||||
//line lib/promrelabel/debug.qtpl:178
|
||||
hasError = true
|
||||
|
||||
//line lib/promrelabel/debug.qtpl:237
|
||||
//line lib/promrelabel/debug.qtpl:179
|
||||
} else {
|
||||
//line lib/promrelabel/debug.qtpl:238
|
||||
//line lib/promrelabel/debug.qtpl:180
|
||||
}
|
||||
//line lib/promrelabel/debug.qtpl:239
|
||||
//line lib/promrelabel/debug.qtpl:181
|
||||
if outErr != nil {
|
||||
//line lib/promrelabel/debug.qtpl:239
|
||||
//line lib/promrelabel/debug.qtpl:181
|
||||
qw422016.N().S(`"outLabels":`)
|
||||
//line lib/promrelabel/debug.qtpl:240
|
||||
//line lib/promrelabel/debug.qtpl:182
|
||||
qw422016.N().Q(`<span style="color: #D15757">` + outErr.Error() + `</span>`)
|
||||
//line lib/promrelabel/debug.qtpl:241
|
||||
//line lib/promrelabel/debug.qtpl:183
|
||||
hasError = true
|
||||
|
||||
//line lib/promrelabel/debug.qtpl:242
|
||||
//line lib/promrelabel/debug.qtpl:184
|
||||
}
|
||||
//line lib/promrelabel/debug.qtpl:242
|
||||
//line lib/promrelabel/debug.qtpl:184
|
||||
qw422016.N().S(`}}`)
|
||||
//line lib/promrelabel/debug.qtpl:245
|
||||
//line lib/promrelabel/debug.qtpl:187
|
||||
if i != len(dss)-1 {
|
||||
//line lib/promrelabel/debug.qtpl:245
|
||||
//line lib/promrelabel/debug.qtpl:187
|
||||
qw422016.N().S(`,`)
|
||||
//line lib/promrelabel/debug.qtpl:245
|
||||
//line lib/promrelabel/debug.qtpl:187
|
||||
}
|
||||
//line lib/promrelabel/debug.qtpl:246
|
||||
//line lib/promrelabel/debug.qtpl:188
|
||||
}
|
||||
//line lib/promrelabel/debug.qtpl:246
|
||||
//line lib/promrelabel/debug.qtpl:188
|
||||
qw422016.N().S(`]`)
|
||||
//line lib/promrelabel/debug.qtpl:248
|
||||
//line lib/promrelabel/debug.qtpl:190
|
||||
if len(dss) > 0 && !hasError {
|
||||
//line lib/promrelabel/debug.qtpl:248
|
||||
//line lib/promrelabel/debug.qtpl:190
|
||||
qw422016.N().S(`,"originalLabels":`)
|
||||
//line lib/promrelabel/debug.qtpl:250
|
||||
//line lib/promrelabel/debug.qtpl:192
|
||||
qw422016.N().Q(mustFormatLabels(dss[0].In))
|
||||
//line lib/promrelabel/debug.qtpl:250
|
||||
//line lib/promrelabel/debug.qtpl:192
|
||||
qw422016.N().S(`,"resultingLabels":`)
|
||||
//line lib/promrelabel/debug.qtpl:251
|
||||
//line lib/promrelabel/debug.qtpl:193
|
||||
qw422016.N().Q(mustFormatLabels(dss[len(dss)-1].Out))
|
||||
//line lib/promrelabel/debug.qtpl:252
|
||||
//line lib/promrelabel/debug.qtpl:194
|
||||
}
|
||||
//line lib/promrelabel/debug.qtpl:253
|
||||
//line lib/promrelabel/debug.qtpl:195
|
||||
}
|
||||
//line lib/promrelabel/debug.qtpl:253
|
||||
//line lib/promrelabel/debug.qtpl:195
|
||||
qw422016.N().S(`}`)
|
||||
//line lib/promrelabel/debug.qtpl:255
|
||||
//line lib/promrelabel/debug.qtpl:197
|
||||
}
|
||||
|
||||
//line lib/promrelabel/debug.qtpl:255
|
||||
func WriteRelabelDebugStepsJSON(qq422016 qtio422016.Writer, targetURL, targetID string, dss []DebugStep, metric, relabelConfigs string, urlRelabelIndexLength, urlRelabelIndexCurrent int, isTargetRelabel bool, err error) {
|
||||
//line lib/promrelabel/debug.qtpl:255
|
||||
//line lib/promrelabel/debug.qtpl:197
|
||||
func WriteRelabelDebugStepsJSON(qq422016 qtio422016.Writer, targetURL, targetID string, dss []DebugStep, metric, relabelConfigs string, err error) {
|
||||
//line lib/promrelabel/debug.qtpl:197
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line lib/promrelabel/debug.qtpl:255
|
||||
StreamRelabelDebugStepsJSON(qw422016, targetURL, targetID, dss, metric, relabelConfigs, urlRelabelIndexLength, urlRelabelIndexCurrent, isTargetRelabel, err)
|
||||
//line lib/promrelabel/debug.qtpl:255
|
||||
//line lib/promrelabel/debug.qtpl:197
|
||||
StreamRelabelDebugStepsJSON(qw422016, targetURL, targetID, dss, metric, relabelConfigs, err)
|
||||
//line lib/promrelabel/debug.qtpl:197
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line lib/promrelabel/debug.qtpl:255
|
||||
//line lib/promrelabel/debug.qtpl:197
|
||||
}
|
||||
|
||||
//line lib/promrelabel/debug.qtpl:255
|
||||
func RelabelDebugStepsJSON(targetURL, targetID string, dss []DebugStep, metric, relabelConfigs string, urlRelabelIndexLength, urlRelabelIndexCurrent int, isTargetRelabel bool, err error) string {
|
||||
//line lib/promrelabel/debug.qtpl:255
|
||||
//line lib/promrelabel/debug.qtpl:197
|
||||
func RelabelDebugStepsJSON(targetURL, targetID string, dss []DebugStep, metric, relabelConfigs string, err error) string {
|
||||
//line lib/promrelabel/debug.qtpl:197
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line lib/promrelabel/debug.qtpl:255
|
||||
WriteRelabelDebugStepsJSON(qb422016, targetURL, targetID, dss, metric, relabelConfigs, urlRelabelIndexLength, urlRelabelIndexCurrent, isTargetRelabel, err)
|
||||
//line lib/promrelabel/debug.qtpl:255
|
||||
//line lib/promrelabel/debug.qtpl:197
|
||||
WriteRelabelDebugStepsJSON(qb422016, targetURL, targetID, dss, metric, relabelConfigs, err)
|
||||
//line lib/promrelabel/debug.qtpl:197
|
||||
qs422016 := string(qb422016.B)
|
||||
//line lib/promrelabel/debug.qtpl:255
|
||||
//line lib/promrelabel/debug.qtpl:197
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line lib/promrelabel/debug.qtpl:255
|
||||
//line lib/promrelabel/debug.qtpl:197
|
||||
return qs422016
|
||||
//line lib/promrelabel/debug.qtpl:255
|
||||
//line lib/promrelabel/debug.qtpl:197
|
||||
}
|
||||
|
||||
//line lib/promrelabel/debug.qtpl:257
|
||||
//line lib/promrelabel/debug.qtpl:199
|
||||
func streamlabelsWithHighlight(qw422016 *qt422016.Writer, labels *promutil.Labels, highlight map[string]struct{}, color string) {
|
||||
//line lib/promrelabel/debug.qtpl:259
|
||||
//line lib/promrelabel/debug.qtpl:201
|
||||
labelsList := labels.GetLabels()
|
||||
metricName := ""
|
||||
for i, label := range labelsList {
|
||||
@@ -565,153 +501,153 @@ func streamlabelsWithHighlight(qw422016 *qt422016.Writer, labels *promutil.Label
|
||||
}
|
||||
}
|
||||
|
||||
//line lib/promrelabel/debug.qtpl:269
|
||||
//line lib/promrelabel/debug.qtpl:211
|
||||
if metricName != "" {
|
||||
//line lib/promrelabel/debug.qtpl:270
|
||||
//line lib/promrelabel/debug.qtpl:212
|
||||
if _, ok := highlight["__name__"]; ok {
|
||||
//line lib/promrelabel/debug.qtpl:270
|
||||
//line lib/promrelabel/debug.qtpl:212
|
||||
qw422016.N().S(`<span style="font-weight:bold;color:`)
|
||||
//line lib/promrelabel/debug.qtpl:271
|
||||
//line lib/promrelabel/debug.qtpl:213
|
||||
qw422016.E().S(color)
|
||||
//line lib/promrelabel/debug.qtpl:271
|
||||
//line lib/promrelabel/debug.qtpl:213
|
||||
qw422016.N().S(`">`)
|
||||
//line lib/promrelabel/debug.qtpl:271
|
||||
//line lib/promrelabel/debug.qtpl:213
|
||||
qw422016.E().S(metricName)
|
||||
//line lib/promrelabel/debug.qtpl:271
|
||||
//line lib/promrelabel/debug.qtpl:213
|
||||
qw422016.N().S(`</span>`)
|
||||
//line lib/promrelabel/debug.qtpl:272
|
||||
//line lib/promrelabel/debug.qtpl:214
|
||||
} else {
|
||||
//line lib/promrelabel/debug.qtpl:273
|
||||
//line lib/promrelabel/debug.qtpl:215
|
||||
qw422016.E().S(metricName)
|
||||
//line lib/promrelabel/debug.qtpl:274
|
||||
//line lib/promrelabel/debug.qtpl:216
|
||||
}
|
||||
//line lib/promrelabel/debug.qtpl:275
|
||||
//line lib/promrelabel/debug.qtpl:217
|
||||
if len(labelsList) == 0 {
|
||||
//line lib/promrelabel/debug.qtpl:275
|
||||
//line lib/promrelabel/debug.qtpl:217
|
||||
return
|
||||
//line lib/promrelabel/debug.qtpl:275
|
||||
//line lib/promrelabel/debug.qtpl:217
|
||||
}
|
||||
//line lib/promrelabel/debug.qtpl:276
|
||||
//line lib/promrelabel/debug.qtpl:218
|
||||
}
|
||||
//line lib/promrelabel/debug.qtpl:276
|
||||
//line lib/promrelabel/debug.qtpl:218
|
||||
qw422016.N().S(`{`)
|
||||
//line lib/promrelabel/debug.qtpl:278
|
||||
//line lib/promrelabel/debug.qtpl:220
|
||||
for i, label := range labelsList {
|
||||
//line lib/promrelabel/debug.qtpl:279
|
||||
//line lib/promrelabel/debug.qtpl:221
|
||||
if _, ok := highlight[label.Name]; ok {
|
||||
//line lib/promrelabel/debug.qtpl:279
|
||||
//line lib/promrelabel/debug.qtpl:221
|
||||
qw422016.N().S(`<span style="font-weight:bold;color:`)
|
||||
//line lib/promrelabel/debug.qtpl:280
|
||||
//line lib/promrelabel/debug.qtpl:222
|
||||
qw422016.E().S(color)
|
||||
//line lib/promrelabel/debug.qtpl:280
|
||||
//line lib/promrelabel/debug.qtpl:222
|
||||
qw422016.N().S(`">`)
|
||||
//line lib/promrelabel/debug.qtpl:280
|
||||
//line lib/promrelabel/debug.qtpl:222
|
||||
qw422016.E().S(label.Name)
|
||||
//line lib/promrelabel/debug.qtpl:280
|
||||
//line lib/promrelabel/debug.qtpl:222
|
||||
qw422016.N().S(`=`)
|
||||
//line lib/promrelabel/debug.qtpl:280
|
||||
//line lib/promrelabel/debug.qtpl:222
|
||||
qw422016.E().Q(label.Value)
|
||||
//line lib/promrelabel/debug.qtpl:280
|
||||
//line lib/promrelabel/debug.qtpl:222
|
||||
qw422016.N().S(`</span>`)
|
||||
//line lib/promrelabel/debug.qtpl:281
|
||||
//line lib/promrelabel/debug.qtpl:223
|
||||
} else {
|
||||
//line lib/promrelabel/debug.qtpl:282
|
||||
//line lib/promrelabel/debug.qtpl:224
|
||||
qw422016.E().S(label.Name)
|
||||
//line lib/promrelabel/debug.qtpl:282
|
||||
//line lib/promrelabel/debug.qtpl:224
|
||||
qw422016.N().S(`=`)
|
||||
//line lib/promrelabel/debug.qtpl:282
|
||||
//line lib/promrelabel/debug.qtpl:224
|
||||
qw422016.E().Q(label.Value)
|
||||
//line lib/promrelabel/debug.qtpl:283
|
||||
//line lib/promrelabel/debug.qtpl:225
|
||||
}
|
||||
//line lib/promrelabel/debug.qtpl:284
|
||||
//line lib/promrelabel/debug.qtpl:226
|
||||
if i < len(labelsList)-1 {
|
||||
//line lib/promrelabel/debug.qtpl:284
|
||||
//line lib/promrelabel/debug.qtpl:226
|
||||
qw422016.N().S(`,`)
|
||||
//line lib/promrelabel/debug.qtpl:284
|
||||
//line lib/promrelabel/debug.qtpl:226
|
||||
qw422016.N().S(` `)
|
||||
//line lib/promrelabel/debug.qtpl:284
|
||||
//line lib/promrelabel/debug.qtpl:226
|
||||
}
|
||||
//line lib/promrelabel/debug.qtpl:285
|
||||
//line lib/promrelabel/debug.qtpl:227
|
||||
}
|
||||
//line lib/promrelabel/debug.qtpl:285
|
||||
//line lib/promrelabel/debug.qtpl:227
|
||||
qw422016.N().S(`}`)
|
||||
//line lib/promrelabel/debug.qtpl:287
|
||||
//line lib/promrelabel/debug.qtpl:229
|
||||
}
|
||||
|
||||
//line lib/promrelabel/debug.qtpl:287
|
||||
//line lib/promrelabel/debug.qtpl:229
|
||||
func writelabelsWithHighlight(qq422016 qtio422016.Writer, labels *promutil.Labels, highlight map[string]struct{}, color string) {
|
||||
//line lib/promrelabel/debug.qtpl:287
|
||||
//line lib/promrelabel/debug.qtpl:229
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line lib/promrelabel/debug.qtpl:287
|
||||
//line lib/promrelabel/debug.qtpl:229
|
||||
streamlabelsWithHighlight(qw422016, labels, highlight, color)
|
||||
//line lib/promrelabel/debug.qtpl:287
|
||||
//line lib/promrelabel/debug.qtpl:229
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line lib/promrelabel/debug.qtpl:287
|
||||
//line lib/promrelabel/debug.qtpl:229
|
||||
}
|
||||
|
||||
//line lib/promrelabel/debug.qtpl:287
|
||||
//line lib/promrelabel/debug.qtpl:229
|
||||
func labelsWithHighlight(labels *promutil.Labels, highlight map[string]struct{}, color string) string {
|
||||
//line lib/promrelabel/debug.qtpl:287
|
||||
//line lib/promrelabel/debug.qtpl:229
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line lib/promrelabel/debug.qtpl:287
|
||||
//line lib/promrelabel/debug.qtpl:229
|
||||
writelabelsWithHighlight(qb422016, labels, highlight, color)
|
||||
//line lib/promrelabel/debug.qtpl:287
|
||||
//line lib/promrelabel/debug.qtpl:229
|
||||
qs422016 := string(qb422016.B)
|
||||
//line lib/promrelabel/debug.qtpl:287
|
||||
//line lib/promrelabel/debug.qtpl:229
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line lib/promrelabel/debug.qtpl:287
|
||||
//line lib/promrelabel/debug.qtpl:229
|
||||
return qs422016
|
||||
//line lib/promrelabel/debug.qtpl:287
|
||||
//line lib/promrelabel/debug.qtpl:229
|
||||
}
|
||||
|
||||
//line lib/promrelabel/debug.qtpl:289
|
||||
//line lib/promrelabel/debug.qtpl:231
|
||||
func streammustFormatLabels(qw422016 *qt422016.Writer, s string) {
|
||||
//line lib/promrelabel/debug.qtpl:290
|
||||
//line lib/promrelabel/debug.qtpl:232
|
||||
labels, err := promutil.NewLabelsFromString(s)
|
||||
|
||||
//line lib/promrelabel/debug.qtpl:291
|
||||
//line lib/promrelabel/debug.qtpl:233
|
||||
if err != nil {
|
||||
//line lib/promrelabel/debug.qtpl:291
|
||||
//line lib/promrelabel/debug.qtpl:233
|
||||
qw422016.N().S(`<span style="color: red" title="error parsing labels:`)
|
||||
//line lib/promrelabel/debug.qtpl:292
|
||||
//line lib/promrelabel/debug.qtpl:234
|
||||
qw422016.E().S(err.Error())
|
||||
//line lib/promrelabel/debug.qtpl:292
|
||||
//line lib/promrelabel/debug.qtpl:234
|
||||
qw422016.N().S(`">`)
|
||||
//line lib/promrelabel/debug.qtpl:292
|
||||
//line lib/promrelabel/debug.qtpl:234
|
||||
qw422016.E().S("error parsing labels: " + err.Error())
|
||||
//line lib/promrelabel/debug.qtpl:292
|
||||
//line lib/promrelabel/debug.qtpl:234
|
||||
qw422016.N().S(`</span>`)
|
||||
//line lib/promrelabel/debug.qtpl:293
|
||||
//line lib/promrelabel/debug.qtpl:235
|
||||
} else {
|
||||
//line lib/promrelabel/debug.qtpl:294
|
||||
//line lib/promrelabel/debug.qtpl:236
|
||||
streamlabelsWithHighlight(qw422016, labels, nil, "")
|
||||
//line lib/promrelabel/debug.qtpl:295
|
||||
//line lib/promrelabel/debug.qtpl:237
|
||||
}
|
||||
//line lib/promrelabel/debug.qtpl:296
|
||||
//line lib/promrelabel/debug.qtpl:238
|
||||
}
|
||||
|
||||
//line lib/promrelabel/debug.qtpl:296
|
||||
//line lib/promrelabel/debug.qtpl:238
|
||||
func writemustFormatLabels(qq422016 qtio422016.Writer, s string) {
|
||||
//line lib/promrelabel/debug.qtpl:296
|
||||
//line lib/promrelabel/debug.qtpl:238
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line lib/promrelabel/debug.qtpl:296
|
||||
//line lib/promrelabel/debug.qtpl:238
|
||||
streammustFormatLabels(qw422016, s)
|
||||
//line lib/promrelabel/debug.qtpl:296
|
||||
//line lib/promrelabel/debug.qtpl:238
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line lib/promrelabel/debug.qtpl:296
|
||||
//line lib/promrelabel/debug.qtpl:238
|
||||
}
|
||||
|
||||
//line lib/promrelabel/debug.qtpl:296
|
||||
//line lib/promrelabel/debug.qtpl:238
|
||||
func mustFormatLabels(s string) string {
|
||||
//line lib/promrelabel/debug.qtpl:296
|
||||
//line lib/promrelabel/debug.qtpl:238
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line lib/promrelabel/debug.qtpl:296
|
||||
//line lib/promrelabel/debug.qtpl:238
|
||||
writemustFormatLabels(qb422016, s)
|
||||
//line lib/promrelabel/debug.qtpl:296
|
||||
//line lib/promrelabel/debug.qtpl:238
|
||||
qs422016 := string(qb422016.B)
|
||||
//line lib/promrelabel/debug.qtpl:296
|
||||
//line lib/promrelabel/debug.qtpl:238
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line lib/promrelabel/debug.qtpl:296
|
||||
//line lib/promrelabel/debug.qtpl:238
|
||||
return qs422016
|
||||
//line lib/promrelabel/debug.qtpl:296
|
||||
//line lib/promrelabel/debug.qtpl:238
|
||||
}
|
||||
|
||||
@@ -10,10 +10,10 @@ import (
|
||||
|
||||
// TestWriteRelabelDebugSupportFormats verifies the relabeling debug input, rules and output.
|
||||
func TestWriteRelabelDebugSupportFormats(t *testing.T) {
|
||||
f := func(input, rules, expect string) {
|
||||
f := func(input, rule, expect string) {
|
||||
// execute
|
||||
outputWriter := bytes.NewBuffer(nil)
|
||||
writeRelabelDebug(outputWriter, false, "", input, rules, 0, 0, "json", nil)
|
||||
writeRelabelDebug(outputWriter, false, "", input, rule, "json", nil)
|
||||
|
||||
// the response is in JSON with HTML content, extract the `resultingLabels` in JSON and unescape it.
|
||||
resultingLabels := fastjson.GetString(outputWriter.Bytes(), `resultingLabels`)
|
||||
@@ -41,20 +41,4 @@ func TestWriteRelabelDebugSupportFormats(t *testing.T) {
|
||||
f(`{_name__="metric_name"`, ruleTestParsing, ``)
|
||||
f(`_name__="metric_name}"`, ruleTestParsing, ``)
|
||||
f(`metrics_name}"`, ruleTestParsing, ``)
|
||||
|
||||
// test multiple rules including remote writes
|
||||
// drop all labels and add one in URL relabeling
|
||||
rule1 := `
|
||||
- action: labeldrop
|
||||
regex: "drop_me_metrics_relabel"
|
||||
`
|
||||
rule2 := `
|
||||
- action: labeldrop
|
||||
regex: "drop_me_remote_write_relabel"
|
||||
`
|
||||
rule3 := `
|
||||
- target_label: add_me_url_relabel
|
||||
replacement: added
|
||||
`
|
||||
f(`{__name__="metric_name", drop_me_metrics_relabel="1", drop_me_remote_write_relabel="2"}`, rule1+rule2+rule3, `metric_name{add_me_url_relabel="added"}`)
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
@@ -54,18 +53,10 @@ func newClient(ctx context.Context, sw *ScrapeWork) (*client, error) {
|
||||
return nil
|
||||
}
|
||||
dialFunc := netutil.NewStatDialFunc("vm_promscrape")
|
||||
if sw.UnixSocket != "" {
|
||||
dialFunc = netutil.NewStatDialFuncWithDial("vm_promscrape", func(ctx context.Context, _, _ string) (net.Conn, error) {
|
||||
return netutil.Dialer.DialContext(ctx, "unix", sw.UnixSocket)
|
||||
})
|
||||
}
|
||||
proxyURL := sw.ProxyURL
|
||||
var proxyURLFunc func(*http.Request) (*url.URL, error)
|
||||
|
||||
if proxyURL != nil {
|
||||
if sw.UnixSocket != "" {
|
||||
return nil, fmt.Errorf("proxyURL: %q cannot be used for scraping unix_socket target: %q", proxyURL, sw.UnixSocket)
|
||||
}
|
||||
// case for direct http proxy connection.
|
||||
// must be used for http based scrape targets
|
||||
// since standard golang http.transport has special case for it
|
||||
|
||||
@@ -1325,9 +1325,6 @@ func (swc *scrapeWorkConfig) getScrapeWork(target string, extraLabels, metaLabel
|
||||
}
|
||||
streamParse = b
|
||||
}
|
||||
// Read __unix_socket__ option from __unix_socket__ label.
|
||||
unixSocket := labels.Get("__unix_socket__")
|
||||
|
||||
// Remove labels with "__" prefix according to https://www.robustperception.io/life-of-a-label/
|
||||
labels.RemoveLabelsWithDoubleUnderscorePrefix()
|
||||
// Add missing "instance" label according to https://www.robustperception.io/life-of-a-label
|
||||
@@ -1372,7 +1369,6 @@ func (swc *scrapeWorkConfig) getScrapeWork(target string, extraLabels, metaLabel
|
||||
LabelLimit: labelLimit,
|
||||
NoStaleMarkers: swc.noStaleMarkers,
|
||||
AuthToken: at,
|
||||
UnixSocket: unixSocket,
|
||||
|
||||
jobNameOriginal: swc.jobName,
|
||||
}
|
||||
|
||||
@@ -3,104 +3,34 @@ package promscrape
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promutil"
|
||||
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/httpserver"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promrelabel"
|
||||
)
|
||||
|
||||
// WriteMetricRelabelDebug serves requests to /metric-relabel-debug page.
|
||||
// remotewrite-related relabel configs could be empty as vmsingle doesn't provide remote write feature.
|
||||
func WriteMetricRelabelDebug(w http.ResponseWriter, r *http.Request, rwGlobalRelabelConfigs string, rwURLRelabelConfigss []string) {
|
||||
// WriteMetricRelabelDebug serves requests to /metric-relabel-debug page
|
||||
func WriteMetricRelabelDebug(w http.ResponseWriter, r *http.Request) {
|
||||
targetID := r.FormValue("id")
|
||||
metric := r.FormValue("metric")
|
||||
relabelConfigs := r.FormValue("relabel_configs")
|
||||
|
||||
// if set, it means user selected another URL from the dropdown and everything will be reloaded.
|
||||
reloadRWURLRelabelConfigs := r.FormValue("reload_url_relabel_configs")
|
||||
rwURLRelabelConfigsIdxStr := r.FormValue("url_relabel_configs_index")
|
||||
|
||||
format := r.FormValue("format")
|
||||
var err error
|
||||
|
||||
// if all per-URL config is empty, it means no per-URL rule is configured.
|
||||
// set it to 0 so the user do not see the options in debug page.
|
||||
rwURLRelabelConfigsLength := 0
|
||||
for _, urlRelabelConfig := range rwURLRelabelConfigss {
|
||||
if urlRelabelConfig != "" {
|
||||
rwURLRelabelConfigsLength = len(rwURLRelabelConfigss)
|
||||
break
|
||||
if metric == "" && relabelConfigs == "" && targetID != "" {
|
||||
pcs, labels, ok := getMetricRelabelContextByTargetID(targetID)
|
||||
if !ok {
|
||||
err = fmt.Errorf("cannot find target for id=%s", targetID)
|
||||
targetID = ""
|
||||
} else {
|
||||
metric = labels.String()
|
||||
relabelConfigs = pcs.String()
|
||||
}
|
||||
}
|
||||
|
||||
rwURLRelabelConfigsIdx, idxErr := strconv.Atoi(rwURLRelabelConfigsIdxStr)
|
||||
if idxErr != nil {
|
||||
rwURLRelabelConfigsIdx = -1
|
||||
}
|
||||
|
||||
// load the initial data with specific remote write URL index (default 0) in 2 cases:
|
||||
// - relabel config is empty. load scrape relabel (if targetID exist) + remote write related relabel (always).
|
||||
// - `reload` is set. load scrape relabel (if targetID exist) + reload remote write related relabel (by the URL index).
|
||||
init := metric == "" && relabelConfigs == "" && reloadRWURLRelabelConfigs == ""
|
||||
reload := reloadRWURLRelabelConfigs != ""
|
||||
if init || reload {
|
||||
// scrape related relabel labels & rules
|
||||
var (
|
||||
pcs = &promrelabel.ParsedConfigs{} // could be empty
|
||||
labels *promutil.Labels
|
||||
ok bool
|
||||
)
|
||||
if targetID != "" {
|
||||
pcs, labels, ok = getMetricRelabelContextByTargetID(targetID)
|
||||
if !ok {
|
||||
err = fmt.Errorf("cannot find target for id=%s", targetID)
|
||||
targetID = ""
|
||||
} else {
|
||||
metric = "up"
|
||||
metric += labels.String()
|
||||
}
|
||||
}
|
||||
|
||||
// general relabel rules (remote write)
|
||||
// set the per-URL remote write relabel according to index, any error will fall back the index to 0.
|
||||
rwURLRelabelConfigs := ""
|
||||
if len(rwURLRelabelConfigss) > 0 {
|
||||
// ignore the error if the input is invalid or exceed the length, and fallback to 0.
|
||||
if rwURLRelabelConfigsIdx < 0 || rwURLRelabelConfigsIdx >= len(rwURLRelabelConfigss) {
|
||||
rwURLRelabelConfigsIdx = 0
|
||||
}
|
||||
rwURLRelabelConfigs = rwURLRelabelConfigss[rwURLRelabelConfigsIdx]
|
||||
}
|
||||
|
||||
relabelConfigs = composeRelabelConfigs(pcs.String(), rwGlobalRelabelConfigs, rwURLRelabelConfigs, rwURLRelabelConfigsIdx)
|
||||
}
|
||||
|
||||
if format == "json" {
|
||||
httpserver.EnableCORS(w, r)
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
}
|
||||
promrelabel.WriteMetricRelabelDebug(w, targetID, metric, relabelConfigs, rwURLRelabelConfigsLength, rwURLRelabelConfigsIdx, format, err)
|
||||
}
|
||||
|
||||
func composeRelabelConfigs(relabelConfigs, rwGlobalRelabelConfigs, rwURLRelabelConfigs string, rwURLIdx int) string {
|
||||
if relabelConfigs != "" {
|
||||
relabelConfigs = "# -promscrape.config .scrape_configs[].metric_relabel_configs\n" + strings.TrimSpace(relabelConfigs) + "\n"
|
||||
}
|
||||
|
||||
if rwGlobalRelabelConfigs != "" {
|
||||
relabelConfigs += "\n# -remoteWrite.relabelConfig"
|
||||
relabelConfigs += "\n" + strings.TrimSpace(rwGlobalRelabelConfigs) + "\n"
|
||||
}
|
||||
|
||||
if rwURLRelabelConfigs != "" {
|
||||
relabelConfigs += fmt.Sprintf("\n# -remoteWrite.urlRelabelConfig=remote-write-url-%d", rwURLIdx)
|
||||
relabelConfigs += "\n" + strings.TrimSpace(rwURLRelabelConfigs) + "\n"
|
||||
}
|
||||
|
||||
return relabelConfigs
|
||||
promrelabel.WriteMetricRelabelDebug(w, targetID, metric, relabelConfigs, format, err)
|
||||
}
|
||||
|
||||
// WriteTargetRelabelDebug generates response for /target-relabel-debug page
|
||||
@@ -118,7 +48,7 @@ func WriteTargetRelabelDebug(w http.ResponseWriter, r *http.Request) {
|
||||
targetID = ""
|
||||
} else {
|
||||
metric = labels.labelsString()
|
||||
relabelConfigs = "# -promscrape.config .scrape_configs[].relabel_configs\n" + pcs.String()
|
||||
relabelConfigs = pcs.String()
|
||||
}
|
||||
}
|
||||
if format == "json" {
|
||||
|
||||
@@ -157,9 +157,6 @@ type ScrapeWork struct {
|
||||
// The Tenant Info
|
||||
AuthToken *auth.Token
|
||||
|
||||
// Optional path to Unix domain socket for scraping metrics over Unix domain socket.
|
||||
UnixSocket string
|
||||
|
||||
// The original 'job_name'
|
||||
jobNameOriginal string
|
||||
}
|
||||
@@ -177,12 +174,12 @@ func (sw *ScrapeWork) key() string {
|
||||
// Do not take into account OriginalLabels, since they can be changed with relabeling.
|
||||
// Do not take into account RelabelConfigs, since it is already applied to Labels.
|
||||
// Take into account JobNameOriginal in order to capture the case when the original job_name is changed via relabeling.
|
||||
key := fmt.Sprintf("JobNameOriginal=%s, ScrapeURL=%s, UnixSocket=%s, ScrapeInterval=%s, ScrapeTimeout=%s, HonorLabels=%v, "+
|
||||
key := fmt.Sprintf("JobNameOriginal=%s, ScrapeURL=%s, ScrapeInterval=%s, ScrapeTimeout=%s, HonorLabels=%v, "+
|
||||
"HonorTimestamps=%v, DenyRedirects=%v, Labels=%s, ExternalLabels=%s, MaxScrapeSize=%d, "+
|
||||
"ProxyURL=%s, ProxyAuthConfig=%s, AuthConfig=%s, MetricRelabelConfigs=%q, "+
|
||||
"SampleLimit=%d, DisableCompression=%v, DisableKeepAlive=%v, StreamParse=%v, "+
|
||||
"ScrapeAlignInterval=%s, ScrapeOffset=%s, SeriesLimit=%d, LabelLimit=%d, NoStaleMarkers=%v",
|
||||
sw.jobNameOriginal, sw.ScrapeURL, sw.UnixSocket, sw.ScrapeInterval, sw.ScrapeTimeout, sw.HonorLabels,
|
||||
sw.jobNameOriginal, sw.ScrapeURL, sw.ScrapeInterval, sw.ScrapeTimeout, sw.HonorLabels,
|
||||
sw.HonorTimestamps, sw.DenyRedirects, sw.Labels.String(), sw.ExternalLabels.String(), sw.MaxScrapeSize,
|
||||
sw.ProxyURL.String(), sw.ProxyAuthConfig.String(), sw.AuthConfig.String(), sw.MetricRelabelConfigs.String(),
|
||||
sw.SampleLimit, sw.DisableCompression, sw.DisableKeepAlive, sw.StreamParse,
|
||||
|
||||
@@ -28,9 +28,9 @@ func (rs *Rows) Reset() {
|
||||
rs.tu.reset()
|
||||
}
|
||||
|
||||
// Unmarshal unmarshals json line protocol from s.
|
||||
// Unmarshal unmarshals influx line protocol rows from s.
|
||||
//
|
||||
// See https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/#json-line-format
|
||||
// See https://docs.influxdata.com/influxdb/v1.7/write_protocols/line_protocol_tutorial/
|
||||
//
|
||||
// s shouldn't be modified when rs is in use.
|
||||
func (rs *Rows) Unmarshal(s string) {
|
||||
|
||||
@@ -485,65 +485,78 @@ func TestIndexDBOpenClose(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestIndexDB(t *testing.T) {
|
||||
defer testRemoveAll(t)
|
||||
const metricGroups = 10
|
||||
timestamp := time.Now().UnixMilli()
|
||||
|
||||
f := func(t *testing.T, concurrency int, disablePerDayIndex bool) {
|
||||
const metricGroups = 10
|
||||
now := time.Now().UTC()
|
||||
timestamp := now.UnixMilli()
|
||||
date := uint64(timestamp / msecPerDay)
|
||||
searchTR := TimeRange{
|
||||
MinTimestamp: time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.UTC).UnixMilli(),
|
||||
MaxTimestamp: time.Date(now.Year(), now.Month(), now.Day(), 23, 59, 59, 999_999_999, time.UTC).UnixMilli(),
|
||||
}
|
||||
if disablePerDayIndex {
|
||||
searchTR = globalIndexTimeRange
|
||||
}
|
||||
|
||||
s := MustOpenStorage(t.Name(), OpenOptions{
|
||||
DisablePerDayIndex: disablePerDayIndex,
|
||||
})
|
||||
defer s.MustClose()
|
||||
t.Run("serial", func(t *testing.T) {
|
||||
const path = "TestIndexDB-serial"
|
||||
s := MustOpenStorage(path, OpenOptions{})
|
||||
ptw := s.tb.MustGetPartition(timestamp)
|
||||
db := ptw.pt.idb
|
||||
mns, tsids, err := testIndexDBGetOrCreateTSIDByName(db, metricGroups, timestamp)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %s", err)
|
||||
}
|
||||
if err := testIndexDBCheckTSIDByName(db, mns, tsids, timestamp, false); err != nil {
|
||||
t.Fatalf("unexpected error: %s", err)
|
||||
}
|
||||
|
||||
// Re-open the storage and verify it works as expected.
|
||||
s.tb.PutPartition(ptw)
|
||||
s.MustClose()
|
||||
s = MustOpenStorage(path, OpenOptions{})
|
||||
|
||||
ptw = s.tb.MustGetPartition(timestamp)
|
||||
db = ptw.pt.idb
|
||||
if err := testIndexDBCheckTSIDByName(db, mns, tsids, timestamp, false); err != nil {
|
||||
t.Fatalf("unexpected error: %s", err)
|
||||
}
|
||||
|
||||
s.tb.PutPartition(ptw)
|
||||
s.MustClose()
|
||||
fs.MustRemoveDir(path)
|
||||
})
|
||||
|
||||
t.Run("concurrent", func(t *testing.T) {
|
||||
const path = "TestIndexDB-concurrent"
|
||||
s := MustOpenStorage(path, OpenOptions{})
|
||||
ptw := s.tb.MustGetPartition(timestamp)
|
||||
defer s.tb.PutPartition(ptw)
|
||||
db := ptw.pt.idb
|
||||
|
||||
var wg sync.WaitGroup
|
||||
errs := make([]error, concurrency)
|
||||
isConcurrent := concurrency > 1
|
||||
for i := range concurrency {
|
||||
wg.Go(func() {
|
||||
mns, tsids, err := testIndexDBGetOrCreateTSIDByName(db, metricGroups, date)
|
||||
ch := make(chan error, 3)
|
||||
for range cap(ch) {
|
||||
go func() {
|
||||
mns, tsid, err := testIndexDBGetOrCreateTSIDByName(db, metricGroups, timestamp)
|
||||
if err != nil {
|
||||
errs[i] = fmt.Errorf("testIndexDBGetOrCreateTSIDByName failed unexpectedly: %w", err)
|
||||
ch <- err
|
||||
return
|
||||
}
|
||||
if err := testIndexDBCheckTSIDByName(db, mns, tsids, date, searchTR, isConcurrent); err != nil {
|
||||
errs[i] = fmt.Errorf("testIndexDBCheckTSIDByName failed unexpectedly: %w", err)
|
||||
if err := testIndexDBCheckTSIDByName(db, mns, tsid, timestamp, true); err != nil {
|
||||
ch <- err
|
||||
return
|
||||
}
|
||||
})
|
||||
ch <- nil
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
for i, err := range errs {
|
||||
if err != nil {
|
||||
t.Errorf("[worker %d] %s", i, err)
|
||||
deadlineCh := time.After(30 * time.Second)
|
||||
for range cap(ch) {
|
||||
select {
|
||||
case err := <-ch:
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %s", err)
|
||||
}
|
||||
case <-deadlineCh:
|
||||
t.Fatalf("timeout")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, concurrency := range []int{1, 4} {
|
||||
for _, disablePerDayIndex := range []bool{false, true} {
|
||||
name := fmt.Sprintf("concurrency=%d/disablePerDayIndex=%t", concurrency, disablePerDayIndex)
|
||||
t.Run(name, func(t *testing.T) {
|
||||
f(t, concurrency, disablePerDayIndex)
|
||||
// Repeat the same test on non-empty reopened storage.
|
||||
f(t, concurrency, disablePerDayIndex)
|
||||
})
|
||||
}
|
||||
}
|
||||
s.tb.PutPartition(ptw)
|
||||
s.MustClose()
|
||||
fs.MustRemoveDir(path)
|
||||
})
|
||||
}
|
||||
|
||||
func testIndexDBGetOrCreateTSIDByName(db *indexDB, metricGroups int, date uint64) ([]MetricName, []TSID, error) {
|
||||
func testIndexDBGetOrCreateTSIDByName(db *indexDB, metricGroups int, timestamp int64) ([]MetricName, []TSID, error) {
|
||||
r := rand.New(rand.NewSource(1))
|
||||
// Create tsids.
|
||||
var mns []MetricName
|
||||
@@ -551,6 +564,8 @@ func testIndexDBGetOrCreateTSIDByName(db *indexDB, metricGroups int, date uint64
|
||||
|
||||
is := db.getIndexSearch(noDeadline)
|
||||
|
||||
date := uint64(timestamp) / msecPerDay
|
||||
|
||||
var metricNameBuf []byte
|
||||
for i := range 401 {
|
||||
var mn MetricName
|
||||
@@ -587,7 +602,7 @@ func testIndexDBGetOrCreateTSIDByName(db *indexDB, metricGroups int, date uint64
|
||||
return mns, tsids, nil
|
||||
}
|
||||
|
||||
func testIndexDBCheckTSIDByName(db *indexDB, mns []MetricName, tsids []TSID, date uint64, tr TimeRange, isConcurrent bool) error {
|
||||
func testIndexDBCheckTSIDByName(db *indexDB, mns []MetricName, tsids []TSID, timestamp int64, isConcurrent bool) error {
|
||||
timeseriesCounters := make(map[uint64]bool)
|
||||
var tsidLocal TSID
|
||||
var metricNameCopy []byte
|
||||
@@ -603,7 +618,7 @@ func testIndexDBCheckTSIDByName(db *indexDB, mns []MetricName, tsids []TSID, dat
|
||||
metricName := mn.Marshal(nil)
|
||||
|
||||
is := db.getIndexSearch(noDeadline)
|
||||
if !is.getTSIDByMetricName(&tsidLocal, metricName, date) {
|
||||
if !is.getTSIDByMetricName(&tsidLocal, metricName, uint64(timestamp)/msecPerDay) {
|
||||
return fmt.Errorf("cannot obtain tsid #%d for mn %s", i, mn)
|
||||
}
|
||||
db.putIndexSearch(is)
|
||||
@@ -637,7 +652,7 @@ func testIndexDBCheckTSIDByName(db *indexDB, mns []MetricName, tsids []TSID, dat
|
||||
}
|
||||
|
||||
// Test SearchLabelValues
|
||||
lvs, err := db.SearchLabelValues(nil, "__name__", nil, tr, 1e5, 1e9, noDeadline)
|
||||
lvs, err := db.SearchLabelValues(nil, "__name__", nil, TimeRange{}, 1e5, 1e9, noDeadline)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error in SearchLabelValues(labelName=%q): %w", "__name__", err)
|
||||
}
|
||||
@@ -646,7 +661,7 @@ func testIndexDBCheckTSIDByName(db *indexDB, mns []MetricName, tsids []TSID, dat
|
||||
}
|
||||
for i := range mn.Tags {
|
||||
tag := &mn.Tags[i]
|
||||
lvs, err := db.SearchLabelValues(nil, string(tag.Key), nil, tr, 1e5, 1e9, noDeadline)
|
||||
lvs, err := db.SearchLabelValues(nil, string(tag.Key), nil, TimeRange{}, 1e5, 1e9, noDeadline)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error in SearchLabelValues(labelName=%q): %w", tag.Key, err)
|
||||
}
|
||||
@@ -657,10 +672,10 @@ func testIndexDBCheckTSIDByName(db *indexDB, mns []MetricName, tsids []TSID, dat
|
||||
}
|
||||
}
|
||||
|
||||
// Test SearchLabelNames (empty filter)
|
||||
lns, err := db.SearchLabelNames(nil, nil, tr, 1e5, 1e9, noDeadline)
|
||||
// Test SearchLabelNames (empty filters, global time range)
|
||||
lns, err := db.SearchLabelNames(nil, nil, TimeRange{}, 1e5, 1e9, noDeadline)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error in SearchLabelNames(empty filter): %w", err)
|
||||
return fmt.Errorf("error in SearchLabelNames(empty filter, global time range): %w", err)
|
||||
}
|
||||
if _, ok := lns["__name__"]; !ok {
|
||||
return fmt.Errorf("cannot find __name__ in %q", lns)
|
||||
@@ -685,6 +700,10 @@ func testIndexDBCheckTSIDByName(db *indexDB, mns []MetricName, tsids []TSID, dat
|
||||
}
|
||||
|
||||
// Try tag filters.
|
||||
tr := TimeRange{
|
||||
MinTimestamp: timestamp - msecPerDay,
|
||||
MaxTimestamp: timestamp + msecPerDay,
|
||||
}
|
||||
for i := range mns {
|
||||
mn := &mns[i]
|
||||
tsid := &tsids[i]
|
||||
|
||||
@@ -528,87 +528,113 @@ func TestStorageDeletePendingSeries(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestStorageDeleteSeries(t *testing.T) {
|
||||
defer testRemoveAll(t)
|
||||
path := "TestStorageDeleteSeries"
|
||||
s := MustOpenStorage(path, OpenOptions{})
|
||||
|
||||
for _, concurrency := range []int{1, 4} {
|
||||
for _, disablePerDayIndex := range []bool{false, true} {
|
||||
name := fmt.Sprintf("concurrency=%d/disablePerDayIndex=%t", concurrency, disablePerDayIndex)
|
||||
t.Run(name, func(t *testing.T) {
|
||||
testStorageDeleteSeries(t, concurrency, disablePerDayIndex)
|
||||
})
|
||||
}
|
||||
// Verify no label names exist
|
||||
lns, err := s.SearchLabelNames(nil, nil, TimeRange{}, 1e5, 1e9, noDeadline)
|
||||
if err != nil {
|
||||
t.Fatalf("error in SearchLabelNames() at the start: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func testStorageDeleteSeries(t *testing.T, concurrency int, disablePerDayIndex bool) {
|
||||
tr := TimeRange{
|
||||
MinTimestamp: time.Date(2026, 1, 15, 0, 0, 0, 0, time.UTC).UnixMilli(),
|
||||
MaxTimestamp: time.Date(2026, 1, 15, 23, 59, 59, 999_999_999, time.UTC).UnixMilli(),
|
||||
if len(lns) != 0 {
|
||||
t.Fatalf("found non-empty tag keys at the start: %q", lns)
|
||||
}
|
||||
|
||||
s := MustOpenStorage(t.Name(), OpenOptions{
|
||||
DisablePerDayIndex: disablePerDayIndex,
|
||||
})
|
||||
defer s.MustClose()
|
||||
|
||||
errs := make([]error, concurrency)
|
||||
var wg sync.WaitGroup
|
||||
for workerNum := range concurrency {
|
||||
wg.Go(func() {
|
||||
var err error
|
||||
for range 10 {
|
||||
err = testStorageDeleteSeriesForWorker(workerNum, s, tr)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
t.Run("serial", func(t *testing.T) {
|
||||
for i := range 3 {
|
||||
if err := testStorageDeleteSeries(s, 0); err != nil {
|
||||
t.Fatalf("unexpected error on iteration %d: %s", i, err)
|
||||
}
|
||||
errs[workerNum] = err
|
||||
})
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
for i, err := range errs {
|
||||
if err != nil {
|
||||
t.Fatalf("[worker %d] %s", i, err)
|
||||
// Re-open the storage in order to check how deleted metricIDs
|
||||
// are persisted.
|
||||
s.MustClose()
|
||||
s = MustOpenStorage(path, OpenOptions{})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("concurrent", func(t *testing.T) {
|
||||
ch := make(chan error, 3)
|
||||
for i := range cap(ch) {
|
||||
go func(workerNum int) {
|
||||
var err error
|
||||
for range 2 {
|
||||
err = testStorageDeleteSeries(s, workerNum)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
ch <- err
|
||||
}(i)
|
||||
}
|
||||
tt := time.NewTimer(30 * time.Second)
|
||||
for i := range cap(ch) {
|
||||
select {
|
||||
case err := <-ch:
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error on iteration %d: %s", i, err)
|
||||
}
|
||||
case <-tt.C:
|
||||
t.Fatalf("timeout on iteration %d", i)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// Verify no more tag keys exist
|
||||
lns, err = s.SearchLabelNames(nil, nil, TimeRange{}, 1e5, 1e9, noDeadline)
|
||||
if err != nil {
|
||||
t.Fatalf("error in SearchLabelNames after the test: %s", err)
|
||||
}
|
||||
if len(lns) != 0 {
|
||||
t.Fatalf("found non-empty tag keys after the test: %q", lns)
|
||||
}
|
||||
|
||||
s.MustClose()
|
||||
fs.MustRemoveDir(path)
|
||||
}
|
||||
|
||||
func testStorageDeleteSeriesForWorker(workerNum int, s *Storage, tr TimeRange) error {
|
||||
func testStorageDeleteSeries(s *Storage, workerNum int) error {
|
||||
rng := rand.New(rand.NewSource(1))
|
||||
const rowsPerMetric = 100
|
||||
const metricsCount = 30
|
||||
|
||||
workerTag := fmt.Appendf(nil, "workerTag_%d", workerNum)
|
||||
|
||||
lnsAll := make(map[string]bool)
|
||||
lnsAll["__name__"] = true
|
||||
for i := range metricsCount {
|
||||
mn := MetricName{
|
||||
MetricGroup: fmt.Appendf(nil, "metric_%d_%d", i, workerNum),
|
||||
Tags: []Tag{
|
||||
{[]byte("job"), fmt.Appendf(nil, "job_%d_%d", i, workerNum)},
|
||||
{[]byte("instance"), fmt.Appendf(nil, "instance_%d_%d", i, workerNum)},
|
||||
{workerTag, []byte("foobar")},
|
||||
},
|
||||
var mrs []MetricRow
|
||||
var mn MetricName
|
||||
job := fmt.Sprintf("job_%d_%d", i, workerNum)
|
||||
instance := fmt.Sprintf("instance_%d_%d", i, workerNum)
|
||||
mn.Tags = []Tag{
|
||||
{[]byte("job"), []byte(job)},
|
||||
{[]byte("instance"), []byte(instance)},
|
||||
{workerTag, []byte("foobar")},
|
||||
}
|
||||
for i := range mn.Tags {
|
||||
lnsAll[string(mn.Tags[i].Key)] = true
|
||||
}
|
||||
mn.MetricGroup = fmt.Appendf(nil, "metric_%d_%d", i, workerNum)
|
||||
metricNameRaw := mn.marshalRaw(nil)
|
||||
|
||||
var mrs []MetricRow
|
||||
for range rowsPerMetric {
|
||||
mrs = append(mrs, MetricRow{
|
||||
MetricNameRaw: mn.marshalRaw(nil),
|
||||
Timestamp: tr.MinTimestamp + rng.Int63n(tr.MaxTimestamp-tr.MinTimestamp),
|
||||
Value: rng.NormFloat64() * 1e6,
|
||||
})
|
||||
timestamp := rng.Int63n(1e10)
|
||||
value := rng.NormFloat64() * 1e6
|
||||
|
||||
mr := MetricRow{
|
||||
MetricNameRaw: metricNameRaw,
|
||||
Timestamp: timestamp,
|
||||
Value: value,
|
||||
}
|
||||
mrs = append(mrs, mr)
|
||||
}
|
||||
s.AddRows(mrs, defaultPrecisionBits)
|
||||
}
|
||||
s.DebugFlush()
|
||||
|
||||
// Verify tag values exist
|
||||
tvs, err := s.SearchLabelValues(nil, string(workerTag), nil, tr, 1e5, 1e9, noDeadline)
|
||||
tvs, err := s.SearchLabelValues(nil, string(workerTag), nil, TimeRange{}, 1e5, 1e9, noDeadline)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error in SearchLabelValues before metrics removal: %w", err)
|
||||
}
|
||||
@@ -617,7 +643,7 @@ func testStorageDeleteSeriesForWorker(workerNum int, s *Storage, tr TimeRange) e
|
||||
}
|
||||
|
||||
// Verify tag keys exist
|
||||
lns, err := s.SearchLabelNames(nil, nil, tr, 1e5, 1e9, noDeadline)
|
||||
lns, err := s.SearchLabelNames(nil, nil, TimeRange{}, 1e5, 1e9, noDeadline)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error in SearchLabelNames before metrics removal: %w", err)
|
||||
}
|
||||
@@ -625,18 +651,20 @@ func testStorageDeleteSeriesForWorker(workerNum int, s *Storage, tr TimeRange) e
|
||||
return fmt.Errorf("unexpected label names before metrics removal: %w", err)
|
||||
}
|
||||
|
||||
countMetricBlocks := func(tfs *TagFilters) (int, error) {
|
||||
var sr Search
|
||||
sr.Init(nil, s, []*TagFilters{tfs}, tr, 1e5, noDeadline)
|
||||
defer sr.MustClose()
|
||||
var sr Search
|
||||
tr := TimeRange{
|
||||
MinTimestamp: 0,
|
||||
MaxTimestamp: 2e10,
|
||||
}
|
||||
metricBlocksCount := func(tfs *TagFilters) int {
|
||||
// Verify the number of blocks
|
||||
n := 0
|
||||
sr.Init(nil, s, []*TagFilters{tfs}, tr, 1e5, noDeadline)
|
||||
for sr.NextMetricBlock() {
|
||||
n++
|
||||
}
|
||||
if err := sr.Error(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return n, nil
|
||||
sr.MustClose()
|
||||
return n
|
||||
}
|
||||
for i := range metricsCount {
|
||||
tfs := NewTagFilters()
|
||||
@@ -647,26 +675,17 @@ func testStorageDeleteSeriesForWorker(workerNum int, s *Storage, tr TimeRange) e
|
||||
if err := tfs.Add([]byte("job"), []byte(job), false, false); err != nil {
|
||||
return fmt.Errorf("cannot add job tag filter: %w", err)
|
||||
}
|
||||
n, err := countMetricBlocks(tfs)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to count metric blocks for tfs=%s: %w", tfs, err)
|
||||
}
|
||||
if n == 0 {
|
||||
if n := metricBlocksCount(tfs); n == 0 {
|
||||
return fmt.Errorf("expecting non-zero number of metric blocks for tfs=%s", tfs)
|
||||
}
|
||||
deletedCount, err := s.DeleteSeries(nil, []*TagFilters{tfs}, 1e9)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot delete metrics: %w", err)
|
||||
}
|
||||
s.DebugFlush()
|
||||
if deletedCount == 0 {
|
||||
return fmt.Errorf("expecting non-zero number of deleted metrics on iteration %d", i)
|
||||
}
|
||||
n, err = countMetricBlocks(tfs)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to count metric blocks for tfs=%s: %w", tfs, err)
|
||||
}
|
||||
if n != 0 {
|
||||
if n := metricBlocksCount(tfs); n != 0 {
|
||||
return fmt.Errorf("expecting zero metric blocks after DeleteSeries call for tfs=%s; got %d blocks", tfs, n)
|
||||
}
|
||||
|
||||
@@ -675,7 +694,6 @@ func testStorageDeleteSeriesForWorker(workerNum int, s *Storage, tr TimeRange) e
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot delete empty tfss: %w", err)
|
||||
}
|
||||
s.DebugFlush()
|
||||
if deletedCount != 0 {
|
||||
return fmt.Errorf("expecting zero deleted metrics for empty tfss; got %d", deletedCount)
|
||||
}
|
||||
@@ -686,14 +704,10 @@ func testStorageDeleteSeriesForWorker(workerNum int, s *Storage, tr TimeRange) e
|
||||
if err := tfs.Add(nil, fmt.Appendf(nil, "metric_.+_%d", workerNum), false, true); err != nil {
|
||||
return fmt.Errorf("cannot add regexp tag filter for worker metrics: %w", err)
|
||||
}
|
||||
n, err := countMetricBlocks(tfs)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to count metric blocks for tfs=%s: %w", tfs, err)
|
||||
}
|
||||
if n != 0 {
|
||||
if n := metricBlocksCount(tfs); n != 0 {
|
||||
return fmt.Errorf("expecting zero metric blocks after deleting all the metrics; got %d blocks", n)
|
||||
}
|
||||
tvs, err = s.SearchLabelValues(nil, string(workerTag), nil, tr, 1e5, 1e9, noDeadline)
|
||||
tvs, err = s.SearchLabelValues(nil, string(workerTag), nil, TimeRange{}, 1e5, 1e9, noDeadline)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error in SearchLabelValues after all the metrics are removed: %w", err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user