mirror of
https://github.com/status-im/status-go.git
synced 2025-01-11 15:14:52 +00:00
eeca435064
Update vendor Integrate rendezvous into status node Add a test with failover using rendezvous Use multiple servers in client Use discovery V5 by default and test that node can be started with rendezvous discovet Fix linter Update rendezvous client to one with instrumented stream Address feedback Fix test with updated topic limits Apply several suggestions Change log to debug for request errors because we continue execution Remove web3js after rebase Update rendezvous package
281 lines
9.1 KiB
Go
281 lines
9.1 KiB
Go
package loggabletracer
|
|
|
|
import (
|
|
"time"
|
|
|
|
writer "github.com/ipfs/go-log/writer"
|
|
opentracing "github.com/opentracing/opentracing-go"
|
|
)
|
|
|
|
// Tracer extends the opentracing.Tracer interface with methods to
|
|
// probe implementation state, for use by loggabletracer consumers.
|
|
type Tracer interface {
|
|
opentracing.Tracer
|
|
|
|
// Options gets the Options used in New() or NewWithOptions().
|
|
Options() Options
|
|
}
|
|
|
|
// Options allows creating a customized Tracer via NewWithOptions. The object
|
|
// must not be updated when there is an active tracer using it.
|
|
type Options 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.
|
|
// For example,
|
|
//
|
|
// func(traceID uint64) { return traceID % 64 == 0 }
|
|
//
|
|
// samples every 64th trace on average.
|
|
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
|
|
}
|
|
|
|
// DefaultOptions returns an Options object with a 1 in 64 sampling rate and
|
|
// all options disabled. A Recorder needs to be set manually before using the
|
|
// returned object with a Tracer.
|
|
func DefaultOptions() Options {
|
|
return Options{
|
|
ShouldSample: func(traceID uint64) bool { return traceID%64 == 0 },
|
|
MaxLogsPerSpan: 100,
|
|
}
|
|
}
|
|
|
|
// NewWithOptions creates a customized Tracer.
|
|
func NewWithOptions(opts Options) opentracing.Tracer {
|
|
rval := &LoggableTracer{options: opts}
|
|
rval.accessorPropagator = &accessorPropagator{rval}
|
|
return rval
|
|
}
|
|
|
|
// New creates and returns a standard Tracer which defers completed Spans to
|
|
// `recorder`.
|
|
// Spans created by this Tracer support the ext.SamplingPriority tag: Setting
|
|
// ext.SamplingPriority causes the Span to be Sampled from that point on.
|
|
func New(recorder SpanRecorder) opentracing.Tracer {
|
|
opts := DefaultOptions()
|
|
opts.Recorder = recorder
|
|
return NewWithOptions(opts)
|
|
}
|
|
|
|
// Implements the `Tracer` interface.
|
|
type LoggableTracer struct {
|
|
options Options
|
|
textPropagator *textMapPropagator
|
|
binaryPropagator *binaryPropagator
|
|
accessorPropagator *accessorPropagator
|
|
}
|
|
|
|
func (t *LoggableTracer) StartSpan(
|
|
operationName string,
|
|
opts ...opentracing.StartSpanOption,
|
|
) opentracing.Span {
|
|
|
|
if !writer.WriterGroup.Active() {
|
|
return opentracing.NoopTracer.StartSpan(opentracing.NoopTracer{}, operationName)
|
|
}
|
|
|
|
sso := opentracing.StartSpanOptions{}
|
|
for _, o := range opts {
|
|
o.Apply(&sso)
|
|
}
|
|
return t.StartSpanWithOptions(operationName, sso)
|
|
}
|
|
|
|
func (t *LoggableTracer) getSpan() *spanImpl {
|
|
if t.options.EnableSpanPool {
|
|
sp := spanPool.Get().(*spanImpl)
|
|
sp.reset()
|
|
return sp
|
|
}
|
|
return &spanImpl{}
|
|
}
|
|
|
|
func (t *LoggableTracer) StartSpanWithOptions(
|
|
operationName string,
|
|
opts opentracing.StartSpanOptions,
|
|
) opentracing.Span {
|
|
if !writer.WriterGroup.Active() {
|
|
return opentracing.NoopTracer.StartSpan(opentracing.NoopTracer{}, operationName)
|
|
}
|
|
// 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()
|
|
// Look for a parent in the list of References.
|
|
//
|
|
// TODO: would be nice if loggabletracer did something with all
|
|
// References, not just the first one.
|
|
ReferencesLoop:
|
|
for _, ref := range opts.References {
|
|
switch ref.Type {
|
|
case opentracing.ChildOfRef,
|
|
opentracing.FollowsFromRef:
|
|
|
|
refCtx, ok := ref.ReferencedContext.(SpanContext)
|
|
if !ok {
|
|
// Could be a noopSpanContext
|
|
// Ignore that parent.
|
|
continue
|
|
}
|
|
sp.raw.Context.TraceID = refCtx.TraceID
|
|
sp.raw.Context.SpanID = randomID()
|
|
sp.raw.Context.Sampled = refCtx.Sampled
|
|
sp.raw.ParentSpanID = refCtx.SpanID
|
|
|
|
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 == 0 {
|
|
// No parent Span found; allocate new trace and span ids and determine
|
|
// the Sampled status.
|
|
sp.raw.Context.TraceID, sp.raw.Context.SpanID = randomID2()
|
|
sp.raw.Context.Sampled = t.options.ShouldSample(sp.raw.Context.TraceID)
|
|
}
|
|
|
|
return t.startSpanInternal(
|
|
sp,
|
|
operationName,
|
|
startTime,
|
|
tags,
|
|
)
|
|
}
|
|
|
|
func (t *LoggableTracer) 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 *LoggableTracer) Inject(sc opentracing.SpanContext, format interface{}, carrier interface{}) error {
|
|
if !writer.WriterGroup.Active() {
|
|
return opentracing.NoopTracer.Inject(opentracing.NoopTracer{}, sc, format, carrier)
|
|
}
|
|
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 *LoggableTracer) Extract(format interface{}, carrier interface{}) (opentracing.SpanContext, error) {
|
|
if !writer.WriterGroup.Active() {
|
|
return opentracing.NoopTracer.Extract(opentracing.NoopTracer{}, format, carrier)
|
|
}
|
|
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 *LoggableTracer) Options() Options {
|
|
return t.options
|
|
}
|