mirror of
https://github.com/open-telemetry/opentelemetry-go.git
synced 2025-04-09 07:03:54 +02:00
Remove WithSpan method from Tracer interface (#1043)
* Remove WithSpan method from Tracer interface Also remove implementation in SDK. * Add panic event reporting to span End * Update Changelog with changes * Update CHANGELOG.md * Update README.md Fix code tabs * Refactor span End * Fix un-ended span from feedback.
This commit is contained in:
parent
f9ba15f2d1
commit
2dfa5e4fe1
@ -20,11 +20,15 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
|
||||
|
||||
- The `grpctrace` instrumentation was moved to the `go.opentelemetry.io/contrib` repository and out of this repository.
|
||||
This move includes moving the `grpc` example to the `go.opentelemetry.io/contrib` as well. (#1027)
|
||||
- The `WithSpan` method of the `Tracer` interface.
|
||||
The functionality this method provided was limited compared to what a user can provide themselves.
|
||||
It was removed with the understanding that if there is sufficient user need it can be added back based on actual user usage. (#1043)
|
||||
|
||||
### Fixed
|
||||
|
||||
- The `semconv.HTTPServerMetricAttributesFromHTTPRequest()` function no longer generates the high-cardinality `http.request.content.length` label. (#1031)
|
||||
- Correct instrumentation version tag in Jaeger exporter. (#1037)
|
||||
- The SDK span will now set an error event if the `End` method is called during a panic (i.e. it was deferred). (#1043)
|
||||
|
||||
## [0.10.0] - 2020-07-29
|
||||
|
||||
|
19
README.md
19
README.md
@ -50,22 +50,9 @@ func main() {
|
||||
defer pusher.Stop()
|
||||
|
||||
tracer := global.Tracer("ex.com/basic")
|
||||
|
||||
tracer.WithSpan(context.Background(), "foo",
|
||||
func(ctx context.Context) error {
|
||||
tracer.WithSpan(ctx, "bar",
|
||||
func(ctx context.Context) error {
|
||||
tracer.WithSpan(ctx, "baz",
|
||||
func(ctx context.Context) error {
|
||||
return nil
|
||||
},
|
||||
)
|
||||
return nil
|
||||
},
|
||||
)
|
||||
return nil
|
||||
},
|
||||
)
|
||||
ctx, span := tracer.Start(context.Background(), "main")
|
||||
defer span.End()
|
||||
/* … */
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -114,15 +114,6 @@ func (t *tracer) setDelegate(provider trace.Provider) {
|
||||
t.once.Do(func() { t.delegate = provider.Tracer(t.name, t.opts...) })
|
||||
}
|
||||
|
||||
// WithSpan implements trace.Tracer by forwarding the call to t.delegate if
|
||||
// set, otherwise it forwards the call to a NoopTracer.
|
||||
func (t *tracer) WithSpan(ctx context.Context, name string, body func(context.Context) error, opts ...trace.StartOption) error {
|
||||
if t.delegate != nil {
|
||||
return t.delegate.WithSpan(ctx, name, body, opts...)
|
||||
}
|
||||
return trace.NoopTracer{}.WithSpan(ctx, name, body, opts...)
|
||||
}
|
||||
|
||||
// Start implements trace.Tracer by forwarding the call to t.delegate if
|
||||
// set, otherwise it forwards the call to a NoopTracer.
|
||||
func (t *tracer) Start(ctx context.Context, name string, opts ...trace.StartOption) (context.Context, trace.Span) {
|
||||
|
@ -31,13 +31,9 @@ func TestTraceWithSDK(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
gtp := global.TraceProvider()
|
||||
tracer1 := gtp.Tracer("pre")
|
||||
// This is started before an SDK was registered and should be dropped.
|
||||
_, span1 := tracer1.Start(ctx, "span1")
|
||||
|
||||
// This should be dropped.
|
||||
if err := tracer1.WithSpan(ctx, "withSpan1", func(context.Context) error { return nil }); err != nil {
|
||||
t.Errorf("failed to wrap function with span prior to initialization: %v", err)
|
||||
}
|
||||
|
||||
sr := new(testtrace.StandardSpanRecorder)
|
||||
tp := testtrace.NewProvider(testtrace.WithSpanRecorder(sr))
|
||||
global.SetTraceProvider(tp)
|
||||
@ -48,17 +44,11 @@ func TestTraceWithSDK(t *testing.T) {
|
||||
// The existing Tracer should have been configured to now use the configured SDK.
|
||||
_, span2 := tracer1.Start(ctx, "span2")
|
||||
span2.End()
|
||||
if err := tracer1.WithSpan(ctx, "withSpan2", func(context.Context) error { return nil }); err != nil {
|
||||
t.Errorf("failed to wrap function with span post initialization: %v", err)
|
||||
}
|
||||
|
||||
// The global trace Provider should now create Tracers that also use the newly configured SDK.
|
||||
tracer2 := gtp.Tracer("post")
|
||||
_, span3 := tracer2.Start(ctx, "span3")
|
||||
span3.End()
|
||||
if err := tracer2.WithSpan(ctx, "withSpan3", func(context.Context) error { return nil }); err != nil {
|
||||
t.Errorf("failed to wrap function with span post initialization with new tracer: %v", err)
|
||||
}
|
||||
|
||||
filterNames := func(spans []*testtrace.Span) []string {
|
||||
names := make([]string, len(spans))
|
||||
@ -67,7 +57,7 @@ func TestTraceWithSDK(t *testing.T) {
|
||||
}
|
||||
return names
|
||||
}
|
||||
expected := []string{"span2", "withSpan2", "span3", "withSpan3"}
|
||||
expected := []string{"span2", "span3"}
|
||||
assert.ElementsMatch(t, expected, filterNames(sr.Started()))
|
||||
assert.ElementsMatch(t, expected, filterNames(sr.Completed()))
|
||||
}
|
||||
|
@ -16,7 +16,6 @@ package testharness
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
@ -177,138 +176,6 @@ func (h *Harness) TestTracer(subjectFactory func() trace.Tracer) {
|
||||
})
|
||||
})
|
||||
|
||||
h.t.Run("#WithSpan", func(t *testing.T) {
|
||||
t.Run("returns nil if the body does not return an error", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
e := matchers.NewExpecter(t)
|
||||
subject := subjectFactory()
|
||||
|
||||
err := subject.WithSpan(context.Background(), "test", func(ctx context.Context) error {
|
||||
return nil
|
||||
})
|
||||
|
||||
e.Expect(err).ToBeNil()
|
||||
})
|
||||
|
||||
t.Run("propagates the error from the body if the body errors", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
e := matchers.NewExpecter(t)
|
||||
subject := subjectFactory()
|
||||
|
||||
expectedErr := errors.New("test error")
|
||||
|
||||
err := subject.WithSpan(context.Background(), "test", func(ctx context.Context) error {
|
||||
return expectedErr
|
||||
})
|
||||
|
||||
e.Expect(err).ToMatchError(expectedErr)
|
||||
})
|
||||
|
||||
t.Run("propagates the original context to the body", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
e := matchers.NewExpecter(t)
|
||||
subject := subjectFactory()
|
||||
|
||||
ctxKey := testCtxKey{}
|
||||
ctxValue := "ctx value"
|
||||
ctx := context.WithValue(context.Background(), ctxKey, ctxValue)
|
||||
|
||||
var actualCtx context.Context
|
||||
|
||||
err := subject.WithSpan(ctx, "test", func(ctx context.Context) error {
|
||||
actualCtx = ctx
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
e.Expect(err).ToBeNil()
|
||||
|
||||
e.Expect(actualCtx.Value(ctxKey)).ToEqual(ctxValue)
|
||||
})
|
||||
|
||||
t.Run("stores a span containing the expected properties on the context provided to the body function", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
e := matchers.NewExpecter(t)
|
||||
subject := subjectFactory()
|
||||
|
||||
var span trace.Span
|
||||
|
||||
err := subject.WithSpan(context.Background(), "test", func(ctx context.Context) error {
|
||||
span = trace.SpanFromContext(ctx)
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
e.Expect(err).ToBeNil()
|
||||
|
||||
e.Expect(span).NotToBeNil()
|
||||
|
||||
e.Expect(span.Tracer()).ToEqual(subject)
|
||||
e.Expect(span.SpanContext().IsValid()).ToBeTrue()
|
||||
})
|
||||
|
||||
t.Run("starts spans with unique trace and span IDs", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
e := matchers.NewExpecter(t)
|
||||
subject := subjectFactory()
|
||||
|
||||
var span1 trace.Span
|
||||
var span2 trace.Span
|
||||
|
||||
err := subject.WithSpan(context.Background(), "span1", func(ctx context.Context) error {
|
||||
span1 = trace.SpanFromContext(ctx)
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
e.Expect(err).ToBeNil()
|
||||
|
||||
err = subject.WithSpan(context.Background(), "span2", func(ctx context.Context) error {
|
||||
span2 = trace.SpanFromContext(ctx)
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
e.Expect(err).ToBeNil()
|
||||
|
||||
sc1 := span1.SpanContext()
|
||||
sc2 := span2.SpanContext()
|
||||
|
||||
e.Expect(sc1.TraceID).NotToEqual(sc2.TraceID)
|
||||
e.Expect(sc1.SpanID).NotToEqual(sc2.SpanID)
|
||||
})
|
||||
|
||||
t.Run("propagates a parent's trace ID through the context", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
e := matchers.NewExpecter(t)
|
||||
subject := subjectFactory()
|
||||
|
||||
ctx, parent := subject.Start(context.Background(), "parent")
|
||||
|
||||
var child trace.Span
|
||||
|
||||
err := subject.WithSpan(ctx, "child", func(ctx context.Context) error {
|
||||
child = trace.SpanFromContext(ctx)
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
e.Expect(err).ToBeNil()
|
||||
|
||||
psc := parent.SpanContext()
|
||||
csc := child.SpanContext()
|
||||
|
||||
e.Expect(csc.TraceID).ToEqual(psc.TraceID)
|
||||
e.Expect(csc.SpanID).NotToEqual(psc.SpanID)
|
||||
})
|
||||
})
|
||||
|
||||
h.testSpan(subjectFactory)
|
||||
}
|
||||
|
||||
@ -340,19 +207,6 @@ func (h *Harness) testSpan(tracerFactory func() trace.Tracer) {
|
||||
|
||||
return subject
|
||||
},
|
||||
"Span created via Tracer#WithSpan": func() trace.Span {
|
||||
tracer := tracerFactory()
|
||||
|
||||
var actualCtx context.Context
|
||||
|
||||
_ = tracer.WithSpan(context.Background(), "test", func(ctx context.Context) error {
|
||||
actualCtx = ctx
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return trace.SpanFromContext(actualCtx)
|
||||
},
|
||||
}
|
||||
|
||||
for mechanismName, mechanism := range mechanisms {
|
||||
|
@ -54,16 +54,6 @@ func WithInstrumentationVersion(version string) TracerOption {
|
||||
type Tracer interface {
|
||||
// Start a span.
|
||||
Start(ctx context.Context, spanName string, opts ...StartOption) (context.Context, Span)
|
||||
|
||||
// WithSpan wraps the execution of the fn function with a span.
|
||||
// It starts a new span, sets it as an active span in the context,
|
||||
// executes the fn function and closes the span before returning the result of fn.
|
||||
WithSpan(
|
||||
ctx context.Context,
|
||||
spanName string,
|
||||
fn func(ctx context.Context) error,
|
||||
opts ...StartOption,
|
||||
) error
|
||||
}
|
||||
|
||||
// EndConfig provides options to set properties of span at the time of ending
|
||||
|
@ -22,11 +22,6 @@ type NoopTracer struct{}
|
||||
|
||||
var _ Tracer = NoopTracer{}
|
||||
|
||||
// WithSpan wraps around execution of func with noop span.
|
||||
func (t NoopTracer) WithSpan(ctx context.Context, name string, body func(context.Context) error, opts ...StartOption) error {
|
||||
return body(ctx)
|
||||
}
|
||||
|
||||
// Start starts a noop span.
|
||||
func (NoopTracer) Start(ctx context.Context, name string, opts ...StartOption) (context.Context, Span) {
|
||||
span := NoopSpan{}
|
||||
|
@ -86,10 +86,3 @@ func (t *Tracer) Start(ctx context.Context, name string, opts ...trace.StartOpti
|
||||
}
|
||||
return trace.ContextWithSpan(ctx, span), span
|
||||
}
|
||||
|
||||
func (t *Tracer) WithSpan(ctx context.Context, name string, body func(ctx context.Context) error, opts ...trace.StartOption) error {
|
||||
ctx, span := t.Start(ctx, name, opts...)
|
||||
defer span.End()
|
||||
|
||||
return body(ctx)
|
||||
}
|
||||
|
@ -256,46 +256,6 @@ func TestTracer(t *testing.T) {
|
||||
e.Expect(links[link2.SpanContext]).ToEqual(link2.Attributes)
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("#WithSpan", func(t *testing.T) {
|
||||
testTracedSpan(t, func(tracer trace.Tracer, name string) (trace.Span, error) {
|
||||
var span trace.Span
|
||||
|
||||
err := tracer.WithSpan(context.Background(), name, func(ctx context.Context) error {
|
||||
span = trace.SpanFromContext(ctx)
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return span, err
|
||||
})
|
||||
|
||||
t.Run("honors StartOptions", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
e := matchers.NewExpecter(t)
|
||||
|
||||
attr1 := kv.String("a", "1")
|
||||
attr2 := kv.String("b", "2")
|
||||
|
||||
subject := tp.Tracer(t.Name())
|
||||
var span trace.Span
|
||||
err := subject.WithSpan(context.Background(), "test", func(ctx context.Context) error {
|
||||
span = trace.SpanFromContext(ctx)
|
||||
|
||||
return nil
|
||||
}, trace.WithAttributes(attr1, attr2))
|
||||
e.Expect(err).ToBeNil()
|
||||
|
||||
testSpan, ok := span.(*testtrace.Span)
|
||||
e.Expect(ok).ToBeTrue()
|
||||
|
||||
attributes := testSpan.Attributes()
|
||||
e.Expect(attributes[attr1.Key]).ToEqual(attr1.Value)
|
||||
e.Expect(attributes[attr2.Key]).ToEqual(attr2.Value)
|
||||
})
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func testTracedSpan(t *testing.T, fn func(tracer trace.Tracer, name string) (trace.Span, error)) {
|
||||
|
@ -71,12 +71,6 @@ func NewMockTracer() *MockTracer {
|
||||
}
|
||||
}
|
||||
|
||||
func (t *MockTracer) WithSpan(ctx context.Context, name string, body func(context.Context) error, opts ...oteltrace.StartOption) error {
|
||||
ctx, span := t.Start(ctx, name, opts...)
|
||||
defer span.End()
|
||||
return body(ctx)
|
||||
}
|
||||
|
||||
func (t *MockTracer) Start(ctx context.Context, name string, opts ...oteltrace.StartOption) (context.Context, oteltrace.Span) {
|
||||
spanOpts := oteltrace.StartConfig{}
|
||||
for _, opt := range opts {
|
||||
|
@ -71,20 +71,6 @@ func (t *WrapperTracer) otelTracer() oteltrace.Tracer {
|
||||
return t.tracer
|
||||
}
|
||||
|
||||
// WithSpan forwards the call to the wrapped tracer with a modified
|
||||
// body callback, which sets the active OpenTracing span before
|
||||
// calling the original callback.
|
||||
func (t *WrapperTracer) WithSpan(ctx context.Context, name string, body func(context.Context) error, opts ...oteltrace.StartOption) error {
|
||||
return t.otelTracer().WithSpan(ctx, name, func(ctx context.Context) error {
|
||||
span := oteltrace.SpanFromContext(ctx)
|
||||
if spanWithExtension, ok := span.(migration.OverrideTracerSpanExtension); ok {
|
||||
spanWithExtension.OverrideTracer(t)
|
||||
}
|
||||
ctx = t.bridge.ContextWithBridgeSpan(ctx, span)
|
||||
return body(ctx)
|
||||
}, opts...)
|
||||
}
|
||||
|
||||
// Start forwards the call to the wrapped tracer. It also tries to
|
||||
// override the tracer of the returned span if the span implements the
|
||||
// OverrideTracerSpanExtension interface.
|
||||
|
@ -67,11 +67,13 @@ func main() {
|
||||
valuerecorder := valuerecorderTwo.Bind(commonLabels...)
|
||||
defer valuerecorder.Unbind()
|
||||
|
||||
err = tracer.WithSpan(ctx, "operation", func(ctx context.Context) error {
|
||||
err = func(ctx context.Context) error {
|
||||
var span trace.Span
|
||||
ctx, span = tracer.Start(ctx, "operation")
|
||||
defer span.End()
|
||||
|
||||
trace.SpanFromContext(ctx).AddEvent(ctx, "Nice operation!", kv.Key("bogons").Int(100))
|
||||
|
||||
trace.SpanFromContext(ctx).SetAttributes(anotherKey.String("yes"))
|
||||
span.AddEvent(ctx, "Nice operation!", kv.Key("bogons").Int(100))
|
||||
span.SetAttributes(anotherKey.String("yes"))
|
||||
|
||||
meter.RecordBatch(
|
||||
// Note: call-site variables added as context Entries:
|
||||
@ -81,20 +83,18 @@ func main() {
|
||||
valuerecorderTwo.Measurement(2.0),
|
||||
)
|
||||
|
||||
return tracer.WithSpan(
|
||||
ctx,
|
||||
"Sub operation...",
|
||||
func(ctx context.Context) error {
|
||||
trace.SpanFromContext(ctx).SetAttributes(lemonsKey.String("five"))
|
||||
return func(ctx context.Context) error {
|
||||
var span trace.Span
|
||||
ctx, span = tracer.Start(ctx, "Sub operation...")
|
||||
defer span.End()
|
||||
|
||||
trace.SpanFromContext(ctx).AddEvent(ctx, "Sub span event")
|
||||
span.SetAttributes(lemonsKey.String("five"))
|
||||
span.AddEvent(ctx, "Sub span event")
|
||||
valuerecorder.Record(ctx, 1.3)
|
||||
|
||||
valuerecorder.Record(ctx, 1.3)
|
||||
|
||||
return nil
|
||||
},
|
||||
)
|
||||
})
|
||||
return nil
|
||||
}(ctx)
|
||||
}(ctx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
@ -29,19 +29,15 @@ var (
|
||||
// SubOperation is an example to demonstrate the use of named tracer.
|
||||
// It creates a named tracer with its package path.
|
||||
func SubOperation(ctx context.Context) error {
|
||||
|
||||
// Using global provider. Alternative is to have application provide a getter
|
||||
// for its component to get the instance of the provider.
|
||||
tr := global.Tracer("example/namedtracer/foo")
|
||||
return tr.WithSpan(
|
||||
ctx,
|
||||
"Sub operation...",
|
||||
func(ctx context.Context) error {
|
||||
trace.SpanFromContext(ctx).SetAttributes(lemonsKey.String("five"))
|
||||
|
||||
trace.SpanFromContext(ctx).AddEvent(ctx, "Sub span event")
|
||||
var span trace.Span
|
||||
ctx, span = tr.Start(ctx, "Sub operation...")
|
||||
defer span.End()
|
||||
span.SetAttributes(lemonsKey.String("five"))
|
||||
span.AddEvent(ctx, "Sub span event")
|
||||
|
||||
return nil
|
||||
},
|
||||
)
|
||||
return nil
|
||||
}
|
||||
|
@ -65,15 +65,12 @@ func main() {
|
||||
barKey.String("bar1"),
|
||||
)
|
||||
|
||||
err := tracer.WithSpan(ctx, "operation", func(ctx context.Context) error {
|
||||
|
||||
trace.SpanFromContext(ctx).AddEvent(ctx, "Nice operation!", kv.Key("bogons").Int(100))
|
||||
|
||||
trace.SpanFromContext(ctx).SetAttributes(anotherKey.String("yes"))
|
||||
|
||||
return foo.SubOperation(ctx)
|
||||
})
|
||||
if err != nil {
|
||||
var span trace.Span
|
||||
ctx, span = tracer.Start(ctx, "operation")
|
||||
defer span.End()
|
||||
span.AddEvent(ctx, "Nice operation!", kv.Key("bogons").Int(100))
|
||||
span.SetAttributes(anotherKey.String("yes"))
|
||||
if err := foo.SubOperation(ctx); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
@ -42,20 +42,37 @@ var (
|
||||
)
|
||||
|
||||
loopCounter = metric.Must(meter).NewInt64Counter("function.loops")
|
||||
paramValue = metric.Must(meter).NewFloat64ValueRecorder("function.param")
|
||||
paramValue = metric.Must(meter).NewInt64ValueRecorder("function.param")
|
||||
|
||||
nameKey = kv.Key("function.name")
|
||||
)
|
||||
|
||||
func myFunction(ctx context.Context, values ...float64) error {
|
||||
nameKV := nameKey.String("myFunction")
|
||||
boundCount := loopCounter.Bind(nameKV)
|
||||
boundValue := paramValue.Bind(nameKV)
|
||||
for _, value := range values {
|
||||
boundCount.Add(ctx, 1)
|
||||
boundValue.Record(ctx, value)
|
||||
}
|
||||
return nil
|
||||
func add(ctx context.Context, x, y int64) int64 {
|
||||
nameKV := nameKey.String("add")
|
||||
|
||||
var span trace.Span
|
||||
ctx, span = tracer.Start(ctx, "Addition")
|
||||
defer span.End()
|
||||
|
||||
loopCounter.Add(ctx, 1, nameKV)
|
||||
paramValue.Record(ctx, x, nameKV)
|
||||
paramValue.Record(ctx, y, nameKV)
|
||||
|
||||
return x + y
|
||||
}
|
||||
|
||||
func multiply(ctx context.Context, x, y int64) int64 {
|
||||
nameKV := nameKey.String("multiply")
|
||||
|
||||
var span trace.Span
|
||||
ctx, span = tracer.Start(ctx, "Multiplication")
|
||||
defer span.End()
|
||||
|
||||
loopCounter.Add(ctx, 1, nameKV)
|
||||
paramValue.Record(ctx, x, nameKV)
|
||||
paramValue.Record(ctx, y, nameKV)
|
||||
|
||||
return x * y
|
||||
}
|
||||
|
||||
func Example() {
|
||||
@ -70,22 +87,6 @@ func Example() {
|
||||
}
|
||||
defer pusher.Stop()
|
||||
|
||||
err = tracer.WithSpan(
|
||||
context.Background(),
|
||||
"myFunction/call",
|
||||
func(ctx context.Context) error {
|
||||
err := tracer.WithSpan(
|
||||
ctx,
|
||||
"internal/call",
|
||||
func(ctx context.Context) error { return myFunction(ctx, 200, 100, 5000, 600) },
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return myFunction(ctx, 100, 200, 500, 800)
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatal("Failed to call myFunction", err)
|
||||
}
|
||||
ctx := context.Background()
|
||||
log.Println("the answer is", add(ctx, multiply(ctx, multiply(ctx, 2, 2), 10), 2))
|
||||
}
|
||||
|
@ -43,14 +43,6 @@ type MockTracer struct {
|
||||
|
||||
var _ apitrace.Tracer = (*MockTracer)(nil)
|
||||
|
||||
// WithSpan does nothing except executing the body.
|
||||
func (mt *MockTracer) WithSpan(ctx context.Context, name string, body func(context.Context) error, opts ...apitrace.StartOption) error {
|
||||
ctx, span := mt.Start(ctx, name, opts...)
|
||||
defer span.End()
|
||||
|
||||
return body(ctx)
|
||||
}
|
||||
|
||||
// Start starts a MockSpan. It creates a new Span based on Parent SpanContext option.
|
||||
// TracdID is used from Parent Span Context and SpanID is assigned.
|
||||
// If Parent SpanContext option is not specified then random TraceID is used.
|
||||
|
@ -109,11 +109,23 @@ func (s *span) SetAttribute(k string, v interface{}) {
|
||||
}
|
||||
}
|
||||
|
||||
// End ends the span adding an error event if it was called while panicking.
|
||||
func (s *span) End(options ...apitrace.EndOption) {
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if recovered := recover(); recovered != nil {
|
||||
// Record but don't stop the panic.
|
||||
defer panic(recovered)
|
||||
s.addEventWithTimestamp(
|
||||
time.Now(),
|
||||
errorEventName,
|
||||
errorTypeKey.String(typeStr(recovered)),
|
||||
errorMessageKey.String(fmt.Sprint(recovered)),
|
||||
)
|
||||
}
|
||||
|
||||
if s.executionTracerTaskEnd != nil {
|
||||
s.executionTracerTaskEnd()
|
||||
}
|
||||
@ -164,19 +176,21 @@ func (s *span) RecordError(ctx context.Context, err error, opts ...apitrace.Erro
|
||||
s.SetStatus(cfg.StatusCode, "")
|
||||
}
|
||||
|
||||
errType := reflect.TypeOf(err)
|
||||
errTypeString := fmt.Sprintf("%s.%s", errType.PkgPath(), errType.Name())
|
||||
if errTypeString == "." {
|
||||
// PkgPath() and Name() may be empty for builtin Types
|
||||
errTypeString = errType.String()
|
||||
}
|
||||
|
||||
s.AddEventWithTimestamp(ctx, cfg.Timestamp, errorEventName,
|
||||
errorTypeKey.String(errTypeString),
|
||||
errorTypeKey.String(typeStr(err)),
|
||||
errorMessageKey.String(err.Error()),
|
||||
)
|
||||
}
|
||||
|
||||
func typeStr(i interface{}) string {
|
||||
t := reflect.TypeOf(i)
|
||||
if t.PkgPath() == "" && t.Name() == "" {
|
||||
// Likely a builtin type.
|
||||
return t.String()
|
||||
}
|
||||
return fmt.Sprintf("%s.%s", t.PkgPath(), t.Name())
|
||||
}
|
||||
|
||||
func (s *span) Tracer() apitrace.Tracer {
|
||||
return s.tracer
|
||||
}
|
||||
|
@ -27,6 +27,8 @@ import (
|
||||
"go.opentelemetry.io/otel/api/global"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/grpc/codes"
|
||||
|
||||
"go.opentelemetry.io/otel/api/kv"
|
||||
@ -1146,3 +1148,26 @@ func TestWithInstrumentationVersion(t *testing.T) {
|
||||
t.Errorf("WithResource:\n -got +want %s", diff)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSpanCapturesPanic(t *testing.T) {
|
||||
var te testExporter
|
||||
tp, _ := NewProvider(WithSyncer(&te))
|
||||
_, span := tp.Tracer("CatchPanic").Start(
|
||||
context.Background(),
|
||||
"span",
|
||||
apitrace.WithRecord(),
|
||||
)
|
||||
|
||||
f := func() {
|
||||
defer span.End()
|
||||
panic(errors.New("error message"))
|
||||
}
|
||||
require.PanicsWithError(t, "error message", f)
|
||||
require.Len(t, te.spans, 1)
|
||||
require.Len(t, te.spans[0].MessageEvents, 1)
|
||||
assert.Equal(t, te.spans[0].MessageEvents[0].Name, errorEventName)
|
||||
assert.Equal(t, te.spans[0].MessageEvents[0].Attributes, []kv.KeyValue{
|
||||
errorTypeKey.String("*errors.errorString"),
|
||||
errorMessageKey.String("error message"),
|
||||
})
|
||||
}
|
||||
|
@ -66,14 +66,3 @@ func (tr *tracer) Start(ctx context.Context, name string, o ...apitrace.StartOpt
|
||||
span.executionTracerTaskEnd = end
|
||||
return apitrace.ContextWithSpan(ctx, span), span
|
||||
}
|
||||
|
||||
func (tr *tracer) WithSpan(ctx context.Context, name string, body func(ctx context.Context) error, opts ...apitrace.StartOption) error {
|
||||
ctx, span := tr.Start(ctx, name, opts...)
|
||||
defer span.End()
|
||||
|
||||
if err := body(ctx); err != nil {
|
||||
// TODO: set event with boolean attribute for error.
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user