diff --git a/README.md b/README.md index a7e7e67..a1350ba 100644 --- a/README.md +++ b/README.md @@ -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. | @@ -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 @@ -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: diff --git a/stackdriver_exporter.go b/stackdriver_exporter.go index 33cd2e0..e63247d 100644 --- a/stackdriver_exporter.go +++ b/stackdriver_exporter.go @@ -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.", @@ -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.", @@ -137,7 +137,7 @@ 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 @@ -145,7 +145,7 @@ func getDefaultGCPProject(ctx context.Context) (*string, error) { 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) { @@ -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) @@ -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()) }