diff --git a/remotes/docker/resolver.go b/remotes/docker/resolver.go index ffeb0c74f..b69d46c02 100644 --- a/remotes/docker/resolver.go +++ b/remotes/docker/resolver.go @@ -36,11 +36,9 @@ import ( remoteerrors "github.com/containerd/containerd/remotes/errors" "github.com/containerd/containerd/tracing" "github.com/containerd/containerd/version" - digest "github.com/opencontainers/go-digest" + "github.com/opencontainers/go-digest" ocispec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/sirupsen/logrus" - semconv "go.opentelemetry.io/otel/semconv/v1.12.0" - oteltrace "go.opentelemetry.io/otel/trace" ) var ( @@ -573,17 +571,15 @@ func (r *request) do(ctx context.Context) (*http.Response, error) { _, httpSpan := tracing.StartSpan( ctx, tracing.Name("remotes.docker.resolver", "HTTPRequest"), - oteltrace.WithSpanKind(oteltrace.SpanKindClient), - oteltrace.WithAttributes(semconv.HTTPClientAttributesFromHTTPRequest(req)...), + tracing.WithHTTPRequest(req), ) + defer httpSpan.End() resp, err := client.Do(req) if err != nil { httpSpan.SetStatus(err) - httpSpan.End() return nil, fmt.Errorf("failed to do request: %w", err) } - httpSpan.SetAttributes(semconv.HTTPAttributesFromHTTPStatusCode(resp.StatusCode)...) - httpSpan.End() + httpSpan.SetAttributes(tracing.HTTPStatusCodeAttributes(resp.StatusCode)...) log.G(ctx).WithFields(responseFields(resp)).Debug("fetch response received") return resp, nil } diff --git a/tracing/tracing.go b/tracing/tracing.go index d9cb7cc0c..0b6083d8a 100644 --- a/tracing/tracing.go +++ b/tracing/tracing.go @@ -18,20 +18,44 @@ package tracing import ( "context" + "net/http" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" + semconv "go.opentelemetry.io/otel/semconv/v1.12.0" "go.opentelemetry.io/otel/trace" ) -// StartSan starts child span in a context. -func StartSpan(ctx context.Context, opName string, opts ...trace.SpanStartOption) (context.Context, *Span) { +// StartConfig defines configuration for a new span object. +type StartConfig struct { + spanOpts []trace.SpanStartOption +} + +type SpanOpt func(config *StartConfig) + +// WithHTTPRequest marks span as a HTTP request operation from client to server. +// It'll append attributes from the HTTP request object and mark it with `SpanKindClient` type. +func WithHTTPRequest(request *http.Request) SpanOpt { + return func(config *StartConfig) { + config.spanOpts = append(config.spanOpts, + trace.WithSpanKind(trace.SpanKindClient), // A client making a request to a server + trace.WithAttributes(semconv.HTTPClientAttributesFromHTTPRequest(request)...), // Add HTTP attributes + ) + } +} + +// StartSpan starts child span in a context. +func StartSpan(ctx context.Context, opName string, opts ...SpanOpt) (context.Context, *Span) { + config := StartConfig{} + for _, fn := range opts { + fn(&config) + } tracer := otel.Tracer("") if parent := trace.SpanFromContext(ctx); parent != nil && parent.SpanContext().IsValid() { tracer = parent.TracerProvider().Tracer("") } - ctx, span := tracer.Start(ctx, opName, opts...) + ctx, span := tracer.Start(ctx, opName, config.spanOpts...) return ctx, &Span{otelSpan: span} } @@ -59,7 +83,7 @@ func (s *Span) AddEvent(name string, options ...trace.EventOption) { s.otelSpan.AddEvent(name, options...) } -// SetSpanStatus sets the status of the current span. +// SetStatus sets the status of the current span. // If an error is encountered, it records the error and sets span status to Error. func (s *Span) SetStatus(err error) { if err != nil { @@ -84,3 +108,9 @@ func Name(names ...string) string { func Attribute(k string, v interface{}) attribute.KeyValue { return any(k, v) } + +// HTTPStatusCodeAttributes generates attributes of the HTTP namespace as specified by the OpenTelemetry +// specification for a span. +func HTTPStatusCodeAttributes(code int) []attribute.KeyValue { + return semconv.HTTPAttributesFromHTTPStatusCode(code) +}