Add resource type and resource label support to stackdriver output (#5391)

This commit is contained in:
Robert Edström 2019-02-08 21:13:33 +01:00 committed by Daniel Nelson
parent 52bd698046
commit 3f9860a685
3 changed files with 79 additions and 10 deletions

View File

@ -7,6 +7,10 @@ Requires `project` to specify where Stackdriver metrics will be delivered to.
Metrics are grouped by the `namespace` variable and metric key - eg: `custom.googleapis.com/telegraf/system/load5`
[Resource type](https://cloud.google.com/monitoring/api/resources) is configured by the `resource_type` variable (default `global`).
Additional resource labels can be configured by `resource_labels`. By default the required `project_id` label is always set to the `project` variable.
### Configuration
```toml
@ -16,6 +20,15 @@ Metrics are grouped by the `namespace` variable and metric key - eg: `custom.goo
# The namespace for the metric descriptor
namespace = "telegraf"
# Custom resource type
resource_type = "generic_node"
# Additonal resource labels
[outputs.stackdriver.resource_labels]
node_id = "$HOSTNAME"
namespace = "myapp"
location = "eu-north0"
```
### Restrictions

View File

@ -19,8 +19,10 @@ import (
// Stackdriver is the Google Stackdriver config info.
type Stackdriver struct {
Project string
Namespace string
Project string
Namespace string
ResourceType string `toml:"resource_type"`
ResourceLabels map[string]string `toml:"resource_labels"`
client *monitoring.MetricClient
}
@ -47,11 +49,21 @@ const (
)
var sampleConfig = `
# GCP Project
project = "erudite-bloom-151019"
[[outputs.stackdriver]]
# GCP Project
project = "erudite-bloom-151019"
# The namespace for the metric descriptor
namespace = "telegraf"
# The namespace for the metric descriptor
namespace = "telegraf"
# Custom resource type
resource_type = "generic_node"
# Additonal resource labels
[outputs.stackdriver.resource_labels]
node_id = "$HOSTNAME"
namespace = "myapp"
location = "eu-north0"
`
// Connect initiates the primary connection to the GCP project.
@ -64,6 +76,16 @@ func (s *Stackdriver) Connect() error {
return fmt.Errorf("Namespace is a required field for stackdriver output")
}
if s.ResourceType == "" {
s.ResourceType = "global"
}
if s.ResourceLabels == nil {
s.ResourceLabels = make(map[string]string, 1)
}
s.ResourceLabels["project_id"] = s.Project
if s.client == nil {
ctx := context.Background()
client, err := monitoring.NewMetricClient(ctx)
@ -137,10 +159,8 @@ func (s *Stackdriver) Write(metrics []telegraf.Metric) error {
},
MetricKind: metricKind,
Resource: &monitoredrespb.MonitoredResource{
Type: "global",
Labels: map[string]string{
"project_id": s.Project,
},
Type: s.ResourceType,
Labels: s.ResourceLabels,
},
Points: []*monitoringpb.Point{
dataPoint,

View File

@ -97,6 +97,42 @@ func TestWrite(t *testing.T) {
require.NoError(t, err)
err = s.Write(testutil.MockMetrics())
require.NoError(t, err)
request := mockMetric.reqs[0].(*monitoringpb.CreateTimeSeriesRequest)
require.Equal(t, request.TimeSeries[0].Resource.Type, "global")
require.Equal(t, request.TimeSeries[0].Resource.Labels["project_id"], "projects/[PROJECT]")
}
func TestWriteResourceTypeAndLabels(t *testing.T) {
expectedResponse := &emptypb.Empty{}
mockMetric.err = nil
mockMetric.reqs = nil
mockMetric.resps = append(mockMetric.resps[:0], expectedResponse)
c, err := monitoring.NewMetricClient(context.Background(), clientOpt)
if err != nil {
t.Fatal(err)
}
s := &Stackdriver{
Project: fmt.Sprintf("projects/%s", "[PROJECT]"),
Namespace: "test",
ResourceType: "foo",
ResourceLabels: map[string]string{
"mylabel": "myvalue",
},
client: c,
}
err = s.Connect()
require.NoError(t, err)
err = s.Write(testutil.MockMetrics())
require.NoError(t, err)
request := mockMetric.reqs[0].(*monitoringpb.CreateTimeSeriesRequest)
require.Equal(t, request.TimeSeries[0].Resource.Type, "foo")
require.Equal(t, request.TimeSeries[0].Resource.Labels["project_id"], "projects/[PROJECT]")
require.Equal(t, request.TimeSeries[0].Resource.Labels["mylabel"], "myvalue")
}
func TestWriteAscendingTime(t *testing.T) {