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 % 50 50
Test Date: 2026-07-11 13:35:19 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            1 :   @override
      15            2 :   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            2 :   CountingSampler(
      21              :     int countInterval, {
      22              :     List<SamplingCondition>? overrideConditions,
      23              :   })  : _countInterval = countInterval,
      24            2 :         _overrideConditions = overrideConditions ?? [] {
      25            2 :     if (countInterval <= 0) {
      26            1 :       throw ArgumentError('countInterval must be positive');
      27              :     }
      28              :   }
      29              : 
      30            2 :   @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            4 :     for (final condition in _overrideConditions) {
      41            2 :       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           10 :     _currentCount = (_currentCount + 1) % _countInterval;
      55            4 :     final shouldSample = _currentCount == 0;
      56              : 
      57            2 :     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            2 :   @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            2 :     final shouldRecord = shouldSampleCondition(
      91              :       name: name,
      92              :       spanKind: spanKind,
      93              :       attributes: attributes,
      94              :     );
      95              : 
      96            2 :     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            2 :   ErrorSamplingCondition();
     115              : 
     116              :   /// Returns the string description of this sampling condition.
     117              :   ///
     118              :   /// This is used for logging and debugging purposes.
     119            1 :   @override
     120              :   String get description => 'ErrorSamplingCondition';
     121              : 
     122            2 :   @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            2 :     final statusCode = attributes.getString('otel.status_code');
     139            2 :     final statusMessage = attributes.getString('otel.status_description');
     140              : 
     141            2 :     return statusCode == 'ERROR' ||
     142            1 :         (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            2 :   NamePatternSamplingCondition(this.pattern);
     161              : 
     162              :   /// Returns a string description of this sampling condition.
     163            1 :   @override
     164            2 :   String get description => 'NamePatternSamplingCondition{$pattern}';
     165              : 
     166            2 :   @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            4 :     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            1 :   @override
     209            2 :   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            2 :   AttributeSamplingCondition(
     223              :     this.key, {
     224              :     this.stringValue,
     225              :     this.boolValue,
     226              :     this.intValue,
     227              :     this.doubleValue,
     228              :   }) {
     229              :     var nonNullCount = 0;
     230            2 :     if (stringValue != null) {
     231            2 :       nonNullCount++;
     232              :     }
     233            2 :     if (boolValue != null) {
     234            1 :       nonNullCount++;
     235              :     }
     236            2 :     if (intValue != null) {
     237            1 :       nonNullCount++;
     238              :     }
     239            2 :     if (doubleValue != null) {
     240            1 :       nonNullCount++;
     241              :     }
     242            2 :     if (nonNullCount != 1) {
     243            1 :       throw ArgumentError(
     244            5 :         'One of the type values must be non-null. string: $stringValue, bool: $boolValue, int: $intValue, double: $doubleValue',
     245              :       );
     246              :     }
     247              :   }
     248              : 
     249            2 :   @override
     250              : 
     251              :   /// Determines whether a span should be sampled based on its properties.
     252              :   ///
     253              :   /// This method checks if the span has attributes matching the specific key and value
     254              :   /// configured in this condition.
     255              :   ///
     256              :   /// @param name The name of the span
     257              :   /// @param spanKind The kind of span
     258              :   /// @param attributes The attributes of the span
     259              :   /// @return true if the span's attributes match the configured values, false otherwise
     260              :   bool shouldSampleCondition({
     261              :     required String name,
     262              :     required SpanKind spanKind,
     263              :     required Attributes? attributes,
     264              :   }) {
     265              :     if (attributes == null) {
     266              :       return false;
     267              :     }
     268            2 :     if (stringValue != null) {
     269            8 :       return attributes.getString(key) == stringValue;
     270              :     }
     271            1 :     if (boolValue != null) {
     272            4 :       return attributes.getBool(key) == boolValue;
     273              :     }
     274            1 :     if (intValue != null) {
     275            4 :       return attributes.getInt(key) == intValue;
     276              :     }
     277            1 :     if (doubleValue != null) {
     278            4 :       return attributes.getDouble(key) == doubleValue;
     279              :     }
     280              :     return false;
     281              :   }
     282              : }
        

Generated by: LCOV version 2.0-1