-
Notifications
You must be signed in to change notification settings - Fork 8.2k
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
[Infra Monitoring UI] Add event module filter to metrics table #133872
[Infra Monitoring UI] Add event module filter to metrics table #133872
Conversation
@elasticmachine merge upstream |
@elasticmachine merge upstream |
Pinging @elastic/infra-monitoring-ui (Team:Infra Monitoring UI) |
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.
Nice! Thanks for adding the tests as well!
I would still want @weltenwort to approve as well on the DSL query we end up with, so we don't risk breaking future usages.
@@ -81,6 +87,25 @@ export function useContainerMetricsTable({ | |||
}; | |||
} | |||
|
|||
function addEventModuleFilter( |
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.
function addEventModuleFilter( | |
function addKubernetesModuleFilter( |
I feel this would be more clear about which kind of event we want to filter on?
@elasticmachine merge upstream |
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.
Thanks indeed for adding the hook unit tests.
{ | ||
...filterClauseDsl, | ||
}, |
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.
When typeof filterClauseDsl === 'undefined'
, then this would put an empty object into the filter
array. How about we avoid adding it in the first place?
{ | |
...filterClauseDsl, | |
}, | |
...(filterClauseDsl != null ? [filterClauseDsl] : []), |
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.
good catch! I'll just change your suggestion for an array to an object
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.
but we're merging into an array? 🤔
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.
ah, I see now. At first glance I thought you were adding it to the filter array. You are actually concatenating. Got it
...ublic/components/infrastructure_node_metrics_tables/container/use_container_metrics_table.ts
Outdated
Show resolved
Hide resolved
filter: [ | ||
{ | ||
term: { | ||
'event.module': 'system', |
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 is closely coupled to the knowledge that's encoded in hostMetricsMap
above. Would there be a reasonable way to automatically derive this from the map? Or at least co-locate this magic string so it doesn't diverge?
What if, instead of filtering for the module, we filtered for the presence of the metrics fields from hostMetricsMap
directly? It would me more parsimonious than introducing another constant.
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 agree with your concern, and we thought about this but the risk is that other documents populate the same fields and are they then the "same" value or not? See #131308 (comment)
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.
Valid point. But the metric field names are rather specific, so I don't imagine the likelihood would be very high. Or do you have a specific confounding source in mind? Additionally, the confounding source could also erroneously set the module name.
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.
In the ticket I linked, Carlos has data from the APM agent that has the same fields (not all, but some) for the Host table (not sure if the values match what the system module would report though) but in this case it doesn't report the module "wrong". So I don't know :D I don't have a good answer here
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.
hm, yes... for now the module filter seems like the simplest workaround. still, could we co-locate that knowledge somehow with the metric field names, @crespocarlos?
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.
sure! I'll see how that can be improved.
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.
The refactoring was a slightly bigger than I expected, but I think now the knowledge about how the query will need is centralized
@elasticmachine merge upstream |
buildkite test this |
@elasticmachine merge upstream |
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.
Thanks for combining the metrics descriptors 👏 I left a few more questions below.
const [containerMetricsOptions, setContainerMetricsOptions] = useState<MetricsExplorerOptions>(); | ||
|
||
useEffect(() => { | ||
if (!containerMetricsOptions) { | ||
const { options } = metricsToApiOptions(containerMetricsQueryConfig, filterClauseDsl); | ||
setContainerMetricsOptions(options); | ||
} | ||
}, [filterClauseDsl, containerMetricsOptions]); | ||
|
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.
Does this have to be mutable state? Or could this be computed state (e.g. with useMemo
)?
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.
hmm, indeed. I think when I first decided to go with useEffect
I had dependencies that were mutable. Despite filterClauseDsl
being passed via props, it is predefined on the client and never changes. I guess we can go with useMemo
here. There is no reason to delay the first render here.
.../public/components/infrastructure_node_metrics_tables/shared/hooks/metrics_to_api_options.ts
Show resolved
Hide resolved
expect(options).toEqual({ | ||
aggregation: 'avg', | ||
groupBy: 'test.node.type.groupingField', | ||
filterQuery: JSON.stringify({ bool: { filter: [{ term: { 'event.module': 'test' } }, {}] } }), |
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.
We don't want this empty object in here, right?
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.
yup. it's outdated
@@ -5,44 +5,59 @@ | |||
* 2.0. | |||
*/ | |||
|
|||
import type { MetricsMap } from './metrics_to_api_options'; | |||
import type { MetricsExplorerOptions } from '../../../../pages/metrics/metrics_explorer/hooks/use_metrics_explorer_options'; | |||
import { createMetricByFieldLookup, MetricsQueryOptions } from './metrics_to_api_options'; |
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.
Do we want to let the compiler auto-sort and deduplicate these imports (and others in the PR)?
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 was too optmistic that vscode would've added the new import to the existing list
const fields = Object.keys(metricMap) as T[]; | ||
const metrics = Object.values(metricMap) as Array<NodeMetricsExplorerOptionsMetric<T>>; |
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.
Nit: These assertions could be even safer if we let the compiler derive the types, for example:
const fields = Object.keys(metricMap) as T[]; | |
const metrics = Object.values(metricMap) as Array<NodeMetricsExplorerOptionsMetric<T>>; | |
const fields = Object.keys(metricMap) as Array<keyof typeof metricMap>; | |
const metrics = Object.values(metricMap) as ObjectValues<typeof metricMap>; |
where ObjectValues<T>
is defined in
export type ObjectValues<T> = Array<T[keyof T]>; |
What do you think?
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.
nice! seems neat
|
||
const filterClauseWithEventModuleFilter = { | ||
bool: { | ||
filter: [{ term: { 'event.module': 'kubernetes' } }, { ...filterClauseDsl }], |
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 expected value might be outdated.
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.
it sure is!
'kubernetes.container.memory.usage.bytes': { | ||
aggregation: 'avg', | ||
field: 'kubernetes.container.memory.usage.bytes', | ||
groupByField: 'kubernetes.pod.name', |
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.
Could this incorrectly mix the metrics of multiple containers since a pod might have multiple containers? Would kubernetes.container.id
be more suitable?
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.
Good catch. It should indeed be container.id
.
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.
LGTM 👍🏼
return { | ||
bool: { | ||
filter: !!filterClauseDsl ? [sourceFilter, filterClauseDsl] : [sourceFilter], | ||
}, | ||
}; |
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.
return { | |
bool: { | |
filter: !!filterClauseDsl ? [sourceFilter, filterClauseDsl] : [sourceFilter], | |
}, | |
}; | |
const filter = [sourceFilter]; | |
if (filterClauseDsl) { | |
filter.push(filterClauseDsl); | |
} | |
return { | |
bool: { | |
filter, | |
}, | |
}; |
NIt: I'm just allergic to ternaries for the sake of readability.
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 tend to agree with that. But here it doesn't seem to be harming the readability. But it's my personal opinion. I could apply your suggestion if it's hurting you eyes :)!
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.
One could argue that mutating the list can be considered an eyesore too 😉
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.
The mutation hurts the soul, not the eyes :D
I'm fine either way @crespocarlos, as long as I get to complain about ternaries :)
import { metricsToApiOptions, useInfrastructureNodeMetrics } from '../shared'; | ||
import { createMetricByFieldLookup } from '../shared/hooks/metrics_to_api_options'; |
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.
Nit: Maybe export/import this via ../shared
instead?
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.
totally!
buildkite test this |
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.
LGTM, nicely done
import { metricsToApiOptions, useInfrastructureNodeMetrics } from '../shared'; | ||
import { createMetricByFieldLookup } from '../shared/hooks/metrics_to_api_options'; |
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.
tiny nit: looks like this could be imported from ../shared
like in the sibling hook variants 😇
buildkite test this |
💚 Build SucceededMetrics [docs]Async chunks
History
To update your PR or re-run it, just comment with: |
Summary
this PR fixes #131308 by adding
event.module
filter to metrics table queries. For hosts it will filter bysystem
and for pods and containers bykubernetes
. This will filter out some undesired rows without relevant metric data.Example
Without the filter:
With filter:
Checklist
For maintainers