podman

Форк
0
207 строк · 5.4 Кб
1
package client
2

3
import (
4
	"fmt"
5
	"net/http"
6
	"strings"
7

8
	"github.com/go-openapi/runtime"
9
	"github.com/go-openapi/strfmt"
10
	"go.opentelemetry.io/otel"
11
	"go.opentelemetry.io/otel/attribute"
12
	"go.opentelemetry.io/otel/codes"
13
	"go.opentelemetry.io/otel/propagation"
14
	semconv "go.opentelemetry.io/otel/semconv/v1.12.0"
15
	"go.opentelemetry.io/otel/trace"
16
)
17

18
const (
19
	instrumentationVersion = "1.0.0"
20
	tracerName             = "go-openapi"
21
)
22

23
type config struct {
24
	Tracer            trace.Tracer
25
	Propagator        propagation.TextMapPropagator
26
	SpanStartOptions  []trace.SpanStartOption
27
	SpanNameFormatter func(*runtime.ClientOperation) string
28
	TracerProvider    trace.TracerProvider
29
}
30

31
type OpenTelemetryOpt interface {
32
	apply(*config)
33
}
34

35
type optionFunc func(*config)
36

37
func (o optionFunc) apply(c *config) {
38
	o(c)
39
}
40

41
// WithTracerProvider specifies a tracer provider to use for creating a tracer.
42
// If none is specified, the global provider is used.
43
func WithTracerProvider(provider trace.TracerProvider) OpenTelemetryOpt {
44
	return optionFunc(func(c *config) {
45
		if provider != nil {
46
			c.TracerProvider = provider
47
		}
48
	})
49
}
50

51
// WithPropagators configures specific propagators. If this
52
// option isn't specified, then the global TextMapPropagator is used.
53
func WithPropagators(ps propagation.TextMapPropagator) OpenTelemetryOpt {
54
	return optionFunc(func(c *config) {
55
		if ps != nil {
56
			c.Propagator = ps
57
		}
58
	})
59
}
60

61
// WithSpanOptions configures an additional set of
62
// trace.SpanOptions, which are applied to each new span.
63
func WithSpanOptions(opts ...trace.SpanStartOption) OpenTelemetryOpt {
64
	return optionFunc(func(c *config) {
65
		c.SpanStartOptions = append(c.SpanStartOptions, opts...)
66
	})
67
}
68

69
// WithSpanNameFormatter takes a function that will be called on every
70
// request and the returned string will become the Span Name.
71
func WithSpanNameFormatter(f func(op *runtime.ClientOperation) string) OpenTelemetryOpt {
72
	return optionFunc(func(c *config) {
73
		c.SpanNameFormatter = f
74
	})
75
}
76

77
func defaultTransportFormatter(op *runtime.ClientOperation) string {
78
	if op.ID != "" {
79
		return op.ID
80
	}
81

82
	return fmt.Sprintf("%s_%s", strings.ToLower(op.Method), op.PathPattern)
83
}
84

85
type openTelemetryTransport struct {
86
	transport runtime.ClientTransport
87
	host      string
88
	tracer    trace.Tracer
89
	config    *config
90
}
91

92
func newOpenTelemetryTransport(transport runtime.ClientTransport, host string, opts []OpenTelemetryOpt) *openTelemetryTransport {
93
	tr := &openTelemetryTransport{
94
		transport: transport,
95
		host:      host,
96
	}
97

98
	defaultOpts := []OpenTelemetryOpt{
99
		WithSpanOptions(trace.WithSpanKind(trace.SpanKindClient)),
100
		WithSpanNameFormatter(defaultTransportFormatter),
101
		WithPropagators(otel.GetTextMapPropagator()),
102
		WithTracerProvider(otel.GetTracerProvider()),
103
	}
104

105
	c := newConfig(append(defaultOpts, opts...)...)
106
	tr.config = c
107

108
	return tr
109
}
110

111
func (t *openTelemetryTransport) Submit(op *runtime.ClientOperation) (interface{}, error) {
112
	if op.Context == nil {
113
		return t.transport.Submit(op)
114
	}
115

116
	params := op.Params
117
	reader := op.Reader
118

119
	var span trace.Span
120
	defer func() {
121
		if span != nil {
122
			span.End()
123
		}
124
	}()
125

126
	op.Params = runtime.ClientRequestWriterFunc(func(req runtime.ClientRequest, reg strfmt.Registry) error {
127
		span = t.newOpenTelemetrySpan(op, req.GetHeaderParams())
128
		return params.WriteToRequest(req, reg)
129
	})
130

131
	op.Reader = runtime.ClientResponseReaderFunc(func(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) {
132
		if span != nil {
133
			statusCode := response.Code()
134
			span.SetAttributes(attribute.Int(string(semconv.HTTPStatusCodeKey), statusCode))
135
			span.SetStatus(semconv.SpanStatusFromHTTPStatusCodeAndSpanKind(statusCode, trace.SpanKindClient))
136
		}
137

138
		return reader.ReadResponse(response, consumer)
139
	})
140

141
	submit, err := t.transport.Submit(op)
142
	if err != nil && span != nil {
143
		span.RecordError(err)
144
		span.SetStatus(codes.Error, err.Error())
145
	}
146

147
	return submit, err
148
}
149

150
func (t *openTelemetryTransport) newOpenTelemetrySpan(op *runtime.ClientOperation, header http.Header) trace.Span {
151
	ctx := op.Context
152

153
	tracer := t.tracer
154
	if tracer == nil {
155
		if span := trace.SpanFromContext(ctx); span.SpanContext().IsValid() {
156
			tracer = newTracer(span.TracerProvider())
157
		} else {
158
			tracer = newTracer(otel.GetTracerProvider())
159
		}
160
	}
161

162
	ctx, span := tracer.Start(ctx, t.config.SpanNameFormatter(op), t.config.SpanStartOptions...)
163

164
	var scheme string
165
	if len(op.Schemes) > 0 {
166
		scheme = op.Schemes[0]
167
	}
168

169
	span.SetAttributes(
170
		attribute.String("net.peer.name", t.host),
171
		attribute.String(string(semconv.HTTPRouteKey), op.PathPattern),
172
		attribute.String(string(semconv.HTTPMethodKey), op.Method),
173
		attribute.String("span.kind", trace.SpanKindClient.String()),
174
		attribute.String("http.scheme", scheme),
175
	)
176

177
	carrier := propagation.HeaderCarrier(header)
178
	t.config.Propagator.Inject(ctx, carrier)
179

180
	return span
181
}
182

183
func newTracer(tp trace.TracerProvider) trace.Tracer {
184
	return tp.Tracer(tracerName, trace.WithInstrumentationVersion(version()))
185
}
186

187
func newConfig(opts ...OpenTelemetryOpt) *config {
188
	c := &config{
189
		Propagator: otel.GetTextMapPropagator(),
190
	}
191

192
	for _, opt := range opts {
193
		opt.apply(c)
194
	}
195

196
	// Tracer is only initialized if manually specified. Otherwise, can be passed with the tracing context.
197
	if c.TracerProvider != nil {
198
		c.Tracer = newTracer(c.TracerProvider)
199
	}
200

201
	return c
202
}
203

204
// Version is the current release version of the go-runtime instrumentation.
205
func version() string {
206
	return instrumentationVersion
207
}
208

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.