@@ -15,36 +15,36 @@ import OpenTelemetryApi
15
15
/// exports the spans to wake up and start a new export cycle.
16
16
/// This batchSpanProcessor can cause high contention in a very high traffic service.
17
17
public 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
23
21
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
+ }
48
48
49
49
public let isStartRequired = false
50
50
public let isEndRequired = true
@@ -72,105 +72,95 @@ public struct BatchSpanProcessor: SpanProcessor {
72
72
/// the data.
73
73
/// The list of batched data is protected by a NSCondition which ensures full concurrency.
74
74
private 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
89
111
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
+ )
127
141
}
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
- }
156
142
}
143
+ }
144
+
145
+ deinit {
146
+ // Cleanup all gauge observer
147
+ self . queueSizeGauge? . close ( )
148
+ self . spanGaugeObserver? . close ( )
149
+ }
157
150
158
- deinit {
159
- // Cleanup all gauge observer
160
- self . queueSizeGauge? . close ( )
161
- self . spanGaugeObserver? . close ( )
162
- }
163
-
164
151
func addSpan( span: ReadableSpan ) {
165
152
cond. lock ( )
166
153
defer { cond. unlock ( ) }
167
154
168
155
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
+ ] )
170
160
return
171
161
}
172
162
spanList. append ( span)
173
-
163
+
174
164
// Notify the worker thread that at half of the queue is available. It will take
175
165
// time anyway for the thread to wake up.
176
166
if spanList. count >= halfMaxQueueSize {
@@ -180,18 +170,18 @@ private class BatchWorker: Thread {
180
170
181
171
override func main( ) {
182
172
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)
195
185
}
196
186
} while !self . isCancelled
197
187
}
@@ -228,16 +218,18 @@ private class BatchWorker: Thread {
228
218
timeoutTimer. cancel ( )
229
219
}
230
220
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
+ }
242
233
}
234
+ }
243
235
}
0 commit comments