@@ -17,8 +17,11 @@ package exporterparser
1717import (
1818 "context"
1919 "fmt"
20+ "sync"
21+ "time"
2022
2123 "contrib.go.opencensus.io/exporter/stackdriver"
24+ "go.opencensus.io/trace"
2225
2326 "github.com/census-instrumentation/opencensus-service/data"
2427 "github.com/census-instrumentation/opencensus-service/exporter"
@@ -28,6 +31,7 @@ type stackdriverConfig struct {
2831 ProjectID string `yaml:"project,omitempty"`
2932 EnableTracing bool `yaml:"enable_tracing,omitempty"`
3033 EnableMetrics bool `yaml:"enable_metrics,omitempty"`
34+ MetricPrefix string `yaml:"metric_prefix,omitempty"`
3135}
3236
3337type stackdriverExporter struct {
@@ -65,20 +69,35 @@ func StackdriverTraceExportersFromYAML(config []byte) (tes []exporter.TraceExpor
6569 }
6670
6771 sde , serr := stackdriver .NewExporter (stackdriver.Options {
68- ProjectID : sc .ProjectID ,
72+ ProjectID : sc .ProjectID ,
73+ MetricPrefix : sc .MetricPrefix ,
74+
75+ // Stackdriver Metrics mandates a minimum of 60 seconds for
76+ // reporting metrics. We have to enforce this as per the advisory
77+ // at https://cloud.google.com/monitoring/custom-metrics/creating-metrics#writing-ts
78+ // which says:
79+ //
80+ // "If you want to write more than one point to the same time series, then use a separate call
81+ // to the timeSeries.create method for each point. Don't make the calls faster than one time per
82+ // minute. If you are adding data points to different time series, then there is no rate limitation."
83+ BundleDelayThreshold : 61 * time .Second ,
6984 })
7085 if serr != nil {
7186 return nil , nil , nil , fmt .Errorf ("Cannot configure Stackdriver Trace exporter: %v" , serr )
7287 }
7388
74- stexp := & stackdriverExporter {exporter : sde }
89+ exp := & stackdriverExporter {
90+ exporter : sde ,
91+ }
92+
7593 if sc .EnableTracing {
76- tes = append (tes , stexp )
94+ tes = append (tes , exp )
7795 }
96+
7897 if sc .EnableMetrics {
79- // TODO: (@odeke-em, @songya23) implement ExportMetrics for Stackdriver.
80- // mes = append(mes, oexp)
98+ mes = append (mes , exp )
8199 }
100+
82101 doneFns = append (doneFns , func () error {
83102 sde .Flush ()
84103 return nil
@@ -92,3 +111,29 @@ func (sde *stackdriverExporter) ExportSpans(ctx context.Context, td data.TraceDa
92111 // upload can use the context and information from the Node.
93112 return exportSpans (ctx , "stackdriver" , sde .exporter , td )
94113}
114+
115+ var _ exporter.MetricsExporter = (* stackdriverExporter )(nil )
116+
117+ func (sde * stackdriverExporter ) ExportMetricsData (ctx context.Context , md data.MetricsData ) error {
118+ ctx , span := trace .StartSpan (ctx ,
119+ "opencensus.service.exporter.stackdriver.ExportMetricsData" ,
120+ trace .WithSampler (trace .NeverSample ()))
121+ defer span .End ()
122+
123+ var setErrorOnce sync.Once
124+
125+ for _ , metric := range md .Metrics {
126+ err := sde .exporter .ExportMetric (ctx , md .Node , md .Resource , metric )
127+ if err != nil {
128+ setErrorOnce .Do (func () {
129+ span .SetStatus (trace.Status {Code : trace .StatusCodeInternal , Message : err .Error ()})
130+ })
131+
132+ span .Annotate ([]trace.Attribute {
133+ trace .StringAttribute ("error" , err .Error ()),
134+ }, "Error encountered" )
135+ }
136+ }
137+
138+ return nil
139+ }
0 commit comments