@@ -8,22 +8,97 @@ import (
88 "go.opentelemetry.io/otel"
99 "go.opentelemetry.io/otel/attribute"
1010 "go.opentelemetry.io/otel/codes"
11+ "go.opentelemetry.io/otel/propagation"
1112 semconv "go.opentelemetry.io/otel/semconv/v1.12.0"
1213 "go.opentelemetry.io/otel/trace"
1314)
1415
16+ type config struct {
17+ Tracer trace.Tracer
18+ Propagators propagation.TextMapPropagator
19+ SpanStartOptions []trace.SpanStartOption
20+ SpanNameFormatter func (string , * http.Request ) string
21+ TracerProvider trace.TracerProvider
22+ }
23+
24+ type OpenTelemetryOption interface {
25+ apply (* config )
26+ }
27+
28+ type optionFunc func (* config )
29+
30+ func (o optionFunc ) apply (c * config ) {
31+ o (c )
32+ }
33+
34+ // WithTracerProvider specifies a tracer provider to use for creating a tracer.
35+ // If none is specified, the global provider is used.
36+ func WithTracerProvider (provider trace.TracerProvider ) OpenTelemetryOption {
37+ return optionFunc (func (c * config ) {
38+ if provider != nil {
39+ c .TracerProvider = provider
40+ }
41+ })
42+ }
43+
44+ // WithPropagators configures specific propagators. If this
45+ // option isn't specified, then the global TextMapPropagator is used.
46+ func WithPropagators (ps propagation.TextMapPropagator ) OpenTelemetryOption {
47+ return optionFunc (func (c * config ) {
48+ if ps != nil {
49+ c .Propagators = ps
50+ }
51+ })
52+ }
53+
54+ // WithSpanOptions configures an additional set of
55+ // trace.SpanOptions, which are applied to each new span.
56+ func WithSpanOptions (opts ... trace.SpanStartOption ) OpenTelemetryOption {
57+ return optionFunc (func (c * config ) {
58+ c .SpanStartOptions = append (c .SpanStartOptions , opts ... )
59+ })
60+ }
61+
1562type openTelemetryTransport struct {
16- transport runtime.ClientTransport
17- host string
18- opts []trace.SpanStartOption
63+ transport runtime.ClientTransport
64+ host string
65+ spanStartOptions []trace.SpanStartOption
66+ propagator propagation.TextMapPropagator
67+ provider trace.TracerProvider
68+ tracer trace.Tracer
69+ config * config
70+ }
71+
72+ // newConfig creates a new config struct and applies opts to it.
73+ func newConfig (opts ... OpenTelemetryOption ) * config {
74+ c := & config {
75+ Propagators : otel .GetTextMapPropagator (),
76+ }
77+
78+ for _ , opt := range opts {
79+ opt .apply (c )
80+ }
81+
82+ // Tracer is only initialized if manually specified. Otherwise, can be passed with the tracing context.
83+ if c .TracerProvider != nil {
84+ c .Tracer = newTracer (c .TracerProvider )
85+ }
86+
87+ return c
1988}
2089
21- func newOpenTelemetryTransport (transport runtime.ClientTransport , host string , opts []trace.SpanStartOption ) runtime.ClientTransport {
22- return & openTelemetryTransport {
23- transport : transport ,
24- host : host ,
25- opts : opts ,
90+ func newOpenTelemetryTransport (transport runtime.ClientTransport , host string , opts []OpenTelemetryOption ) * openTelemetryTransport {
91+ t := & openTelemetryTransport {
92+ transport : transport ,
93+ host : host ,
94+ provider : otel .GetTracerProvider (),
95+ propagator : otel .GetTextMapPropagator (),
2696 }
97+
98+ c := newConfig (opts ... )
99+ t .config = c
100+
101+ return t
27102}
28103
29104func (t * openTelemetryTransport ) Submit (op * runtime.ClientOperation ) (interface {}, error ) {
@@ -42,7 +117,7 @@ func (t *openTelemetryTransport) Submit(op *runtime.ClientOperation) (interface{
42117 }()
43118
44119 op .Params = runtime .ClientRequestWriterFunc (func (req runtime.ClientRequest , reg strfmt.Registry ) error {
45- span = createOpenTelemetryClientSpan (op , req .GetHeaderParams (), t . host , t . opts )
120+ span = t . newOpenTelemetrySpan (op , req .GetHeaderParams ())
46121 return params .WriteToRequest (req , reg )
47122 })
48123
@@ -65,50 +140,42 @@ func (t *openTelemetryTransport) Submit(op *runtime.ClientOperation) (interface{
65140 return submit , err
66141}
67142
68- func createOpenTelemetryClientSpan ( op * runtime.ClientOperation , _ http.Header , host string , opts []trace. SpanStartOption ) trace.Span {
143+ func ( t * openTelemetryTransport ) newOpenTelemetrySpan ( op * runtime.ClientOperation , header http.Header ) trace.Span {
69144 ctx := op .Context
70- if span := trace .SpanFromContext (ctx ); span .IsRecording () {
71- // TODO: Can we get the version number for use with trace.WithInstrumentationVersion?
72- tracer := otel .GetTracerProvider ().Tracer ("" )
73145
74- ctx , span = tracer .Start (ctx , operationName (op ), opts ... )
75- op .Context = ctx
76-
77- //TODO: There's got to be a better way to do this without the request, right?
78- var scheme string
79- if len (op .Schemes ) == 1 {
80- scheme = op .Schemes [0 ]
146+ tracer := t .tracer
147+ if tracer == nil {
148+ if span := trace .SpanFromContext (ctx ); span .SpanContext ().IsValid () {
149+ tracer = newTracer (span .TracerProvider ())
150+ } else {
151+ tracer = newTracer (otel .GetTracerProvider ())
81152 }
153+ }
82154
83- span .SetAttributes (
84- attribute .String ("net.peer.name" , host ),
85- // attribute.String("net.peer.port", ""),
86- attribute .String (string (semconv .HTTPRouteKey ), op .PathPattern ),
87- attribute .String (string (semconv .HTTPMethodKey ), op .Method ),
88- attribute .String ("span.kind" , trace .SpanKindClient .String ()),
89- attribute .String ("http.scheme" , scheme ),
90- )
155+ ctx , span := tracer .Start (ctx , operationName (op ), t .spanStartOptions ... )
91156
92- return span
157+ // TODO: Can we get the underlying request so we can wire these bits up easily?
158+ // span.SetAttributes(semconv.HTTPClientAttributesFromHTTPRequest()...)
159+ var scheme string
160+ if len (op .Schemes ) == 1 {
161+ scheme = op .Schemes [0 ]
93162 }
94163
95- // if span != nil {
96- // opts = append(opts, ext.SpanKindRPCClient)
97- // span, _ = opentracing.StartSpanFromContextWithTracer(
98- // ctx, span.Tracer(), operationName(op), opts...)
164+ span .SetAttributes (
165+ attribute .String ("net.peer.name" , t .host ),
166+ // attribute.String("net.peer.port", ""),
167+ attribute .String (string (semconv .HTTPRouteKey ), op .PathPattern ),
168+ attribute .String (string (semconv .HTTPMethodKey ), op .Method ),
169+ attribute .String ("span.kind" , trace .SpanKindClient .String ()),
170+ attribute .String ("http.scheme" , scheme ),
171+ )
99172
100- // ext.Component.Set(span, "go-openapi")
101- // ext.PeerHostname.Set(span, host)
102- // span.SetTag("http.path", op.PathPattern)
103- // ext.HTTPMethod.Set(span, op.Method)
173+ carrier := propagation .HeaderCarrier (header )
174+ t .propagator .Inject (ctx , carrier )
104175
105- // _ = span.Tracer().Inject(
106- // span.Context(),
107- // opentracing.HTTPHeaders,
108- // opentracing.HTTPHeadersCarrier(header))
109-
110- // return span
111- // }
176+ return span
177+ }
112178
113- return nil
179+ func newTracer (tp trace.TracerProvider ) trace.Tracer {
180+ return tp .Tracer ("go-runtime" , trace .WithInstrumentationVersion ("1.0.0" ))
114181}
0 commit comments