Class: OneApm::Collector::TransactionSampler
- Inherits:
-
Object
- Object
- OneApm::Collector::TransactionSampler
- Defined in:
- lib/one_apm/collector/containers/transaction_sampler.rb
Overview
This class contains the logic for recording and storing transaction traces (sometimes referred to as ‘transaction samples’).
A transaction trace is a detailed timeline of the events that happened during the processing of a single transaction, including database calls, template rendering calls, and other instrumented method calls.
Defined Under Namespace
Modules: Shim
Constant Summary collapse
- OA_MAX_DATA_LENGTH =
16384
Instance Attribute Summary collapse
- #dev_mode_sample_buffer ⇒ Object readonly
- #last_sample ⇒ Object readonly
- #xray_sample_buffer ⇒ Object readonly
Class Method Summary collapse
-
.truncate_message(message) ⇒ Object
Truncates the message to ‘OA_MAX_DATA_LENGTH` if needed, and appends an ellipsis because it makes the trucation clearer in the UI.
Instance Method Summary collapse
-
#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.
- #build_database_statement(sql, config, explainer) ⇒ Object
- #count ⇒ Object
- #custom_parameters_from_transaction(txn) ⇒ Object
- #enabled? ⇒ Boolean
-
#harvest! ⇒ Object
Gather transaction traces that we’d like to transmit to the server.
- #harvest_from_sample_buffers ⇒ Object
-
#ignore_transaction(state) ⇒ Object
Tells the builder to ignore a transaction, if we are currently creating one.
-
#initialize ⇒ TransactionSampler
constructor
A new instance of TransactionSampler.
- #merge!(previous) ⇒ Object
-
#notice_nosql(key, duration) ⇒ Object
Attaches an additional non-SQL query parameter to the current transaction trace segment.
- #notice_nosql_statement(statement, duration) ⇒ Object
-
#notice_pop_frame(state, frame, time = Time.now) ⇒ Object
Informs the transaction sample builder about the end of a traced frame.
-
#notice_push_frame(state, time = Time.now) ⇒ Object
This delegates to the builder to create a new open transaction segment for the frame, beginning at the optionally specified time.
-
#notice_sql(sql, config, duration, state = nil, &explainer) ⇒ Object
Attaches an SQL query on the current transaction trace segment.
-
#notice_transaction_cpu_time(state, cpu_time) ⇒ Object
Sets the CPU time used by a transaction, delegates to the builder.
-
#on_finishing_transaction(state, txn, time = Time.now, gc_time = nil) ⇒ Object
This is called when we are done with the transaction.
- #on_start_transaction(state, start_time, uri = nil) ⇒ Object
- #prepare_samples(samples) ⇒ Object
-
#reset! ⇒ Object
reset samples without rebooting the web server (used by dev mode).
-
#start_builder(state, 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_sample(sample) ⇒ Object
-
#tl_builder ⇒ Object
The current thread-local transaction sample builder.
Constructor Details
#initialize ⇒ TransactionSampler
Returns a new instance of TransactionSampler.
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 |
# File 'lib/one_apm/collector/containers/transaction_sampler.rb', line 33 def initialize @xray_sample_buffer = OneApm::Transaction::XraySampleBuffer.new @dev_mode_sample_buffer = OneApm::Transaction::DeveloperModeSampleBuffer.new @sample_buffers = [] @sample_buffers << @xray_sample_buffer @sample_buffers << @dev_mode_sample_buffer @sample_buffers << OneApm::Transaction::SlowestSampleBuffer.new @sample_buffers << OneApm::Transaction::SyntheticsSampleBuffer.new @sample_buffers << OneApm::Transaction::ForcePersistSampleBuffer.new # 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 OneApm::Manager.config.register_callback(:'transaction_tracer.enabled') do |enabled| if enabled threshold = OneApm::Manager.config[:'transaction_tracer.transaction_threshold'] OneApm::Manager.logger.debug "Transaction tracing threshold is #{threshold} seconds." else OneApm::Manager.logger.debug "Transaction traces will not be sent to the OneApm service." end end OneApm::Manager.config.register_callback(:'transaction_tracer.record_sql') do |config| if config == 'raw' OneApm::Manager.logger.warn("Agent is configured to send raw SQL to the service") end end end |
Instance Attribute Details
#dev_mode_sample_buffer ⇒ Object (readonly)
31 32 33 |
# File 'lib/one_apm/collector/containers/transaction_sampler.rb', line 31 def dev_mode_sample_buffer @dev_mode_sample_buffer end |
#last_sample ⇒ Object (readonly)
31 32 33 |
# File 'lib/one_apm/collector/containers/transaction_sampler.rb', line 31 def last_sample @last_sample end |
#xray_sample_buffer ⇒ Object (readonly)
31 32 33 |
# File 'lib/one_apm/collector/containers/transaction_sampler.rb', line 31 def xray_sample_buffer @xray_sample_buffer end |
Class Method Details
.truncate_message(message) ⇒ Object
Truncates the message to ‘OA_MAX_DATA_LENGTH` if needed, and appends an ellipsis because it makes the trucation clearer in the UI
196 197 198 199 200 201 202 |
# File 'lib/one_apm/collector/containers/transaction_sampler.rb', line 196 def self.() if .length > (OA_MAX_DATA_LENGTH - 4) [0..OA_MAX_DATA_LENGTH - 4] + '...' else end end |
Instance Method Details
#add_segment_parameters(params) ⇒ Object
Set parameters on the current segment.
276 277 278 279 280 |
# File 'lib/one_apm/collector/containers/transaction_sampler.rb', line 276 def add_segment_parameters(params) builder = tl_builder return unless builder params.each { |k,v| builder.current_segment[k] = v } end |
#append_backtrace(segment, duration) ⇒ Object
Appends a backtrace to a segment if that segment took longer than the specified duration
206 207 208 209 210 211 212 |
# File 'lib/one_apm/collector/containers/transaction_sampler.rb', line 206 def append_backtrace(segment, duration) if duration >= OneApm::Manager.config[:'transaction_tracer.stack_trace_threshold'] backtrace = caller backtrace.reject! { |t| t.include?('one_apm') } segment[:backtrace] = backtrace.join("\n") end end |
#build_database_statement(sql, config, explainer) ⇒ Object
239 240 241 242 243 244 245 246 247 248 |
# File 'lib/one_apm/collector/containers/transaction_sampler.rb', line 239 def build_database_statement(sql, config, explainer) statement = OneApm::Agent::Database::Statement.new(OneApm::Agent::Database.capture_query(sql)) if config statement.adapter = config[:adapter] statement.config = config end statement.explainer = explainer statement end |
#count ⇒ Object
313 314 315 316 317 318 |
# File 'lib/one_apm/collector/containers/transaction_sampler.rb', line 313 def count @samples_lock.synchronize do samples = @sample_buffers.inject([]) { |all, b| all.concat(b.samples) } samples.uniq.size end end |
#custom_parameters_from_transaction(txn) ⇒ Object
101 102 103 104 105 106 107 |
# File 'lib/one_apm/collector/containers/transaction_sampler.rb', line 101 def custom_parameters_from_transaction(txn) if OneApm::Manager.config[:'transaction_tracer.capture_attributes'] txn.custom_parameters else {} end end |
#enabled? ⇒ Boolean
65 66 67 |
# File 'lib/one_apm/collector/containers/transaction_sampler.rb', line 65 def enabled? OneApm::Manager.config[:'transaction_tracer.enabled'] end |
#harvest! ⇒ Object
Gather transaction traces that we’d like to transmit to the server.
283 284 285 286 287 288 289 290 |
# File 'lib/one_apm/collector/containers/transaction_sampler.rb', line 283 def harvest! return [] unless enabled? samples = @samples_lock.synchronize do @last_sample = nil harvest_from_sample_buffers end prepare_samples(samples) end |
#harvest_from_sample_buffers ⇒ Object
320 321 322 323 324 325 326 327 |
# File 'lib/one_apm/collector/containers/transaction_sampler.rb', line 320 def harvest_from_sample_buffers # map + flatten hit mocking issues calling to_ary on 1.9.2. We only # want a single level flatten anyway, but, as you probably already # know, Ruby 1.8.6 :/ result = [] @sample_buffers.each { |buffer| result.concat(buffer.harvest_samples) } result.uniq end |
#ignore_transaction(state) ⇒ 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
161 162 163 164 |
# File 'lib/one_apm/collector/containers/transaction_sampler.rb', line 161 def ignore_transaction(state) builder = state.transaction_sample_builder builder.ignore_transaction if builder end |
#merge!(previous) ⇒ Object
305 306 307 308 309 310 311 |
# File 'lib/one_apm/collector/containers/transaction_sampler.rb', line 305 def merge!(previous) @samples_lock.synchronize do @sample_buffers.each do |buffer| buffer.store_previous(previous) end end end |
#notice_nosql(key, duration) ⇒ Object
Attaches an additional non-SQL query parameter to the current transaction trace segment.
This may be used for recording a query against a key-value store like memcached or redis.
This method should be used only by gem authors wishing to extend the Ruby agent to instrument uninstrumented key-value stores - it should generally not be called directly from application code.
265 266 267 268 |
# File 'lib/one_apm/collector/containers/transaction_sampler.rb', line 265 def notice_nosql(key, duration) builder = tl_builder notice_extra_data(builder, key, duration, :key) end |
#notice_nosql_statement(statement, duration) ⇒ Object
270 271 272 273 |
# File 'lib/one_apm/collector/containers/transaction_sampler.rb', line 270 def notice_nosql_statement(statement, duration) builder = tl_builder notice_extra_data(builder, statement, duration, :statement) end |
#notice_pop_frame(state, frame, time = Time.now) ⇒ Object
Informs the transaction sample builder about the end of a traced frame
94 95 96 97 98 99 |
# File 'lib/one_apm/collector/containers/transaction_sampler.rb', line 94 def notice_pop_frame(state, frame, time = Time.now) builder = state.transaction_sample_builder return unless builder raise "finished already???" if builder.sample.finished builder.trace_exit(frame, time.to_f) end |
#notice_push_frame(state, time = Time.now) ⇒ Object
This delegates to the builder to create a new open transaction segment for the frame, 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
82 83 84 85 86 87 88 89 90 91 |
# File 'lib/one_apm/collector/containers/transaction_sampler.rb', line 82 def notice_push_frame(state, time=Time.now) builder = state.transaction_sample_builder return unless builder segment = builder.trace_entry(time.to_f) if @dev_mode_sample_buffer @dev_mode_sample_buffer.visit_segment(segment) end segment end |
#notice_sql(sql, config, duration, state = nil, &explainer) ⇒ Object
Attaches an SQL query on the current transaction trace segment.
This method should be used only by gem authors wishing to extend the Ruby agent to instrument new database interfaces - it should generally not be called directly from application code.
228 229 230 231 232 233 234 235 236 237 |
# File 'lib/one_apm/collector/containers/transaction_sampler.rb', line 228 def notice_sql(sql, config, duration, state=nil, &explainer) # some statements (particularly INSERTS with large BLOBS # may be very large; we should trim them to a maximum usable length state ||= OneApm::TransactionState.tl_get builder = state.transaction_sample_builder if state.is_sql_recorded? statement = build_database_statement(sql, config, explainer) notice_extra_data(builder, statement, duration, :sql) end end |
#notice_transaction_cpu_time(state, cpu_time) ⇒ Object
Sets the CPU time used by a transaction, delegates to the builder
167 168 169 170 |
# File 'lib/one_apm/collector/containers/transaction_sampler.rb', line 167 def notice_transaction_cpu_time(state, cpu_time) builder = state.transaction_sample_builder builder.set_transaction_cpu_time(cpu_time) if builder end |
#on_finishing_transaction(state, txn, time = Time.now, gc_time = nil) ⇒ 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 frames appended to it.
It sets various instance variables to the finished sample, depending on which settings are active. See ‘store_sample`
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 |
# File 'lib/one_apm/collector/containers/transaction_sampler.rb', line 116 def on_finishing_transaction(state, txn, time=Time.now, gc_time=nil) last_builder = state.transaction_sample_builder return unless last_builder && enabled? state.transaction_sample_builder = nil return if last_builder.ignored? last_builder.set_request_params(txn.filtered_params) last_builder.set_transaction_name(txn.best_name) last_builder.finish_trace(time.to_f, custom_parameters_from_transaction(txn)) last_sample = last_builder.sample last_sample.guid = txn.guid last_sample.set_custom_param(:gc_time, gc_time) if gc_time if state.is_cross_app? last_sample.set_custom_param(:'bw.trip_id', txn.cat_trip_id(state)) last_sample.set_custom_param(:'bw.path_hash', txn.cat_path_hash(state)) end if txn.is_synthetics_request? last_sample.set_custom_param(:'bw.synthetics_resource_id', txn.synthetics_resource_id) last_sample.set_custom_param(:'bw.synthetics_job_id', txn.synthetics_job_id) last_sample.set_custom_param(:'bw.synthetics_monitor_id', txn.synthetics_monitor_id) last_sample.synthetics_resource_id = txn.synthetics_resource_id end @samples_lock.synchronize do @last_sample = last_sample store_sample(@last_sample) @last_sample end end |
#on_start_transaction(state, start_time, uri = nil) ⇒ Object
69 70 71 72 73 74 75 |
# File 'lib/one_apm/collector/containers/transaction_sampler.rb', line 69 def on_start_transaction(state, start_time, uri=nil) if enabled? start_builder(state, start_time.to_f) builder = state.transaction_sample_builder builder.set_transaction_uri(uri) if builder end end |
#prepare_samples(samples) ⇒ Object
292 293 294 295 296 297 298 299 300 301 302 303 |
# File 'lib/one_apm/collector/containers/transaction_sampler.rb', line 292 def prepare_samples(samples) samples.select do |sample| begin sample.prepare_to_send! rescue => e OneApm::Manager.logger.error("Failed to prepare transaction trace. Error: ", e) false else true end end end |
#reset! ⇒ Object
reset samples without rebooting the web server (used by dev mode)
330 331 332 333 334 335 |
# File 'lib/one_apm/collector/containers/transaction_sampler.rb', line 330 def reset! @samples_lock.synchronize do @last_sample = nil @sample_buffers.each { |sample_buffer| sample_buffer.reset! } end end |
#start_builder(state, 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
343 344 345 346 347 348 349 |
# File 'lib/one_apm/collector/containers/transaction_sampler.rb', line 343 def start_builder(state, time=nil) if !enabled? || !state.is_transaction_traced? || !state.is_execution_traced? state.transaction_sample_builder = nil else state.transaction_sample_builder ||= OneApm::TransactionSampleBuilder.new(time) end end |
#store_sample(sample) ⇒ Object
151 152 153 154 155 |
# File 'lib/one_apm/collector/containers/transaction_sampler.rb', line 151 def store_sample(sample) @sample_buffers.each do |sample_buffer| sample_buffer.store(sample) end end |
#tl_builder ⇒ Object
The current thread-local transaction sample builder
352 353 354 |
# File 'lib/one_apm/collector/containers/transaction_sampler.rb', line 352 def tl_builder OneApm::TransactionState.tl_get.transaction_sample_builder end |