class Object
Constants
- OA_CAS_CLIENT_METHODS
- OA_CURB_MINIMUM_VERSION
- OA_EXCON_MIDDLEWARE_MINIMUM_VERSION
- OA_EXCON_MINIMUM_VERSION
We have two ways of instrumenting Excon:
-
For newer versions, use the middleware mechanism
Excon
exposes -
For older versions, monkey-patch Excon::Connection#request
OA_EXCON_MINIMUM_VERSION
is the minimum version we attempt to instrument at all.OA_EXCON_MIDDLEWARE_MINIMUM_VERSION
is the min version we use the newerinstrumentation for.
Note that middlewares were added to
Excon
prior to 0.19, but we don't use middleware-based instrumentation prior to that version because it didn't expose a way for middlewares to know about request failures.Why don't we use Excon.defaults? While this might seem a perfect fit, it unfortunately isn't suitable in current form. Someone might reasonably set the default instrumentor to something else after we install our instrumentation. Ideally, excon would itself conform to the subscribe interface of ActiveSupport::Notifications, so we could safely subscribe and not be clobbered by future subscribers, but alas, it does not yet.
-
- OA_HTTPCLIENT_MINIMUM_VERSION
Public Class Methods
# File lib/one_apm/inst/background_job/resque.rb, line 58 def self.new(*args) super(*args).extend OneApm::Agent::Instrumentation::ResqueInstrumentationInstaller end
Public Instance Methods
# File lib/one_apm/inst/nosql/redis.rb, line 74 def _send_to_one_apm(args, elapsed) if OneApm::Manager.config[:"transaction_tracer.record_sql"] == "obfuscated" args.map! do |arg| if arg.empty? arg else [arg.first] + ["?"] * (arg.count - 1) end end end OneApm::Agent::Datastore.notice_statement(args.inspect, elapsed) end
# File lib/one_apm/inst/nosql/redis.rb, line 48 def call_pipelined_with_oneapm_trace(commands, *rest) # Report each command as a metric suffixed with _pipelined, so the # user can at least see what all the commands were. additional = commands.map do |c| name = c.kind_of?(Array) ? c[0] : c "Datastore/operation/#{oneapm_product}/#{name.to_s.downcase}_pipelined" end callback = proc do |result, metric, elapsed| _send_to_one_apm(commands, elapsed) additional.each do |additional_metric| OneApm::Support::MethodTracer.trace_execution_scoped(additional_metric) do # No-op, just getting them as placeholders in the trace tree end end end OneApm::Agent::Datastore.wrap(oneapm_product, 'pipelined', nil, callback) do call_pipelined_without_oneapm_trace commands, *rest end end
# File lib/one_apm/inst/framework/grape.rb, line 85 def call_with_one_apm(env) begin response = call_without_one_apm(env) ensure begin endpoint = env[::OneApm::Agent::Instrumentation::Grape::OA_API_ENDPOINT] ::OneApm::Agent::Instrumentation::Grape.handle_transaction(endpoint, self.class.name) rescue => e OneApm::Manager.logger.warn("Error in Grape instrumentation", e) end end response end
# File lib/one_apm/inst/nosql/redis.rb, line 30 def call_with_oneapm_trace(*args, &blk) method_name = args[0].is_a?(Array) ? args[0][0] : args[0] filtered_command = filter method_name callback = proc do |result, metric, elapsed| _send_to_one_apm(args, elapsed) end OneApm::Agent::Datastore.wrap(oneapm_product, filtered_command, nil, callback) do call_without_oneapm_trace(*args, &blk) end end
# File lib/one_apm/inst/nosql/mongo.rb, line 99 def ensure_index_with_one_apm_trace(spec, opts = {}, &block) metrics = one_apm_generate_metrics(:ensureIndex) trace_execution_scoped(metrics) do t0 = Time.now result = OneApm::Manager.disable_all_tracing do ensure_index_without_one_apm_trace(spec, opts, &block) end spec = case spec when Array Hash[spec] when String, Symbol { spec => 1 } else spec.dup end one_apm_notice_statement(t0, spec, :ensureIndex) result end end
# File lib/one_apm/inst/nosql/redis.rb, line 87 def filter command need_filtered = OneApm::Manager.config[:'redis.ignore_commands'].map(&:to_s).include?(command.to_s) if need_filtered OneApm::Manager.logger.debug "Redis filter command: #{command}" nil else command end end
# File lib/one_apm/inst/background_job/resque.rb, line 69 def fork_with_oneapm(*args, &block) OneApm::Manager.agent.synchronize_with_harvest do fork_without_oneapm(*args, &block) # Reached in parent, not expected in the child since Resque # uses the block form of fork end end
# File lib/one_apm/inst/nosql/mongo.rb, line 36 def hook_instrument_method(target_class) target_class.class_eval do include OneApm::Support::MethodTracer # It's key that this method eats all exceptions, as it rests between the # Mongo operation the user called and us returning them the data. Be safe! def one_apm_notice_statement(t0, payload, name) statement = OneApm::Agent::Datastore::Mongo::StatementFormatter.format(payload, name) if statement OneApm::Manager.agent.transaction_sampler.notice_nosql_statement(statement, (Time.now - t0).to_f) end rescue => e OneApm::Manager.logger.debug("Exception during Mongo statement gathering", e) end def one_apm_generate_metrics(operation, payload = nil) payload ||= { :collection => self.name, :database => self.db.name } OneApm::Agent::Datastore::Mongo::MetricTranslator.metrics_for(operation, payload) end def instrument_with_one_apm_trace(name, payload = {}, &block) metrics = one_apm_generate_metrics(name, payload) trace_execution_scoped(metrics) do t0 = Time.now result = OneApm::Manager.disable_all_tracing do instrument_without_one_apm_trace(name, payload, &block) end one_apm_notice_statement(t0, payload, name) result end end alias_method :instrument_without_one_apm_trace, :instrument alias_method :instrument, :instrument_with_one_apm_trace end end
# File lib/one_apm/inst/nosql/mongo.rb, line 29 def hook_instrument_methods hook_instrument_method(::Mongo::Collection) hook_instrument_method(::Mongo::Connection) hook_instrument_method(::Mongo::Cursor) hook_instrument_method(::Mongo::CollectionWriter) if defined?(::Mongo::CollectionWriter) end
# File lib/one_apm/inst/background_job/delayed_job.rb, line 20 def initialize_with_one_apm(*args) initialize_without_one_apm(*args) worker_name = case when self.respond_to?(:name) then self.name when self.class.respond_to?(:default_name) then self.class.default_name end OneApm::DelayedJobInjection.worker_name = worker_name if defined?(::Delayed::Job) && ::Delayed::Job.method_defined?(:invoke_job) install_oneapm_job_tracer OneApm::Probe.instance.init_plugin :dispatcher => :delayed_job else OneApm::Manager.logger.warn("Did not find a Delayed::Job class responding to invoke_job, aborting DJ instrumentation") end end
# File lib/one_apm/inst/http_clients/excon.rb, line 42 def install_excon_instrumentation(excon_version) require 'one_apm/agent/cross_app/cross_app_tracing' require 'one_apm/support/http_clients/excon_wrappers' if excon_version >= OA_EXCON_MIDDLEWARE_MINIMUM_VERSION install_middleware_excon_instrumentation else install_legacy_excon_instrumentation end end
# File lib/one_apm/inst/http_clients/excon.rb, line 65 def install_legacy_excon_instrumentation OneApm::Manager.logger.info 'Installing legacy Excon instrumentation' require 'one_apm/inst/http_clients/excon/connection' ::Excon::Connection.install_oneapm_instrumentation end
# File lib/one_apm/inst/http_clients/excon.rb, line 53 def install_middleware_excon_instrumentation OneApm::Manager.logger.info 'Installing middleware-based Excon instrumentation' require 'one_apm/inst/http_clients/excon/middleware' defaults = Excon.defaults if defaults[:middlewares] defaults[:middlewares] << ::Excon::Middleware::OneApmCrossAppTracing else OneApm::Manager.logger.warn("Did not find :middlewares key in Excon.defaults, skipping Excon instrumentation") end end
# File lib/one_apm/inst/nosql/mongo2.rb, line 111 def install_mongo_command_subscriber require 'one_apm/agent/datastore/metric_helper' require 'one_apm/agent/datastore/mongo/command_formatter' ::Mongo::Monitoring::Global.subscribe( ::Mongo::Monitoring::COMMAND, OneApm::Agent::Instrumentation::MongoCommandSubscriber.new ) end
# File lib/one_apm/inst/nosql/mongo.rb, line 20 def install_mongo_instrumentation require 'one_apm/agent/datastore/mongo/metric_translator' require 'one_apm/agent/datastore/mongo/statement_formatter' hook_instrument_methods instrument_save instrument_ensure_index end
# File lib/one_apm/inst/background_job/delayed_job.rb, line 39 def install_oneapm_job_tracer Delayed::Job.class_eval do include OneApm::Agent::Instrumentation::TransactionBase if self.instance_methods.include?('name') || self.instance_methods.include?(:name) add_transaction_tracer "invoke_job", :category => 'OtherTransaction/DelayedJob', :path => '#{self.name}' else add_transaction_tracer "invoke_job", :category => 'OtherTransaction/DelayedJob' end end end
# File lib/one_apm/inst/framework/grape.rb, line 83 def instrument_call ::Grape::API.class_eval do def call_with_one_apm(env) begin response = call_without_one_apm(env) ensure begin endpoint = env[::OneApm::Agent::Instrumentation::Grape::OA_API_ENDPOINT] ::OneApm::Agent::Instrumentation::Grape.handle_transaction(endpoint, self.class.name) rescue => e OneApm::Manager.logger.warn("Error in Grape instrumentation", e) end end response end alias_method :call_without_one_apm, :call alias_method :call, :call_with_one_apm end end
# File lib/one_apm/inst/nosql/mongo.rb, line 97 def instrument_ensure_index ::Mongo::Collection.class_eval do def ensure_index_with_one_apm_trace(spec, opts = {}, &block) metrics = one_apm_generate_metrics(:ensureIndex) trace_execution_scoped(metrics) do t0 = Time.now result = OneApm::Manager.disable_all_tracing do ensure_index_without_one_apm_trace(spec, opts, &block) end spec = case spec when Array Hash[spec] when String, Symbol { spec => 1 } else spec.dup end one_apm_notice_statement(t0, spec, :ensureIndex) result end end alias_method :ensure_index_without_one_apm_trace, :ensure_index alias_method :ensure_index, :ensure_index_with_one_apm_trace end end
# File lib/one_apm/inst/nosql/mongo.rb, line 76 def instrument_save ::Mongo::Collection.class_eval do def save_with_one_apm_trace(doc, opts = {}, &block) metrics = one_apm_generate_metrics(:save) trace_execution_scoped(metrics) do t0 = Time.now result = OneApm::Manager.disable_all_tracing do save_without_one_apm_trace(doc, opts, &block) end one_apm_notice_statement(t0, doc, :save) result end end alias_method :save_without_one_apm_trace, :save alias_method :save, :save_with_one_apm_trace end end
# File lib/one_apm/inst/nosql/mongo.rb, line 56 def instrument_with_one_apm_trace(name, payload = {}, &block) metrics = one_apm_generate_metrics(name, payload) trace_execution_scoped(metrics) do t0 = Time.now result = OneApm::Manager.disable_all_tracing do instrument_without_one_apm_trace(name, payload, &block) end one_apm_notice_statement(t0, payload, name) result end end
# File lib/one_apm/inst/rails3/action_controller.rb, line 160 def instrument_with_oneapm(name, payload = {}, &block) identifier = payload[:identifier] scope_name = "View/#{OneApm::Agent::Instrumentation::Rails3::ActionView::OneApm.template_metric(identifier)}/Partial" trace_execution_scoped(scope_name) do instrument_without_oneapm(name, payload, &block) end end
# File lib/one_apm/inst/dispatcher/puma.rb, line 6 def oa_puma_hooks Puma.cli_config.options[:worker_boot] || Puma.cli_config.options[:before_worker_boot] end
# File lib/one_apm/inst/nosql/mongo.rb, line 51 def one_apm_generate_metrics(operation, payload = nil) payload ||= { :collection => self.name, :database => self.db.name } OneApm::Agent::Datastore::Mongo::MetricTranslator.metrics_for(operation, payload) end
It's key that this method eats all exceptions, as it rests between the Mongo operation the user called and us returning them the data. Be safe!
# File lib/one_apm/inst/nosql/mongo.rb, line 42 def one_apm_notice_statement(t0, payload, name) statement = OneApm::Agent::Datastore::Mongo::StatementFormatter.format(payload, name) if statement OneApm::Manager.agent.transaction_sampler.notice_nosql_statement(statement, (Time.now - t0).to_f) end rescue => e OneApm::Manager.logger.debug("Exception during Mongo statement gathering", e) end
determine the path that is used in the metric name for the called controller action
# File lib/one_apm/inst/rails/action_controller.rb, line 108 def oneapm_metric_path(action_name_override = nil) action_part = action_name_override || action_name if action_name_override || self.class.action_methods.include?(action_part) "#{self.class.controller_path}/#{action_part}" else "#{self.class.controller_path}/(other)" end end
Make a note of an exception associated with the currently executing controller action. Note that this used to be available on Object
but we replaced that global method with OneApm::Agent#notice_error. Use that one outside of controller actions.
# File lib/one_apm/inst/rails/errors.rb, line 26 def oneapm_notice_error(exception, custom_params = {}) OneApm::Transaction.notice_error exception, :custom_params => custom_params, :request => request end
# File lib/one_apm/inst/nosql/redis.rb, line 25 def oneapm_product @product ||= OneApm::Agent::Datastore.oneapm_product("Redis", host, port) end
# File lib/one_apm/inst/rails/action_controller.rb, line 96 def perform_action_with_oneapm_trace_wrapper options = {} options[:params] = (respond_to?(:filter_parameters)) ? filter_parameters(params) : params perform_action_with_oneapm_trace(options) { perform_action_without_oneapm_trace } end
# File lib/one_apm/inst/http_clients/thrift.rb, line 107 def receive_message_with_oneapm(result_klass) begin op = operator(result_klass) op_started = started_time(op) result = receive_message_without_oneapm(result_klass) state = OneApm::TransactionState.tl_get OneApm::Agent::CrossAppTracingMessage.finish_trace(state, op_started, segment, rpc_request, metrics(op)) result rescue => e OneApm::Manager.logger.error "Thrift receive_message error: #{e}" ensure rpc_reset! result end end
# File lib/one_apm/inst/rails3/action_controller.rb, line 100 def render_with_oneapm(*args, &block) options = if @virtual_path && @virtual_path.starts_with?('/') # file render {:file => true } else {} end template_metric = OneApm::Agent::Instrumentation::Rails3::ActionView::OneApm.template_metric(@identifier, options) render_type = OneApm::Agent::Instrumentation::Rails3::ActionView::OneApm.render_type(@identifier) str = "View/#{template_metric}/#{render_type}" trace_execution_scoped str do render_without_oneapm(*args, &block) end end
# File lib/one_apm/inst/rails/errors.rb, line 30 def rescue_action_with_oneapm_trace(exception) rescue_action_without_oneapm_trace exception OneApm::Transaction.notice_error exception, :request => request end
# File lib/one_apm/inst/nosql/mongo.rb, line 78 def save_with_one_apm_trace(doc, opts = {}, &block) metrics = one_apm_generate_metrics(:save) trace_execution_scoped(metrics) do t0 = Time.now result = OneApm::Manager.disable_all_tracing do save_without_one_apm_trace(doc, opts, &block) end one_apm_notice_statement(t0, doc, :save) result end end
# File lib/one_apm/inst/background_job/rabbitmq.rb, line 28 def send_frame_with_oneapm(frame, signal_activity = true) perform_action_with_oneapm_trace(trace_args(frame)) do send_frame_without_oneapm(frame, signal_activity) end end
# File lib/one_apm/inst/background_job/rabbitmq.rb, line 44 def send_frame_without_timeout_with_oneapm(frame, signal_activity = true) perform_action_with_oneapm_trace(trace_args(frame)) do send_frame_without_timeout_wihout_oneapm(frame, signal_activity) end end
# File lib/one_apm/inst/background_job/rabbitmq.rb, line 36 def send_frameset_with_oneapm(frames, channel) perform_action_with_oneapm_trace(trace_args(frames[0])) do send_frameset_without_oneapm(frames, channel) end end
# File lib/one_apm/inst/http_clients/thrift.rb, line 79 def send_message_with_oneapm(name, args_class, args = {}) state = OneApm::TransactionState.tl_get t0 = Time.now self.segment = OneApm::Agent::CrossAppTracingMessage.start_trace(state, t0, rpc_request) operations[name] = { :started_time => t0 } send_message_without_oneapm(name, args_class, args) end
# File lib/one_apm/inst/http_clients/thrift.rb, line 89 def send_oneway_message_with_oneapm(name, args_class, args = {}) begin state = OneApm::TransactionState.tl_get t0 = Time.now segment = OneApm::Agent::CrossAppTracingMessage.start_trace(state, t0, rpc_request) result = send_oneway_message_without_oneapm(name, args_class, args) OneApm::Agent::CrossAppTracingMessage.finish_trace(state, t0, segment, rpc_request, metrics(name)) result rescue => e OneApm::Manager.logger.error "Thrift receive_message error: #{e}" ensure rpc_reset! result end end
# File lib/one_apm/inst/orm/sequel.rb, line 14 def supported_sequel_version? Sequel.const_defined?( :MAJOR ) && ( Sequel::MAJOR > 3 || Sequel::MAJOR == 3 && Sequel::MINOR >= 37 ) end
# File lib/one_apm/inst/background_job/rabbitmq.rb, line 19 def trace_args(frame) class_name = frame.respond_to?(:method_class) ? frame.method_class.name : frame.class.name.downcase { :name => 'perform', :class_name => class_name, :category => 'OtherTransaction/RabbitMQ' } end