Skip to content

Commit

Permalink
Sanitize metric type prefixes
Browse files Browse the repository at this point in the history
When more than one prefix matches the same metric descriptor, this will
throw the error "collected metric xxx was collected before with the
same name and label values".

For example, using the metric type prefixes
  foo.googleapis.com/bar (a prefix)
and
  foo.googleapis.com/bar/baz (a metric)
will result in an error because both match the metric
  foo.googleapis.com/bar/baz.

Further, using the metric type prefixes
  foo.googleapis.com/bar/baz (a metric)
and
  foo.googleapis.com/bar/baz_count (a metric)
will result in an error because both match the metric
  foo.googleapis.com/bar/baz_count.

While the first pitfall could be expected by the user, the latter will
come as a complete surprise to anyone who is not aware that
stackdriver-exporter internally uses an MQL query in the form of
  metric.type = starts_with("<prefix>")
to filter the metrics.

Avoid this by sanitizing the provided metric type prefixes in the
following way:
- Drop any duplicate prefixes
- Sort the prefixes (required by the next step)
- Drop any prefixes that start with another prefix present in the input

Signed-off-by: Edwin Mackenzie-Owen <edwin.mowen@gmail.com>
  • Loading branch information
sysedwinistrator committed Mar 19, 2024
1 parent 205417f commit b3247d4
Showing 1 changed file with 43 additions and 1 deletion.
44 changes: 43 additions & 1 deletion stackdriver_exporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"fmt"
"net/http"
"os"
"slices"
"strings"

"github.com/PuerkitoBio/rehttp"
Expand Down Expand Up @@ -303,7 +304,7 @@ func main() {

level.Info(logger).Log("msg", "Using Google Cloud Project IDs", "projectIDs", fmt.Sprintf("%v", projectIDs))

metricsTypePrefixes := strings.Split(*monitoringMetricsTypePrefixes, ",")
metricsTypePrefixes := parseMetricTypePrefixes()
metricExtraFilters := parseMetricExtraFilters()

if *metricsPath == *stackdriverMetricsPath {
Expand Down Expand Up @@ -353,6 +354,47 @@ func main() {
}
}

func parseMetricTypePrefixes() (metricTypePrefixes []string) {
inputPrefixes := strings.Split(*monitoringMetricsTypePrefixes, ",")

// only keep unique prefixes
uniqueKeys := make(map[string]bool)
uniquePrefixes := []string{}
for _, prefix := range inputPrefixes {
if _, ok := uniqueKeys[prefix]; !ok {
uniqueKeys[prefix] = true
uniquePrefixes = append(uniquePrefixes, prefix)
}
}

// drop prefixes that start with another existing prefix to avoid error:
// "collected metric xxx was collected before with the same name and label values"
slices.Sort(uniquePrefixes)
for i, prefix := range uniquePrefixes {
if i == 0 {
metricTypePrefixes = []string{prefix}
} else {
previousIndex := len(metricTypePrefixes) - 1

// current prefix starts with previous one
if strings.HasPrefix(prefix, metricTypePrefixes[previousIndex]) {
// drop current prefix
continue
}

// previous prefix starts with current prefix
if strings.HasPrefix(metricTypePrefixes[previousIndex], prefix) {
// drop previous prefix
metricTypePrefixes = metricTypePrefixes[:previousIndex]
}

metricTypePrefixes = append(metricTypePrefixes, prefix)
}
}

return metricTypePrefixes
}

func parseMetricExtraFilters() []collectors.MetricFilter {
var extraFilters []collectors.MetricFilter
for _, ef := range *monitoringMetricsExtraFilter {
Expand Down

0 comments on commit b3247d4

Please sign in to comment.