Skip to content

Commit

Permalink
Replace comma-delimited string flags with repeatable flags
Browse files Browse the repository at this point in the history
Signed-off-by: Tommy Li <[email protected]>
  • Loading branch information
tommyzli committed Aug 30, 2024
1 parent a3570d6 commit 9abb63b
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 27 deletions.
14 changes: 8 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,11 @@ If you are still using the legacy [Access scopes][access-scopes], the `https://w

| Flag | Required | Default | Description |
| ----------------------------------- | -------- |---------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `google.project-id` | No | GCloud SDK auto-discovery | Comma seperated list of Google Project IDs |
| `google.project-id` | No | GCloud SDK auto-discovery | Repeatable flag of Google Project IDs |
| `google.projects.filter` | No | | GCloud projects filter expression. See more [here](https://cloud.google.com/sdk/gcloud/reference/projects/list). |
| `monitoring.metrics-ingest-delay` | No | | Offsets metric collection by a delay appropriate for each metric type, e.g. because bigquery metrics are slow to appear |
| `monitoring.drop-delegated-projects` | No | No | Drop metrics from attached projects and fetch `project_id` only. |
| `monitoring.metrics-type-prefixes` | Yes | | Comma separated Google Stackdriver Monitoring Metric Type prefixes (see [example][metrics-prefix-example] and [available metrics][metrics-list]) |
| `monitoring.metrics-prefixes` | Yes | | Repeatable flag of Google Stackdriver Monitoring Metric Type prefixes (see [example][metrics-prefix-example] and [available metrics][metrics-list]) |
| `monitoring.metrics-interval` | No | `5m` | Metric's timestamp interval to request from the Google Stackdriver Monitoring Metrics API. Only the most recent data point is used |
| `monitoring.metrics-offset` | No | `0s` | Offset (into the past) for the metric's timestamp interval to request from the Google Stackdriver Monitoring Metrics API, to handle latency in published metrics |
| `monitoring.filters` | No | | Additonal filters to be sent on the Monitoring API call. Add multiple filters by providing this parameter multiple times. See [monitoring.filters](#using-filters) for more info. |
Expand Down Expand Up @@ -144,7 +144,8 @@ If we want to get all `CPU` (`compute.googleapis.com/instance/cpu`) and `Disk` (
```
stackdriver_exporter \
--google.project-id=my-test-project \
--monitoring.metrics-type-prefixes "compute.googleapis.com/instance/cpu,compute.googleapis.com/instance/disk"
--monitoring.metrics-prefixes "compute.googleapis.com/instance/cpu"
--monitoring.metrics-prefixes "compute.googleapis.com/instance/disk"
```

### Using filters
Expand All @@ -163,16 +164,17 @@ Example: \
pubsub.googleapis.com/subscription/num_undelivered_messages (apply to only the specific subscription metric) \

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

Full example
```
stackdriver_exporter \
--google.project-id=my-test-project \
--monitoring.metrics-type-prefixes='pubsub.googleapis.com/subscription,compute.googleapis.com/instance/cpu' \
--monitoring.metrics-prefixes='pubsub.googleapis.com/subscription' \
--monitoring.metrics-prefixes='compute.googleapis.com/instance/cpu' \
--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.*")'
```

Using projects filter:
Expand Down
41 changes: 20 additions & 21 deletions stackdriver_exporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,9 @@ var (
"web.stackdriver-telemetry-path", "Path under which to expose Stackdriver metrics.",
).Default("/metrics").String()

projectID = kingpin.Flag(
"google.project-id", "Comma seperated list of Google Project IDs.",
).String()
projectIDs = kingpin.Flag(
"google.project-id", "Google Project IDs. Repeat this flag for multiple projects.",
).Strings()

projectsFilter = kingpin.Flag(
"google.projects.filter", "Google projects search filter.",
Expand Down Expand Up @@ -87,9 +87,9 @@ var (

// Monitoring collector flags

monitoringMetricsTypePrefixes = kingpin.Flag(
"monitoring.metrics-type-prefixes", "Comma separated Google Stackdriver Monitoring Metric Type prefixes.",
).Required().String()
monitoringMetricsPrefixes = kingpin.Flag(
"monitoring.metrics-prefixes", "Google Stackdriver Monitoring Metric Type prefixes. Repeat this flag to scrape multiple prefixes.",
).Required().Strings()

monitoringMetricsInterval = kingpin.Flag(
"monitoring.metrics-interval", "Interval to request the Google Stackdriver Monitoring Metrics for. Only the most recent data point is used.",
Expand Down Expand Up @@ -137,15 +137,15 @@ func init() {
prometheus.MustRegister(versioncollector.NewCollector("stackdriver_exporter"))
}

func getDefaultGCPProject(ctx context.Context) (*string, error) {
func getDefaultGCPProject(ctx context.Context) (*[]string, error) {
credentials, err := google.FindDefaultCredentials(ctx, compute.ComputeScope)
if err != nil {
return nil, err
}
if credentials.ProjectID == "" {
return nil, fmt.Errorf("unable to identify the gcloud project. Got empty string")
}
return &credentials.ProjectID, nil
return &[]string{credentials.ProjectID}, nil
}

func createMonitoringService(ctx context.Context) (*monitoring.Service, error) {
Expand Down Expand Up @@ -271,10 +271,10 @@ func main() {
logger := promlog.New(promlogConfig)

ctx := context.Background()
if *projectID == "" && *projectsFilter == "" {
if len(*projectIDs) == 0 && *projectsFilter == "" {
level.Info(logger).Log("msg", "Neither projectID nor projectsFilter was provided. Trying to discover it")
var err error
projectID, err = getDefaultGCPProject(ctx)
projectIDs, err = getDefaultGCPProject(ctx)
if err != nil {
level.Error(logger).Log("msg", "no explicit projectID and error trying to discover default GCloud project", "err", err)
os.Exit(1)
Expand All @@ -287,43 +287,42 @@ func main() {
os.Exit(1)
}

var projectIDs []string
var discoveredProjectIDs []string

if *projectsFilter != "" {
projectIDs, err = utils.GetProjectIDsFromFilter(ctx, *projectsFilter)
discoveredProjectIDs, err = utils.GetProjectIDsFromFilter(ctx, *projectsFilter)
if err != nil {
level.Error(logger).Log("msg", "failed to get project IDs from filter", "err", err)
os.Exit(1)
}
}

if *projectID != "" {
projectIDs = append(projectIDs, strings.Split(*projectID, ",")...)
if len(*projectIDs) > 0 {
discoveredProjectIDs = append(discoveredProjectIDs, *projectIDs...)
}

level.Info(logger).Log(
"msg", "Starting stackdriver_exporter",
"version", version.Info(),
"build_context", version.BuildContext(),
"projects", *projectID,
"metric_prefixes", *monitoringMetricsTypePrefixes,
"projects", *projectIDs,
"metric_prefixes", *monitoringMetricsPrefixes,
"extra_filters", strings.Join(*monitoringMetricsExtraFilter, ","),
"projectIDs", fmt.Sprintf("%v", projectIDs),
"projectIDs", fmt.Sprintf("%v", discoveredProjectIDs),
"projectsFilter", *projectsFilter,
)

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

if *metricsPath == *stackdriverMetricsPath {
handler := newHandler(
projectIDs, metricsTypePrefixes, metricExtraFilters, monitoringService, logger, prometheus.DefaultGatherer)
discoveredProjectIDs, parsedMetricsPrefixes, metricExtraFilters, monitoringService, logger, prometheus.DefaultGatherer)
http.Handle(*metricsPath, promhttp.InstrumentMetricHandler(prometheus.DefaultRegisterer, handler))
} else {
level.Info(logger).Log("msg", "Serving Stackdriver metrics at separate path", "path", *stackdriverMetricsPath)
handler := newHandler(
projectIDs, metricsTypePrefixes, metricExtraFilters, monitoringService, logger, nil)
discoveredProjectIDs, parsedMetricsPrefixes, metricExtraFilters, monitoringService, logger, nil)
http.Handle(*stackdriverMetricsPath, promhttp.InstrumentMetricHandler(prometheus.DefaultRegisterer, handler))
http.Handle(*metricsPath, promhttp.Handler())
}
Expand Down

0 comments on commit 9abb63b

Please sign in to comment.