Merge branch 'public-single-node' into pmm-6401-read-prometheus-data-files

This commit is contained in:
Aliaksandr Valialkin 2023-01-24 09:33:54 -08:00
commit 21140318cc
No known key found for this signature in database
GPG key ID: A72BEC6CD3D0DED1
172 changed files with 4996 additions and 3003 deletions

View file

@ -15,6 +15,8 @@ jobs:
steps:
- name: Code checkout
uses: actions/checkout@v3
with:
ref: ${{ github.event.workflow_run.head_branch }}
- name: Setup Go
uses: actions/setup-go@v3

View file

@ -1085,7 +1085,9 @@ The [deduplication](#deduplication) isn't applied for the data exported in nativ
## How to import time series data
Time series data can be imported into VictoriaMetrics via any supported data ingestion protocol:
VictoriaMetrics can discover and scrape metrics from Prometheus-compatible targets (aka "pull" protocol) -
see [these docs](#how-to-scrape-prometheus-exporters-such-as-node-exporter).
Additionally, VictoriaMetrics can accept metrics via the following popular data ingestion protocols (aka "push" protocols):
* [Prometheus remote_write API](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#remote_write). See [these docs](#prometheus-setup) for details.
* DataDog `submit metrics` API. See [these docs](#how-to-send-data-from-datadog-agent) for details.

View file

@ -129,7 +129,7 @@ func setUp() {
storagePath = filepath.Join(os.TempDir(), testStorageSuffix)
processFlags()
logger.Init()
vmstorage.InitWithoutMetrics(promql.ResetRollupResultCacheIfNeeded)
vmstorage.Init(promql.ResetRollupResultCacheIfNeeded)
vmselect.Init()
vminsert.Init()
go httpserver.Serve(*httpListenAddr, requestHandler)
@ -189,10 +189,8 @@ func tearDown() {
func TestWriteRead(t *testing.T) {
t.Run("write", testWrite)
vmstorage.Storage.DebugFlush()
time.Sleep(1 * time.Second)
vmstorage.Stop()
// open storage after stop in write
vmstorage.InitWithoutMetrics(promql.ResetRollupResultCacheIfNeeded)
t.Run("read", testRead)
}

View file

@ -1,12 +1,12 @@
package prometheusimport
import (
"io"
"net/http"
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmagent/common"
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmagent/remotewrite"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/auth"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/httpserver"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/prompbmarshal"
parserCommon "github.com/VictoriaMetrics/VictoriaMetrics/lib/protoparser/common"
parser "github.com/VictoriaMetrics/VictoriaMetrics/lib/protoparser/prometheus"
@ -33,14 +33,9 @@ func InsertHandler(at *auth.Token, req *http.Request) error {
isGzipped := req.Header.Get("Content-Encoding") == "gzip"
return parser.ParseStream(req.Body, defaultTimestamp, isGzipped, func(rows []parser.Row) error {
return insertRows(at, rows, extraLabels)
}, nil)
}
// InsertHandlerForReader processes metrics from given reader with optional gzip format
func InsertHandlerForReader(r io.Reader, isGzipped bool) error {
return parser.ParseStream(r, 0, isGzipped, func(rows []parser.Row) error {
return insertRows(nil, rows, nil)
}, nil)
}, func(s string) {
httpserver.LogError(req, s)
})
}
func insertRows(at *auth.Token, rows []parser.Row, extraLabels []prompbmarshal.Label) error {

View file

@ -0,0 +1,56 @@
package prometheusimport
import (
"bytes"
"flag"
"log"
"net/http"
"net/http/httptest"
"strings"
"testing"
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmagent/remotewrite"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/protoparser/common"
)
var (
srv *httptest.Server
testOutput *bytes.Buffer
)
func TestInsertHandler(t *testing.T) {
setUp()
defer tearDown()
req := httptest.NewRequest("POST", "/insert/0/api/v1/import/prometheus", bytes.NewBufferString(`{"foo":"bar"}
go_memstats_alloc_bytes_total 1`))
if err := InsertHandler(nil, req); err != nil {
t.Errorf("unxepected error %s", err)
}
expectedMsg := "cannot unmarshal Prometheus line"
if !strings.Contains(testOutput.String(), expectedMsg) {
t.Errorf("output %q should contain %q", testOutput.String(), expectedMsg)
}
}
func setUp() {
srv = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(204)
}))
flag.Parse()
remoteWriteFlag := "remoteWrite.url"
if err := flag.Lookup(remoteWriteFlag).Value.Set(srv.URL); err != nil {
log.Fatalf("unable to set %q with value %q, err: %v", remoteWriteFlag, srv.URL, err)
}
logger.Init()
common.StartUnmarshalWorkers()
remotewrite.Init()
testOutput = &bytes.Buffer{}
logger.SetOutputForTests(testOutput)
}
func tearDown() {
common.StopUnmarshalWorkers()
srv.Close()
logger.ResetOutputForTest()
}

View file

@ -1,7 +1,6 @@
package promremotewrite
import (
"io"
"net/http"
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmagent/common"
@ -33,13 +32,6 @@ func InsertHandler(at *auth.Token, req *http.Request) error {
})
}
// InsertHandlerForReader processes metrics from given reader
func InsertHandlerForReader(at *auth.Token, r io.Reader) error {
return parser.ParseStream(r, func(tss []prompb.TimeSeries) error {
return insertRows(at, tss, nil)
})
}
func insertRows(at *auth.Token, timeseries []prompb.TimeSeries, extraLabels []prompbmarshal.Label) error {
ctx := common.GetPushCtx()
defer common.PutPushCtx(ctx)

View file

@ -1,7 +1,6 @@
package vmimport
import (
"io"
"net/http"
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmagent/common"
@ -36,13 +35,6 @@ func InsertHandler(at *auth.Token, req *http.Request) error {
})
}
// InsertHandlerForReader processes metrics from given reader
func InsertHandlerForReader(r io.Reader, isGzipped bool) error {
return parser.ParseStream(r, isGzipped, func(rows []parser.Row) error {
return insertRows(nil, rows, nil)
})
}
func insertRows(at *auth.Token, rows []parser.Row, extraLabels []prompbmarshal.Label) error {
ctx := common.GetPushCtx()
defer common.PutPushCtx(ctx)

View file

@ -64,17 +64,18 @@ func TestManagerUpdateConcurrent(t *testing.T) {
wg := sync.WaitGroup{}
wg.Add(workers)
for i := 0; i < workers; i++ {
go func() {
go func(n int) {
defer wg.Done()
r := rand.New(rand.NewSource(int64(n)))
for i := 0; i < iterations; i++ {
rnd := rand.Intn(len(paths))
rnd := r.Intn(len(paths))
cfg, err := config.Parse([]string{paths[rnd]}, notifier.ValidateTemplates, true)
if err != nil { // update can fail and this is expected
continue
}
_ = m.update(context.Background(), cfg, false)
}
}()
}(i)
}
wg.Wait()
}

View file

@ -162,14 +162,15 @@ consul_sd_configs:
wg := sync.WaitGroup{}
wg.Add(workers)
for i := 0; i < workers; i++ {
go func() {
go func(n int) {
defer wg.Done()
r := rand.New(rand.NewSource(int64(n)))
for i := 0; i < iterations; i++ {
rnd := rand.Intn(len(paths))
rnd := r.Intn(len(paths))
_ = cw.reload(paths[rnd]) // update can fail and this is expected
_ = cw.notifiers()
}
}()
}(i)
}
wg.Wait()
}

View file

@ -27,12 +27,13 @@ func TestClient_Push(t *testing.T) {
if err != nil {
t.Fatalf("failed to create client: %s", err)
}
r := rand.New(rand.NewSource(1))
const rowsN = 1e4
var sent int
for i := 0; i < rowsN; i++ {
s := prompbmarshal.TimeSeries{
Samples: []prompbmarshal.Sample{{
Value: rand.Float64(),
Value: r.Float64(),
Timestamp: time.Now().Unix(),
}},
}

View file

@ -5,6 +5,7 @@ import (
"github.com/VictoriaMetrics/VictoriaMetrics/app/vminsert/common"
"github.com/VictoriaMetrics/VictoriaMetrics/app/vminsert/relabel"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/httpserver"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/prompbmarshal"
parserCommon "github.com/VictoriaMetrics/VictoriaMetrics/lib/protoparser/common"
parser "github.com/VictoriaMetrics/VictoriaMetrics/lib/protoparser/prometheus"
@ -29,7 +30,9 @@ func InsertHandler(req *http.Request) error {
isGzipped := req.Header.Get("Content-Encoding") == "gzip"
return parser.ParseStream(req.Body, defaultTimestamp, isGzipped, func(rows []parser.Row) error {
return insertRows(rows, extraLabels)
}, nil)
}, func(s string) {
httpserver.LogError(req, s)
})
}
func insertRows(rows []parser.Row, extraLabels []prompbmarshal.Label) error {

View file

@ -35,9 +35,10 @@ var (
)
var benchValues = func() []float64 {
r := rand.New(rand.NewSource(1))
values := make([]float64, 1000)
for i := range values {
values[i] = rand.Float64() * 100
values[i] = r.Float64() * 100
}
return values
}()

View file

@ -1,12 +1,14 @@
{
"files": {
"main.css": "./static/css/main.e8e53cbf.css",
"main.js": "./static/js/main.58fe7908.js",
"main.css": "./static/css/main.9ca6b743.css",
"main.js": "./static/js/main.8969be5f.js",
"static/js/27.c1ccfd29.chunk.js": "./static/js/27.c1ccfd29.chunk.js",
"static/media/Lato-Regular.ttf": "./static/media/Lato-Regular.d714fec1633b69a9c2e9.ttf",
"static/media/Lato-Bold.ttf": "./static/media/Lato-Bold.32360ba4b57802daa4d6.ttf",
"index.html": "./index.html"
},
"entrypoints": [
"static/css/main.e8e53cbf.css",
"static/js/main.58fe7908.js"
"static/css/main.9ca6b743.css",
"static/js/main.8969be5f.js"
]
}

View file

@ -1 +1 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="./favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="VM-UI is a metric explorer for Victoria Metrics"/><link rel="apple-touch-icon" href="./apple-touch-icon.png"/><link rel="icon" type="image/png" sizes="32x32" href="./favicon-32x32.png"><link rel="manifest" href="./manifest.json"/><title>VM UI</title><link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono&family=Lato:wght@300;400;700&display=swap" rel="stylesheet"><script src="./dashboards/index.js" type="module"></script><script defer="defer" src="./static/js/main.58fe7908.js"></script><link href="./static/css/main.e8e53cbf.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="./favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="UI for VictoriaMetrics"/><link rel="apple-touch-icon" href="./apple-touch-icon.png"/><link rel="icon" type="image/png" sizes="32x32" href="./favicon-32x32.png"><link rel="manifest" href="./manifest.json"/><title>VM UI</title><script src="./dashboards/index.js" type="module"></script><meta name="twitter:card" content="summary_large_image"><meta name="twitter:image" content="./preview.jpg"><meta name="twitter:title" content="UI for VictoriaMetrics"><meta name="twitter:description" content="Explore and troubleshoot your VictoriaMetrics data"><meta name="twitter:site" content="@VictoriaMetrics"><meta property="og:title" content="Metric explorer for VictoriaMetrics"><meta property="og:description" content="Explore and troubleshoot your VictoriaMetrics data"><meta property="og:image" content="./preview.jpg"><meta property="og:type" content="website"><script defer="defer" src="./static/js/main.8969be5f.js"></script><link href="./static/css/main.9ca6b743.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -7,7 +7,7 @@
/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */
/**
* @remix-run/router v1.0.5
* @remix-run/router v1.3.0
*
* Copyright (c) Remix Software Inc.
*
@ -18,7 +18,7 @@
*/
/**
* React Router DOM v6.4.5
* React Router DOM v6.7.0
*
* Copyright (c) Remix Software Inc.
*
@ -29,7 +29,7 @@
*/
/**
* React Router v6.4.5
* React Router v6.7.0
*
* Copyright (c) Remix Software Inc.
*

View file

@ -85,14 +85,6 @@ func CheckTimeRange(tr storage.TimeRange) error {
// Init initializes vmstorage.
func Init(resetCacheIfNeeded func(mrs []storage.MetricRow)) {
InitWithoutMetrics(resetCacheIfNeeded)
registerStorageMetrics(Storage)
}
// InitWithoutMetrics must be called instead of Init inside tests.
//
// This allows multiple Init / Stop cycles.
func InitWithoutMetrics(resetCacheIfNeeded func(mrs []storage.MetricRow)) {
if err := encoding.CheckPrecisionBits(uint8(*precisionBits)); err != nil {
logger.Fatalf("invalid `-precisionBits`: %s", err)
}
@ -131,6 +123,7 @@ func InitWithoutMetrics(resetCacheIfNeeded func(mrs []storage.MetricRow)) {
sizeBytes := tm.SmallSizeBytes + tm.BigSizeBytes
logger.Infof("successfully opened storage %q in %.3f seconds; partsCount: %d; blocksCount: %d; rowsCount: %d; sizeBytes: %d",
*DataPath, time.Since(startTime).Seconds(), partsCount, blocksCount, rowsCount, sizeBytes)
registerStorageMetrics(Storage)
promdb.Init(retentionPeriod.Msecs)
}

File diff suppressed because it is too large Load diff

View file

@ -7,7 +7,7 @@
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="VM-UI is a metric explorer for Victoria Metrics"
content="UI for VictoriaMetrics"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/apple-touch-icon.png" />
<link rel="icon" type="image/png" sizes="32x32" href="%PUBLIC_URL%/favicon-32x32.png">
@ -26,10 +26,18 @@
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>VM UI</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono&family=Lato:wght@300;400;700&display=swap" rel="stylesheet">
<script src="%PUBLIC_URL%/dashboards/index.js" type="module"></script>
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:image" content="%PUBLIC_URL%/preview.jpg">
<meta name="twitter:title" content="UI for VictoriaMetrics">
<meta name="twitter:description" content="Explore and troubleshoot your VictoriaMetrics data">
<meta name="twitter:site" content="@VictoriaMetrics">
<meta property="og:title" content="Metric explorer for VictoriaMetrics">
<meta property="og:description" content="Explore and troubleshoot your VictoriaMetrics data">
<meta property="og:image" content="%PUBLIC_URL%/preview.jpg">
<meta property="og:type" content="website">
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

View file

@ -17,15 +17,11 @@ const App: FC = () => {
const [loadingTheme, setLoadingTheme] = useState(true);
if (loadingTheme) return (
<>
<Spinner/>
<ThemeProvider setLoadingTheme={setLoadingTheme}/>;
</>
);
return <>
<HashRouter>
{loadingTheme && <Spinner/>}
<ThemeProvider setLoadingTheme={setLoadingTheme}/>
<HashRouter key={`${loadingTheme}`}>
<AppContextProvider>
<Routes>
<Route

View file

@ -3,11 +3,13 @@ import uPlot, { Options as uPlotOptions } from "uplot";
import useResize from "../../../hooks/useResize";
import { BarChartProps } from "./types";
import "./style.scss";
import { useAppState } from "../../../state/common/StateContext";
const BarChart: FC<BarChartProps> = ({
data,
container,
configs }) => {
const { darkTheme } = useAppState();
const uPlotRef = useRef<HTMLDivElement>(null);
const [uPlotInst, setUPlotInst] = useState<uPlot>();
@ -28,7 +30,7 @@ const BarChart: FC<BarChartProps> = ({
const u = new uPlot(options, data, uPlotRef.current);
setUPlotInst(u);
return u.destroy;
}, [uPlotRef.current, layoutSize]);
}, [uPlotRef.current, layoutSize, darkTheme]);
useEffect(() => updateChart(), [data]);

View file

@ -25,7 +25,6 @@ $chart-tooltip-y: -1 * ($padding-small + $chart-tooltip-half-icon);
pointer-events: none;
&_sticky {
background-color: $color-dove-gray;
pointer-events: auto;
z-index: 99;
}

View file

@ -21,6 +21,7 @@ import "./style.scss";
import classNames from "classnames";
import ChartTooltip, { ChartTooltipProps } from "../ChartTooltip/ChartTooltip";
import dayjs from "dayjs";
import { useAppState } from "../../../state/common/StateContext";
export interface LineChartProps {
metrics: MetricResult[];
@ -47,6 +48,8 @@ const LineChart: FC<LineChartProps> = ({
container,
height
}) => {
const { darkTheme } = useAppState();
const uPlotRef = useRef<HTMLDivElement>(null);
const [isPanning, setPanning] = useState(false);
const [xRange, setXRange] = useState({ min: period.start, max: period.end });
@ -222,7 +225,7 @@ const LineChart: FC<LineChartProps> = ({
setUPlotInst(u);
setXRange({ min: period.start, max: period.end });
return u.destroy;
}, [uPlotRef.current, series, layoutSize, height]);
}, [uPlotRef.current, series, layoutSize, height, darkTheme]);
useEffect(() => {
window.addEventListener("keydown", handleKeyDown);

View file

@ -13,6 +13,7 @@ import { getAppModeEnable } from "../../../utils/app-mode";
import classNames from "classnames";
import Timezones from "./Timezones/Timezones";
import { useTimeDispatch, useTimeState } from "../../../state/time/TimeStateContext";
import ThemeControl from "../ThemeControl/ThemeControl";
const title = "Settings";
@ -82,6 +83,9 @@ const GlobalSettings: FC = () => {
onChange={setTimezone}
/>
</div>
<div className="vm-server-configurator__input">
<ThemeControl/>
</div>
<div className="vm-server-configurator__footer">
<Button
variant="outlined"

View file

@ -23,7 +23,7 @@
display: inline-flex;
align-items: center;
justify-content: center;
background-color: rgba($color-black, 0.06);
background-color: $color-hover-black;
padding: calc($padding-small/2);
border-radius: $border-radius-small;
}

View file

@ -57,6 +57,7 @@ const GraphSettings: FC<GraphSettingsProps> = ({ yaxis, setYaxisLimits, toggleEn
</h3>
<Button
size="small"
variant="text"
startIcon={<CloseIcon/>}
onClick={handleClose}
/>

View file

@ -158,7 +158,7 @@ const StepConfigurator: FC = () => {
className="vm-link vm-link_colored"
href="https://docs.victoriametrics.com/keyConcepts.html#range-query"
target="_blank"
rel="noreferrer"
rel="help noreferrer"
>
Read more about Range query
</a>

View file

@ -29,7 +29,7 @@
padding: 0.2em 0.4em;
margin: 0 0.2em;
font-size: 85%;
background-color: rgba($color-black, 0.05);
background-color: $color-hover-black;
border-radius: 6px;
}
}

View file

@ -0,0 +1,44 @@
import React from "react";
import "./style.scss";
import classNames from "classnames";
import { useAppDispatch, useAppState } from "../../../state/common/StateContext";
const options = [
{ title: "Light", value: false },
{ title: "Dark", value: true }
];
const ThemeControl = () => {
const { darkTheme } = useAppState();
const dispatch = useAppDispatch();
const createHandlerClickItem = (value: boolean) => () => {
dispatch({ type: "SET_DARK_THEME", payload: value });
};
return (
<div className="vm-theme-control">
<div className="vm-server-configurator__title">
Theme preferences
</div>
<div className="vm-theme-control-options">
<div
className="vm-theme-control-options__highlight"
style={{ left: darkTheme ? "50%" : 0 }}
/>
{options.map(item => (
<div
className={classNames({
"vm-theme-control-options__item": true,
"vm-theme-control-options__item_active": item.value === darkTheme
})}
onClick={createHandlerClickItem(item.value)}
key={item.title}
>{item.title}</div>
))}
</div>
</div>
);
};
export default ThemeControl;

View file

@ -0,0 +1,44 @@
@use "src/styles/variables" as *;
.vm-theme-control {
&-options {
position: relative;
display: inline-flex;
border: $border-divider;
border-radius: $border-radius-medium;
overflow: hidden;
&__item {
position: relative;
padding: $padding-small $padding-global;
border-right: $border-divider;
color: $color-text;
cursor: pointer;
z-index: 2;
transition: color 200ms ease-out;
user-select: none;
&:last-child {
border: none;
}
&_active {
color: $color-primary-text;
}
&:hover {
box-shadow: $box-shadow-popper;
}
}
&__highlight {
position: absolute;
top: 0;
left: 0;
background-color: $color-primary;
height: 100%;
width: 50%;
transition: left 150ms ease-in;
}
}
}

View file

@ -13,11 +13,14 @@ import useResize from "../../../../hooks/useResize";
import DatePicker from "../../../Main/DatePicker/DatePicker";
import "./style.scss";
import useClickOutside from "../../../../hooks/useClickOutside";
import classNames from "classnames";
import { useAppState } from "../../../../state/common/StateContext";
export const TimeSelector: FC = () => {
const { darkTheme } = useAppState();
const wrapperRef = useRef<HTMLDivElement>(null);
const documentSize = useResize(document.body);
const displayFullDate = useMemo(() => documentSize.width > 1120, [documentSize]);
const displayFullDate = useMemo(() => documentSize.width > 1280, [documentSize]);
const [until, setUntil] = useState<string>();
const [from, setFrom] = useState<string>();
@ -120,7 +123,7 @@ export const TimeSelector: FC = () => {
return <>
<div ref={buttonRef}>
<Tooltip title="Time range controls">
<Tooltip title={displayFullDate ? "Time range controls" : dateTitle}>
<Button
className={appModeEnable ? "" : "vm-header-button"}
variant="contained"
@ -144,7 +147,12 @@ export const TimeSelector: FC = () => {
ref={wrapperRef}
>
<div className="vm-time-selector-left">
<div className="vm-time-selector-left-inputs">
<div
className={classNames({
"vm-time-selector-left-inputs": true,
"vm-time-selector-left-inputs_dark": darkTheme
})}
>
<div
className="vm-time-selector-left-inputs__date"
ref={fromRef}

View file

@ -18,6 +18,10 @@
align-items: flex-start;
justify-content: stretch;
&_dark &__date {
border-color: $color-text-disabled;
}
&__date {
display: grid;
grid-template-columns: 1fr 14px;
@ -70,7 +74,7 @@
display: inline-flex;
align-items: center;
justify-content: center;
background-color: rgba($color-black, 0.06);
background-color: $color-hover-black;
padding: calc($padding-small/2);
border-radius: $border-radius-small;
}

View file

@ -39,7 +39,7 @@
code {
padding: 0.2em 0.4em;
font-size: 85%;
background-color: rgba($color-black, 0.05);
background-color: $color-hover-black;
border-radius: 6px;
}
}

View file

@ -1,7 +1,7 @@
import React, { FC } from "preact/compat";
import dayjs from "dayjs";
import "./style.scss";
import { LogoIcon } from "../../Main/Icons";
import { IssueIcon, LogoIcon, WikiIcon } from "../../Main/Icons";
const Footer: FC = () => {
const copyrightYears = `2019-${dayjs().format("YYYY")}`;
@ -11,18 +11,28 @@ const Footer: FC = () => {
className="vm-link vm-footer__website"
target="_blank"
href="https://victoriametrics.com/"
rel="noreferrer"
rel="me noreferrer"
>
<LogoIcon/>
victoriametrics.com
</a>
<a
className="vm-link"
className="vm-link vm-footer__link"
target="_blank"
href="https://docs.victoriametrics.com/#vmui"
rel="help noreferrer"
>
<WikiIcon/>
Documentation
</a>
<a
className="vm-link vm-footer__link"
target="_blank"
href="https://github.com/VictoriaMetrics/VictoriaMetrics/issues/new/choose"
rel="noreferrer"
>
create an issue
<IssueIcon/>
Create an issue
</a>
<div className="vm-footer__copyright">
&copy; {copyrightYears} VictoriaMetrics

View file

@ -5,10 +5,12 @@
align-items: center;
justify-content: center;
padding: $padding-medium;
gap: $padding-large;
gap: $padding-medium;
border-top: $border-divider;
color: $color-text-secondary;
background: $color-background-body;
&__link,
&__website {
display: grid;
grid-template-columns: 12px auto;
@ -17,6 +19,14 @@
gap: 6px;
}
&__website {
margin-right: $padding-global;
}
&__link {
grid-template-columns: 14px auto;
}
&__copyright {
text-align: right;
flex-grow: 1;

View file

@ -1,86 +1,58 @@
import React, { FC, useMemo, useState } from "preact/compat";
import React, { FC, useMemo } from "preact/compat";
import { ExecutionControls } from "../../Configurators/TimeRangeSettings/ExecutionControls/ExecutionControls";
import { setQueryStringWithoutPageReload } from "../../../utils/query-string";
import { TimeSelector } from "../../Configurators/TimeRangeSettings/TimeSelector/TimeSelector";
import GlobalSettings from "../../Configurators/GlobalSettings/GlobalSettings";
import { useLocation, useNavigate } from "react-router-dom";
import router, { RouterOptions, routerOptions } from "../../../router";
import { useEffect } from "react";
import ShortcutKeys from "../../Main/ShortcutKeys/ShortcutKeys";
import { getAppModeEnable, getAppModeParams } from "../../../utils/app-mode";
import CardinalityDatePicker from "../../Configurators/CardinalityDatePicker/CardinalityDatePicker";
import { LogoFullIcon } from "../../Main/Icons";
import { getCssVariable } from "../../../utils/theme";
import Tabs from "../../Main/Tabs/Tabs";
import "./style.scss";
import classNames from "classnames";
import { useDashboardsState } from "../../../state/dashboards/DashboardsStateContext";
import StepConfigurator from "../../Configurators/StepConfigurator/StepConfigurator";
import { useAppState } from "../../../state/common/StateContext";
import HeaderNav from "./HeaderNav/HeaderNav";
const Header: FC = () => {
const primaryColor = getCssVariable("color-primary");
const { darkTheme } = useAppState();
const appModeEnable = getAppModeEnable();
const { dashboardsSettings } = useDashboardsState();
const { headerStyles: {
background = appModeEnable ? "#FFF" : primaryColor,
color = appModeEnable ? primaryColor : "#FFF",
} = {} } = getAppModeParams();
const primaryColor = useMemo(() => {
const variable = darkTheme ? "color-background-block" : "color-primary";
return getCssVariable(variable);
}, [darkTheme]);
const { background, color } = useMemo(() => {
const { headerStyles: {
background = appModeEnable ? "#FFF" : primaryColor,
color = appModeEnable ? primaryColor : "#FFF",
} = {} } = getAppModeParams();
return { background, color };
}, [primaryColor]);
const navigate = useNavigate();
const { search, pathname } = useLocation();
const routes = useMemo(() => ([
{
label: routerOptions[router.home].title,
value: router.home,
},
{
label: routerOptions[router.metrics].title,
value: router.metrics,
},
{
label: routerOptions[router.cardinality].title,
value: router.cardinality,
},
{
label: routerOptions[router.topQueries].title,
value: router.topQueries,
},
{
label: routerOptions[router.trace].title,
value: router.trace,
},
{
label: routerOptions[router.dashboards].title,
value: router.dashboards,
hide: appModeEnable || !dashboardsSettings.length
}
]), [appModeEnable, dashboardsSettings]);
const [activeMenu, setActiveMenu] = useState(pathname);
const headerSetup = useMemo(() => {
return ((routerOptions[pathname] || {}) as RouterOptions).header || {};
}, [pathname]);
const onClickLogo = () => {
navigateHandler(router.home);
navigate({ pathname: router.home, search: search });
setQueryStringWithoutPageReload({});
window.location.reload();
};
const navigateHandler = (pathname: string) => {
navigate({ pathname, search: search });
};
useEffect(() => {
setActiveMenu(pathname);
}, [pathname]);
return <header
className={classNames({
"vm-header": true,
"vm-header_app": appModeEnable
"vm-header_app": appModeEnable,
"vm-header_dark": darkTheme
})}
style={{ background, color }}
>
@ -93,14 +65,10 @@ const Header: FC = () => {
<LogoFullIcon/>
</div>
)}
<div className="vm-header-nav">
<Tabs
isNavLink
activeItem={activeMenu}
items={routes.filter(r => !r.hide)}
color={color}
/>
</div>
<HeaderNav
color={color}
background={background}
/>
<div className="vm-header__settings">
{headerSetup?.stepControl && <StepConfigurator/>}
{headerSetup?.timeSelector && <TimeSelector/>}

View file

@ -0,0 +1,89 @@
import React, { FC, useMemo, useState } from "preact/compat";
import router, { routerOptions } from "../../../../router";
import { getAppModeEnable } from "../../../../utils/app-mode";
import { useLocation } from "react-router-dom";
import { useDashboardsState } from "../../../../state/dashboards/DashboardsStateContext";
import { useEffect } from "react";
import "./style.scss";
import NavItem from "./NavItem";
import NavSubItem from "./NavSubItem";
interface HeaderNavProps {
color: string
background: string
}
const HeaderNav: FC<HeaderNavProps> = ({ color, background }) => {
const appModeEnable = getAppModeEnable();
const { dashboardsSettings } = useDashboardsState();
const { pathname } = useLocation();
const [activeMenu, setActiveMenu] = useState(pathname);
const menu = useMemo(() => ([
{
label: routerOptions[router.home].title,
value: router.home,
},
{
label: "Explore",
submenu: [
{
label: routerOptions[router.metrics].title,
value: router.metrics,
},
{
label: routerOptions[router.cardinality].title,
value: router.cardinality,
},
{
label: routerOptions[router.topQueries].title,
value: router.topQueries,
},
]
},
{
label: routerOptions[router.trace].title,
value: router.trace,
},
{
label: routerOptions[router.dashboards].title,
value: router.dashboards,
hide: appModeEnable || !dashboardsSettings.length,
}
].filter(r => !r.hide)), [appModeEnable, dashboardsSettings]);
useEffect(() => {
setActiveMenu(pathname);
}, [pathname]);
return (
<nav className="vm-header-nav">
{menu.map(m => (
m.submenu
? (
<NavSubItem
key={m.label}
activeMenu={activeMenu}
label={m.label || ""}
submenu={m.submenu}
color={color}
background={background}
/>
)
: (
<NavItem
key={m.value}
activeMenu={activeMenu}
value={m.value}
label={m.label || ""}
color={color}
/>
)
))}
</nav>
);
};
export default HeaderNav;

View file

@ -0,0 +1,30 @@
import React, { FC } from "preact/compat";
import { NavLink } from "react-router-dom";
import classNames from "classnames";
interface NavItemProps {
activeMenu: string,
label: string,
value: string,
color?: string
}
const NavItem: FC<NavItemProps> = ({
activeMenu,
label,
value,
color
}) => (
<NavLink
className={classNames({
"vm-header-nav-item": true,
"vm-header-nav-item_active": activeMenu === value // || m.submenu?.find(m => m.value === activeMenu)
})}
style={{ color }}
to={value}
>
{label}
</NavLink>
);
export default NavItem;

View file

@ -0,0 +1,96 @@
import React, { FC, useRef, useState } from "preact/compat";
import { useLocation } from "react-router-dom";
import classNames from "classnames";
import { ArrowDropDownIcon } from "../../../Main/Icons";
import Popper from "../../../Main/Popper/Popper";
import NavItem from "./NavItem";
import { useEffect } from "react";
interface NavItemProps {
activeMenu: string,
label: string,
submenu: {label: string | undefined, value: string}[],
color?: string
background?: string
}
const NavSubItem: FC<NavItemProps> = ({
activeMenu,
label,
color,
background,
submenu
}) => {
const { pathname } = useLocation();
const [openSubmenu, setOpenSubmenu] = useState(false);
const [menuTimeout, setMenuTimeout] = useState<NodeJS.Timeout | null>(null);
const buttonRef = useRef<HTMLDivElement>(null);
const handleOpenSubmenu = () => {
setOpenSubmenu(true);
if (menuTimeout) clearTimeout(menuTimeout);
};
const handleCloseSubmenu = () => {
setOpenSubmenu(false);
};
const handleMouseLeave = () => {
if (menuTimeout) clearTimeout(menuTimeout);
const timeout = setTimeout(handleCloseSubmenu, 300);
setMenuTimeout(timeout);
};
const handleMouseEnterPopup = () => {
if (menuTimeout) clearTimeout(menuTimeout);
};
useEffect(() => {
handleCloseSubmenu();
}, [pathname]);
return (
<div
className={classNames({
"vm-header-nav-item": true,
"vm-header-nav-item_sub": true,
"vm-header-nav-item_open": openSubmenu,
"vm-header-nav-item_active": submenu.find(m => m.value === activeMenu)
})}
style={{ color }}
onMouseEnter={handleOpenSubmenu}
onMouseLeave={handleMouseLeave}
ref={buttonRef}
>
{label}
<ArrowDropDownIcon/>
<Popper
open={openSubmenu}
placement="bottom-left"
offset={{ top: 12, left: 0 }}
onClose={handleCloseSubmenu}
buttonRef={buttonRef}
>
<div
className="vm-header-nav-item-submenu"
style={{ background }}
onMouseLeave={handleMouseLeave}
onMouseEnter={handleMouseEnterPopup}
>
{submenu.map(sm => (
<NavItem
key={sm.value}
activeMenu={activeMenu}
value={sm.value}
label={sm.label || ""}
/>
))}
</div>
</Popper>
</div>
);
};
export default NavSubItem;

View file

@ -0,0 +1,63 @@
@use "src/styles/variables" as *;
.vm-header-nav {
display: flex;
align-items: center;
justify-content: flex-start;
gap: $padding-global;
font-size: $font-size-small;
font-weight: bold;
&-item {
position: relative;
padding: $padding-global $padding-small;
opacity: 0.5;
cursor: pointer;
transition: opacity 200ms ease-in;
text-transform: uppercase;
&_sub {
display: grid;
grid-template-columns: auto 14px;
align-items: center;
justify-content: center;
gap: 4px;
cursor: default;
}
&:hover {
opacity: 1;
}
&_active {
opacity: 1;
}
svg {
transform: rotate(0deg);
transition: transform 200ms ease-in;
}
&_open {
svg {
transform: rotate(180deg);
}
}
&-submenu {
display: grid;
white-space: nowrap;
padding: $padding-small;
color: $color-white;
border-radius: 2px;
opacity: 1;
transform-origin: top center;
font-size: $font-size-small;
font-weight: bold;
&-item {
cursor: pointer;
}
}
}
}

View file

@ -6,17 +6,18 @@
align-items: center;
justify-content: flex-start;
padding: $padding-small $padding-medium;
gap: $padding-large;
gap: 0 $padding-large;
z-index: 99;
&_app {
padding: $padding-small 0;
}
@media (max-width: 1200px) {
gap: $padding-global;
.vm-tabs {
gap: 0;
&_dark {
.vm-header-button,
button:before,
button {
background-color: $color-background-block;
}
}
@ -45,7 +46,7 @@
&-nav {
font-size: $font-size-small;
font-weight: 600;
font-weight: bold;
}
&__settings {

View file

@ -14,7 +14,7 @@ const Layout: FC = () => {
const { pathname } = useLocation();
useEffect(() => {
const defaultTitle = "VM UI";
const defaultTitle = "vmui";
const routeTitle = routerOptions[pathname]?.title;
document.title = routeTitle ? `${routeTitle} - ${defaultTitle}` : defaultTitle;
}, [pathname]);

View file

@ -3,6 +3,7 @@ import { ReactNode } from "react";
import classNames from "classnames";
import { ErrorIcon, InfoIcon, SuccessIcon, WarningIcon } from "../Icons";
import "./style.scss";
import { useAppState } from "../../../state/common/StateContext";
interface AlertProps {
variant?: "success" | "error" | "info" | "warning"
@ -19,12 +20,14 @@ const icons = {
const Alert: FC<AlertProps> = ({
variant,
children }) => {
const { darkTheme } = useAppState();
return (
<div
className={classNames({
"vm-alert": true,
[`vm-alert_${variant}`]: variant
[`vm-alert_${variant}`]: variant,
"vm-alert_dark": darkTheme
})}
>
<div className="vm-alert__icon">{icons[variant || "info"]}</div>

View file

@ -11,7 +11,7 @@
border-radius: $border-radius-medium;
box-shadow: $box-shadow;
font-size: $font-size-medium;
font-weight: 500;
font-weight: normal;
color: $color-text;
line-height: 20px;
@ -75,4 +75,14 @@
background-color: $color-warning;
}
}
&_dark {
&:after {
opacity: 0.1;
}
}
&_dark &__content {
filter: none;
}
}

View file

@ -10,7 +10,7 @@ $button-radius: 6px;
padding: 6px 14px;
font-size: $font-size-small;
line-height: 15px;
font-weight: 500;
font-weight: normal;
min-height: 31px;
border-radius: $button-radius;
color: $color-white;
@ -21,7 +21,7 @@ $button-radius: 6px;
white-space: nowrap;
&:hover:after {
background-color: rgba($color-black, 0.05);
background-color: $color-hover-black;
}
&:before,

View file

@ -104,7 +104,7 @@
transition: color 200ms ease, background-color 300ms ease-in-out;
&:hover {
background-color: rgba($color-black, 0.05);
background-color: $color-hover-black;
}
&_empty {
@ -144,7 +144,7 @@
transition: color 200ms ease, background-color 300ms ease-in-out;
&:hover {
background-color: rgba($color-black, 0.05);
background-color: $color-hover-black;
}
&_selected {
@ -270,6 +270,10 @@
justify-content: space-between;
margin-top: $padding-global;
&_dark &__input {
border-color: $color-text-disabled;
}
span {
margin: 0 $padding-small;
}
@ -277,11 +281,13 @@
&__input {
width: 64px;
height: 32px;
border: 1px solid $color-alto;
border: $border-divider;
border-radius: $border-radius-small;
font-size: $font-size-medium;
padding: 2px $padding-small;
text-align: center;
background-color: transparent;
color: $color-text;
&:focus {
border-color: $color-primary;

View file

@ -2,6 +2,7 @@ import React, { FC, useEffect, useMemo, useRef, useState } from "preact/compat";
import { Dayjs } from "dayjs";
import { FormEvent, FocusEvent } from "react";
import classNames from "classnames";
import { useAppState } from "../../../../state/common/StateContext";
interface CalendarTimepickerProps {
selectDate: Dayjs
@ -13,6 +14,7 @@ enum TimeUnits { hour, minutes, seconds }
const TimePicker: FC<CalendarTimepickerProps>= ({ selectDate, onChangeTime, onClose }) => {
const { darkTheme } = useAppState();
const [activeField, setActiveField] = useState<TimeUnits>(TimeUnits.hour);
const [hours, setHours] = useState(selectDate.format("HH"));
@ -154,7 +156,12 @@ const TimePicker: FC<CalendarTimepickerProps>= ({ selectDate, onChangeTime, onCl
</div>
))}
</div>
<div className="vm-calendar-time-picker-fields">
<div
className={classNames({
"vm-calendar-time-picker-fields": true,
"vm-calendar-time-picker-fields_dark": darkTheme
})}
>
<input
className="vm-calendar-time-picker-fields__input"
value={hours}

View file

@ -344,3 +344,48 @@ export const TimelineIcon = () => (
></path>
</svg>
);
export const WikiIcon = () => (
<svg
viewBox="0 0 24 24"
fill="currentColor"
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M21 5C19.89 4.65 18.67 4.5 17.5 4.5C15.55 4.5 13.45 4.9 12 6C10.55 4.9 8.45 4.5 6.5 4.5C5.33 4.5 4.11 4.65 3 5C2.25 5.25 1.6 5.55 1 6V20.6C1 20.85 1.25 21.1 1.5 21.1C1.6 21.1 1.65 21.1 1.75 21.05C3.15 20.3 4.85 20 6.5 20C8.2 20 10.65 20.65 12 21.5C13.35 20.65 15.8 20 17.5 20C19.15 20 20.85 20.3 22.25 21.05C22.35 21.1 22.4 21.1 22.5 21.1C22.75 21.1 23 20.85 23 20.6V6C22.4 5.55 21.75 5.25 21 5ZM21 18.5C19.9 18.15 18.7 18 17.5 18C15.8 18 13.35 18.65 12 19.5C10.65 18.65 8.2 18 6.5 18C5.3 18 4.1 18.15 3 18.5V7C4.1 6.65 5.3 6.5 6.5 6.5C8.2 6.5 10.65 7.15 12 8C13.35 7.15 15.8 6.5 17.5 6.5C18.7 6.5 19.9 6.65 21 7V18.5Z"
/>
<path
d="M17.5 10.5C18.38 10.5 19.23 10.59 20 10.76V9.24C19.21 9.09 18.36 9 17.5 9C15.8 9 14.26 9.29 13 9.83V11.49C14.13 10.85 15.7 10.5 17.5 10.5ZM13 12.49V14.15C14.13 13.51 15.7 13.16 17.5 13.16C18.38 13.16 19.23 13.25 20 13.42V11.9C19.21 11.75 18.36 11.66 17.5 11.66C15.8 11.66 14.26 11.96 13 12.49ZM17.5 14.33C15.8 14.33 14.26 14.62 13 15.16V16.82C14.13 16.18 15.7 15.83 17.5 15.83C18.38 15.83 19.23 15.92 20 16.09V14.57C19.21 14.41 18.36 14.33 17.5 14.33Z"
/>
<path
d="M6.5 10.5C5.62 10.5 4.77 10.59 4 10.76V9.24C4.79 9.09 5.64 9 6.5 9C8.2 9 9.74 9.29 11 9.83V11.49C9.87 10.85 8.3 10.5 6.5 10.5ZM11 12.49V14.15C9.87 13.51 8.3 13.16 6.5 13.16C5.62 13.16 4.77 13.25 4 13.42V11.9C4.79 11.75 5.64 11.66 6.5 11.66C8.2 11.66 9.74 11.96 11 12.49ZM6.5 14.33C8.2 14.33 9.74 14.62 11 15.16V16.82C9.87 16.18 8.3 15.83 6.5 15.83C5.62 15.83 4.77 15.92 4 16.09V14.57C4.79 14.41 5.64 14.33 6.5 14.33Z"
/>
</svg>
);
export const IssueIcon = () => (
<svg
viewBox="0 0 24 24"
fill="currentColor"
>
<path
d="M12 2C6.49 2 2 6.49 2 12s4.49 10 10 10 10-4.49 10-10S17.51 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm3-8c0 1.66-1.34 3-3 3s-3-1.34-3-3 1.34-3 3-3 3 1.34 3 3z"
></path>
</svg>
);
export const QuestionIcon = () => (
<svg
viewBox="0 0 24 24"
fill="currentColor"
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M12 2C6.48 2 2 6.48 2 12C2 17.52 6.48 22 12 22C17.52 22 22 17.52 22 12C22 6.48 17.52 2 12 2ZM12 6C9.79 6 8 7.79 8 10H10C10 8.9 10.9 8 12 8C13.1 8 14 8.9 14 10C14 10.8792 13.4202 11.3236 12.7704 11.8217C11.9421 12.4566 11 13.1787 11 15H13C13 13.9046 13.711 13.2833 14.4408 12.6455C15.21 11.9733 16 11.2829 16 10C16 7.79 14.21 6 12 6ZM13 16V18H11V16H13Z"
/>
</svg>
);

View file

@ -11,7 +11,7 @@
&-track {
width: 100%;
height: 20px;
background-color: rgba($color-black, 0.05);
background-color: $color-hover-black;
border-radius: $border-radius-small;
&__thumb {

View file

@ -8,7 +8,7 @@ $padding-modal: 22px;
left: 0;
right: 0;
bottom: 0;
z-index: 100;
z-index: 999;
display: flex;
align-items: center;
justify-content: center;
@ -16,9 +16,11 @@ $padding-modal: 22px;
&-content {
padding: $padding-modal;
background: $color-white;
background: $color-background-block;
box-shadow: 0 0 24px rgba($color-black, 0.07);
border-radius: $border-radius-small;
max-height: 90vh;
overflow: auto;
&-header {
display: grid;

View file

@ -3,6 +3,7 @@ import classNames from "classnames";
import { ArrowDropDownIcon, CloseIcon } from "../Icons";
import { FormEvent, MouseEvent } from "react";
import Autocomplete from "../Autocomplete/Autocomplete";
import { useAppState } from "../../../state/common/StateContext";
import "./style.scss";
interface SelectProps {
@ -26,6 +27,7 @@ const Select: FC<SelectProps> = ({
autofocus,
onChange
}) => {
const { darkTheme } = useAppState();
const [search, setSearch] = useState("");
const autocompleteAnchorEl = useRef<HTMLDivElement>(null);
@ -106,7 +108,12 @@ const Select: FC<SelectProps> = ({
}, []);
return (
<div className="vm-select">
<div
className={classNames({
"vm-select": true,
"vm-select_dark": darkTheme
})}
>
<div
className="vm-select-input"
onClick={handleToggleList}

View file

@ -24,7 +24,7 @@
display: inline-flex;
align-items: center;
justify-content: center;
background-color: rgba($color-black, 0.06);
background-color: $color-hover-black;
padding: 2px 2px 2px 6px;
border-radius: $border-radius-small;
font-size: $font-size;
@ -60,6 +60,8 @@
z-index: 2;
min-width: 100px;
flex-grow: 1;
background-color: transparent;
color: $color-text;
&:placeholder-shown {
width: auto;

View file

@ -35,7 +35,7 @@
line-height: 2;
color: $color-text;
text-align: center;
background-color: $color-white;
background-color: $color-background-body;
background-repeat: repeat-x;
border: $border-divider;
border-radius: 4px;

View file

@ -1,5 +1,7 @@
import React, { CSSProperties, FC } from "preact/compat";
import "./style.scss";
import classNames from "classnames";
import { getFromStorage } from "../../../utils/storage";
interface SpinnerProps {
containerStyles?: CSSProperties;
@ -8,7 +10,10 @@ interface SpinnerProps {
const Spinner: FC<SpinnerProps> = ({ containerStyles = {}, message }) => (
<div
className="vm-spinner"
className={classNames({
"vm-spinner": true,
"vm-spinner_dark": getFromStorage("DARK_THEME")
})}
style={containerStyles && {}}
>
<div className="half-circle-spinner">

View file

@ -15,6 +15,10 @@
z-index: 99;
animation: vm-fade 2s cubic-bezier(0.280, 0.840, 0.420, 1.1);
&_dark {
background-color: rgba($color-black, 0.2);
}
&__message {
margin-top: $padding-medium;
white-space: pre-line;

View file

@ -1,6 +1,7 @@
import React, { FC, KeyboardEvent, useEffect, useRef, HTMLInputTypeAttribute, ReactNode } from "react";
import classNames from "classnames";
import { useMemo } from "preact/compat";
import { useAppState } from "../../../state/common/StateContext";
import "./style.scss";
interface TextFieldProps {
@ -38,6 +39,7 @@ const TextField: FC<TextFieldProps> = ({
onFocus,
onBlur
}) => {
const { darkTheme } = useAppState();
const inputRef = useRef<HTMLInputElement>(null);
const textareaRef = useRef<HTMLTextAreaElement>(null);
@ -81,6 +83,7 @@ const TextField: FC<TextFieldProps> = ({
className={classNames({
"vm-text-field": true,
"vm-text-field_textarea": type === "textarea",
"vm-text-field_dark": darkTheme
})}
data-replicated-value={value}
>

View file

@ -67,6 +67,8 @@
min-height: 34px;
resize: none;
overflow: hidden;
background-color: transparent;
color: $color-text;
&:focus {
border: 1px solid $color-primary;

View file

@ -2,6 +2,8 @@ import { FC, useEffect } from "preact/compat";
import { getContrastColor } from "../../../utils/color";
import { getCssVariable, setCssVariable } from "../../../utils/theme";
import { AppParams, getAppModeParams } from "../../../utils/app-mode";
import { getFromStorage } from "../../../utils/storage";
import { darkPalette, lightPalette } from "../../../constants/palette";
interface StyleVariablesProps {
setLoadingTheme: (val: boolean) => void
@ -27,13 +29,6 @@ export const ThemeProvider: FC<StyleVariablesProps> = ({ setLoadingTheme }) => {
setCssVariable("scrollbar-height", `${innerHeight - clientHeight}px`);
};
const setAppModePalette = () => {
colorVariables.forEach(variable => {
const colorFromAppMode = palette[variable as keyof AppParams["palette"]];
if (colorFromAppMode) setCssVariable(`color-${variable}`, colorFromAppMode);
});
};
const setContrastText = () => {
colorVariables.forEach(variable => {
const color = getCssVariable(`color-${variable}`);
@ -42,11 +37,34 @@ export const ThemeProvider: FC<StyleVariablesProps> = ({ setLoadingTheme }) => {
});
};
const setAppModePalette = () => {
colorVariables.forEach(variable => {
const colorFromAppMode = palette[variable as keyof AppParams["palette"]];
if (colorFromAppMode) setCssVariable(`color-${variable}`, colorFromAppMode);
});
setContrastText();
};
const setTheme = () => {
const darkTheme = getFromStorage("DARK_THEME");
const palette = darkTheme ? darkPalette : lightPalette;
Object.entries(palette).forEach(([variable, value]) => {
setCssVariable(variable, value);
});
setContrastText();
};
useEffect(() => {
setAppModePalette();
setScrollbarSize();
setContrastText();
setTheme();
setLoadingTheme(false);
window.addEventListener("storage", setTheme);
return () => {
window.removeEventListener("storage", setTheme);
};
}, []);
return null;

View file

@ -4,6 +4,7 @@ import Trace from "../Trace";
import { ArrowDownIcon } from "../../Main/Icons";
import "./style.scss";
import classNames from "classnames";
import { useAppState } from "../../../state/common/StateContext";
interface RecursiveProps {
trace: Trace;
@ -15,6 +16,7 @@ interface OpenLevels {
}
const NestedNav: FC<RecursiveProps> = ({ trace, totalMsec }) => {
const { darkTheme } = useAppState();
const [openLevels, setOpenLevels] = useState({} as OpenLevels);
const handleListClick = (level: number) => () => {
@ -26,7 +28,12 @@ const NestedNav: FC<RecursiveProps> = ({ trace, totalMsec }) => {
const progress = trace.duration / totalMsec * 100;
return (
<div className="vm-nested-nav">
<div
className={classNames({
"vm-nested-nav": true,
"vm-nested-nav_dark": darkTheme,
})}
>
<div
className="vm-nested-nav-header"
onClick={handleListClick(trace.idValue)}

View file

@ -5,6 +5,10 @@
border-radius: $border-radius-small;
background-color: rgba($color-tropical-blue, 0.4);
&_dark {
background-color: rgba($color-black, 0.1);
}
&-header {
display: grid;
grid-template-columns: auto 1fr;
@ -15,7 +19,7 @@
cursor: pointer;
&:hover {
background-color: rgba($color-black, 0.06);
background-color: $color-hover-black;
}
&__icon {

View file

@ -14,5 +14,6 @@
transform: translateY(-32px);
font-size: $font-size;
line-height: 1.4;
white-space: pre-wrap;
}
}

View file

@ -0,0 +1,37 @@
export const darkPalette = {
"color-primary": "#589DF6",
"color-secondary": "#316eca",
"color-error": "#e5534b",
"color-warning": "#c69026",
"color-info": "#539bf5",
"color-success": "#57ab5a",
"color-background-body": "#22272e",
"color-background-block": "#2d333b",
"color-background-tooltip": "rgba(22, 22, 22, 0.6)",
"color-text": "#cdd9e5",
"color-text-secondary": "#768390",
"color-text-disabled": "#636e7b",
"box-shadow": "rgba(0, 0, 0, 0.16) 1px 2px 6px",
"box-shadow-popper": "rgba(0, 0, 0, 0.2) 0px 2px 8px 0px",
"border-divider": "1px solid rgba(99, 110, 123, 0.5)",
"color-hover-black": "rgba(0, 0, 0, 0.12)"
};
export const lightPalette = {
"color-primary": "#3F51B5",
"color-secondary": "#E91E63",
"color-error": "#FD080E",
"color-warning": "#FF8308",
"color-info": "#03A9F4",
"color-success": "#4CAF50",
"color-background-body": "#FEFEFF",
"color-background-block": "#FFFFFF",
"color-background-tooltip": "rgba(97,97,97, 0.92)",
"color-text": "#110f0f",
"color-text-secondary": "#706F6F",
"color-text-disabled": "#A09F9F",
"box-shadow": "rgba(0, 0, 0, 0.08) 1px 2px 6px",
"box-shadow-popper": "rgba(0, 0, 0, 0.1) 0px 2px 8px 0px",
"border-divider": "1px solid rgba(0, 0, 0, 0.15)",
"color-hover-black": "rgba(0, 0, 0, 0.06)"
};

View file

@ -0,0 +1,430 @@
export default [
"Africa/Abidjan",
"Africa/Accra",
"Africa/Addis_Ababa",
"Africa/Algiers",
"Africa/Asmera",
"Africa/Bamako",
"Africa/Bangui",
"Africa/Banjul",
"Africa/Bissau",
"Africa/Blantyre",
"Africa/Brazzaville",
"Africa/Bujumbura",
"Africa/Cairo",
"Africa/Casablanca",
"Africa/Ceuta",
"Africa/Conakry",
"Africa/Dakar",
"Africa/Dar_es_Salaam",
"Africa/Djibouti",
"Africa/Douala",
"Africa/El_Aaiun",
"Africa/Freetown",
"Africa/Gaborone",
"Africa/Harare",
"Africa/Johannesburg",
"Africa/Juba",
"Africa/Kampala",
"Africa/Khartoum",
"Africa/Kigali",
"Africa/Kinshasa",
"Africa/Lagos",
"Africa/Libreville",
"Africa/Lome",
"Africa/Luanda",
"Africa/Lubumbashi",
"Africa/Lusaka",
"Africa/Malabo",
"Africa/Maputo",
"Africa/Maseru",
"Africa/Mbabane",
"Africa/Mogadishu",
"Africa/Monrovia",
"Africa/Nairobi",
"Africa/Ndjamena",
"Africa/Niamey",
"Africa/Nouakchott",
"Africa/Ouagadougou",
"Africa/Porto-Novo",
"Africa/Sao_Tome",
"Africa/Tripoli",
"Africa/Tunis",
"Africa/Windhoek",
"America/Adak",
"America/Anchorage",
"America/Anguilla",
"America/Antigua",
"America/Araguaina",
"America/Argentina/La_Rioja",
"America/Argentina/Rio_Gallegos",
"America/Argentina/Salta",
"America/Argentina/San_Juan",
"America/Argentina/San_Luis",
"America/Argentina/Tucuman",
"America/Argentina/Ushuaia",
"America/Aruba",
"America/Asuncion",
"America/Bahia",
"America/Bahia_Banderas",
"America/Barbados",
"America/Belem",
"America/Belize",
"America/Blanc-Sablon",
"America/Boa_Vista",
"America/Bogota",
"America/Boise",
"America/Buenos_Aires",
"America/Cambridge_Bay",
"America/Campo_Grande",
"America/Cancun",
"America/Caracas",
"America/Catamarca",
"America/Cayenne",
"America/Cayman",
"America/Chicago",
"America/Chihuahua",
"America/Coral_Harbour",
"America/Cordoba",
"America/Costa_Rica",
"America/Creston",
"America/Cuiaba",
"America/Curacao",
"America/Danmarkshavn",
"America/Dawson",
"America/Dawson_Creek",
"America/Denver",
"America/Detroit",
"America/Dominica",
"America/Edmonton",
"America/Eirunepe",
"America/El_Salvador",
"America/Fort_Nelson",
"America/Fortaleza",
"America/Glace_Bay",
"America/Godthab",
"America/Goose_Bay",
"America/Grand_Turk",
"America/Grenada",
"America/Guadeloupe",
"America/Guatemala",
"America/Guayaquil",
"America/Guyana",
"America/Halifax",
"America/Havana",
"America/Hermosillo",
"America/Indiana/Knox",
"America/Indiana/Marengo",
"America/Indiana/Petersburg",
"America/Indiana/Tell_City",
"America/Indiana/Vevay",
"America/Indiana/Vincennes",
"America/Indiana/Winamac",
"America/Indianapolis",
"America/Inuvik",
"America/Iqaluit",
"America/Jamaica",
"America/Jujuy",
"America/Juneau",
"America/Kentucky/Monticello",
"America/Kralendijk",
"America/La_Paz",
"America/Lima",
"America/Los_Angeles",
"America/Louisville",
"America/Lower_Princes",
"America/Maceio",
"America/Managua",
"America/Manaus",
"America/Marigot",
"America/Martinique",
"America/Matamoros",
"America/Mazatlan",
"America/Mendoza",
"America/Menominee",
"America/Merida",
"America/Metlakatla",
"America/Mexico_City",
"America/Miquelon",
"America/Moncton",
"America/Monterrey",
"America/Montevideo",
"America/Montreal",
"America/Montserrat",
"America/Nassau",
"America/New_York",
"America/Nipigon",
"America/Nome",
"America/Noronha",
"America/North_Dakota/Beulah",
"America/North_Dakota/Center",
"America/North_Dakota/New_Salem",
"America/Ojinaga",
"America/Panama",
"America/Pangnirtung",
"America/Paramaribo",
"America/Phoenix",
"America/Port-au-Prince",
"America/Port_of_Spain",
"America/Porto_Velho",
"America/Puerto_Rico",
"America/Punta_Arenas",
"America/Rainy_River",
"America/Rankin_Inlet",
"America/Recife",
"America/Regina",
"America/Resolute",
"America/Rio_Branco",
"America/Santa_Isabel",
"America/Santarem",
"America/Santiago",
"America/Santo_Domingo",
"America/Sao_Paulo",
"America/Scoresbysund",
"America/Sitka",
"America/St_Barthelemy",
"America/St_Johns",
"America/St_Kitts",
"America/St_Lucia",
"America/St_Thomas",
"America/St_Vincent",
"America/Swift_Current",
"America/Tegucigalpa",
"America/Thule",
"America/Thunder_Bay",
"America/Tijuana",
"America/Toronto",
"America/Tortola",
"America/Vancouver",
"America/Whitehorse",
"America/Winnipeg",
"America/Yakutat",
"America/Yellowknife",
"Antarctica/Casey",
"Antarctica/Davis",
"Antarctica/DumontDUrville",
"Antarctica/Macquarie",
"Antarctica/Mawson",
"Antarctica/McMurdo",
"Antarctica/Palmer",
"Antarctica/Rothera",
"Antarctica/Syowa",
"Antarctica/Troll",
"Antarctica/Vostok",
"Arctic/Longyearbyen",
"Asia/Aden",
"Asia/Almaty",
"Asia/Amman",
"Asia/Anadyr",
"Asia/Aqtau",
"Asia/Aqtobe",
"Asia/Ashgabat",
"Asia/Atyrau",
"Asia/Baghdad",
"Asia/Bahrain",
"Asia/Baku",
"Asia/Bangkok",
"Asia/Barnaul",
"Asia/Beirut",
"Asia/Bishkek",
"Asia/Brunei",
"Asia/Calcutta",
"Asia/Chita",
"Asia/Choibalsan",
"Asia/Colombo",
"Asia/Damascus",
"Asia/Dhaka",
"Asia/Dili",
"Asia/Dubai",
"Asia/Dushanbe",
"Asia/Famagusta",
"Asia/Gaza",
"Asia/Hebron",
"Asia/Hong_Kong",
"Asia/Hovd",
"Asia/Irkutsk",
"Asia/Jakarta",
"Asia/Jayapura",
"Asia/Jerusalem",
"Asia/Kabul",
"Asia/Kamchatka",
"Asia/Karachi",
"Asia/Katmandu",
"Asia/Khandyga",
"Asia/Krasnoyarsk",
"Asia/Kuala_Lumpur",
"Asia/Kuching",
"Asia/Kuwait",
"Asia/Macau",
"Asia/Magadan",
"Asia/Makassar",
"Asia/Manila",
"Asia/Muscat",
"Asia/Nicosia",
"Asia/Novokuznetsk",
"Asia/Novosibirsk",
"Asia/Omsk",
"Asia/Oral",
"Asia/Phnom_Penh",
"Asia/Pontianak",
"Asia/Pyongyang",
"Asia/Qatar",
"Asia/Qostanay",
"Asia/Qyzylorda",
"Asia/Rangoon",
"Asia/Riyadh",
"Asia/Saigon",
"Asia/Sakhalin",
"Asia/Samarkand",
"Asia/Seoul",
"Asia/Shanghai",
"Asia/Singapore",
"Asia/Srednekolymsk",
"Asia/Taipei",
"Asia/Tashkent",
"Asia/Tbilisi",
"Asia/Tehran",
"Asia/Thimphu",
"Asia/Tokyo",
"Asia/Tomsk",
"Asia/Ulaanbaatar",
"Asia/Urumqi",
"Asia/Ust-Nera",
"Asia/Vientiane",
"Asia/Vladivostok",
"Asia/Yakutsk",
"Asia/Yekaterinburg",
"Asia/Yerevan",
"Atlantic/Azores",
"Atlantic/Bermuda",
"Atlantic/Canary",
"Atlantic/Cape_Verde",
"Atlantic/Faeroe",
"Atlantic/Madeira",
"Atlantic/Reykjavik",
"Atlantic/South_Georgia",
"Atlantic/St_Helena",
"Atlantic/Stanley",
"Australia/Adelaide",
"Australia/Brisbane",
"Australia/Broken_Hill",
"Australia/Currie",
"Australia/Darwin",
"Australia/Eucla",
"Australia/Hobart",
"Australia/Lindeman",
"Australia/Lord_Howe",
"Australia/Melbourne",
"Australia/Perth",
"Australia/Sydney",
"Europe/Amsterdam",
"Europe/Andorra",
"Europe/Astrakhan",
"Europe/Athens",
"Europe/Belgrade",
"Europe/Berlin",
"Europe/Bratislava",
"Europe/Brussels",
"Europe/Bucharest",
"Europe/Budapest",
"Europe/Busingen",
"Europe/Chisinau",
"Europe/Copenhagen",
"Europe/Dublin",
"Europe/Gibraltar",
"Europe/Guernsey",
"Europe/Helsinki",
"Europe/Isle_of_Man",
"Europe/Istanbul",
"Europe/Jersey",
"Europe/Kaliningrad",
"Europe/Kiev",
"Europe/Kirov",
"Europe/Lisbon",
"Europe/Ljubljana",
"Europe/London",
"Europe/Luxembourg",
"Europe/Madrid",
"Europe/Malta",
"Europe/Mariehamn",
"Europe/Minsk",
"Europe/Monaco",
"Europe/Moscow",
"Europe/Oslo",
"Europe/Paris",
"Europe/Podgorica",
"Europe/Prague",
"Europe/Riga",
"Europe/Rome",
"Europe/Samara",
"Europe/San_Marino",
"Europe/Sarajevo",
"Europe/Saratov",
"Europe/Simferopol",
"Europe/Skopje",
"Europe/Sofia",
"Europe/Stockholm",
"Europe/Tallinn",
"Europe/Tirane",
"Europe/Ulyanovsk",
"Europe/Uzhgorod",
"Europe/Vaduz",
"Europe/Vatican",
"Europe/Vienna",
"Europe/Vilnius",
"Europe/Volgograd",
"Europe/Warsaw",
"Europe/Zagreb",
"Europe/Zaporozhye",
"Europe/Zurich",
"Indian/Antananarivo",
"Indian/Chagos",
"Indian/Christmas",
"Indian/Cocos",
"Indian/Comoro",
"Indian/Kerguelen",
"Indian/Mahe",
"Indian/Maldives",
"Indian/Mauritius",
"Indian/Mayotte",
"Indian/Reunion",
"Pacific/Apia",
"Pacific/Auckland",
"Pacific/Bougainville",
"Pacific/Chatham",
"Pacific/Easter",
"Pacific/Efate",
"Pacific/Enderbury",
"Pacific/Fakaofo",
"Pacific/Fiji",
"Pacific/Funafuti",
"Pacific/Galapagos",
"Pacific/Gambier",
"Pacific/Guadalcanal",
"Pacific/Guam",
"Pacific/Honolulu",
"Pacific/Johnston",
"Pacific/Kiritimati",
"Pacific/Kosrae",
"Pacific/Kwajalein",
"Pacific/Majuro",
"Pacific/Marquesas",
"Pacific/Midway",
"Pacific/Nauru",
"Pacific/Niue",
"Pacific/Norfolk",
"Pacific/Noumea",
"Pacific/Pago_Pago",
"Pacific/Palau",
"Pacific/Pitcairn",
"Pacific/Ponape",
"Pacific/Port_Moresby",
"Pacific/Rarotonga",
"Pacific/Saipan",
"Pacific/Tahiti",
"Pacific/Tarawa",
"Pacific/Tongatapu",
"Pacific/Truk",
"Pacific/Wake",
"Pacific/Wallis"
];

View file

@ -4,11 +4,12 @@ import { useFetchQueryOptions } from "../../../hooks/useFetchQueryOptions";
import { ErrorTypes } from "../../../types";
import { useQueryDispatch, useQueryState } from "../../../state/query/QueryStateContext";
import Switch from "../../../components/Main/Switch/Switch";
import { PlayIcon } from "../../../components/Main/Icons";
import { PlayIcon, QuestionIcon } from "../../../components/Main/Icons";
import Button from "../../../components/Main/Button/Button";
import TextField from "../../../components/Main/TextField/TextField";
import "./style.scss";
import { useMemo } from "preact/compat";
import Tooltip from "../../../components/Main/Tooltip/Tooltip";
export interface CardinalityConfiguratorProps {
onSetHistory: (step: number) => void;
@ -92,13 +93,13 @@ const CardinalityConfigurator: FC<CardinalityConfiguratorProps> = ({
onChange={onFocusLabelChange}
/>
</div>
<div className="vm-cardinality-configurator-controls__item">
<Switch
label={"Autocomplete"}
value={autocomplete}
onChange={onChangeAutocomplete}
/>
</div>
</div>
<div className="vm-cardinality-configurator-bottom__autocomplete">
<Switch
label={"Autocomplete"}
value={autocomplete}
onChange={onChangeAutocomplete}
/>
</div>
<div className="vm-cardinality-configurator-bottom">
<div className="vm-cardinality-configurator-bottom__info">
@ -106,6 +107,19 @@ const CardinalityConfigurator: FC<CardinalityConfiguratorProps> = ({
at <b>{date}</b>{match && <span> for series selector <b>{match}</b></span>}.
Show top {topN} entries per table.
</div>
<a
className="vm-cardinality-configurator-bottom__docs"
href="https://victoriametrics.com/blog/cardinality-explorer/"
target="_blank"
rel="help noreferrer"
>
<Tooltip title="Example of using">
<Button
variant="text"
startIcon={<QuestionIcon/>}
/>
</Tooltip>
</a>
<Button
startIcon={<PlayIcon/>}
onClick={onRunQuery}

View file

@ -18,12 +18,18 @@
&-bottom {
display: grid;
grid-template-columns: 1fr auto;
grid-template-columns: 1fr auto auto;
align-items: flex-end;
gap: $padding-medium;
gap: $padding-small;
&__info {
font-size: $font-size;
}
&__docs {
display: flex;
align-items: center;
justify-content: center;
}
}
}

View file

@ -89,6 +89,7 @@ const TableSettings: FC<TableSettingsProps> = ({ data, defaultColumns = [], onCh
onClick={handleClose}
startIcon={<CloseIcon/>}
size="small"
variant="text"
/>
</div>
<div className="vm-table-settings-popper-list">

View file

@ -2,20 +2,25 @@
.vm-json-form {
display: grid;
grid-template-rows: auto calc(90vh - 78px - ($padding-medium*3)) auto;
grid-template-rows: auto calc(70vh - 78px - ($padding-medium*3)) auto;
gap: $padding-global;
width: 70vw;
max-width: 1000px;
max-height: 900px;
overflow: hidden;
&_one-field {
grid-template-rows: calc(90vh - 78px - ($padding-medium*3)) auto;
grid-template-rows: calc(70vh - 78px - ($padding-medium*3)) auto;
}
.vm-text-field_textarea {
}
textarea {
overflow: auto;
width: 100%;
height: 100%;
max-height: 900px;
}
&-footer {

View file

@ -151,7 +151,7 @@ const TracePage: FC = () => {
className="vm-link vm-link_colored"
href="https://docs.victoriametrics.com/#query-tracing"
target="_blank"
rel="noreferrer"
rel="help noreferrer"
>
https://docs.victoriametrics.com/#query-tracing
</a>

View file

@ -1,18 +1,22 @@
import { getDefaultServer } from "../../utils/default-server-url";
import { getQueryStringValue } from "../../utils/query-string";
import { getFromStorage, saveToStorage } from "../../utils/storage";
export interface AppState {
serverUrl: string;
tenantId: number;
darkTheme: boolean
}
export type Action =
| { type: "SET_SERVER", payload: string }
| { type: "SET_TENANT_ID", payload: number }
| { type: "SET_DARK_THEME", payload: boolean }
export const initialState: AppState = {
serverUrl: getDefaultServer(),
tenantId: Number(getQueryStringValue("g0.tenantID", 0)),
darkTheme: !!getFromStorage("DARK_THEME")
};
export function reducer(state: AppState, action: Action): AppState {
@ -27,6 +31,12 @@ export function reducer(state: AppState, action: Action): AppState {
...state,
tenantId: action.payload
};
case "SET_DARK_THEME":
saveToStorage("DARK_THEME", action.payload);
return {
...state,
darkTheme: action.payload
};
default:
throw new Error();
}

View file

@ -9,7 +9,7 @@
&:hover,
&_active {
background-color: rgba($color-black, 0.06);
background-color: $color-hover-black;
}
&_multiselect {

View file

@ -6,10 +6,11 @@
gap: $padding-small;
align-items: center;
justify-content: space-between;
background-color: $color-primary;
background-color: $color-background-block;
padding: $padding-small $padding-small $padding-small $padding-global;
border-radius: $border-radius-small $border-radius-small 0 0;
color: $color-white;
color: $color-text;
border-bottom: $border-divider;
&__title {
font-weight: bold;

View file

@ -12,7 +12,7 @@
transition: background-color 200ms ease;
&:hover:not(&_header) {
background-color: rgba($color-black, 0.05);
background-color: $color-hover-black;
}
&_header {
@ -43,7 +43,7 @@
cursor: pointer;
&:hover {
background-color: rgba($color-black, 0.05);
background-color: $color-hover-black;
}
}
@ -54,7 +54,8 @@
}
&_gray {
color: rgba($color-black, 0.4);
color: $color-text;
opacity: 0.4;
}
&_right {

View file

@ -9,6 +9,7 @@ html, body, #root {
background-repeat: no-repeat;
background-attachment: fixed;
margin: 0;
background-color: $color-background-body;
}
body {
@ -54,3 +55,24 @@ input[type=number]::-webkit-outer-spin-button {
svg {
width: 100%;
}
/* Works on Firefox */
* {
scrollbar-width: thin;
scrollbar-color: $color-text-disabled $color-background-block;
}
/* Works on Chrome, Edge, and Safari */
*::-webkit-scrollbar {
width: 12px;
}
*::-webkit-scrollbar-track {
background: $color-background-block;
}
*::-webkit-scrollbar-thumb {
background-color: $color-text-disabled;
border-radius: 20px;
border: 3px solid $color-background-block;
}

View file

@ -0,0 +1,13 @@
@font-face {
font-family: 'Lato';
src: url('../assets/fonts/Lato/Lato-Regular.ttf');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'Lato';
src: url('../assets/fonts/Lato/Lato-Bold.ttf');
font-weight: bold;
font-style: normal;
}

View file

@ -1,6 +1,7 @@
@forward "variables";
@forward "core";
@forward "reset";
@forward "fonts";
@forward "./components/header-button";
@forward "./components/list";
@ -30,4 +31,17 @@
/* backgrounds */
--color-background-body: #FEFEFF;
--color-background-block: #FFFFFF;
--color-background-tooltip: rgba(97,97,97, 0.92);
/* text */
--color-text: #110f0f;
--color-text-secondary: #706F6F;
--color-text-disabled: #A09F9F;
/* box-shadow */
--box-shadow: rgba(0, 0, 0, 0.08) 1px 2px 6px;
--box-shadow-popper: rgba(0, 0, 0, 0.1) 0px 2px 8px 0px;
--border-divider: 1px solid rgba(0, 0, 0, 0.15);
--color-hover-black: rgba(0, 0, 0, 0.06);
}

View file

@ -13,14 +13,11 @@ $color-warning-text: var(--color-warning-text);
$color-info-text: var(--color-info-text);
$color-success-text: var(--color-success-text);
$color-text: #110f0f;
$color-text-secondary: rgba($color-text, 0.6);
$color-text-disabled: rgba($color-text, 0.4);
$color-text: var(--color-text);
$color-text-secondary: var(--color-text-secondary);
$color-text-disabled: var(--color-text-disabled);
$color-black: #110f0f;
$color-dove-gray: #616161;
$color-silver: #C4C4C4;
$color-alto: #D8D8D8;
$color-white: #ffffff;
$color-dodger-blue: #1A90FF;
@ -30,8 +27,7 @@ $color-tropical-blue: #C9E3F6;
/************* background *************/
$color-background-body: var(--color-background-body);
$color-background-block: var(--color-background-block);
$color-background-modal: rgba($color-black, 0.7);
$color-background-tooltip: rgba($color-dove-gray, 0.92);
$color-background-tooltip: var(--color-background-tooltip);
/************* padding *************/
@ -43,7 +39,7 @@ $padding-small: 8px;
/************* fonts *************/
$font-family-global: 'Lato', sans-serif;
$font-family-monospace: 'JetBrains Mono', monospace;
$font-family-monospace: monospace;
$font-size-large: 16px;
$font-size-medium: 14px;
$font-size: 12px;
@ -51,7 +47,7 @@ $font-size-small: 10px;
/************* border *************/
$border-divider: 1px solid rgba($color-black, 0.15);
$border-divider: var(--border-divider);
/************* border-radius *************/
@ -61,6 +57,7 @@ $border-radius-large: 16px;
/************* box-shadows *************/
$box-shadow: 1px 2px 12px rgba($color-black, 0.08);
$box-shadow-bottom: rgba($color-black, 0.04) 0px 3px 5px;
$box-shadow-popper: rgba($color-black, 0.1) 0px 2px 8px 0px;
$box-shadow: var(--box-shadow);
$box-shadow-popper: var(--box-shadow-popper);
$color-hover-black: var(--color-hover-black);

View file

@ -7,6 +7,7 @@ export type StorageKeys = "BASIC_AUTH_DATA"
| "SERIES_LIMITS"
| "TABLE_COMPACT"
| "TIMEZONE"
| "DARK_THEME"
export const saveToStorage = (key: StorageKeys, value: string | boolean | Record<string, unknown>): void => {
if (value) {
@ -15,6 +16,7 @@ export const saveToStorage = (key: StorageKeys, value: string | boolean | Record
} else {
removeFromStorage([key]);
}
window.dispatchEvent(new Event("storage"));
};
// TODO: make this aware of data type that is stored

View file

@ -2,6 +2,7 @@ import { RelativeTimeOption, TimeParams, TimePeriod, Timezone } from "../types";
import dayjs, { UnitTypeShort } from "dayjs";
import { getQueryStringValue } from "./query-string";
import { DATE_ISO_FORMAT } from "../constants/date";
import timezones from "../constants/timezones";
const MAX_ITEMS_PER_CHART = window.innerWidth / 4;
@ -9,7 +10,8 @@ export const limitsDurations = { min: 1, max: 1.578e+11 }; // min: 1 ms, max: 5
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
export const supportedTimezones = Intl.supportedValuesOf("timeZone") as string[];
const supportedValuesOf = Intl.supportedValuesOf;
export const supportedTimezones = supportedValuesOf ? supportedValuesOf("timeZone") as string[] : timezones;
// The list of supported units could be the following -
// https://prometheus.io/docs/prometheus/latest/querying/basics/#time-durations

View file

@ -4,6 +4,7 @@ import { getSecondsFromDuration, roundToMilliseconds } from "../time";
import { AxisRange } from "../../state/graph/reducer";
import { formatTicks, sizeAxis } from "./helpers";
import { TimeParams } from "../../types";
import { getCssVariable } from "../theme";
// see https://github.com/leeoniya/uPlot/tree/master/docs#axis--grid-opts
const timeValues = [
@ -22,10 +23,11 @@ export const getAxes = (series: Series[], unit?: string): Axis[] => Array.from(n
scale: a,
show: true,
size: sizeAxis,
stroke: getCssVariable("color-text"),
font: "10px Arial",
values: (u: uPlot, ticks: number[]) => formatTicks(u, ticks, unit)
};
if (!a) return { space: 80, values: timeValues };
if (!a) return { space: 80, values: timeValues, stroke: getCssVariable("color-text") };
if (!(Number(a) % 2)) return { ...axis, side: 1 };
return axis;
});

View file

@ -1,5 +1,6 @@
/* eslint-disable */
import uPlot from "uplot";
import {getCssVariable} from "../theme";
export const seriesBarsPlugin = (opts) => {
let pxRatio;
@ -88,7 +89,7 @@ export const seriesBarsPlugin = (opts) => {
u.ctx.save();
u.ctx.font = font;
u.ctx.fillStyle = "black";
u.ctx.fillStyle = getCssVariable("color-text");
uPlot.orient(u, sidx, (
series,

View file

@ -0,0 +1 @@
### Helm chart has been moved to [helm-chart](https://github.com/VictoriaMetrics/helm-charts) repository.

View file

@ -19,9 +19,11 @@ See also [case studies](https://docs.victoriametrics.com/CaseStudies.html).
* [VictoriaMetrics vs. OpenTSDB](https://blg.robot-house.us/posts/tsdbs-grow/)
* [Monitoring of multiple OpenShift clusters with VictoriaMetrics](https://medium.com/ibm-garage/monitoring-of-multiple-openshift-clusters-with-victoriametrics-d4f0979e2544)
* [Fly's Prometheus Metrics](https://fly.io/blog/measuring-fly/)
* [Ultra Monitoring with Victoria Metrics](https://dev.to/aws-builders/ultra-monitoring-with-victoria-metrics-1p2)
* [Infrastructure monitoring with Prometheus at Zerodha](https://zerodha.tech/blog/infra-monitoring-at-zerodha/)
* [Sismology: Iguana Solutions Monitoring System](https://medium.com/nerd-for-tech/sismology-iguana-solutions-monitoring-system-f46e4170447f)
* [Prometheus High Availability and Fault Tolerance strategy, long term storage with VictoriaMetrics](https://medium.com/miro-engineering/prometheus-high-availability-and-fault-tolerance-strategy-long-term-storage-with-victoriametrics-82f6f3f0409e)
* [Monitoring with Prometheus, Grafana, AlertManager and VictoriaMetrics](https://www.sensedia.com/post/monitoring-with-prometheus-grafana-alertmanager-and-victoriametrics)
* [How we improved our Kubernetes monitoring at Smarkets, and how you could too](https://smarketshq.com/monitoring-kubernetes-clusters-41a4b24c19e3)
* [Kubernetes and VictoriaMetrics in Mist v4.6](https://mist.io/blog/2021-11-26-kubernetes-and-victoriametrics-in-Mist-v4-6)
* [Foiled by the Firewall: A Tale of Transition From Prometheus to VictoriaMetrics](https://www.percona.com/blog/2020/12/01/foiled-by-the-firewall-a-tale-of-transition-from-prometheus-to-victoriametrics/)
@ -59,6 +61,9 @@ See also [case studies](https://docs.victoriametrics.com/CaseStudies.html).
* [Prometheus, Grafana, and Kubernetes, Oh My!](https://www.groundcover.com/blog/prometheus-grafana-kubernetes)
* [Explaining modern server monitoring stacks for self-hosting](https://dataswamp.org/~solene/2022-09-11-exploring-monitoring-stacks.html)
* [How do We Keep Metrics for a Long Time in VictoriaMetrics](https://www.youtube.com/watch?v=SGZjY7xgDwE)
* [Brewblox: InfluxDB to Victoria Metrics](https://www.brewblox.com/dev/decisions/20210718_victoria_metrics.html)
* [VictoriaMetrics static scraper](https://blog.differentpla.net/blog/2022/10/16/victoria-metrics-static-scraper/)
* [VictoriaMetrics and Open Cosmos boldly takes edge computing to the edge of space](https://www.iot-now.com/2022/07/19/122423-victoriametrics-and-open-cosmos-boldly-takes-edge-computing-to-the-edge-of-space/)
## Our articles

View file

@ -15,6 +15,15 @@ The following tip changes can be tested by building VictoriaMetrics components f
## tip
* FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): add dark mode - it can be seleted via `settings` menu in the top right corner. See [this pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/3704).
* FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): improve visual appearance of the top menu. See [this feature request](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3678).
* FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): embed fonts into binary instead of loading them from external sources. This allows using `vmui` in full from isolated networks without access to Internet. Thanks to @ScottKevill for [the pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/3696).
* FEATURE: [vmagent](https://docs.victoriametrics.com/vmagent.html): reduce memory usage when sending stale markers for targets, which expose big number of metrics. See [this](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3668) and [this](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3675) issues.
* FEATURE: add `-internStringMaxLen` command-line flag, which can be used for fine-tuning RAM vs CPU usage in certain workloads. For example, if the stored time series contain long labels, then it may be useful reducing the `-internStringMaxLen` in order to reduce memory usage at the cost of increased CPU usage. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3692).
* BUGFIX: [VictoriaMetrics cluster](https://docs.victoriametrics.com/Cluster-VictoriaMetrics.html): propagate all the timeout-related errors from `vmstorage` to `vmselect` when `vmstorage`. Previously some timeout errors weren't returned from `vmselect` to `vmstorage`. Instead, `vmstorage` could log the error and close the connection to `vmselect`, so `vmselect` was logging cryptic errors such as `cannot execute funcName="..." on vmstorage "...": EOF`.
* BUGFIX: [vmui](https://docs.victoriametrics.com/#vmui): add support for time zone selection for older versions of browsers. See [this pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/3680).
* BUGFIX: [vmagent](https://docs.victoriametrics.com/vmagent.html): update API version for [ec2_sd_configs](https://docs.victoriametrics.com/sd_configs.html#ec2_sd_configs) to fix [the issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3700) with missing `__meta_ec2_availability_zone_id` attribute.
## [v1.86.2](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.86.2)

View file

@ -1086,7 +1086,9 @@ The [deduplication](#deduplication) isn't applied for the data exported in nativ
## How to import time series data
Time series data can be imported into VictoriaMetrics via any supported data ingestion protocol:
VictoriaMetrics can discover and scrape metrics from Prometheus-compatible targets (aka "pull" protocol) -
see [these docs](#how-to-scrape-prometheus-exporters-such-as-node-exporter).
Additionally, VictoriaMetrics can accept metrics via the following popular data ingestion protocols (aka "push" protocols):
* [Prometheus remote_write API](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#remote_write). See [these docs](#prometheus-setup) for details.
* DataDog `submit metrics` API. See [these docs](#how-to-send-data-from-datadog-agent) for details.

View file

@ -94,7 +94,8 @@ The helm chart repository [https://github.com/VictoriaMetrics/helm-charts/](http
4. Update `cluster` chart versions in [`values.yaml`](https://github.com/VictoriaMetrics/helm-charts/blob/master/charts/victoria-metrics-cluster/values.yaml), bump version for `vmselect`, `vminsert` and `vmstorage` and [`Chart.yaml`](https://github.com/VictoriaMetrics/helm-charts/blob/master/charts/victoria-metrics-cluster/Chart.yaml)
5. Update `k8s-stack` chart versions in [`values.yaml`](https://github.com/VictoriaMetrics/helm-charts/blob/master/charts/victoria-metrics-k8s-stack/values.yaml), bump version for `vmselect`, `vminsert`, `vmstorage`, `vmsingle`, `vmalert`, `vmagent` and [`Chart.yaml`](https://github.com/VictoriaMetrics/helm-charts/blob/master/charts/victoria-metrics-k8s-stack/Chart.yaml)
6. Update `single-node` chart version in [`values.yaml`](https://github.com/VictoriaMetrics/helm-charts/blob/master/charts/victoria-metrics-single/values.yaml) and [`Chart.yaml`](https://github.com/VictoriaMetrics/helm-charts/blob/master/charts/victoria-metrics-single/Chart.yaml)
8. Run `make gen-doc`
7. Update `vmgateway` chart version in [`values.yaml`](https://github.com/VictoriaMetrics/helm-charts/blob/master/charts/victoria-metrics-gateway/values.yamll) and [`Chart.yaml`](https://github.com/VictoriaMetrics/helm-charts/blob/master/charts/victoria-metrics-gateway/Chart.yaml)
8. Run `make gen-docs`
9. Run `make package` that creates or updates zip file with the packed chart
10. Run `make merge`. It creates or updates metadata for charts in index.yaml
11. Push changes to master. `master` is a source of truth

View file

@ -1089,7 +1089,9 @@ The [deduplication](#deduplication) isn't applied for the data exported in nativ
## How to import time series data
Time series data can be imported into VictoriaMetrics via any supported data ingestion protocol:
VictoriaMetrics can discover and scrape metrics from Prometheus-compatible targets (aka "pull" protocol) -
see [these docs](#how-to-scrape-prometheus-exporters-such-as-node-exporter).
Additionally, VictoriaMetrics can accept metrics via the following popular data ingestion protocols (aka "push" protocols):
* [Prometheus remote_write API](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#remote_write). See [these docs](#prometheus-setup) for details.
* DataDog `submit metrics` API. See [these docs](#how-to-send-data-from-datadog-agent) for details.

16
go.mod
View file

@ -3,7 +3,7 @@ module github.com/VictoriaMetrics/VictoriaMetrics
go 1.19
require (
cloud.google.com/go/storage v1.28.1
cloud.google.com/go/storage v1.29.0
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.3.0
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.6.1
github.com/VictoriaMetrics/fastcache v1.12.0
@ -26,13 +26,13 @@ require (
github.com/golang/snappy v0.0.4
github.com/googleapis/gax-go/v2 v2.7.0
github.com/influxdata/influxdb v1.11.0
github.com/klauspost/compress v1.15.14
github.com/klauspost/compress v1.15.15
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-runewidth v0.0.14 // indirect
github.com/oklog/ulid v1.3.1
github.com/prometheus/common v0.39.0 // indirect
github.com/prometheus/prometheus v0.41.0
github.com/urfave/cli/v2 v2.23.7
github.com/urfave/cli/v2 v2.24.1
github.com/valyala/fastjson v1.6.4
github.com/valyala/fastrand v1.1.0
github.com/valyala/fasttemplate v1.2.2
@ -42,19 +42,19 @@ require (
golang.org/x/net v0.5.0
golang.org/x/oauth2 v0.4.0
golang.org/x/sys v0.4.0
google.golang.org/api v0.107.0
google.golang.org/api v0.108.0
gopkg.in/yaml.v2 v2.4.0
)
require (
cloud.google.com/go v0.108.0 // indirect
cloud.google.com/go v0.109.0 // indirect
cloud.google.com/go/compute v1.15.1 // indirect
cloud.google.com/go/compute/metadata v0.2.3 // indirect
cloud.google.com/go/iam v0.10.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2 // indirect
github.com/VividCortex/ewma v1.2.0 // indirect
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect
github.com/aws/aws-sdk-go v1.44.180 // indirect
github.com/aws/aws-sdk-go v1.44.184 // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.13.8 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.21 // indirect
@ -107,13 +107,13 @@ require (
go.opentelemetry.io/otel/trace v1.11.2 // indirect
go.uber.org/atomic v1.10.0 // indirect
go.uber.org/goleak v1.2.0 // indirect
golang.org/x/exp v0.0.0-20230113213754-f9f960f08ad4 // indirect
golang.org/x/exp v0.0.0-20230118134722-a68e582fa157 // indirect
golang.org/x/sync v0.1.0 // indirect
golang.org/x/text v0.6.0 // indirect
golang.org/x/time v0.3.0 // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20230113154510-dbe35b8444a5 // indirect
google.golang.org/genproto v0.0.0-20230119192704-9d59e20e5cd1 // indirect
google.golang.org/grpc v1.52.0 // indirect
google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect

32
go.sum
View file

@ -13,8 +13,8 @@ cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKV
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
cloud.google.com/go v0.108.0 h1:xntQwnfn8oHGX0crLVinvHM+AhXvi3QHQIEcX/2hiWk=
cloud.google.com/go v0.108.0/go.mod h1:lNUfQqusBJp0bgAg6qrHgYFYbTB+dOiob1itwnlD33Q=
cloud.google.com/go v0.109.0 h1:38CZoKGlCnPZjGdyj0ZfpoGae0/wgNfy5F0byyxg0Gk=
cloud.google.com/go v0.109.0/go.mod h1:2sYycXt75t/CSB5R9M2wPU1tJmire7AQZTPtITcGBVE=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
@ -39,8 +39,8 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
cloud.google.com/go/storage v1.28.1 h1:F5QDG5ChchaAVQhINh24U99OWHURqrW8OmQcGKXcbgI=
cloud.google.com/go/storage v1.28.1/go.mod h1:Qnisd4CqDdo6BGs2AD5LLnEsmSQ80wQ5ogcBBKhU86Y=
cloud.google.com/go/storage v1.29.0 h1:6weCgzRvMg7lzuUurI4697AqIRPU1SvzHhynwpW31jI=
cloud.google.com/go/storage v1.29.0/go.mod h1:4puEjyTKnku6gfKoTfNOU/W+a9JyuVNxjpS5GBrB8h4=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/Azure/azure-sdk-for-go v65.0.0+incompatible h1:HzKLt3kIwMm4KeJYTdx9EbjRYTySD/t8i1Ee/W5EGXw=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.3.0 h1:VuHAcMq8pU1IWNT/m5yRaGqbK0BiQKHT8X4DTp9CHdI=
@ -87,8 +87,8 @@ github.com/andybalholm/brotli v1.0.2/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu
github.com/andybalholm/brotli v1.0.3/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/armon/go-metrics v0.3.10 h1:FR+drcQStOe+32sYyJYyZ7FIdgoGGBnwLl+flodp8Uo=
github.com/aws/aws-sdk-go v1.38.35/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
github.com/aws/aws-sdk-go v1.44.180 h1:VLZuAHI9fa/3WME5JjpVjcPCNfpGHVMiHx8sLHWhMgI=
github.com/aws/aws-sdk-go v1.44.180/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
github.com/aws/aws-sdk-go v1.44.184 h1:/MggyE66rOImXJKl1HqhLQITvWvqIV7w1Q4MaG6FHUo=
github.com/aws/aws-sdk-go v1.44.184/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
github.com/aws/aws-sdk-go-v2 v1.17.3 h1:shN7NlnVzvDUgPQ+1rLMSxY8OWRNDRYtiqe0p/PgrhY=
github.com/aws/aws-sdk-go-v2 v1.17.3/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10 h1:dK82zF6kkPeCo8J1e+tGx4JdvDIQzj7ygIoLg8WMuGs=
@ -316,8 +316,8 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
github.com/klauspost/compress v1.13.5/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/compress v1.15.14 h1:i7WCKDToww0wA+9qrUZ1xOjp218vfFo3nTU6UHp+gOc=
github.com/klauspost/compress v1.15.14/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM=
github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw=
github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4=
github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b h1:udzkj9S/zlT5X367kqJis0QP7YMxobob6zhzq6Yre00=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
@ -423,8 +423,8 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/urfave/cli/v2 v2.23.7 h1:YHDQ46s3VghFHFf1DdF+Sh7H4RqhcM+t0TmZRJx4oJY=
github.com/urfave/cli/v2 v2.23.7/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc=
github.com/urfave/cli/v2 v2.24.1 h1:/QYYr7g0EhwXEML8jO+8OYt5trPnLHS0p3mrgExJ5NU=
github.com/urfave/cli/v2 v2.24.1/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.30.0/go.mod h1:2rsYD01CKFrjjsvFxx75KlEUNpWNBY9JWD3K/7o2Cus=
@ -487,8 +487,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/exp v0.0.0-20230113213754-f9f960f08ad4 h1:CNkDRtCj8otM5CFz5jYvbr8ioXX8flVsLfDWEj0M5kk=
golang.org/x/exp v0.0.0-20230113213754-f9f960f08ad4/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
golang.org/x/exp v0.0.0-20230118134722-a68e582fa157 h1:fiNkyhJPUvxbRPbCqY/D9qdjmPzfHcpK3P4bM4gioSY=
golang.org/x/exp v0.0.0-20230118134722-a68e582fa157/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@ -703,8 +703,8 @@ google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0M
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
google.golang.org/api v0.107.0 h1:I2SlFjD8ZWabaIFOfeEDg3pf0BHJDh6iYQ1ic3Yu/UU=
google.golang.org/api v0.107.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY=
google.golang.org/api v0.108.0 h1:WVBc/faN0DkKtR43Q/7+tPny9ZoLZdIiAyG5Q9vFClg=
google.golang.org/api v0.108.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@ -742,8 +742,8 @@ google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7Fc
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20230113154510-dbe35b8444a5 h1:wJT65XLOzhpSPCdAmmKfz94SlmnQzDzjm3Cj9k3fsXY=
google.golang.org/genproto v0.0.0-20230113154510-dbe35b8444a5/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
google.golang.org/genproto v0.0.0-20230119192704-9d59e20e5cd1 h1:wSjSSQW7LuPdv3m1IrSN33nVxH/kID6OIKy+FMwGB2k=
google.golang.org/genproto v0.0.0-20230119192704-9d59e20e5cd1/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=

View file

@ -109,7 +109,7 @@ func (cfg *Config) GetEC2APIResponse(action, filtersQueryString, nextPageToken s
if len(nextPageToken) > 0 {
apiURL += fmt.Sprintf("&NextToken=%s", url.QueryEscape(nextPageToken))
}
apiURL += "&Version=2013-10-15"
apiURL += "&Version=2016-11-15"
req, err := newSignedGetRequest(apiURL, "ec2", cfg.region, ac)
if err != nil {
return nil, fmt.Errorf("cannot create signed request: %w", err)

View file

@ -1,6 +1,7 @@
package bytesutil
import (
"flag"
"strings"
"sync"
"sync/atomic"
@ -8,6 +9,9 @@ import (
"github.com/VictoriaMetrics/VictoriaMetrics/lib/fasttime"
)
var internStringMaxLen = flag.Int("internStringMaxLen", 300, "The maximum length for strings to intern. Lower limit may save memory at the cost of higher CPU usage. "+
"See https://en.wikipedia.org/wiki/String_interning")
// InternBytes interns b as a string
func InternBytes(b []byte) string {
s := ToUnsafeString(b)
@ -30,6 +34,12 @@ func InternString(s string) string {
}
// Make a new copy for s in order to remove references from possible bigger string s refers to.
sCopy := strings.Clone(s)
if len(sCopy) > *internStringMaxLen {
// Do not intern long strings, since this may result in high memory usage
// like in https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3692
return sCopy
}
e := &ismEntry{
lastAccessTime: ct,
s: sCopy,

View file

@ -506,8 +506,9 @@ func TestFloatToDecimalRoundtrip(t *testing.T) {
f(vMin)
f(vStaleNaN)
r := rand.New(rand.NewSource(1))
for i := 0; i < 1e4; i++ {
v := rand.NormFloat64()
v := r.NormFloat64()
f(v)
f(v * 1e-6)
f(v * 1e6)

Some files were not shown because too many files have changed in this diff Show more