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.4 % 108 102
Test Date: 2026-08-27 23:42:02 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           20 :   SimpleSpanProcessor(this._spanExporter);
      21              : 
      22           19 :   @override
      23              :   Future<void> onStart(Span span, Context? parentContext) async {
      24           19 :     if (OTelLog.isDebug()) {
      25           19 :       OTelLog.debug(
      26           95 :         'SimpleSpanProcessor: onStart called for span ${span.spanContext.spanId}, traceId: ${span.spanContext.traceId}',
      27              :       );
      28              :     }
      29              :   }
      30              : 
      31           19 :   @override
      32              :   Future<void> onEnd(Span span) async {
      33           19 :     if (OTelLog.isDebug()) {
      34           19 :       OTelLog.debug(
      35           76 :         'SimpleSpanProcessor: onEnd called for span ${span.name} with ID ${span.spanContext.spanId}',
      36              :       );
      37              :     }
      38           19 :     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              :     // Per the Trace SDK spec (Sampling), span exporters MUST receive
      49              :     // spans with the Sampled flag set and SHOULD NOT receive the ones
      50              :     // that do not (e.g. RECORD_ONLY spans record but are not exported).
      51           57 :     if (!span.spanContext.traceFlags.isSampled) {
      52            1 :       if (OTelLog.isDebug()) {
      53            1 :         OTelLog.debug(
      54            3 :           'SimpleSpanProcessor: Skipping export - span ${span.spanContext.spanId} is not sampled',
      55              :         );
      56              :       }
      57              :       return;
      58              :     }
      59              : 
      60              :     // Verify the span has a valid end time
      61           19 :     if (span.endTime == null) {
      62            1 :       if (OTelLog.isWarn()) {
      63            1 :         OTelLog.warn(
      64            4 :           'SimpleSpanProcessor: Span ${span.name} with ID ${span.spanContext.spanId} has no end time, which suggests it may not be properly ended',
      65              :         );
      66              :       }
      67              :       // Continue with export anyway
      68              :     }
      69              : 
      70           19 :     if (OTelLog.isDebug()) {
      71           19 :       OTelLog.debug(
      72           76 :         'SimpleSpanProcessor: Exporting span ${span.spanContext.spanId} with name ${span.name}',
      73              :       );
      74              :     }
      75              : 
      76              :     try {
      77              :       // Create a copy of the span list to avoid concurrent modification issues
      78           19 :       final spanToExport = [span];
      79           19 :       if (OTelLog.isDebug()) {
      80           19 :         OTelLog.debug('SimpleSpanProcessor: Created list of spans to export');
      81              :       }
      82              : 
      83           38 :       final pendingExport = _spanExporter.export(spanToExport);
      84           38 :       _pendingExports.add(pendingExport);
      85           19 :       if (OTelLog.isDebug()) {
      86           19 :         OTelLog.debug(
      87              :           'SimpleSpanProcessor: Added export to pending exports list',
      88              :         );
      89              :       }
      90              : 
      91              :       // Directly await the export for better reliability in tests
      92              :       try {
      93           19 :         if (OTelLog.isDebug()) {
      94           19 :           OTelLog.debug(
      95           38 :             'SimpleSpanProcessor: Awaiting export completion for span ${span.name}',
      96              :           );
      97              :         }
      98              :         await pendingExport;
      99           19 :         if (OTelLog.isDebug()) {
     100           19 :           OTelLog.debug(
     101           76 :             'SimpleSpanProcessor: Successfully exported span ${span.name} with ID ${span.spanContext.spanId}',
     102              :           );
     103              :         }
     104              :       } catch (e, stackTrace) {
     105            4 :         if (OTelLog.isError()) {
     106            4 :           OTelLog.error(
     107           12 :             'SimpleSpanProcessor: Export error while processing span ${span.spanContext.spanId}: $e',
     108              :           );
     109            8 :           OTelLog.error('Stack trace: $stackTrace');
     110              :         }
     111              :       } finally {
     112           38 :         _pendingExports.remove(pendingExport);
     113           19 :         if (OTelLog.isDebug()) {
     114           19 :           OTelLog.debug(
     115              :             'SimpleSpanProcessor: Removed export from pending list',
     116              :           );
     117              :         }
     118              :       }
     119              :     } catch (e, stackTrace) {
     120            1 :       if (OTelLog.isError()) {
     121            1 :         OTelLog.error(
     122            3 :           'SimpleSpanProcessor: Failed to start export for span ${span.spanContext.spanId}: $e',
     123              :         );
     124            2 :         OTelLog.error('Stack trace: $stackTrace');
     125              :       }
     126              :     }
     127              :   }
     128              : 
     129            3 :   @override
     130              :   Future<void> onNameUpdate(Span span, String newName) async {
     131              :     // Simple processor doesn't need to do anything for name updates
     132              :     // since it only processes spans when they end
     133            3 :     if (OTelLog.isDebug()) {
     134            3 :       OTelLog.debug(
     135            9 :         'SimpleSpanProcessor: Name updated for span ${span.spanContext.spanId} to $newName',
     136              :       );
     137              :     }
     138              :   }
     139              : 
     140           19 :   @override
     141              :   Future<void> shutdown() async {
     142           19 :     if (_isShutdown) {
     143           11 :       if (OTelLog.isDebug()) {
     144           11 :         OTelLog.debug('SimpleSpanProcessor: Already shut down');
     145              :       }
     146              :       return;
     147              :     }
     148              : 
     149           19 :     if (OTelLog.isDebug()) {
     150           19 :       OTelLog.debug(
     151           57 :         'SimpleSpanProcessor: Shutting down - waiting for ${_pendingExports.length} pending exports',
     152              :       );
     153              :     }
     154           19 :     _isShutdown = true;
     155              : 
     156              :     try {
     157           38 :       if (_pendingExports.isNotEmpty) {
     158            1 :         if (OTelLog.isDebug()) {
     159            1 :           OTelLog.debug(
     160            3 :             'SimpleSpanProcessor: Waiting for ${_pendingExports.length} pending exports to complete',
     161              :           );
     162              :         }
     163              :         try {
     164            2 :           await Future.wait(_pendingExports);
     165            1 :           if (OTelLog.isDebug()) {
     166            1 :             OTelLog.debug('SimpleSpanProcessor: All pending exports completed');
     167              :           }
     168              :         } catch (e) {
     169            0 :           if (OTelLog.isError()) {
     170            0 :             OTelLog.error(
     171            0 :               'SimpleSpanProcessor: Error waiting for pending exports: $e',
     172              :             );
     173              :           }
     174              :         }
     175              :       }
     176              : 
     177              :       try {
     178           19 :         if (OTelLog.isDebug()) {
     179           19 :           OTelLog.debug('SimpleSpanProcessor: Shutting down exporter');
     180              :         }
     181           38 :         await _spanExporter.shutdown();
     182           19 :         if (OTelLog.isDebug()) {
     183           19 :           OTelLog.debug('SimpleSpanProcessor: Exporter shutdown complete');
     184              :         }
     185              :       } catch (e) {
     186            2 :         if (OTelLog.isError()) {
     187            2 :           OTelLog.error(
     188            2 :             'SimpleSpanProcessor: Error shutting down exporter: $e',
     189              :           );
     190              :         }
     191              :       }
     192              : 
     193           19 :       if (OTelLog.isDebug()) {
     194           19 :         OTelLog.debug('SimpleSpanProcessor: Shutdown complete');
     195              :       }
     196              :     } catch (e, stackTrace) {
     197            0 :       if (OTelLog.isError()) {
     198            0 :         OTelLog.error('SimpleSpanProcessor: Error during shutdown: $e');
     199            0 :         OTelLog.error('Stack trace: $stackTrace');
     200              :       }
     201              :     }
     202              :   }
     203              : 
     204           18 :   @override
     205              :   Future<void> forceFlush() async {
     206           18 :     if (_isShutdown) {
     207            6 :       if (OTelLog.isDebug()) {
     208            6 :         OTelLog.debug(
     209              :           'SimpleSpanProcessor: Cannot force flush - processor is shut down',
     210              :         );
     211              :       }
     212              :       return;
     213              :     }
     214              : 
     215           17 :     if (OTelLog.isDebug()) {
     216           17 :       OTelLog.debug(
     217           51 :         'SimpleSpanProcessor: Force flushing - waiting for ${_pendingExports.length} pending exports',
     218              :       );
     219              :     }
     220              : 
     221              :     try {
     222           34 :       if (_pendingExports.isEmpty) {
     223           14 :         if (OTelLog.isDebug()) {
     224           14 :           OTelLog.debug('SimpleSpanProcessor: No pending exports to flush');
     225              :         }
     226              :         // If there are no pending exports, just force flush the exporter
     227           28 :         await _spanExporter.forceFlush();
     228              :       } else {
     229           11 :         if (OTelLog.isDebug()) {
     230           11 :           OTelLog.debug(
     231           33 :             'SimpleSpanProcessor: Waiting for ${_pendingExports.length} pending exports',
     232              :           );
     233              :         }
     234           22 :         await Future.wait(_pendingExports);
     235           11 :         if (OTelLog.isDebug()) {
     236           11 :           OTelLog.debug('SimpleSpanProcessor: All pending exports completed');
     237              :         }
     238              : 
     239              :         // Also force flush the exporter
     240           11 :         if (OTelLog.isDebug()) {
     241           11 :           OTelLog.debug('SimpleSpanProcessor: Force flushing exporter');
     242              :         }
     243           22 :         await _spanExporter.forceFlush();
     244              :       }
     245              : 
     246           17 :       if (OTelLog.isDebug()) {
     247           17 :         OTelLog.debug('SimpleSpanProcessor: Force flush complete');
     248              :       }
     249              :     } catch (e, stackTrace) {
     250            2 :       if (OTelLog.isError()) {
     251            4 :         OTelLog.error('SimpleSpanProcessor: Error during force flush: $e');
     252            4 :         OTelLog.error('Stack trace: $stackTrace');
     253              :       }
     254              :     }
     255              :   }
     256              : }
        

Generated by: LCOV version 2.0-1