@@ -69,9 +69,16 @@ type batchSpanProcessor struct {
69
69
e SpanExporter
70
70
o BatchSpanProcessorOptions
71
71
72
- queue chan ReadOnlySpan
73
- dropped uint32
74
- callbackRegistration metric.Registration
72
+ queue chan ReadOnlySpan
73
+ dropped uint32
74
+
75
+ callbackRegistration metric.Registration
76
+ spansProcessedCounter metric.Int64Counter
77
+ successAttributes metric.MeasurementOption
78
+ alreadyShutdownAttributes metric.MeasurementOption
79
+ noExporterAttributes metric.MeasurementOption
80
+ notSampledAttributes metric.MeasurementOption
81
+ queueFullAttributes metric.MeasurementOption
75
82
76
83
batch []ReadOnlySpan
77
84
batchMutex sync.Mutex
@@ -130,6 +137,15 @@ func NewBatchSpanProcessor(exporter SpanExporter, options ...BatchSpanProcessorO
130
137
return bsp
131
138
}
132
139
140
+ var processorID atomic.Uint64
141
+
142
+ // nextProcessorID returns an identifier for this batch span processor,
143
+ // starting with 0 and incrementing by 1 each time it is called.
144
+ func nextProcessorID () int64 {
145
+ return int64 (processorID .Add (1 ) - 1 )
146
+ }
147
+
148
+ // configureSelfObservability configures metrics for the batch span processor.
133
149
func (bsp * batchSpanProcessor ) configureSelfObservability () {
134
150
mp := otel .GetMeterProvider ()
135
151
if ! x .SelfObservability .Enabled () {
@@ -140,23 +156,44 @@ func (bsp *batchSpanProcessor) configureSelfObservability() {
140
156
metric .WithInstrumentationVersion (version ()),
141
157
)
142
158
143
- queueSizeCounter , err := meter .Int64ObservableUpDownCounter ("otel.sdk.span.processor.queue_size" ,
159
+ queueCapacityUpDownCounter , err := meter .Int64ObservableUpDownCounter ("otel.sdk.span.processor.queue_capacity" ,
160
+ metric .WithUnit ("{span}" ),
161
+ metric .WithDescription ("The maximum number of spans the queue of a given instance of an SDK span processor can hold." ),
162
+ )
163
+ if err != nil {
164
+ otel .Handle (err )
165
+ }
166
+ queueSizeUpDownCounter , err := meter .Int64ObservableUpDownCounter ("otel.sdk.span.processor.queue_size" ,
144
167
metric .WithUnit ("{span}" ),
145
168
metric .WithDescription ("The number of spans in the queue of a given instance of an SDK span processor." ),
146
169
)
147
170
if err != nil {
148
171
otel .Handle (err )
149
172
}
150
-
151
- attrsOpt := metric .WithAttributes (
152
- attribute . String ( "otel.sdk.component.name" , fmt . Sprintf ( "batching_span_processor/%p" , bsp ) ),
173
+ bsp . spansProcessedCounter , err = meter . Int64Counter ( "otel.sdk.span.processor.spans_processed" ,
174
+ metric .WithUnit ( "{span}" ),
175
+ metric . WithDescription ( "The number of spans for which the processing has finished, either successful or failed." ),
153
176
)
177
+ if err != nil {
178
+ otel .Handle (err )
179
+ }
180
+
181
+ componentTypeAttr := attribute .String ("otel.sdk.component.type" , "batching_span_processor" )
182
+ componentNameAttr := attribute .String ("otel.sdk.component.name" , fmt .Sprintf ("batching_span_processor/%d" , nextProcessorID ()))
183
+ bsp .successAttributes = metric .WithAttributes (componentNameAttr , componentTypeAttr , attribute .String ("error.type" , "" ))
184
+ bsp .alreadyShutdownAttributes = metric .WithAttributes (componentNameAttr , componentTypeAttr , attribute .String ("error.type" , "already_shutdown" ))
185
+ bsp .noExporterAttributes = metric .WithAttributes (componentNameAttr , componentTypeAttr , attribute .String ("error.type" , "no_exporter" ))
186
+ bsp .notSampledAttributes = metric .WithAttributes (componentNameAttr , componentTypeAttr , attribute .String ("error.type" , "not_sampled" ))
187
+ bsp .queueFullAttributes = metric .WithAttributes (componentNameAttr , componentTypeAttr , attribute .String ("error.type" , "queue_full" ))
188
+ callabckAttributesOpt := metric .WithAttributes (componentNameAttr , componentTypeAttr )
154
189
bsp .callbackRegistration , err = meter .RegisterCallback (
155
190
func (ctx context.Context , o metric.Observer ) error {
156
- o .ObserveInt64 (queueSizeCounter , int64 (len (bsp .queue )), attrsOpt )
191
+ o .ObserveInt64 (queueSizeUpDownCounter , int64 (len (bsp .queue )), callabckAttributesOpt )
192
+ o .ObserveInt64 (queueCapacityUpDownCounter , int64 (bsp .o .MaxQueueSize ), callabckAttributesOpt )
193
+ // TODO: can we track the number of spans batched, but not exported?
157
194
return nil
158
195
},
159
- queueSizeCounter )
196
+ queueSizeUpDownCounter , queueCapacityUpDownCounter )
160
197
if err != nil {
161
198
otel .Handle (err )
162
199
}
@@ -167,13 +204,16 @@ func (bsp *batchSpanProcessor) OnStart(parent context.Context, s ReadWriteSpan)
167
204
168
205
// OnEnd method enqueues a ReadOnlySpan for later processing.
169
206
func (bsp * batchSpanProcessor ) OnEnd (s ReadOnlySpan ) {
207
+ ctx := context .Background ()
170
208
// Do not enqueue spans after Shutdown.
171
209
if bsp .stopped .Load () {
210
+ bsp .spansProcessedCounter .Add (ctx , 1 , bsp .alreadyShutdownAttributes )
172
211
return
173
212
}
174
213
175
214
// Do not enqueue spans if we are just going to drop them.
176
215
if bsp .e == nil {
216
+ bsp .spansProcessedCounter .Add (ctx , 1 , bsp .noExporterAttributes )
177
217
return
178
218
}
179
219
bsp .enqueue (s )
@@ -315,6 +355,7 @@ func (bsp *batchSpanProcessor) exportSpans(ctx context.Context) error {
315
355
316
356
if l := len (bsp .batch ); l > 0 {
317
357
global .Debug ("exporting spans" , "count" , len (bsp .batch ), "total_dropped" , atomic .LoadUint32 (& bsp .dropped ))
358
+ bsp .spansProcessedCounter .Add (ctx , int64 (len (bsp .batch )), bsp .successAttributes )
318
359
err := bsp .e .ExportSpans (ctx , bsp .batch )
319
360
320
361
// A new batch is always created after exporting, even if the batch failed to be exported.
@@ -416,19 +457,23 @@ func (bsp *batchSpanProcessor) enqueue(sd ReadOnlySpan) {
416
457
417
458
func (bsp * batchSpanProcessor ) enqueueBlockOnQueueFull (ctx context.Context , sd ReadOnlySpan ) bool {
418
459
if ! sd .SpanContext ().IsSampled () {
460
+ bsp .spansProcessedCounter .Add (ctx , 1 , bsp .notSampledAttributes )
419
461
return false
420
462
}
421
463
464
+ // TODO: Can we track the number of spans blocking on the queue?
422
465
select {
423
466
case bsp .queue <- sd :
424
467
return true
425
468
case <- ctx .Done ():
469
+ bsp .spansProcessedCounter .Add (ctx , 1 , bsp .queueFullAttributes )
426
470
return false
427
471
}
428
472
}
429
473
430
- func (bsp * batchSpanProcessor ) enqueueDrop (_ context.Context , sd ReadOnlySpan ) bool {
474
+ func (bsp * batchSpanProcessor ) enqueueDrop (ctx context.Context , sd ReadOnlySpan ) bool {
431
475
if ! sd .SpanContext ().IsSampled () {
476
+ bsp .spansProcessedCounter .Add (ctx , 1 , bsp .notSampledAttributes )
432
477
return false
433
478
}
434
479
@@ -437,6 +482,7 @@ func (bsp *batchSpanProcessor) enqueueDrop(_ context.Context, sd ReadOnlySpan) b
437
482
return true
438
483
default :
439
484
atomic .AddUint32 (& bsp .dropped , 1 )
485
+ bsp .spansProcessedCounter .Add (ctx , 1 , bsp .queueFullAttributes )
440
486
}
441
487
return false
442
488
}
0 commit comments