Class: Statsig::StatsigLogger

Inherits:
Object
  • Object
show all
Defined in:
lib/statsig_logger.rb

Instance Method Summary collapse

Constructor Details

#initialize(network, options, error_boundary) ⇒ StatsigLogger

Returns a new instance of StatsigLogger.



12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# File 'lib/statsig_logger.rb', line 12

def initialize(network, options, error_boundary)
  @network = network
  @events = []
  @options = options

  @logging_pool = Concurrent::ThreadPoolExecutor.new(
    name: 'statsig-logger',
    min_threads: @options.logger_threadpool_size,
    max_threads: @options.logger_threadpool_size,
    # max jobs pending before we start dropping
    max_queue: 100,
    fallback_policy: :discard
  )

  @error_boundary = error_boundary
  @background_flush = periodic_flush
  @deduper = Concurrent::Set.new()
  @interval = 0
  @flush_mutex = Mutex.new
end

Instance Method Details

#flushObject



144
145
146
147
148
149
150
151
152
153
154
155
# File 'lib/statsig_logger.rb', line 144

def flush
  @flush_mutex.synchronize do
    if @events.length.zero?
      return
    end

    events_clone = @events
    @events = []
    flush_events = events_clone.map { |e| e.serialize }
    @network.post_logs(flush_events)
  end
end

#flush_asyncObject



138
139
140
141
142
# File 'lib/statsig_logger.rb', line 138

def flush_async
  @logging_pool.post do
    flush
  end
end

#log_config_exposure(user, config_name, rule_id, secondary_exposures, eval_details, context = nil) ⇒ Object



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/statsig_logger.rb', line 58

def log_config_exposure(user, config_name, rule_id, secondary_exposures, eval_details, context = nil)
  event = StatsigEvent.new($config_exposure_event)
  event.user = user
   = {
    config: config_name,
    ruleID: rule_id || Statsig::Const::EMPTY_STR,
  }
  return false if not is_unique_exposure(user, $config_exposure_event, )
  event. = 
  event.secondary_exposures = secondary_exposures.is_a?(Array) ? secondary_exposures : []

  safe_add_eval_details(eval_details, event)
  safe_add_exposure_context(context, event)
  log_event(event)
end

#log_diagnostics_event(diagnostics, context, user = nil) ⇒ Object



101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/statsig_logger.rb', line 101

def log_diagnostics_event(diagnostics, context, user = nil)
  return if diagnostics.nil?
  if @options.disable_diagnostics_logging
    diagnostics.clear_markers(context)
    return
  end

  event = StatsigEvent.new($diagnostics_event)
  event.user = user
  serialized = diagnostics.serialize_with_sampling(context)
  diagnostics.clear_markers(context)
  return if serialized[:markers].empty?

  event. = serialized
  log_event(event)
end

#log_event(event) ⇒ Object



33
34
35
36
37
38
# File 'lib/statsig_logger.rb', line 33

def log_event(event)
  @events.push(event)
  if @events.length >= @options.logging_max_buffer_size
    flush_async
  end
end

#log_gate_exposure(user, gate_name, value, rule_id, secondary_exposures, eval_details, context = nil) ⇒ Object



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/statsig_logger.rb', line 40

def log_gate_exposure(user, gate_name, value, rule_id, secondary_exposures, eval_details, context = nil)
  event = StatsigEvent.new($gate_exposure_event)
  event.user = user
   = {
    gate: gate_name,
    gateValue: value.to_s,
    ruleID: rule_id || Statsig::Const::EMPTY_STR,
  }
  return false if not is_unique_exposure(user, $gate_exposure_event, )
  event. = 

  event.secondary_exposures = secondary_exposures.is_a?(Array) ? secondary_exposures : []

  safe_add_eval_details(eval_details, event)
  safe_add_exposure_context(context, event)
  log_event(event)
end

#log_layer_exposure(user, layer, parameter_name, config_evaluation, context = nil) ⇒ Object



74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/statsig_logger.rb', line 74

def log_layer_exposure(user, layer, parameter_name, config_evaluation, context = nil)
  exposures = config_evaluation.undelegated_sec_exps || []
  allocated_experiment = Statsig::Const::EMPTY_STR
  is_explicit = (config_evaluation.explicit_parameters&.include? parameter_name) || false
  if is_explicit
    allocated_experiment = config_evaluation.config_delegate
    exposures = config_evaluation.secondary_exposures
  end

  event = StatsigEvent.new($layer_exposure_event)
  event.user = user
   = {
    config: layer.name,
    ruleID: layer.rule_id || Statsig::Const::EMPTY_STR,
    allocatedExperiment: allocated_experiment,
    parameterName: parameter_name,
    isExplicitParameter: String(is_explicit)
  }
  return false unless is_unique_exposure(user, $layer_exposure_event, )
  event. = 
  event.secondary_exposures = exposures.is_a?(Array) ? exposures : []

  safe_add_eval_details(config_evaluation.evaluation_details, event)
  safe_add_exposure_context(context, event)
  log_event(event)
end

#maybe_restart_background_threadsObject



157
158
159
160
161
# File 'lib/statsig_logger.rb', line 157

def maybe_restart_background_threads
  if @background_flush.nil? || !@background_flush.alive?
    @background_flush = periodic_flush
  end
end

#periodic_flushObject



118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/statsig_logger.rb', line 118

def periodic_flush
  Thread.new do
    @error_boundary.capture(task: lambda {
      loop do
        sleep @options.logging_interval_seconds
        flush_async
        @interval += 1
        @deduper.clear if @interval % 2 == 0
      end
    })
  end
end

#shutdownObject



131
132
133
134
135
136
# File 'lib/statsig_logger.rb', line 131

def shutdown
  @background_flush&.exit
  @logging_pool.shutdown
  @logging_pool.wait_for_termination(timeout = 3)
  flush
end