Skip to content
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

[metrics_transform_processor] Add filtering capabilities matching metric label values for applying changes #3201

Merged

Conversation

hossain-rayhan
Copy link
Contributor

@hossain-rayhan hossain-rayhan commented Apr 21, 2021

Description:
This change adds a new feature which will support to filter metrics and apply changes matching metric names and label values together. Earlier, we were able to filter metrics using only metric names and apply our transforms to all the metrics. This enhanced filtering feature will give a new option to apply changes to a certain set of metrics where we have a match for metric name and specific set of metric labels.

Can you share a use case?
Yes. Using prometheus receiver in Kubernetes, we get same metrics (same name) for containers as well as pods. We can differentiate the metrics using metric label values. In our use cases we need to rename some of the pod level metrics without affecting the container level metrics. Hence, we need an option for applying changes to metircs based on metric names and metric label values together.

Why did you put this change in metricstransformprocessor?
Metrics transform processor already have the insert and update functions. It applies changes to metrics matching the metric name. Its super easy and meaningful to add couple of lines to the filtering options to match metrics using metric label values besides metric names.

Testing:
Wrote Unit tests and tested manually on local machine.

Documentation:
Updated README with proper config examples.

@hossain-rayhan hossain-rayhan requested a review from a team April 21, 2021 20:59
@codecov
Copy link

codecov bot commented Apr 21, 2021

Codecov Report

Merging #3201 (a4d48a5) into main (48eaccc) will decrease coverage by 0.00%.
The diff coverage is 100.00%.

❗ Current head a4d48a5 differs from pull request most recent head 9c29946. Consider uploading reports for the commit 9c29946 to get more accurate results
Impacted file tree graph

@@            Coverage Diff             @@
##             main    #3201      +/-   ##
==========================================
- Coverage   91.92%   91.92%   -0.01%     
==========================================
  Files         493      493              
  Lines       23946    23973      +27     
