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

Generated by: LCOV version 2.0-1