# encoding: utf-8 require 'ting_yun/agent/transaction/traced_method_stack' require 'ting_yun/agent/transaction/transaction_timings' module TingYun module Agent # This is THE location to store thread local information during a transaction # Need a new piece of data? Add a method here, NOT a new thread local variable. class TransactionState # Request data attr_accessor :transaction_sample_builder attr_reader :current_transaction, :traced_method_stack, :timings # Sql Sampler Transaction Data attr_accessor :sql_sampler_transaction_data, :client_transaction_id, :client_tingyun_id_secret, :client_req_id, :thrift_return_data, :extenel_req_id, :externel_time def self.tl_get tl_state_for(Thread.current) end # This method should only be used by TransactionState for access to the # current thread's state or to provide read-only accessors for other threads # # If ever exposed, this requires additional synchronization def self.tl_state_for(thread) state = thread[:tingyun_transaction_state] if state.nil? state = TransactionState.new thread[:tingyun_transaction_state] = state end state end def initialize @untraced = [] @current_transaction = nil @traced_method_stack = TingYun::Agent::TracedMethodStack.new @record_tt = nil end # This starts the timer for the transaction. def reset(transaction=nil) # We purposefully don't reset @untraced, @record_tt and @record_sql,@client_transaction_id,@client_tingyun_id_secret # since those are managed by TingYun::Agent.disable_* calls explicitly # and (more importantly) outside the scope of a transaction @current_transaction = transaction @traced_method_stack.clear @transaction_sample_builder = nil @sql_sampler_transaction_data = nil @thrift_return_data = nil @timings = nil @client_req_id = nil end # TT's and SQL attr_accessor :record_tt, :record_sql attr_accessor :untraced def push_traced(should_trace) @untraced << should_trace end def pop_traced @untraced.pop if @untraced end def execution_traced? @untraced.nil? || @untraced.last != false end def sql_recorded? @record_sql != false end def transaction_traced? @record_tt != false end def request_guid return nil unless current_transaction current_transaction.guid end def init_sql_transaction(obj) @sql_sampler_transaction_data = obj end def self.process_thrift_data(data) state = tl_get state.thrift_return_data = data builder = state.transaction_sample_builder builder.set_txId_and_txData(state.request_guid, data) if builder end def save_referring_transaction_info(data) data = Array(data) @client_tingyun_id_secret = data.shift data.each do |e| if m = e.match(/x=/) @client_transaction_id = m.post_match elsif m = e.match(/r=/) @client_req_id = m.post_match elsif m = e.match(/e=/) @extenel_req_id = m.post_match elsif m = e.match(/s=/) @externel_time = m.post_match end end end def timings @timings ||= TingYun::Agent::TransactionTimings.new(transaction_queue_time, transaction_start_time) end def transaction_start_time current_transaction.nil? ? 0.0 : current_transaction.start_time end def transaction_queue_time current_transaction.nil? ? 0.0 : current_transaction.apdex.queue_time end def transaction_name current_transaction.nil? ? nil : current_transaction.best_name end def trace_id transaction_sample_builder.nil? ? nil : transaction_sample_builder.trace.guid end def same_account? server_info = TingYun::Agent.config[:tingyunIdSecret].split('|') client_info = (@client_tingyun_id_secret || '').split('|') if server_info[0] && !server_info[0].empty? && server_info[0] == client_info[0] return true else return false end end # if you wanna call the method, you must make sure current_transaction is not nil at first # if current_transaction # add_custom_params(:key1,:value1) # add_custom_params(:key2,:value2) # end # public api def add_custom_params(key, value) current_transaction.attributes.add_custom_params(key, value) end # same to add_custom_params def merge_request_parameters(hash) current_transaction.attributes.merge_request_parameters(hash) end end end end