Skip to content

Commit 97a70f7

Browse files
authored
feat(googlecloudmonitoring): support monitoring filters (#37264)
<!--Ex. Fixing a bug - Describe the bug and how this fixes the issue. Ex. Adding a feature - Explain what this achieves.--> #### Description <!-- Issue number (e.g. #1234) or full URL to issue, if applicable. --> #### Link to tracking issue Fixes part of #36898 <!--Describe what testing was performed and which tests were added.--> #### Testing <!--Describe the documentation added.--> #### Documentation <!--Please delete paragraphs that you did not use before submitting.-->
1 parent 9fe87a2 commit 97a70f7

File tree

6 files changed

+61
-24
lines changed

6 files changed

+61
-24
lines changed
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Use this changelog template to create an entry for release notes.
2+
3+
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
4+
change_type: enhancement
5+
6+
# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver)
7+
component: googlecloudmonitoringreceiver
8+
9+
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
10+
note: support use [monitoring filters](https://cloud.google.com/monitoring/api/v3/filters) to filter metrics
11+
12+
# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists.
13+
issues: [36898]
14+
15+
# (Optional) One or more lines of additional information to render under the primary note.
16+
# These lines will be padded with 2 spaces and then inserted directly into the document.
17+
# Use pipe (|) for multiline entries.
18+
subtext:
19+
20+
# If your change doesn't affect end users or the exported elements of any package,
21+
# you should instead start your pull request title with [chore] or use the "Skip Changelog" label.
22+
# Optional: The change log or logs in which this entry should be included.
23+
# e.g. '[user]' or '[user, api]'
24+
# Include 'user' if the change is relevant to end users.
25+
# Include 'api' if there is a change to a library API.
26+
# Default: '[user]'
27+
change_logs: []

receiver/googlecloudmonitoringreceiver/README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,10 @@ receivers:
4141

4242
Each single metric can have the following configuration:
4343

44-
- `metric_name` (Required): The specific metric name to collect.
44+
- `metric_name` (Optional): The specific metric name to collect.
45+
- `metric_descriptor_filter` (Optional): Filter for [listing metric descriptors](https://cloud.google.com/monitoring/api/v3/filters#metric-descriptor-filter). Only support `project` and `metric.type` as filter objects.
4546

47+
One of `metric_name` and `metric_descriptor_filter` MUST be specified, but MUST not be specified at the same time.
4648

4749
## Authentication with Google Cloud
4850

receiver/googlecloudmonitoringreceiver/config.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ type Config struct {
2626

2727
type MetricConfig struct {
2828
MetricName string `mapstructure:"metric_name"`
29+
// Filter for listing metric descriptors. Only support `project` and `metric.type` as filter objects.
30+
// See https://cloud.google.com/monitoring/api/v3/filters#metric-descriptor-filter for more details.
31+
MetricDescriptorFilter string `mapstructure:"metric_descriptor_filter"`
2932
}
3033

3134
func (config *Config) Validate() error {
@@ -47,8 +50,12 @@ func (config *Config) Validate() error {
4750
}
4851

4952
func (metric MetricConfig) Validate() error {
50-
if metric.MetricName == "" {
51-
return errors.New("field \"metric_name\" is required and cannot be empty for metric configuration")
53+
if metric.MetricName != "" && metric.MetricDescriptorFilter != "" {
54+
return errors.New("fields \"metric_name\" and \"metric_descriptor_filter\" cannot both have value")
55+
}
56+
57+
if metric.MetricName == "" && metric.MetricDescriptorFilter == "" {
58+
return errors.New("fields \"metric_name\" and \"metric_descriptor_filter\" cannot both be empty")
5259
}
5360

5461
return nil

receiver/googlecloudmonitoringreceiver/config_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ func TestLoadConfig(t *testing.T) {
4141
{
4242
MetricName: "connectors.googleapis.com/flex/instance/cpu/usage_time",
4343
},
44+
{
45+
MetricDescriptorFilter: "metric.type = starts_with(\"compute.googleapis.com\")",
46+
},
4447
},
4548
},
4649
cfg,

receiver/googlecloudmonitoringreceiver/receiver.go

Lines changed: 18 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ type monitoringReceiver struct {
3030
client *monitoring.MetricClient
3131
metricsBuilder *internal.MetricsBuilder
3232
mutex sync.RWMutex
33-
metricDescriptors map[string]*metric.MetricDescriptor
33+
metricDescriptors map[string]*metric.MetricDescriptor // key is the Type of MetricDescriptor
3434
}
3535

3636
func newGoogleCloudMonitoringReceiver(cfg *Config, logger *zap.Logger) *monitoringReceiver {
@@ -78,7 +78,7 @@ func (mr *monitoringReceiver) Shutdown(context.Context) error {
7878

7979
func (mr *monitoringReceiver) Scrape(ctx context.Context) (pmetric.Metrics, error) {
8080
var (
81-
gInternal time.Duration
81+
gInterval time.Duration
8282
gDelay time.Duration
8383
calStartTime time.Time
8484
calEndTime time.Time
@@ -89,20 +89,13 @@ func (mr *monitoringReceiver) Scrape(ctx context.Context) (pmetric.Metrics, erro
8989
metrics := pmetric.NewMetrics()
9090

9191
// Iterate over each metric in the configuration to calculate start/end times and construct the filter query.
92-
for _, metric := range mr.config.MetricsList {
93-
// Acquire read lock to safely read metricDescriptors
94-
mr.mutex.RLock()
95-
metricDesc, exists := mr.metricDescriptors[metric.MetricName]
96-
mr.mutex.RUnlock()
97-
if !exists {
98-
mr.logger.Warn("Metric descriptor not found", zap.String("metric_name", metric.MetricName))
99-
continue
100-
}
101-
92+
mr.mutex.RLock()
93+
defer mr.mutex.RUnlock()
94+
for metricType, metricDesc := range mr.metricDescriptors {
10295
// Set interval and delay times, using defaults if not provided
103-
gInternal = mr.config.CollectionInterval
104-
if gInternal <= 0 {
105-
gInternal = defaultCollectionInterval
96+
gInterval = mr.config.CollectionInterval
97+
if gInterval <= 0 {
98+
gInterval = defaultCollectionInterval
10699
}
107100

108101
gDelay = metricDesc.GetMetadata().GetIngestDelay().AsDuration()
@@ -111,10 +104,10 @@ func (mr *monitoringReceiver) Scrape(ctx context.Context) (pmetric.Metrics, erro
111104
}
112105

113106
// Calculate the start and end times
114-
calStartTime, calEndTime = calculateStartEndTime(gInternal, gDelay)
107+
calStartTime, calEndTime = calculateStartEndTime(gInterval, gDelay)
115108

116109
// Get the filter query for the metric
117-
filterQuery = getFilterQuery(metric)
110+
filterQuery = fmt.Sprintf(`metric.type = "%s"`, metricType)
118111

119112
// Define the request to list time series data
120113
tsReq := &monitoringpb.ListTimeSeriesRequest{
@@ -241,10 +234,14 @@ func calculateStartEndTime(interval, delay time.Duration) (time.Time, time.Time)
241234
// getFilterQuery constructs a filter query string based on the provided metric.
242235
func getFilterQuery(metric MetricConfig) string {
243236
var filterQuery string
244-
const baseQuery = `metric.type =`
245237

246-
// If a specific metric name is provided, use it in the filter query
247-
filterQuery = fmt.Sprintf(`%s "%s"`, baseQuery, metric.MetricName)
238+
// see https://cloud.google.com/monitoring/api/v3/filters
239+
if metric.MetricName != "" {
240+
filterQuery = fmt.Sprintf(`metric.type = "%s"`, metric.MetricName)
241+
} else {
242+
filterQuery = metric.MetricDescriptorFilter
243+
}
244+
248245
return filterQuery
249246
}
250247

@@ -322,7 +319,7 @@ func (mr *monitoringReceiver) convertGCPTimeSeriesToMetrics(metrics pmetric.Metr
322319
// TODO: Add support for EXPONENTIAL_HISTOGRAM
323320
default:
324321
metricError := fmt.Sprintf("\n Unsupported metric kind: %v\n", timeSeries.GetMetricKind())
325-
mr.logger.Info(metricError)
322+
mr.logger.Warn(metricError)
326323
}
327324
}
328325

receiver/googlecloudmonitoringreceiver/testdata/config.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ googlecloudmonitoring:
44
metrics_list:
55
- metric_name: "compute.googleapis.com/instance/cpu/usage_time"
66
- metric_name: "connectors.googleapis.com/flex/instance/cpu/usage_time"
7+
- metric_descriptor_filter: "metric.type = starts_with(\"compute.googleapis.com\")"

0 commit comments

Comments
 (0)