Skip to content
This repository was archived by the owner on Dec 3, 2024. It is now read-only.

Commit e5aea7c

Browse files
committed
Replace TaskInfo with Config.
This changes the method for creating a new stackdriver.Sink. NewSink will now apply defaults.
1 parent de9a6c4 commit e5aea7c

File tree

4 files changed

+109
-34
lines changed

4 files changed

+109
-34
lines changed

README.md

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,9 @@ This library provides a stackdriver sink for applications instrumented with the
1414
import "github.com/google/go-metrics-stackdriver"
1515
...
1616
client, _ := monitoring.NewMetricClient(context.Background())
17-
ss := stackdriver.NewSink(60*time.Second, &stackdriver.TaskInfo{
17+
ss := stackdriver.NewSink(client, &stackdriver.Config{
1818
ProjectID: projectID,
19-
Location: "us-east1-c",
20-
Job: "example-app",
21-
}, client)
19+
})
2220
...
2321
ss.SetGauge([]string{"foo"}, 42)
2422
ss.IncrCounter([]string{"baz"}, 1)

example/main.go

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -46,16 +46,13 @@ func main() {
4646
projectID = os.Getenv("GOOGLE_CLOUD_PROJECT")
4747
}
4848

49-
taskInfo := &stackdriver.TaskInfo{
50-
ProjectID: projectID,
51-
Location: "us-east1-c",
52-
Job: "example",
53-
}
54-
55-
log.Printf("initializing sink: %v", taskInfo)
49+
log.Printf("initializing sink")
5650

5751
// create sink
58-
ss := stackdriver.NewSink(60*time.Second, taskInfo, client)
52+
ss := stackdriver.NewSink(client, &stackdriver.Config{
53+
ProjectID: projectID,
54+
Location: "us-east1-c",
55+
})
5956

6057
// capture ctrl+c
6158
c := make(chan os.Signal, 1)

stackdriver.go

