@@ -15,36 +15,36 @@ import OpenTelemetryApi
1515/// exports the spans to wake up and start a new export cycle.
1616/// This batchSpanProcessor can cause high contention in a very high traffic service.
1717public struct BatchSpanProcessor : SpanProcessor {
18- fileprivate static let SPAN_PROCESSOR_TYPE_LABEL : String = " processorType "
19- fileprivate static let SPAN_PROCESSOR_DROPPED_LABEL : String = " dropped "
20- fileprivate static let SPAN_PROCESSOR_TYPE_VALUE : String = BatchSpanProcessor . name
21-
22- fileprivate var worker : BatchWorker
18+ fileprivate static let SPAN_PROCESSOR_TYPE_LABEL : String = " processorType "
19+ fileprivate static let SPAN_PROCESSOR_DROPPED_LABEL : String = " dropped "
20+ fileprivate static let SPAN_PROCESSOR_TYPE_VALUE : String = BatchSpanProcessor . name
2321
24- public static var name : String {
25- String ( describing: Self . self)
26- }
27-
28- public init (
29- spanExporter: SpanExporter ,
30- meterProvider: StableMeterProvider ,
31- scheduleDelay: TimeInterval = 5 ,
32- exportTimeout: TimeInterval = 30 ,
33- maxQueueSize: Int = 2048 ,
34- maxExportBatchSize: Int = 512 ,
35- willExportCallback: ( ( inout [ SpanData ] ) -> Void ) ? = nil
36- ) {
37- worker = BatchWorker (
38- spanExporter: spanExporter,
39- meterProvider: meterProvider,
40- scheduleDelay: scheduleDelay,
41- exportTimeout: exportTimeout,
42- maxQueueSize: maxQueueSize,
43- maxExportBatchSize: maxExportBatchSize,
44- willExportCallback: willExportCallback
45- )
46- worker. start ( )
47- }
22+ fileprivate var worker : BatchWorker
23+
24+ public static var name : String {
25+ String ( describing: Self . self)
26+ }
27+
28+ public init (
29+ spanExporter: SpanExporter ,
30+ meterProvider: StableMeterProvider ? = nil ,
31+ scheduleDelay: TimeInterval = 5 ,
32+ exportTimeout: TimeInterval = 30 ,
33+ maxQueueSize: Int = 2048 ,
34+ maxExportBatchSize: Int = 512 ,
35+ willExportCallback: ( ( inout [ SpanData ] ) -> Void ) ? = nil
36+ ) {
37+ worker = BatchWorker (
38+ spanExporter: spanExporter,
39+ meterProvider: meterProvider,
40+ scheduleDelay: scheduleDelay,
41+ exportTimeout: exportTimeout,
42+ maxQueueSize: maxQueueSize,
43+ maxExportBatchSize: maxExportBatchSize,
44+ willExportCallback: willExportCallback
45+ )
46+ worker. start ( )
47+ }
4848
4949 public let isStartRequired = false
5050 public let isEndRequired = true
@@ -72,105 +72,95 @@ public struct BatchSpanProcessor: SpanProcessor {
7272/// the data.
7373/// The list of batched data is protected by a NSCondition which ensures full concurrency.
7474private class BatchWorker : Thread {
75- let spanExporter : SpanExporter
76- let meterProvider : StableMeterProvider
77- let scheduleDelay : TimeInterval
78- let maxQueueSize : Int
79- let exportTimeout : TimeInterval
80- let maxExportBatchSize : Int
81- let willExportCallback : ( ( inout [ SpanData ] ) -> Void ) ?
82- let halfMaxQueueSize : Int
83- private let cond = NSCondition ( )
84- var spanList = [ ReadableSpan] ( )
85- var queue : OperationQueue
86-
87- private var queueSizeGauge : ObservableLongGauge ?
88- private var spanGaugeObserver : ObservableLongGauge ?
75+ let spanExporter : SpanExporter
76+ let meterProvider : StableMeterProvider ?
77+ let scheduleDelay : TimeInterval
78+ let maxQueueSize : Int
79+ let exportTimeout : TimeInterval
80+ let maxExportBatchSize : Int
81+ let willExportCallback : ( ( inout [ SpanData ] ) -> Void ) ?
82+ let halfMaxQueueSize : Int
83+ private let cond = NSCondition ( )
84+ var spanList = [ ReadableSpan] ( )
85+ var queue : OperationQueue
86+
87+ private var queueSizeGauge : ObservableLongGauge ?
88+ private var spanGaugeObserver : ObservableLongGauge ?
89+ private var processedSpansCounter : LongCounter ?
90+
91+ init (
92+ spanExporter: SpanExporter ,
93+ meterProvider: StableMeterProvider ? = nil ,
94+ scheduleDelay: TimeInterval ,
95+ exportTimeout: TimeInterval ,
96+ maxQueueSize: Int ,
97+ maxExportBatchSize: Int ,
98+ willExportCallback: ( ( inout [ SpanData ] ) -> Void ) ?
99+ ) {
100+ self . spanExporter = spanExporter
101+ self . meterProvider = meterProvider
102+ self . scheduleDelay = scheduleDelay
103+ self . exportTimeout = exportTimeout
104+ self . maxQueueSize = maxQueueSize
105+ halfMaxQueueSize = maxQueueSize >> 1
106+ self . maxExportBatchSize = maxExportBatchSize
107+ self . willExportCallback = willExportCallback
108+ queue = OperationQueue ( )
109+ queue. name = " BatchWorker Queue "
110+ queue. maxConcurrentOperationCount = 1
89111
90- private var processedSpansCounter : LongCounter ?
91- private let droppedAttrs : [ String : AttributeValue ]
92- private let exportedAttrs : [ String : AttributeValue ]
93- private let spanGaugeBuilder : LongGaugeBuilder
94- init (
95- spanExporter: SpanExporter ,
96- meterProvider: StableMeterProvider ,
97- scheduleDelay: TimeInterval ,
98- exportTimeout: TimeInterval ,
99- maxQueueSize: Int ,
100- maxExportBatchSize: Int ,
101- willExportCallback: ( ( inout [ SpanData ] ) -> Void ) ?
102- ) {
103- self . spanExporter = spanExporter
104- self . meterProvider = meterProvider
105- self . scheduleDelay = scheduleDelay
106- self . exportTimeout = exportTimeout
107- self . maxQueueSize = maxQueueSize
108- halfMaxQueueSize = maxQueueSize >> 1
109- self . maxExportBatchSize = maxExportBatchSize
110- self . willExportCallback = willExportCallback
111- queue = OperationQueue ( )
112- queue. name = " BatchWorker Queue "
113- queue. maxConcurrentOperationCount = 1
114-
115- let meter = meterProvider. meterBuilder ( name: " io.opentelemetry.sdk.trace " ) . build ( )
116-
117- var longGaugeSdk = meter. gaugeBuilder ( name: " queueSize " ) . ofLongs ( ) as? LongGaugeBuilderSdk
118- longGaugeSdk = longGaugeSdk? . setDescription ( " The number of items queued " )
119- longGaugeSdk = longGaugeSdk? . setUnit ( " 1 " )
120- self . queueSizeGauge = longGaugeSdk? . buildWithCallback { result in
121- result. record (
122- value: maxQueueSize,
123- attributes: [
124- BatchSpanProcessor . SPAN_PROCESSOR_TYPE_LABEL: . string( BatchSpanProcessor . SPAN_PROCESSOR_TYPE_VALUE)
125- ]
126- )
112+ if let meter = meterProvider? . meterBuilder ( name: " io.opentelemetry.sdk.trace " ) . build ( ) {
113+
114+ var longGaugeSdk = meter. gaugeBuilder ( name: " queueSize " ) . ofLongs ( ) as? LongGaugeBuilderSdk
115+ longGaugeSdk = longGaugeSdk? . setDescription ( " The number of items queued " )
116+ longGaugeSdk = longGaugeSdk? . setUnit ( " 1 " )
117+ self . queueSizeGauge = longGaugeSdk? . buildWithCallback { result in
118+ result. record (
119+ value: maxQueueSize,
120+ attributes: [
121+ BatchSpanProcessor . SPAN_PROCESSOR_TYPE_LABEL: . string( BatchSpanProcessor . SPAN_PROCESSOR_TYPE_VALUE)
122+ ]
123+ )
124+ }
125+
126+ var longCounterSdk = meter. counterBuilder ( name: " processedSpans " ) as? LongCounterMeterBuilderSdk
127+ longCounterSdk = longCounterSdk? . setUnit ( " 1 " )
128+ longCounterSdk = longCounterSdk? . setDescription ( " The number of spans processed by the BatchSpanProcessor. [dropped=true if they were dropped due to high throughput] " )
129+ processedSpansCounter = longCounterSdk? . build ( )
130+
131+ // Subscribe to new gauge observer
132+ self . spanGaugeObserver = meter. gaugeBuilder ( name: " spanSize " )
133+ . ofLongs ( )
134+ . buildWithCallback { [ count = spanList. count] result in
135+ result. record (
136+ value: count,
137+ attributes: [
138+ BatchSpanProcessor . SPAN_PROCESSOR_TYPE_LABEL: . string( BatchSpanProcessor . SPAN_PROCESSOR_TYPE_VALUE)
139+ ]
140+ )
127141 }
128-
129- self . spanGaugeBuilder = meter. gaugeBuilder ( name: " spanSize " )
130- . ofLongs ( )
131-
132- var longCounterSdk = meter. counterBuilder ( name: " processedSpans " ) as? LongCounterMeterBuilderSdk
133- longCounterSdk = longCounterSdk? . setUnit ( " 1 " )
134- longCounterSdk = longCounterSdk? . setDescription ( " The number of spans processed by the BatchSpanProcessor. [dropped=true if they were dropped due to high throughput] " )
135- processedSpansCounter = longCounterSdk? . build ( )
136-
137- droppedAttrs = [
138- BatchSpanProcessor . SPAN_PROCESSOR_TYPE_LABEL: . string( BatchSpanProcessor . SPAN_PROCESSOR_TYPE_VALUE) ,
139- BatchSpanProcessor . SPAN_PROCESSOR_DROPPED_LABEL: . bool( true )
140- ]
141- exportedAttrs = [
142- BatchSpanProcessor . SPAN_PROCESSOR_TYPE_LABEL: . string( BatchSpanProcessor . SPAN_PROCESSOR_TYPE_VALUE) ,
143- BatchSpanProcessor . SPAN_PROCESSOR_DROPPED_LABEL: . bool( false )
144- ]
145-
146- // Subscribe to new gauge observer
147- self . spanGaugeObserver = self . spanGaugeBuilder
148- . buildWithCallback { [ count = spanList. count] result in
149- result. record (
150- value: count,
151- attributes: [
152- BatchSpanProcessor . SPAN_PROCESSOR_TYPE_LABEL: . string( BatchSpanProcessor . SPAN_PROCESSOR_TYPE_VALUE)
153- ]
154- )
155- }
156142 }
143+ }
144+
145+ deinit {
146+ // Cleanup all gauge observer
147+ self . queueSizeGauge? . close ( )
148+ self . spanGaugeObserver? . close ( )
149+ }
157150
158- deinit {
159- // Cleanup all gauge observer
160- self . queueSizeGauge? . close ( )
161- self . spanGaugeObserver? . close ( )
162- }
163-
164151 func addSpan( span: ReadableSpan ) {
165152 cond. lock ( )
166153 defer { cond. unlock ( ) }
167154
168155 if spanList. count == maxQueueSize {
169- processedSpansCounter? . add ( value: 1 , attribute: droppedAttrs)
156+ processedSpansCounter? . add ( value: 1 , attribute: [
157+ BatchSpanProcessor . SPAN_PROCESSOR_TYPE_LABEL: . string( BatchSpanProcessor . SPAN_PROCESSOR_TYPE_VALUE) ,
158+ BatchSpanProcessor . SPAN_PROCESSOR_DROPPED_LABEL: . bool( true )
159+ ] )
170160 return
171161 }
172162 spanList. append ( span)
173-
163+
174164 // Notify the worker thread that at half of the queue is available. It will take
175165 // time anyway for the thread to wake up.
176166 if spanList. count >= halfMaxQueueSize {
@@ -180,18 +170,18 @@ private class BatchWorker: Thread {
180170
181171 override func main( ) {
182172 repeat {
183- autoreleasepool {
184- var spansCopy : [ ReadableSpan ]
185- cond. lock ( )
186- if spanList. count < maxExportBatchSize {
187- repeat {
188- cond. wait ( until: Date ( ) . addingTimeInterval ( scheduleDelay) )
189- } while spanList. isEmpty && !self . isCancelled
190- }
191- spansCopy = spanList
192- spanList. removeAll ( )
193- cond. unlock ( )
194- self . exportBatch ( spanList: spansCopy, explicitTimeout: self . exportTimeout)
173+ autoreleasepool {
174+ var spansCopy : [ ReadableSpan ]
175+ cond. lock ( )
176+ if spanList. count < maxExportBatchSize {
177+ repeat {
178+ cond. wait ( until: Date ( ) . addingTimeInterval ( scheduleDelay) )
179+ } while spanList. isEmpty && !self . isCancelled
180+ }
181+ spansCopy = spanList
182+ spanList. removeAll ( )
183+ cond. unlock ( )
184+ self . exportBatch ( spanList: spansCopy, explicitTimeout: self . exportTimeout)
195185 }
196186 } while !self . isCancelled
197187 }
@@ -228,16 +218,18 @@ private class BatchWorker: Thread {
228218 timeoutTimer. cancel ( )
229219 }
230220
231- private func exportAction( spanList: [ ReadableSpan ] , explicitTimeout: TimeInterval ? = nil ) {
232- stride ( from: 0 , to: spanList. endIndex, by: maxExportBatchSize) . forEach {
233- var spansToExport = spanList [ $0 ..< min ( $0 + maxExportBatchSize, spanList. count) ] . map { $0. toSpanData ( ) }
234- willExportCallback ? ( & spansToExport)
235- let result = spanExporter. export ( spans: spansToExport, explicitTimeout: explicitTimeout)
236- if result == . success {
237- cond. lock ( )
238- processedSpansCounter? . add ( value: spanList. count, attribute: exportedAttrs)
239- cond. unlock ( )
240- }
241- }
221+ private func exportAction( spanList: [ ReadableSpan ] , explicitTimeout: TimeInterval ? = nil ) {
222+ stride ( from: 0 , to: spanList. endIndex, by: maxExportBatchSize) . forEach {
223+ var spansToExport = spanList [ $0 ..< min ( $0 + maxExportBatchSize, spanList. count) ] . map { $0. toSpanData ( ) }
224+ willExportCallback ? ( & spansToExport)
225+ let result = spanExporter. export ( spans: spansToExport, explicitTimeout: explicitTimeout)
226+ if result == . success {
227+ cond. lock ( )
228+ processedSpansCounter? . add ( value: spanList. count, attribute: [
229+ BatchSpanProcessor . SPAN_PROCESSOR_TYPE_LABEL: . string( BatchSpanProcessor . SPAN_PROCESSOR_TYPE_VALUE) ,
230+ BatchSpanProcessor . SPAN_PROCESSOR_DROPPED_LABEL: . bool( false ) ] )
231+ cond. unlock ( ) ;
232+ }
242233 }
234+ }
243235}
0 commit comments