Skip to content

Commit

Permalink
Chore: Upgrade otel dependencies (#967)
Browse files Browse the repository at this point in the history
  • Loading branch information
marefr committed Apr 30, 2024
1 parent d4d1f4e commit 1bf15db
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 212 deletions.
18 changes: 9 additions & 9 deletions backend/httpclient/tracing_middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/propagation"
semconv "go.opentelemetry.io/otel/semconv/v1.25.0"
"go.opentelemetry.io/otel/trace"

"github.com/grafana/grafana-plugin-sdk-go/backend/tracing"
Expand All @@ -33,11 +34,7 @@ func TracingMiddleware(tracer trace.Tracer) Middleware {
ctx, span := t.Start(req.Context(), "HTTP Outgoing Request", trace.WithSpanKind(trace.SpanKindClient))
defer span.End()

ctx = httptrace.WithClientTrace(
ctx,
otelhttptrace.NewClientTrace(ctx, otelhttptrace.WithoutSubSpans(), otelhttptrace.WithoutHeaders()),
)

ctx = httptrace.WithClientTrace(ctx, otelhttptrace.NewClientTrace(ctx, otelhttptrace.WithoutSubSpans(), otelhttptrace.WithoutHeaders()))
req = req.WithContext(ctx)
for k, v := range opts.Labels {
span.SetAttributes(attribute.Key(k).String(v))
Expand All @@ -46,10 +43,13 @@ func TracingMiddleware(tracer trace.Tracer) Middleware {
otel.GetTextMapPropagator().Inject(ctx, propagation.HeaderCarrier(req.Header))
res, err := next.RoundTrip(req)

span.SetAttributes(attribute.String("http.url", req.URL.String()))
span.SetAttributes(attribute.String("http.method", req.Method))
span.SetAttributes(
semconv.HTTPURL(req.URL.String()),
semconv.HTTPMethod(req.Method),
)

if err != nil {
span.SetStatus(codes.Error, "request failed")
span.RecordError(err)
return res, err
}
Expand All @@ -58,10 +58,10 @@ func TracingMiddleware(tracer trace.Tracer) Middleware {
// we avoid measuring contentlength less than zero because it indicates
// that the content size is unknown. https://godoc.org/github.com/badu/http#Response
if res.ContentLength > 0 {
span.SetAttributes(attribute.Key(httpContentLengthTagKey).Int64(res.ContentLength))
span.SetAttributes(attribute.Int64(httpContentLengthTagKey, res.ContentLength))
}

span.SetAttributes(attribute.Int("http.status_code", res.StatusCode))
span.SetAttributes(semconv.HTTPStatusCode(res.StatusCode))
if res.StatusCode >= 400 {
span.SetStatus(codes.Error, fmt.Sprintf("error with HTTP status code %s", strconv.Itoa(res.StatusCode)))
}
Expand Down
215 changes: 60 additions & 155 deletions backend/httpclient/tracing_middleware_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,118 +9,15 @@ import (
"github.com/stretchr/testify/require"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
"go.opentelemetry.io/otel/sdk/trace/tracetest"
semconv "go.opentelemetry.io/otel/semconv/v1.25.0"
"go.opentelemetry.io/otel/trace"
"go.opentelemetry.io/otel/trace/embedded"

"github.com/grafana/grafana-plugin-sdk-go/backend/httpclient"
"github.com/grafana/grafana-plugin-sdk-go/internal/tracerprovider"
)

type mockTracerProvider struct {
embedded.TracerProvider
}

var _ trace.TracerProvider = mockTracerProvider{}

func (p mockTracerProvider) Tracer(string, ...trace.TracerOption) trace.Tracer {
return &mockTracer{}
}

type mockTracer struct {
embedded.Tracer

spans []*mockSpan
}

var _ trace.Tracer = &mockTracer{}

func (t *mockTracer) Start(ctx context.Context, name string, opts ...trace.SpanStartOption) (context.Context, trace.Span) {
config := trace.NewSpanStartConfig(opts...)
span := &mockSpan{}
span.SetName(name)
span.SetAttributes(config.Attributes()...)
t.spans = append(t.spans, span)
return trace.ContextWithSpan(ctx, span), span
}

// mockSpan is an implementation of Span that preforms no operations.
type mockSpan struct {
embedded.Span

name string
ended bool

errored bool
errs []error

statusCode codes.Code
statusMessage string

attributes []attribute.KeyValue
events []string
}

var _ trace.Span = &mockSpan{}

// checkValid panics if s has already ended, otherwise it does nothing.
// This ensures that ended spans are never edited afterwards.
func (s *mockSpan) checkValid() {
if s.ended {
panic("span already ended")
}
}

func (s *mockSpan) attributesMap() map[attribute.Key]attribute.Value {
m := make(map[attribute.Key]attribute.Value, len(s.attributes))
for _, attr := range s.attributes {
m[attr.Key] = attr.Value
}
return m
}

func (*mockSpan) SpanContext() trace.SpanContext { return trace.SpanContext{} }

func (*mockSpan) IsRecording() bool { return false }

func (s *mockSpan) SetStatus(code codes.Code, message string) {
s.checkValid()
s.statusCode = code
s.statusMessage = message
}

func (s *mockSpan) SetError(errored bool) {
s.checkValid()
s.errored = errored
}

func (s *mockSpan) SetAttributes(kv ...attribute.KeyValue) {
s.checkValid()
s.attributes = append(s.attributes, kv...)
}

func (s *mockSpan) End(...trace.SpanEndOption) {
s.checkValid()
s.ended = true
}

func (s *mockSpan) RecordError(err error, _ ...trace.EventOption) {
s.checkValid()
s.errs = append(s.errs, err)
}

func (s *mockSpan) AddEvent(event string, _ ...trace.EventOption) {
s.checkValid()
s.events = append(s.events, event)
}

func (s *mockSpan) SetName(name string) {
s.checkValid()
s.name = name
}

func (*mockSpan) TracerProvider() trace.TracerProvider { return mockTracerProvider{} }

func TestTracingMiddlewareWithDefaultTracerDataRace(t *testing.T) {
var tracer trace.Tracer

Expand All @@ -141,7 +38,9 @@ func TestTracingMiddlewareWithDefaultTracerDataRace(t *testing.T) {

func TestTracingMiddleware(t *testing.T) {
t.Run("GET request that returns 200 OK should start and capture span", func(t *testing.T) {
tracer := &mockTracer{}
spanRecorder := tracetest.NewSpanRecorder()
provider := sdktrace.NewTracerProvider(sdktrace.WithSpanProcessor(spanRecorder))
tracer := provider.Tracer("test")

finalRoundTripper := httpclient.RoundTripperFunc(func(req *http.Request) (*http.Response, error) {
return &http.Response{StatusCode: http.StatusOK, Request: req}, nil
Expand Down Expand Up @@ -169,24 +68,28 @@ func TestTracingMiddleware(t *testing.T) {
require.NoError(t, res.Body.Close())
}

require.Len(t, tracer.spans, 1)
span := tracer.spans[0]
require.Equal(t, "HTTP Outgoing Request", span.name)
require.True(t, span.ended)
require.False(t, span.errored)
require.Equal(t, codes.Unset, span.statusCode)
require.Empty(t, span.statusMessage)
require.Equal(t, map[attribute.Key]attribute.Value{
"l1": attribute.StringValue("v1"),
"l2": attribute.StringValue("v2"),
"http.url": attribute.StringValue("http://test.com/query"),
"http.method": attribute.StringValue("GET"),
"http.status_code": attribute.Int64Value(200),
}, span.attributesMap())
spans := spanRecorder.Ended()

require.Len(t, spans, 1)
span := spans[0]
require.Equal(t, "HTTP Outgoing Request", span.Name())
require.False(t, span.EndTime().IsZero())
require.False(t, span.Status().Code == codes.Error)
require.Equal(t, codes.Unset, span.Status().Code)
require.Empty(t, span.Status().Description)
require.ElementsMatch(t, []attribute.KeyValue{
attribute.String("l1", "v1"),
attribute.String("l2", "v2"),
semconv.HTTPURL("http://test.com/query"),
semconv.HTTPMethod(http.MethodGet),
semconv.HTTPStatusCode(http.StatusOK),
}, span.Attributes())
})

t.Run("GET request that returns 400 Bad Request should start and capture span", func(t *testing.T) {
tracer := &mockTracer{}
spanRecorder := tracetest.NewSpanRecorder()
provider := sdktrace.NewTracerProvider(sdktrace.WithSpanProcessor(spanRecorder))
tracer := provider.Tracer("test")

finalRoundTripper := httpclient.RoundTripperFunc(func(req *http.Request) (*http.Response, error) {
return &http.Response{StatusCode: http.StatusBadRequest, Request: req}, nil
Expand Down Expand Up @@ -214,27 +117,31 @@ func TestTracingMiddleware(t *testing.T) {
require.NoError(t, res.Body.Close())
}

require.Len(t, tracer.spans, 1)
span := tracer.spans[0]
require.Equal(t, "HTTP Outgoing Request", span.name)
require.True(t, span.ended)
require.False(t, span.errored)
require.Equal(t, codes.Error, span.statusCode)
require.Equal(t, "error with HTTP status code 400", span.statusMessage)
require.Equal(t, map[attribute.Key]attribute.Value{
"l1": attribute.StringValue("v1"),
"l2": attribute.StringValue("v2"),
"http.url": attribute.StringValue("http://test.com/query"),
"http.method": attribute.StringValue("GET"),
"http.status_code": attribute.Int64Value(400),
}, span.attributesMap())
spans := spanRecorder.Ended()

require.Len(t, spans, 1)
span := spans[0]
require.Equal(t, "HTTP Outgoing Request", span.Name())
require.False(t, span.EndTime().IsZero())
require.Equal(t, codes.Error, span.Status().Code)
require.Equal(t, "error with HTTP status code 400", span.Status().Description)
require.Equal(t, []attribute.KeyValue{
attribute.String("l1", "v1"),
attribute.String("l2", "v2"),
semconv.HTTPURL("http://test.com/query"),
semconv.HTTPMethod(http.MethodGet),
semconv.HTTPStatusCode(http.StatusBadRequest),
}, span.Attributes())
})

t.Run("POST request that returns 200 OK should start and capture span", func(t *testing.T) {
tracer := &mockTracer{}
spanRecorder := tracetest.NewSpanRecorder()
provider := sdktrace.NewTracerProvider(sdktrace.WithSpanProcessor(spanRecorder))
tracer := provider.Tracer("test")

resContentLength := int64(10)
finalRoundTripper := httpclient.RoundTripperFunc(func(req *http.Request) (*http.Response, error) {
return &http.Response{StatusCode: http.StatusOK, Request: req, ContentLength: 10}, nil
return &http.Response{StatusCode: http.StatusOK, Request: req, ContentLength: resContentLength}, nil
})

mw := httpclient.TracingMiddleware(tracer)
Expand All @@ -259,24 +166,22 @@ func TestTracingMiddleware(t *testing.T) {
require.NoError(t, res.Body.Close())
}

require.Len(t, tracer.spans, 1)
span := tracer.spans[0]
require.Equal(t, "HTTP Outgoing Request", span.name)
require.True(t, span.ended)
require.False(t, span.errored)
require.Equal(t, codes.Unset, span.statusCode)
require.Empty(t, span.statusMessage)
attrMap := span.attributesMap()
_, ok = attrMap["http.content_length"]
require.True(t, ok, "http.content_length does not exist")
delete(attrMap, "http.content_length")
require.Equal(t, map[attribute.Key]attribute.Value{
"l1": attribute.StringValue("v1"),
"l2": attribute.StringValue("v2"),
"http.url": attribute.StringValue("http://test.com/query"),
"http.method": attribute.StringValue("POST"),
"http.status_code": attribute.Int64Value(200),
}, attrMap)
spans := spanRecorder.Ended()

require.Len(t, spans, 1)
span := spans[0]
require.Equal(t, "HTTP Outgoing Request", span.Name())
require.False(t, span.EndTime().IsZero())
require.Equal(t, codes.Unset, span.Status().Code)
require.Empty(t, span.Status().Description)
require.ElementsMatch(t, []attribute.KeyValue{
attribute.String("l1", "v1"),
attribute.String("l2", "v2"),
semconv.HTTPURL("http://test.com/query"),
semconv.HTTPMethod(http.MethodPost),
semconv.HTTPStatusCode(http.StatusOK),
attribute.Int64("http.content_length", resContentLength),
}, span.Attributes())
})

t.Run("propagation", func(t *testing.T) {
Expand Down
2 changes: 1 addition & 1 deletion backend/setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (

"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
semconv "go.opentelemetry.io/otel/semconv/v1.4.0"
semconv "go.opentelemetry.io/otel/semconv/v1.25.0"

"github.com/grafana/grafana-plugin-sdk-go/backend/log"
"github.com/grafana/grafana-plugin-sdk-go/backend/tracing"
Expand Down
29 changes: 14 additions & 15 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,15 @@ require (
github.com/stretchr/testify v1.9.0
github.com/unknwon/bra v0.0.0-20200517080246-1e3013ecaff8
github.com/urfave/cli v1.22.15
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.49.0
go.opentelemetry.io/contrib/propagators/jaeger v1.22.0
go.opentelemetry.io/contrib/samplers/jaegerremote v0.18.0
go.opentelemetry.io/otel v1.24.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0
go.opentelemetry.io/otel/sdk v1.24.0
go.opentelemetry.io/otel/trace v1.24.0
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.51.0
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.51.0
go.opentelemetry.io/contrib/propagators/jaeger v1.26.0
go.opentelemetry.io/contrib/samplers/jaegerremote v0.20.0
go.opentelemetry.io/otel v1.26.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.26.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.26.0
go.opentelemetry.io/otel/sdk v1.26.0
go.opentelemetry.io/otel/trace v1.26.0
golang.org/x/exp v0.0.0-20231006140011-7918f672742d
golang.org/x/net v0.24.0
golang.org/x/oauth2 v0.18.0
Expand All @@ -49,7 +49,7 @@ require (
github.com/bahlo/generic-list-go v0.2.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/buger/jsonparser v1.1.1 // indirect
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
Expand All @@ -69,7 +69,7 @@ require (
github.com/google/gofuzz v1.1.0 // indirect
github.com/gorilla/mux v1.8.1 // indirect
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1 // indirect
github.com/hashicorp/yamux v0.1.1 // indirect
github.com/invopop/yaml v0.2.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
Expand All @@ -94,14 +94,13 @@ require (
github.com/unknwon/log v0.0.0-20150304194804-e617c87089d3 // indirect
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
github.com/zeebo/xxh3 v1.0.2 // indirect
go.opentelemetry.io/otel/metric v1.24.0 // indirect
go.opentelemetry.io/proto/otlp v1.1.0 // indirect
go.opentelemetry.io/otel/metric v1.26.0 // indirect
go.opentelemetry.io/proto/otlp v1.2.0 // indirect
golang.org/x/mod v0.13.0 // indirect
golang.org/x/tools v0.14.0 // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
google.golang.org/appengine v1.6.8 // indirect
google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240415180920-8c6c420018be // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be // indirect
gopkg.in/fsnotify/fsnotify.v1 v1.4.7 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
Expand Down

0 comments on commit 1bf15db

Please sign in to comment.