LCOV - code coverage report
Current view: top level - src - otel.dart (source / functions) Coverage Total Hit
Test: lcov.info Lines: 71.4 % 287 205
Test Date: 2025-11-15 13:23:01 Functions: - 0 0

            Line data    Source code
       1              : // Licensed under the Apache License, Version 2.0
       2              : // Copyright 2025, Michael Bushe, All rights reserved.
       3              : 
       4              : import 'dart:typed_data';
       5              : 
       6              : import 'package:dartastic_opentelemetry/dartastic_opentelemetry.dart';
       7              : import 'package:dartastic_opentelemetry_api/dartastic_opentelemetry_api.dart';
       8              : import 'package:meta/meta.dart';
       9              : 
      10              : /// Main entry point for the OpenTelemetry SDK.
      11              : ///
      12              : /// The [OTel] class provides static methods for initializing the SDK and
      13              : /// creating OpenTelemetry objects such as Tracers, Spans, Meters, and other
      14              : /// components necessary for instrumentation.
      15              : ///
      16              : /// To use the SDK, you must first call [initialize] to set up the global
      17              : /// configuration and install the SDK implementation. After initialization,
      18              : /// you can use the various factory methods to create OpenTelemetry objects.
      19              : ///
      20              : /// Example usage:
      21              : /// ```dart
      22              : /// await OTel.initialize(
      23              : ///   serviceName: 'my-service',
      24              : ///   serviceVersion: '1.0.0',
      25              : ///   endpoint: 'https://otel-collector.example.com:4317',
      26              : /// );
      27              : ///
      28              : /// final tracer = OTel.tracer();
      29              : /// final span = tracer.startSpan('my-operation');
      30              : /// // ... perform work ...
      31              : /// span.end();
      32              : /// ```
      33              : ///
      34              : /// The tenant_id and the resources from platform resource detection are merged
      35              : /// with resource attributes with resource attributes taking priority.
      36              : /// The values must be valid Attribute types (String, bool, int, double, or
      37              : /// List\<String>, List\<bool>, List\<int> or List\<double>).
      38              : class OTel {
      39              :   static OTelSDKFactory? _otelFactory;
      40              :   static Sampler? _defaultSampler;
      41              : 
      42              :   /// Default resource for the SDK.
      43              :   ///
      44              :   /// This is set during initialization and used by tracer and meter providers
      45              :   /// that don't have a specific resource set.
      46              :   static Resource? defaultResource;
      47              : 
      48              :   /// API key for Dartastic.io backend, if used.
      49              :   static String? dartasticApiKey;
      50              : 
      51              :   /// Default service name used if none is provided.
      52              :   static const defaultServiceName = "@dart/dartastic_opentelemetry";
      53              : 
      54              :   /// Default OTEL endpoint
      55              :   static const defaultEndpoint = "http://localhost:4317";
      56              : 
      57              :   /// Default tracer name used if none is provided.
      58              :   static const String _defaultTracerName = 'dartastic';
      59              : 
      60              :   /// Default tracer name that can be customized.
      61            3 :   static String defaultTracerName = _defaultTracerName;
      62              : 
      63              :   /// Default tracer version.
      64              :   static String defaultTracerVersion = "1.0.0";
      65              : 
      66              :   /// Initializes the OpenTelemetry SDK with the specified configuration.
      67              :   ///
      68              :   /// This method must be called before any other OpenTelemetry operations.
      69              :   /// It sets up the global configuration and installs the SDK implementation.
      70              :   ///
      71              :   /// When OTelLog.debug is true or the environmental variable
      72              :   /// OTEL_CONSOLE_EXPORTER is set to true, a ConsoleExporter is added to the
      73              :   /// exports to print spans.
      74              :   ///
      75              :   /// @param endpoint The endpoint URL for the OpenTelemetry collector (default: http://localhost:4317)
      76              :   /// @param secure Whether to use TLS for the connection (default: true)
      77              :   /// @param serviceName Name that uniquely identifies the service (default: "@dart/dartastic_opentelemetry")
      78              :   /// @param serviceVersion Version of the service (defaults to the OTel spec version)
      79              :   /// @param tracerName Name of the default tracer (default: "dartastic")
      80              :   /// @param tracerVersion Version of the default tracer (default: null)
      81              :   /// @param resourceAttributes Additional attributes for the resource
      82              :   /// @param spanProcessor Custom span processor (default: BatchSpanProcessor with OtlpGrpcSpanExporter)
      83              :   /// @param sampler Sampling strategy to use (default: AlwaysOnSampler)
      84              :   /// @param spanKind Default span kind (default: SpanKind.server)
      85              :   /// @param metricExporter Custom metric exporter for metrics
      86              :   /// @param metricReader Custom metric reader for metrics
      87              :   /// @param enableMetrics Whether to enable metrics collection (default: true)
      88              :   /// @param dartasticApiKey API key for Dartastic.io backend
      89              :   /// @param tenantId Tenant ID for multi-tenant backends (required for Dartastic.io)
      90              :   /// @param detectPlatformResources Whether to detect platform resources (default: true)
      91              :   /// @param oTelFactoryCreationFunction Factory function for creating OTelSDKFactory instances
      92              :   /// @return A Future that completes when initialization is done
      93              :   /// @throws StateError if called more than once
      94              :   /// @throws ArgumentError if required parameters are invalid
      95           73 :   static Future<void> initialize({
      96              :     String? endpoint,
      97              :     bool? secure,
      98              :     String? serviceName,
      99              :     String? serviceVersion,
     100              :     String? tracerName,
     101              :     String? tracerVersion,
     102              :     Attributes? resourceAttributes,
     103              :     SpanProcessor? spanProcessor,
     104              :     Sampler sampler = const AlwaysOnSampler(),
     105              :     SpanKind spanKind = SpanKind.server,
     106              :     MetricExporter? metricExporter,
     107              :     MetricReader? metricReader,
     108              :     bool enableMetrics = true,
     109              :     String? dartasticApiKey,
     110              :     String? tenantId,
     111              :     bool detectPlatformResources = true,
     112              :     OTelFactoryCreationFunction? oTelFactoryCreationFunction =
     113              :         otelSDKFactoryFactoryFunction,
     114              :   }) async {
     115              :     // Apply environment variables only if parameters are not provided
     116              :     final envServiceName = serviceName == null
     117           26 :         ? OTelEnv.getServiceConfig()['serviceName'] as String?
     118              :         : null;
     119              :     final envServiceVersion = serviceVersion == null
     120          114 :         ? OTelEnv.getServiceConfig()['serviceVersion'] as String?
     121              :         : null;
     122              : 
     123              :     serviceName ??= envServiceName;
     124              :     serviceVersion ??= envServiceVersion;
     125              : 
     126              :     final otlpConfig = (endpoint == null || secure == null)
     127           73 :         ? OTelEnv.getOtlpConfig(signal: 'traces')
     128            0 :         : <String, dynamic>{};
     129              :     final envEndpoint =
     130           55 :         endpoint == null ? otlpConfig['endpoint'] as String? : null;
     131           73 :     final envInsecure = secure == null ? otlpConfig['insecure'] as bool? : null;
     132              : 
     133              :     endpoint ??= envEndpoint;
     134              :     if (secure == null) {
     135              :       if (envInsecure != null) {
     136              :         secure = !envInsecure;
     137              :       } else {
     138              :         secure = true;
     139              :       }
     140              :     }
     141              : 
     142              :     // Apply defaults if still null
     143              :     serviceName ??= defaultServiceName;
     144              :     serviceVersion ??= '1.0.0';
     145              :     endpoint ??= defaultEndpoint;
     146              :     // secure is guaranteed non-null from above
     147              : 
     148              :     // Log environment variable usage
     149           73 :     if (OTelLog.isDebug()) {
     150              :       if (envServiceName != null) {
     151            0 :         OTelLog.debug('Using service name from environment: $serviceName');
     152              :       }
     153              :       if (envServiceVersion != null) {
     154            0 :         OTelLog.debug(
     155            0 :             'Using service version from environment: $serviceVersion');
     156              :       }
     157              :       if (envEndpoint != null) {
     158            0 :         OTelLog.debug('Using endpoint from environment: $endpoint');
     159              :       }
     160              :       if (envInsecure != null) {
     161            0 :         OTelLog.debug('Using insecure setting from environment: $envInsecure');
     162              :       }
     163              :     }
     164              : 
     165              :     // Get otlpConfig for exporter creation later
     166           73 :     final otlpConfigForExporter = OTelEnv.getOtlpConfig(signal: 'traces');
     167              : 
     168              :     // Get resource attributes from environment and merge with provided ones
     169           73 :     final envResourceAttrs = OTelEnv.getResourceAttributes();
     170           73 :     if (envResourceAttrs.isNotEmpty) {
     171              :       if (resourceAttributes != null) {
     172              :         // Merge with provided attributes - provided ones take precedence
     173            0 :         final mergedAttrs = Map<String, Object>.from(envResourceAttrs);
     174            0 :         resourceAttributes.toList().forEach((attr) {
     175            0 :           mergedAttrs[attr.key] = attr.value;
     176              :         });
     177            0 :         resourceAttributes = OTel.attributesFromMap(mergedAttrs);
     178              :       } else {
     179            0 :         resourceAttributes = OTel.attributesFromMap(envResourceAttrs);
     180              :       }
     181              :     }
     182              :     if (OTelFactory.otelFactory != null) {
     183            1 :       throw StateError(
     184              :           'OTelAPI can only be initialized once. If you need multiple endpoints or service names or versions create a named TracerProvider');
     185              :     }
     186              : 
     187           73 :     if (endpoint.isEmpty) {
     188            1 :       throw ArgumentError(
     189              :           'endpoint must not be the empty string.'); //TODO validate url
     190              :     }
     191           73 :     if (serviceName.isEmpty) {
     192            1 :       throw ArgumentError('serviceName must not be the empty string.');
     193              :     }
     194           73 :     if (serviceVersion.isEmpty) {
     195            1 :       throw ArgumentError('serviceVersion must not be the empty string.');
     196              :     }
     197              :     final factoryFactory =
     198              :         oTelFactoryCreationFunction ?? otelSDKFactoryFactoryFunction;
     199              :     // Initialize with default sampler
     200              :     _defaultSampler = sampler;
     201              :     OTel.defaultTracerName = tracerName ?? _defaultTracerName;
     202              :     OTel.defaultTracerVersion = tracerVersion ?? defaultTracerVersion;
     203              :     OTel.dartasticApiKey = dartasticApiKey;
     204              :     // Initialize logging from environment variables if needed
     205           73 :     initializeLogging();
     206              : 
     207           73 :     OTelFactory.otelFactory = factoryFactory(
     208              :         apiEndpoint: endpoint,
     209              :         apiServiceName: serviceName,
     210              :         apiServiceVersion: serviceVersion);
     211              : 
     212           73 :     if (OTelLog.isDebug()) {
     213           73 :       OTelLog.debug(
     214           73 :           'OTel initialized with endpoint: $endpoint, service: $serviceName');
     215              :     }
     216              : 
     217           73 :     final serviceResourceAttributes = {
     218              :       'service.name': serviceName,
     219              :       'service.version': serviceVersion,
     220              :     };
     221              :     // Create initial resource with service attributes
     222              :     var baseResource =
     223          146 :         OTel.resource(OTel.attributesFromMap(serviceResourceAttributes));
     224              : 
     225              :     if (tenantId != null) {
     226              :       // Create a separate tenant_id resource to ensure it's preserved
     227              :       final tenantResource =
     228            0 :           OTel.resource(OTel.attributesFromMap({'tenant_id': tenantId}));
     229            0 :       if (OTelLog.isDebug()) {
     230            0 :         OTelLog.debug(
     231            0 :             'OTel.initialize: Creating tenant_id resource with: $tenantId');
     232              :       }
     233              :       // Merge tenant into the base resource
     234            0 :       baseResource = baseResource.merge(tenantResource);
     235              :     }
     236              : 
     237              :     // Initialize with tenant-aware resource
     238              :     var mergedResource = baseResource;
     239              :     if (detectPlatformResources) {
     240           45 :       final resourceDetector = PlatformResourceDetector.create();
     241           45 :       final platformResource = await resourceDetector.detect();
     242              :       // Merge platform resource with our service resource - our service resource takes precedence
     243           45 :       mergedResource = platformResource.merge(mergedResource);
     244              : 
     245           45 :       if (OTelLog.isDebug()) {
     246           45 :         OTelLog.debug('Resource after platform merge:');
     247          180 :         mergedResource.attributes.toList().forEach((attr) {
     248          180 :           if (attr.key == 'tenant_id' || attr.key == 'service.name') {
     249          180 :             OTelLog.debug('  ${attr.key}: ${attr.value}');
     250              :           }
     251              :         });
     252              :       }
     253              :     }
     254              :     if (resourceAttributes != null) {
     255            0 :       final initResources = OTel.resource(resourceAttributes);
     256              :       // Merge user-provided attributes - they have highest priority
     257            0 :       mergedResource = mergedResource.merge(initResources);
     258              : 
     259            0 :       if (OTelLog.isDebug()) {
     260            0 :         OTelLog.debug('Resource after user attributes merge:');
     261            0 :         mergedResource.attributes.toList().forEach((attr) {
     262            0 :           if (attr.key == 'tenant_id' || attr.key == 'service.name') {
     263            0 :             OTelLog.debug('  ${attr.key}: ${attr.value}');
     264              :           }
     265              :         });
     266              :       }
     267              :     }
     268              :     // Set the final merged resource as default
     269              :     OTel.defaultResource = mergedResource;
     270              : 
     271           73 :     if (OTelLog.isDebug()) {
     272              :       // Final check to ensure tenant_id is preserved
     273              :       if (tenantId != null && OTel.defaultResource != null) {
     274              :         bool hasTenantId = false;
     275            0 :         OTel.defaultResource!.attributes.toList().forEach((attr) {
     276            0 :           if (attr.key == 'tenant_id') {
     277              :             hasTenantId = true;
     278            0 :             if (OTelLog.isDebug()) {
     279            0 :               OTelLog.debug(
     280            0 :                   'Final resource check - tenant_id is present: ${attr.value}');
     281              :             }
     282              :           }
     283              :         });
     284              : 
     285              :         if (!hasTenantId) {
     286              :           // As a last resort, add the tenant_id directly
     287            0 :           if (OTelLog.isDebug()) {
     288            0 :             OTelLog.debug('tenant_id was missing - adding it as fallback');
     289              :           }
     290              :           final tenantResource =
     291            0 :               OTel.resource(OTel.attributesFromMap({'tenant_id': tenantId}));
     292            0 :           OTel.defaultResource = OTel.defaultResource!.merge(tenantResource);
     293              :         }
     294              :       }
     295              :     }
     296              : 
     297              :     if (spanProcessor == null) {
     298              :       // Determine which exporter to create based on environment or defaults
     299           72 :       final exporterType = OTelEnv.getExporter(signal: 'traces') ?? 'otlp';
     300              : 
     301           72 :       if (exporterType != 'none') {
     302              :         // Determine protocol - default to http/protobuf if not set
     303              :         final protocol =
     304           72 :             otlpConfigForExporter['protocol'] as String? ?? 'http/protobuf';
     305              : 
     306              :         SpanExporter exporter;
     307           72 :         if (exporterType == 'console') {
     308            0 :           exporter = ConsoleExporter();
     309           72 :         } else if (exporterType == 'otlp') {
     310              :           // Create appropriate exporter based on protocol
     311           72 :           if (protocol == 'grpc') {
     312            0 :             exporter = OtlpGrpcSpanExporter(
     313            0 :               OtlpGrpcExporterConfig(
     314              :                 endpoint: endpoint,
     315              :                 insecure: !secure,
     316              :                 headers:
     317            0 :                     otlpConfigForExporter['headers'] as Map<String, String>? ??
     318            0 :                         {},
     319            0 :                 timeout: otlpConfigForExporter['timeout'] as Duration? ??
     320              :                     const Duration(seconds: 10),
     321            0 :                 compression: otlpConfigForExporter['compression'] == 'gzip',
     322            0 :                 certificate: otlpConfigForExporter['certificate'] as String?,
     323            0 :                 clientKey: otlpConfigForExporter['clientKey'] as String?,
     324              :                 clientCertificate:
     325            0 :                     otlpConfigForExporter['clientCertificate'] as String?,
     326              :               ),
     327              :             );
     328              :           } else {
     329              :             // Default to http/protobuf
     330              :             // For HTTP, adjust endpoint if it's the gRPC default
     331              :             String httpEndpoint = endpoint;
     332           72 :             if (endpoint == defaultEndpoint) {
     333              :               httpEndpoint = 'http://localhost:4318';
     334              :             }
     335           72 :             exporter = OtlpHttpSpanExporter(
     336           72 :               OtlpHttpExporterConfig(
     337              :                 endpoint: httpEndpoint,
     338              :                 headers:
     339           72 :                     otlpConfigForExporter['headers'] as Map<String, String>? ??
     340           72 :                         {},
     341           72 :                 timeout: otlpConfigForExporter['timeout'] as Duration? ??
     342              :                     const Duration(seconds: 10),
     343          144 :                 compression: otlpConfigForExporter['compression'] == 'gzip',
     344           72 :                 certificate: otlpConfigForExporter['certificate'] as String?,
     345           72 :                 clientKey: otlpConfigForExporter['clientKey'] as String?,
     346              :                 clientCertificate:
     347           72 :                     otlpConfigForExporter['clientCertificate'] as String?,
     348              :               ),
     349              :             );
     350              :           }
     351              :         } else {
     352              :           // Fallback to gRPC for backward compatibility
     353            0 :           exporter = OtlpGrpcSpanExporter(
     354            0 :             OtlpGrpcExporterConfig(
     355              :               endpoint: endpoint,
     356              :               insecure: !secure,
     357              :             ),
     358              :           );
     359              :         }
     360              : 
     361              :         // Only add ConsoleExporter in debug mode or if explicitly requested
     362           72 :         final exporters = <SpanExporter>[exporter];
     363           72 :         if (OTelLog.isDebug() ||
     364              :             const bool.fromEnvironment('OTEL_CONSOLE_EXPORTER',
     365              :                 defaultValue: false)) {
     366          144 :           exporters.add(ConsoleExporter());
     367              :         }
     368              : 
     369           72 :         spanProcessor = BatchSpanProcessor(
     370          216 :           exporters.length == 1 ? exporter : CompositeExporter(exporters),
     371              :           const BatchSpanProcessorConfig(
     372              :             maxQueueSize: 2048,
     373              :             scheduleDelay: Duration(seconds: 1),
     374              :             maxExportBatchSize: 512,
     375              :           ),
     376              :         );
     377              :       }
     378              :       // If exporterType == 'none', spanProcessor remains null and no processor is added
     379              :     }
     380              : 
     381              :     // Create and configure TracerProvider
     382              :     if (spanProcessor != null) {
     383          146 :       OTel.tracerProvider().addSpanProcessor(spanProcessor);
     384              :     }
     385              : 
     386              :     // Configure metrics if enabled
     387              :     if (enableMetrics) {
     388              :       // If no explicit metric exporter is provided, create one with the same endpoint
     389              :       if (metricExporter == null && metricReader == null) {
     390           58 :         MetricsConfiguration.configureMeterProvider(
     391              :           endpoint: endpoint,
     392              :           secure: secure,
     393              :           resource: OTel.defaultResource,
     394              :         );
     395              :       } else {
     396              :         // Use the provided exporter and/or reader
     397           12 :         MetricsConfiguration.configureMeterProvider(
     398              :           endpoint: endpoint,
     399              :           secure: secure,
     400              :           metricExporter: metricExporter,
     401              :           metricReader: metricReader,
     402              :           resource: OTel.defaultResource,
     403              :         );
     404              :       }
     405              :     }
     406              :   }
     407              : 
     408              :   /// Creates a Resource with the specified attributes and schema URL.
     409              :   ///
     410              :   /// Resources represent the entity producing telemetry, such as a service,
     411              :   /// process, or device. They are a collection of attributes that provide
     412              :   /// identifying information about the entity.
     413              :   ///
     414              :   /// @param attributes Attributes describing the resource
     415              :   /// @param schemaUrl Optional URL of the schema defining the attributes
     416              :   /// @return A new Resource instance
     417           73 :   static Resource resource(Attributes? attributes, [String? schemaUrl]) {
     418           73 :     _getAndCacheOtelFactory();
     419              :     return (_otelFactory as OTelSDKFactory)
     420           73 :         .resource(attributes ?? OTel.attributes(), schemaUrl);
     421              :   }
     422              : 
     423              :   /// Creates a new ContextKey with the given name.
     424              :   ///
     425              :   /// Context keys are used to store and retrieve values in a Context.
     426              :   /// Each instance will be unique, even with the same name, per the OTel spec.
     427              :   /// The name is for debugging purposes only.
     428              :   ///
     429              :   /// @param name The name of the context key (for debugging only)
     430              :   /// @return A new ContextKey instance
     431            1 :   static ContextKey<T> contextKey<T>(String name) {
     432            1 :     _getAndCacheOtelFactory();
     433            2 :     return _otelFactory!.contextKey(name, ContextKey.generateContextKeyId());
     434              :   }
     435              : 
     436              :   /// Creates a new Context with optional Baggage and SpanContext.
     437              :   ///
     438              :   /// Contexts are used to propagate information across the execution path,
     439              :   /// such as trace context, baggage, and other cross-cutting concerns.
     440              :   ///
     441              :   /// @param baggage Optional baggage to include in the context
     442              :   /// @param spanContext Optional span context to include in the context
     443              :   /// @return A new Context instance
     444           11 :   static Context context({Baggage? baggage, SpanContext? spanContext}) {
     445           11 :     _getAndCacheOtelFactory();
     446           11 :     var context = OTelFactory.otelFactory!.context(baggage: baggage);
     447              :     if (spanContext != null) {
     448            1 :       context = context.copyWithSpanContext(spanContext);
     449              :     }
     450              :     return context;
     451              :   }
     452              : 
     453              :   /// Gets a TracerProvider for creating Tracers.
     454              :   ///
     455              :   /// If name is null, this returns the global default TracerProvider, which shares
     456              :   /// the endpoint, serviceName, serviceVersion, sampler and resource set in initialize().
     457              :   /// If the name is not null, it returns a TracerProvider for the name that was added
     458              :   /// with addTracerProvider.
     459              :   ///
     460              :   /// The endpoint, serviceName, serviceVersion, sampler and resource set flow down
     461              :   /// to the [Tracer]s created by the TracerProvider and the [Span]
     462              :   /// created by those tracers
     463              :   /// @param name Optional name of a specific TracerProvider
     464              :   /// @return The TracerProvider instance
     465           73 :   static TracerProvider tracerProvider({String? name}) {
     466           73 :     final tracerProvider = OTelAPI.tracerProvider(name) as TracerProvider;
     467              :     // Ensure the resource is properly set
     468           73 :     if (tracerProvider.resource == null && defaultResource != null) {
     469           73 :       tracerProvider.resource = defaultResource;
     470           73 :       if (OTelLog.isDebug()) {
     471           73 :         OTelLog.debug('OTel.tracerProvider: Setting resource from default');
     472              :         if (defaultResource != null) {
     473          292 :           defaultResource!.attributes.toList().forEach((attr) {
     474          292 :             if (attr.key == 'tenant_id' || attr.key == 'service.name') {
     475          292 :               OTelLog.debug('  ${attr.key}: ${attr.value}');
     476              :             }
     477              :           });
     478              :         }
     479              :       }
     480              :     }
     481              : 
     482           73 :     tracerProvider.sampler ??= _defaultSampler;
     483              :     return tracerProvider;
     484              :   }
     485              : 
     486              :   /// Gets a MeterProvider for creating Meters.
     487              :   ///
     488              :   /// If name is null, this returns the global default MeterProvider, which shares
     489              :   /// the endpoint, serviceName, serviceVersion and resource set in initialize().
     490              :   /// If the name is not null, it returns a MeterProvider for the name that was added
     491              :   /// with addMeterProvider.
     492              :   ///
     493              :   /// @param name Optional name of a specific MeterProvider
     494              :   /// @return The MeterProvider instance
     495           69 :   static MeterProvider meterProvider({String? name}) {
     496           69 :     final meterProvider = OTelAPI.meterProvider(name) as MeterProvider;
     497           69 :     meterProvider.resource ??= defaultResource;
     498              :     return meterProvider;
     499              :   }
     500              : 
     501              :   /// Adds or replaces a named TracerProvider.
     502              :   ///
     503              :   /// This allows for creating multiple TracerProviders with different configurations,
     504              :   /// which can be useful for sending telemetry to different backends or with different
     505              :   /// settings.
     506              :   ///
     507              :   /// @param name The name of the TracerProvider
     508              :   /// @param endpoint Optional custom endpoint URL
     509              :   /// @param serviceName Optional custom service name
     510              :   /// @param serviceVersion Optional custom service version
     511              :   /// @param resource Optional custom resource
     512              :   /// @param sampler Optional custom sampler
     513              :   /// @return The newly created or replaced TracerProvider
     514            5 :   static TracerProvider addTracerProvider(
     515              :     String name, {
     516              :     String? endpoint,
     517              :     String? serviceName,
     518              :     String? serviceVersion,
     519              :     Resource? resource,
     520              :     Sampler? sampler,
     521              :   }) {
     522            5 :     final sdkTracerProvider = OTelAPI.addTracerProvider(name) as TracerProvider;
     523            5 :     sdkTracerProvider.resource = resource ?? defaultResource;
     524            5 :     sdkTracerProvider.sampler = sampler ?? _defaultSampler;
     525              :     return sdkTracerProvider;
     526              :   }
     527              : 
     528              :   /// @return the [TracerProvider]s, the global default and named ones.
     529           73 :   static List<APITracerProvider> tracerProviders() {
     530           73 :     return OTelAPI.tracerProviders();
     531              :   }
     532              : 
     533              :   /// Gets the default Tracer from the default TracerProvider.
     534              :   ///
     535              :   /// This is a convenience method for getting a Tracer with the default configuration.
     536              :   /// The endpoint, serviceName, serviceVersion, sampler and resource all flow down
     537              :   /// from the OTel defaults set during initialization.
     538              :   ///
     539              :   /// @return The default Tracer instance
     540            3 :   static Tracer tracer() {
     541            6 :     return tracerProvider().getTracer(
     542            3 :       defaultTracerName,
     543              :       version: defaultTracerVersion,
     544              :     );
     545              :   }
     546              : 
     547              :   /// Adds or replaces a named MeterProvider.
     548              :   ///
     549              :   /// This allows for creating multiple MeterProviders with different configurations,
     550              :   /// which can be useful for sending metrics to different backends or with different
     551              :   /// settings.
     552              :   ///
     553              :   /// @param name The name of the MeterProvider
     554              :   /// @param endpoint Optional custom endpoint URL
     555              :   /// @param serviceName Optional custom service name
     556              :   /// @param serviceVersion Optional custom service version
     557              :   /// @param resource Optional custom resource
     558              :   /// @return The newly created or replaced MeterProvider
     559            1 :   static MeterProvider addMeterProvider(
     560              :     String name, {
     561              :     String? endpoint,
     562              :     String? serviceName,
     563              :     String? serviceVersion,
     564              :     Resource? resource,
     565              :   }) {
     566            1 :     _getAndCacheOtelFactory();
     567            1 :     final mp = _otelFactory!.addMeterProvider(name,
     568              :         endpoint: endpoint,
     569              :         serviceName: serviceName,
     570              :         serviceVersion: serviceVersion) as MeterProvider;
     571            1 :     mp.resource = resource ?? defaultResource;
     572              :     return mp;
     573              :   }
     574              : 
     575              :   /// @return the [MeterProvider]s, the global default and named ones.
     576           73 :   static List<APIMeterProvider> meterProviders() {
     577           73 :     return OTelAPI.meterProviders();
     578              :   }
     579              : 
     580              :   /// Gets the default Meter from the default MeterProvider.
     581              :   ///
     582              :   /// This is a convenience method for getting a Meter with the default configuration.
     583              :   /// The endpoint, serviceName, serviceVersion and resource all flow down from
     584              :   /// the OTel defaults set during initialization.
     585              :   ///
     586              :   /// @param name Optional custom name for the meter (defaults to defaultTracerName)
     587              :   /// @return The default Meter instance
     588           12 :   static Meter meter([String? name]) {
     589           24 :     return meterProvider().getMeter(
     590            1 :         name: name ?? defaultTracerName,
     591              :         version: defaultTracerVersion) as Meter;
     592              :   }
     593              : 
     594              :   /// Creates a SpanContext with the specified parameters.
     595              :   ///
     596              :   /// A SpanContext represents the portion of a span that must be propagated
     597              :   /// to descendant spans and across process boundaries. It contains the
     598              :   /// traceId, spanId, traceFlags, and traceState.
     599              :   ///
     600              :   /// @param traceId The trace ID (defaults to a new random ID)
     601              :   /// @param spanId The span ID (defaults to a new random ID)
     602              :   /// @param parentSpanId The parent span ID (defaults to an invalid span ID)
     603              :   /// @param traceFlags Trace flags (defaults to NONE_FLAG)
     604              :   /// @param traceState Trace state
     605              :   /// @param isRemote Whether this context was received from a remote source
     606              :   /// @return A new SpanContext instance
     607           35 :   static SpanContext spanContext(
     608              :       {TraceId? traceId,
     609              :       SpanId? spanId,
     610              :       SpanId? parentSpanId,
     611              :       TraceFlags? traceFlags,
     612              :       TraceState? traceState,
     613              :       bool? isRemote}) {
     614           35 :     return OTelAPI.spanContext(
     615            2 :       traceId: traceId ?? OTel.traceId(),
     616            2 :       spanId: spanId ?? OTel.spanId(),
     617           13 :       parentSpanId: parentSpanId ?? spanIdInvalid(),
     618            9 :       traceFlags: traceFlags ?? OTelAPI.traceFlags(),
     619              :       traceState: traceState,
     620              :       isRemote: isRemote,
     621              :     );
     622              :   }
     623              : 
     624              :   /// Creates a child SpanContext from a parent context.
     625              :   ///
     626              :   /// This creates a new SpanContext that shares the same traceId as the parent,
     627              :   /// but has a new spanId and the parentSpanId set to the parent's spanId.
     628              :   ///
     629              :   /// @param parent The parent SpanContext
     630              :   /// @return A new child SpanContext
     631            1 :   static SpanContext spanContextFromParent(SpanContext parent) {
     632            1 :     _getAndCacheOtelFactory();
     633            1 :     return OTelFactory.otelFactory!.spanContextFromParent(parent);
     634              :   }
     635              : 
     636              :   /// Creates an invalid SpanContext (all zeros).
     637              :   ///
     638              :   /// An invalid SpanContext represents the absence of a trace context.
     639              :   ///
     640              :   /// @return An invalid SpanContext instance
     641            1 :   static SpanContext spanContextInvalid() {
     642            1 :     _getAndCacheOtelFactory();
     643            1 :     return OTelFactory.otelFactory!.spanContextInvalid();
     644              :   }
     645              : 
     646              :   /// Creates a SpanEvent with the current timestamp.
     647              :   ///
     648              :   /// Note: Per [OTEP 0265](https://opentelemetry.io/docs/specs/semconv/general/events/),
     649              :   /// span events are being deprecated and will be replaced by the Logging API in future versions.
     650              :   ///
     651              :   /// @param name The name of the event
     652              :   /// @param attributes Attributes to associate with the event
     653              :   /// @return A new SpanEvent instance with the current timestamp
     654            0 :   static SpanEvent spanEventNow(String name, Attributes attributes) {
     655            0 :     _getAndCacheOtelFactory();
     656            0 :     return spanEvent(name, attributes, DateTime.now());
     657              :   }
     658              : 
     659              :   /// Creates a SpanEvent with the specified parameters.
     660              :   ///
     661              :   /// Note: Per [OTEP 0265](https://opentelemetry.io/docs/specs/semconv/general/events/),
     662              :   /// span events are being deprecated and will be replaced by the Logging API in future versions.
     663              :   ///
     664              :   /// @param name The name of the event
     665              :   /// @param attributes Optional attributes to associate with the event
     666              :   /// @param timestamp Optional timestamp for the event (defaults to null)
     667              :   /// @return A new SpanEvent instance
     668            2 :   static SpanEvent spanEvent(String name,
     669              :       [Attributes? attributes, DateTime? timestamp]) {
     670            2 :     _getAndCacheOtelFactory();
     671            2 :     return _otelFactory!.spanEvent(name, attributes, timestamp);
     672              :   }
     673              : 
     674              :   /// Creates a Baggage with key-value pairs.
     675              :   ///
     676              :   /// Baggage is a set of key-value pairs that can be propagated across service boundaries
     677              :   /// along with the trace context. It can be used to add contextual information to traces.
     678              :   ///
     679              :   /// @param keyValuePairs A map of key-value pairs to include in the baggage
     680              :   /// @return A new Baggage instance
     681            0 :   static Baggage baggageForMap(Map<String, String> keyValuePairs) {
     682            0 :     _getAndCacheOtelFactory();
     683            0 :     return _otelFactory!.baggageForMap(keyValuePairs);
     684              :   }
     685              : 
     686              :   /// Creates a BaggageEntry with the specified value and optional metadata.
     687              :   ///
     688              :   /// @param value The value of the baggage entry
     689              :   /// @param metadata Optional metadata for the baggage entry
     690              :   /// @return A new BaggageEntry instance
     691            2 :   static BaggageEntry baggageEntry(String value, [String? metadata]) {
     692            2 :     _getAndCacheOtelFactory();
     693            2 :     return _otelFactory!.baggageEntry(value, metadata);
     694              :   }
     695              : 
     696              :   /// Creates a Baggage with the specified entries.
     697              :   ///
     698              :   /// @param entries Optional map of baggage entries
     699              :   /// @return A new Baggage instance
     700            2 :   static Baggage baggage([Map<String, BaggageEntry>? entries]) {
     701            2 :     _getAndCacheOtelFactory();
     702            2 :     return _otelFactory!.baggage(entries);
     703              :   }
     704              : 
     705              :   /// Creates a Baggage instance from a JSON representation.
     706              :   ///
     707              :   /// @param json JSON representation of a baggage
     708              :   /// @return A new Baggage instance
     709            0 :   static Baggage baggageFromJson(Map<String, dynamic> json) {
     710            0 :     return OTelAPI.baggageFromJson(json);
     711              :   }
     712              : 
     713              :   /// Creates a string attribute.
     714              :   ///
     715              :   /// @param name The name of the attribute
     716              :   /// @param value The string value of the attribute
     717              :   /// @return A new Attribute instance
     718            2 :   static Attribute<String> attributeString(String name, String value) {
     719            2 :     _getAndCacheOtelFactory();
     720            2 :     return _otelFactory!.attributeString(name, value);
     721              :   }
     722              : 
     723              :   /// Creates a boolean attribute.
     724              :   ///
     725              :   /// @param name The name of the attribute
     726              :   /// @param value The boolean value of the attribute
     727              :   /// @return A new Attribute instance
     728            1 :   static Attribute<bool> attributeBool(String name, bool value) {
     729            1 :     _getAndCacheOtelFactory();
     730            1 :     return _otelFactory!.attributeBool(name, value);
     731              :   }
     732              : 
     733              :   /// Creates an integer attribute.
     734              :   ///
     735              :   /// @param name The name of the attribute
     736              :   /// @param value The integer value of the attribute
     737              :   /// @return A new Attribute instance
     738            2 :   static Attribute<int> attributeInt(String name, int value) {
     739            2 :     _getAndCacheOtelFactory();
     740            2 :     return _otelFactory!.attributeInt(name, value);
     741              :   }
     742              : 
     743              :   /// Creates a double attribute.
     744              :   ///
     745              :   /// @param name The name of the attribute
     746              :   /// @param value The double value of the attribute
     747              :   /// @return A new Attribute instance
     748            1 :   static Attribute<double> attributeDouble(String name, double value) {
     749            1 :     _getAndCacheOtelFactory();
     750            1 :     return _otelFactory!.attributeDouble(name, value);
     751              :   }
     752              : 
     753              :   /// Creates a string list attribute.
     754              :   ///
     755              :   /// @param name The name of the attribute
     756              :   /// @param value The list of string values
     757              :   /// @return A new Attribute instance
     758            0 :   static Attribute<List<String>> attributeStringList(
     759              :       String name, List<String> value) {
     760            0 :     _getAndCacheOtelFactory();
     761            0 :     return _otelFactory!.attributeStringList(name, value);
     762              :   }
     763              : 
     764              :   /// Creates a boolean list attribute.
     765              :   ///
     766              :   /// @param name The name of the attribute
     767              :   /// @param value The list of boolean values
     768              :   /// @return A new Attribute instance
     769            0 :   static Attribute<List<bool>> attributeBoolList(
     770              :       String name, List<bool> value) {
     771            0 :     _getAndCacheOtelFactory();
     772            0 :     return _otelFactory!.attributeBoolList(name, value);
     773              :   }
     774              : 
     775              :   /// Creates an integer list attribute.
     776              :   ///
     777              :   /// @param name The name of the attribute
     778              :   /// @param value The list of integer values
     779              :   /// @return A new Attribute instance
     780            0 :   static Attribute<List<int>> attributeIntList(String name, List<int> value) {
     781            0 :     _getAndCacheOtelFactory();
     782            0 :     return _otelFactory!.attributeIntList(name, value);
     783              :   }
     784              : 
     785              :   /// Creates a double list attribute.
     786              :   ///
     787              :   /// @param name The name of the attribute
     788              :   /// @param value The list of double values
     789              :   /// @return A new Attribute instance
     790            0 :   static Attribute<List<double>> attributeDoubleList(
     791              :       String name, List<double> value) {
     792            0 :     _getAndCacheOtelFactory();
     793            0 :     return _otelFactory!.attributeDoubleList(name, value);
     794              :   }
     795              : 
     796              :   /// Creates an empty Attributes collection.
     797              :   ///
     798              :   /// @return A new empty Attributes collection
     799            0 :   static Attributes createAttributes() {
     800            0 :     _getAndCacheOtelFactory();
     801            0 :     return _otelFactory!.attributes();
     802              :   }
     803              : 
     804              :   /// Creates an Attributes collection from a list of Attribute objects.
     805              :   ///
     806              :   /// @param entries Optional list of Attribute objects
     807              :   /// @return A new Attributes collection
     808            8 :   static Attributes attributes([List<Attribute>? entries]) {
     809              :     // Cheating here since Attributes is unlikely to be overriden in a
     810              :     // factory and is often called before initialize
     811              :     return _otelFactory == null
     812            0 :         ? AttributesCreate.create(entries ?? [])
     813            8 :         : _otelFactory!.attributes(entries);
     814              :   }
     815              : 
     816              :   /// Creates an Attributes collection from a map of named values.
     817              :   ///
     818              :   /// String, bool, int, double, or Lists of those types get converted
     819              :   /// to the matching typed attribute. DateTime gets converted to a
     820              :   /// String attribute with the UTC time string.
     821              :   ///
     822              :   /// Unlike most methods, this does not create the OTelFactory if
     823              :   /// one does not exist, instead it uses the OTelAPI's attributesFromMap.
     824              :   ///
     825              :   /// Alternatively, consider using the toAttributes()
     826              :   /// extension on \<String, Map>{}.
     827              :   /// @param namedMap Map of attribute names to values
     828              :   /// @return A new Attributes collection
     829           73 :   static Attributes attributesFromMap(Map<String, Object> namedMap) {
     830              :     if (_otelFactory == null) {
     831           73 :       return OTelAPI.attributesFromMap(namedMap);
     832              :     } else {
     833           45 :       return _otelFactory!.attributesFromMap(namedMap);
     834              :     }
     835              :   }
     836              : 
     837              :   /// Creates an Attributes collection from a list of Attribute objects.
     838              :   ///
     839              :   /// @param attributeList List of Attribute objects
     840              :   /// @return A new Attributes collection
     841            1 :   static Attributes attributesFromList(List<Attribute> attributeList) {
     842            1 :     _getAndCacheOtelFactory();
     843            1 :     return _otelFactory!.attributesFromList(attributeList);
     844              :   }
     845              : 
     846              :   /// Creates a TraceState with the specified entries.
     847              :   ///
     848              :   /// TraceState carries vendor-specific trace identification data across systems.
     849              :   ///
     850              :   /// @param entries Optional map of key-value pairs for the trace state
     851              :   /// @return A new TraceState instance
     852            6 :   static TraceState traceState(Map<String, String>? entries) {
     853            6 :     _getAndCacheOtelFactory();
     854            6 :     return _otelFactory!.traceState(entries);
     855              :   }
     856              : 
     857              :   /// Creates TraceFlags with the specified flags.
     858              :   ///
     859              :   /// TraceFlags are used to encode bit field flags in the trace context.
     860              :   /// The most commonly used flag is SAMPLED_FLAG, which indicates
     861              :   /// that the trace should be sampled.
     862              :   ///
     863              :   /// @param flags Optional flags value (default: NONE_FLAG)
     864              :   /// @return A new TraceFlags instance
     865           32 :   static TraceFlags traceFlags([int? flags]) {
     866           32 :     _getAndCacheOtelFactory();
     867           32 :     return _otelFactory!.traceFlags(flags ?? TraceFlags.NONE_FLAG);
     868              :   }
     869              : 
     870              :   /// Generates a new random TraceId.
     871              :   ///
     872              :   /// @return A new random TraceId
     873           33 :   static TraceId traceId() {
     874           66 :     return traceIdOf(IdGenerator.generateTraceId());
     875              :   }
     876              : 
     877              :   /// Creates a TraceId from the specified bytes.
     878              :   ///
     879              :   /// @param traceId The bytes for the trace ID (must be exactly 16 bytes)
     880              :   /// @return A new TraceId instance
     881              :   /// @throws ArgumentError if traceId is not exactly 16 bytes
     882           34 :   static TraceId traceIdOf(Uint8List traceId) {
     883           34 :     _getAndCacheOtelFactory();
     884           68 :     if (traceId.length != TraceId.traceIdLength) {
     885            0 :       throw ArgumentError(
     886            0 :           'Trace ID must be exactly ${TraceId.traceIdLength} bytes, got ${traceId.length} bytes');
     887              :     }
     888           34 :     return OTelFactory.otelFactory!.traceId(traceId);
     889              :   }
     890              : 
     891              :   /// Creates a TraceId from a hex string.
     892              :   ///
     893              :   /// @param hexString Hexadecimal representation of the trace ID
     894              :   /// @return A new TraceId instance
     895            6 :   static TraceId traceIdFrom(String hexString) {
     896            6 :     return OTelAPI.traceIdFrom(hexString);
     897              :   }
     898              : 
     899              :   /// Creates an invalid TraceId (all zeros).
     900              :   ///
     901              :   /// @return An invalid TraceId instance
     902            1 :   static TraceId traceIdInvalid() {
     903            2 :     return traceIdOf(TraceId.invalidTraceIdBytes);
     904              :   }
     905              : 
     906              :   /// Generates a new random SpanId.
     907              :   ///
     908              :   /// @return A new random SpanId
     909           32 :   static SpanId spanId() {
     910           64 :     return spanIdOf(IdGenerator.generateSpanId());
     911              :   }
     912              : 
     913              :   /// Creates a SpanId from the specified bytes.
     914              :   ///
     915              :   /// @param spanId The bytes for the span ID (must be exactly 8 bytes)
     916              :   /// @return A new SpanId instance
     917              :   /// @throws ArgumentError if spanId is not exactly 8 bytes
     918           37 :   static SpanId spanIdOf(Uint8List spanId) {
     919           37 :     _getAndCacheOtelFactory();
     920           74 :     if (spanId.length != 8) {
     921            0 :       throw ArgumentError(
     922            0 :           'Span ID must be exactly 8 bytes, got ${spanId.length} bytes');
     923              :     }
     924           37 :     return _otelFactory!.spanId(spanId);
     925              :   }
     926              : 
     927              :   /// Creates a SpanId from a hex string.
     928              :   ///
     929              :   /// @param hexString Hexadecimal representation of the span ID
     930              :   /// @return A new SpanId instance
     931            6 :   static SpanId spanIdFrom(String hexString) {
     932            6 :     return OTelAPI.spanIdFrom(hexString);
     933              :   }
     934              : 
     935              :   /// Creates an invalid SpanId (all zeros).
     936              :   ///
     937              :   /// @return An invalid SpanId instance
     938           35 :   static SpanId spanIdInvalid() {
     939           70 :     return spanIdOf(SpanId.invalidSpanIdBytes);
     940              :   }
     941              : 
     942              :   /// Creates a SpanLink with the specified SpanContext and optional attributes.
     943              :   ///
     944              :   /// SpanLinks are used to associate spans that may be causally related
     945              :   /// but not via a parent-child relationship.
     946              :   ///
     947              :   /// @param spanContext The SpanContext to link to
     948              :   /// @param attributes Optional attributes to associate with the link
     949              :   /// @return A new SpanLink instance
     950            3 :   static SpanLink spanLink(SpanContext spanContext, {Attributes? attributes}) {
     951            3 :     _getAndCacheOtelFactory();
     952            3 :     return _otelFactory!.spanLink(spanContext, attributes: attributes);
     953              :   }
     954              : 
     955              :   /// Retrieves and caches the OTelFactory instance.
     956              :   ///
     957              :   /// @return The OTelFactory instance
     958              :   /// @throws StateError if initialize() has not been called
     959           73 :   static OTelFactory _getAndCacheOtelFactory() {
     960              :     if (_otelFactory != null) {
     961              :       return _otelFactory!;
     962              :     }
     963              :     if (OTelFactory.otelFactory == null) {
     964            0 :       throw StateError('initialize() must be called first.');
     965              :     }
     966              :     return _otelFactory = OTelFactory.otelFactory! as OTelSDKFactory;
     967              :   }
     968              : 
     969              :   /// Initializes logging based on environment variables.
     970              :   ///
     971              :   /// This can be called separately from initialize(), but initialize() will
     972              :   /// call it automatically if not already done.
     973           73 :   static void initializeLogging() {
     974              :     // Initialize log settings from environment variables
     975           73 :     OTelEnv.initializeLogging();
     976              : 
     977           73 :     if (OTelLog.isDebug()) {
     978           73 :       OTelLog.debug('OTel logging initialized');
     979              :     }
     980              :   }
     981              : 
     982              :   /// Flushes and shuts down trace and metric providers,
     983              :   /// processors and exporters.  Typically called from [OTel.shutdown]
     984           73 :   static Future<void> shutdown() async {
     985              :     // Shutdown any tracer providers to clean up span processors
     986              :     try {
     987           73 :       final tracerProviders = OTel.tracerProviders();
     988          145 :       for (final tracerProvider in tracerProviders) {
     989           72 :         if (OTelLog.isDebug()) {
     990           71 :           OTelLog.debug('OTel: Shutting down tracer providers');
     991              :         }
     992           72 :         if (tracerProvider is TracerProvider) {
     993              :           try {
     994           72 :             await tracerProvider.forceFlush();
     995           72 :             if (OTelLog.isDebug()) {
     996           71 :               OTelLog.debug('OTel: Tracer provider flush complete');
     997              :             }
     998              :           } catch (e) {
     999            0 :             if (OTelLog.isDebug()) {
    1000            0 :               OTelLog.debug('OTel: Error during tracer provider flush: $e');
    1001              :             }
    1002              :           }
    1003              :         }
    1004              :         try {
    1005           72 :           await tracerProvider.shutdown();
    1006           72 :           if (OTelLog.isDebug()) {
    1007           71 :             OTelLog.debug('OTel: Tracer provider shutdown complete');
    1008              :           }
    1009              :         } catch (e) {
    1010            0 :           if (OTelLog.isDebug()) {
    1011            0 :             OTelLog.debug('OTel: Error during tracer provider shutdown: $e');
    1012              :           }
    1013              :         }
    1014              :       }
    1015              :     } catch (e) {
    1016            0 :       if (OTelLog.isDebug()) {
    1017            0 :         OTelLog.debug('OTel: Error accessing tracer provider: $e');
    1018              :       }
    1019              :     }
    1020              : 
    1021              :     // Shutdown meter providers to clean up metric readers and exporters
    1022           73 :     final meterProviders = OTel.meterProviders();
    1023          141 :     for (var meterProvider in meterProviders) {
    1024              :       try {
    1025           68 :         if (OTelLog.isDebug()) {
    1026           67 :           OTelLog.debug('OTel: Shutting down meter provider');
    1027              :         }
    1028           68 :         await meterProvider.shutdown();
    1029           68 :         if (OTelLog.isDebug()) {
    1030           67 :           OTelLog.debug('OTel: Meter provider shutdown complete');
    1031              :         }
    1032              :       } catch (e) {
    1033            0 :         if (OTelLog.isDebug()) {
    1034            0 :           OTelLog.debug('OTel: Error during meter provider shutdown: $e');
    1035              :         }
    1036              :       }
    1037              :     }
    1038              :   }
    1039              : 
    1040              :   /// Resets the OTel state for testing purposes.
    1041              :   ///
    1042              :   /// This method should only be used in tests to reset the state between test runs.
    1043              :   /// It shuts down all tracer and meter providers and resets all static fields.
    1044              :   ///
    1045              :   /// @return A Future that completes when the reset is done
    1046           73 :   @visibleForTesting
    1047              :   static Future<void> reset() async {
    1048          144 :     if (OTelLog.isDebug()) OTelLog.debug('OTel: Resetting state');
    1049              : 
    1050           73 :     await shutdown();
    1051              : 
    1052              :     // Reset all static fields
    1053              :     _otelFactory = null;
    1054              :     _defaultSampler = null;
    1055              :     defaultResource = null;
    1056              :     dartasticApiKey = null;
    1057          144 :     if (OTelLog.isDebug()) OTelLog.debug('OTel: Reset static fields');
    1058              : 
    1059              :     // Reset API state
    1060              :     try {
    1061              :       // ignore: invalid_use_of_visible_for_testing_member
    1062           73 :       OTelAPI.reset();
    1063          144 :       if (OTelLog.isDebug()) OTelLog.debug('OTel: Reset OTelAPI');
    1064              :     } catch (e) {
    1065            0 :       if (OTelLog.isDebug()) OTelLog.debug('OTel: Error resetting OTelAPI: $e');
    1066              :     }
    1067              : 
    1068              :     // Reset OTelFactory
    1069              :     OTelFactory.otelFactory = null;
    1070          144 :     if (OTelLog.isDebug()) OTelLog.debug('OTel: Reset OTelFactory');
    1071              : 
    1072          144 :     if (OTelLog.isDebug()) OTelLog.debug('OTel: Cleared test environment');
    1073              : 
    1074              :     // Add a short delay to ensure resources are released
    1075           73 :     await Future<void>.delayed(const Duration(milliseconds: 250));
    1076          144 :     if (OTelLog.isDebug()) OTelLog.debug('OTel: Reset complete');
    1077              :   }
    1078              : 
    1079              :   /// Creates a new InstrumentationScope.
    1080              :   ///
    1081              :   /// [name] is required and represents the instrumentation scope name (e.g. 'io.opentelemetry.contrib.mongodb')
    1082              :   /// [version] is optional and specifies the version of the instrumentation scope, defaults to '1.0.0'
    1083              :   /// [schemaUrl] is optional and specifies the Schema URL
    1084              :   /// [attributes] is optional and specifies instrumentation scope attributes
    1085            1 :   static InstrumentationScope instrumentationScope(
    1086              :       {required String name,
    1087              :       String version = '1.0.0',
    1088              :       String? schemaUrl,
    1089              :       Attributes? attributes}) {
    1090            1 :     return OTelAPI.instrumentationScope(
    1091              :         name: name,
    1092              :         version: version,
    1093              :         schemaUrl: schemaUrl,
    1094              :         attributes: attributes);
    1095              :   }
    1096              : }
        

Generated by: LCOV version 2.0-1