Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Initial tracing implementation #285

Merged
merged 1 commit into from Dec 7, 2020
Merged

feat: Initial tracing implementation #285

merged 1 commit into from Dec 7, 2020

Conversation

rhcarvalho
Copy link
Contributor

@rhcarvalho rhcarvalho commented Oct 16, 2020

This adds the StartSpan function and related APIs to the SDK.

The initial support focuses on manual instrumentation and HTTP servers
based on net/http.

Tracing is opt-in. Use one of the new options, TracesSampleRate or
TracesSampler, when initializing the SDK to enable sending transactions
and spans to Sentry.

The tracing APIs rely heavily on the standard Context type from Go, and
integrate with the SDKs notion of scopes.

See example/http/main.go for an example of how the new APIs are meant to
be used in practice.

While the basic functionality should be in place, more features are
planned for later.

image

@rhcarvalho rhcarvalho mentioned this pull request Oct 16, 2020
@rhcarvalho rhcarvalho linked an issue Nov 23, 2020 that may be closed by this pull request
@rhcarvalho rhcarvalho changed the base branch from master to feat/tracing December 1, 2020 22:02
@rhcarvalho rhcarvalho changed the title Add tracing support feat: Initial tracing implementation Dec 1, 2020
@rhcarvalho rhcarvalho closed this Dec 1, 2020
@rhcarvalho rhcarvalho deleted the branch master December 1, 2020 22:12
@rhcarvalho rhcarvalho reopened this Dec 1, 2020
@rhcarvalho rhcarvalho changed the base branch from feat/tracing to master December 1, 2020 22:16
@rhcarvalho rhcarvalho force-pushed the tracing branch 4 times, most recently from cbab8df to cca482c Compare December 2, 2020 00:43
Copy link
Contributor Author

@rhcarvalho rhcarvalho left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Notes for myself / reviewers.

}
}
}
```
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removing this example from the README to keep the document more concise. The example is available in full in the example directory, as well as we have some "how to get started" info both in doc comments for the package and in the official docs.

// This is an example web server to demonstrate how to instrument web servers
// with Sentry.
// This is an example web server to demonstrate how to instrument error and
// performance monitoring with Sentry.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔔 The changes in this file / example should give a taste of what performance instrumentation feels like.

@@ -83,11 +83,20 @@ func (h *Handler) handle(handler http.Handler) http.HandlerFunc {
hub := sentry.GetHubFromContext(ctx)
if hub == nil {
hub = sentry.CurrentHub().Clone()
ctx = sentry.SetHubOnContext(ctx, hub)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ This line moved.

Before, we'd unconditionally called SetHubOnContext, perhaps creating one unnecessary level of contexts (contexts are like a stack, and there would be two stack items with values pointing to the same hub).

Now, we only update the context if there was no hub previously stored. This is the common case and the reason why the old behavior probably did not affect many people.

What should not have changed is that every HTTP handler in user code will be guaranteed to have a hub ready for use. That is, GetHubFromContext never returns nil inside of an handler.

@@ -367,6 +367,15 @@ func GetHubFromContext(ctx context.Context) *Hub {
return nil
}

// hubFromContext returns either a hub stored in the context or the current hub.
// The return value is guaranteed to be non-nil, unlike GetHubFromContext.
func hubFromContext(ctx context.Context) *Hub {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This variation of GetHubFromContext is useful in various span methods, because when doing manual instrumentation it makes sense that StartSpan, etc, should always find a hub.

I decided not to export for now as users can still use GetHubFromContext which has more appropriate semantics in HTTP handlers: handlers should never accidentally use the global hub.

t.Fatalf("out of range [0.0, 1.0): %f", n)
}
}
// TODO: verify that distribution is uniform
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW, I've verified manually, didn't put time onto a formal validation.


// The TracesSamplerFunc type is an adapter to allow the use of ordinary
// functions as a TracesSampler.
type TracesSamplerFunc func(ctx SamplingContext) bool
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should make it easy for people to write their own sampling logic if they wish.


// fixedRateSampler is a TracesSampler that samples root spans randomly at a
// uniform rate.
type fixedRateSampler struct {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'll probably export this sampler in a later release to allow people to compose logic in their custom sampler.

// child := span.StartChild("top")
// SpanCheck{
// RecorderLen: 1,
// }.Check(t, child)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test is just a stub for SpanFromContext. Contents are commented out because spanFromContext doesn't return a "good enough dummy" to allow for the flow described by the test.

In a future pass through the tracing implementation, we either remove spanFromContext and everything related to it, or have to implement it such to make this test pass.

@rhcarvalho rhcarvalho marked this pull request as ready for review December 2, 2020 00:51
doc.go Outdated Show resolved Hide resolved
Comment on lines +136 to +138
for _, option := range options {
option(&span)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does it work exactly? I've seen you do scope.SetTransaction() somewhere as the option to this call.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Short version:

span := sentry.StartSpan(ctx, "http.server",
	sentry.TransactionName(fmt.Sprintf("%s %s", r.Method, r.URL.Path)),
	sentry.ContinueFromRequest(r),
	func(s *sentry.Span) {
		s.SpanID[4] = 9
		s.Description = "my description"
		// ...
	},
)

StartSpan takes zero or more functions that take a pointer of span argument (a.k.a. SpanOption type), and those functions can mutate the span.

We provide some helpers like TransactionName and ContinueFromRequest (the equivalent of "FromTraceparent"), but users can also roll their own functions to customize the span, get/set any exported fields.


Long version is https://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis

//
// TRACE_ID - SPAN_ID - SAMPLED
// [[:xdigit:]]{32}-[[:xdigit:]]{16}-[01]
var sentryTracePattern = regexp.MustCompile(`^([[:xdigit:]]{32})-([[:xdigit:]]{16})(?:-([01]))?$`)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, TIL :xdigit:

Copy link
Member

@HazAT HazAT left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why don't we have startTransaction?

HazAT
HazAT previously approved these changes Dec 7, 2020
This adds the StartSpan function and related APIs to the SDK.

The initial support focuses on manual instrumentation and HTTP servers
based on net/http.

Tracing is opt-in. Use one of the new options, TracesSampleRate or
TracesSampler, when initializing the SDK to enable sending transactions
and spans to Sentry.

The tracing APIs rely heavily on the standard Context type from Go, and
integrate with the SDKs notion of scopes.

See example/http/main.go for an example of how the new APIs are meant to
be used in practice.

While the basic functionality should be in place, more features are
planned for later.
@rhcarvalho rhcarvalho deleted the tracing branch December 7, 2020 17:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Sentry metrics
3 participants