Class: NewRelic::Agent::TransactionSampler
- Inherits:
-
Object
- Object
- NewRelic::Agent::TransactionSampler
- Defined in:
- lib/new_relic/agent/transaction_sampler.rb
Overview
This class contains the logic of sampling a transaction - creation and modification of transaction samples
Defined Under Namespace
Modules: Shim
Constant Summary collapse
- BUILDER_KEY =
:transaction_sample_builder
- MAX_DATA_LENGTH =
16384
Instance Attribute Summary collapse
-
#disabled ⇒ Object
readonly
Returns the value of attribute disabled.
-
#last_sample ⇒ Object
readonly
Returns the value of attribute last_sample.
-
#random_sampling ⇒ Object
Returns the value of attribute random_sampling.
-
#samples ⇒ Object
readonly
Returns the value of attribute samples.
-
#sampling_rate ⇒ Object
Returns the value of attribute sampling_rate.
-
#slow_capture_threshold ⇒ Object
Returns the value of attribute slow_capture_threshold.
Class Method Summary collapse
-
.truncate_message(message) ⇒ Object
Truncates the message to ‘MAX_DATA_LENGTH` if needed, and appends an ellipsis because it makes the trucation clearer in the UI.
Instance Method Summary collapse
- #add_force_persist_to(result) ⇒ Object
-
#add_random_sample_to(result) ⇒ Object
Every 1/n harvests, adds the most recent sample to the harvest array if it exists.
-
#add_samples_to(result) ⇒ Object
Returns an array of slow samples, with either one or two elements - one element unless random sampling is enabled.
-
#add_segment_parameters(params) ⇒ Object
Set parameters on the current segment.
-
#append_backtrace(segment, duration) ⇒ Object
Appends a backtrace to a segment if that segment took longer than the specified duration.
-
#append_new_message(old_message, message) ⇒ Object
Allows the addition of multiple pieces of metadata to one segment - i.e.
-
#builder ⇒ Object
The current thread-local transaction sample builder.
-
#capture_segment_trace ⇒ Object
in developer mode, capture the stack trace with the segment.
-
#clamp_number_tts(tts, limit) ⇒ Object
JON - THIS CODE NEEDS A UNIT TEST.
-
#clear_builder ⇒ Object
Sets the thread local variable storing the transaction sample builder to nil to clear it.
-
#current_sample_id ⇒ Object
Returns the current sample id, delegated from ‘builder`.
- #enabled? ⇒ Boolean
-
#harvest(previous = []) ⇒ Object
get the set of collected samples, merging into previous samples, and clear the collected sample list.
-
#ignore_transaction ⇒ Object
Tells the builder to ignore a transaction, if we are currently creating one.
-
#initialize ⇒ TransactionSampler
constructor
A new instance of TransactionSampler.
-
#notice_first_scope_push(time) ⇒ Object
Creates a new transaction sample builder, unless the transaction sampler is disabled.
-
#notice_nosql(key, duration) ⇒ Object
Adds non-sql metadata to a segment - generally the memcached key.
-
#notice_pop_scope(scope, time = Time.now) ⇒ Object
Informs the transaction sample builder about the end of a traced scope.
-
#notice_profile(profile) ⇒ Object
For developer mode profiling support - delegates to the builder.
-
#notice_push_scope(scope, time = Time.now) ⇒ Object
This delegates to the builder to create a new open transaction segment for the specified scope, beginning at the optionally specified time.
-
#notice_scope_empty(time = Time.now) ⇒ Object
This is called when we are done with the transaction.
-
#notice_sql(sql, config, duration) ⇒ Object
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 duration is seconds, float value..
-
#notice_transaction(path, uri = nil, params = {}) ⇒ Object
Delegates to the builder to store the path, uri, and parameters if the sampler is active.
-
#notice_transaction_cpu_time(cpu_time) ⇒ Object
Sets the CPU time used by a transaction, delegates to the builder.
-
#rename_scope_segment(new_name) ⇒ Object
Rename the latest scope’s segment in the builder to
new_name
. -
#reset! ⇒ Object
reset samples without rebooting the web server.
-
#scope_depth ⇒ Object
Defaults to zero, otherwise delegated to the transaction sample builder.
-
#slowest_sample?(old_sample, new_sample) ⇒ Boolean
Checks to see if the old sample exists, or if its duration is less than the new sample.
-
#start_builder(time = nil) ⇒ Object
Checks to see if the transaction sampler is disabled, if transaction trace recording is disabled by a thread local, or if execution is untraced - if so it clears the transaction sample builder from the thread local, otherwise it generates a new transaction sample builder with the stated time as a starting point and saves it in the thread local variable.
- #store_force_persist(sample) ⇒ Object
-
#store_random_sample(sample) ⇒ Object
Only active when random sampling is true - this is very rarely used.
-
#store_sample(sample) ⇒ Object
Samples can be stored in three places: the random sample variable, when random sampling is active, the developer mode slower than the current occupant of that slot.
-
#store_sample_for_developer_mode(sample) ⇒ Object
Samples take up a ton of memory, so we only store a lot of them in developer mode - we truncate to @max_samples.
-
#store_slowest_sample(sample) ⇒ Object
Sets @slowest_sample to the passed in sample if it is slower than the current sample in @slowest_sample.
-
#truncate_samples ⇒ Object
Smashes the @samples array down to the length of @max_samples by taking the last @max_samples elements of the array.
Constructor Details
#initialize ⇒ TransactionSampler
Returns a new instance of TransactionSampler.
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 |
# File 'lib/new_relic/agent/transaction_sampler.rb', line 26 def initialize # @samples is an array of recent samples up to @max_samples in # size - it's only used by developer mode @samples = [] @force_persist = [] @max_samples = 100 # @harvest_count is a count of harvests used for random # sampling - we pull 1 @random_sample in every @sampling_rate harvests @harvest_count = 0 @random_sample = nil @sampling_rate = Agent.config[:sample_rate] # This lock is used to synchronize access to the @last_sample # and related variables. It can become necessary on JRuby or # any 'honest-to-god'-multithreaded system @samples_lock = Mutex.new Agent.config.register_callback(:'transaction_tracer.enabled') do |enabled| if enabled threshold = Agent.config[:'transaction_tracer.transaction_threshold'] ::NewRelic::Agent.logger.debug "Transaction tracing threshold is #{threshold} seconds." else ::NewRelic::Agent.logger.debug "Transaction traces will not be sent to the New Relic service." end end Agent.config.register_callback(:'transaction_tracer.record_sql') do |config| if config == 'raw' ::NewRelic::Agent.logger.warn("Agent is configured to send raw SQL to the service") end end end |
Instance Attribute Details
#disabled ⇒ Object (readonly)
Returns the value of attribute disabled.
24 25 26 |
# File 'lib/new_relic/agent/transaction_sampler.rb', line 24 def disabled @disabled end |
#last_sample ⇒ Object (readonly)
Returns the value of attribute last_sample.
24 25 26 |
# File 'lib/new_relic/agent/transaction_sampler.rb', line 24 def last_sample @last_sample end |
#random_sampling ⇒ Object
Returns the value of attribute random_sampling.
22 23 24 |
# File 'lib/new_relic/agent/transaction_sampler.rb', line 22 def random_sampling @random_sampling end |
#samples ⇒ Object (readonly)
Returns the value of attribute samples.
24 25 26 |
# File 'lib/new_relic/agent/transaction_sampler.rb', line 24 def samples @samples end |
#sampling_rate ⇒ Object
Returns the value of attribute sampling_rate.
22 23 24 |
# File 'lib/new_relic/agent/transaction_sampler.rb', line 22 def sampling_rate @sampling_rate end |
#slow_capture_threshold ⇒ Object
Returns the value of attribute slow_capture_threshold.
23 24 25 |
# File 'lib/new_relic/agent/transaction_sampler.rb', line 23 def slow_capture_threshold @slow_capture_threshold end |
Class Method Details
.truncate_message(message) ⇒ Object
Truncates the message to ‘MAX_DATA_LENGTH` if needed, and appends an ellipsis because it makes the trucation clearer in the UI
284 285 286 287 288 289 290 |
# File 'lib/new_relic/agent/transaction_sampler.rb', line 284 def self.() if .length > (MAX_DATA_LENGTH - 4) [0..MAX_DATA_LENGTH - 4] + '...' else end end |
Instance Method Details
#add_force_persist_to(result) ⇒ Object
352 353 354 355 |
# File 'lib/new_relic/agent/transaction_sampler.rb', line 352 def add_force_persist_to(result) result.concat(@force_persist) @force_persist = [] end |
#add_random_sample_to(result) ⇒ Object
Every 1/n harvests, adds the most recent sample to the harvest array if it exists. Makes sure that the random sample is not also the slowest sample for this harvest by ‘uniq!`ing the result array
random sampling is very, very seldom used
341 342 343 344 345 346 347 348 349 350 |
# File 'lib/new_relic/agent/transaction_sampler.rb', line 341 def add_random_sample_to(result) return unless @random_sample && Agent.config[:sample_rate] && Agent.config[:sample_rate].to_i > 0 @harvest_count += 1 if (@harvest_count.to_i % Agent.config[:sample_rate].to_i) == 0 result << @random_sample if @random_sample @harvest_count = 0 end nil # don't assume this method returns anything end |
#add_samples_to(result) ⇒ Object
Returns an array of slow samples, with either one or two elements - one element unless random sampling is enabled. The sample returned will be the slowest sample among those available during this harvest
361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 |
# File 'lib/new_relic/agent/transaction_sampler.rb', line 361 def add_samples_to(result) # pull out force persist force_persist = result.select {|sample| sample.force_persist} || [] result.reject! {|sample| sample.force_persist} force_persist.each {|sample| store_force_persist(sample)} result << @slowest_sample if @slowest_sample result.compact! result = result.sort_by { |x| x.duration } result = result[-1..-1] || [] # take the slowest sample add_random_sample_to(result) add_force_persist_to(result) result.uniq end |
#add_segment_parameters(params) ⇒ Object
Set parameters on the current segment.
330 331 332 333 |
# File 'lib/new_relic/agent/transaction_sampler.rb', line 330 def add_segment_parameters( params ) return unless builder builder.current_segment.params.merge!( params ) end |
#append_backtrace(segment, duration) ⇒ Object
Appends a backtrace to a segment if that segment took longer than the specified duration
304 305 306 307 308 309 |
# File 'lib/new_relic/agent/transaction_sampler.rb', line 304 def append_backtrace(segment, duration) if (duration >= Agent.config[:'transaction_tracer.stack_trace_threshold'] || Thread.current[:capture_deep_tt]) segment[:backtrace] = caller.join("\n") end end |
#append_new_message(old_message, message) ⇒ Object
Allows the addition of multiple pieces of metadata to one segment - i.e. traced method calls multiple sql queries
294 295 296 297 298 299 300 |
# File 'lib/new_relic/agent/transaction_sampler.rb', line 294 def (, ) if + ";\n" + else end end |
#builder ⇒ Object
The current thread-local transaction sample builder
447 448 449 |
# File 'lib/new_relic/agent/transaction_sampler.rb', line 447 def builder Thread::current[BUILDER_KEY] end |
#capture_segment_trace ⇒ Object
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
104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
# File 'lib/new_relic/agent/transaction_sampler.rb', line 104 def capture_segment_trace return unless Agent.config[: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 |
#clamp_number_tts(tts, limit) ⇒ Object
JON - THIS CODE NEEDS A UNIT TEST
408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 |
# File 'lib/new_relic/agent/transaction_sampler.rb', line 408 def clamp_number_tts(tts, limit) tts.sort! do |a,b| if a.force_persist && b.force_persist b.duration <=> a.duration elsif a.force_persist -1 elsif b.force_persist 1 else b.duration <=> a.duration end end tts[0..(limit-1)] end |
#clear_builder ⇒ Object
Sets the thread local variable storing the transaction sample builder to nil to clear it
453 454 455 |
# File 'lib/new_relic/agent/transaction_sampler.rb', line 453 def clear_builder Thread::current[BUILDER_KEY] = nil end |
#current_sample_id ⇒ Object
Returns the current sample id, delegated from ‘builder`
62 63 64 65 |
# File 'lib/new_relic/agent/transaction_sampler.rb', line 62 def current_sample_id b=builder b and b.sample_id end |
#enabled? ⇒ Boolean
67 68 69 |
# File 'lib/new_relic/agent/transaction_sampler.rb', line 67 def enabled? Agent.config[:'transaction_tracer.enabled'] || Agent.config[:developer_mode] end |
#harvest(previous = []) ⇒ Object
get the set of collected samples, merging into previous samples, and clear the collected sample list. Truncates samples to a specified segment_limit to save memory and bandwith transmitting samples to the server.
384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 |
# File 'lib/new_relic/agent/transaction_sampler.rb', line 384 def harvest(previous=[]) return [] if !enabled? result = Array(previous) @samples_lock.synchronize do result = add_samples_to(result) # clear previous transaction samples @slowest_sample = nil @random_sample = nil @last_sample = nil end # Clamp the number of TTs we'll keep in memory and send # result = clamp_number_tts(result, 20) if result.length > 20 # 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(Agent.config[:'transaction_tracer.limit_segments']) } result end |
#ignore_transaction ⇒ Object
Tells the builder to ignore a transaction, if we are currently creating one. Only causes the sample to be ignored upon end of the transaction, and does not change the metrics gathered outside of the sampler
243 244 245 |
# File 'lib/new_relic/agent/transaction_sampler.rb', line 243 def ignore_transaction builder.ignore_transaction if builder end |
#notice_first_scope_push(time) ⇒ Object
Creates a new transaction sample builder, unless the transaction sampler is disabled. Takes a time parameter for the start of the transaction sample
83 84 85 |
# File 'lib/new_relic/agent/transaction_sampler.rb', line 83 def notice_first_scope_push(time) start_builder(time.to_f) if enabled? end |
#notice_nosql(key, duration) ⇒ Object
Adds non-sql metadata to a segment - generally the memcached key
duration is seconds, float value.
325 326 327 |
# File 'lib/new_relic/agent/transaction_sampler.rb', line 325 def notice_nosql(key, duration) notice_extra_data(key, duration, :key) end |
#notice_pop_scope(scope, time = Time.now) ⇒ Object
Informs the transaction sample builder about the end of a traced scope
135 136 137 138 139 |
# File 'lib/new_relic/agent/transaction_sampler.rb', line 135 def notice_pop_scope(scope, time = Time.now) return unless builder raise "frozen already???" if builder.sample.frozen? builder.trace_exit(scope, time.to_f) end |
#notice_profile(profile) ⇒ Object
For developer mode profiling support - delegates to the builder
248 249 250 |
# File 'lib/new_relic/agent/transaction_sampler.rb', line 248 def notice_profile(profile) builder.set_profile(profile) if builder end |
#notice_push_scope(scope, time = Time.now) ⇒ Object
This delegates to the builder to create a new open transaction segment for the specified scope, beginning at the optionally specified time.
Note that in developer mode, this captures a stacktrace for the beginning of each segment, which can be fairly slow
93 94 95 96 97 98 99 |
# File 'lib/new_relic/agent/transaction_sampler.rb', line 93 def notice_push_scope(scope, time=Time.now) return unless builder builder.trace_entry(scope, time.to_f) capture_segment_trace if Agent.config[:developer_mode] end |
#notice_scope_empty(time = Time.now) ⇒ Object
This is called when we are done with the transaction. We’ve unwound the stack to the top level. It also clears the transaction sample builder so that it won’t continue to have scopes appended to it.
It sets various instance variables to the finished sample, depending on which settings are active. See ‘store_sample`
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 |
# File 'lib/new_relic/agent/transaction_sampler.rb', line 148 def notice_scope_empty(time=Time.now) last_builder = builder return unless last_builder last_builder.finish_trace(time.to_f) clear_builder return if last_builder.ignored? @samples_lock.synchronize do # NB this instance variable may be used elsewhere, it's not # just a side effect @last_sample = last_builder.sample store_sample(@last_sample) end end |
#notice_sql(sql, config, duration) ⇒ Object
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 duration is seconds, float value.
315 316 317 318 319 |
# File 'lib/new_relic/agent/transaction_sampler.rb', line 315 def notice_sql(sql, config, duration) if NewRelic::Agent.is_sql_recorded? notice_extra_data(sql, duration, :sql, config, :connection_config) end end |
#notice_transaction(path, uri = nil, params = {}) ⇒ Object
Delegates to the builder to store the path, uri, and parameters if the sampler is active
235 236 237 |
# File 'lib/new_relic/agent/transaction_sampler.rb', line 235 def notice_transaction(path, uri=nil, params={}) builder.set_transaction_info(path, uri, params) if enabled? && builder end |
#notice_transaction_cpu_time(cpu_time) ⇒ Object
Sets the CPU time used by a transaction, delegates to the builder
253 254 255 |
# File 'lib/new_relic/agent/transaction_sampler.rb', line 253 def notice_transaction_cpu_time(cpu_time) builder.set_transaction_cpu_time(cpu_time) if builder end |
#rename_scope_segment(new_name) ⇒ Object
Rename the latest scope’s segment in the builder to new_name
.
120 121 122 123 |
# File 'lib/new_relic/agent/transaction_sampler.rb', line 120 def rename_scope_segment( new_name ) return unless builder builder.rename_current_segment( new_name ) end |
#reset! ⇒ Object
reset samples without rebooting the web server
425 426 427 428 429 430 |
# File 'lib/new_relic/agent/transaction_sampler.rb', line 425 def reset! @samples = [] @last_sample = nil @random_sample = nil @slowest_sample = nil end |
#scope_depth ⇒ Object
Defaults to zero, otherwise delegated to the transaction sample builder
127 128 129 130 131 |
# File 'lib/new_relic/agent/transaction_sampler.rb', line 127 def scope_depth return 0 unless builder builder.scope_depth end |
#slowest_sample?(old_sample, new_sample) ⇒ Boolean
Checks to see if the old sample exists, or if its duration is less than the new sample
221 222 223 |
# File 'lib/new_relic/agent/transaction_sampler.rb', line 221 def slowest_sample?(old_sample, new_sample) old_sample.nil? || (new_sample.duration > old_sample.duration) end |
#start_builder(time = nil) ⇒ Object
Checks to see if the transaction sampler is disabled, if transaction trace recording is disabled by a thread local, or if execution is untraced - if so it clears the transaction sample builder from the thread local, otherwise it generates a new transaction sample builder with the stated time as a starting point and saves it in the thread local variable
438 439 440 441 442 443 444 |
# File 'lib/new_relic/agent/transaction_sampler.rb', line 438 def start_builder(time=nil) if !enabled? || !NewRelic::Agent.is_transaction_traced? || !NewRelic::Agent.is_execution_traced? clear_builder else Thread::current[BUILDER_KEY] ||= TransactionSampleBuilder.new(time) end end |
#store_force_persist(sample) ⇒ Object
191 192 193 194 195 196 197 198 199 |
# File 'lib/new_relic/agent/transaction_sampler.rb', line 191 def store_force_persist(sample) @force_persist << sample # WARNING - this clamp should be configurable if @force_persist.length > 15 @force_persist.sort! {|a,b| b.duration <=> a.duration} @force_persist = @force_persist[0..14] end end |
#store_random_sample(sample) ⇒ Object
Only active when random sampling is true - this is very rarely used. Always store the most recent sample so that random sampling can pick a few of the samples to store, upon harvest
187 188 189 |
# File 'lib/new_relic/agent/transaction_sampler.rb', line 187 def store_random_sample(sample) @random_sample = sample if Agent.config[:'transaction_tracer.random_sample'] end |
#store_sample(sample) ⇒ Object
Samples can be stored in three places: the random sample variable, when random sampling is active, the developer mode slower than the current occupant of that slot
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 |
# File 'lib/new_relic/agent/transaction_sampler.rb', line 168 def store_sample(sample) sampler_methods = [ :store_slowest_sample ] if Agent.config[:developer_mode] sampler_methods << :store_sample_for_developer_mode end if Agent.config[:'transaction_tracer.random_sample'] sampler_methods << :store_random_sample end sampler_methods.each{|sym| send(sym, sample) } if NewRelic::Agent::TransactionInfo.get.force_persist_sample?(sample) store_force_persist(sample) end end |
#store_sample_for_developer_mode(sample) ⇒ Object
Samples take up a ton of memory, so we only store a lot of them in developer mode - we truncate to @max_samples
203 204 205 206 207 208 |
# File 'lib/new_relic/agent/transaction_sampler.rb', line 203 def store_sample_for_developer_mode(sample) return unless Agent.config[:developer_mode] @samples = [] unless @samples @samples << sample truncate_samples end |
#store_slowest_sample(sample) ⇒ Object
Sets @slowest_sample to the passed in sample if it is slower than the current sample in @slowest_sample
212 213 214 215 216 217 |
# File 'lib/new_relic/agent/transaction_sampler.rb', line 212 def store_slowest_sample(sample) if slowest_sample?(@slowest_sample, sample) && sample.threshold && sample.duration >= sample.threshold @slowest_sample = sample end end |
#truncate_samples ⇒ Object
Smashes the @samples array down to the length of @max_samples by taking the last @max_samples elements of the array
227 228 229 230 231 |
# File 'lib/new_relic/agent/transaction_sampler.rb', line 227 def truncate_samples if @samples.length > @max_samples @samples = @samples[-@max_samples..-1] end end |