@@ -23,6 +23,8 @@ import (
2323 "context"
2424 "errors"
2525 "fmt"
26+ "github.com/golang/protobuf/proto"
27+ "github.com/golang/protobuf/ptypes/any"
2628 "github.com/golang/protobuf/ptypes/timestamp"
2729 "go.opencensus.io/trace"
2830
4143 errUnspecifiedMetricKind = errors .New ("metric kind is unpsecified" )
4244)
4345
46+ const (
47+ exemplarAttachmentTypeString = "type.googleapis.com/google.protobuf.StringValue"
48+ exemplarAttachmentTypeSpanCtx = "type.googleapis.com/google.monitoring.v3.SpanContext"
49+
50+ // TODO(songy23): add support for this.
51+ // exemplarAttachmentTypeDroppedLabels = "type.googleapis.com/google.monitoring.v3.DroppedLabels"
52+ )
53+
4454// ExportMetrics exports OpenCensus Metrics to Stackdriver Monitoring.
4555func (se * statsExporter ) ExportMetrics (ctx context.Context , metrics []* metricdata.Metric ) error {
4656 if len (metrics ) == 0 {
@@ -340,7 +350,7 @@ func (se *statsExporter) metricTsToMpbPoint(ts *metricdata.TimeSeries, metricKin
340350 startTime = nil
341351 }
342352
343- spt , err := metricPointToMpbPoint (startTime , & pt )
353+ spt , err := metricPointToMpbPoint (startTime , & pt , se . o . ProjectID )
344354 if err != nil {
345355 return nil , err
346356 }
@@ -349,12 +359,12 @@ func (se *statsExporter) metricTsToMpbPoint(ts *metricdata.TimeSeries, metricKin
349359 return sptl , nil
350360}
351361
352- func metricPointToMpbPoint (startTime * timestamp.Timestamp , pt * metricdata.Point ) (* monitoringpb.Point , error ) {
362+ func metricPointToMpbPoint (startTime * timestamp.Timestamp , pt * metricdata.Point , projectID string ) (* monitoringpb.Point , error ) {
353363 if pt == nil {
354364 return nil , nil
355365 }
356366
357- mptv , err := metricPointToMpbValue (pt )
367+ mptv , err := metricPointToMpbValue (pt , projectID )
358368 if err != nil {
359369 return nil , err
360370 }
@@ -369,7 +379,7 @@ func metricPointToMpbPoint(startTime *timestamp.Timestamp, pt *metricdata.Point)
369379 return mpt , nil
370380}
371381
372- func metricPointToMpbValue (pt * metricdata.Point ) (* monitoringpb.TypedValue , error ) {
382+ func metricPointToMpbValue (pt * metricdata.Point , projectID string ) (* monitoringpb.TypedValue , error ) {
373383 if pt == nil {
374384 return nil , nil
375385 }
@@ -423,18 +433,67 @@ func metricPointToMpbValue(pt *metricdata.Point) (*monitoringpb.TypedValue, erro
423433 },
424434 }
425435 }
426- mv .DistributionValue .BucketCounts = addZeroBucketCountOnCondition (insertZeroBound , metricBucketToBucketCounts (dv .Buckets )... )
436+ bucketCounts , exemplars := metricBucketToBucketCountsAndExemplars (dv .Buckets , projectID )
437+ mv .DistributionValue .BucketCounts = addZeroBucketCountOnCondition (insertZeroBound , bucketCounts ... )
438+ mv .DistributionValue .Exemplars = exemplars
427439
428440 tval = & monitoringpb.TypedValue {Value : mv }
429441 }
430442
431443 return tval , err
432444}
433445
434- func metricBucketToBucketCounts (buckets []metricdata.Bucket ) []int64 {
446+ func metricBucketToBucketCountsAndExemplars (buckets []metricdata.Bucket , projectID string ) ( []int64 , [] * distributionpb. Distribution_Exemplar ) {
435447 bucketCounts := make ([]int64 , len (buckets ))
448+ var exemplars []* distributionpb.Distribution_Exemplar
436449 for i , bucket := range buckets {
437450 bucketCounts [i ] = bucket .Count
451+ if bucket .Exemplar != nil {
452+ exemplars = append (exemplars , metricExemplarToPbExemplar (bucket .Exemplar , projectID ))
453+ }
454+ }
455+ return bucketCounts , exemplars
456+ }
457+
458+ func metricExemplarToPbExemplar (exemplar * metricdata.Exemplar , projectID string ) * distributionpb.Distribution_Exemplar {
459+ return & distributionpb.Distribution_Exemplar {
460+ Value : exemplar .Value ,
461+ Timestamp : timestampProto (exemplar .Timestamp ),
462+ Attachments : attachmentsToPbAttachments (exemplar .Attachments , projectID ),
463+ }
464+ }
465+
466+ func attachmentsToPbAttachments (attachments metricdata.Attachments , projectID string ) []* any.Any {
467+ var pbAttachments []* any.Any
468+ for _ , v := range attachments {
469+ switch v .(type ) {
470+ case trace.SpanContext :
471+ spanCtx , _ := v .(trace.SpanContext )
472+ pbAttachments = append (pbAttachments , toPbSpanCtxAttachment (spanCtx , projectID ))
473+ default :
474+ // Treat everything else as plain string for now.
475+ // TODO(songy23): add support for dropped label attachments.
476+ pbAttachments = append (pbAttachments , toPbStringAttachment (v ))
477+ }
478+ }
479+ return pbAttachments
480+ }
481+
482+ func toPbStringAttachment (v interface {}) * any.Any {
483+ s := fmt .Sprintf ("%v" , v )
484+ return & any.Any {
485+ TypeUrl : exemplarAttachmentTypeString ,
486+ Value : []byte (s ),
487+ }
488+ }
489+
490+ func toPbSpanCtxAttachment (spanCtx trace.SpanContext , projectID string ) * any.Any {
491+ pbSpanCtx := monitoringpb.SpanContext {
492+ SpanName : fmt .Sprintf ("projects/%s/traces/%s/spans/%s" , projectID , spanCtx .TraceID .String (), spanCtx .SpanID .String ()),
493+ }
494+ bytes , _ := proto .Marshal (& pbSpanCtx )
495+ return & any.Any {
496+ TypeUrl : exemplarAttachmentTypeSpanCtx ,
497+ Value : bytes ,
438498 }
439- return bucketCounts
440499}
0 commit comments