You've already forked opentelemetry-go
mirror of
https://github.com/open-telemetry/opentelemetry-go.git
synced 2025-07-15 01:04:25 +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:
@ -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.
|
- 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)
|
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
|
### Fixed
|
||||||
|
|
||||||
- The `semconv.HTTPServerMetricAttributesFromHTTPRequest()` function no longer generates the high-cardinality `http.request.content.length` label. (#1031)
|
- The `semconv.HTTPServerMetricAttributesFromHTTPRequest()` function no longer generates the high-cardinality `http.request.content.length` label. (#1031)
|
||||||
- Correct instrumentation version tag in Jaeger exporter. (#1037)
|
- 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
|
## [0.10.0] - 2020-07-29
|
||||||
|
|
||||||
|
19
README.md
19
README.md
@ -50,22 +50,9 @@ func main() {
|
|||||||
defer pusher.Stop()
|
defer pusher.Stop()
|
||||||
|
|
||||||
tracer := global.Tracer("ex.com/basic")
|
tracer := global.Tracer("ex.com/basic")
|
||||||
|
ctx, span := tracer.Start(context.Background(), "main")
|
||||||
tracer.WithSpan(context.Background(), "foo",
|
defer span.End()
|
||||||
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
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -114,15 +114,6 @@ func (t *tracer) setDelegate(provider trace.Provider) {
|
|||||||
t.once.Do(func() { t.delegate = provider.Tracer(t.name, t.opts...) })
|
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
|
// Start implements trace.Tracer by forwarding the call to t.delegate if
|
||||||
// set, otherwise it forwards the call to a NoopTracer.
|
// 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) {
|
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()
|
ctx := context.Background()
|
||||||
gtp := global.TraceProvider()
|
gtp := global.TraceProvider()
|
||||||
tracer1 := gtp.Tracer("pre")
|
tracer1 := gtp.Tracer("pre")
|
||||||
|
// This is started before an SDK was registered and should be dropped.
|
||||||
_, span1 := tracer1.Start(ctx, "span1")
|
_, 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)
|
sr := new(testtrace.StandardSpanRecorder)
|
||||||
tp := testtrace.NewProvider(testtrace.WithSpanRecorder(sr))
|
tp := testtrace.NewProvider(testtrace.WithSpanRecorder(sr))
|
||||||
global.SetTraceProvider(tp)
|
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.
|
// The existing Tracer should have been configured to now use the configured SDK.
|
||||||
_, span2 := tracer1.Start(ctx, "span2")
|
_, span2 := tracer1.Start(ctx, "span2")
|
||||||
span2.End()
|
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.
|
// The global trace Provider should now create Tracers that also use the newly configured SDK.
|
||||||
tracer2 := gtp.Tracer("post")
|
tracer2 := gtp.Tracer("post")
|
||||||
_, span3 := tracer2.Start(ctx, "span3")
|
_, span3 := tracer2.Start(ctx, "span3")
|
||||||
span3.End()
|
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 {
|
filterNames := func(spans []*testtrace.Span) []string {
|
||||||
names := make([]string, len(spans))
|
names := make([]string, len(spans))
|
||||||
@ -67,7 +57,7 @@ func TestTraceWithSDK(t *testing.T) {
|
|||||||
}
|
}
|
||||||
return names
|
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.Started()))
|
||||||
assert.ElementsMatch(t, expected, filterNames(sr.Completed()))
|
assert.ElementsMatch(t, expected, filterNames(sr.Completed()))
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,6 @@ package testharness
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"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)
|
h.testSpan(subjectFactory)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -340,19 +207,6 @@ func (h *Harness) testSpan(tracerFactory func() trace.Tracer) {
|
|||||||
|
|
||||||
return subject
|
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 {
|
for mechanismName, mechanism := range mechanisms {
|
||||||
|
@ -54,16 +54,6 @@ func WithInstrumentationVersion(version string) TracerOption {
|
|||||||
type Tracer interface {
|
type Tracer interface {
|
||||||
// Start a span.
|
// Start a span.
|
||||||
Start(ctx context.Context, spanName string, opts ...StartOption) (context.Context, 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
|
// EndConfig provides options to set properties of span at the time of ending
|
||||||
|
@ -22,11 +22,6 @@ type NoopTracer struct{}
|
|||||||
|
|
||||||
var _ Tracer = NoopTracer{}
|
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.
|
// Start starts a noop span.
|
||||||
func (NoopTracer) Start(ctx context.Context, name string, opts ...StartOption) (context.Context, Span) {
|
func (NoopTracer) Start(ctx context.Context, name string, opts ...StartOption) (context.Context, Span) {
|
||||||
span := NoopSpan{}
|
span := NoopSpan{}
|
||||||
|
@ -86,10 +86,3 @@ func (t *Tracer) Start(ctx context.Context, name string, opts ...trace.StartOpti
|
|||||||
}
|
}
|
||||||
return trace.ContextWithSpan(ctx, span), span
|
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)
|
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)) {
|
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) {
|
func (t *MockTracer) Start(ctx context.Context, name string, opts ...oteltrace.StartOption) (context.Context, oteltrace.Span) {
|
||||||
spanOpts := oteltrace.StartConfig{}
|
spanOpts := oteltrace.StartConfig{}
|
||||||
for _, opt := range opts {
|
for _, opt := range opts {
|
||||||
|
@ -71,20 +71,6 @@ func (t *WrapperTracer) otelTracer() oteltrace.Tracer {
|
|||||||
return t.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
|
// Start forwards the call to the wrapped tracer. It also tries to
|
||||||
// override the tracer of the returned span if the span implements the
|
// override the tracer of the returned span if the span implements the
|
||||||
// OverrideTracerSpanExtension interface.
|
// OverrideTracerSpanExtension interface.
|
||||||
|
@ -67,11 +67,13 @@ func main() {
|
|||||||
valuerecorder := valuerecorderTwo.Bind(commonLabels...)
|
valuerecorder := valuerecorderTwo.Bind(commonLabels...)
|
||||||
defer valuerecorder.Unbind()
|
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))
|
span.AddEvent(ctx, "Nice operation!", kv.Key("bogons").Int(100))
|
||||||
|
span.SetAttributes(anotherKey.String("yes"))
|
||||||
trace.SpanFromContext(ctx).SetAttributes(anotherKey.String("yes"))
|
|
||||||
|
|
||||||
meter.RecordBatch(
|
meter.RecordBatch(
|
||||||
// Note: call-site variables added as context Entries:
|
// Note: call-site variables added as context Entries:
|
||||||
@ -81,20 +83,18 @@ func main() {
|
|||||||
valuerecorderTwo.Measurement(2.0),
|
valuerecorderTwo.Measurement(2.0),
|
||||||
)
|
)
|
||||||
|
|
||||||
return tracer.WithSpan(
|
return func(ctx context.Context) error {
|
||||||
ctx,
|
var span trace.Span
|
||||||
"Sub operation...",
|
ctx, span = tracer.Start(ctx, "Sub operation...")
|
||||||
func(ctx context.Context) error {
|
defer span.End()
|
||||||
trace.SpanFromContext(ctx).SetAttributes(lemonsKey.String("five"))
|
|
||||||
|
|
||||||
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 {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
@ -29,19 +29,15 @@ var (
|
|||||||
// SubOperation is an example to demonstrate the use of named tracer.
|
// SubOperation is an example to demonstrate the use of named tracer.
|
||||||
// It creates a named tracer with its package path.
|
// It creates a named tracer with its package path.
|
||||||
func SubOperation(ctx context.Context) error {
|
func SubOperation(ctx context.Context) error {
|
||||||
|
|
||||||
// Using global provider. Alternative is to have application provide a getter
|
// Using global provider. Alternative is to have application provide a getter
|
||||||
// for its component to get the instance of the provider.
|
// for its component to get the instance of the provider.
|
||||||
tr := global.Tracer("example/namedtracer/foo")
|
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"),
|
barKey.String("bar1"),
|
||||||
)
|
)
|
||||||
|
|
||||||
err := tracer.WithSpan(ctx, "operation", func(ctx context.Context) error {
|
var span trace.Span
|
||||||
|
ctx, span = tracer.Start(ctx, "operation")
|
||||||
trace.SpanFromContext(ctx).AddEvent(ctx, "Nice operation!", kv.Key("bogons").Int(100))
|
defer span.End()
|
||||||
|
span.AddEvent(ctx, "Nice operation!", kv.Key("bogons").Int(100))
|
||||||
trace.SpanFromContext(ctx).SetAttributes(anotherKey.String("yes"))
|
span.SetAttributes(anotherKey.String("yes"))
|
||||||
|
if err := foo.SubOperation(ctx); err != nil {
|
||||||
return foo.SubOperation(ctx)
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,20 +42,37 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
loopCounter = metric.Must(meter).NewInt64Counter("function.loops")
|
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")
|
nameKey = kv.Key("function.name")
|
||||||
)
|
)
|
||||||
|
|
||||||
func myFunction(ctx context.Context, values ...float64) error {
|
func add(ctx context.Context, x, y int64) int64 {
|
||||||
nameKV := nameKey.String("myFunction")
|
nameKV := nameKey.String("add")
|
||||||
boundCount := loopCounter.Bind(nameKV)
|
|
||||||
boundValue := paramValue.Bind(nameKV)
|
var span trace.Span
|
||||||
for _, value := range values {
|
ctx, span = tracer.Start(ctx, "Addition")
|
||||||
boundCount.Add(ctx, 1)
|
defer span.End()
|
||||||
boundValue.Record(ctx, value)
|
|
||||||
}
|
loopCounter.Add(ctx, 1, nameKV)
|
||||||
return nil
|
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() {
|
func Example() {
|
||||||
@ -70,22 +87,6 @@ func Example() {
|
|||||||
}
|
}
|
||||||
defer pusher.Stop()
|
defer pusher.Stop()
|
||||||
|
|
||||||
err = tracer.WithSpan(
|
ctx := context.Background()
|
||||||
context.Background(),
|
log.Println("the answer is", add(ctx, multiply(ctx, multiply(ctx, 2, 2), 10), 2))
|
||||||
"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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -43,14 +43,6 @@ type MockTracer struct {
|
|||||||
|
|
||||||
var _ apitrace.Tracer = (*MockTracer)(nil)
|
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.
|
// 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.
|
// TracdID is used from Parent Span Context and SpanID is assigned.
|
||||||
// If Parent SpanContext option is not specified then random TraceID is used.
|
// 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) {
|
func (s *span) End(options ...apitrace.EndOption) {
|
||||||
if s == nil {
|
if s == nil {
|
||||||
return
|
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 {
|
if s.executionTracerTaskEnd != nil {
|
||||||
s.executionTracerTaskEnd()
|
s.executionTracerTaskEnd()
|
||||||
}
|
}
|
||||||
@ -164,19 +176,21 @@ func (s *span) RecordError(ctx context.Context, err error, opts ...apitrace.Erro
|
|||||||
s.SetStatus(cfg.StatusCode, "")
|
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,
|
s.AddEventWithTimestamp(ctx, cfg.Timestamp, errorEventName,
|
||||||
errorTypeKey.String(errTypeString),
|
errorTypeKey.String(typeStr(err)),
|
||||||
errorMessageKey.String(err.Error()),
|
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 {
|
func (s *span) Tracer() apitrace.Tracer {
|
||||||
return s.tracer
|
return s.tracer
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,8 @@ import (
|
|||||||
"go.opentelemetry.io/otel/api/global"
|
"go.opentelemetry.io/otel/api/global"
|
||||||
|
|
||||||
"github.com/google/go-cmp/cmp"
|
"github.com/google/go-cmp/cmp"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
|
|
||||||
"go.opentelemetry.io/otel/api/kv"
|
"go.opentelemetry.io/otel/api/kv"
|
||||||
@ -1146,3 +1148,26 @@ func TestWithInstrumentationVersion(t *testing.T) {
|
|||||||
t.Errorf("WithResource:\n -got +want %s", diff)
|
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
|
span.executionTracerTaskEnd = end
|
||||||
return apitrace.ContextWithSpan(ctx, span), span
|
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
|
|
||||||
}
|
|
||||||
|
Reference in New Issue
Block a user