Skip to content

Commit 5c4714e

Browse files
Added google cloud monitoring receiver
1 parent 1146a37 commit 5c4714e

23 files changed

+715
-2
lines changed
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: new_component
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: Adding new component - [Google Cloud monitoring](https://cloud.google.com/monitoring/api/metrics_gcp) receiver to fetch GCP Cloud Metrics and transform to OpenTelemetry compatible format.
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: [33762]
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: []

.github/CODEOWNERS

+1
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,7 @@ receiver/windowseventlogreceiver/ @open-teleme
296296
receiver/windowsperfcountersreceiver/ @open-telemetry/collector-contrib-approvers @dashpole @alxbl @pjanotti
297297
receiver/zipkinreceiver/ @open-telemetry/collector-contrib-approvers @MovieStoreGuy @andrzej-stencel @crobert-1
298298
receiver/zookeeperreceiver/ @open-telemetry/collector-contrib-approvers @djaglowski
299+
receiver/googlecloudmonitoringreceiver/ @open-telemetry/collector-contrib-approvers @dashpole @TylerHelmuth @abhishek-at-cloudwerx
299300

300301
testbed/ @open-telemetry/collector-contrib-approvers @open-telemetry/collector-approvers
301302
testbed/mockdatasenders/mockdatadogagentexporter/ @open-telemetry/collector-contrib-approvers @boostchicken

.github/ISSUE_TEMPLATE/bug_report.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,7 @@ body:
228228
- receiver/flinkmetrics
229229
- receiver/fluentforward
230230
- receiver/gitprovider
231+
- receiver/googlecloudmonitoring
231232
- receiver/googlecloudpubsub
232233
- receiver/googlecloudspanner
233234
- receiver/haproxy

.github/ISSUE_TEMPLATE/feature_request.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,7 @@ body:
222222
- receiver/flinkmetrics
223223
- receiver/fluentforward
224224
- receiver/gitprovider
225+
- receiver/googlecloudmonitoring
225226
- receiver/googlecloudpubsub
226227
- receiver/googlecloudspanner
227228
- receiver/haproxy

.github/ISSUE_TEMPLATE/other.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,7 @@ body:
222222
- receiver/flinkmetrics
223223
- receiver/fluentforward
224224
- receiver/gitprovider
225+
- receiver/googlecloudmonitoring
225226
- receiver/googlecloudpubsub
226227
- receiver/googlecloudspanner
227228
- receiver/haproxy

.github/ISSUE_TEMPLATE/unmaintained.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,7 @@ body:
227227
- receiver/flinkmetrics
228228
- receiver/fluentforward
229229
- receiver/gitprovider
230+
- receiver/googlecloudmonitoring
230231
- receiver/googlecloudpubsub
231232
- receiver/googlecloudspanner
232233
- receiver/haproxy

cmd/otelcontribcol/go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ module github.com/open-telemetry/opentelemetry-collector-contrib/cmd/otelcontrib
44

55
go 1.21.0
66

7-
toolchain go1.21.12
7+
toolchain go1.22.4
88

99
require (
1010
github.com/open-telemetry/opentelemetry-collector-contrib/confmap/provider/s3provider v0.106.1

cmd/oteltestbedcol/go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ module github.com/open-telemetry/opentelemetry-collector-contrib/cmd/oteltestbed
44

55
go 1.21.0
66

7-
toolchain go1.21.12
7+
toolchain go1.22.4
88

99
require (
1010
github.com/open-telemetry/opentelemetry-collector-contrib/exporter/carbonexporter v0.106.1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
include ../../Makefile.Common
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# Google Cloud Monitoring Receiver
2+
3+
<!-- status autogenerated section -->
4+
| Status | |
5+
| ------------- |-----------|
6+
| Stability | [development]: metrics |
7+
| Distributions | [contrib] |
8+
| Issues | [![Open issues](https://img.shields.io/github/issues-search/open-telemetry/opentelemetry-collector-contrib?query=is%3Aissue%20is%3Aopen%20label%3Areceiver%2Fgooglecloudmonitoring%20&label=open&color=orange&logo=opentelemetry)](https://github.com/open-telemetry/opentelemetry-collector-contrib/issues?q=is%3Aopen+is%3Aissue+label%3Areceiver%2Fgooglecloudmonitoring) [![Closed issues](https://img.shields.io/github/issues-search/open-telemetry/opentelemetry-collector-contrib?query=is%3Aissue%20is%3Aclosed%20label%3Areceiver%2Fgooglecloudmonitoring%20&label=closed&color=blue&logo=opentelemetry)](https://github.com/open-telemetry/opentelemetry-collector-contrib/issues?q=is%3Aclosed+is%3Aissue+label%3Areceiver%2Fgooglecloudmonitoring) |
9+
| [Code Owners](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/CONTRIBUTING.md#becoming-a-code-owner) | [@dashpole](https://www.github.com/dashpole), [@TylerHelmuth](https://www.github.com/TylerHelmuth), [@abhishek-at-cloudwerx](https://www.github.com/abhishek-at-cloudwerx) |
10+
11+
[development]: https://github.com/open-telemetry/opentelemetry-collector#development
12+
[contrib]: https://github.com/open-telemetry/opentelemetry-collector-releases/tree/main/distributions/otelcol-contrib
13+
<!-- end autogenerated section -->
14+
15+
The primary objective of the Google Cloud Monitoring Receiver is to gather time series metrics data from all Google services and convert this data into a pipeline format that facilitates further use.
16+
17+
This receiver gets GCP (Google Clout Platform) metrics from [GCP Monitoring REST API] via the [Google SDK for GCP Metrics] and then convert those timeseries data into OTel Format [Pipeline Data].
18+
19+
[GCP Monitoring REST API]: https://cloud.google.com/monitoring/api/ref_v3/rest/v3/projects.timeSeries/list
20+
[Google SDK for GCP Metrics]: https://pkg.go.dev/cloud.google.com/go/monitoring/apiv3
21+
[Pipeline Data]: https://pkg.go.dev/go.opentelemetry.io/collector/pdata
22+
23+
## Configuration
24+
The following configuration options are supported:
25+
26+
```yaml
27+
receivers:
28+
googlecloudmonitoring:
29+
collection_interval: 120s
30+
region: us-central1
31+
project_id: my-project-id
32+
service_account_key: "path/to/service_account.json"
33+
services:
34+
- service_name: "compute"
35+
metric_name: "compute.googleapis.com/instance/cpu/usage_time"
36+
delay: 60s
37+
- service_name: "connectors"
38+
metric_name: "connectors.googleapis.com/flex/instance/cpu/usage_time"
39+
delay: 60s
40+
```
41+
42+
- `collection_interval` (Optional): The interval at which metrics are collected. Default is 60s.
43+
- `region` (Required): The GCP region where the services are located.
44+
- `project_id` (Required): The GCP project ID.
45+
- `service_account_key` (Required): The path to the service account key JSON file.
46+
- `services` (Required): A list of services to monitor.
47+
48+
Each service can have the following configuration:
49+
50+
- `service_name` (Required): The name of the GCP service (e.g., `compute`).
51+
- `delay` (Optional): The delay before starting the collection of metrics for this service. Default is 0s.
52+
- `metric_name` (Optional): The specific metric name to collect. If not set, all metrics are collected.
53+
54+
### Filtering
55+
56+
**Metrics Parameters** :
57+
58+
- A monitoring filter that specifies which time series should be returned. The filter must specify a single metric type. For example: `metric_name: "compute.googleapis.com/instance/cpu/usage_time"`
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package googlecloudmonitoringreceiver // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/googlecloudmonitoringreceiver"
5+
6+
import (
7+
"errors"
8+
"fmt"
9+
"time"
10+
11+
"go.opentelemetry.io/collector/receiver/scraperhelper"
12+
)
13+
14+
const minCollectionIntervalSeconds = 60
15+
16+
type Config struct {
17+
scraperhelper.ControllerConfig `mapstructure:",squash"`
18+
19+
Region string `mapstructure:"region"`
20+
ProjectID string `mapstructure:"project_id"`
21+
ServiceAccountKey string `mapstructure:"service_account_key"`
22+
Services []Service `mapstructure:"services"`
23+
}
24+
25+
type Service struct {
26+
ServiceName string `mapstructure:"service_name"`
27+
MetricName string `mapstructure:"metric_name"`
28+
Delay time.Duration `mapstructure:"delay"`
29+
}
30+
31+
func (config *Config) Validate() error {
32+
if config.CollectionInterval.Seconds() < minCollectionIntervalSeconds {
33+
return fmt.Errorf("\"collection_interval\" must be not lower than %v seconds, current value is %v seconds", minCollectionIntervalSeconds, config.CollectionInterval.Seconds())
34+
}
35+
36+
if len(config.Services) == 0 {
37+
return errors.New("missing required field \"services\" or its value is empty")
38+
}
39+
40+
for _, service := range config.Services {
41+
if err := service.Validate(); err != nil {
42+
return err
43+
}
44+
}
45+
46+
return nil
47+
}
48+
49+
func (service Service) Validate() error {
50+
if service.ServiceName == "" {
51+
return errors.New("field \"service_name\" is required and cannot be empty for service configuration")
52+
}
53+
54+
if service.Delay < 0 {
55+
return errors.New("field \"delay\" cannot be negative for service configuration")
56+
}
57+
58+
return nil
59+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package googlecloudmonitoringreceiver
5+
6+
import (
7+
"path/filepath"
8+
"testing"
9+
"time"
10+
11+
"github.com/stretchr/testify/assert"
12+
"github.com/stretchr/testify/require"
13+
"go.opentelemetry.io/collector/component"
14+
"go.opentelemetry.io/collector/confmap/confmaptest"
15+
"go.opentelemetry.io/collector/receiver/scraperhelper"
16+
17+
"github.com/open-telemetry/opentelemetry-collector-contrib/receiver/googlecloudmonitoringreceiver/internal/metadata"
18+
)
19+
20+
func TestLoadConfig(t *testing.T) {
21+
cm, err := confmaptest.LoadConf(filepath.Join("testdata", "config.yaml"))
22+
require.NoError(t, err)
23+
factory := NewFactory()
24+
cfg := factory.CreateDefaultConfig()
25+
26+
sub, err := cm.Sub(component.NewIDWithName(metadata.Type, "").String())
27+
require.NoError(t, err)
28+
require.NoError(t, sub.Unmarshal(cfg))
29+
30+
assert.Equal(t,
31+
&Config{
32+
ControllerConfig: scraperhelper.ControllerConfig{
33+
CollectionInterval: 120 * time.Second,
34+
InitialDelay: 1 * time.Second,
35+
},
36+
Region: "us-central1",
37+
ProjectID: "my-project-id",
38+
ServiceAccountKey: "path/to/service_account.json",
39+
Services: []Service{
40+
{
41+
ServiceName: "compute",
42+
Delay: 60 * time.Second,
43+
MetricName: "compute.googleapis.com/instance/cpu/usage_time",
44+
},
45+
{
46+
ServiceName: "connectors",
47+
Delay: 60 * time.Second,
48+
MetricName: "connectors.googleapis.com/flex/instance/cpu/usage_time",
49+
},
50+
},
51+
},
52+
cfg,
53+
)
54+
}
55+
56+
func TestValidateService(t *testing.T) {
57+
testCases := map[string]struct {
58+
service Service
59+
requireError bool
60+
}{
61+
"Valid Service": {
62+
Service{
63+
ServiceName: "service_name",
64+
Delay: 0,
65+
}, false},
66+
"Empty ServiceName": {
67+
Service{
68+
ServiceName: "",
69+
Delay: 0,
70+
}, true},
71+
"Negative Delay": {
72+
Service{
73+
ServiceName: "service_name",
74+
Delay: -1,
75+
}, true},
76+
}
77+
78+
for name, testCase := range testCases {
79+
t.Run(name, func(t *testing.T) {
80+
err := testCase.service.Validate()
81+
if testCase.requireError {
82+
require.Error(t, err)
83+
} else {
84+
require.NoError(t, err)
85+
}
86+
})
87+
}
88+
}
89+
90+
func TestValidateConfig(t *testing.T) {
91+
validService := Service{
92+
ServiceName: "service_name",
93+
Delay: 0 * time.Second,
94+
}
95+
96+
testCases := map[string]struct {
97+
services []Service
98+
collectionInterval time.Duration
99+
requireError bool
100+
}{
101+
"Valid Config": {[]Service{validService}, 60 * time.Second, false},
102+
"Empty Services": {nil, 60 * time.Second, true},
103+
"Invalid Service in Services": {[]Service{{}}, 60 * time.Second, true},
104+
"Invalid Collection Interval": {[]Service{validService}, 0 * time.Second, true},
105+
}
106+
107+
for name, testCase := range testCases {
108+
t.Run(name, func(t *testing.T) {
109+
cfg := &Config{
110+
ControllerConfig: scraperhelper.ControllerConfig{
111+
CollectionInterval: testCase.collectionInterval,
112+
},
113+
Services: testCase.services,
114+
}
115+
116+
err := cfg.Validate()
117+
if testCase.requireError {
118+
require.Error(t, err)
119+
} else {
120+
require.NoError(t, err)
121+
}
122+
})
123+
}
124+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
//go:generate mdatagen metadata.yaml
5+
6+
package googlecloudmonitoringreceiver // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/googlecloudmonitoringreceiver"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package googlecloudmonitoringreceiver // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/googlecloudmonitoringreceiver"
5+
6+
import (
7+
"context"
8+
9+
"go.opentelemetry.io/collector/component"
10+
"go.opentelemetry.io/collector/consumer"
11+
"go.opentelemetry.io/collector/receiver"
12+
"go.opentelemetry.io/collector/receiver/scraperhelper"
13+
14+
"github.com/open-telemetry/opentelemetry-collector-contrib/receiver/googlecloudmonitoringreceiver/internal/metadata"
15+
)
16+
17+
func NewFactory() receiver.Factory {
18+
return receiver.NewFactory(
19+
metadata.Type,
20+
createDefaultConfig,
21+
receiver.WithMetrics(createMetricsReceiver, metadata.MetricsStability))
22+
}
23+
24+
// createDefaultConfig creates the default exporter configuration
25+
func createDefaultConfig() component.Config {
26+
return &Config{
27+
ControllerConfig: scraperhelper.NewDefaultControllerConfig(),
28+
}
29+
}
30+
31+
func createMetricsReceiver(
32+
_ context.Context,
33+
settings receiver.Settings,
34+
baseCfg component.Config,
35+
consumer consumer.Metrics,
36+
) (receiver.Metrics, error) {
37+
38+
rCfg := baseCfg.(*Config)
39+
r := newGoogleCloudMonitoringReceiver(rCfg, settings.Logger)
40+
41+
scraper, err := scraperhelper.NewScraper(metadata.Type.String(), r.Scrape, scraperhelper.WithStart(r.Start),
42+
scraperhelper.WithShutdown(r.Shutdown))
43+
if err != nil {
44+
return nil, err
45+
}
46+
47+
return scraperhelper.NewScraperControllerReceiver(&rCfg.ControllerConfig, settings, consumer,
48+
scraperhelper.AddScraper(scraper))
49+
}

0 commit comments

Comments
 (0)