@@ -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.
75107func 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
0 commit comments