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

#disabledObject (readonly)

Returns the value of attribute disabled.



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

def disabled
  @disabled
end

#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
39
# File 'lib/new_relic/agent/transaction_sampler.rb', line 36

def disable
  @disabled = true
  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.



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
192
193
194
# File 'lib/new_relic/agent/transaction_sampler.rb', line 155

def harvest(previous = nil, slow_threshold = 2.0)
  return [] if disabled
  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



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

def ignore_transaction
  builder.ignore_transaction if builder
end

#notice_first_scope_push(time) ⇒ Object



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

def notice_first_scope_push(time)
  start_builder(time) unless disabled
end

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



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

def notice_pop_scope(scope, time = Time.now.to_f)
  return unless builder
  raise "frozen already???" if builder.sample.frozen?
  builder.trace_exit(scope, time)
end

#notice_profile(profile) ⇒ Object



119
120
121
# File 'lib/new_relic/agent/transaction_sampler.rb', line 119

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

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



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

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.



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

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



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

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, uri = nil, params = {}) ⇒ Object



112
113
114
# File 'lib/new_relic/agent/transaction_sampler.rb', line 112

def notice_transaction(path, uri=nil, params={})
  builder.set_transaction_info(path, uri, params) if !disabled && builder
end

#notice_transaction_cpu_time(cpu_time) ⇒ Object



123
124
125
# File 'lib/new_relic/agent/transaction_sampler.rb', line 123

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



197
198
199
200
# File 'lib/new_relic/agent/transaction_sampler.rb', line 197

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

#scope_depthObject



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

def scope_depth
  return 0 unless builder

  builder.scope_depth
end