@@ -16,35 +16,73 @@ package trace
16
16
17
17
import (
18
18
"context"
19
+ "errors"
19
20
"fmt"
21
+ "os"
22
+ "strings"
20
23
21
24
"go.opencensus.io/trace"
22
- "golang.org/x/xerrors"
25
+ "go.opentelemetry.io/otel"
26
+ "go.opentelemetry.io/otel/attribute"
27
+ "go.opentelemetry.io/otel/codes"
28
+ ottrace "go.opentelemetry.io/otel/trace"
23
29
"google.golang.org/api/googleapi"
24
30
"google.golang.org/genproto/googleapis/rpc/code"
25
31
"google.golang.org/grpc/status"
26
32
)
27
33
34
+ const (
35
+ telemetryPlatformTracing = "GOOGLE_API_GO_EXPERIMENTAL_TELEMETRY_PLATFORM_TRACING"
36
+ telemetryPlatformTracingOpenCensus = "opencensus"
37
+ telemetryPlatformTracingOpenTelemetry = "opentelemetry"
38
+ )
39
+
40
+ var (
41
+ // TODO(chrisdsmith): What is the correct name for the OpenTelemetry tracer?
42
+ OpenTelemetryTracerName string = "cloud.google.com/go/internal/trace"
43
+ )
44
+
45
+ func IsOpenCensusTracingEnabled () bool {
46
+ return ! IsOpenTelemetryTracingEnabled ()
47
+ }
48
+
49
+ func IsOpenTelemetryTracingEnabled () bool {
50
+ env := strings .TrimSpace (os .Getenv (telemetryPlatformTracing ))
51
+ return strings .ToLower (env ) == telemetryPlatformTracingOpenTelemetry
52
+ }
53
+
28
54
// StartSpan adds a span to the trace with the given name.
29
55
func StartSpan (ctx context.Context , name string ) context.Context {
30
- ctx , _ = trace .StartSpan (ctx , name )
56
+ if IsOpenTelemetryTracingEnabled () {
57
+ ctx , _ = otel .GetTracerProvider ().Tracer (OpenTelemetryTracerName ).Start (ctx , name )
58
+ } else {
59
+ ctx , _ = trace .StartSpan (ctx , name )
60
+ }
31
61
return ctx
32
62
}
33
63
34
64
// EndSpan ends a span with the given error.
35
65
func EndSpan (ctx context.Context , err error ) {
36
- span := trace .FromContext (ctx )
37
- if err != nil {
38
- span .SetStatus (toStatus (err ))
66
+ if IsOpenTelemetryTracingEnabled () {
67
+ span := ottrace .SpanFromContext (ctx )
68
+ if err != nil {
69
+ span .SetStatus (codes .Error , toOpenTelemetryStatusDescription (err ))
70
+ span .RecordError (err )
71
+ }
72
+ span .End ()
73
+ } else {
74
+ span := trace .FromContext (ctx )
75
+ if err != nil {
76
+ span .SetStatus (toStatus (err ))
77
+ }
78
+ span .End ()
39
79
}
40
- span .End ()
41
80
}
42
81
43
- // toStatus interrogates an error and converts it to an appropriate
44
- // OpenCensus status.
82
+ // toStatus converts an error to an equivalent OpenCensus status.
45
83
func toStatus (err error ) trace.Status {
46
84
var err2 * googleapi.Error
47
- if ok := xerrors .As (err , & err2 ); ok {
85
+ if ok := errors .As (err , & err2 ); ok {
48
86
return trace.Status {Code : httpStatusCodeToOCCode (err2 .Code ), Message : err2 .Message }
49
87
} else if s , ok := status .FromError (err ); ok {
50
88
return trace.Status {Code : int32 (s .Code ()), Message : s .Message ()}
@@ -53,6 +91,18 @@ func toStatus(err error) trace.Status {
53
91
}
54
92
}
55
93
94
+ // toOpenTelemetryStatus converts an error to an equivalent OpenTelemetry status description.
95
+ func toOpenTelemetryStatusDescription (err error ) string {
96
+ var err2 * googleapi.Error
97
+ if ok := errors .As (err , & err2 ); ok {
98
+ return err2 .Message
99
+ } else if s , ok := status .FromError (err ); ok {
100
+ return s .Message ()
101
+ } else {
102
+ return err .Error ()
103
+ }
104
+ }
105
+
56
106
// TODO(deklerk): switch to using OpenCensus function when it becomes available.
57
107
// Reference: https://github.com/googleapis/googleapis/blob/26b634d2724ac5dd30ae0b0cbfb01f07f2e4050e/google/rpc/code.proto
58
108
func httpStatusCodeToOCCode (httpStatusCode int ) int32 {
@@ -86,10 +136,25 @@ func httpStatusCodeToOCCode(httpStatusCode int) int32 {
86
136
}
87
137
}
88
138
139
+ // TracePrintf retrieves the current OpenCensus or OpenTelemetry span from context, then:
140
+ // * calls Span.Annotatef if OpenCensus is enabled; or
141
+ // * calls Span.AddEvent if OpenTelemetry is enabled.
142
+ //
89
143
// TODO: (odeke-em): perhaps just pass around spans due to the cost
90
144
// incurred from using trace.FromContext(ctx) yet we could avoid
91
145
// throwing away the work done by ctx, span := trace.StartSpan.
92
146
func TracePrintf (ctx context.Context , attrMap map [string ]interface {}, format string , args ... interface {}) {
147
+ if IsOpenTelemetryTracingEnabled () {
148
+ attrs := otAttrs (attrMap )
149
+ ottrace .SpanFromContext (ctx ).AddEvent (fmt .Sprintf (format , args ... ), ottrace .WithAttributes (attrs ... ))
150
+ } else {
151
+ attrs := ocAttrs (attrMap )
152
+ trace .FromContext (ctx ).Annotatef (attrs , format , args ... )
153
+ }
154
+ }
155
+
156
+ // ocAttrs converts a generic map to OpenCensus attributes.
157
+ func ocAttrs (attrMap map [string ]interface {}) []trace.Attribute {
93
158
var attrs []trace.Attribute
94
159
for k , v := range attrMap {
95
160
var a trace.Attribute
@@ -107,5 +172,27 @@ func TracePrintf(ctx context.Context, attrMap map[string]interface{}, format str
107
172
}
108
173
attrs = append (attrs , a )
109
174
}
110
- trace .FromContext (ctx ).Annotatef (attrs , format , args ... )
175
+ return attrs
176
+ }
177
+
178
+ // otAttrs converts a generic map to OpenTelemetry attributes.
179
+ func otAttrs (attrMap map [string ]interface {}) []attribute.KeyValue {
180
+ var attrs []attribute.KeyValue
181
+ for k , v := range attrMap {
182
+ var a attribute.KeyValue
183
+ switch v := v .(type ) {
184
+ case string :
185
+ a = attribute .Key (k ).String (v )
186
+ case bool :
187
+ a = attribute .Key (k ).Bool (v )
188
+ case int :
189
+ a = attribute .Key (k ).Int (v )
190
+ case int64 :
191
+ a = attribute .Key (k ).Int64 (v )
192
+ default :
193
+ a = attribute .Key (k ).String (fmt .Sprintf ("%#v" , v ))
194
+ }
195
+ attrs = append (attrs , a )
196
+ }
197
+ return attrs
111
198
}
0 commit comments