LCOV - code coverage report
Current view: top level - src/trace/export/otlp/http - otlp_http_span_exporter_config.dart (source / functions) Coverage Total Hit
Test: lcov.info Lines: 68.6 % 51 35
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 '../certificate_utils.dart';
       5              : 
       6              : /// Configuration for the OpenTelemetry span exporter that exports spans using OTLP over HTTP/protobuf
       7              : class OtlpHttpExporterConfig {
       8              :   /// The endpoint to export spans to (e.g., 'http://localhost:4318/v1/traces')
       9              :   /// Default: 'http://localhost:4318'
      10              :   final String endpoint;
      11              : 
      12              :   /// Additional HTTP headers to include in the export requests
      13              :   final Map<String, String> headers;
      14              : 
      15              :   /// The timeout for export HTTP requests
      16              :   /// Default: 10 seconds
      17              :   final Duration timeout;
      18              : 
      19              :   /// Whether to use gzip compression for the HTTP body
      20              :   /// Default: false
      21              :   final bool compression;
      22              : 
      23              :   /// Maximum number of retries for failed export requests
      24              :   /// Default: 3
      25              :   final int maxRetries;
      26              : 
      27              :   /// Base delay for exponential backoff when retrying
      28              :   /// Default: 100 milliseconds
      29              :   final Duration baseDelay;
      30              : 
      31              :   /// Maximum delay for exponential backoff when retrying
      32              :   /// Default: 1 second
      33              :   final Duration maxDelay;
      34              : 
      35              :   /// Path to the TLS certificate file for secure connections.
      36              :   final String? certificate;
      37              : 
      38              :   /// Path to the client key file for secure connections with client authentication.
      39              :   final String? clientKey;
      40              : 
      41              :   /// Path to the client certificate file for secure connections with client authentication.
      42              :   final String? clientCertificate;
      43              : 
      44              :   /// Creates a new configuration for the OTLP HTTP span exporter
      45              :   ///
      46              :   /// The endpoint must be a valid URL and will default to http://localhost:4318
      47              :   /// if not specified. The path '/v1/traces' will be appended if not already present.
      48           72 :   OtlpHttpExporterConfig({
      49              :     String endpoint = 'http://localhost:4318',
      50              :     Map<String, String>? headers,
      51              :     Duration timeout = const Duration(seconds: 10),
      52              :     this.compression = false,
      53              :     int maxRetries = 3,
      54              :     Duration baseDelay = const Duration(milliseconds: 100),
      55              :     Duration maxDelay = const Duration(seconds: 1),
      56              :     this.certificate,
      57              :     this.clientKey,
      58              :     this.clientCertificate,
      59           72 :   })  : endpoint = _validateEndpoint(endpoint),
      60           72 :         headers = _validateHeaders(headers ?? {}),
      61           72 :         timeout = _validateTimeout(timeout),
      62           72 :         maxRetries = _validateRetries(maxRetries),
      63           72 :         baseDelay = _validateDelay(baseDelay, 'baseDelay'),
      64           72 :         maxDelay = _validateDelay(maxDelay, 'maxDelay') {
      65          144 :     if (baseDelay.compareTo(maxDelay) > 0) {
      66            0 :       throw ArgumentError('maxDelay cannot be less than baseDelay');
      67              :     }
      68          288 :     _validateCertificates(certificate, clientKey, clientCertificate);
      69              :   }
      70              : 
      71           72 :   static Map<String, String> _validateHeaders(Map<String, String> headers) {
      72           72 :     final normalized = <String, String>{};
      73           72 :     for (final entry in headers.entries) {
      74            0 :       if (entry.key.isEmpty || entry.value.isEmpty) {
      75            0 :         throw ArgumentError('Header keys and values cannot be empty');
      76              :       }
      77            0 :       normalized[entry.key.toLowerCase()] = entry.value;
      78              :     }
      79              :     return normalized;
      80              :   }
      81              : 
      82           72 :   static String _validateEndpoint(String endpoint) {
      83           72 :     if (endpoint.isEmpty) {
      84            0 :       throw ArgumentError('Endpoint cannot be empty');
      85              :     }
      86              : 
      87              :     // Handle common localhost variants and validate basic format
      88           72 :     endpoint = endpoint.trim();
      89              : 
      90              :     // First check for invalid formats
      91           72 :     if (endpoint.contains(' ')) {
      92            0 :       throw ArgumentError('Endpoint cannot contain spaces: $endpoint');
      93              :     }
      94              : 
      95              :     // Ensure endpoint starts with http:// or https://
      96           72 :     final lcEndpoint = endpoint.toLowerCase();
      97           72 :     if (!lcEndpoint.startsWith('http://') &&
      98            0 :         !lcEndpoint.startsWith('https://')) {
      99            0 :       endpoint = 'http://$endpoint';
     100              :     }
     101              : 
     102              :     // Default port for OTLP/HTTP is 4318
     103           72 :     if (lcEndpoint == 'http://localhost' ||
     104           72 :         lcEndpoint == 'http://127.0.0.1' ||
     105           72 :         lcEndpoint == 'https://localhost' ||
     106           72 :         lcEndpoint == 'https://127.0.0.1') {
     107            0 :       return '$endpoint:4318';
     108              :     }
     109              : 
     110              :     // Handle URL format validation
     111              :     try {
     112           72 :       final uri = Uri.parse(endpoint);
     113          144 :       if (uri.host.isEmpty) {
     114            0 :         throw ArgumentError('Invalid host in endpoint: $endpoint');
     115              :       }
     116              : 
     117              :       // If there's no port and no explicit path, ensure we have the correct default port
     118          144 :       if (uri.port == 0 && !endpoint.contains(':') && uri.path.isEmpty) {
     119            0 :         return '${uri.scheme}://${uri.host}:4318';
     120              :       }
     121              : 
     122              :       return endpoint;
     123              :     } catch (e) {
     124            0 :       if (e is ArgumentError) rethrow;
     125            0 :       throw ArgumentError('Invalid URL format in endpoint: $endpoint');
     126              :     }
     127              :   }
     128              : 
     129           72 :   static Duration _validateTimeout(Duration timeout) {
     130           72 :     if (timeout < const Duration(milliseconds: 1) ||
     131           72 :         timeout > const Duration(minutes: 10)) {
     132            0 :       throw ArgumentError('Timeout must be between 1ms and 10 minutes');
     133              :     }
     134              :     return timeout;
     135              :   }
     136              : 
     137           72 :   static int _validateRetries(int retries) {
     138           72 :     if (retries < 0) {
     139            0 :       throw ArgumentError('maxRetries cannot be negative');
     140              :     }
     141              :     return retries;
     142              :   }
     143              : 
     144           72 :   static Duration _validateDelay(Duration delay, String name) {
     145           72 :     if (delay < const Duration(milliseconds: 1) ||
     146           72 :         delay > const Duration(minutes: 5)) {
     147            0 :       throw ArgumentError('$name must be between 1ms and 5 minutes');
     148              :     }
     149              :     return delay;
     150              :   }
     151              : 
     152           72 :   static void _validateCertificates(
     153              :       String? cert, String? key, String? clientCert) {
     154           72 :     CertificateUtils.validateCertificates(
     155              :       certificate: cert,
     156              :       clientKey: key,
     157              :       clientCertificate: clientCert,
     158              :     );
     159              :   }
     160              : }
        

Generated by: LCOV version 2.0-1