mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2026-05-25 04:27:38 +03:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7d7d17d192 | ||
|
|
0a8b4281e5 |
@@ -51,13 +51,20 @@ func RequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
lmp := cp.NewLogMessageProcessor("jsonline")
|
||||
streamName := fmt.Sprintf("remoteAddr=%s, requestURI=%q", httpserver.GetQuotedRemoteAddr(r), r.RequestURI)
|
||||
processStreamInternal(streamName, reader, cp.TimeField, cp.MsgFields, lmp)
|
||||
err = processStreamInternal(streamName, reader, cp.TimeField, cp.MsgFields, lmp)
|
||||
lmp.MustClose()
|
||||
|
||||
requestDuration.UpdateDuration(startTime)
|
||||
if err != nil {
|
||||
logger.Errorf("jsonline: %s", err)
|
||||
} else {
|
||||
// update requestDuration only for successfully parsed requests.
|
||||
// There is no need in updating requestDuration for request errors,
|
||||
// since their timings are usually much smaller than the timing for successful request parsing.
|
||||
requestDuration.UpdateDuration(startTime)
|
||||
}
|
||||
}
|
||||
|
||||
func processStreamInternal(streamName string, r io.Reader, timeField string, msgFields []string, lmp insertutils.LogMessageProcessor) {
|
||||
func processStreamInternal(streamName string, r io.Reader, timeField string, msgFields []string, lmp insertutils.LogMessageProcessor) error {
|
||||
wcr := writeconcurrencylimiter.GetReader(r)
|
||||
defer writeconcurrencylimiter.PutReader(wcr)
|
||||
|
||||
@@ -69,10 +76,10 @@ func processStreamInternal(streamName string, r io.Reader, timeField string, msg
|
||||
wcr.DecConcurrency()
|
||||
if err != nil {
|
||||
errorsTotal.Inc()
|
||||
logger.Warnf("jsonline: cannot read line #%d in /jsonline request: %s", n, err)
|
||||
return fmt.Errorf("cannot read line #%d in /jsonline request: %s", n, err)
|
||||
}
|
||||
if !ok {
|
||||
return
|
||||
return nil
|
||||
}
|
||||
n++
|
||||
}
|
||||
@@ -89,17 +96,16 @@ func readLine(lr *insertutils.LineReader, timeField string, msgFields []string,
|
||||
}
|
||||
|
||||
p := logstorage.GetJSONParser()
|
||||
defer logstorage.PutJSONParser(p)
|
||||
|
||||
if err := p.ParseLogMessage(line); err != nil {
|
||||
return true, fmt.Errorf("cannot parse json-encoded line: %w; line contents: %q", err, line)
|
||||
return false, fmt.Errorf("cannot parse json-encoded log entry: %w", err)
|
||||
}
|
||||
ts, err := insertutils.ExtractTimestampFromFields(timeField, p.Fields)
|
||||
if err != nil {
|
||||
return true, fmt.Errorf("cannot get timestamp from json-encoded line: %w; line contents: %q", err, line)
|
||||
return false, fmt.Errorf("cannot get timestamp: %w", err)
|
||||
}
|
||||
logstorage.RenameField(p.Fields, msgFields, "_msg")
|
||||
lmp.AddRow(ts, p.Fields, nil)
|
||||
logstorage.PutJSONParser(p)
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
@@ -7,14 +7,16 @@ import (
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/app/vlinsert/insertutils"
|
||||
)
|
||||
|
||||
func TestProcessStreamInternal(t *testing.T) {
|
||||
func TestProcessStreamInternal_Success(t *testing.T) {
|
||||
f := func(data, timeField, msgField string, timestampsExpected []int64, resultExpected string) {
|
||||
t.Helper()
|
||||
|
||||
msgFields := []string{msgField}
|
||||
tlp := &insertutils.TestLogMessageProcessor{}
|
||||
r := bytes.NewBufferString(data)
|
||||
processStreamInternal("test", r, timeField, msgFields, tlp)
|
||||
if err := processStreamInternal("test", r, timeField, msgFields, tlp); err != nil {
|
||||
t.Fatalf("unexpected error: %s", err)
|
||||
}
|
||||
|
||||
if err := tlp.Verify(timestampsExpected, resultExpected); err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -43,37 +45,22 @@ func TestProcessStreamInternal(t *testing.T) {
|
||||
resultExpected = `{"log.offset":"71770","log.file.path":"/var/log/auth.log","message":"foobar"}
|
||||
{"message":"baz"}`
|
||||
f(data, timeField, msgField, timestampsExpected, resultExpected)
|
||||
}
|
||||
|
||||
func TestProcessStreamInternal_Failure(t *testing.T) {
|
||||
f := func(data string) {
|
||||
t.Helper()
|
||||
|
||||
tlp := &insertutils.TestLogMessageProcessor{}
|
||||
r := bytes.NewBufferString(data)
|
||||
if err := processStreamInternal("test", r, "time", nil, tlp); err == nil {
|
||||
t.Fatalf("expecting non-nil error")
|
||||
}
|
||||
}
|
||||
|
||||
// invalid json
|
||||
data = "foobar"
|
||||
timeField = "@timestamp"
|
||||
msgField = "aaa"
|
||||
timestampsExpected = nil
|
||||
resultExpected = ``
|
||||
f(data, timeField, msgField, timestampsExpected, resultExpected)
|
||||
f("foobar")
|
||||
|
||||
// invalid timestamp field
|
||||
data = `{"time":"foobar"}`
|
||||
timeField = "time"
|
||||
msgField = "abc"
|
||||
timestampsExpected = nil
|
||||
resultExpected = ``
|
||||
f(data, timeField, msgField, timestampsExpected, resultExpected)
|
||||
|
||||
// invalid lines among valid lines
|
||||
data = `
|
||||
dsfodmasd
|
||||
|
||||
{"time":"2023-06-06T04:48:11.735Z","log":{"offset":71770,"file":{"path":"/var/log/auth.log"}},"message":"foobar"}
|
||||
invalid line
|
||||
{"time":"2023-06-06T04:48:12.735+01:00","message":"baz"}
|
||||
asbsdf
|
||||
|
||||
`
|
||||
timeField = "time"
|
||||
msgField = "message"
|
||||
timestampsExpected = []int64{1686026891735000000, 1686023292735000000}
|
||||
resultExpected = `{"log.offset":"71770","log.file.path":"/var/log/auth.log","_msg":"foobar"}
|
||||
{"_msg":"baz"}`
|
||||
f(data, timeField, msgField, timestampsExpected, resultExpected)
|
||||
f(`{"time":"foobar"}`)
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
VITE_APP_TYPE=victoriametrics
|
||||
FAST_REFRESH=false
|
||||
@@ -1 +0,0 @@
|
||||
VITE_APP_TYPE=victorialogs
|
||||
@@ -1 +0,0 @@
|
||||
VITE_APP_TYPE=vmanomaly
|
||||
48
app/vmui/packages/vmui/.eslintrc.js
Normal file
48
app/vmui/packages/vmui/.eslintrc.js
Normal file
@@ -0,0 +1,48 @@
|
||||
// eslint-disable-next-line no-undef
|
||||
module.exports = {
|
||||
"env": {
|
||||
"browser": true,
|
||||
"es2021": true
|
||||
},
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:react/recommended",
|
||||
"plugin:@typescript-eslint/recommended"
|
||||
],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"ecmaFeatures": { "jsx": true },
|
||||
"ecmaVersion": 12,
|
||||
"sourceType": "module"
|
||||
},
|
||||
"plugins": [
|
||||
"react",
|
||||
"@typescript-eslint"
|
||||
],
|
||||
"rules": {
|
||||
"@typescript-eslint/no-unused-vars": ["warn", { "varsIgnorePattern": "^_" }],
|
||||
"react/jsx-closing-bracket-location": [1, "line-aligned"],
|
||||
"react/jsx-max-props-per-line":[1, { "maximum": 1 }],
|
||||
"react/jsx-first-prop-new-line": [1, "multiline"],
|
||||
"object-curly-spacing": [2, "always"],
|
||||
"indent": ["error", 2, { "SwitchCase": 1 }],
|
||||
"linebreak-style": ["error", "unix"],
|
||||
"quotes": ["error", "double"],
|
||||
"semi": ["error", "always"],
|
||||
"react/prop-types": 0
|
||||
},
|
||||
"settings": {
|
||||
"react": {
|
||||
"pragma": "React", // Pragma to use, default to "React"
|
||||
"version": "detect"
|
||||
},
|
||||
"linkComponents": [
|
||||
// Components used as alternatives to <a> for linking, eg. <Link to={ url } />
|
||||
"Hyperlink",
|
||||
{
|
||||
"name": "Link", "linkAttribute": "to"
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
42
app/vmui/packages/vmui/config-overrides.js
Normal file
42
app/vmui/packages/vmui/config-overrides.js
Normal file
@@ -0,0 +1,42 @@
|
||||
/* eslint-disable */
|
||||
const { override, addExternalBabelPlugin, addWebpackAlias, addWebpackPlugin } = require("customize-cra");
|
||||
const webpack = require("webpack");
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
// This will replace the default check
|
||||
const pathIndexHTML = (() => {
|
||||
switch (process.env.REACT_APP_TYPE) {
|
||||
case 'logs':
|
||||
return 'src/html/victorialogs.html';
|
||||
case 'anomaly':
|
||||
return 'src/html/vmanomaly.html';
|
||||
default:
|
||||
return 'src/html/victoriametrics.html';
|
||||
}
|
||||
})();
|
||||
const fileContent = fs.readFileSync(path.resolve(__dirname, pathIndexHTML), 'utf8');
|
||||
fs.writeFileSync(path.resolve(__dirname, 'public/index.html'), fileContent);
|
||||
|
||||
module.exports = override(
|
||||
addExternalBabelPlugin("@babel/plugin-proposal-nullish-coalescing-operator"),
|
||||
addWebpackAlias({
|
||||
"react": "preact/compat",
|
||||
"react-dom/test-utils": "preact/test-utils",
|
||||
"react-dom": "preact/compat", // Must be below test-utils
|
||||
"react/jsx-runtime": "preact/jsx-runtime"
|
||||
}),
|
||||
addWebpackPlugin(
|
||||
new webpack.NormalModuleReplacementPlugin(
|
||||
/\.\/App/,
|
||||
function (resource) {
|
||||
if (process.env.REACT_APP_TYPE === "logs") {
|
||||
resource.request = "./AppLogs";
|
||||
}
|
||||
if (process.env.REACT_APP_TYPE === "anomaly") {
|
||||
resource.request = "./AppAnomaly";
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
);
|
||||
@@ -1,23 +0,0 @@
|
||||
import { readFile } from "fs/promises";
|
||||
import { IndexHtmlTransform } from "vite";
|
||||
|
||||
/**
|
||||
* Vite plugin to dynamically load index.html based on the current mode.
|
||||
* If a specific mode-based index file (e.g., index.victorialogs.html) exists, it is used.
|
||||
* Otherwise, the default index.html is loaded.
|
||||
*/
|
||||
export default function dynamicIndexHtmlPlugin({ mode }) {
|
||||
return {
|
||||
name: "vm-dynamic-index-html",
|
||||
transformIndexHtml: {
|
||||
order: "pre",
|
||||
handler: async () => {
|
||||
try {
|
||||
return await readFile(`./index.${mode}.html`, "utf8");
|
||||
} catch (error) {
|
||||
return await readFile("./index.html", "utf8");
|
||||
}
|
||||
}
|
||||
} as IndexHtmlTransform
|
||||
};
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
import react from "eslint-plugin-react";
|
||||
import typescriptEslint from "@typescript-eslint/eslint-plugin";
|
||||
import globals from "globals";
|
||||
import tsParser from "@typescript-eslint/parser";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import js from "@eslint/js";
|
||||
import { FlatCompat } from "@eslint/eslintrc";
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
const compat = new FlatCompat({
|
||||
baseDirectory: __dirname,
|
||||
recommendedConfig: js.configs.recommended,
|
||||
allConfig: js.configs.all
|
||||
});
|
||||
|
||||
export default [...compat.extends(
|
||||
"eslint:recommended",
|
||||
"plugin:react/recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
), {
|
||||
plugins: {
|
||||
react,
|
||||
"@typescript-eslint": typescriptEslint,
|
||||
},
|
||||
|
||||
languageOptions: {
|
||||
globals: {
|
||||
...globals.browser,
|
||||
},
|
||||
|
||||
parser: tsParser,
|
||||
ecmaVersion: 12,
|
||||
sourceType: "module",
|
||||
|
||||
parserOptions: {
|
||||
ecmaFeatures: {
|
||||
jsx: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
settings: {
|
||||
react: {
|
||||
pragma: "React",
|
||||
version: "detect",
|
||||
},
|
||||
|
||||
linkComponents: ["Hyperlink", {
|
||||
name: "Link",
|
||||
linkAttribute: "to",
|
||||
}],
|
||||
},
|
||||
|
||||
rules: {
|
||||
"@typescript-eslint/no-unused-expressions": ["error", {
|
||||
allowShortCircuit: true,
|
||||
allowTernary: true
|
||||
}],
|
||||
|
||||
"@typescript-eslint/no-unused-vars": ["warn", {
|
||||
"argsIgnorePattern": "^_",
|
||||
"caughtErrors": "none",
|
||||
"caughtErrorsIgnorePattern": "^_",
|
||||
"destructuredArrayIgnorePattern": "^_",
|
||||
"varsIgnorePattern": "^_",
|
||||
"ignoreRestSiblings": true
|
||||
}],
|
||||
|
||||
"react/jsx-closing-bracket-location": [1, "line-aligned"],
|
||||
|
||||
"react/jsx-max-props-per-line": [1, {
|
||||
maximum: 1,
|
||||
}],
|
||||
|
||||
"react/jsx-first-prop-new-line": [1, "multiline"],
|
||||
"object-curly-spacing": [2, "always"],
|
||||
|
||||
indent: ["error", 2, {
|
||||
SwitchCase: 1,
|
||||
}],
|
||||
|
||||
"linebreak-style": ["error", "unix"],
|
||||
quotes: ["error", "double"],
|
||||
semi: ["error", "always"],
|
||||
"react/prop-types": 0,
|
||||
|
||||
},
|
||||
}];
|
||||
20119
app/vmui/packages/vmui/package-lock.json
generated
20119
app/vmui/packages/vmui/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -3,40 +3,50 @@
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"homepage": "./",
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@types/lodash.debounce": "^4.0.9",
|
||||
"@types/lodash.get": "^4.4.9",
|
||||
"@types/qs": "^6.9.18",
|
||||
"@types/react": "^19.0.8",
|
||||
"@types/react-input-mask": "^3.0.6",
|
||||
"@types/lodash.throttle": "^4.1.9",
|
||||
"@types/node": "^22.5.4",
|
||||
"@types/qs": "^6.9.15",
|
||||
"@types/react-input-mask": "^3.0.5",
|
||||
"@types/react-router-dom": "^5.3.3",
|
||||
"@types/webpack-env": "^1.18.5",
|
||||
"classnames": "^2.5.1",
|
||||
"dayjs": "^1.11.13",
|
||||
"lodash.debounce": "^4.0.8",
|
||||
"lodash.get": "^4.4.2",
|
||||
"marked": "^15.0.6",
|
||||
"marked-emoji": "^1.4.3",
|
||||
"preact": "^10.25.4",
|
||||
"qs": "^6.14.0",
|
||||
"lodash.throttle": "^4.1.1",
|
||||
"marked": "^14.1.2",
|
||||
"marked-emoji": "^1.4.2",
|
||||
"preact": "^10.23.2",
|
||||
"qs": "^6.13.0",
|
||||
"react-input-mask": "^2.0.4",
|
||||
"react-router-dom": "^7.1.5",
|
||||
"uplot": "^1.6.31",
|
||||
"vite": "^6.0.11",
|
||||
"web-vitals": "^4.2.4"
|
||||
"react-router-dom": "^6.26.2",
|
||||
"sass": "^1.78.0",
|
||||
"source-map-explorer": "^2.5.3",
|
||||
"typescript": "~4.6.2",
|
||||
"uplot": "^1.6.30",
|
||||
"web-vitals": "^4.2.3"
|
||||
},
|
||||
"scripts": {
|
||||
"prestart": "npm run copy-metricsql-docs",
|
||||
"start": "vite",
|
||||
"start:logs": "vite --mode victorialogs",
|
||||
"start:anomaly": "vite --mode vmanomaly",
|
||||
"build": "vite build",
|
||||
"build:logs": "vite build --mode victorialogs",
|
||||
"build:anomaly": "vite build --mode vmanomaly",
|
||||
"lint": "eslint 'src/**/*.{ts,tsx}'",
|
||||
"lint:fix": "eslint 'src/**/*.{ts,tsx}' --fix",
|
||||
"copy-metricsql-docs": "cp ../../../../docs/MetricsQL.md src/assets/MetricsQL.md || true",
|
||||
"preview": "vite preview"
|
||||
"start": "react-app-rewired start",
|
||||
"start:logs": "cross-env REACT_APP_TYPE=logs npm run start",
|
||||
"start:anomaly": "cross-env REACT_APP_TYPE=anomaly npm run start",
|
||||
"build": "GENERATE_SOURCEMAP=false react-app-rewired build",
|
||||
"build:logs": "cross-env REACT_APP_TYPE=logs npm run build",
|
||||
"build:anomaly": "cross-env REACT_APP_TYPE=anomaly npm run build",
|
||||
"lint": "eslint src --ext tsx,ts",
|
||||
"lint:fix": "eslint src --ext tsx,ts --fix",
|
||||
"analyze": "source-map-explorer 'build/static/js/*.js'",
|
||||
"copy-metricsql-docs": "cp ../../../../docs/MetricsQL.md src/assets/MetricsQL.md || true"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
"react-app",
|
||||
"react-app/jest"
|
||||
]
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
@@ -51,24 +61,26 @@
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6",
|
||||
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.7",
|
||||
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
|
||||
"@eslint/eslintrc": "^3.2.0",
|
||||
"@eslint/js": "^9.19.0",
|
||||
"@preact/preset-vite": "^2.10.1",
|
||||
"@types/node": "^22.13.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.22.0",
|
||||
"@typescript-eslint/parser": "^8.22.0",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.15.0",
|
||||
"@typescript-eslint/parser": "^5.15.0",
|
||||
"cross-env": "^7.0.3",
|
||||
"eslint": "^9.19.0",
|
||||
"eslint-plugin-react": "^7.37.4",
|
||||
"globals": "^15.14.0",
|
||||
"http-proxy-middleware": "^3.0.3",
|
||||
"postcss": "^8.5.1",
|
||||
"rollup-plugin-visualizer": "^5.14.0",
|
||||
"sass": "^1.83.4",
|
||||
"sass-embedded": "^1.83.4",
|
||||
"typescript": "^5.7.3",
|
||||
"webpack": "^5.97.1"
|
||||
"customize-cra": "^1.0.0",
|
||||
"eslint": "^8.44.0",
|
||||
"eslint-config-react-app": "^7.0.1",
|
||||
"eslint-plugin-react": "^7.36.1",
|
||||
"http-proxy-middleware": "^3.0.2",
|
||||
"react-app-rewired": "^2.2.1",
|
||||
"webpack": "^5.94.0"
|
||||
},
|
||||
"overrides": {
|
||||
"react-app-rewired": {
|
||||
"nth-check": "^2.0.1"
|
||||
},
|
||||
"css-select": {
|
||||
"nth-check": "^2.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,4 +38,4 @@ const AppAnomaly: FC = () => {
|
||||
</>;
|
||||
};
|
||||
|
||||
export default AppAnomaly;
|
||||
export default AppAnomaly;
|
||||
@@ -1,6 +1,7 @@
|
||||
import React, { FC, useCallback, useEffect, useRef, useState, createPortal } from "preact/compat";
|
||||
import React, { FC, useCallback, useEffect, useRef, useState } from "preact/compat";
|
||||
import { MouseEvent as ReactMouseEvent } from "react";
|
||||
import useEventListener from "../../../hooks/useEventListener";
|
||||
import ReactDOM from "react-dom";
|
||||
import classNames from "classnames";
|
||||
import uPlot from "uplot";
|
||||
import Button from "../../Main/Button/Button";
|
||||
@@ -48,7 +49,7 @@ const ChartTooltip: FC<ChartTooltipProps> = ({
|
||||
onClose && onClose(id);
|
||||
};
|
||||
|
||||
const handleMouseDown = (e: ReactMouseEvent<HTMLButtonElement>) => {
|
||||
const handleMouseDown = (e: ReactMouseEvent) => {
|
||||
setMoved(true);
|
||||
setMoving(true);
|
||||
const { clientX, clientY } = e;
|
||||
@@ -106,7 +107,7 @@ const ChartTooltip: FC<ChartTooltipProps> = ({
|
||||
|
||||
if (!u) return null;
|
||||
|
||||
return createPortal((
|
||||
return ReactDOM.createPortal((
|
||||
<div
|
||||
className={classNames({
|
||||
"vm-chart-tooltip": true,
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
@use "src/styles/variables" as *;
|
||||
@use 'sass:color';
|
||||
|
||||
$color-bar: #33BB55;
|
||||
$color-bar-highest: #F79420;
|
||||
@@ -8,7 +7,7 @@ $color-bar-highest: #F79420;
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr;
|
||||
height: 100%;
|
||||
padding-bottom: calc($font-size-small / 2);
|
||||
padding-bottom: calc($font-size-small/2);
|
||||
overflow: hidden;
|
||||
|
||||
&-y-axis {
|
||||
@@ -55,19 +54,19 @@ $color-bar-highest: #F79420;
|
||||
flex-grow: 1;
|
||||
width: 100%;
|
||||
min-width: 1px;
|
||||
height: calc(100% - ($font-size-small * 4));
|
||||
height: calc(100% - ($font-size-small*4));
|
||||
background-color: $color-bar;
|
||||
transition: background-color 200ms ease-in;
|
||||
|
||||
&:hover {
|
||||
background-color: color.scale($color-bar, $lightness: 40%);
|
||||
background-color: lighten($color-bar, 10%);
|
||||
}
|
||||
|
||||
&:first-child {
|
||||
background-color: $color-bar-highest;
|
||||
|
||||
&:hover {
|
||||
background-color: color.scale($color-bar-highest, $lightness: 40%);
|
||||
background-color: lighten($color-bar-highest, 10%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,11 +12,14 @@ import Timezones from "./Timezones/Timezones";
|
||||
import ThemeControl from "../ThemeControl/ThemeControl";
|
||||
import useDeviceDetect from "../../../hooks/useDeviceDetect";
|
||||
import useBoolean from "../../../hooks/useBoolean";
|
||||
import { AppType } from "../../../types/appType";
|
||||
import SwitchMarkdownParsing from "../LogsSettings/MarkdownParsing/SwitchMarkdownParsing";
|
||||
import { APP_TYPE_LOGS } from "../../../constants/appType";
|
||||
|
||||
const title = "Settings";
|
||||
|
||||
const { REACT_APP_TYPE } = process.env;
|
||||
const isLogsApp = REACT_APP_TYPE === AppType.logs;
|
||||
|
||||
export interface ChildComponentHandle {
|
||||
handleApply: () => void;
|
||||
}
|
||||
@@ -45,21 +48,21 @@ const GlobalSettings: FC = () => {
|
||||
|
||||
const controls = [
|
||||
{
|
||||
show: !appModeEnable && !APP_TYPE_LOGS,
|
||||
show: !appModeEnable && !isLogsApp,
|
||||
component: <ServerConfigurator
|
||||
ref={serverSettingRef}
|
||||
onClose={handleClose}
|
||||
/>
|
||||
},
|
||||
{
|
||||
show: !APP_TYPE_LOGS,
|
||||
show: !isLogsApp,
|
||||
component: <LimitsConfigurator
|
||||
ref={limitsSettingRef}
|
||||
onClose={handleClose}
|
||||
/>
|
||||
},
|
||||
{
|
||||
show: APP_TYPE_LOGS,
|
||||
show: isLogsApp,
|
||||
component: <SwitchMarkdownParsing/>
|
||||
},
|
||||
{
|
||||
|
||||
@@ -36,7 +36,7 @@ export class QueryAutocompleteCache {
|
||||
put(key: QueryAutocompleteCacheItem, value: string[]) {
|
||||
if (this.map.size >= this.maxSize) {
|
||||
const firstKey = this.map.keys().next().value;
|
||||
firstKey && this.map.delete(firstKey);
|
||||
this.map.delete(firstKey);
|
||||
}
|
||||
this.map.set(JSON.stringify(key), value);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React, { FC, useEffect, useRef, useState } from "preact/compat";
|
||||
import { KeyboardEvent } from "react";
|
||||
import { ErrorTypes } from "../../../types";
|
||||
import TextField, { TextFieldKeyboardEvent } from "../../Main/TextField/TextField";
|
||||
import TextField from "../../Main/TextField/TextField";
|
||||
import "./style.scss";
|
||||
import { QueryStats } from "../../../api/types";
|
||||
import { partialWarning, seriesFetchedWarning } from "./warningText";
|
||||
@@ -80,7 +81,7 @@ const QueryEditor: FC<QueryEditorProps> = ({
|
||||
setCaretPositionInput([caretPosition, caretPosition]);
|
||||
};
|
||||
|
||||
const handleKeyDown = (e: TextFieldKeyboardEvent) => {
|
||||
const handleKeyDown = (e: KeyboardEvent) => {
|
||||
const { key, ctrlKey, metaKey, shiftKey } = e;
|
||||
|
||||
const value = (e.target as HTMLTextAreaElement).value || "";
|
||||
|
||||
@@ -137,7 +137,12 @@ const StepConfigurator: FC = () => {
|
||||
startIcon={<TimelineIcon/>}
|
||||
onClick={toggleOpenOptions}
|
||||
>
|
||||
STEP {customStep}
|
||||
<p>
|
||||
STEP
|
||||
<p className="vm-step-control__value">
|
||||
{customStep}
|
||||
</p>
|
||||
</p>
|
||||
</Button>
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
@@ -8,6 +8,11 @@
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
&__value {
|
||||
display: inline;
|
||||
margin-left: 3px;
|
||||
}
|
||||
|
||||
&-popper {
|
||||
display: grid;
|
||||
gap: $padding-small;
|
||||
|
||||
@@ -14,8 +14,8 @@ interface ButtonProps {
|
||||
disabled?: boolean
|
||||
children?: ReactNode
|
||||
className?: string
|
||||
onClick?: (e: ReactMouseEvent<HTMLButtonElement>) => void
|
||||
onMouseDown?: (e: ReactMouseEvent<HTMLButtonElement>) => void
|
||||
onClick?: (e: ReactMouseEvent<HTMLButtonElement, MouseEvent>) => void
|
||||
onMouseDown?: (e: ReactMouseEvent<HTMLButtonElement, MouseEvent>) => void
|
||||
}
|
||||
|
||||
const Button: FC<ButtonProps> = ({
|
||||
|
||||
@@ -46,7 +46,7 @@ const DateTimeInput: FC<DateTimeInputProps> = ({
|
||||
onChange(maskedValue);
|
||||
};
|
||||
|
||||
const handleKeyUp = (e: KeyboardEvent<HTMLInputElement>) => {
|
||||
const handleKeyUp = (e: KeyboardEvent) => {
|
||||
if (e.key === "Enter") {
|
||||
onChange(maskedValue);
|
||||
setAwaitChangeForEnter(true);
|
||||
|
||||
@@ -89,8 +89,7 @@ const TimePicker: FC<CalendarTimepickerProps>= ({ selectDate, onChangeTime, onCl
|
||||
};
|
||||
|
||||
const handleFocusInput = (unit: TimeUnits, e: FocusEvent<HTMLInputElement>) => {
|
||||
const target = e.target as HTMLInputElement;
|
||||
target && target.select();
|
||||
e.target.select();
|
||||
setActiveField(unit);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React, { FC, useCallback, useEffect, createPortal } from "preact/compat";
|
||||
import React, { FC, useCallback, useEffect } from "preact/compat";
|
||||
import ReactDOM from "react-dom";
|
||||
import { CloseIcon } from "../Icons";
|
||||
import Button from "../Button/Button";
|
||||
import { ReactNode, MouseEvent } from "react";
|
||||
@@ -57,7 +58,7 @@ const Modal: FC<ModalProps> = ({
|
||||
useEventListener("popstate", handlePopstate);
|
||||
useEventListener("keyup", handleKeyUp);
|
||||
|
||||
return createPortal((
|
||||
return ReactDOM.createPortal((
|
||||
<div
|
||||
className={classNames({
|
||||
"vm-modal": true,
|
||||
|
||||
@@ -1,15 +1,6 @@
|
||||
import React, {
|
||||
FC,
|
||||
MouseEvent as ReactMouseEvent,
|
||||
ReactNode,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
useCallback,
|
||||
createPortal
|
||||
} from "react";
|
||||
import React, { FC, MouseEvent as ReactMouseEvent, ReactNode, useEffect, useMemo, useRef, useState } from "react";
|
||||
import classNames from "classnames";
|
||||
import ReactDOM from "react-dom";
|
||||
import "./style.scss";
|
||||
import useClickOutside from "../../../hooks/useClickOutside";
|
||||
import useDeviceDetect from "../../../hooks/useDeviceDetect";
|
||||
@@ -17,6 +8,7 @@ import Button from "../Button/Button";
|
||||
import { CloseIcon } from "../Icons";
|
||||
import { useLocation, useNavigate } from "react-router-dom";
|
||||
import useEventListener from "../../../hooks/useEventListener";
|
||||
import { useCallback } from "preact/compat";
|
||||
|
||||
interface PopperProps {
|
||||
children: ReactNode
|
||||
@@ -127,7 +119,7 @@ const Popper: FC<PopperProps> = ({
|
||||
return position;
|
||||
}, [buttonRef, placement, isOpen, children, fullWidth]);
|
||||
|
||||
const handleClickClose = (e: ReactMouseEvent<HTMLButtonElement>) => {
|
||||
const handleClickClose = (e: ReactMouseEvent<HTMLButtonElement, MouseEvent>) => {
|
||||
e.stopPropagation();
|
||||
onClose();
|
||||
};
|
||||
@@ -164,7 +156,7 @@ const Popper: FC<PopperProps> = ({
|
||||
|
||||
return (
|
||||
<>
|
||||
{(isOpen || !popperSize.width) && createPortal((
|
||||
{(isOpen || !popperSize.width) && ReactDOM.createPortal((
|
||||
<div
|
||||
className={classNames({
|
||||
"vm-popper": true,
|
||||
|
||||
@@ -11,7 +11,7 @@ interface MultipleSelectedValueProps {
|
||||
const MultipleSelectedValue: FC<MultipleSelectedValueProps> = ({ values, onRemoveItem }) => {
|
||||
const { isMobile } = useDeviceDetect();
|
||||
|
||||
const createHandleClick = (value: string) => (e: MouseEvent<HTMLDivElement>) => {
|
||||
const createHandleClick = (value: string) => (e: MouseEvent) => {
|
||||
onRemoveItem(value);
|
||||
e.stopPropagation();
|
||||
};
|
||||
|
||||
@@ -92,7 +92,7 @@ const Select: FC<SelectProps> = ({
|
||||
setSearch((e.target as HTMLInputElement).value);
|
||||
};
|
||||
|
||||
const createHandleClick = (value: string) => (e: MouseEvent<HTMLDivElement>) => {
|
||||
const createHandleClick = (value: string) => (e: MouseEvent) => {
|
||||
handleSelected(value);
|
||||
e.stopPropagation();
|
||||
};
|
||||
|
||||
@@ -15,8 +15,6 @@ import useDeviceDetect from "../../../hooks/useDeviceDetect";
|
||||
import TextFieldMessage from "./TextFieldMessage";
|
||||
import "./style.scss";
|
||||
|
||||
export type TextFieldKeyboardEvent = KeyboardEvent<HTMLInputElement | HTMLTextAreaElement>;
|
||||
|
||||
interface TextFieldProps {
|
||||
label?: string,
|
||||
value?: string | number
|
||||
@@ -33,7 +31,7 @@ interface TextFieldProps {
|
||||
caretPosition?: [number, number]
|
||||
onChange?: (value: string) => void
|
||||
onEnter?: () => void
|
||||
onKeyDown?: (e: TextFieldKeyboardEvent) => void
|
||||
onKeyDown?: (e: KeyboardEvent) => void
|
||||
onFocus?: () => void
|
||||
onBlur?: () => void
|
||||
onChangeCaret?: (position: [number, number]) => void
|
||||
@@ -86,7 +84,7 @@ const TextField: FC<TextFieldProps> = ({
|
||||
updateCaretPosition(e.currentTarget);
|
||||
};
|
||||
|
||||
const handleKeyDown = (e: TextFieldKeyboardEvent) => {
|
||||
const handleKeyDown = (e: KeyboardEvent<HTMLInputElement | HTMLTextAreaElement>) => {
|
||||
onKeyDown && onKeyDown(e);
|
||||
const { key, ctrlKey, metaKey } = e;
|
||||
const isEnter = key === "Enter";
|
||||
@@ -97,7 +95,7 @@ const TextField: FC<TextFieldProps> = ({
|
||||
}
|
||||
};
|
||||
|
||||
const handleKeyUp = (e: TextFieldKeyboardEvent) => {
|
||||
const handleKeyUp = (e: KeyboardEvent<HTMLInputElement | HTMLTextAreaElement>) => {
|
||||
updateCaretPosition(e.currentTarget);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import React, { FC, useEffect, useMemo, useRef, useState, Fragment, createPortal } from "preact/compat";
|
||||
import React, { FC, useEffect, useMemo, useRef, useState, Fragment } from "preact/compat";
|
||||
import ReactDOM from "react-dom";
|
||||
import "./style.scss";
|
||||
import { ReactNode } from "react";
|
||||
import { ExoticComponent } from "react";
|
||||
import useDeviceDetect from "../../../hooks/useDeviceDetect";
|
||||
|
||||
interface TooltipProps {
|
||||
@@ -23,7 +25,7 @@ const Tooltip: FC<TooltipProps> = ({
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [popperSize, setPopperSize] = useState({ width: 0, height: 0 });
|
||||
|
||||
const buttonRef = useRef<ReactNode>(null);
|
||||
const buttonRef = useRef<ExoticComponent>(null);
|
||||
const popperRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const onScrollWindow = () => setIsOpen(false);
|
||||
@@ -118,7 +120,7 @@ const Tooltip: FC<TooltipProps> = ({
|
||||
{children}
|
||||
</Fragment>
|
||||
|
||||
{!isMobile && isOpen && createPortal((
|
||||
{!isMobile && isOpen && ReactDOM.createPortal((
|
||||
<div
|
||||
className="vm-tooltip"
|
||||
ref={popperRef}
|
||||
|
||||
@@ -8,8 +8,8 @@ import Switch from "../../Main/Switch/Switch";
|
||||
import { arrayEquals } from "../../../utils/array";
|
||||
import classNames from "classnames";
|
||||
import useBoolean from "../../../hooks/useBoolean";
|
||||
import TextField, { TextFieldKeyboardEvent } from "../../Main/TextField/TextField";
|
||||
import { useState } from "react";
|
||||
import TextField from "../../Main/TextField/TextField";
|
||||
import { KeyboardEvent, useState } from "react";
|
||||
import Modal from "../../Main/Modal/Modal";
|
||||
import { useSearchParams } from "react-router-dom";
|
||||
|
||||
@@ -96,7 +96,7 @@ const TableSettings: FC<TableSettingsProps> = ({
|
||||
setIndexFocusItem(-1);
|
||||
};
|
||||
|
||||
const handleKeyDown = (e: TextFieldKeyboardEvent) => {
|
||||
const handleKeyDown = (e: KeyboardEvent) => {
|
||||
const arrowUp = e.key === "ArrowUp";
|
||||
const arrowDown = e.key === "ArrowDown";
|
||||
const enter = e.key === "Enter";
|
||||
|
||||
@@ -6,8 +6,8 @@ const dateColumns = ["date", "timestamp", "time"];
|
||||
export function descendingComparator<T>(a: T, b: T, orderBy: keyof T) {
|
||||
const valueA = a[orderBy];
|
||||
const valueB = b[orderBy];
|
||||
const parsedValueA = dateColumns.includes(String(orderBy)) ? dayjs(`${valueA}`).unix() : valueA;
|
||||
const parsedValueB = dateColumns.includes(String(orderBy)) ? dayjs(`${valueB}`).unix() : valueB;
|
||||
const parsedValueA = dateColumns.includes(`${orderBy}`) ? dayjs(`${valueA}`).unix() : valueA;
|
||||
const parsedValueB = dateColumns.includes(`${orderBy}`) ? dayjs(`${valueB}`).unix() : valueB;
|
||||
if (parsedValueB < parsedValueA) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
export enum AppType {
|
||||
victoriametrics = "victoriametrics",
|
||||
victorialogs = "victorialogs",
|
||||
vmanomaly = "vmanomaly",
|
||||
}
|
||||
|
||||
export const APP_TYPE = import.meta.env.VITE_APP_TYPE;
|
||||
export const APP_TYPE_VM = APP_TYPE === AppType.victoriametrics;
|
||||
export const APP_TYPE_LOGS = APP_TYPE === AppType.victorialogs;
|
||||
export const APP_TYPE_ANOMALY = APP_TYPE === AppType.vmanomaly;
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { useAppDispatch } from "../state/common/StateContext";
|
||||
import { useEffect, useState } from "preact/compat";
|
||||
import { ErrorTypes } from "../types";
|
||||
import { APP_TYPE_VM } from "../constants/appType";
|
||||
|
||||
const useFetchFlags = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
@@ -11,7 +10,7 @@ const useFetchFlags = () => {
|
||||
|
||||
useEffect(() => {
|
||||
const fetchAppConfig = async () => {
|
||||
if (!APP_TYPE_VM) return;
|
||||
if (process.env.REACT_APP_TYPE) return;
|
||||
setError("");
|
||||
setIsLoading(true);
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ import { useTimeDispatch } from "../state/time/TimeStateContext";
|
||||
import { getFromStorage } from "../utils/storage";
|
||||
import dayjs from "dayjs";
|
||||
import { getBrowserTimezone } from "../utils/time";
|
||||
import { APP_TYPE_VM } from "../constants/appType";
|
||||
|
||||
const disabledDefaultTimezone = Boolean(getFromStorage("DISABLED_DEFAULT_TIMEZONE"));
|
||||
|
||||
@@ -29,7 +28,7 @@ const useFetchDefaultTimezone = () => {
|
||||
};
|
||||
|
||||
const fetchDefaultTimezone = async () => {
|
||||
if (!serverUrl || !APP_TYPE_VM) return;
|
||||
if (!serverUrl || process.env.REACT_APP_TYPE) return;
|
||||
setError("");
|
||||
setIsLoading(true);
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { useAppDispatch, useAppState } from "../state/common/StateContext";
|
||||
import { useEffect, useState } from "preact/compat";
|
||||
import { ErrorTypes } from "../types";
|
||||
import { APP_TYPE_VM } from "../constants/appType";
|
||||
|
||||
const useFetchFlags = () => {
|
||||
const { serverUrl } = useAppState();
|
||||
@@ -12,7 +11,7 @@ const useFetchFlags = () => {
|
||||
|
||||
useEffect(() => {
|
||||
const fetchFlags = async () => {
|
||||
if (!serverUrl || !APP_TYPE_VM) return;
|
||||
if (!serverUrl || process.env.REACT_APP_TYPE) return;
|
||||
setError("");
|
||||
setIsLoading(true);
|
||||
|
||||
|
||||
@@ -12,8 +12,8 @@ import { useCustomPanelState } from "../state/customPanel/CustomPanelStateContex
|
||||
import { isHistogramData } from "../utils/metric";
|
||||
import { useGraphState } from "../state/graph/GraphStateContext";
|
||||
import { getStepFromDuration } from "../utils/time";
|
||||
import { AppType } from "../types/appType";
|
||||
import { getQueryStringValue } from "../utils/query-string";
|
||||
import { APP_TYPE_ANOMALY } from "../constants/appType";
|
||||
|
||||
interface FetchQueryParams {
|
||||
predefinedQuery?: string[]
|
||||
@@ -49,6 +49,8 @@ interface FetchDataParams {
|
||||
hideQuery?: number[]
|
||||
}
|
||||
|
||||
const isAnomalyUI = AppType.anomaly === process.env.REACT_APP_TYPE;
|
||||
|
||||
export const useFetchQuery = ({
|
||||
predefinedQuery,
|
||||
visible,
|
||||
@@ -132,7 +134,7 @@ export const useFetchQuery = ({
|
||||
}
|
||||
|
||||
const preventChangeType = !!getQueryStringValue("display_mode", null);
|
||||
isHistogramResult = !APP_TYPE_ANOMALY && isDisplayChart && !preventChangeType && isHistogramData(resp.data.result);
|
||||
isHistogramResult = !isAnomalyUI && isDisplayChart && !preventChangeType && isHistogramData(resp.data.result);
|
||||
seriesLimit = isHistogramResult ? Infinity : defaultLimit;
|
||||
const freeTempSize = seriesLimit - tempData.length;
|
||||
resp.data.result.slice(0, freeTempSize).forEach((d: MetricBase) => {
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<link rel="icon" href="/favicon.svg" />
|
||||
<link rel="apple-touch-icon" href="/favicon.svg" />
|
||||
<link rel="mask-icon" href="/favicon.svg" color="#000000">
|
||||
<link rel="icon" href="%PUBLIC_URL%/favicon.svg" />
|
||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/favicon.svg" />
|
||||
<link rel="mask-icon" href="%PUBLIC_URL%/favicon.svg" color="#000000">
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=5"/>
|
||||
<meta name="theme-color" content="#000000"/>
|
||||
@@ -13,13 +13,13 @@
|
||||
manifest.json provides metadata used when your web app is installed on a
|
||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||
-->
|
||||
<link rel="manifest" href="/manifest.json"/>
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json"/>
|
||||
<!--
|
||||
Notice the use of in the tags above.
|
||||
Notice the use of %PUBLIC_URL% in the tags above.
|
||||
It will be replaced with the URL of the `public` folder during the build.
|
||||
Only files inside the `public` folder can be referenced from the HTML.
|
||||
|
||||
Unlike "/favicon.ico" or "favicon.ico", "/favicon.ico" will
|
||||
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
@@ -29,7 +29,7 @@
|
||||
<meta name="twitter:title" content="UI for VictoriaLogs">
|
||||
<meta name="twitter:site" content="@https://victoriametrics.com/products/victorialogs/">
|
||||
<meta name="twitter:description" content="Explore your log data with VictoriaLogs UI">
|
||||
<meta name="twitter:image" content="/preview.jpg">
|
||||
<meta name="twitter:image" content="%PUBLIC_URL%/preview.jpg">
|
||||
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:title" content="UI for VictoriaLogs">
|
||||
@@ -49,6 +49,5 @@
|
||||
To begin the development, run `npm start` or `yarn start`.
|
||||
To create a production bundle, use `npm run build` or `yarn build`.
|
||||
-->
|
||||
<script type="module" src="/src/index.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -2,9 +2,9 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<link rel="icon" href="/favicon.svg"/>
|
||||
<link rel="apple-touch-icon" href="/favicon.svg"/>
|
||||
<link rel="mask-icon" href="/favicon.svg" color="#000000">
|
||||
<link rel="icon" href="%PUBLIC_URL%/favicon.svg" />
|
||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/favicon.svg" />
|
||||
<link rel="mask-icon" href="%PUBLIC_URL%/favicon.svg" color="#000000">
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=5"/>
|
||||
<meta name="theme-color" content="#000000"/>
|
||||
@@ -13,24 +13,24 @@
|
||||
manifest.json provides metadata used when your web app is installed on a
|
||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||
-->
|
||||
<link rel="manifest" href="/manifest.json"/>
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json"/>
|
||||
<!--
|
||||
Notice the use of in the tags above.
|
||||
Notice the use of %PUBLIC_URL% in the tags above.
|
||||
It will be replaced with the URL of the `public` folder during the build.
|
||||
Only files inside the `public` folder can be referenced from the HTML.
|
||||
|
||||
Unlike "/favicon.ico" or "favicon.ico", "/favicon.ico" will
|
||||
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
<title>vmui</title>
|
||||
<script src="/dashboards/index.js" type="module"></script>
|
||||
<script src="%PUBLIC_URL%/dashboards/index.js" type="module"></script>
|
||||
|
||||
<meta name="twitter:card" content="summary">
|
||||
<meta name="twitter:title" content="UI for VictoriaMetrics">
|
||||
<meta name="twitter:site" content="@https://victoriametrics.com/">
|
||||
<meta name="twitter:description" content="Explore and troubleshoot your VictoriaMetrics data">
|
||||
<meta name="twitter:image" content="/preview.jpg">
|
||||
<meta name="twitter:image" content="%PUBLIC_URL%/preview.jpg">
|
||||
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:title" content="UI for VictoriaMetrics">
|
||||
@@ -50,6 +50,5 @@
|
||||
To begin the development, run `npm start` or `yarn start`.
|
||||
To create a production bundle, use `npm run build` or `yarn build`.
|
||||
-->
|
||||
<script type="module" src="/src/index.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -2,9 +2,9 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<link rel="icon" href="/favicon.svg" />
|
||||
<link rel="apple-touch-icon" href="/favicon.svg" />
|
||||
<link rel="mask-icon" href="/favicon.svg" color="#000000">
|
||||
<link rel="icon" href="%PUBLIC_URL%/favicon.svg" />
|
||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/favicon.svg" />
|
||||
<link rel="mask-icon" href="%PUBLIC_URL%/favicon.svg" color="#000000">
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=5"/>
|
||||
<meta name="theme-color" content="#000000"/>
|
||||
@@ -13,13 +13,13 @@
|
||||
manifest.json provides metadata used when your web app is installed on a
|
||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||
-->
|
||||
<link rel="manifest" href="/manifest.json"/>
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json"/>
|
||||
<!--
|
||||
Notice the use of in the tags above.
|
||||
Notice the use of %PUBLIC_URL% in the tags above.
|
||||
It will be replaced with the URL of the `public` folder during the build.
|
||||
Only files inside the `public` folder can be referenced from the HTML.
|
||||
|
||||
Unlike "/favicon.ico" or "favicon.ico", "/favicon.ico" will
|
||||
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
@@ -29,7 +29,7 @@
|
||||
<meta name="twitter:title" content="UI for VictoriaMetrics Anomaly Detection">
|
||||
<meta name="twitter:site" content="@https://victoriametrics.com/products/enterprise/anomaly-detection/">
|
||||
<meta name="twitter:description" content="Detect anomalies in your metrics with VictoriaMetrics Anomaly Detection UI">
|
||||
<meta name="twitter:image" content="/preview.jpg">
|
||||
<meta name="twitter:image" content="%PUBLIC_URL%/preview.jpg">
|
||||
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:title" content="UI for VictoriaMetrics Anomaly Detection">
|
||||
@@ -49,6 +49,5 @@
|
||||
To begin the development, run `npm start` or `yarn start`.
|
||||
To create a production bundle, use `npm run build` or `yarn build`.
|
||||
-->
|
||||
<script type="module" src="/src/index.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -3,23 +3,9 @@ import "./constants/dayjsPlugins";
|
||||
import App from "./App";
|
||||
import reportWebVitals from "./reportWebVitals";
|
||||
import "./styles/style.scss";
|
||||
import { APP_TYPE, AppType } from "./constants/appType";
|
||||
import AppLogs from "./AppLogs";
|
||||
import AppAnomaly from "./AppAnomaly";
|
||||
|
||||
const getAppComponent = () => {
|
||||
switch (APP_TYPE) {
|
||||
case AppType.victorialogs:
|
||||
return <AppLogs/>;
|
||||
case AppType.vmanomaly:
|
||||
return <AppAnomaly/>;
|
||||
default:
|
||||
return <App/>;
|
||||
}
|
||||
};
|
||||
|
||||
const root = document.getElementById("root");
|
||||
if (root) render(getAppComponent(), root);
|
||||
if (root) render(<App />, root);
|
||||
|
||||
|
||||
// If you want to start measuring performance in your app, pass a function
|
||||
|
||||
@@ -13,16 +13,19 @@ import HeaderControls, { ControlsProps } from "./HeaderControls/HeaderControls";
|
||||
import useDeviceDetect from "../../hooks/useDeviceDetect";
|
||||
import useWindowSize from "../../hooks/useWindowSize";
|
||||
import { ComponentType } from "react";
|
||||
import { APP_TYPE, AppType } from "../../constants/appType";
|
||||
import { AppType } from "../../types/appType";
|
||||
|
||||
export interface HeaderProps {
|
||||
controlsComponent: ComponentType<ControlsProps>
|
||||
}
|
||||
const { REACT_APP_TYPE } = process.env;
|
||||
const isCustomApp = REACT_APP_TYPE === AppType.logs || REACT_APP_TYPE === AppType.anomaly;
|
||||
|
||||
const Logo = () => {
|
||||
switch (APP_TYPE) {
|
||||
case AppType.victorialogs:
|
||||
switch (REACT_APP_TYPE) {
|
||||
case AppType.logs:
|
||||
return <LogoLogsIcon/>;
|
||||
case AppType.vmanomaly:
|
||||
case AppType.anomaly:
|
||||
return <LogoAnomalyIcon/>;
|
||||
default:
|
||||
return <LogoIcon/>;
|
||||
@@ -78,7 +81,10 @@ const Header: FC<HeaderProps> = ({ controlsComponent }) => {
|
||||
<>
|
||||
{!appModeEnable && (
|
||||
<div
|
||||
className="vm-header-logo"
|
||||
className={classNames({
|
||||
"vm-header-logo": true,
|
||||
"vm-header-logo_logs": isCustomApp
|
||||
})}
|
||||
onClick={onClickLogo}
|
||||
style={{ color }}
|
||||
>
|
||||
@@ -96,6 +102,7 @@ const Header: FC<HeaderProps> = ({ controlsComponent }) => {
|
||||
className={classNames({
|
||||
"vm-header-logo": true,
|
||||
"vm-header-logo_mobile": true,
|
||||
"vm-header-logo_logs": isCustomApp
|
||||
})}
|
||||
onClick={onClickLogo}
|
||||
style={{ color }}
|
||||
|
||||
@@ -8,13 +8,16 @@ import MenuBurger from "../../../components/Main/MenuBurger/MenuBurger";
|
||||
import useDeviceDetect from "../../../hooks/useDeviceDetect";
|
||||
import "./style.scss";
|
||||
import useBoolean from "../../../hooks/useBoolean";
|
||||
import { APP_TYPE_LOGS } from "../../../constants/appType";
|
||||
import { AppType } from "../../../types/appType";
|
||||
|
||||
interface SidebarHeaderProps {
|
||||
background: string
|
||||
color: string
|
||||
}
|
||||
|
||||
const { REACT_APP_TYPE } = process.env;
|
||||
const isLogsApp = REACT_APP_TYPE === AppType.logs;
|
||||
|
||||
const SidebarHeader: FC<SidebarHeaderProps> = ({
|
||||
background,
|
||||
color,
|
||||
@@ -61,7 +64,7 @@ const SidebarHeader: FC<SidebarHeaderProps> = ({
|
||||
/>
|
||||
</div>
|
||||
<div className="vm-header-sidebar-menu-settings">
|
||||
{!isMobile && !APP_TYPE_LOGS && <ShortcutKeys showTitle={true}/>}
|
||||
{!isMobile && !isLogsApp && <ShortcutKeys showTitle={true}/>}
|
||||
</div>
|
||||
</div>
|
||||
</div>;
|
||||
|
||||
@@ -47,14 +47,14 @@
|
||||
justify-content: flex-start;
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
max-width: 75px;
|
||||
min-width: 75px;
|
||||
max-width: 65px;
|
||||
min-width: 65px;
|
||||
margin-bottom: 2px;
|
||||
overflow: hidden;
|
||||
|
||||
svg {
|
||||
max-width: 75px;
|
||||
min-width: 75px;
|
||||
max-width: 65px;
|
||||
min-width: 65px;
|
||||
}
|
||||
|
||||
&_mobile {
|
||||
@@ -62,5 +62,10 @@
|
||||
min-width: 65px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
&_logs, &_logs svg {
|
||||
max-width: 75px;
|
||||
min-width: 75px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ const EnhancedTable: FC<TableProps> = ({
|
||||
const [orderBy, setOrderBy] = useState<keyof Data>(defaultSortColumn);
|
||||
|
||||
const handleRequestSort = (
|
||||
event: MouseEvent<HTMLTableCellElement>,
|
||||
event: MouseEvent<unknown>,
|
||||
property: keyof Data,
|
||||
) => {
|
||||
const isAsc = orderBy === property && order === "asc";
|
||||
|
||||
@@ -7,7 +7,7 @@ import Tooltip from "../../../components/Main/Tooltip/Tooltip";
|
||||
|
||||
export function EnhancedTableHead(props: EnhancedHeaderTableProps) {
|
||||
const { order, orderBy, onRequestSort, headerCells } = props;
|
||||
const createSortHandler = (property: keyof Data) => (event: MouseEvent<HTMLTableCellElement>) => {
|
||||
const createSortHandler = (property: keyof Data) => (event: MouseEvent<unknown>) => {
|
||||
onRequestSort(event, property);
|
||||
};
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ export interface HeadCell {
|
||||
}
|
||||
|
||||
export interface EnhancedHeaderTableProps {
|
||||
onRequestSort: (event: MouseEvent<HTMLTableCellElement>, property: keyof Data) => void;
|
||||
onRequestSort: (event: MouseEvent<unknown>, property: keyof Data) => void;
|
||||
order: Order;
|
||||
orderBy: string;
|
||||
rowCount: number;
|
||||
|
||||
@@ -3,11 +3,10 @@ import { useCustomPanelDispatch, useCustomPanelState } from "../../state/customP
|
||||
import { ChartIcon, CodeIcon, TableIcon } from "../../components/Main/Icons";
|
||||
import Tabs from "../../components/Main/Tabs/Tabs";
|
||||
import { DisplayType } from "../../types";
|
||||
import { ReactNode } from "react";
|
||||
|
||||
type DisplayTab = {
|
||||
value: DisplayType
|
||||
icon: ReactNode
|
||||
icon: JSX.Element
|
||||
label: string
|
||||
prometheusCode: number
|
||||
}
|
||||
|
||||
@@ -114,7 +114,7 @@ const QueryConfigurator: FC<QueryConfiguratorProps> = ({
|
||||
setStateQuery(prev => prev.filter((q, i) => i !== index));
|
||||
};
|
||||
|
||||
const handleToggleHideQuery = (e: ReactMouseEvent<HTMLButtonElement>, index: number) => {
|
||||
const handleToggleHideQuery = (e: ReactMouseEvent<HTMLButtonElement, MouseEvent>, index: number) => {
|
||||
const { ctrlKey, metaKey } = e;
|
||||
const ctrlMetaKey = ctrlKey || metaKey;
|
||||
|
||||
@@ -160,7 +160,7 @@ const QueryConfigurator: FC<QueryConfiguratorProps> = ({
|
||||
setHideQuery(prev => prev.includes(i) ? prev.filter(n => n !== i) : prev.map(n => n > i ? n - 1 : n));
|
||||
};
|
||||
|
||||
const createHandlerHideQuery = (i: number) => (e: ReactMouseEvent<HTMLButtonElement>) => {
|
||||
const createHandlerHideQuery = (i: number) => (e: ReactMouseEvent<HTMLButtonElement, MouseEvent>) => {
|
||||
handleToggleHideQuery(e, i);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
@use "src/styles/variables" as *;
|
||||
@use 'sass:color';
|
||||
|
||||
$font-size-logs: var(--font-size-logs, $font-size-small);
|
||||
|
||||
@@ -80,8 +79,8 @@ $font-size-logs: var(--font-size-logs, $font-size-small);
|
||||
&__pair {
|
||||
order: 0;
|
||||
padding: calc($padding-global / 2) $padding-global;
|
||||
background-color: color.scale($color-tropical-blue, $lightness: 60%);
|
||||
color: color.scale($color-tropical-blue, $lightness: -60%);
|
||||
background-color: lighten($color-tropical-blue, 6%);
|
||||
color: darken($color-dodger-blue, 20%);
|
||||
border-radius: $border-radius-medium;
|
||||
transition: background-color 0.3s ease-in, transform 0.1s ease-in, opacity 0.3s ease-in;
|
||||
white-space: nowrap;
|
||||
@@ -106,7 +105,7 @@ $font-size-logs: var(--font-size-logs, $font-size-small);
|
||||
}
|
||||
|
||||
&_dark {
|
||||
color: color.scale($color-dodger-blue, $lightness: 40%);
|
||||
color: lighten($color-dodger-blue, 20%);
|
||||
background-color: $color-background-body;
|
||||
opacity: 0.8;
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ const PredefinedDashboard: FC<PredefinedDashboardProps> = ({
|
||||
setPanelsWidth(width);
|
||||
}, [resize, sizeSection]);
|
||||
|
||||
const handleMouseDown = (e: ReactMouseEvent<HTMLButtonElement>, i: number) => {
|
||||
const handleMouseDown = (e: ReactMouseEvent<HTMLButtonElement, MouseEvent>, i: number) => {
|
||||
setResize({
|
||||
start: e.clientX,
|
||||
target: i,
|
||||
|
||||
@@ -3,7 +3,6 @@ import { DashboardSettings, ErrorTypes } from "../../../types";
|
||||
import { useAppState } from "../../../state/common/StateContext";
|
||||
import { useDashboardsDispatch } from "../../../state/dashboards/DashboardsStateContext";
|
||||
import { getAppModeEnable } from "../../../utils/app-mode";
|
||||
import { APP_TYPE_VM } from "../../../constants/appType";
|
||||
|
||||
const importModule = async (filename: string) => {
|
||||
const data = await fetch(`./dashboards/${filename}`);
|
||||
@@ -35,7 +34,7 @@ export const useFetchDashboards = (): {
|
||||
};
|
||||
|
||||
const fetchRemoteDashboards = async () => {
|
||||
if (!serverUrl || !APP_TYPE_VM) return;
|
||||
if (!serverUrl || process.env.REACT_APP_TYPE) return;
|
||||
setError("");
|
||||
setIsLoading(true);
|
||||
|
||||
|
||||
@@ -108,12 +108,10 @@ const QueryAnalyzer: FC = () => {
|
||||
};
|
||||
|
||||
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
|
||||
if (!e.target) return;
|
||||
const target = e.target as HTMLInputElement;
|
||||
setError("");
|
||||
const files = Array.from(target.files || []);
|
||||
const files = Array.from(e.target.files || []);
|
||||
handleReadFiles(files);
|
||||
target.value = "";
|
||||
e.target.value = "";
|
||||
};
|
||||
|
||||
const handleCloseError = () => {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { FC, useEffect, useMemo } from "react";
|
||||
import React, { FC, useEffect, useMemo, KeyboardEvent } from "react";
|
||||
import { useFetchTopQueries } from "./hooks/useFetchTopQueries";
|
||||
import Spinner from "../../components/Main/Spinner/Spinner";
|
||||
import TopQueryPanel from "./TopQueryPanel/TopQueryPanel";
|
||||
@@ -8,7 +8,7 @@ import dayjs from "dayjs";
|
||||
import { TopQueryStats } from "../../types";
|
||||
import Button from "../../components/Main/Button/Button";
|
||||
import { PlayIcon } from "../../components/Main/Icons";
|
||||
import TextField, { TextFieldKeyboardEvent } from "../../components/Main/TextField/TextField";
|
||||
import TextField from "../../components/Main/TextField/TextField";
|
||||
import Alert from "../../components/Main/Alert/Alert";
|
||||
import Tooltip from "../../components/Main/Tooltip/Tooltip";
|
||||
import "./style.scss";
|
||||
@@ -55,7 +55,7 @@ const TopQueries: FC = () => {
|
||||
setMaxLifetime(value);
|
||||
};
|
||||
|
||||
const onKeyDown = (e: TextFieldKeyboardEvent) => {
|
||||
const onKeyDown = (e: KeyboardEvent) => {
|
||||
if (e.key === "Enter") fetch();
|
||||
};
|
||||
|
||||
|
||||
@@ -56,12 +56,10 @@ const TracePage: FC = () => {
|
||||
};
|
||||
|
||||
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
|
||||
if (!e.target) return;
|
||||
const target = e.target as HTMLInputElement;
|
||||
setErrors([]);
|
||||
const files = Array.from(target.files || []);
|
||||
const files = Array.from(e.target.files || []);
|
||||
handleReadFiles(files);
|
||||
target.value = "";
|
||||
e.target.value = "";
|
||||
};
|
||||
|
||||
const handleTraceDelete = (trace: Trace) => {
|
||||
|
||||
1
app/vmui/packages/vmui/src/react-app-env.d.ts
vendored
Normal file
1
app/vmui/packages/vmui/src/react-app-env.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/// <reference types="react-scripts" />
|
||||
@@ -1,4 +1,4 @@
|
||||
import { APP_TYPE_LOGS } from "../constants/appType";
|
||||
import { AppType } from "../types/appType";
|
||||
|
||||
const router = {
|
||||
home: "/",
|
||||
@@ -34,12 +34,15 @@ export interface RouterOptions {
|
||||
header: RouterOptionsHeader
|
||||
}
|
||||
|
||||
const { REACT_APP_TYPE } = process.env;
|
||||
const isLogsApp = REACT_APP_TYPE === AppType.logs;
|
||||
|
||||
const routerOptionsDefault = {
|
||||
header: {
|
||||
tenant: true,
|
||||
stepControl: !APP_TYPE_LOGS,
|
||||
timeSelector: !APP_TYPE_LOGS,
|
||||
executionControls: !APP_TYPE_LOGS,
|
||||
stepControl: !isLogsApp,
|
||||
timeSelector: !isLogsApp,
|
||||
executionControls: !isLogsApp,
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -2,9 +2,11 @@ import { getAppModeEnable } from "../utils/app-mode";
|
||||
import { useDashboardsState } from "../state/dashboards/DashboardsStateContext";
|
||||
import { useAppState } from "../state/common/StateContext";
|
||||
import { useMemo } from "preact/compat";
|
||||
import { AppType } from "../types/appType";
|
||||
import { processNavigationItems } from "./utils";
|
||||
import { getAnomalyNavigation, getDefaultNavigation, getLogsNavigation } from "./navigation";
|
||||
import { APP_TYPE, AppType } from "../constants/appType";
|
||||
|
||||
const appType = process.env.REACT_APP_TYPE;
|
||||
|
||||
const useNavigationMenu = () => {
|
||||
const appModeEnable = getAppModeEnable();
|
||||
@@ -23,10 +25,10 @@ const useNavigationMenu = () => {
|
||||
|
||||
|
||||
const menu = useMemo(() => {
|
||||
switch (APP_TYPE) {
|
||||
case AppType.victorialogs:
|
||||
switch (appType) {
|
||||
case AppType.logs:
|
||||
return getLogsNavigation();
|
||||
case AppType.vmanomaly:
|
||||
case AppType.anomaly:
|
||||
return getAnomalyNavigation();
|
||||
default:
|
||||
return getDefaultNavigation(navigationConfig);
|
||||
|
||||
@@ -28,7 +28,7 @@ export function reducer(state: LogsState, action: LogsAction): LogsState {
|
||||
case "SET_AUTOCOMPLETE_CACHE": {
|
||||
if (state.autocompleteCache.size >= AUTOCOMPLETE_LIMITS.cacheLimit) {
|
||||
const firstKey = state.autocompleteCache.keys().next().value;
|
||||
firstKey && state.autocompleteCache.delete(firstKey);
|
||||
state.autocompleteCache.delete(firstKey);
|
||||
}
|
||||
state.autocompleteCache.set(action.payload.key, action.payload.value);
|
||||
|
||||
|
||||
4
app/vmui/packages/vmui/src/types/appType.ts
Normal file
4
app/vmui/packages/vmui/src/types/appType.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export enum AppType {
|
||||
logs = "logs",
|
||||
anomaly = "anomaly",
|
||||
}
|
||||
@@ -4,7 +4,7 @@ export const arrayEquals = (a: (string|number)[], b: (string|number)[]) => {
|
||||
|
||||
export function groupByMultipleKeys<T>(items: T[], keys: (keyof T)[]): { keys: string[], values: T[] }[] {
|
||||
const groups = items.reduce((result, item) => {
|
||||
const compositeKey = keys.map(key => `${String(key)}: ${item[key] || "-"}`).join("|");
|
||||
const compositeKey = keys.map(key => `${key}: ${item[key] || "-"}`).join("|");
|
||||
|
||||
(result[compositeKey] = result[compositeKey] || []).push(item);
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import React, { ComponentProps, FC, ReactNode } from "react";
|
||||
import React, { ComponentProps, FC } from "react";
|
||||
|
||||
type Props = { children: ReactNode };
|
||||
type Props = { children: JSX.Element };
|
||||
|
||||
export const combineComponents = (...components: FC<Props>[]): FC<Props> => {
|
||||
return components.reduce(
|
||||
(AccumulatedComponents, CurrentComponent) => {
|
||||
// eslint-disable-next-line react/display-name
|
||||
return ({ children }: ComponentProps<FC<Props>>): ReactNode => (
|
||||
return ({ children }: ComponentProps<FC<Props>>): JSX.Element => (
|
||||
<AccumulatedComponents>
|
||||
<CurrentComponent>{children}</CurrentComponent>
|
||||
</AccumulatedComponents>
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { getAppModeParams } from "./app-mode";
|
||||
import { replaceTenantId } from "./tenants";
|
||||
import { APP_TYPE, AppType } from "../constants/appType";
|
||||
import { AppType } from "../types/appType";
|
||||
import { getFromStorage } from "./storage";
|
||||
const { REACT_APP_TYPE } = process.env;
|
||||
|
||||
export const getDefaultServer = (tenantId?: string): string => {
|
||||
const { serverURL } = getAppModeParams();
|
||||
@@ -11,10 +12,10 @@ export const getDefaultServer = (tenantId?: string): string => {
|
||||
const defaultURL = window.location.href.replace(/\/(?:prometheus\/)?(?:graph|vmui)\/.*/, "/prometheus");
|
||||
const url = serverURL || storageURL || defaultURL;
|
||||
|
||||
switch (APP_TYPE) {
|
||||
case AppType.victorialogs:
|
||||
switch (REACT_APP_TYPE) {
|
||||
case AppType.logs:
|
||||
return logsURL;
|
||||
case AppType.vmanomaly:
|
||||
case AppType.anomaly:
|
||||
return storageURL || anomalyURL;
|
||||
default:
|
||||
return tenantId ? replaceTenantId(url, tenantId) : url;
|
||||
|
||||
@@ -3,7 +3,7 @@ import dayjs, { UnitTypeShort } from "dayjs";
|
||||
import { getQueryStringValue } from "./query-string";
|
||||
import { DATE_ISO_FORMAT } from "../constants/date";
|
||||
import timezones from "../constants/timezones";
|
||||
import { APP_TYPE_LOGS } from "../constants/appType";
|
||||
import { AppType } from "../types/appType";
|
||||
|
||||
const MAX_ITEMS_PER_CHART = window.innerWidth / 4;
|
||||
const MAX_ITEMS_PER_HISTOGRAM = window.innerWidth / 40;
|
||||
@@ -160,10 +160,11 @@ export const dateFromSeconds = (epochTimeInSeconds: number): Date => {
|
||||
const getYesterday = () => dayjs().tz().subtract(1, "day").endOf("day").toDate();
|
||||
const getToday = () => dayjs().tz().endOf("day").toDate();
|
||||
|
||||
const isLogsApp = process.env.REACT_APP_TYPE === AppType.logs;
|
||||
export const relativeTimeOptions: RelativeTimeOption[] = [
|
||||
{ title: "Last 5 minutes", duration: "5m", isDefault: APP_TYPE_LOGS },
|
||||
{ title: "Last 5 minutes", duration: "5m", isDefault: isLogsApp },
|
||||
{ title: "Last 15 minutes", duration: "15m" },
|
||||
{ title: "Last 30 minutes", duration: "30m", isDefault: !APP_TYPE_LOGS },
|
||||
{ title: "Last 30 minutes", duration: "30m", isDefault: !isLogsApp },
|
||||
{ title: "Last 1 hour", duration: "1h" },
|
||||
{ title: "Last 3 hours", duration: "3h" },
|
||||
{ title: "Last 6 hours", duration: "6h" },
|
||||
|
||||
2
app/vmui/packages/vmui/src/vite-env.d.ts
vendored
2
app/vmui/packages/vmui/src/vite-env.d.ts
vendored
@@ -1,2 +0,0 @@
|
||||
/// <reference types="vite/client" />
|
||||
/// <reference types="vite/types/importMeta.d.ts" />
|
||||
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"types": ["vite/client"],
|
||||
"target": "es5",
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
@@ -20,13 +19,7 @@
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx",
|
||||
"downlevelIteration": true,
|
||||
"paths": {
|
||||
"react": ["./node_modules/preact/compat/"],
|
||||
"react/jsx-runtime": ["./node_modules/preact/jsx-runtime"],
|
||||
"react-dom": ["./node_modules/preact/compat/"],
|
||||
"react-dom/*": ["./node_modules/preact/compat/*"]
|
||||
}
|
||||
"downlevelIteration": true
|
||||
},
|
||||
"include": [
|
||||
"src"
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
import * as path from "path";
|
||||
|
||||
import { defineConfig } from "vite";
|
||||
import preact from "@preact/preset-vite";
|
||||
import dynamicIndexHtmlPlugin from "./config/plugins/dynamicIndexHtml";
|
||||
|
||||
export default defineConfig(({ mode }) => {
|
||||
return {
|
||||
base: "",
|
||||
plugins: [
|
||||
preact(),
|
||||
dynamicIndexHtmlPlugin({ mode })
|
||||
],
|
||||
assetsInclude: ["**/*.md"],
|
||||
server: {
|
||||
open: true,
|
||||
port: 3000,
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
"src": path.resolve(__dirname, "src"),
|
||||
},
|
||||
},
|
||||
build: {
|
||||
outDir: "./build",
|
||||
rollupOptions: {
|
||||
output: {
|
||||
manualChunks(id) {
|
||||
if (id.includes("node_modules")) {
|
||||
return "vendor";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ services:
|
||||
# storing logs and serving read queries.
|
||||
victorialogs:
|
||||
container_name: victorialogs
|
||||
image: victoriametrics/victoria-logs:v1.9.0-victorialogs
|
||||
image: victoriametrics/victoria-logs:v1.8.0-victorialogs
|
||||
command:
|
||||
- "--storageDataPath=/vlogs"
|
||||
- "--httpListenAddr=:9428"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
services:
|
||||
# meta service will be ignored by compose
|
||||
.victorialogs:
|
||||
image: docker.io/victoriametrics/victoria-logs:v1.9.0-victorialogs
|
||||
image: docker.io/victoriametrics/victoria-logs:v1.8.0-victorialogs
|
||||
command:
|
||||
- -storageDataPath=/vlogs
|
||||
- -loggerFormat=json
|
||||
|
||||
@@ -3,7 +3,7 @@ version: "3"
|
||||
services:
|
||||
# Run `make package-victoria-logs` to build victoria-logs image
|
||||
vlogs:
|
||||
image: docker.io/victoriametrics/victoria-logs:v1.9.0-victorialogs
|
||||
image: docker.io/victoriametrics/victoria-logs:v1.8.0-victorialogs
|
||||
volumes:
|
||||
- vlogs:/vlogs
|
||||
ports:
|
||||
|
||||
@@ -16,21 +16,15 @@ according to [these docs](https://docs.victoriametrics.com/victorialogs/quicksta
|
||||
|
||||
## tip
|
||||
|
||||
## [v1.9.0](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.9.0-victorialogs)
|
||||
|
||||
Released at 2025-02-10
|
||||
|
||||
* FEATURE: [LogsQL](https://docs.victoriametrics.com/victorialogs/logsql/): improve performance for [`stats by (...) ...`](https://docs.victoriametrics.com/victorialogs/logsql/#stats-pipe) by up to 30% when it is applied to big number of `by (...)` groups.
|
||||
* FEATURE: [LogsQL](https://docs.victoriametrics.com/victorialogs/logsql/): improve performance for [`top` pipe](https://docs.victoriametrics.com/victorialogs/logsql/#top-pipe) by up to 30% when it is applied to big number of unique values.
|
||||
* FEATURE: [`stats` pipe](https://docs.victoriametrics.com/victorialogs/logsql/#stats-pipe): improve performance for [`count_uniq`](https://docs.victoriametrics.com/victorialogs/logsql/#count_uniq-stats) and [`count_uniq_hash`](https://docs.victoriametrics.com/victorialogs/logsql/#count_uniq_hash-stats) functions by up to 30% when they are applied to big number of unique values.
|
||||
* FEATURE: [`block_stats` pipe](https://docs.victoriametrics.com/victorialogs/logsql/#block_stats-pipe): return the path to the part where every data block is stored. The path to the part is returned in the `part_path` field. This allows investigating the distribution of data blocks among parts.
|
||||
* FEATURE: reduce VictoriaLogs startup time by multiple times when it opens a large datastore with big [retention](https://docs.victoriametrics.com/victorialogs/#retention).
|
||||
* FEATURE: [data ingestion](https://docs.victoriametrics.com/victorialogs/data-ingestion/): accept timestamps with microsecond and nanosecond precision at [`_time` field](https://docs.victoriametrics.com/victorialogs/keyconcepts/#time-field).
|
||||
* FEATURE: [JSON lines data ingestion](https://docs.victoriametrics.com/victorialogs/data-ingestion/#json-stream-api): continue parsing lines after encountering parse errors for some lines. Previously the input JSON lines' stream was closed after the first parse error.
|
||||
* FEATURE: [web UI](https://docs.victoriametrics.com/victorialogs/querying/#web-ui): add the `_msg` field to the list of fields for the group view, allowing users to select multiple fields, including `_msg`, for log display.
|
||||
|
||||
* BUGFIX: [LogsQL](https://docs.victoriametrics.com/victorialogs/logsql/): properly limit [`concurrency` query option](https://docs.victoriametrics.com/victorialogs/logsql/#query-options) for [`stats`](https://docs.victoriametrics.com/victorialogs/logsql/#stats-pipe), [`uniq`](https://docs.victoriametrics.com/victorialogs/logsql/#uniq-pipe) and [`top`](https://docs.victoriametrics.com/victorialogs/logsql/#top-pipe). This prevents from `runtime error: index out of range` panic. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/8201).
|
||||
* BUGFIX: [`sort` pipe](https://docs.victoriametrics.com/victorialogs/logsql/#sort-pipe): properly sort [RFC3339 timestamps](https://www.rfc-editor.org/rfc/rfc3339) with variable sub-second precision. Previosuly such timestamps were sorted using [natural sorting](https://en.wikipedia.org/wiki/Natural_sort_order), and this could lead to unexpected results. For example, `2025-02-21T10:20:30.9Z` was incorrectly considered smaller than `2025-02-21T10:20:30.012Z`, since the last one had higher decimal value after the last dot.
|
||||
* BUGFIX: [data ingestion](https://docs.victoriametrics.com/victorialogs/data-ingestion/): drop log entries with too long field names and log the dropped log entries with the `ignoring log entry with too long field name` message, so human operators could notice and fix the ingestion of incorrect logs ASAP. Previously too long field names were silently truncated to shorter values. This isn't what most users expect. See [why VictoriaLogs has a limit on the field name length](https://docs.victoriametrics.com/victorialogs/faq/#what-is-the-maximum-supported-field-name-length).
|
||||
* BUGFIX: [web UI](https://docs.victoriametrics.com/victorialogs/querying/#web-ui): fix transparency for bars in the hits bar chart to improve visibility. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/8152).
|
||||
* BUGFIX: [web UI](https://docs.victoriametrics.com/victorialogs/querying/#web-ui): fix `Group by field` dropdown menu not displaying any options in Group View settings. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/8153).
|
||||
|
||||
@@ -33,8 +33,8 @@ Just download archive for the needed Operating system and architecture, unpack i
|
||||
For example, the following commands download VictoriaLogs archive for Linux/amd64, unpack and run it:
|
||||
|
||||
```sh
|
||||
curl -L -O https://github.com/VictoriaMetrics/VictoriaMetrics/releases/download/v1.9.0-victorialogs/victoria-logs-linux-amd64-v1.9.0-victorialogs.tar.gz
|
||||
tar xzf victoria-logs-linux-amd64-v1.9.0-victorialogs.tar.gz
|
||||
curl -L -O https://github.com/VictoriaMetrics/VictoriaMetrics/releases/download/v1.8.0-victorialogs/victoria-logs-linux-amd64-v1.8.0-victorialogs.tar.gz
|
||||
tar xzf victoria-logs-linux-amd64-v1.8.0-victorialogs.tar.gz
|
||||
./victoria-logs-prod
|
||||
```
|
||||
|
||||
@@ -58,7 +58,7 @@ Here is the command to run VictoriaLogs in a Docker container:
|
||||
|
||||
```sh
|
||||
docker run --rm -it -p 9428:9428 -v ./victoria-logs-data:/victoria-logs-data \
|
||||
docker.io/victoriametrics/victoria-logs:v1.9.0-victorialogs
|
||||
docker.io/victoriametrics/victoria-logs:v1.8.0-victorialogs
|
||||
```
|
||||
|
||||
See also:
|
||||
|
||||
@@ -23,8 +23,8 @@ or from [docker images](https://hub.docker.com/r/victoriametrics/vlogscli/tags).
|
||||
### Running `vlogscli` from release binary
|
||||
|
||||
```sh
|
||||
curl -L -O https://github.com/VictoriaMetrics/VictoriaMetrics/releases/download/v1.9.0-victorialogs/vlogscli-linux-amd64-v1.9.0-victorialogs.tar.gz
|
||||
tar xzf vlogscli-linux-amd64-v1.9.0-victorialogs.tar.gz
|
||||
curl -L -O https://github.com/VictoriaMetrics/VictoriaMetrics/releases/download/v1.8.0-victorialogs/vlogscli-linux-amd64-v1.8.0-victorialogs.tar.gz
|
||||
tar xzf vlogscli-linux-amd64-v1.8.0-victorialogs.tar.gz
|
||||
./vlogscli-prod
|
||||
```
|
||||
|
||||
|
||||
@@ -20,9 +20,11 @@ See also [LTS releases](https://docs.victoriametrics.com/lts-releases/).
|
||||
|
||||
* SECURITY: upgrade Go builder from Go1.23.5 to Go1.23.6. See the list of issues addressed in [Go1.23.6](https://github.com/golang/go/issues?q=milestone%3AGo1.23.6+label%3ACherryPickApproved).
|
||||
|
||||
* BUGFIX: [vmalert](https://docs.victoriametrics.com/vmalert/): fix polluted alert messages when multiple Alertmanager instances are configured as notifiers with alert_relabel_configs.. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/8040), and thanks to @evkuzin for [the pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/8258).
|
||||
|
||||
## [v1.111.0](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.111.0)
|
||||
|
||||
Released at 2025-02-10
|
||||
Released at 2025-02-07
|
||||
|
||||
**Update note 1: [Single-node VictoriaMetrics](https://docs.victoriametrics.com/) and [vmstorage](https://docs.victoriametrics.com/victoriametrics/) stop exposing `vm_index_search_duration_seconds` histogram metric. This metric records time spent on search operations in the index. It was introduced in [v1.56.0](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.56.0). However, this metric was used neither in dashboards nor in alerting rules. It also has high cardinality because index search operations latency can differ by 3 orders of magnitude. Hence, dropping it as unused.**
|
||||
|
||||
@@ -38,33 +40,6 @@ Released at 2025-02-10
|
||||
* BUGFIX: [Single-node VictoriaMetrics](https://docs.victoriametrics.com/) and [vmselect](https://docs.victoriametrics.com/cluster-victoriametrics/): fix discrepancies when using `or` binary operator. See [this](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/7759) and [this](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/7640) issues for details.
|
||||
* BUGFIX: [vmsingle](https://docs.victoriametrics.com/single-server-victoriametrics/) and `vmstorage` in [VictoriaMetrics cluster](https://docs.victoriametrics.com/cluster-victoriametrics/): properly update number of unique series for [cardinality limiter](https://docs.victoriametrics.com/#cardinality-limiter) on ingestion. Previously, limit could undercount the real number of the ingested unique series.
|
||||
|
||||
## [v1.110.1](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.110.1)
|
||||
|
||||
Released at 2025-02-10
|
||||
|
||||
**v1.110.x is a line of [LTS releases](https://docs.victoriametrics.com/lts-releases/). It contains important up-to-date bugfixes for [VictoriaMetrics enterprise](https://docs.victoriametrics.com/enterprise.html).
|
||||
All these fixes are also included in [the latest community release](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/latest).
|
||||
The v1.110.x line will be supported for at least 12 months since [v1.110.0](https://docs.victoriametrics.com/changelog/#v11100) release**
|
||||
|
||||
* BUGFIX: [vmagent](https://docs.victoriametrics.com/vmagent/): properly perform graceful shutdown for kafka client.
|
||||
* BUGFIX: [vmagent](https://docs.victoriametrics.com/vmagent/): properly add message metadata headers for kafka remoteWrite target.
|
||||
* BUGFIX: [vmui](https://docs.victoriametrics.com/#vmui): improve clipboard error handling in tables and code snippets by showing detailed messages with possible reasons. See [this pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/7778).
|
||||
* BUGFIX: [vmsingle](https://docs.victoriametrics.com/single-server-victoriametrics/) and `vmstorage` in [VictoriaMetrics cluster](https://docs.victoriametrics.com/cluster-victoriametrics/): properly update number of unique series for [cardinality limiter](https://docs.victoriametrics.com/#cardinality-limiter) on ingestion. Previously, limit could undercount the real number of the ingested unique series.
|
||||
* BUGFIX: [Single-node VictoriaMetrics](https://docs.victoriametrics.com/) and [vmselect](https://docs.victoriametrics.com/cluster-victoriametrics/): fix discrepancies when using `or` binary operator. See [this](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/7759) and [this](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/7640) issues for details.
|
||||
* BUGFIX: [vmui](https://docs.victoriametrics.com/#vmui) for [VictoriaMetrics enterprise](https://docs.victoriametrics.com/enterprise.html) components: properly display enterprise features when the enterprise version is used.
|
||||
|
||||
## [v1.102.13](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.102.13)
|
||||
|
||||
Released at 2025-02-10
|
||||
|
||||
**v1.102.x is a line of [LTS releases](https://docs.victoriametrics.com/lts-releases/). It contains important up-to-date bugfixes for [VictoriaMetrics enterprise](https://docs.victoriametrics.com/enterprise.html).
|
||||
All these fixes are also included in [the latest community release](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/latest).
|
||||
The v1.102.x line will be supported for at least 12 months since [v1.102.0](https://docs.victoriametrics.com/changelog/#v11020) release**
|
||||
|
||||
* BUGFIX: [export API](https://docs.victoriametrics.com/#how-to-export-time-series): cancel export process on client connection close. Previously client connection close was ignored and VictoriaMetrics started to hog CPU by exporting metrics to nowhere until it export all of them.
|
||||
* BUGFIX: [Single-node VictoriaMetrics](https://docs.victoriametrics.com/) and [vmselect](https://docs.victoriametrics.com/cluster-victoriametrics/): fix discrepancies when using `or` binary operator. See [this](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/7759) and [this](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/7640) issues for details.
|
||||
* BUGFIX: [vmsingle](https://docs.victoriametrics.com/single-server-victoriametrics/) and `vmstorage` in [VictoriaMetrics cluster](https://docs.victoriametrics.com/cluster-victoriametrics/): properly update number of unique series for [cardinality limiter](https://docs.victoriametrics.com/#cardinality-limiter) on ingestion. Previously, limit could undercount the real number of the ingested unique series.
|
||||
|
||||
## [v1.102.12](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.102.12)
|
||||
|
||||
Released at 2025-01-28
|
||||
|
||||
@@ -1,25 +1,6 @@
|
||||
## Next release
|
||||
|
||||
- TODO
|
||||
|
||||
## 0.8.16
|
||||
|
||||
**Release date:** 07 Feb 2025
|
||||
|
||||
 
|
||||
|
||||
- add `.Values.server.vmServiceScrape` for [VMOperator](https://docs.victoriametrics.com/operator/) [VMServiceScrape](https://docs.victoriametrics.com/operator/api/#vmservicescrape) resource
|
||||
- update victorialogs version to [v1.8.0](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.8.0-victorialogs)
|
||||
|
||||
## 0.8.15
|
||||
|
||||
**Release date:** 06 Feb 2025
|
||||
|
||||
 
|
||||
|
||||
- added ability to override default headless service .Values.server.service.clusterIP with empty value
|
||||
- vector chart 0.37.x -> 0.40.x
|
||||
- updated common dependency 0.0.37 -> 0.0.39
|
||||
|
||||
## 0.8.14
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
@@ -910,7 +910,7 @@ readOnlyRootFilesystem: true
|
||||
<td>server.service.clusterIP</td>
|
||||
<td>string</td>
|
||||
<td><pre class="helm-vars-default-value language-yaml" lang="">
|
||||
<code class="language-yaml">None
|
||||
<code class="language-yaml">""
|
||||
</code>
|
||||
</pre>
|
||||
</td>
|
||||
@@ -1179,69 +1179,6 @@ readOnlyRootFilesystem: true
|
||||
</pre>
|
||||
</td>
|
||||
<td><p>Pod topologySpreadConstraints</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>server.vmServiceScrape.annotations</td>
|
||||
<td>object</td>
|
||||
<td><pre class="helm-vars-default-value language-yaml" lang="plaintext">
|
||||
<code class="language-yaml">{}
|
||||
</code>
|
||||
</pre>
|
||||
</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>server.vmServiceScrape.enabled</td>
|
||||
<td>bool</td>
|
||||
<td><pre class="helm-vars-default-value language-yaml" lang="">
|
||||
<code class="language-yaml">false
|
||||
</code>
|
||||
</pre>
|
||||
</td>
|
||||
<td><p>Enable deployment of VMServiceScrape for server component. This is Victoria Metrics operator object</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>server.vmServiceScrape.extraLabels</td>
|
||||
<td>object</td>
|
||||
<td><pre class="helm-vars-default-value language-yaml" lang="plaintext">
|
||||
<code class="language-yaml">{}
|
||||
</code>
|
||||
</pre>
|
||||
</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>server.vmServiceScrape.metricRelabelings</td>
|
||||
<td>list</td>
|
||||
<td><pre class="helm-vars-default-value language-yaml" lang="plaintext">
|
||||
<code class="language-yaml">[]
|
||||
</code>
|
||||
</pre>
|
||||
</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>server.vmServiceScrape.relabelings</td>
|
||||
<td>list</td>
|
||||
<td><pre class="helm-vars-default-value language-yaml" lang="plaintext">
|
||||
<code class="language-yaml">[]
|
||||
</code>
|
||||
</pre>
|
||||
</td>
|
||||
<td><p>Commented. TLS configuration to use when scraping the endpoint tlsConfig: insecureSkipVerify: true</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>server.vmServiceScrape.targetPort</td>
|
||||
<td>string</td>
|
||||
<td><pre class="helm-vars-default-value language-yaml" lang="">
|
||||
<code class="language-yaml">http
|
||||
</code>
|
||||
</pre>
|
||||
</td>
|
||||
<td><p>target port</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
||||
@@ -1,14 +1,6 @@
|
||||
## Next release
|
||||
|
||||
- updated common dependency 0.0.37 -> 0.0.39
|
||||
|
||||
## 0.15.7
|
||||
|
||||
**Release date:** 05 Feb 2025
|
||||
|
||||
 
|
||||
|
||||
- added `.Values.allowedMetricsEndpoints` to set allowed scrape endpoints. See [this issue](https://github.com/VictoriaMetrics/helm-charts/issues/1970) for details.
|
||||
- TODO
|
||||
|
||||
## 0.15.6
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
@@ -164,16 +164,6 @@ Change the values according to the need of the environment in ``victoria-metrics
|
||||
<td><p>Pod affinity</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>allowedMetricsEndpoints[0]</td>
|
||||
<td>string</td>
|
||||
<td><pre class="helm-vars-default-value language-yaml" lang="">
|
||||
<code class="language-yaml">/metrics
|
||||
</code>
|
||||
</pre>
|
||||
</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>annotations</td>
|
||||
<td>object</td>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
## Next release
|
||||
|
||||
- updated common dependency 0.0.37 -> 0.0.39
|
||||
- TODO
|
||||
|
||||
## 0.13.8
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
## Next release
|
||||
|
||||
- updated common dependency 0.0.37 -> 0.0.39
|
||||
- TODO
|
||||
|
||||
## 1.7.2
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
## Next release
|
||||
|
||||
- updated common dependency 0.0.37 -> 0.0.39
|
||||
- TODO
|
||||
|
||||
## 0.8.6
|
||||
|
||||
|
||||
@@ -626,17 +626,6 @@ name: ""
|
||||
</pre>
|
||||
</td>
|
||||
<td><p>Existing secret name</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>lifecycle</td>
|
||||
<td>object</td>
|
||||
<td><pre class="helm-vars-default-value language-yaml" lang="plaintext">
|
||||
<code class="language-yaml">{}
|
||||
</code>
|
||||
</pre>
|
||||
</td>
|
||||
<td><p>Specify pod lifecycle</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
||||
@@ -2,24 +2,6 @@
|
||||
|
||||
- TODO
|
||||
|
||||
## 0.17.6
|
||||
|
||||
**Release date:** 06 Feb 2025
|
||||
|
||||
 
|
||||
|
||||
- Reverted enabling headless service for vmselect by default.
|
||||
|
||||
## 0.17.5
|
||||
|
||||
**Release date:** 06 Feb 2025
|
||||
|
||||
 
|
||||
|
||||
- added ability to override default headless service `.Values.vmselect.service.clusterIP` with empty value
|
||||
- added `.Values.common.image.tag` to set the same tag for all cluster components.
|
||||
- updated common dependency 0.0.37 -> 0.0.39
|
||||
|
||||
## 0.17.4
|
||||
|
||||
**Release date:** 04 Feb 2025
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
@@ -145,19 +145,7 @@ Change the values according to the need of the environment in ``victoria-metrics
|
||||
</code>
|
||||
</pre>
|
||||
</td>
|
||||
<td><p>use SRV discovery for storageNode and selectNode flags for enterprise version</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>common.image</td>
|
||||
<td>object</td>
|
||||
<td><pre class="helm-vars-default-value language-yaml" lang="plaintext">
|
||||
<code class="language-yaml">tag: ""
|
||||
</code>
|
||||
</pre>
|
||||
</td>
|
||||
<td><p>common for all components image configuration</p>
|
||||
</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>extraObjects</td>
|
||||
@@ -212,17 +200,6 @@ Change the values according to the need of the environment in ``victoria-metrics
|
||||
</pre>
|
||||
</td>
|
||||
<td><p>Image registry, that can be shared across multiple helm charts</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>global.image.vm.tag</td>
|
||||
<td>string</td>
|
||||
<td><pre class="helm-vars-default-value language-yaml" lang="">
|
||||
<code class="language-yaml">""
|
||||
</code>
|
||||
</pre>
|
||||
</td>
|
||||
<td><p>Image tag for all vm charts</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -705,17 +682,6 @@ loggerFormat: json
|
||||
</pre>
|
||||
</td>
|
||||
<td><p>Init containers for vmauth</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>vmauth.lifecycle</td>
|
||||
<td>object</td>
|
||||
<td><pre class="helm-vars-default-value language-yaml" lang="plaintext">
|
||||
<code class="language-yaml">{}
|
||||
</code>
|
||||
</pre>
|
||||
</td>
|
||||
<td><p>Specify pod lifecycle</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -1523,17 +1489,6 @@ loggerFormat: json
|
||||
</pre>
|
||||
</td>
|
||||
<td><p>Init containers for vminsert</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>vminsert.lifecycle</td>
|
||||
<td>object</td>
|
||||
<td><pre class="helm-vars-default-value language-yaml" lang="plaintext">
|
||||
<code class="language-yaml">{}
|
||||
</code>
|
||||
</pre>
|
||||
</td>
|
||||
<td><p>Specify pod lifecycle</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -2386,17 +2341,6 @@ loggerFormat: json
|
||||
</pre>
|
||||
</td>
|
||||
<td><p>Init containers for vmselect</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>vmselect.lifecycle</td>
|
||||
<td>object</td>
|
||||
<td><pre class="helm-vars-default-value language-yaml" lang="plaintext">
|
||||
<code class="language-yaml">{}
|
||||
</code>
|
||||
</pre>
|
||||
</td>
|
||||
<td><p>Specify pod lifecycle</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -3269,17 +3213,6 @@ loggerFormat: json
|
||||
</pre>
|
||||
</td>
|
||||
<td><p>Init containers for vmstorage</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>vmstorage.lifecycle</td>
|
||||
<td>object</td>
|
||||
<td><pre class="helm-vars-default-value language-yaml" lang="plaintext">
|
||||
<code class="language-yaml">{}
|
||||
</code>
|
||||
</pre>
|
||||
</td>
|
||||
<td><p>Specify pod lifecycle</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
||||
@@ -4,14 +4,6 @@
|
||||
|
||||
- TODO
|
||||
|
||||
## 0.0.39
|
||||
|
||||
**Release date:** 05 Feb 2025
|
||||
|
||||

|
||||
|
||||
- Fix overwrite per service empty registry
|
||||
|
||||
## 0.0.38
|
||||
|
||||
**Release date:** 04 Feb 2025
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
## Next release
|
||||
|
||||
- updated common dependency 0.0.37 -> 0.0.39
|
||||
- TODO
|
||||
|
||||
## 0.7.4
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
## Next release
|
||||
|
||||
- updated common dependency 0.0.37 -> 0.0.39
|
||||
- TODO
|
||||
|
||||
## 0.6.6
|
||||
|
||||
|
||||
@@ -2,31 +2,6 @@
|
||||
|
||||
- TODO
|
||||
|
||||
## 0.36.0
|
||||
|
||||
**Release date:** 07 Feb 2025
|
||||
|
||||
 
|
||||
|
||||
- updates operator to [v0.53.0](https://github.com/VictoriaMetrics/operator/releases/tag/v0.53.0) version
|
||||
|
||||
## 0.35.7
|
||||
|
||||
**Release date:** 06 Feb 2025
|
||||
|
||||
 
|
||||
|
||||
- Added .Values.alertmanager.useManagedConfig to switch storing Alertmanager config in VMAlertmanagerConfig CR instead of k8s Secret. See [this issue](https://github.com/VictoriaMetrics/helm-charts/issues/1968).
|
||||
- updated common dependency 0.0.37 -> 0.0.39
|
||||
|
||||
## 0.35.6
|
||||
|
||||
**Release date:** 05 Feb 2025
|
||||
|
||||
 
|
||||
|
||||
- Use GrafanaDatasource name sanitizing to fix plugin import
|
||||
|
||||
## 0.35.5
|
||||
|
||||
**Release date:** 04 Feb 2025
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
@@ -515,6 +515,8 @@ Change the values according to the need of the environment in ``victoria-metrics
|
||||
- name: blackhole
|
||||
route:
|
||||
receiver: blackhole
|
||||
templates:
|
||||
- /etc/vm/configs/**/*.tmpl
|
||||
</code>
|
||||
</pre>
|
||||
</td>
|
||||
@@ -611,17 +613,6 @@ selectAllByDefault: true
|
||||
</pre>
|
||||
</td>
|
||||
<td><p>Extra alert templates</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>alertmanager.useManagedConfig</td>
|
||||
<td>bool</td>
|
||||
<td><pre class="helm-vars-default-value language-yaml" lang="">
|
||||
<code class="language-yaml">false
|
||||
</code>
|
||||
</pre>
|
||||
</td>
|
||||
<td><p>enable storing .Values.alertmanager.config in VMAlertmanagerConfig instead of k8s Secret</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
||||
@@ -1,22 +1,6 @@
|
||||
## Next release
|
||||
|
||||
- updated common dependency 0.0.37 -> 0.0.39
|
||||
|
||||
## 0.42.0
|
||||
|
||||
**Release date:** 05 Feb 2025
|
||||
|
||||
 
|
||||
|
||||
- updates operator to [v0.53.0](https://github.com/VictoriaMetrics/operator/releases/tag/v0.53.0) version
|
||||
|
||||
## 0.41.2
|
||||
|
||||
**Release date:** 05 Feb 2025
|
||||
|
||||
 
|
||||
|
||||
- added `.Values.allowedMetricsEndpoints`
|
||||
- TODO
|
||||
|
||||
## 0.41.1
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
@@ -384,26 +384,6 @@ duration: 45800h0m0s
|
||||
<td><p>Pod affinity</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>allowedMetricsEndpoints[0]</td>
|
||||
<td>string</td>
|
||||
<td><pre class="helm-vars-default-value language-yaml" lang="">
|
||||
<code class="language-yaml">/metrics
|
||||
</code>
|
||||
</pre>
|
||||
</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>allowedMetricsEndpoints[1]</td>
|
||||
<td>string</td>
|
||||
<td><pre class="helm-vars-default-value language-yaml" lang="">
|
||||
<code class="language-yaml">/metrics/resources
|
||||
</code>
|
||||
</pre>
|
||||
</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>annotations</td>
|
||||
<td>object</td>
|
||||
|
||||
@@ -2,15 +2,6 @@
|
||||
|
||||
- TODO
|
||||
|
||||
## 0.13.9
|
||||
|
||||
**Release date:** 06 Feb 2025
|
||||
|
||||
 
|
||||
|
||||
- added ability to override default headless service .Values.server.service.clusterIP with empty value
|
||||
- updated common dependency 0.0.37 -> 0.0.39
|
||||
|
||||
## 0.13.8
|
||||
|
||||
**Release date:** 27 Jan 2025
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
@@ -1206,7 +1206,7 @@ scrape_configs:
|
||||
<td>server.service.clusterIP</td>
|
||||
<td>string</td>
|
||||
<td><pre class="helm-vars-default-value language-yaml" lang="">
|
||||
<code class="language-yaml">None
|
||||
<code class="language-yaml">""
|
||||
</code>
|
||||
</pre>
|
||||
</td>
|
||||
|
||||
@@ -708,28 +708,16 @@ func lessString(a, b string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
if iA, okA := tryParseInt64(a); okA {
|
||||
if iB, okB := tryParseInt64(b); okB {
|
||||
return iA < iB
|
||||
}
|
||||
nA, okA := tryParseUint64(a)
|
||||
nB, okB := tryParseUint64(b)
|
||||
if okA && okB {
|
||||
return nA < nB
|
||||
}
|
||||
|
||||
if uA, okA := tryParseUint64(a); okA {
|
||||
if uB, okB := tryParseUint64(b); okB {
|
||||
return uA < uB
|
||||
}
|
||||
}
|
||||
|
||||
if tsA, okA := TryParseTimestampRFC3339Nano(a); okA {
|
||||
if tsB, okB := TryParseTimestampRFC3339Nano(b); okB {
|
||||
return tsA < tsB
|
||||
}
|
||||
}
|
||||
|
||||
if fA, okA := tryParseNumber(a); okA {
|
||||
if fB, okB := tryParseNumber(b); okB {
|
||||
return fA < fB
|
||||
}
|
||||
fA, okA := tryParseNumber(a)
|
||||
fB, okB := tryParseNumber(b)
|
||||
if okA && okB {
|
||||
return fA < fB
|
||||
}
|
||||
|
||||
return stringsutil.LessNatural(a, b)
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
package logstorage
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestLessString(t *testing.T) {
|
||||
f := func(a, b string, resultExpected bool) {
|
||||
t.Helper()
|
||||
|
||||
result := lessString(a, b)
|
||||
if result != resultExpected {
|
||||
t.Fatalf("unexpected result for lessString(%q, %q); got %v; want %v", a, b, result, resultExpected)
|
||||
}
|
||||
}
|
||||
|
||||
f("", "", false)
|
||||
f("a", "", false)
|
||||
f("", "a", true)
|
||||
f("foo", "bar", false)
|
||||
f("bar", "foo", true)
|
||||
f("foo", "foo", false)
|
||||
f("foo1", "foo", false)
|
||||
f("foo", "foo1", true)
|
||||
|
||||
// integers
|
||||
f("123", "9", false)
|
||||
f("9", "123", true)
|
||||
f("-123", "9", true)
|
||||
f("9", "-123", false)
|
||||
|
||||
// floating point numbers
|
||||
f("1e3", "5", false)
|
||||
f("5", "1e3", true)
|
||||
|
||||
// timestamps
|
||||
f("2025-01-15T10:20:30.1", "2025-01-15T10:20:30.09", false)
|
||||
f("2025-01-15T10:20:30.09", "2025-01-15T10:20:30.1", true)
|
||||
|
||||
// versions
|
||||
f("v1.23.4", "v1.23.10", true)
|
||||
f("v1.23.10", "v1.23.4", false)
|
||||
|
||||
// durations
|
||||
f("1h", "5s", false)
|
||||
f("5s", "1h", true)
|
||||
|
||||
// bytes
|
||||
f("1MB", "5KB", false)
|
||||
f("5KB", "1MB", true)
|
||||
|
||||
f("1.5M", "5.1K", false)
|
||||
f("5.1K", "1.5M", true)
|
||||
f("1.5M", "1.5M", false)
|
||||
}
|
||||
Reference in New Issue
Block a user