# encoding: utf-8 require 'sequel' unless defined?( Sequel ) require 'oneapm_rpm' unless defined?( OneApm ) require 'one_apm/agent/database/active_record_helper' module Sequel module OneApmInstrumentation include OneApm::Support::MethodTracer, OneApm::Agent::Instrumentation::ActiveRecordHelper # Instrument all queries that go through #execute_query. def log_yield(sql, args=nil) state = OneApm::TransactionState.tl_get return super unless state.is_execution_traced? t0 = Time.now rval = super t1 = Time.now begin duration = t1 - t0 record_metrics(sql, args, duration) notice_sql(state, sql, args, t0, t1) rescue => err OneApm::Manager.logger.debug "while recording metrics for Sequel", err end return rval end def generate_metrics(operation, config = {}) @product ||= database_info(config) metric_for(@product, operation).compact end # Record metrics for the specified +sql+ and +args+ using the specified # +duration+. def record_metrics(sql, args, duration) operation = operator_metric_for(sql, args) engine = OneApm::Manager.agent.stats_engine base, *other_metrics = generate_metrics(operation, self.opts) engine.tl_record_scoped_and_unscoped_metrics(base, other_metrics, duration) end THREAD_SAFE_CONNECTION_POOL_CLASSES = [ (defined?(::Sequel::ThreadedConnectionPool) && ::Sequel::ThreadedConnectionPool), ].compact.freeze # Record the given +sql+ within a new frame, using the given +start+ and # +finish+ times. def notice_sql(state, sql, args, start, finish) operation = operator_metric_for(sql, args) base, *other_metrics = generate_metrics(operation, self.opts) agent = OneApm::Manager.agent duration = finish - start stack = state.traced_method_stack begin frame = stack.push_frame(state, :sequel, start) explainer = Proc.new do |*| if THREAD_SAFE_CONNECTION_POOL_CLASSES.include?(self.pool.class) self[ sql ].explain else OneApm::Manager.logger.log_once(:info, :sequel_explain_skipped, "Not running SQL explains because Sequel is not in recognized multi-threaded mode") nil end end agent.transaction_sampler.notice_sql(sql, self.opts, duration, state, &explainer) agent.sql_sampler.notice_sql(sql, base, self.opts, duration, state, &explainer) ensure stack.pop_frame(state, frame, metric, finish) end end # Derive a primary database metric for the specified +sql+. def primary_metric_for(sql, _) return metric_for_sql(OneApm::Helper.correctly_encoded(sql)) end def operator_metric_for(sql, _) return operator_for_sql(OneApm::Helper.correctly_encoded(sql)) end end # module OneApmInstrumentation OneApm::Manager.logger.debug "Registering the :oneapm_instrumentation extension." Database.register_extension(:oneapm_instrumentation, OneApmInstrumentation) end # module Sequel