github.com/mier85/go-sensor@v1.30.1-0.20220920111756-9bf41b3bc7e0/recorder.go (about) 1 // (c) Copyright IBM Corp. 2021 2 // (c) Copyright Instana Inc. 2016 3 4 package instana 5 6 import ( 7 "context" 8 "fmt" 9 "sync" 10 "time" 11 ) 12 13 // A SpanRecorder handles all of the `RawSpan` data generated via an 14 // associated `Tracer` (see `NewStandardTracer`) instance. It also names 15 // the containing process and provides access to a straightforward tag map. 16 type SpanRecorder interface { 17 // Implementations must determine whether and where to store `span`. 18 RecordSpan(span *spanS) 19 // Flush forces sending any buffered finished spans 20 Flush(context.Context) error 21 } 22 23 // Recorder accepts spans, processes and queues them 24 // for delivery to the backend. 25 type Recorder struct { 26 sync.RWMutex 27 spans []Span 28 testMode bool 29 30 queueWhenNotReady bool 31 flushInterval time.Duration 32 } 33 34 // RecorderOption lets you specify more options for the recorder 35 type RecorderOption func(recorder *Recorder) 36 37 // RecorderWithQueueWhenNotReady will enable queuing even when the sensor is not yet ready 38 func RecorderWithQueueWhenNotReady() RecorderOption { 39 return func(recorder *Recorder) { 40 recorder.queueWhenNotReady = true 41 } 42 } 43 44 // RecorderWithFlushInterval lets you specify how often traces should be flushed 45 func RecorderWithFlushInterval(flushInterval time.Duration) RecorderOption { 46 return func(recorder *Recorder) { 47 recorder.flushInterval = flushInterval 48 } 49 } 50 51 // NewRecorder initializes a new span recorder 52 func NewRecorder(options ...RecorderOption) *Recorder { 53 r := &Recorder{ 54 flushInterval: 1 * time.Second, 55 queueWhenNotReady: false, 56 } 57 58 for _, option := range options { 59 option(r) 60 } 61 62 ticker := time.NewTicker(r.flushInterval) 63 go func() { 64 for range ticker.C { 65 if sensor.Agent().Ready() { 66 go r.Flush(context.Background()) 67 } 68 } 69 }() 70 71 return r 72 } 73 74 // NewTestRecorder initializes a new span recorder that keeps all collected 75 // until they are requested. This recorder does not send spans to the agent (used for testing) 76 func NewTestRecorder() *Recorder { 77 return &Recorder{ 78 testMode: true, 79 } 80 } 81 82 // RecordSpan accepts spans to be recorded and added to the span queue 83 // for eventual reporting to the host agent. 84 func (r *Recorder) RecordSpan(span *spanS) { 85 // If we're not announced and not in test mode then just 86 // return 87 if !r.testMode && !sensor.Agent().Ready() && !r.queueWhenNotReady { 88 return 89 } 90 91 r.Lock() 92 defer r.Unlock() 93 94 if len(r.spans) == sensor.options.MaxBufferedSpans { 95 r.spans = r.spans[1:] 96 } 97 98 r.spans = append(r.spans, newSpan(span)) 99 100 if r.testMode || !sensor.Agent().Ready() { 101 return 102 } 103 104 if len(r.spans) >= sensor.options.ForceTransmissionStartingAt { 105 sensor.logger.Debug("forcing ", len(r.spans), "span(s) to the agent") 106 go r.Flush(context.Background()) 107 } 108 } 109 110 // QueuedSpansCount returns the number of queued spans 111 // Used only in tests currently. 112 func (r *Recorder) QueuedSpansCount() int { 113 r.RLock() 114 defer r.RUnlock() 115 return len(r.spans) 116 } 117 118 // GetQueuedSpans returns a copy of the queued spans and clears the queue. 119 func (r *Recorder) GetQueuedSpans() []Span { 120 r.Lock() 121 defer r.Unlock() 122 123 // Copy queued spans 124 queuedSpans := make([]Span, len(r.spans)) 125 copy(queuedSpans, r.spans) 126 127 // and clear out the source 128 r.clearQueuedSpans() 129 return queuedSpans 130 } 131 132 // Flush sends queued spans to the agent 133 func (r *Recorder) Flush(ctx context.Context) error { 134 spansToSend := r.GetQueuedSpans() 135 if len(spansToSend) == 0 { 136 return nil 137 } 138 139 if err := sensor.Agent().SendSpans(spansToSend); err != nil { 140 r.Lock() 141 defer r.Unlock() 142 143 // put failed spans in front of the queue to make sure they are evicted first 144 // whenever the queue length exceeds options.MaxBufferedSpans 145 r.spans = append(spansToSend, r.spans...) 146 147 return fmt.Errorf("failed to send collected spans to the agent: %s", err) 148 } 149 150 return nil 151 } 152 153 // clearQueuedSpans brings the span queue to empty/0/nada 154 // This function doesn't take the Lock so make sure to have 155 // the write lock before calling. 156 // This is meant to be called from GetQueuedSpans which handles 157 // locking. 158 func (r *Recorder) clearQueuedSpans() { 159 r.spans = r.spans[:0] 160 }