# encoding: utf-8 module OneApm module Support module MethodTracer module Helpers MAX_ALLOWED_METRIC_DURATION = 1_000_000_000 # roughly 31 years extend self def record_metrics(state, first_name, other_names, duration, exclusive, options) record_scoped_metric = options.has_key?(:scoped_metric) ? options[:scoped_metric] : true stats_engine = OneApm::Manager.agent.stats_engine if record_scoped_metric stats_engine.record_scoped_and_unscoped_metrics(state, first_name, other_names, duration, exclusive) else metrics = [first_name].concat(other_names) stats_engine.record_unscoped_metrics(state, metrics, duration, exclusive) end end def log_errors(code_area) yield rescue => e OneApm::Manager.logger.error("Caught exception in #{code_area}.", e) end def trace_execution_scoped_header(state, t0) log_errors(:trace_execution_scoped_header) do stack = state.traced_method_stack stack.push_frame(state, :method_tracer, t0) end end def trace_execution_scoped_footer(state, t0, first_name, metric_names, expected_frame, options, t1 = Time.now.to_f) log_errors(:trace_method_execution_footer) do return unless expected_frame stack = state.traced_method_stack create_metrics = options.has_key?(:metric) ? options[:metric] : true frame = stack.pop_frame(state, expected_frame, first_name, t1, create_metrics) return unless create_metrics duration = t1 - t0 exclusive = duration - frame.children_time if duration >= MAX_ALLOWED_METRIC_DURATION OneApm::Manager.logger.log_once(:warn, "too_huge_metric:#{first_name}", "Ignoring metric #{first_name} with unacceptably large duration: #{duration} s") return end if duration < 0 OneApm::Manager.logger.log_once(:warn, "metric_duration_negative:#{first_name}", "Metric #{first_name} has negative duration: #{duration} s") end if exclusive < 0 OneApm::Manager.logger.log_once(:warn, "metric_exclusive_negative:#{first_name}", "Metric #{first_name} has negative exclusive time: duration = #{duration} s, child_time = #{frame.children_time}") end record_metrics(state, first_name, metric_names, duration, exclusive, options) end end def trace_execution_scoped(metric_names, options={}) state = OneApm::TransactionState.tl_get return yield unless state.is_execution_traced? metric_names = Array(metric_names) first_name = metric_names.shift return yield unless first_name additional_metrics_callback = options[:additional_metrics_callback] start_time = Time.now.to_f expected_scope = trace_execution_scoped_header(state, start_time) begin result = yield metric_names += Array(additional_metrics_callback.call) if additional_metrics_callback result ensure trace_execution_scoped_footer(state, start_time, first_name, metric_names, expected_scope, options) end end end end end end