Class: NewRelic::Agent::TransactionSampler

Inherits:
Object
  • Object
show all
Defined in:
lib/new_relic/agent/transaction_sampler.rb

Defined Under Namespace

Modules: Shim

Constant Summary collapse

BUILDER_KEY =
:transaction_sample_builder
MAX_SQL_LENGTH =

some statements (particularly INSERTS with large BLOBS may be very large; we should trim them to a maximum usable length config is the driver configuration for the connection

16384

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeTransactionSampler

Returns a new instance of TransactionSampler.



19
20
21
22
23
24
25
26
27
28
29
# File 'lib/new_relic/agent/transaction_sampler.rb', line 19

def initialize
  @samples = []
  @harvest_count = 0
  @max_samples = 100
  @random_sample = nil
  config = NewRelic::Control.instance
  sampler_config = config.fetch('transaction_tracer', {})
  @segment_limit = sampler_config.fetch('limit_segments', 4000)
  @stack_trace_threshold = sampler_config.fetch('stack_trace_threshold', 0.500).to_f
  @samples_lock = Mutex.new
end

Instance Attribute Details

#last_sampleObject (readonly)

Returns the value of attribute last_sample.



17
18
19
# File 'lib/new_relic/agent/transaction_sampler.rb', line 17

def last_sample
  @last_sample
end

#random_samplingObject

Returns the value of attribute random_sampling.



16
17
18
# File 'lib/new_relic/agent/transaction_sampler.rb', line 16

def random_sampling
  @random_sampling
end

#samplesObject (readonly)

Returns the value of attribute samples.



17
18
19
# File 'lib/new_relic/agent/transaction_sampler.rb', line 17

def samples
  @samples
end

#sampling_rateObject

Returns the value of attribute sampling_rate.



16
17
18
# File 'lib/new_relic/agent/transaction_sampler.rb', line 16

def sampling_rate
  @sampling_rate
end

#stack_trace_thresholdObject

Returns the value of attribute stack_trace_threshold.



16
17
18
# File 'lib/new_relic/agent/transaction_sampler.rb', line 16

def stack_trace_threshold
  @stack_trace_threshold
end

Instance Method Details

#current_sample_idObject



31
32
33
34
# File 'lib/new_relic/agent/transaction_sampler.rb', line 31

def current_sample_id
  b=builder
  b and b.sample_id
end

#disableObject



36
37
38
# File 'lib/new_relic/agent/transaction_sampler.rb', line 36

def disable
  NewRelic::Agent.instance.stats_engine.remove_transaction_sampler self
end

#harvest(previous = nil, slow_threshold = 2.0) ⇒ Object

get the set of collected samples, merging into previous samples, and clear the collected sample list.



153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/new_relic/agent/transaction_sampler.rb', line 153

def harvest(previous = nil, slow_threshold = 2.0)
  result = []
  previous ||= []
  
  previous = [previous] unless previous.is_a?(Array)
  
  previous_slowest = previous.inject(nil) {|a,ts| (a) ? ((a.duration > ts.duration) ? a : ts) : ts}
  
  @samples_lock.synchronize do
    
    if @random_sampling        
      @harvest_count += 1
      
      if (@harvest_count % @sampling_rate) == 0
        result << @random_sample if @random_sample
      else
        @random_sample = nil   # if we don't nil this out, then we won't send the slowest if slowest == @random_sample
      end
    end
    
    slowest = @slowest_sample
    @slowest_sample = nil
    
    if slowest && slowest != @random_sample && slowest.duration >= slow_threshold
      if previous_slowest.nil? || previous_slowest.duration < slowest.duration
        result << slowest
      else
        result << previous_slowest
      end
    end

    @random_sample = nil
    @last_sample = nil
  end
  # Truncate the samples at 2100 segments. The UI will clamp them at 2000 segments anyway.
  # This will save us memory and bandwidth.
  result.each { |sample| sample.truncate(@segment_limit) }
  result
end

#ignore_transactionObject



114
115
116
# File 'lib/new_relic/agent/transaction_sampler.rb', line 114

def ignore_transaction
  builder.ignore_transaction if builder
end

#notice_first_scope_push(time) ⇒ Object



45
46
47
# File 'lib/new_relic/agent/transaction_sampler.rb', line 45

def notice_first_scope_push(time)
  start_builder(time)
end

#notice_pop_scope(scope, time = Time.now.to_f) ⇒ Object



79
80
81
82
# File 'lib/new_relic/agent/transaction_sampler.rb', line 79

def notice_pop_scope(scope, time = Time.now.to_f)
  return unless builder
  builder.trace_exit(scope, time)
end

#notice_profile(profile) ⇒ Object



117
118
119
# File 'lib/new_relic/agent/transaction_sampler.rb', line 117

def notice_profile(profile)
  builder.set_profile(profile) if builder
end

#notice_push_scope(scope, time = Time.now.to_f) ⇒ Object



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/new_relic/agent/transaction_sampler.rb', line 49

def notice_push_scope(scope, time=Time.now.to_f)
  
  return unless builder
  
  builder.trace_entry(scope, time)
  
  # in developer mode, capture the stack trace with the segment.
  # this is cpu and memory expensive and therefore should not be
  # turned on in production mode
  if NewRelic::Control.instance.developer_mode?
    segment = builder.current_segment
    if segment
      # Strip stack frames off the top that match /new_relic/agent/
      trace = caller
      while trace.first =~/\/lib\/new_relic\/agent\//
        trace.shift
      end
      
      trace = trace[0..39] if trace.length > 40
      segment[:backtrace] = trace
    end
  end
end

#notice_scope_empty(time = Time.now.to_f) ⇒ Object

This is called when we are done with the transaction. We’ve unwound the stack to the top level.



86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/new_relic/agent/transaction_sampler.rb', line 86

def notice_scope_empty(time=Time.now.to_f)
  
  last_builder = builder
  return unless last_builder

  last_builder.finish_trace(time)
  clear_builder
  return if last_builder.ignored?

  @samples_lock.synchronize do
    @last_sample = last_builder.sample
    
    @random_sample = @last_sample if @random_sampling
    
    # ensure we don't collect more than a specified number of samples in memory
    @samples << @last_sample if NewRelic::Control.instance.developer_mode?
    @samples.shift while @samples.length > @max_samples
    
    if @slowest_sample.nil? || @slowest_sample.duration < @last_sample.duration
      @slowest_sample = @last_sample
    end
  end
end

#notice_sql(sql, config, duration) ⇒ Object



130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
# File 'lib/new_relic/agent/transaction_sampler.rb', line 130

def notice_sql(sql, config, duration)
  return unless builder
  if Thread::current[:record_sql] != false
    segment = builder.current_segment
    if segment
      current_sql = segment[:sql]
      sql = current_sql + ";\n" + sql if current_sql

      if sql.length > (MAX_SQL_LENGTH - 4)
        sql = sql[0..MAX_SQL_LENGTH-4] + '...'
      end
      
      segment[:sql] = sql
      segment[:connection_config] = config
      segment[:backtrace] = caller.join("\n") if duration >= @stack_trace_threshold 
    end
  end
end

#notice_transaction(path, request = nil, params = {}) ⇒ Object



110
111
112
# File 'lib/new_relic/agent/transaction_sampler.rb', line 110

def notice_transaction(path, request=nil, params={})
  builder.set_transaction_info(path, request, params) if start_builder
end

#notice_transaction_cpu_time(cpu_time) ⇒ Object



121
122
123
# File 'lib/new_relic/agent/transaction_sampler.rb', line 121

def notice_transaction_cpu_time(cpu_time)
  builder.set_transaction_cpu_time(cpu_time) if builder
end

#reset!Object

reset samples without rebooting the web server



194
195
196
197
# File 'lib/new_relic/agent/transaction_sampler.rb', line 194

def reset!
  @samples = []
  @last_sample = nil
end

#scope_depthObject



73
74
75
76
77
# File 'lib/new_relic/agent/transaction_sampler.rb', line 73

def scope_depth
  return 0 unless builder

  builder.scope_depth
end