From 83c463517a544415e8b50aa3303efad5fc3f4841 Mon Sep 17 00:00:00 2001 From: Daniel Kontsek Date: Tue, 15 Apr 2025 13:58:13 +0200 Subject: [PATCH] Fix metrics filter with colon `:` in query value Example of a metric filter affected by this: ``` cloudsql.googleapis.com/database:resource.labels.database_id="project-id:database-name" ``` + Added/updated tests + Updated README with this example and a note about quoting special characters Signed-off-by: Daniel Kontsek --- README.md | 6 ++++-- utils/utils.go | 6 +++--- utils/utils_test.go | 20 ++++++++++++++++++++ 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 0d04e053..ec330d6c 100644 --- a/README.md +++ b/README.md @@ -165,7 +165,7 @@ Example: \ The `filter_query` will be applied to a final metrics API query when querying for metric data. You can read more about the metric API filter options in GCPs documentation https://cloud.google.com/monitoring/api/v3/filters -The final query sent to the metrics API already includes filters for project and metric type. Each applicable `filter_query` will be appended to the query with an AND +The final query sent to the metrics API already includes filters for project and metric type. Each applicable `filter_query` will be appended to the query with an `AND`. String filter values that contain special characters (e.g. `:` colon) must be quoted with quotation marks `"`. Please always check logs for potential syntax errors from GCP. Full example ``` @@ -173,8 +173,10 @@ stackdriver_exporter \ --google.project-ids=my-test-project \ --monitoring.metrics-prefixes='pubsub.googleapis.com/subscription' \ --monitoring.metrics-prefixes='compute.googleapis.com/instance/cpu' \ + --monitoring.metrics-prefixes='cloudsql.googleapis.com/database' \ --monitoring.filters='pubsub.googleapis.com/subscription:resource.labels.subscription_id=monitoring.regex.full_match("us-west4.*my-team-subs.*")' \ - --monitoring.filters='compute.googleapis.com/instance/cpu:resource.labels.instance=monitoring.regex.full_match("us-west4.*my-team-subs.*")' + --monitoring.filters='compute.googleapis.com/instance/cpu:resource.labels.instance=monitoring.regex.full_match("us-west4.*my-team-subs.*")' \ + --monitoring.filters='cloudsql.googleapis.com/database:resource.labels.database_id="my-test-project:my-database-name"' ``` Using projects filter: diff --git a/utils/utils.go b/utils/utils.go index 58ac9eca..ce97f097 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -42,11 +42,11 @@ func NormalizeMetricName(metricName string) string { } func SplitExtraFilter(extraFilter string, separator string) (string, string) { - mPrefix := strings.Split(extraFilter, separator) - if mPrefix[0] == extraFilter { + mPrefix := strings.SplitN(extraFilter, separator, 2) + if len(mPrefix) != 2 { return "", "" } - return mPrefix[0], strings.Join(mPrefix[1:], "") + return mPrefix[0], mPrefix[1] } func ProjectResource(projectID string) string { diff --git a/utils/utils_test.go b/utils/utils_test.go index 0a2c031a..a179cf14 100644 --- a/utils/utils_test.go +++ b/utils/utils_test.go @@ -31,3 +31,23 @@ var _ = Describe("ProjectResource", func() { Expect(ProjectResource("fake-project-1")).To(Equal("projects/fake-project-1")) }) }) + +var _ = Describe("SplitExtraFilter", func() { + It("returns an empty string from incomplete filter", func() { + metricPrefix, filterQuery := SplitExtraFilter("This_is__a-MetricName.Example/with/no/filter", ":") + Expect(metricPrefix).To(Equal("")) + Expect(filterQuery).To(Equal("")) + }) + + It("returns a metric prefix and filter query from basic filter", func() { + metricPrefix, filterQuery := SplitExtraFilter("This_is__a-MetricName.Example/with:filter.name=filter_value", ":") + Expect(metricPrefix).To(Equal("This_is__a-MetricName.Example/with")) + Expect(filterQuery).To(Equal("filter.name=filter_value")) + }) + + It("returns a metric prefix and filter query filter with colon", func() { + metricPrefix, filterQuery := SplitExtraFilter(`This_is__a-MetricName.Example/with:filter.name="filter:value"`, ":") + Expect(metricPrefix).To(Equal("This_is__a-MetricName.Example/with")) + Expect(filterQuery).To(Equal("filter.name=\"filter:value\"")) + }) +})