==========================================
+ Hits        22013    22037      +24     
- Misses       1427     1429       +2     
- Partials      506      507       +1     
Flag Coverage Δ
integration 63.35% <ø> (-0.06%) ⬇️
unit 90.94% <100.00%> (-0.01%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Impacted Files Coverage Δ
processor/metricstransformprocessor/config.go 100.00% <ø> (ø)
processor/metricstransformprocessor/factory.go 98.86% <100.00%> (+0.04%) ⬆️
...stransformprocessor/metrics_transform_processor.go 99.39% <100.00%> (+0.10%) ⬆️
receiver/k8sclusterreceiver/watcher.go 95.29% <0.00%> (-2.36%) ⬇️
processor/groupbytraceprocessor/event.go 95.96% <0.00%> (-0.81%) ⬇️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 48eaccc...9c29946. Read the comment docs.

@bogdandrutu
Copy link
Member

Thanks for this PR @hossain-rayhan, unfortunately we don't want to add new functionality to the metrics transform processor until we change it to use the new pdata instead of the old opencensus

@hossain-rayhan
Copy link
Contributor Author

hossain-rayhan commented Apr 21, 2021

Thanks for this PR @hossain-rayhan, unfortunately we don't want to add new functionality to the metrics transform processor until we change it to use the new pdata instead of the old opencensus

Hi @bogdandrutu, this is a small addition. I understand the concern but this change truely belongs to this metricstransformprocessor. I can give you my word to contribute for changing this part when rewriting the processor. Maybe I can create an issue and assign it to me for this. But for now, this is the fasted path forward for us. Also, you can tag me if we have any open issue for re-writing this processor. Waiting for your opinion. Thanks.

@hossain-rayhan
Copy link
Contributor Author

hossain-rayhan commented Apr 23, 2021

Hi @bogdandrutu, expecting a second thought from you considering my reply on your comment. Let me know if it sounds convincing.

@hossain-rayhan
Copy link
Contributor Author

According to our todays (04/28) Collector SIG meetings decision, we will move forward with this PR. I created an issue to rewrite this part to use OTLP metrics instead of OpenCensus metric type. I will work on this when we plan to rewrite the metricstransformprocessor. Let's review this.

Issue Link to track: #3269
@alolita can you please assign the issue to me?

Thanks @bogdandrutu and @alolita

@tigrannajaryan
Copy link
Member

@bogdandrutu re-assigning to you since you discussed this in SIG meeting.

@hossain-rayhan
Copy link
Contributor Author

Hi @bogdandrutu its blocking our release. A review would be highly appreciated.

@@ -88,15 +91,18 @@ func (f internalFilterStrict) getSubexpNames() []string {
}

type internalFilterRegexp struct {
include *regexp.Regexp
include *regexp.Regexp
matchLabels map[string]string
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
matchLabels map[string]string
matchLabels map[string]*regexp.Regexp

Presuming that this internalFilterRegexp is re-used, it would be better to transform the label matching map values to *regexp.Regexp once up-front rather than every time we call labelMatched().

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.

keyFound = true
for _, timeseries := range metric.Timeseries {
if isRegexp {
re := regexp.MustCompile(value)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be done in the outer loop, not multiple times per filtered label. Ideally, as mentioned above, it would be done once on initialization and re-used.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.

Comment on lines 424 to 427
for idx, label := range metric.MetricDescriptor.LabelKeys {
if label.Key != key {
continue
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can the same label key appear multiple times? If not, it might make sense to build a map from label keys to indexes once at the top of this function and look up indexes directly rather than iterating over the label keys array for every filtered label.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically it won't repeat. But as its not a map, we cannot garuntee I think.

}

// if a label-key is not found and the label-value is non-empty, return false
if !keyFound && !(value == "" || isEmptyExp(value)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this logic matches the description in the comment. It looks like a non-empty label value that is matched by a regexp that could also match an empty label value will hit this condition. What is intended to be tested here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have two different use case here. Say, in our config file we have the following labels to match,

  1. {"container": "my-container"} where the value of the key container is non-empty.
  2. {"container": ""} where the value of the key container is empty-string.

For first case, if we don't find a key, we return false. But for the second case, if we don't find the key, we need to return true which is expected.

As we don't have a way to make sure a given key is not present, I wrote this logic as alternative. If we want to confirm that a given label is not present, we set the value as empty-string.

Hope it helps to explain.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated the comment to make it more clear.

Copy link
Member

@mxiamxia mxiamxia left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@hossain-rayhan
Copy link
Contributor Author

@alolita can you please add the ready_to_get_merged tag on this. Got approval from Anthony and Min. Thanks.

if timeseries.LabelValues[idx].Value != value {
return false
}
break
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because of this break, isn't only the first time series checked?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. That's I checked for. If the first timeseries matches the label, we will add the metric.

Copy link
Contributor

@anuraaga anuraaga May 5, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better to be explicit about that than use a loop then, loop is for looping

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please rephrase for me? I don't think I understand what you meant.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you only want to check the first one can't you use metric.Timeseries[0]?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh I see. Makes sense. Will update.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated, thanks.

@hossain-rayhan
Copy link
Contributor Author

Hi @anuraaga I need your approval. Would you please have another look. Thanks.

@hossain-rayhan
Copy link
Contributor Author

@tigrannajaryan can we get this merged! Thanks.

@tigrannajaryan
Copy link
Member

@bogdandrutu what's the resolution on this? I was not present in the SIG meeting where you discussed this (the meeting notes say that it was discussed, but doesn't tell what was decided).

@hossain-rayhan
Copy link
Contributor Author

@bogdandrutu We need to get this merged today (05/10). Can you please merge this. Thanks.
cc: @alolita

@hossain-rayhan hossain-rayhan force-pushed the metrics_transform_filter branch from 9c29946 to 894f54e Compare May 10, 2021 15:51
@hossain-rayhan
Copy link
Contributor Author

I just rebased the code so that it passes windows/trace unit tests.

@bogdandrutu bogdandrutu merged commit abea898 into open-telemetry:main May 10, 2021
@jmacd
Copy link
Contributor

jmacd commented May 10, 2021

👍

alexperez52 referenced this pull request in open-o11y/opentelemetry-collector-contrib Aug 18, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants