All files / src/plugins SamplingPlugin.ts

100% Statements 94/94
89.28% Branches 25/28
100% Functions 8/8
100% Lines 94/94

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 951x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 18x 18x 18x 18x 18x 18x 18x 18x 18x 18x 18x 18x 18x 1x 1x 16x 16x 16x 1x 1x 16x 16x 14x 14x 13x 13x 14x 16x 1x 1x 16x 16x 13x 13x 13x 16x 11x 11x 2x 16x 1x 1x 19x 19x 19x 19x 1x 1x 18x 18x 19x 19x 1x 1x 13x 13x 4x 13x 1x 1x 16x 16x 6x 6x 6x 6x 6x 6x 6x 10x 16x 1x 1x 1x 1x 1x 1x 1x  
import type { LightPlugin, LightClient, SentryEvent, LightConfig } from '../types';
import { isObject } from '../utils/helper';
import { logDebug } from '../utils/logger';
 
interface SampleRateConfig {
  error?: number;
  performance?: number;
  network?: number;
  behavior?: number;
  [key: string]: number | undefined;
}
 
interface SamplingPluginConfig {
  rates?: SampleRateConfig;
}
 
class SamplingPlugin implements LightPlugin {
  name = 'sampling';
  version = '1.0.0';
 
  private client!: LightClient;
  private config: SamplingPluginConfig = {
    rates: {
      error: 1,
      performance: 1,
      network: 1,
      behavior: 1,
    },
  };
 
  setup(client: LightClient): void {
    this.client = client;
    this.loadConfig();
  }
 
  private loadConfig(): void {
    const clientConfig = this.client.config as LightConfig;
    if (clientConfig && isObject(clientConfig.sampling)) {
      const samplingConfig = clientConfig.sampling as object;
      if ('rates' in samplingConfig && isObject((samplingConfig as { rates?: object }).rates)) {
        this.config.rates = { ...this.config.rates, ...((samplingConfig as { rates: object }).rates as object) } as SampleRateConfig;
      }
    }
  }
 
  private shouldSample(event: SentryEvent): boolean {
    const globalRate = (this.client.config as LightConfig).sampleRate ?? 1;
    if (globalRate <= 0) return false;
 
    const typeRate = this.getTypeRate(event);
 
    if (globalRate >= 1) {
      return this.randomCheck(typeRate);
    }
    return this.randomCheck(globalRate * typeRate);
  }
 
  private getTypeRate(event: SentryEvent): number {
    const rates = this.config.rates || {};
    const type = event.type;
 
    if (type === 'count') {
      return 1;
    }
 
    const rate = rates[type];
    return rate !== undefined ? rate : 1;
  }
 
  private randomCheck(rate: number): boolean {
    if (rate >= 1) return true;
    if (rate <= 0) return false;
    return Math.random() < rate;
  }
 
  beforeReport(event: SentryEvent): SentryEvent | null {
    const sampled = this.shouldSample(event);
    if (!sampled) {
      logDebug('SamplingPlugin', 'Event dropped by sampling', {
        type: event.type,
        globalRate: (this.client.config as LightConfig).sampleRate ?? 1,
        typeRate: this.getTypeRate(event),
      });
      return null;
    }
    return event;
  }
 
  destroy(): void {
    // nothing to clean up
  }
}
 
export default SamplingPlugin;