mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2025-01-30 15:22:07 +00:00
parent
300d701df0
commit
81b5db04f6
5 changed files with 93 additions and 9 deletions
|
@ -14,13 +14,14 @@ interface RecursiveProps {
|
||||||
isRoot?: boolean;
|
isRoot?: boolean;
|
||||||
trace: Trace;
|
trace: Trace;
|
||||||
totalMsec: number;
|
totalMsec: number;
|
||||||
|
isExpandedAll? : boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface OpenLevels {
|
interface OpenLevels {
|
||||||
[x: number]: boolean
|
[x: number]: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const NestedNav: FC<RecursiveProps> = ({ isRoot, trace, totalMsec }) => {
|
const NestedNav: FC<RecursiveProps> = ({ isRoot, trace, totalMsec, isExpandedAll }) => {
|
||||||
const { isDarkTheme } = useAppState();
|
const { isDarkTheme } = useAppState();
|
||||||
const { isMobile } = useDeviceDetect();
|
const { isMobile } = useDeviceDetect();
|
||||||
const [openLevels, setOpenLevels] = useState({} as OpenLevels);
|
const [openLevels, setOpenLevels] = useState({} as OpenLevels);
|
||||||
|
@ -53,6 +54,26 @@ const NestedNav: FC<RecursiveProps> = ({ isRoot, trace, totalMsec }) => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getIdsFromChildren = (tracingData: Trace) => {
|
||||||
|
const ids = [tracingData.idValue];
|
||||||
|
tracingData?.children?.forEach((child) => {
|
||||||
|
ids.push(...getIdsFromChildren(child));
|
||||||
|
});
|
||||||
|
return ids;
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isExpandedAll) {
|
||||||
|
setOpenLevels([]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const allIds = getIdsFromChildren(trace);
|
||||||
|
const openLevels = {} as OpenLevels;
|
||||||
|
allIds.forEach(id => { openLevels[id] = true; });
|
||||||
|
setOpenLevels(openLevels);
|
||||||
|
}, [isExpandedAll]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={classNames({
|
className={classNames({
|
||||||
|
@ -106,13 +127,14 @@ const NestedNav: FC<RecursiveProps> = ({ isRoot, trace, totalMsec }) => {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{openLevels[trace.idValue] && (
|
{(openLevels[trace.idValue]) && (
|
||||||
<div className="vm-nested-nav__childrens">
|
<div className="vm-nested-nav__childrens">
|
||||||
{hasChildren && trace.children.map((trace) => (
|
{hasChildren && trace.children.map((trace) => (
|
||||||
<NestedNav
|
<NestedNav
|
||||||
key={trace.duration}
|
key={trace.duration}
|
||||||
trace={trace}
|
trace={trace}
|
||||||
totalMsec={totalMsec}
|
totalMsec={totalMsec}
|
||||||
|
isExpandedAll={isExpandedAll}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
$color-base-nested-nav: $color-tropical-blue;
|
$color-base-nested-nav: $color-tropical-blue;
|
||||||
$color-base-nested-nav-dark: $color-background-body;
|
$color-base-nested-nav-dark: $color-background-body;
|
||||||
|
$width-line: 2px;
|
||||||
|
$left-position: calc(-1 * $padding-small);
|
||||||
|
|
||||||
.vm-nested-nav {
|
.vm-nested-nav {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
@ -48,19 +50,19 @@ $color-base-nested-nav-dark: $color-background-body;
|
||||||
content: "";
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: calc(50% - 1px);
|
top: calc(50% - 1px);
|
||||||
height: 2px;
|
height: $width-line;
|
||||||
width: $padding-small;
|
width: $padding-small;
|
||||||
background-color: $color-base-nested-nav;
|
background-color: $color-base-nested-nav;
|
||||||
left: calc(-1 * $padding-small);
|
left: $left-position;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:before {
|
&:before {
|
||||||
content: "";
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 50%;
|
bottom: 50%;
|
||||||
left: calc(-1 * $padding-small);
|
left: $left-position;
|
||||||
height: calc(50% + $padding-small);
|
height: calc(50% + $padding-small);
|
||||||
width: 2px;
|
width: $width-line;
|
||||||
background-color: $color-base-nested-nav;
|
background-color: $color-base-nested-nav;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,9 +124,9 @@ $color-base-nested-nav-dark: $color-background-body;
|
||||||
content: "";
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: calc(-1 * $padding-small);
|
left: $left-position;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 2px;
|
width: $width-line;
|
||||||
background-color: $color-base-nested-nav;
|
background-color: $color-base-nested-nav;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React, { FC, useState } from "preact/compat";
|
import React, { FC, useState } from "preact/compat";
|
||||||
import Trace from "./Trace";
|
import Trace from "./Trace";
|
||||||
import Button from "../Main/Button/Button";
|
import Button from "../Main/Button/Button";
|
||||||
import { CodeIcon, DeleteIcon } from "../Main/Icons";
|
import { ArrowDownIcon, CodeIcon, DeleteIcon, DownloadIcon } from "../Main/Icons";
|
||||||
import "./style.scss";
|
import "./style.scss";
|
||||||
import NestedNav from "./NestedNav/NestedNav";
|
import NestedNav from "./NestedNav/NestedNav";
|
||||||
import Alert from "../Main/Alert/Alert";
|
import Alert from "../Main/Alert/Alert";
|
||||||
|
@ -20,6 +20,7 @@ interface TraceViewProps {
|
||||||
const TracingsView: FC<TraceViewProps> = ({ traces, jsonEditor = false, onDeleteClick }) => {
|
const TracingsView: FC<TraceViewProps> = ({ traces, jsonEditor = false, onDeleteClick }) => {
|
||||||
const { isMobile } = useDeviceDetect();
|
const { isMobile } = useDeviceDetect();
|
||||||
const [openTrace, setOpenTrace] = useState<Trace | null>(null);
|
const [openTrace, setOpenTrace] = useState<Trace | null>(null);
|
||||||
|
const [expandedTraces, setExpandedTraces] = useState<number[]>([]);
|
||||||
|
|
||||||
const handleCloseJson = () => {
|
const handleCloseJson = () => {
|
||||||
setOpenTrace(null);
|
setOpenTrace(null);
|
||||||
|
@ -52,6 +53,27 @@ const TracingsView: FC<TraceViewProps> = ({ traces, jsonEditor = false, onDelete
|
||||||
setOpenTrace(tracingData);
|
setOpenTrace(tracingData);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleSaveToFile = (tracingData: Trace) => () => {
|
||||||
|
const blob = new Blob([tracingData.originalJSON], { type: "application/json" });
|
||||||
|
const href = URL.createObjectURL(blob);
|
||||||
|
|
||||||
|
const link = document.createElement("a");
|
||||||
|
link.href = href;
|
||||||
|
link.download = `vmui_trace_${tracingData.queryValue}.json`;
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.click();
|
||||||
|
|
||||||
|
document.body.removeChild(link);
|
||||||
|
URL.revokeObjectURL(href);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleExpandAll = (tracingData: Trace) => () => {
|
||||||
|
setExpandedTraces(prev => prev.includes(tracingData.idValue)
|
||||||
|
? prev.filter(n => n !== tracingData.idValue)
|
||||||
|
: [...prev, tracingData.idValue]
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="vm-tracings-view">
|
<div className="vm-tracings-view">
|
||||||
|
@ -64,6 +86,28 @@ const TracingsView: FC<TraceViewProps> = ({ traces, jsonEditor = false, onDelete
|
||||||
<h3 className="vm-tracings-view-trace-header-title">
|
<h3 className="vm-tracings-view-trace-header-title">
|
||||||
Trace for <b className="vm-tracings-view-trace-header-title__query">{trace.queryValue}</b>
|
Trace for <b className="vm-tracings-view-trace-header-title__query">{trace.queryValue}</b>
|
||||||
</h3>
|
</h3>
|
||||||
|
<Tooltip title={expandedTraces.includes(trace.idValue) ? "Collapse All" : "Expand All"}>
|
||||||
|
<Button
|
||||||
|
variant="text"
|
||||||
|
startIcon={(
|
||||||
|
<div
|
||||||
|
className={classNames({
|
||||||
|
"vm-tracings-view-trace-header__expand-icon": true,
|
||||||
|
"vm-tracings-view-trace-header__expand-icon_open": expandedTraces.includes(trace.idValue) })}
|
||||||
|
><ArrowDownIcon/></div>
|
||||||
|
)}
|
||||||
|
onClick={handleExpandAll(trace)}
|
||||||
|
ariaLabel={expandedTraces.includes(trace.idValue) ? "Collapse All" : "Expand All"}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip title={"Save Trace to JSON"}>
|
||||||
|
<Button
|
||||||
|
variant="text"
|
||||||
|
startIcon={<DownloadIcon/>}
|
||||||
|
onClick={handleSaveToFile(trace)}
|
||||||
|
ariaLabel="Save trace to JSON"
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
<Tooltip title={"Open JSON"}>
|
<Tooltip title={"Open JSON"}>
|
||||||
<Button
|
<Button
|
||||||
variant="text"
|
variant="text"
|
||||||
|
@ -92,6 +136,7 @@ const TracingsView: FC<TraceViewProps> = ({ traces, jsonEditor = false, onDelete
|
||||||
isRoot
|
isRoot
|
||||||
trace={trace}
|
trace={trace}
|
||||||
totalMsec={trace.duration}
|
totalMsec={trace.duration}
|
||||||
|
isExpandedAll={expandedTraces.includes(trace.idValue)}
|
||||||
/>
|
/>
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -21,6 +21,20 @@
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__expand-icon {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 20px;
|
||||||
|
transition: transform 200ms ease-in-out;
|
||||||
|
transform: rotate(-90deg);
|
||||||
|
color: $color-text-secondary;
|
||||||
|
|
||||||
|
&_open {
|
||||||
|
transform: rotate(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__nav {
|
&__nav {
|
||||||
|
|
|
@ -64,6 +64,7 @@ The sandbox cluster installation is running under the constant load generated by
|
||||||
- add an `Export query` button to the graph that saves the result of executing the query in `JSON`. See [this pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/5497).
|
- add an `Export query` button to the graph that saves the result of executing the query in `JSON`. See [this pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/5497).
|
||||||
* FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): add `-vmui.defaultTimezone` flag to set a default timezone. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/5375) and [these docs](https://github.com/VictoriaMetrics/VictoriaMetrics/tree/master/app/vmui#timezone-configuration).
|
* FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): add `-vmui.defaultTimezone` flag to set a default timezone. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/5375) and [these docs](https://github.com/VictoriaMetrics/VictoriaMetrics/tree/master/app/vmui#timezone-configuration).
|
||||||
* FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): include UTC in the timezone selection dropdown for standardized time referencing. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/5375).
|
* FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): include UTC in the timezone selection dropdown for standardized time referencing. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/5375).
|
||||||
|
* FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): add the ability to expand/collapse all tracing entries and download tracing data in .json format. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/5677).
|
||||||
* FEATURE: add [VictoriaMetrics datasource](https://github.com/VictoriaMetrics/grafana-datasource) to docker compose environment. See [this pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/5363).
|
* FEATURE: add [VictoriaMetrics datasource](https://github.com/VictoriaMetrics/grafana-datasource) to docker compose environment. See [this pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/5363).
|
||||||
|
|
||||||
* BUGFIX: properly return the list of matching label names and label values from [`/api/v1/labels`](https://docs.victoriametrics.com/url-examples.html#apiv1labels) and [`/api/v1/label/.../values`](https://docs.victoriametrics.com/url-examples.html#apiv1labelvalues) when the database contains more than `-search.maxUniqueTimeseries` unique [time series](https://docs.victoriametrics.com/keyConcepts.html#time-series) on the selected time range. Previously VictoriaMetrics could return `the number of matching timeseries exceeds ...` error in this case. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/5055).
|
* BUGFIX: properly return the list of matching label names and label values from [`/api/v1/labels`](https://docs.victoriametrics.com/url-examples.html#apiv1labels) and [`/api/v1/label/.../values`](https://docs.victoriametrics.com/url-examples.html#apiv1labelvalues) when the database contains more than `-search.maxUniqueTimeseries` unique [time series](https://docs.victoriametrics.com/keyConcepts.html#time-series) on the selected time range. Previously VictoriaMetrics could return `the number of matching timeseries exceeds ...` error in this case. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/5055).
|
||||||
|
|
Loading…
Reference in a new issue