LCOV - code coverage report
Current view: top level - src/trace/sampling - counting_sampler.dart (source / functions) Coverage Total Hit
Test: lcov.info Lines: 60.0 % 50 30
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 'package:dartastic_opentelemetry_api/dartastic_opentelemetry_api.dart';
       5              : import 'sampler.dart';
       6              : 
       7              : /// A sampler that samples every Nth request.
       8              : /// Optionally can be combined with conditions to override the count-based decision.
       9              : class CountingSampler implements Sampler {
      10              :   final int _countInterval;
      11              :   final List<SamplingCondition> _overrideConditions;
      12              :   int _currentCount = 0;
      13              : 
      14            0 :   @override
      15            0 :   String get description => 'CountingSampler{interval=$_countInterval}';
      16              : 
      17              :   /// Creates a sampler that samples every Nth request.
      18              :   /// [countInterval] must be positive.
      19              :   /// [overrideConditions] are optional conditions that can force sampling regardless of count.
      20            1 :   CountingSampler(
      21              :     int countInterval, {
      22              :     List<SamplingCondition>? overrideConditions,
      23              :   })  : _countInterval = countInterval,
      24            1 :         _overrideConditions = overrideConditions ?? [] {
      25            1 :     if (countInterval <= 0) {
      26            0 :       throw ArgumentError('countInterval must be positive');
      27              :     }
      28              :   }
      29              : 
      30            1 :   @override
      31              :   SamplingResult shouldSample({
      32              :     required Context parentContext,
      33              :     required String traceId,
      34              :     required String name,
      35              :     required SpanKind spanKind,
      36              :     required Attributes? attributes,
      37              :     required List<SpanLink>? links,
      38              :   }) {
      39              :     // Check override conditions first
      40            2 :     for (final condition in _overrideConditions) {
      41            1 :       if (condition.shouldSampleCondition(
      42              :         name: name,
      43              :         spanKind: spanKind,
      44              :         attributes: attributes,
      45              :       )) {
      46              :         return const SamplingResult(
      47              :           decision: SamplingDecision.recordAndSample,
      48              :           source: SamplingDecisionSource.tracerConfig,
      49              :         );
      50              :       }
      51              :     }
      52              : 
      53              :     // Increment counter and check if we should sample
      54            5 :     _currentCount = (_currentCount + 1) % _countInterval;
      55            2 :     final shouldSample = _currentCount == 0;
      56              : 
      57            1 :     return SamplingResult(
      58              :       decision: shouldSample
      59              :           ? SamplingDecision.recordAndSample
      60              :           : SamplingDecision.drop,
      61              :       source: SamplingDecisionSource.tracerConfig,
      62              :     );
      63              :   }
      64              : }
      65              : 
      66              : /// Base class for sampling conditions that can be used with the CountingSampler
      67              : /// to override its default behavior based on span properties.
      68              : abstract class SamplingCondition implements Sampler {
      69              :   /// Determines whether a span should be sampled based on its properties.
      70              :   ///
      71              :   /// @param name The name of the span
      72              :   /// @param spanKind The kind of span
      73              :   /// @param attributes The attributes of the span
      74              :   /// @return true if the span should be sampled, false otherwise
      75              :   bool shouldSampleCondition({
      76              :     required String name,
      77              :     required SpanKind spanKind,
      78              :     required Attributes? attributes,
      79              :   });
      80              : 
      81            1 :   @override
      82              :   SamplingResult shouldSample({
      83              :     required Context parentContext,
      84              :     required String traceId,
      85              :     required String name,
      86              :     required SpanKind spanKind,
      87              :     required Attributes? attributes,
      88              :     required List<SpanLink>? links,
      89              :   }) {
      90            1 :     final shouldRecord = shouldSampleCondition(
      91              :       name: name,
      92              :       spanKind: spanKind,
      93              :       attributes: attributes,
      94              :     );
      95              : 
      96            1 :     return SamplingResult(
      97              :       decision: shouldRecord
      98              :           ? SamplingDecision.recordAndSample
      99              :           : SamplingDecision.drop,
     100              :       source: SamplingDecisionSource.tracerConfig,
     101              :     );
     102              :   }
     103              : }
     104              : 
     105              : /// A sampling condition that forces sampling when a span has an error status.
     106              : ///
     107              : /// This condition can be used to ensure that all spans with errors are sampled,
     108              : /// regardless of other sampling decisions.
     109              : class ErrorSamplingCondition extends SamplingCondition {
     110              :   /// Creates a new ErrorSamplingCondition.
     111              :   ///
     112              :   /// This condition samples spans that have an error status, ensuring that all
     113              :   /// spans with errors are recorded even when other sampling strategies might skip them.
     114            1 :   ErrorSamplingCondition();
     115              : 
     116              :   /// Returns the string description of this sampling condition.
     117              :   ///
     118              :   /// This is used for logging and debugging purposes.
     119            0 :   @override
     120              :   String get description => 'ErrorSamplingCondition';
     121              : 
     122            1 :   @override
     123              : 
     124              :   /// Determines whether a span should be sampled based on its properties.
     125              :   ///
     126              :   /// @param name The name of the span
     127              :   /// @param spanKind The kind of span
     128              :   /// @param attributes The attributes of the span
     129              :   /// @return true if the span should be sampled, false otherwise
     130              :   bool shouldSampleCondition({
     131              :     required String name,
     132              :     required SpanKind spanKind,
     133              :     required Attributes? attributes,
     134              :   }) {
     135              :     if (attributes == null) return false;
     136              : 
     137              :     // Check for error status
     138            1 :     final statusCode = attributes.getString('otel.status_code');
     139            1 :     final statusMessage = attributes.getString('otel.status_description');
     140              : 
     141            1 :     return (statusCode == 'ERROR' ||
     142            0 :         (statusMessage != null && statusMessage.isNotEmpty));
     143              :   }
     144              : }
     145              : 
     146              : /// A sampling condition that forces sampling when a span's name matches a pattern.
     147              : ///
     148              : /// This condition can be used to ensure that spans with names matching a specific
     149              : /// pattern are always sampled, regardless of other sampling decisions.
     150              : class NamePatternSamplingCondition extends SamplingCondition {
     151              :   /// the pattern to match
     152              :   final Pattern pattern;
     153              : 
     154              :   /// Creates a new NamePatternSamplingCondition with the specified pattern.
     155              :   ///
     156              :   /// This condition samples spans whose names match the given pattern, allowing
     157              :   /// targeted sampling of specific operations.
     158              :   ///
     159              :   /// @param pattern The pattern to match against span names
     160            1 :   NamePatternSamplingCondition(this.pattern);
     161              : 
     162              :   /// Returns a string description of this sampling condition.
     163            0 :   @override
     164            0 :   String get description => 'NamePatternSamplingCondition{$pattern}';
     165              : 
     166            1 :   @override
     167              : 
     168              :   /// Determines whether a span should be sampled based on its properties.
     169              :   ///
     170              :   /// This method checks if the span name matches the pattern specified in the constructor.
     171              :   ///
     172              :   /// @param name The name of the span to check against the pattern
     173              :   /// @param spanKind The kind of span (not used in this implementation)
     174              :   /// @param attributes The attributes of the span (not used in this implementation)
     175              :   /// @return true if the span's name matches the pattern, false otherwise
     176              :   bool shouldSampleCondition({
     177              :     required String name,
     178              :     required SpanKind spanKind,
     179              :     required Attributes? attributes,
     180              :   }) {
     181            2 :     return name.contains(pattern);
     182              :   }
     183              : }
     184              : 
     185              : /// A sampling condition that forces sampling when a span has a specific attribute value.
     186              : ///
     187              : /// This condition can be used to ensure that spans with particular attribute values
     188              : /// are always sampled, regardless of other sampling decisions.
     189              : class AttributeSamplingCondition extends SamplingCondition {
     190              :   /// The attribute key to check when determining whether to sample.
     191              :   final String key;
     192              : 
     193              :   /// The string value to match against the attribute, if this is a string attribute.
     194              :   final String? stringValue;
     195              : 
     196              :   /// The boolean value to match against the attribute, if this is a boolean attribute.
     197              :   final bool? boolValue;
     198              : 
     199              :   /// The integer value to match against the attribute, if this is an integer attribute.
     200              :   final int? intValue;
     201              : 
     202              :   /// The double value to match against the attribute, if this is a double attribute.
     203              :   final double? doubleValue;
     204              : 
     205              :   /// Returns a string description of this sampling condition.
     206              :   ///
     207              :   /// Used for logging and debugging purposes.
     208            0 :   @override
     209            0 :   String get description => 'AttributeSamplingCondition{$key}';
     210              : 
     211              :   /// Creates a new AttributeSamplingCondition that matches spans with a specific attribute value.
     212              :   ///
     213              :   /// This condition samples spans that have an attribute with the specified key and value.
     214              :   /// Only one of the type-specific values (stringValue, boolValue, intValue, doubleValue)
     215              :   /// should be provided.
     216              :   ///
     217              :   /// @param key The attribute key to match
     218              :   /// @param stringValue Optional string value to match
     219              :   /// @param boolValue Optional boolean value to match
     220              :   /// @param intValue Optional integer value to match
     221              :   /// @param doubleValue Optional double value to match
     222            1 :   AttributeSamplingCondition(this.key,
     223              :       {this.stringValue, this.boolValue, this.intValue, this.doubleValue}) {
     224              :     int nonNullCount = 0;
     225            1 :     if (stringValue != null) {
     226            1 :       nonNullCount++;
     227              :     }
     228            1 :     if (boolValue != null) {
     229            0 :       nonNullCount++;
     230              :     }
     231            1 :     if (intValue != null) {
     232            0 :       nonNullCount++;
     233              :     }
     234            1 :     if (doubleValue != null) {
     235            0 :       nonNullCount++;
     236              :     }
     237            1 :     if (nonNullCount != 1) {
     238            0 :       throw ArgumentError(
     239            0 :           'One of the type values must be non-null. string: $stringValue, bool: $boolValue, int: $intValue, double: $doubleValue');
     240              :     }
     241              :   }
     242              : 
     243            1 :   @override
     244              : 
     245              :   /// Determines whether a span should be sampled based on its properties.
     246              :   ///
     247              :   /// This method checks if the span has attributes matching the specific key and value
     248              :   /// configured in this condition.
     249              :   ///
     250              :   /// @param name The name of the span
     251              :   /// @param spanKind The kind of span
     252              :   /// @param attributes The attributes of the span
     253              :   /// @return true if the span's attributes match the configured values, false otherwise
     254              :   bool shouldSampleCondition({
     255              :     required String name,
     256              :     required SpanKind spanKind,
     257              :     required Attributes? attributes,
     258              :   }) {
     259              :     if (attributes == null) {
     260              :       return false;
     261              :     }
     262            1 :     if (stringValue != null) {
     263            4 :       return attributes.getString(key) == stringValue;
     264              :     }
     265            0 :     if (boolValue != null) {
     266            0 :       return attributes.getBool(key) == boolValue;
     267              :     }
     268            0 :     if (intValue != null) {
     269            0 :       return attributes.getInt(key) == intValue;
     270              :     }
     271            0 :     if (doubleValue != null) {
     272            0 :       return attributes.getDouble(key) == doubleValue;
     273              :     }
     274              :     return false;
     275              :   }
     276              : }
        

Generated by: LCOV version 2.0-1