-
Notifications
You must be signed in to change notification settings - Fork 221
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
Conversation
cbab8df
to
cca482c
Compare
There was a problem hiding this 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.
} | ||
} | ||
} | ||
``` |
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 { |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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.
traces_sampler.go
Outdated
|
||
// The TracesSamplerFunc type is an adapter to allow the use of ordinary | ||
// functions as a TracesSampler. | ||
type TracesSamplerFunc func(ctx SamplingContext) bool |
There was a problem hiding this comment.
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.
traces_sampler.go
Outdated
|
||
// fixedRateSampler is a TracesSampler that samples root spans randomly at a | ||
// uniform rate. | ||
type fixedRateSampler struct { |
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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.
for _, option := range options { | ||
option(&span) | ||
} |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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]))?$`) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice, TIL :xdigit:
There was a problem hiding this 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
?
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.
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.