-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Summary: Display thresholds values even when not in summaryTrendStats #4698
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -839,7 +839,7 @@ function renderThresholdResults( | |
| : formatter.decorate(failMark, 'red'); | ||
|
|
||
| const sourceText = formatter.decorate( | ||
| `'${threshold.source}'`, | ||
| `'${threshold.source.trim()}'`, | ||
| 'white', | ||
| ); | ||
|
|
||
|
|
@@ -941,13 +941,16 @@ function renderMetricValueForThresholds( | |
| info, | ||
| formatter, | ||
| ) { | ||
| const {trendStats, trendCols, nonTrendValues, nonTrendExtras} = info; | ||
| const {trendStats, trendCols, trendKeys, nonTrendValues, nonTrendExtras} = info; | ||
| const thresholdAgg = threshold.source.split(/[=><]/)[0].trim(); | ||
|
|
||
| let value; | ||
| switch (metric.type) { | ||
| case 'trend': | ||
| value = trendCols[metric.name][trendStats.indexOf(thresholdAgg)] | ||
| const trendStatIndex = trendStats.indexOf(thresholdAgg); | ||
| value = (trendStatIndex !== -1) | ||
| ? trendCols[metric.name]?.[trendStatIndex] | ||
| : trendKeys[metric.name]?.[thresholdAgg]; | ||
| break; | ||
| case 'counter': | ||
| value = (thresholdAgg === 'count') | ||
|
|
@@ -1042,6 +1045,7 @@ function renderTrendValue(value, stat, metric, options) { | |
| * @property {Object} nonTrendValues - The non-trend metric values. | ||
| * @property {Object} nonTrendExtras - The non-trend metric extras. | ||
| * @property {Object} trendCols - The trend columns. | ||
| * @property {Object} trendKeys - The trend keys (values that aren't included within `trendStats`). | ||
| * @property {number[]} trendColMaxLens - The trend column maximum lengths. | ||
| * @property {number} numTrendColumns - The number of trend columns. | ||
| * @property {string[]} trendStats - The trend statistics. | ||
|
|
@@ -1061,6 +1065,10 @@ function computeSummaryInfo(metrics, renderContext, options) { | |
| const nonTrendExtras = {}; | ||
| const trendCols = {}; | ||
|
|
||
| // While "trendCols" contain the values for each "trendStats" aggregation (e.g. p(90) as a sorted array, | ||
| // "trendKeys" is used to store specific aggregation values that aren't part of "trendStats"; mainly for thresholds. | ||
| const trendKeys = {}; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
It doesn't sound right without applying in-depth thinking. Because Trend is itself a stat. Wouldn't it be easier to just use the concept of Threshold?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure 🤷🏻 The code/data structure is quite generic, and while it's true that this is now used for threshold, it could technically be used for anything else, so I tried to keep it consistent with the existing "conventions" (if any) - one data structure holds stats identified by "columns", and this one by "keys" 😅 Overall, that code has many aspects that could be improved (there's already some |
||
|
|
||
| let maxNameWidth = 0; | ||
| let maxNonTrendValueLen = 0; | ||
| let nonTrendExtraMaxLens = []; // FIXME: "lens"? | ||
|
|
@@ -1082,6 +1090,13 @@ function computeSummaryInfo(metrics, renderContext, options) { | |
| maxNameWidth = Math.max(maxNameWidth, strWidth(displayName)); | ||
|
|
||
| if (metric.type === 'trend') { | ||
| const keys = Object.keys(metric.values).reduce((acc, key) => { | ||
| if (!trendStats.includes(key)) { | ||
| acc[key] = renderTrendValue(metric.values[key], key, metric, options); | ||
| } | ||
| return acc; | ||
| }, {}); | ||
|
|
||
| const cols = trendStats.map((stat) => | ||
| renderTrendValue(metric.values[stat], stat, metric, options), | ||
| ); | ||
|
|
@@ -1094,6 +1109,7 @@ function computeSummaryInfo(metrics, renderContext, options) { | |
| ); | ||
| }); | ||
| trendCols[name] = cols; | ||
| trendKeys[name] = keys; | ||
| } else { | ||
| const values = nonTrendMetricValueForSum( | ||
| metric, | ||
|
|
@@ -1127,6 +1143,7 @@ function computeSummaryInfo(metrics, renderContext, options) { | |
| nonTrendExtras, | ||
| trendCols, | ||
| trendColMaxLens, | ||
| trendKeys, | ||
| numTrendColumns, | ||
| trendStats, | ||
| maxNonTrendValueLen, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| package summary | ||
|
|
||
| import ( | ||
| "testing" | ||
|
|
||
| "github.com/stretchr/testify/assert" | ||
| ) | ||
|
|
||
| func Test_extractPercentileThresholdSource(t *testing.T) { | ||
| t.Parallel() | ||
|
|
||
| tests := map[string]struct { | ||
| source string | ||
| expAgg string | ||
| expPercentile float64 | ||
| expIsPercentile bool | ||
| }{ | ||
| "empty source": { | ||
| source: "", expAgg: "", expPercentile: 0, expIsPercentile: false, | ||
| }, | ||
| "invalid source": { | ||
| source: "rate<<<<0.01", expAgg: "", expPercentile: 0, expIsPercentile: false, | ||
| }, | ||
| "rate threshold": { | ||
| source: "rate<0.01", expAgg: "", expPercentile: 0, expIsPercentile: false, | ||
| }, | ||
| "integer percentile": { | ||
| source: "p(95)<200", expAgg: "p(95)", expPercentile: 95, expIsPercentile: true, | ||
| }, | ||
| "floating-point percentile": { | ||
| source: "p(99.9)<=200", expAgg: "p(99.9)", expPercentile: 99.9, expIsPercentile: true, | ||
| }, | ||
| "percentile with whitespaces": { | ||
| source: " p(99.9) < 200 ", expAgg: "p(99.9)", expPercentile: 99.9, expIsPercentile: true, | ||
| }, | ||
| } | ||
| for name, tc := range tests { | ||
| t.Run(name, func(t *testing.T) { | ||
| t.Parallel() | ||
| agg, percentile, isPercentile := extractPercentileThresholdSource(tc.source) | ||
| assert.Equal(t, tc.expAgg, agg) | ||
| assert.Equal(t, tc.expPercentile, percentile) | ||
| assert.Equal(t, tc.expIsPercentile, isPercentile) | ||
| }) | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't directly related, but it's so small that I prefer to add it as part of this PR. It's not a huge deal, but I think it's nice if we trim the threshold's source before displaying it in the summary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the reason for trimming here? If the source includes spaces why we don't interpret it as intentional?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm a bit confused now; is there any reason for doing so?
To me, it feels like when you mistakenly add a leading or trailing whitespace in an input designed to write your name or similar.
Looking at the threshold syntax expression (ref), I can see for instance users adding (or not) whitespaces before/after the
<operator>, for readability, but not why doing so at the beginning/end of the threshold definition.