LCOV - code coverage report
Current view: top level - lib/src/trace/sampling - counting_sampler.dart (source / functions) Coverage Total Hit
Test: lcov.info Lines: 100.0 % 54 54
Test Date: 2026-08-27 23:42:02 Functions: - 0 0

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

Generated by: LCOV version 2.0-1