Opentracing support
This commit is contained in:
parent
8394549857
commit
30ffba78e6
272 changed files with 44352 additions and 63 deletions
440
vendor/github.com/openzipkin/zipkin-go-opentracing/tracer.go
generated
vendored
Normal file
440
vendor/github.com/openzipkin/zipkin-go-opentracing/tracer.go
generated
vendored
Normal file
|
@ -0,0 +1,440 @@
|
|||
package zipkintracer
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
opentracing "github.com/opentracing/opentracing-go"
|
||||
"github.com/opentracing/opentracing-go/ext"
|
||||
|
||||
"github.com/openzipkin/zipkin-go-opentracing/flag"
|
||||
otobserver "github.com/opentracing-contrib/go-observer"
|
||||
)
|
||||
|
||||
// ErrInvalidEndpoint will be thrown if hostPort parameter is corrupted or host
|
||||
// can't be resolved
|
||||
var ErrInvalidEndpoint = errors.New("Invalid Endpoint. Please check hostPort parameter")
|
||||
|
||||
// Tracer extends the opentracing.Tracer interface with methods to
|
||||
// probe implementation state, for use by zipkintracer consumers.
|
||||
type Tracer interface {
|
||||
opentracing.Tracer
|
||||
|
||||
// Options gets the Options used in New() or NewWithOptions().
|
||||
Options() TracerOptions
|
||||
}
|
||||
|
||||
// TracerOptions allows creating a customized Tracer.
|
||||
type TracerOptions struct {
|
||||
// shouldSample is a function which is called when creating a new Span and
|
||||
// determines whether that Span is sampled. The randomized TraceID is supplied
|
||||
// to allow deterministic sampling decisions to be made across different nodes.
|
||||
shouldSample func(traceID uint64) bool
|
||||
// trimUnsampledSpans turns potentially expensive operations on unsampled
|
||||
// Spans into no-ops. More precisely, tags and log events are silently
|
||||
// discarded. If NewSpanEventListener is set, the callbacks will still fire.
|
||||
trimUnsampledSpans bool
|
||||
// recorder receives Spans which have been finished.
|
||||
recorder SpanRecorder
|
||||
// newSpanEventListener can be used to enhance the tracer by effectively
|
||||
// attaching external code to trace events. See NetTraceIntegrator for a
|
||||
// practical example, and event.go for the list of possible events.
|
||||
newSpanEventListener func() func(SpanEvent)
|
||||
// dropAllLogs turns log events on all Spans into no-ops.
|
||||
// If NewSpanEventListener is set, the callbacks will still fire.
|
||||
dropAllLogs bool
|
||||
// MaxLogsPerSpan limits the number of Logs in a span (if set to a nonzero
|
||||
// value). If a span has more logs than this value, logs are dropped as
|
||||
// necessary (and replaced with a log describing how many were dropped).
|
||||
//
|
||||
// About half of the MaxLogPerSpan logs kept are the oldest logs, and about
|
||||
// half are the newest logs.
|
||||
//
|
||||
// If NewSpanEventListener is set, the callbacks will still fire for all log
|
||||
// events. This value is ignored if DropAllLogs is true.
|
||||
maxLogsPerSpan int
|
||||
// debugAssertSingleGoroutine internally records the ID of the goroutine
|
||||
// creating each Span and verifies that no operation is carried out on
|
||||
// it on a different goroutine.
|
||||
// Provided strictly for development purposes.
|
||||
// Passing Spans between goroutine without proper synchronization often
|
||||
// results in use-after-Finish() errors. For a simple example, consider the
|
||||
// following pseudocode:
|
||||
//
|
||||
// func (s *Server) Handle(req http.Request) error {
|
||||
// sp := s.StartSpan("server")
|
||||
// defer sp.Finish()
|
||||
// wait := s.queueProcessing(opentracing.ContextWithSpan(context.Background(), sp), req)
|
||||
// select {
|
||||
// case resp := <-wait:
|
||||
// return resp.Error
|
||||
// case <-time.After(10*time.Second):
|
||||
// sp.LogEvent("timed out waiting for processing")
|
||||
// return ErrTimedOut
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// This looks reasonable at first, but a request which spends more than ten
|
||||
// seconds in the queue is abandoned by the main goroutine and its trace
|
||||
// finished, leading to use-after-finish when the request is finally
|
||||
// processed. Note also that even joining on to a finished Span via
|
||||
// StartSpanWithOptions constitutes an illegal operation.
|
||||
//
|
||||
// Code bases which do not require (or decide they do not want) Spans to
|
||||
// be passed across goroutine boundaries can run with this flag enabled in
|
||||
// tests to increase their chances of spotting wrong-doers.
|
||||
debugAssertSingleGoroutine bool
|
||||
// debugAssertUseAfterFinish is provided strictly for development purposes.
|
||||
// When set, it attempts to exacerbate issues emanating from use of Spans
|
||||
// after calling Finish by running additional assertions.
|
||||
debugAssertUseAfterFinish bool
|
||||
// enableSpanPool enables the use of a pool, so that the tracer reuses spans
|
||||
// after Finish has been called on it. Adds a slight performance gain as it
|
||||
// reduces allocations. However, if you have any use-after-finish race
|
||||
// conditions the code may panic.
|
||||
enableSpanPool bool
|
||||
// logger ...
|
||||
logger Logger
|
||||
// clientServerSameSpan allows for Zipkin V1 style span per RPC. This places
|
||||
// both client end and server end of a RPC call into the same span.
|
||||
clientServerSameSpan bool
|
||||
// debugMode activates Zipkin's debug request allowing for all Spans originating
|
||||
// from this tracer to pass through and bypass sampling. Use with extreme care
|
||||
// as it might flood your system if you have many traces starting from the
|
||||
// service you are instrumenting.
|
||||
debugMode bool
|
||||
// traceID128Bit enables the generation of 128 bit traceIDs in case the tracer
|
||||
// needs to create a root span. By default regular 64 bit traceIDs are used.
|
||||
// Regardless of this setting, the library will propagate and support both
|
||||
// 64 and 128 bit incoming traces from upstream sources.
|
||||
traceID128Bit bool
|
||||
|
||||
observer otobserver.Observer
|
||||
}
|
||||
|
||||
// TracerOption allows for functional options.
|
||||
// See: http://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis
|
||||
type TracerOption func(opts *TracerOptions) error
|
||||
|
||||
// WithSampler allows one to add a Sampler function
|
||||
func WithSampler(sampler Sampler) TracerOption {
|
||||
return func(opts *TracerOptions) error {
|
||||
opts.shouldSample = sampler
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// TrimUnsampledSpans option
|
||||
func TrimUnsampledSpans(trim bool) TracerOption {
|
||||
return func(opts *TracerOptions) error {
|
||||
opts.trimUnsampledSpans = trim
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// DropAllLogs option
|
||||
func DropAllLogs(dropAllLogs bool) TracerOption {
|
||||
return func(opts *TracerOptions) error {
|
||||
opts.dropAllLogs = dropAllLogs
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithLogger option
|
||||
func WithLogger(logger Logger) TracerOption {
|
||||
return func(opts *TracerOptions) error {
|
||||
opts.logger = logger
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// DebugAssertSingleGoroutine option
|
||||
func DebugAssertSingleGoroutine(val bool) TracerOption {
|
||||
return func(opts *TracerOptions) error {
|
||||
opts.debugAssertSingleGoroutine = val
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// DebugAssertUseAfterFinish option
|
||||
func DebugAssertUseAfterFinish(val bool) TracerOption {
|
||||
return func(opts *TracerOptions) error {
|
||||
opts.debugAssertUseAfterFinish = val
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// TraceID128Bit option
|
||||
func TraceID128Bit(val bool) TracerOption {
|
||||
return func(opts *TracerOptions) error {
|
||||
opts.traceID128Bit = val
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// ClientServerSameSpan allows to place client-side and server-side annotations
|
||||
// for a RPC call in the same span (Zipkin V1 behavior) or different spans
|
||||
// (more in line with other tracing solutions). By default this Tracer
|
||||
// uses shared host spans (so client-side and server-side in the same span).
|
||||
// If using separate spans you might run into trouble with Zipkin V1 as clock
|
||||
// skew issues can't be remedied at Zipkin server side.
|
||||
func ClientServerSameSpan(val bool) TracerOption {
|
||||
return func(opts *TracerOptions) error {
|
||||
opts.clientServerSameSpan = val
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// DebugMode allows to set the tracer to Zipkin debug mode
|
||||
func DebugMode(val bool) TracerOption {
|
||||
return func(opts *TracerOptions) error {
|
||||
opts.debugMode = val
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// EnableSpanPool ...
|
||||
func EnableSpanPool(val bool) TracerOption {
|
||||
return func(opts *TracerOptions) error {
|
||||
opts.enableSpanPool = val
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// NewSpanEventListener option
|
||||
func NewSpanEventListener(f func() func(SpanEvent)) TracerOption {
|
||||
return func(opts *TracerOptions) error {
|
||||
opts.newSpanEventListener = f
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithMaxLogsPerSpan option
|
||||
func WithMaxLogsPerSpan(limit int) TracerOption {
|
||||
return func(opts *TracerOptions) error {
|
||||
if limit < 5 || limit > 10000 {
|
||||
return errors.New("invalid MaxLogsPerSpan limit. Should be between 5 and 10000")
|
||||
}
|
||||
opts.maxLogsPerSpan = limit
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// NewTracer creates a new OpenTracing compatible Zipkin Tracer.
|
||||
func NewTracer(recorder SpanRecorder, options ...TracerOption) (opentracing.Tracer, error) {
|
||||
opts := &TracerOptions{
|
||||
recorder: recorder,
|
||||
shouldSample: alwaysSample,
|
||||
trimUnsampledSpans: false,
|
||||
newSpanEventListener: func() func(SpanEvent) { return nil },
|
||||
logger: &nopLogger{},
|
||||
debugAssertSingleGoroutine: false,
|
||||
debugAssertUseAfterFinish: false,
|
||||
clientServerSameSpan: true,
|
||||
debugMode: false,
|
||||
traceID128Bit: false,
|
||||
maxLogsPerSpan: 10000,
|
||||
observer: nil,
|
||||
}
|
||||
for _, o := range options {
|
||||
err := o(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
rval := &tracerImpl{options: *opts}
|
||||
rval.textPropagator = &textMapPropagator{rval}
|
||||
rval.binaryPropagator = &binaryPropagator{rval}
|
||||
rval.accessorPropagator = &accessorPropagator{rval}
|
||||
return rval, nil
|
||||
}
|
||||
|
||||
// Implements the `Tracer` interface.
|
||||
type tracerImpl struct {
|
||||
options TracerOptions
|
||||
textPropagator *textMapPropagator
|
||||
binaryPropagator *binaryPropagator
|
||||
accessorPropagator *accessorPropagator
|
||||
}
|
||||
|
||||
func (t *tracerImpl) StartSpan(
|
||||
operationName string,
|
||||
opts ...opentracing.StartSpanOption,
|
||||
) opentracing.Span {
|
||||
sso := opentracing.StartSpanOptions{}
|
||||
for _, o := range opts {
|
||||
o.Apply(&sso)
|
||||
}
|
||||
return t.startSpanWithOptions(operationName, sso)
|
||||
}
|
||||
|
||||
func (t *tracerImpl) getSpan() *spanImpl {
|
||||
if t.options.enableSpanPool {
|
||||
sp := spanPool.Get().(*spanImpl)
|
||||
sp.reset()
|
||||
return sp
|
||||
}
|
||||
return &spanImpl{}
|
||||
}
|
||||
|
||||
func (t *tracerImpl) startSpanWithOptions(
|
||||
operationName string,
|
||||
opts opentracing.StartSpanOptions,
|
||||
) opentracing.Span {
|
||||
// Start time.
|
||||
startTime := opts.StartTime
|
||||
if startTime.IsZero() {
|
||||
startTime = time.Now()
|
||||
}
|
||||
|
||||
// Tags.
|
||||
tags := opts.Tags
|
||||
|
||||
// Build the new span. This is the only allocation: We'll return this as
|
||||
// an opentracing.Span.
|
||||
sp := t.getSpan()
|
||||
|
||||
if t.options.observer != nil {
|
||||
sp.observer, _ = t.options.observer.OnStartSpan(sp, operationName, opts)
|
||||
}
|
||||
|
||||
// Look for a parent in the list of References.
|
||||
//
|
||||
// TODO: would be nice if basictracer did something with all
|
||||
// References, not just the first one.
|
||||
ReferencesLoop:
|
||||
for _, ref := range opts.References {
|
||||
switch ref.Type {
|
||||
case opentracing.ChildOfRef:
|
||||
refCtx := ref.ReferencedContext.(SpanContext)
|
||||
sp.raw.Context.TraceID = refCtx.TraceID
|
||||
sp.raw.Context.ParentSpanID = &refCtx.SpanID
|
||||
sp.raw.Context.Sampled = refCtx.Sampled
|
||||
sp.raw.Context.Flags = refCtx.Flags
|
||||
sp.raw.Context.Flags &^= flag.IsRoot // unset IsRoot flag if needed
|
||||
|
||||
if t.options.clientServerSameSpan &&
|
||||
tags[string(ext.SpanKind)] == ext.SpanKindRPCServer.Value {
|
||||
sp.raw.Context.SpanID = refCtx.SpanID
|
||||
sp.raw.Context.ParentSpanID = refCtx.ParentSpanID
|
||||
sp.raw.Context.Owner = false
|
||||
} else {
|
||||
sp.raw.Context.SpanID = randomID()
|
||||
sp.raw.Context.ParentSpanID = &refCtx.SpanID
|
||||
sp.raw.Context.Owner = true
|
||||
}
|
||||
|
||||
if l := len(refCtx.Baggage); l > 0 {
|
||||
sp.raw.Context.Baggage = make(map[string]string, l)
|
||||
for k, v := range refCtx.Baggage {
|
||||
sp.raw.Context.Baggage[k] = v
|
||||
}
|
||||
}
|
||||
break ReferencesLoop
|
||||
case opentracing.FollowsFromRef:
|
||||
refCtx := ref.ReferencedContext.(SpanContext)
|
||||
sp.raw.Context.TraceID = refCtx.TraceID
|
||||
sp.raw.Context.ParentSpanID = &refCtx.SpanID
|
||||
sp.raw.Context.Sampled = refCtx.Sampled
|
||||
sp.raw.Context.Flags = refCtx.Flags
|
||||
sp.raw.Context.Flags &^= flag.IsRoot // unset IsRoot flag if needed
|
||||
|
||||
sp.raw.Context.SpanID = randomID()
|
||||
sp.raw.Context.ParentSpanID = &refCtx.SpanID
|
||||
sp.raw.Context.Owner = true
|
||||
|
||||
if l := len(refCtx.Baggage); l > 0 {
|
||||
sp.raw.Context.Baggage = make(map[string]string, l)
|
||||
for k, v := range refCtx.Baggage {
|
||||
sp.raw.Context.Baggage[k] = v
|
||||
}
|
||||
}
|
||||
break ReferencesLoop
|
||||
}
|
||||
}
|
||||
if sp.raw.Context.TraceID.Empty() {
|
||||
// No parent Span found; allocate new trace and span ids and determine
|
||||
// the Sampled status.
|
||||
if t.options.traceID128Bit {
|
||||
sp.raw.Context.TraceID.High = randomID()
|
||||
}
|
||||
sp.raw.Context.TraceID.Low, sp.raw.Context.SpanID = randomID2()
|
||||
sp.raw.Context.Sampled = t.options.shouldSample(sp.raw.Context.TraceID.Low)
|
||||
sp.raw.Context.Flags = flag.IsRoot
|
||||
sp.raw.Context.Owner = true
|
||||
}
|
||||
if t.options.debugMode {
|
||||
sp.raw.Context.Flags |= flag.Debug
|
||||
}
|
||||
return t.startSpanInternal(
|
||||
sp,
|
||||
operationName,
|
||||
startTime,
|
||||
tags,
|
||||
)
|
||||
}
|
||||
|
||||
func (t *tracerImpl) startSpanInternal(
|
||||
sp *spanImpl,
|
||||
operationName string,
|
||||
startTime time.Time,
|
||||
tags opentracing.Tags,
|
||||
) opentracing.Span {
|
||||
sp.tracer = t
|
||||
if t.options.newSpanEventListener != nil {
|
||||
sp.event = t.options.newSpanEventListener()
|
||||
}
|
||||
sp.raw.Operation = operationName
|
||||
sp.raw.Start = startTime
|
||||
sp.raw.Duration = -1
|
||||
sp.raw.Tags = tags
|
||||
|
||||
if t.options.debugAssertSingleGoroutine {
|
||||
sp.SetTag(debugGoroutineIDTag, curGoroutineID())
|
||||
}
|
||||
defer sp.onCreate(operationName)
|
||||
return sp
|
||||
}
|
||||
|
||||
type delegatorType struct{}
|
||||
|
||||
// Delegator is the format to use for DelegatingCarrier.
|
||||
var Delegator delegatorType
|
||||
|
||||
func (t *tracerImpl) Inject(sc opentracing.SpanContext, format interface{}, carrier interface{}) error {
|
||||
switch format {
|
||||
case opentracing.TextMap, opentracing.HTTPHeaders:
|
||||
return t.textPropagator.Inject(sc, carrier)
|
||||
case opentracing.Binary:
|
||||
return t.binaryPropagator.Inject(sc, carrier)
|
||||
}
|
||||
if _, ok := format.(delegatorType); ok {
|
||||
return t.accessorPropagator.Inject(sc, carrier)
|
||||
}
|
||||
return opentracing.ErrUnsupportedFormat
|
||||
}
|
||||
|
||||
func (t *tracerImpl) Extract(format interface{}, carrier interface{}) (opentracing.SpanContext, error) {
|
||||
switch format {
|
||||
case opentracing.TextMap, opentracing.HTTPHeaders:
|
||||
return t.textPropagator.Extract(carrier)
|
||||
case opentracing.Binary:
|
||||
return t.binaryPropagator.Extract(carrier)
|
||||
}
|
||||
if _, ok := format.(delegatorType); ok {
|
||||
return t.accessorPropagator.Extract(carrier)
|
||||
}
|
||||
return nil, opentracing.ErrUnsupportedFormat
|
||||
}
|
||||
|
||||
func (t *tracerImpl) Options() TracerOptions {
|
||||
return t.options
|
||||
}
|
||||
|
||||
// WithObserver assigns an initialized observer to opts.observer
|
||||
func WithObserver(observer otobserver.Observer) TracerOption {
|
||||
return func(opts *TracerOptions) error {
|
||||
opts.observer = observer
|
||||
return nil
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue