@@ -24,10 +24,13 @@ import (
24
24
"google.golang.org/api/support/bundler"
25
25
"google.golang.org/grpc"
26
26
27
+ "go.opencensus.io/stats/view"
27
28
"go.opencensus.io/trace"
28
29
29
- agentcommonpb "github.com/census-instrumentation/opencensus-proto/gen-go/agent/common/v1"
30
+ commonpb "github.com/census-instrumentation/opencensus-proto/gen-go/agent/common/v1"
31
+ agentmetricspb "github.com/census-instrumentation/opencensus-proto/gen-go/agent/metrics/v1"
30
32
agenttracepb "github.com/census-instrumentation/opencensus-proto/gen-go/agent/trace/v1"
33
+ metricspb "github.com/census-instrumentation/opencensus-proto/gen-go/metrics/v1"
31
34
tracepb "github.com/census-instrumentation/opencensus-proto/gen-go/trace/v1"
32
35
)
33
36
@@ -41,6 +44,7 @@ func init() {
41
44
}
42
45
43
46
var _ trace.Exporter = (* Exporter )(nil )
47
+ var _ view.Exporter = (* Exporter )(nil )
44
48
45
49
type Exporter struct {
46
50
connectionState int32
@@ -53,7 +57,8 @@ type Exporter struct {
53
57
serviceName string
54
58
canDialInsecure bool
55
59
traceExporter agenttracepb.TraceService_ExportClient
56
- nodeInfo * agentcommonpb.Node
60
+ metricsExporter agentmetricspb.MetricsService_ExportClient
61
+ nodeInfo * commonpb.Node
57
62
grpcClientConn * grpc.ClientConn
58
63
reconnectionPeriod time.Duration
59
64
@@ -64,6 +69,11 @@ type Exporter struct {
64
69
backgroundConnectionDoneCh chan bool
65
70
66
71
traceBundler * bundler.Bundler
72
+
73
+ // viewDataBundler is the bundler to enable conversion
74
+ // from OpenCensus-Go view.Data to metricspb.Metric.
75
+ // Please do not confuse it with metricsBundler!
76
+ viewDataBundler * bundler.Bundler
67
77
}
68
78
69
79
func NewExporter (opts ... ExporterOption ) (* Exporter , error ) {
@@ -90,7 +100,14 @@ func NewUnstartedExporter(opts ...ExporterOption) (*Exporter, error) {
90
100
traceBundler .DelayThreshold = 2 * time .Second
91
101
traceBundler .BundleCountThreshold = spanDataBufferSize
92
102
e .traceBundler = traceBundler
93
- e .nodeInfo = createNodeInfo (e .serviceName )
103
+
104
+ viewDataBundler := bundler .NewBundler ((* view .Data )(nil ), func (bundle interface {}) {
105
+ e .uploadViewData (bundle .([]* view.Data ))
106
+ })
107
+ viewDataBundler .DelayThreshold = 2 * time .Second
108
+ viewDataBundler .BundleCountThreshold = 500 // TODO: (@odeke-em) make this configurable.
109
+ e .viewDataBundler = viewDataBundler
110
+ e .nodeInfo = NodeWithStartTime (e .serviceName )
94
111
95
112
return e , nil
96
113
}
@@ -155,25 +172,36 @@ func (ae *Exporter) enableConnectionStreams(cc *grpc.ClientConn) error {
155
172
ae .grpcClientConn = cc
156
173
ae .mu .Unlock ()
157
174
175
+ if err := ae .createTraceServiceConnection (ae .grpcClientConn , nodeInfo ); err != nil {
176
+ return err
177
+ }
178
+
179
+ return ae .createMetricsServiceConnection (ae .grpcClientConn , nodeInfo )
180
+ }
181
+
182
+ func (ae * Exporter ) createTraceServiceConnection (cc * grpc.ClientConn , node * commonpb.Node ) error {
158
183
// Initiate the trace service by sending over node identifier info.
159
184
traceSvcClient := agenttracepb .NewTraceServiceClient (cc )
160
185
traceExporter , err := traceSvcClient .Export (context .Background ())
161
186
if err != nil {
162
187
return fmt .Errorf ("Exporter.Start:: TraceServiceClient: %v" , err )
163
188
}
164
189
165
- firstTraceMessage := & agenttracepb.ExportTraceServiceRequest {Node : nodeInfo }
190
+ firstTraceMessage := & agenttracepb.ExportTraceServiceRequest {Node : node }
166
191
if err := traceExporter .Send (firstTraceMessage ); err != nil {
167
192
return fmt .Errorf ("Exporter.Start:: Failed to initiate the Config service: %v" , err )
168
193
}
194
+
195
+ ae .mu .Lock ()
169
196
ae .traceExporter = traceExporter
197
+ ae .mu .Unlock ()
170
198
171
199
// Initiate the config service by sending over node identifier info.
172
200
configStream , err := traceSvcClient .Config (context .Background ())
173
201
if err != nil {
174
202
return fmt .Errorf ("Exporter.Start:: ConfigStream: %v" , err )
175
203
}
176
- firstCfgMessage := & agenttracepb.CurrentLibraryConfig {Node : nodeInfo }
204
+ firstCfgMessage := & agenttracepb.CurrentLibraryConfig {Node : node }
177
205
if err := configStream .Send (firstCfgMessage ); err != nil {
178
206
return fmt .Errorf ("Exporter.Start:: Failed to initiate the Config service: %v" , err )
179
207
}
@@ -185,6 +213,26 @@ func (ae *Exporter) enableConnectionStreams(cc *grpc.ClientConn) error {
185
213
return nil
186
214
}
187
215
216
+ func (ae * Exporter ) createMetricsServiceConnection (cc * grpc.ClientConn , node * commonpb.Node ) error {
217
+ metricsSvcClient := agentmetricspb .NewMetricsServiceClient (cc )
218
+ metricsExporter , err := metricsSvcClient .Export (context .Background ())
219
+ if err != nil {
220
+ return fmt .Errorf ("MetricsExporter: failed to start the service client: %v" , err )
221
+ }
222
+ // Initiate the metrics service by sending over the first message just containing the Node.
223
+ firstMetricsMessage := & agentmetricspb.ExportMetricsServiceRequest {Node : node }
224
+ if err := metricsExporter .Send (firstMetricsMessage ); err != nil {
225
+ return fmt .Errorf ("MetricsExporter:: failed to send the first message: %v" , err )
226
+ }
227
+
228
+ ae .mu .Lock ()
229
+ ae .metricsExporter = metricsExporter
230
+ ae .mu .Unlock ()
231
+
232
+ // With that we are good to go and can start sending metrics
233
+ return nil
234
+ }
235
+
188
236
func (ae * Exporter ) dialToAgent () (* grpc.ClientConn , error ) {
189
237
addr := ae .prepareAgentAddress ()
190
238
var dialOpts []grpc.DialOption
@@ -278,6 +326,13 @@ func (ae *Exporter) ExportSpan(sd *trace.SpanData) {
278
326
_ = ae .traceBundler .Add (sd , 1 )
279
327
}
280
328
329
+ func (ae * Exporter ) ExportView (vd * view.Data ) {
330
+ if vd == nil {
331
+ return
332
+ }
333
+ _ = ae .viewDataBundler .Add (vd , 1 )
334
+ }
335
+
281
336
func ocSpanDataToPbSpans (sdl []* trace.SpanData ) []* tracepb.Span {
282
337
if len (sdl ) == 0 {
283
338
return nil
@@ -314,6 +369,52 @@ func (ae *Exporter) uploadTraces(sdl []*trace.SpanData) {
314
369
}
315
370
}
316
371
372
+ func ocViewDataToPbMetrics (vdl []* view.Data ) []* metricspb.Metric {
373
+ if len (vdl ) == 0 {
374
+ return nil
375
+ }
376
+ metrics := make ([]* metricspb.Metric , 0 , len (vdl ))
377
+ for _ , vd := range vdl {
378
+ if vd != nil {
379
+ vmetrics , err := viewDataToMetrics (vd )
380
+ if err == nil && vmetrics != nil {
381
+ // TODO: (@odeke-em) somehow report this error.
382
+ metrics = append (metrics , vmetrics )
383
+ }
384
+ }
385
+ }
386
+ return metrics
387
+ }
388
+
389
+ func (ae * Exporter ) uploadViewData (vdl []* view.Data ) {
390
+ select {
391
+ case <- ae .stopCh :
392
+ return
393
+
394
+ default :
395
+ if ! ae .connected () {
396
+ println ("not connected!" )
397
+ return
398
+ }
399
+
400
+ protoMetrics := ocViewDataToPbMetrics (vdl )
401
+ if len (protoMetrics ) == 0 {
402
+ return
403
+ }
404
+ err := ae .metricsExporter .Send (& agentmetricspb.ExportMetricsServiceRequest {
405
+ Metrics : protoMetrics ,
406
+ // TODO:(@odeke-em)
407
+ // a) Figure out how to derive a Node from the environment
408
+ // b) Figure out how to derive a Resource from the environment
409
+ // or better letting users of the exporter configure it.
410
+ })
411
+ if err != nil {
412
+ ae .setStateDisconnected ()
413
+ }
414
+ }
415
+ }
416
+
317
417
func (ae * Exporter ) Flush () {
318
418
ae .traceBundler .Flush ()
419
+ ae .viewDataBundler .Flush ()
319
420
}
0 commit comments