# encoding: utf-8 module OneApm class Transaction class << self def start_new_transaction(state, category, options) txn = Transaction.new(category, options) state.reset(txn) txn.start(state) txn end def start(state, category, options) category ||= :controller txn = state.current_transaction if txn txn.create_nested_frame(state, category, options) else txn = start_new_transaction(state, category, options) end txn rescue => e OneApm::Manager.logger.error("Exception during Transaction.start", e) nil end def stop(state, end_time=Time.now) txn = state.current_transaction if txn.nil? OneApm::Manager.logger.error(Transaction::FAILED_TO_STOP_MESSAGE) return end nested_frame = txn.frame_stack.pop if txn.frame_stack.empty? txn.stop(state, end_time, nested_frame) state.reset else nested_name = nested_transaction_name(nested_frame.name) if nested_name.start_with?(Transaction::MIDDLEWARE_PREFIX) summary_metrics = Transaction::MIDDLEWARE_SUMMARY_METRICS else summary_metrics = Transaction::EMPTY_SUMMARY_METRICS end options = Transaction::NESTED_TRACE_STOP_OPTIONS options = Transaction::TRACE_IGNORE_OPTIONS if txn.ignore_frame?(nested_name) OneApm::Support::MethodTracer::Helpers.trace_execution_scoped_footer( state, nested_frame.start_time.to_f, nested_name, summary_metrics, nested_frame, options, end_time.to_f) end :transaction_stopped rescue => e state.reset OneApm::Manager.logger.error("Exception during Transaction.stop", e) nil end def wrap(state, name, category, options = {}) Transaction.start(state, category, options.merge(:transaction_name => name)) begin yield rescue => e Transaction.notice_error(e) raise e ensure Transaction.stop(state) end end def nested_transaction_name(name) if name.start_with?(Transaction::WEB_TRANSACTION_PREFIX) || name.start_with?(Transaction::OTHER_TRANSACTION_PREFIX) "#{Transaction::SUBTRANSACTION_PREFIX}#{name}" else name end end # # * duration: response time # * failed: the request is failed or not # * apdex_s: satisfy # * apdex_t: tolerate # * apdex_f: frustrate # def apdex_bucket(duration, failed, apdex_t) case when failed :apdex_f # frustrate if request failed when duration <= apdex_t :apdex_s # satisfy if duration < tolerate when duration <= 4 * apdex_t :apdex_t # tolerate if duration < 4 * tolerate else :apdex_f # otherwise frustrate end end # Make a safe attempt to get the URI, without the host and query string. def uri_from_request(req) approximate_uri = case when req.respond_to?(:fullpath ) then req.fullpath when req.respond_to?(:path ) then req.path when req.respond_to?(:request_uri) then req.request_uri when req.respond_to?(:uri ) then req.uri when req.respond_to?(:url ) then req.url end return approximate_uri[%r{^(https?://.*?)?(/[^?]*)}, 2] || '/' if approximate_uri end # Make a safe attempt to get the referer from a request object, generally successful when # it's a Rack request. def referer_from_request(req) if req && req.respond_to?(:referer) req.referer.to_s.split('?').first end end end end end