From e9f75b2740e0a70081491f2fa14a95424fd6ecaa Mon Sep 17 00:00:00 2001 From: Arne Luenser Date: Thu, 29 Dec 2022 18:32:44 +0100 Subject: [PATCH] feat: add tracing helper --- go.mod | 1 - otelx/otel_test.go | 2 +- otelx/withspan.go | 54 ++++++++++++++++------ otelx/withspan_test.go | 47 ++++++++++++++++++- pagination/keysetpagination/header_test.go | 2 +- requirex/time_test.go | 2 +- 6 files changed, 89 insertions(+), 19 deletions(-) diff --git a/go.mod b/go.mod index ee2b1944..8fe6c488 100644 --- a/go.mod +++ b/go.mod @@ -34,7 +34,6 @@ require ( github.com/hashicorp/go-retryablehttp v0.7.1 github.com/inhies/go-bytesize v0.0.0-20220417184213-4913239db9cf github.com/instana/go-sensor v1.46.0 - github.com/instana/testify v1.6.2-0.20200721153833-94b1851f4d65 github.com/jackc/pgconn v1.13.0 github.com/jackc/pgx/v4 v4.17.2 github.com/jandelgado/gcov2lcov v1.0.5 diff --git a/otelx/otel_test.go b/otelx/otel_test.go index f5745f32..8d64b187 100644 --- a/otelx/otel_test.go +++ b/otelx/otel_test.go @@ -16,7 +16,7 @@ import ( "testing" "time" - "github.com/instana/testify/assert" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/attribute" "golang.org/x/sync/errgroup" diff --git a/otelx/withspan.go b/otelx/withspan.go index 1896db83..b16ac264 100644 --- a/otelx/withspan.go +++ b/otelx/withspan.go @@ -5,6 +5,7 @@ package otelx import ( "context" + "fmt" "go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/trace" @@ -21,21 +22,48 @@ func WithSpan(ctx context.Context, name string, f func(context.Context) error, o ctx, span := trace.SpanFromContext(ctx).TracerProvider().Tracer("").Start(ctx, name, opts...) defer func() { defer span.End() - if err != nil { - span.SetStatus(codes.Error, err.Error()) - } else if r := recover(); r != nil { - switch e := r.(type) { - case error: - span.SetStatus(codes.Error, "panic: "+e.Error()) - case interface{ String() string }: - span.SetStatus(codes.Error, "panic: "+e.String()) - case string: - span.SetStatus(codes.Error, "panic: "+e) - default: - span.SetStatus(codes.Error, "panic") - } + if r := recover(); r != nil { + setErrorStatusPanic(span, r) panic(r) + } else if err != nil { + span.SetStatus(codes.Error, err.Error()) } }() return f(ctx) } + +// End finishes span, and automatically sets the error state if *err is not nil +// or during panicking. +// +// Usage: +// +// func Divide(ctx context.Context, numerator, denominator int) (ratio int, err error) { +// ctx, span := tracer.Start(ctx, "my-operation") +// defer otelx.End(span, &err) +// if denominator == 0 { +// return 0, errors.New("cannot divide by zero") +// } +// return numerator / denominator, nil +// } +func End(span trace.Span, err *error) { + defer span.End() + if r := recover(); r != nil { + setErrorStatusPanic(span, r) + panic(r) + } + if err == nil || *err == nil { + return + } + span.SetStatus(codes.Error, (*err).Error()) +} + +func setErrorStatusPanic(span trace.Span, recovered any) { + switch e := recovered.(type) { + case error, string, fmt.Stringer: + span.SetStatus(codes.Error, fmt.Sprintf("panic: %v", e)) + default: + span.SetStatus(codes.Error, "panic") + case nil: + // nothing + } +} diff --git a/otelx/withspan_test.go b/otelx/withspan_test.go index 4c64eb64..b1309fde 100644 --- a/otelx/withspan_test.go +++ b/otelx/withspan_test.go @@ -8,10 +8,16 @@ import ( "errors" "testing" - "github.com/instana/testify/assert" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.opentelemetry.io/otel/codes" + sdktrace "go.opentelemetry.io/otel/sdk/trace" + "go.opentelemetry.io/otel/sdk/trace/tracetest" "go.opentelemetry.io/otel/trace" ) +var errPanic = errors.New("panic-error") + func TestWithSpan(t *testing.T) { tracer := trace.NewNoopTracerProvider().Tracer("test") ctx, span := tracer.Start(context.Background(), "parent") @@ -36,4 +42,41 @@ func TestWithSpan(t *testing.T) { }) } -var errPanic = errors.New("panic-error") +func returnsError(ctx context.Context) (err error) { + _, span := trace.SpanFromContext(ctx).TracerProvider().Tracer("").Start(ctx, "returnsError") + defer End(span, &err) + return errors.New("error from returnsError()") +} + +func returnsNamedError(ctx context.Context) (err error) { + ctx, span := trace.SpanFromContext(ctx).TracerProvider().Tracer("").Start(ctx, "returnsNamedError") + defer End(span, &err) + err2 := errors.New("err2 message") + return err2 +} + +func panics(ctx context.Context) (err error) { + _, span := trace.SpanFromContext(ctx).TracerProvider().Tracer("").Start(ctx, "panics") + defer End(span, &err) + panic(errors.New("panic from panics()")) +} + +func TestEnd(t *testing.T) { + recorder := tracetest.NewSpanRecorder() + tracer := sdktrace.NewTracerProvider(sdktrace.WithSpanProcessor(recorder)).Tracer("test") + ctx, span := tracer.Start(context.Background(), "parent") + defer span.End() + + assert.Errorf(t, returnsError(ctx), "error from returnsError()") + require.NotEmpty(t, recorder.Ended()) + assert.Equal(t, recorder.Ended()[len(recorder.Ended())-1].Status(), sdktrace.Status{codes.Error, "error from returnsError()"}) + + assert.Errorf(t, returnsNamedError(ctx), "err2 message") + require.NotEmpty(t, recorder.Ended()) + assert.Equal(t, recorder.Ended()[len(recorder.Ended())-1].Status(), sdktrace.Status{codes.Error, "err2 message"}) + + assert.PanicsWithError(t, "panic from panics()", func() { panics(ctx) }) + require.NotEmpty(t, recorder.Ended()) + assert.Equal(t, recorder.Ended()[len(recorder.Ended())-1].Status(), sdktrace.Status{codes.Error, "panic: panic from panics()"}) + +} diff --git a/pagination/keysetpagination/header_test.go b/pagination/keysetpagination/header_test.go index df4503fb..03d9d527 100644 --- a/pagination/keysetpagination/header_test.go +++ b/pagination/keysetpagination/header_test.go @@ -8,7 +8,7 @@ import ( "net/url" "testing" - "github.com/instana/testify/assert" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/requirex/time_test.go b/requirex/time_test.go index 50644ed4..5f8472a8 100644 --- a/requirex/time_test.go +++ b/requirex/time_test.go @@ -7,7 +7,7 @@ import ( "testing" "time" - "github.com/instana/testify/require" + "github.com/stretchr/testify/require" ) type MockT struct {