LCOV - code coverage report
Current view: top level - lib/src/trace/export - simple_span_processor.dart (source / functions) Coverage Total Hit
Test: lcov.info Lines: 94.2 % 104 98
Test Date: 2026-07-11 13:35:19 Functions: - 0 0

            Line data    Source code
       1              : // Copyright The OpenTelemetry Authors
       2              : // SPDX-License-Identifier: Apache-2.0
       3              : 
       4              : import 'package:dartastic_opentelemetry_api/dartastic_opentelemetry_api.dart';
       5              : 
       6              : import '../span.dart';
       7              : import '../span_processor.dart';
       8              : import 'span_exporter.dart';
       9              : 
      10              : /// A simple SpanProcessor that exports spans synchronously when they end.
      11              : ///
      12              : /// This processor should only be used for testing or debugging purposes as it
      13              : /// blocks until the export is complete.
      14              : class SimpleSpanProcessor implements SpanProcessor {
      15              :   final SpanExporter _spanExporter;
      16              :   bool _isShutdown = false;
      17              :   final List<Future<void>> _pendingExports = [];
      18              : 
      19              :   /// Creates a new SimpleSpanProcessor that exports spans using the given [SpanExporter].
      20           18 :   SimpleSpanProcessor(this._spanExporter);
      21              : 
      22           18 :   @override
      23              :   Future<void> onStart(Span span, Context? parentContext) async {
      24           18 :     if (OTelLog.isDebug()) {
      25           18 :       OTelLog.debug(
      26           90 :         'SimpleSpanProcessor: onStart called for span ${span.spanContext.spanId}, traceId: ${span.spanContext.traceId}',
      27              :       );
      28              :     }
      29              :   }
      30              : 
      31           18 :   @override
      32              :   Future<void> onEnd(Span span) async {
      33           18 :     if (OTelLog.isDebug()) {
      34           18 :       OTelLog.debug(
      35           72 :         'SimpleSpanProcessor: onEnd called for span ${span.name} with ID ${span.spanContext.spanId}',
      36              :       );
      37              :     }
      38           18 :     if (_isShutdown) {
      39            5 :       if (OTelLog.isDebug()) {
      40            5 :         OTelLog.debug(
      41              :           'SimpleSpanProcessor: Skipping export - processor is shutdown',
      42              :         );
      43              :       }
      44            5 :       print('SimpleSpanProcessor: Skipping export - processor is shutdown');
      45              :       return;
      46              :     }
      47              : 
      48              :     // Verify the span has a valid end time
      49           18 :     if (span.endTime == null) {
      50            1 :       if (OTelLog.isWarn()) {
      51            1 :         OTelLog.warn(
      52            4 :           'SimpleSpanProcessor: Span ${span.name} with ID ${span.spanContext.spanId} has no end time, which suggests it may not be properly ended',
      53              :         );
      54              :       }
      55              :       // Continue with export anyway
      56              :     }
      57              : 
      58           18 :     if (OTelLog.isDebug()) {
      59           18 :       OTelLog.debug(
      60           72 :         'SimpleSpanProcessor: Exporting span ${span.spanContext.spanId} with name ${span.name}',
      61              :       );
      62              :     }
      63              : 
      64              :     try {
      65              :       // Create a copy of the span list to avoid concurrent modification issues
      66           18 :       final spanToExport = [span];
      67           18 :       if (OTelLog.isDebug()) {
      68           18 :         OTelLog.debug('SimpleSpanProcessor: Created list of spans to export');
      69              :       }
      70              : 
      71           36 :       final pendingExport = _spanExporter.export(spanToExport);
      72           36 :       _pendingExports.add(pendingExport);
      73           18 :       if (OTelLog.isDebug()) {
      74           18 :         OTelLog.debug(
      75              :           'SimpleSpanProcessor: Added export to pending exports list',
      76              :         );
      77              :       }
      78              : 
      79              :       // Directly await the export for better reliability in tests
      80              :       try {
      81           18 :         if (OTelLog.isDebug()) {
      82           18 :           OTelLog.debug(
      83           36 :             'SimpleSpanProcessor: Awaiting export completion for span ${span.name}',
      84              :           );
      85              :         }
      86              :         await pendingExport;
      87           18 :         if (OTelLog.isDebug()) {
      88           18 :           OTelLog.debug(
      89           72 :             'SimpleSpanProcessor: Successfully exported span ${span.name} with ID ${span.spanContext.spanId}',
      90              :           );
      91              :         }
      92              :       } catch (e, stackTrace) {
      93            4 :         if (OTelLog.isError()) {
      94            4 :           OTelLog.error(
      95           12 :             'SimpleSpanProcessor: Export error while processing span ${span.spanContext.spanId}: $e',
      96              :           );
      97            8 :           OTelLog.error('Stack trace: $stackTrace');
      98              :         }
      99              :       } finally {
     100           36 :         _pendingExports.remove(pendingExport);
     101           18 :         if (OTelLog.isDebug()) {
     102           18 :           OTelLog.debug(
     103              :             'SimpleSpanProcessor: Removed export from pending list',
     104              :           );
     105              :         }
     106              :       }
     107              :     } catch (e, stackTrace) {
     108            1 :       if (OTelLog.isError()) {
     109            1 :         OTelLog.error(
     110            3 :           'SimpleSpanProcessor: Failed to start export for span ${span.spanContext.spanId}: $e',
     111              :         );
     112            2 :         OTelLog.error('Stack trace: $stackTrace');
     113              :       }
     114              :     }
     115              :   }
     116              : 
     117            3 :   @override
     118              :   Future<void> onNameUpdate(Span span, String newName) async {
     119              :     // Simple processor doesn't need to do anything for name updates
     120              :     // since it only processes spans when they end
     121            3 :     if (OTelLog.isDebug()) {
     122            3 :       OTelLog.debug(
     123            9 :         'SimpleSpanProcessor: Name updated for span ${span.spanContext.spanId} to $newName',
     124              :       );
     125              :     }
     126              :   }
     127              : 
     128           17 :   @override
     129              :   Future<void> shutdown() async {
     130           17 :     if (_isShutdown) {
     131           11 :       if (OTelLog.isDebug()) {
     132           11 :         OTelLog.debug('SimpleSpanProcessor: Already shut down');
     133              :       }
     134              :       return;
     135              :     }
     136              : 
     137           17 :     if (OTelLog.isDebug()) {
     138           17 :       OTelLog.debug(
     139           51 :         'SimpleSpanProcessor: Shutting down - waiting for ${_pendingExports.length} pending exports',
     140              :       );
     141              :     }
     142           17 :     _isShutdown = true;
     143              : 
     144              :     try {
     145           34 :       if (_pendingExports.isNotEmpty) {
     146            1 :         if (OTelLog.isDebug()) {
     147            1 :           OTelLog.debug(
     148            3 :             'SimpleSpanProcessor: Waiting for ${_pendingExports.length} pending exports to complete',
     149              :           );
     150              :         }
     151              :         try {
     152            2 :           await Future.wait(_pendingExports);
     153            1 :           if (OTelLog.isDebug()) {
     154            1 :             OTelLog.debug('SimpleSpanProcessor: All pending exports completed');
     155              :           }
     156              :         } catch (e) {
     157            0 :           if (OTelLog.isError()) {
     158            0 :             OTelLog.error(
     159            0 :               'SimpleSpanProcessor: Error waiting for pending exports: $e',
     160              :             );
     161              :           }
     162              :         }
     163              :       }
     164              : 
     165              :       try {
     166           17 :         if (OTelLog.isDebug()) {
     167           17 :           OTelLog.debug('SimpleSpanProcessor: Shutting down exporter');
     168              :         }
     169           34 :         await _spanExporter.shutdown();
     170           17 :         if (OTelLog.isDebug()) {
     171           17 :           OTelLog.debug('SimpleSpanProcessor: Exporter shutdown complete');
     172              :         }
     173              :       } catch (e) {
     174            2 :         if (OTelLog.isError()) {
     175            2 :           OTelLog.error(
     176            2 :             'SimpleSpanProcessor: Error shutting down exporter: $e',
     177              :           );
     178              :         }
     179              :       }
     180              : 
     181           17 :       if (OTelLog.isDebug()) {
     182           17 :         OTelLog.debug('SimpleSpanProcessor: Shutdown complete');
     183              :       }
     184              :     } catch (e, stackTrace) {
     185            0 :       if (OTelLog.isError()) {
     186            0 :         OTelLog.error('SimpleSpanProcessor: Error during shutdown: $e');
     187            0 :         OTelLog.error('Stack trace: $stackTrace');
     188              :       }
     189              :     }
     190              :   }
     191              : 
     192           17 :   @override
     193              :   Future<void> forceFlush() async {
     194           17 :     if (_isShutdown) {
     195            6 :       if (OTelLog.isDebug()) {
     196            6 :         OTelLog.debug(
     197              :           'SimpleSpanProcessor: Cannot force flush - processor is shut down',
     198              :         );
     199              :       }
     200              :       return;
     201              :     }
     202              : 
     203           16 :     if (OTelLog.isDebug()) {
     204           16 :       OTelLog.debug(
     205           48 :         'SimpleSpanProcessor: Force flushing - waiting for ${_pendingExports.length} pending exports',
     206              :       );
     207              :     }
     208              : 
     209              :     try {
     210           32 :       if (_pendingExports.isEmpty) {
     211           13 :         if (OTelLog.isDebug()) {
     212           13 :           OTelLog.debug('SimpleSpanProcessor: No pending exports to flush');
     213              :         }
     214              :         // If there are no pending exports, just force flush the exporter
     215           26 :         await _spanExporter.forceFlush();
     216              :       } else {
     217           10 :         if (OTelLog.isDebug()) {
     218           10 :           OTelLog.debug(
     219           30 :             'SimpleSpanProcessor: Waiting for ${_pendingExports.length} pending exports',
     220              :           );
     221              :         }
     222           20 :         await Future.wait(_pendingExports);
     223           10 :         if (OTelLog.isDebug()) {
     224           10 :           OTelLog.debug('SimpleSpanProcessor: All pending exports completed');
     225              :         }
     226              : 
     227              :         // Also force flush the exporter
     228           10 :         if (OTelLog.isDebug()) {
     229           10 :           OTelLog.debug('SimpleSpanProcessor: Force flushing exporter');
     230              :         }
     231           20 :         await _spanExporter.forceFlush();
     232              :       }
     233              : 
     234           16 :       if (OTelLog.isDebug()) {
     235           16 :         OTelLog.debug('SimpleSpanProcessor: Force flush complete');
     236              :       }
     237              :     } catch (e, stackTrace) {
     238            2 :       if (OTelLog.isError()) {
     239            4 :         OTelLog.error('SimpleSpanProcessor: Error during force flush: $e');
     240            4 :         OTelLog.error('Stack trace: $stackTrace');
     241              :       }
     242              :     }
     243              :   }
     244              : }
        

Generated by: LCOV version 2.0-1