-
Notifications
You must be signed in to change notification settings - Fork 60
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
Add SpanProfiler #448
Add SpanProfiler #448
Conversation
// isRemoteSpan reports whether the span context represents a remote parent. | ||
// | ||
// NOTE(kolesnikovae): this is ugly, but the only reliable method I found. | ||
// The opentracing-go package and Jaeger client are not meant to change as | ||
// both are deprecated. | ||
func isRemoteSpan(c jaeger.SpanContext) bool { | ||
jaegerCtx := *(*jaegerSpanCtx)(unsafe.Pointer(&c)) | ||
return jaegerCtx.remote | ||
} | ||
|
||
// jaegerSpanCtx represents memory layout of the jaeger.SpanContext type. | ||
type jaegerSpanCtx struct { | ||
traceID [16]byte // TraceID | ||
spanID [8]byte // SpanID | ||
parentID [8]byte // SpanID | ||
baggage uintptr // map[string]string | ||
debugID [2]uintptr // string | ||
|
||
// samplingState is a pointer to a struct that has "localRootSpan" member, | ||
// which we could probably use: that would allow omitting quite expensive | ||
// parentSpanContextFromRef call. However, interpreting the pointer and | ||
// the complex struct memory layout is more complicated and dangerous. | ||
samplingState uintptr | ||
|
||
// remote indicates that span context represents a remote parent | ||
remote 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 is, perhaps, the most controversial part. If anyone knows of a way to avoid such unsafe access, I'll be happy to make the necessary changes.
Please note that wrapping jaeger.SpanContext
is not easily achievable, as this concrete type is often expected by users and is utilized in assertions.
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.
To me this seems fine, we could consider running our fork of open tracing while we transition off.
// We create a span wrapper to ensure we remove the newly attached pprof | ||
// labels when span finishes. The need of this wrapper is questioned: | ||
// as we do not have the original context, we could leave the goroutine | ||
// labels – normally, span is finished at the very end of the goroutine's | ||
// lifetime, so no significant side effects should take place. | ||
w := spanWrapper{ | ||
parentPprofCtx: parentCtx, | ||
currentPprofCtx: ctx, | ||
} | ||
w.Span = span.SetTag(profileIDTagKey, spanID) | ||
return &w |
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.
It's worth noting that we could likely avoid wrapping jaeger.Span
and preserve the original type. On the other hand, pprof does not clarify the behavior in case goroutine labels are not cleared (AFAIK).
/cc @cyriltovena |
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.
LGTM
# Conflicts: # CHANGELOG.md
The PR introduces
spanprofiler
. Its purpose is to facilitate "request-scoped" profiles with minimal effort and impact on application performance. The use of OpenTracing and OpenTelemetry SDK interfaces appears to be the simplest solution to this problem. For a detailed description, please refer to the README note.We've successfully integrated and tested this feature in our internal deployments over the past two months with no reported issues.
Checklist
CHANGELOG.md
updated - the order of entries should be[CHANGE]
,[FEATURE]
,[ENHANCEMENT]
,[BUGFIX]