Lines changed: 88 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,14 @@ import (
1818
"context"
1919
"fmt"
2020
"log"
21+
"os"
2122
"path"
23+
"strconv"
2224
"strings"
2325
"sync"
2426
"time"
2527

28+
"cloud.google.com/go/compute/metadata"
2629
monitoring "cloud.google.com/go/monitoring/apiv3"
2730
metrics "github.com/armon/go-metrics"
2831
googlepb "github.com/golang/protobuf/ptypes/timestamp"
@@ -48,17 +51,46 @@ type Sink struct {
4851
histograms map[string]*histogram
4952

5053
bucketer BucketFn
51-
taskInfo *TaskInfo
54+
taskInfo *taskInfo
5255

5356
mu sync.Mutex
5457
}
5558

56-
// TaskInfo must uniquely identify the process that the Sink metrics are
57-
// collecting. These will be added as labels to the metric so that values can
58-
// be filtered/aggregated correctly.
59-
//
60-
// https://cloud.google.com/monitoring/api/resources#tag_generic_task
61-
type TaskInfo struct {
59+
// Config options for the stackdriver Sink.
60+
type Config struct {
61+
// The Google Cloud Project ID to publish metrics to.
62+
// Optional. GCP instance metadata is used to determine the ProjectID if
63+
// not set.
64+
ProjectID string
65+
// The bucketer is used to determine histogram bucket boundaries
66+
// for the sampled metrics.
67+
// Optional. Defaults to DefaultBucketer
68+
Bucketer BucketFn
69+
// The interval between sampled metric points. Must be > 1 minute.
70+
// https://cloud.google.com/monitoring/custom-metrics/creating-metrics#writing-ts
71+
// Optional. Defaults to 1 minute.
72+
ReportingInterval time.Duration
73+
74+
// The location of the running task. See:
75+
// https://cloud.google.com/monitoring/api/resources#tag_generic_task
76+
// Optional. GCP instance metadata is used to determine the location,
77+
// otherwise it defaults to 'global'.
78+
Location string
79+
// The namespace for the running task. See:
80+
// https://cloud.google.com/monitoring/api/resources#tag_generic_task
81+
// Optional. Defaults to 'default'.
82+
Namespace string
83+
// The job name for the running task. See:
84+
// https://cloud.google.com/monitoring/api/resources#tag_generic_task
85+
// Optional. Defaults to the running program name.
86+
Job string
87+
// The task ID for the running task. See:
88+
// https://cloud.google.com/monitoring/api/resources#tag_generic_task
89+
// Optional. Defaults to a combination of hostname+pid.
90+
TaskID string
91+
}
92+
93+
type taskInfo struct {
6294
ProjectID string
6395
Location string
6496
Namespace string
@@ -73,25 +105,62 @@ type BucketFn func(string) []float64
73105
// DefaultBucketer is the default BucketFn used to determing bucketing values
74106
// for metrics.
75107
func DefaultBucketer(name string) []float64 {
76-
return []float64{10.0, 25.0, 50.0, 100.0, 150.0, 250.0, 300.0, 500.0, 1000.0, 2000.0, 5000.0}
108+
return []float64{10.0, 25.0, 50.0, 100.0, 150.0, 200.0, 250.0, 300.0, 500.0, 1000.0, 1500.0, 2000.0, 3000.0, 4000.0, 5000.0}
77109
}
78110

79111
// NewSink creates a Sink to flush metrics to stackdriver every interval. The
80112
// interval should be greater than 1 minute.
81-
func NewSink(interval time.Duration, taskInfo *TaskInfo, client *monitoring.MetricClient) *Sink {
82-
return NewSinkCustomBucket(interval, taskInfo, client, DefaultBucketer)
83-
}
84-
85-
// NewSinkCustomBucket creates a Sink to flush metrics to stackdriver every
86-
// interval. The bucketer is used to determine histogram bucket thresholds
87-
// for the sampled metrics.
88-
func NewSinkCustomBucket(interval time.Duration, taskInfo *TaskInfo, client *monitoring.MetricClient, bucketer BucketFn) *Sink {
113+
func NewSink(client *monitoring.MetricClient, config *Config) *Sink {
89114
s := &Sink{
90115
client: client,
91-
interval: interval,
92-
bucketer: bucketer,
93-
taskInfo: taskInfo,
116+
bucketer: config.Bucketer,
117+
interval: config.ReportingInterval,
118+
taskInfo: &taskInfo{
119+
ProjectID: config.ProjectID,
120+
Location: config.Location,
121+
Namespace: config.Namespace,
122+
Job: config.Job,
123+
TaskID: config.TaskID,
124+
},
125+
}
126+
127+
// apply defaults if not configured explicitly
128+
if s.bucketer == nil {
129+
s.bucketer = DefaultBucketer
130+
}
131+
if s.interval < 60*time.Second {
132+
s.interval = 60 * time.Second
94133
}
134+
if s.taskInfo.ProjectID == "" {
135+
id, err := metadata.ProjectID()
136+
if err != nil {
137+
log.Printf("could not configure go-metrics stackdriver ProjectID: %s", err)
138+
}
139+
s.taskInfo.ProjectID = id
140+
}
141+
if s.taskInfo.Location == "" {
142+
// attempt to detect
143+
zone, err := metadata.Zone()
144+
if err != nil {
145+
log.Printf("could not configure go-metric stackdriver location: %s", err)
146+
zone = "global"
147+
}
148+
s.taskInfo.Location = zone
149+
}
150+
if s.taskInfo.Namespace == "" {
151+
s.taskInfo.Namespace = "default"
152+
}
153+
if s.taskInfo.Job == "" {
154+
s.taskInfo.Job = path.Base(os.Args[0])
155+
}
156+
if s.taskInfo.TaskID == "" {
157+
hostname, err := os.Hostname()
158+
if err != nil {
159+
hostname = "localhost"
160+
}
161+
s.taskInfo.TaskID = "go-" + strconv.Itoa(os.Getpid()) + "@" + hostname
162+
}
163+
95164
s.reset()
96165

97166
// run cancelable goroutine that reports on interval

stackdriver_test.go

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ import (
3131
)
3232

3333
func benchmarkAddSample(concurrency int, b *testing.B) {
34-
ss := NewSink(100*time.Millisecond, &TaskInfo{}, nil)
34+
ss := newTestSink(100*time.Millisecond, nil)
3535
var wg sync.WaitGroup
3636

3737
for i := 0; i < concurrency; i++ {
@@ -61,7 +61,7 @@ func BenchmarkAddSample100(b *testing.B) { benchmarkAddSample(100, b) }
6161
// snapshotted. We isolate and benchmark this copy since all metrics collection
6262
// functions will block until the copy completes.
6363
func benchmarkCopy(samples, gauges, counters int, b *testing.B) {
64-
ss := NewSink(0*time.Second, &TaskInfo{}, nil)
64+
ss := newTestSink(0*time.Second, nil)
6565
for i := 0; i < samples; i++ {
6666
ss.AddSample([]string{fmt.Sprintf("%d", i)}, float32(i)*0.3)
6767
}
@@ -87,7 +87,7 @@ func BenchmarkReport50(b *testing.B) { benchmarkCopy(50, 50, 50, b) }
8787
func BenchmarkReport100(b *testing.B) { benchmarkCopy(100, 100, 100, b) }
8888

8989
func TestSample(t *testing.T) {
90-
ss := NewSink(0*time.Second, &TaskInfo{}, nil)
90+
ss := newTestSink(0*time.Second, nil)
9191

9292
tests := []struct {
9393
name string
@@ -272,3 +272,14 @@ func (s *mockMetricServer) CreateTimeSeries(ctx context.Context, req *monitoring
272272
}
273273
return nil, errors.New("unimplemented")
274274
}
275+
276+
// Skips defaults that are not appropriate for tests.
277+
func newTestSink(interval time.Duration, client *monitoring.MetricClient) *Sink {
278+
s := &Sink{}
279+
s.taskInfo = &taskInfo{}
280+
s.interval = interval
281+
s.bucketer = DefaultBucketer
282+
s.reset()
283+
go s.flushMetrics(context.Background())
284+
return s
285+
}

0 commit comments

Comments
 (0)