mirror of
https://github.com/prometheus/prometheus.git
synced 2026-06-09 00:22:19 -04:00
Merge pull request #17334 from roidelapluie/roidelapluie/explain
UI: Add smoothed/anchored in explain
This commit is contained in:
commit
dca3289c28
10 changed files with 255 additions and 5 deletions
|
|
@ -84,6 +84,8 @@ func translateAST(node parser.Expr) any {
|
|||
"matchers": translateMatchers(vs.LabelMatchers),
|
||||
"timestamp": vs.Timestamp,
|
||||
"startOrEnd": getStartOrEnd(vs.StartOrEnd),
|
||||
"anchored": vs.Anchored,
|
||||
"smoothed": vs.Smoothed,
|
||||
}
|
||||
case *parser.SubqueryExpr:
|
||||
return map[string]any{
|
||||
|
|
@ -124,6 +126,8 @@ func translateAST(node parser.Expr) any {
|
|||
"matchers": translateMatchers(n.LabelMatchers),
|
||||
"timestamp": n.Timestamp,
|
||||
"startOrEnd": getStartOrEnd(n.StartOrEnd),
|
||||
"anchored": n.Anchored,
|
||||
"smoothed": n.Smoothed,
|
||||
}
|
||||
}
|
||||
panic("unsupported node type")
|
||||
|
|
|
|||
|
|
@ -171,9 +171,22 @@ const SelectorExplainView: FC<SelectorExplainViewProps> = ({ node }) => {
|
|||
<Text fz="sm">
|
||||
{node.type === nodeType.vectorSelector ? (
|
||||
<>
|
||||
This node selects the latest (non-stale) sample value within the
|
||||
last{" "}
|
||||
<span className="promql-code promql-duration">{lookbackDelta}</span>
|
||||
This node {node.smoothed ? "smooths the value" : "selects the latest"}{" "}
|
||||
{node.anchored || node.smoothed ? "" : "non-stale "}
|
||||
{!node.smoothed && (
|
||||
<>
|
||||
sample value within the last{" "}
|
||||
<span className="promql-code promql-duration">{lookbackDelta}</span>
|
||||
</>
|
||||
)}
|
||||
{node.smoothed && (
|
||||
<>
|
||||
using <code>smoothed</code> mode (linear interpolation with nearest
|
||||
points within{" "}
|
||||
<span className="promql-code promql-duration">{lookbackDelta}</span>{" "}
|
||||
before and after execution timestamp, ignoring staleness markers)
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
|
|
@ -182,6 +195,21 @@ const SelectorExplainView: FC<SelectorExplainViewProps> = ({ node }) => {
|
|||
{formatPrometheusDuration(node.range)}
|
||||
</span>{" "}
|
||||
of data going backward from the evaluation timestamp
|
||||
{node.anchored && (
|
||||
<>
|
||||
{" "}
|
||||
using <code>anchored</code> mode (includes first sample before or at
|
||||
start boundary, and last sample of the range at end boundary, ignoring staleness markers)
|
||||
</>
|
||||
)}
|
||||
{node.smoothed && (
|
||||
<>
|
||||
{" "}
|
||||
using <code>smoothed</code> mode (applies linear
|
||||
interpolation at the boundaries using nearest samples
|
||||
before and after boundaries, ignoring staleness markers)
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{node.timestamp !== null ? (
|
||||
|
|
|
|||
|
|
@ -56,6 +56,8 @@ const Graph: FC<GraphProps> = ({
|
|||
offset: node.offset,
|
||||
timestamp: node.timestamp,
|
||||
startOrEnd: node.startOrEnd,
|
||||
anchored: node.anchored,
|
||||
smoothed: node.smoothed,
|
||||
}
|
||||
: node
|
||||
);
|
||||
|
|
|
|||
|
|
@ -94,6 +94,8 @@ const LabelsExplorer: FC<LabelsExplorerProps> = ({
|
|||
offset: 0,
|
||||
timestamp: null,
|
||||
startOrEnd: null,
|
||||
anchored: false,
|
||||
smoothed: false,
|
||||
};
|
||||
|
||||
// Based on the selected pool (if any), load the list of targets.
|
||||
|
|
|
|||
|
|
@ -130,12 +130,16 @@ const TreeNode: FC<{
|
|||
// more cheaply. E.g. 'foo[7w]' can be expensive to fully fetch, but wrapping it
|
||||
// in 'last_over_time(foo[7w])' is cheaper and also gives us all the info we
|
||||
// need (number of series and labels).
|
||||
// Strip anchored/smoothed modifiers when wrapping, as last_over_time doesn't support them.
|
||||
let queryNode = node;
|
||||
if (queryNode.type === nodeType.matrixSelector) {
|
||||
const matrixNode = { ...queryNode };
|
||||
matrixNode.anchored = false;
|
||||
matrixNode.smoothed = false;
|
||||
queryNode = {
|
||||
type: nodeType.call,
|
||||
func: functionSignatures["last_over_time"],
|
||||
args: [node],
|
||||
args: [matrixNode],
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -147,6 +147,8 @@ export interface MatrixSelector {
|
|||
offset: number;
|
||||
timestamp: number | null;
|
||||
startOrEnd: StartOrEnd;
|
||||
anchored: boolean;
|
||||
smoothed: boolean;
|
||||
}
|
||||
|
||||
export interface Subquery {
|
||||
|
|
@ -187,6 +189,8 @@ export interface VectorSelector {
|
|||
offset: number;
|
||||
timestamp: number | null;
|
||||
startOrEnd: StartOrEnd;
|
||||
anchored: boolean;
|
||||
smoothed: boolean;
|
||||
}
|
||||
|
||||
export interface Placeholder {
|
||||
|
|
|
|||
|
|
@ -131,6 +131,18 @@ const formatSelector = (
|
|||
]
|
||||
</>
|
||||
)}
|
||||
{node.anchored && (
|
||||
<>
|
||||
{" "}
|
||||
<span className="promql-keyword">anchored</span>
|
||||
</>
|
||||
)}
|
||||
{node.smoothed && (
|
||||
<>
|
||||
{" "}
|
||||
<span className="promql-keyword">smoothed</span>
|
||||
</>
|
||||
)}
|
||||
{formatAtAndOffset(node.timestamp, node.startOrEnd, node.offset)}
|
||||
</>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -56,13 +56,18 @@ const serializeSelector = (node: VectorSelector | MatrixSelector): string => {
|
|||
node.type === nodeType.matrixSelector
|
||||
? `[${formatPrometheusDuration(node.range)}]`
|
||||
: "";
|
||||
const extendedAttribute = node.anchored
|
||||
? " anchored"
|
||||
: node.smoothed
|
||||
? " smoothed"
|
||||
: "";
|
||||
const atAndOffset = serializeAtAndOffset(
|
||||
node.timestamp,
|
||||
node.startOrEnd,
|
||||
node.offset
|
||||
);
|
||||
|
||||
return `${!metricExtendedCharset ? metricName : ""}${matchers.length > 0 ? `{${matchers.join(",")}}` : ""}${range}${atAndOffset}`;
|
||||
return `${!metricExtendedCharset ? metricName : ""}${matchers.length > 0 ? `{${matchers.join(",")}}` : ""}${range}${extendedAttribute}${atAndOffset}`;
|
||||
};
|
||||
|
||||
const serializeNode = (
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@ describe("serializeNode and formatNode", () => {
|
|||
offset: 0,
|
||||
timestamp: null,
|
||||
startOrEnd: null,
|
||||
anchored: false,
|
||||
smoothed: false,
|
||||
},
|
||||
output: "metric_name",
|
||||
},
|
||||
|
|
@ -40,6 +42,8 @@ describe("serializeNode and formatNode", () => {
|
|||
offset: 0,
|
||||
timestamp: null,
|
||||
startOrEnd: null,
|
||||
anchored: false,
|
||||
smoothed: false,
|
||||
},
|
||||
output:
|
||||
'metric_name{label1="value1",label2!="value2",label3=~"value3",label4!~"value4"}',
|
||||
|
|
@ -52,6 +56,8 @@ describe("serializeNode and formatNode", () => {
|
|||
offset: 60000,
|
||||
timestamp: null,
|
||||
startOrEnd: null,
|
||||
anchored: false,
|
||||
smoothed: false,
|
||||
},
|
||||
output: "metric_name offset 1m",
|
||||
},
|
||||
|
|
@ -63,6 +69,8 @@ describe("serializeNode and formatNode", () => {
|
|||
offset: -60000,
|
||||
timestamp: null,
|
||||
startOrEnd: "start",
|
||||
anchored: false,
|
||||
smoothed: false,
|
||||
},
|
||||
output: "metric_name @ start() offset -1m",
|
||||
},
|
||||
|
|
@ -74,6 +82,8 @@ describe("serializeNode and formatNode", () => {
|
|||
offset: -60000,
|
||||
timestamp: null,
|
||||
startOrEnd: "end",
|
||||
anchored: false,
|
||||
smoothed: false,
|
||||
},
|
||||
output: "metric_name @ end() offset -1m",
|
||||
},
|
||||
|
|
@ -85,6 +95,8 @@ describe("serializeNode and formatNode", () => {
|
|||
offset: -60000,
|
||||
timestamp: 123000,
|
||||
startOrEnd: null,
|
||||
anchored: false,
|
||||
smoothed: false,
|
||||
},
|
||||
output: "metric_name @ 123.000 offset -1m",
|
||||
},
|
||||
|
|
@ -98,6 +110,8 @@ describe("serializeNode and formatNode", () => {
|
|||
offset: 60000,
|
||||
timestamp: null,
|
||||
startOrEnd: null,
|
||||
anchored: false,
|
||||
smoothed: false,
|
||||
},
|
||||
output: "metric_name offset 1m",
|
||||
},
|
||||
|
|
@ -110,6 +124,8 @@ describe("serializeNode and formatNode", () => {
|
|||
offset: 0,
|
||||
timestamp: null,
|
||||
startOrEnd: null,
|
||||
anchored: false,
|
||||
smoothed: false,
|
||||
},
|
||||
output: 'metric_name{label1="\\"\\"\\""}',
|
||||
},
|
||||
|
|
@ -129,6 +145,8 @@ describe("serializeNode and formatNode", () => {
|
|||
offset: 600000,
|
||||
timestamp: null,
|
||||
startOrEnd: null,
|
||||
anchored: false,
|
||||
smoothed: false,
|
||||
},
|
||||
output:
|
||||
'metric_name{label1="value1",label2!="value2",label3=~"value3",label4!~"value4"}[5m] offset 10m',
|
||||
|
|
@ -142,6 +160,8 @@ describe("serializeNode and formatNode", () => {
|
|||
offset: -600000,
|
||||
timestamp: 123000,
|
||||
startOrEnd: null,
|
||||
anchored: false,
|
||||
smoothed: false,
|
||||
},
|
||||
output: "metric_name[5m] @ 123.000 offset -10m",
|
||||
},
|
||||
|
|
@ -154,6 +174,8 @@ describe("serializeNode and formatNode", () => {
|
|||
offset: -600000,
|
||||
timestamp: null,
|
||||
startOrEnd: "start",
|
||||
anchored: false,
|
||||
smoothed: false,
|
||||
},
|
||||
output: "metric_name[5m] @ start() offset -10m",
|
||||
},
|
||||
|
|
@ -167,11 +189,164 @@ describe("serializeNode and formatNode", () => {
|
|||
offset: 0,
|
||||
timestamp: null,
|
||||
startOrEnd: null,
|
||||
anchored: false,
|
||||
smoothed: false,
|
||||
},
|
||||
output:
|
||||
'{label1="value1"}',
|
||||
},
|
||||
|
||||
// Anchored and smoothed modifiers.
|
||||
{
|
||||
node: {
|
||||
type: nodeType.vectorSelector,
|
||||
name: "metric_name",
|
||||
matchers: [],
|
||||
offset: 0,
|
||||
timestamp: null,
|
||||
startOrEnd: null,
|
||||
anchored: true,
|
||||
smoothed: false,
|
||||
},
|
||||
output: "metric_name anchored",
|
||||
},
|
||||
{
|
||||
node: {
|
||||
type: nodeType.vectorSelector,
|
||||
name: "metric_name",
|
||||
matchers: [],
|
||||
offset: 0,
|
||||
timestamp: null,
|
||||
startOrEnd: null,
|
||||
anchored: false,
|
||||
smoothed: true,
|
||||
},
|
||||
output: "metric_name smoothed",
|
||||
},
|
||||
{
|
||||
node: {
|
||||
type: nodeType.vectorSelector,
|
||||
name: "metric_name",
|
||||
matchers: [],
|
||||
offset: 60000,
|
||||
timestamp: null,
|
||||
startOrEnd: null,
|
||||
anchored: true,
|
||||
smoothed: false,
|
||||
},
|
||||
output: "metric_name anchored offset 1m",
|
||||
},
|
||||
{
|
||||
node: {
|
||||
type: nodeType.vectorSelector,
|
||||
name: "metric_name",
|
||||
matchers: [],
|
||||
offset: 60000,
|
||||
timestamp: null,
|
||||
startOrEnd: null,
|
||||
anchored: false,
|
||||
smoothed: true,
|
||||
},
|
||||
output: "metric_name smoothed offset 1m",
|
||||
},
|
||||
{
|
||||
node: {
|
||||
type: nodeType.vectorSelector,
|
||||
name: "metric_name",
|
||||
matchers: [],
|
||||
offset: 0,
|
||||
timestamp: 123000,
|
||||
startOrEnd: null,
|
||||
anchored: true,
|
||||
smoothed: false,
|
||||
},
|
||||
output: "metric_name anchored @ 123.000",
|
||||
},
|
||||
{
|
||||
node: {
|
||||
type: nodeType.matrixSelector,
|
||||
name: "metric_name",
|
||||
matchers: [],
|
||||
range: 300000,
|
||||
offset: 0,
|
||||
timestamp: null,
|
||||
startOrEnd: null,
|
||||
anchored: true,
|
||||
smoothed: false,
|
||||
},
|
||||
output: "metric_name[5m] anchored",
|
||||
},
|
||||
{
|
||||
node: {
|
||||
type: nodeType.matrixSelector,
|
||||
name: "metric_name",
|
||||
matchers: [],
|
||||
range: 300000,
|
||||
offset: 0,
|
||||
timestamp: null,
|
||||
startOrEnd: null,
|
||||
anchored: false,
|
||||
smoothed: true,
|
||||
},
|
||||
output: "metric_name[5m] smoothed",
|
||||
},
|
||||
{
|
||||
node: {
|
||||
type: nodeType.matrixSelector,
|
||||
name: "metric_name",
|
||||
matchers: [],
|
||||
range: 300000,
|
||||
offset: 600000,
|
||||
timestamp: null,
|
||||
startOrEnd: null,
|
||||
anchored: true,
|
||||
smoothed: false,
|
||||
},
|
||||
output: "metric_name[5m] anchored offset 10m",
|
||||
},
|
||||
{
|
||||
node: {
|
||||
type: nodeType.matrixSelector,
|
||||
name: "metric_name",
|
||||
matchers: [],
|
||||
range: 300000,
|
||||
offset: 600000,
|
||||
timestamp: null,
|
||||
startOrEnd: null,
|
||||
anchored: false,
|
||||
smoothed: true,
|
||||
},
|
||||
output: "metric_name[5m] smoothed offset 10m",
|
||||
},
|
||||
{
|
||||
node: {
|
||||
type: nodeType.matrixSelector,
|
||||
name: "metric_name",
|
||||
matchers: [],
|
||||
range: 300000,
|
||||
offset: 0,
|
||||
timestamp: 123000,
|
||||
startOrEnd: null,
|
||||
anchored: true,
|
||||
smoothed: false,
|
||||
},
|
||||
output: "metric_name[5m] anchored @ 123.000",
|
||||
},
|
||||
{
|
||||
node: {
|
||||
type: nodeType.matrixSelector,
|
||||
name: "metric_name",
|
||||
matchers: [],
|
||||
range: 300000,
|
||||
offset: 600000,
|
||||
timestamp: 123000,
|
||||
startOrEnd: null,
|
||||
anchored: false,
|
||||
smoothed: true,
|
||||
},
|
||||
output: "metric_name[5m] smoothed @ 123.000 offset 10m",
|
||||
},
|
||||
|
||||
// Aggregations.
|
||||
{
|
||||
node: {
|
||||
|
|
@ -309,6 +484,8 @@ describe("serializeNode and formatNode", () => {
|
|||
offset: 0,
|
||||
timestamp: null,
|
||||
startOrEnd: null,
|
||||
anchored: false,
|
||||
smoothed: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
@ -681,6 +858,8 @@ describe("serializeNode and formatNode", () => {
|
|||
startOrEnd: null,
|
||||
timestamp: null,
|
||||
type: nodeType.vectorSelector,
|
||||
anchored: false,
|
||||
smoothed: false,
|
||||
},
|
||||
grouping: ["a", "ä"],
|
||||
op: aggregationType.sum,
|
||||
|
|
@ -714,6 +893,8 @@ describe("serializeNode and formatNode", () => {
|
|||
startOrEnd: null,
|
||||
timestamp: null,
|
||||
type: nodeType.vectorSelector,
|
||||
anchored: false,
|
||||
smoothed: false,
|
||||
},
|
||||
grouping: ["d", "ä"],
|
||||
op: aggregationType.sum,
|
||||
|
|
@ -744,6 +925,8 @@ describe("serializeNode and formatNode", () => {
|
|||
startOrEnd: null,
|
||||
timestamp: null,
|
||||
type: nodeType.vectorSelector,
|
||||
anchored: false,
|
||||
smoothed: false,
|
||||
},
|
||||
type: nodeType.parenExpr,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -118,6 +118,8 @@ describe("nodeValueType", () => {
|
|||
offset: 0,
|
||||
timestamp: null,
|
||||
startOrEnd: null,
|
||||
anchored: false,
|
||||
smoothed: false,
|
||||
},
|
||||
rhs: { type: nodeType.numberLiteral, val: "1" },
|
||||
matching: null,
|
||||
|
|
@ -138,6 +140,8 @@ describe("nodeValueType", () => {
|
|||
offset: 0,
|
||||
timestamp: null,
|
||||
startOrEnd: null,
|
||||
anchored: false,
|
||||
smoothed: false,
|
||||
},
|
||||
rhs: {
|
||||
type: nodeType.vectorSelector,
|
||||
|
|
@ -146,6 +150,8 @@ describe("nodeValueType", () => {
|
|||
offset: 0,
|
||||
timestamp: null,
|
||||
startOrEnd: null,
|
||||
anchored: false,
|
||||
smoothed: false,
|
||||
},
|
||||
matching: null,
|
||||
bool: false,
|
||||
|
|
|
|||
Loading…
Reference in a new issue