oneapm_rpm 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +30 -0
- data/.rubocop.yml +725 -0
- data/Gemfile +3 -0
- data/Guardfile +7 -0
- data/LICENSE +1 -0
- data/README.md +3 -0
- data/config/cert/cacert.pem +1177 -0
- data/config/database.yml +5 -0
- data/lib/initializers/goliath.rb +11 -0
- data/lib/initializers/other.rb +1 -0
- data/lib/initializers/rails.rb +15 -0
- data/lib/one_apm/agent.rb +253 -0
- data/lib/one_apm/agent/agent.rb +283 -0
- data/lib/one_apm/agent/agent/connect.rb +175 -0
- data/lib/one_apm/agent/agent/container_data_manager.rb +218 -0
- data/lib/one_apm/agent/agent/forkable_dispatcher_functions.rb +96 -0
- data/lib/one_apm/agent/agent/helpers.rb +45 -0
- data/lib/one_apm/agent/agent/start.rb +226 -0
- data/lib/one_apm/agent/agent/start_worker_thread.rb +148 -0
- data/lib/one_apm/agent/busy_calculator.rb +115 -0
- data/lib/one_apm/agent/cross_app/cross_app_monitor.rb +181 -0
- data/lib/one_apm/agent/cross_app/cross_app_tracing.rb +336 -0
- data/lib/one_apm/agent/database.rb +308 -0
- data/lib/one_apm/agent/database/active_record_helper.rb +80 -0
- data/lib/one_apm/agent/database/obfuscation_helpers.rb +76 -0
- data/lib/one_apm/agent/database/obfuscator.rb +78 -0
- data/lib/one_apm/agent/database/postgres_explain_obfuscator.rb +45 -0
- data/lib/one_apm/agent/datastores.rb +175 -0
- data/lib/one_apm/agent/datastores/metric_helper.rb +83 -0
- data/lib/one_apm/agent/datastores/mongo.rb +27 -0
- data/lib/one_apm/agent/datastores/mongo/metric_translator.rb +189 -0
- data/lib/one_apm/agent/datastores/mongo/obfuscator.rb +37 -0
- data/lib/one_apm/agent/datastores/mongo/statement_formatter.rb +51 -0
- data/lib/one_apm/agent/event/event_listener.rb +40 -0
- data/lib/one_apm/agent/event/event_loop.rb +191 -0
- data/lib/one_apm/agent/event/worker_loop.rb +97 -0
- data/lib/one_apm/agent/harvester.rb +48 -0
- data/lib/one_apm/agent/inbound_request_monitor.rb +30 -0
- data/lib/one_apm/agent/javascript_instrumentor.rb +186 -0
- data/lib/one_apm/agent/pipe/pipe_channel_manager.rb +275 -0
- data/lib/one_apm/agent/pipe/pipe_service.rb +81 -0
- data/lib/one_apm/agent/sampler.rb +55 -0
- data/lib/one_apm/agent/sampler_collection.rb +65 -0
- data/lib/one_apm/agent/samplers/cpu_sampler.rb +49 -0
- data/lib/one_apm/agent/samplers/delayed_job_sampler.rb +109 -0
- data/lib/one_apm/agent/samplers/memory_sampler.rb +144 -0
- data/lib/one_apm/agent/samplers/object_sampler.rb +22 -0
- data/lib/one_apm/agent/samplers/vm_sampler.rb +124 -0
- data/lib/one_apm/agent/synthetics_monitor.rb +48 -0
- data/lib/one_apm/agent/threading/agent_thread.rb +74 -0
- data/lib/one_apm/agent/threading/backtrace_node.rb +133 -0
- data/lib/one_apm/agent/threading/backtrace_service.rb +259 -0
- data/lib/one_apm/agent/threading/thread_profile.rb +155 -0
- data/lib/one_apm/collector/collector/helper.rb +139 -0
- data/lib/one_apm/collector/collector/http_connection.rb +254 -0
- data/lib/one_apm/collector/collector/server_methods.rb +71 -0
- data/lib/one_apm/collector/collector_service.rb +123 -0
- data/lib/one_apm/collector/commands/agent_command.rb +17 -0
- data/lib/one_apm/collector/commands/thread_profiler_session.rb +108 -0
- data/lib/one_apm/collector/commands/xray_session.rb +53 -0
- data/lib/one_apm/collector/commands/xray_session_collection.rb +156 -0
- data/lib/one_apm/collector/containers/agent_command_router.rb +153 -0
- data/lib/one_apm/collector/containers/custom_event_aggregator.rb +94 -0
- data/lib/one_apm/collector/containers/error_collector.rb +349 -0
- data/lib/one_apm/collector/containers/sql_sampler.rb +331 -0
- data/lib/one_apm/collector/containers/stats_engine.rb +34 -0
- data/lib/one_apm/collector/containers/transaction_event_aggregator.rb +249 -0
- data/lib/one_apm/collector/containers/transaction_sampler.rb +352 -0
- data/lib/one_apm/collector/containers/utilization_data.rb +36 -0
- data/lib/one_apm/collector/stats_engine/gc_profiler.rb +106 -0
- data/lib/one_apm/collector/stats_engine/metric_stats.rb +243 -0
- data/lib/one_apm/collector/stats_engine/stats_hash.rb +105 -0
- data/lib/one_apm/configuration.rb +429 -0
- data/lib/one_apm/configuration/autostart.rb +41 -0
- data/lib/one_apm/configuration/default_source.rb +1026 -0
- data/lib/one_apm/configuration/environment_source.rb +113 -0
- data/lib/one_apm/configuration/high_security_source.rb +56 -0
- data/lib/one_apm/configuration/manual_source.rb +13 -0
- data/lib/one_apm/configuration/server_source.rb +60 -0
- data/lib/one_apm/configuration/yaml_source.rb +134 -0
- data/lib/one_apm/errors/agent_errors.rb +26 -0
- data/lib/one_apm/errors/internal_agent_error.rb +16 -0
- data/lib/one_apm/errors/noticed_error.rb +79 -0
- data/lib/one_apm/frameworks/external.rb +15 -0
- data/lib/one_apm/frameworks/rails.rb +103 -0
- data/lib/one_apm/frameworks/rails3.rb +37 -0
- data/lib/one_apm/frameworks/rails4.rb +21 -0
- data/lib/one_apm/frameworks/ruby.rb +21 -0
- data/lib/one_apm/frameworks/sinatra.rb +12 -0
- data/lib/one_apm/inst/3rd/active_merchant.rb +35 -0
- data/lib/one_apm/inst/3rd/acts_as_solr.rb +70 -0
- data/lib/one_apm/inst/3rd/authlogic.rb +23 -0
- data/lib/one_apm/inst/3rd/sunspot.rb +31 -0
- data/lib/one_apm/inst/background_job/active_job.rb +88 -0
- data/lib/one_apm/inst/background_job/delayed_job.rb +52 -0
- data/lib/one_apm/inst/background_job/delayed_job_injection.rb +8 -0
- data/lib/one_apm/inst/background_job/resque.rb +107 -0
- data/lib/one_apm/inst/background_job/sidekiq.rb +64 -0
- data/lib/one_apm/inst/dispatcher/passenger.rb +25 -0
- data/lib/one_apm/inst/dispatcher/rainbows.rb +23 -0
- data/lib/one_apm/inst/framework/grape.rb +94 -0
- data/lib/one_apm/inst/framework/padrino.rb +30 -0
- data/lib/one_apm/inst/framework/sinatra.rb +185 -0
- data/lib/one_apm/inst/framework/sinatra/ignorer.rb +50 -0
- data/lib/one_apm/inst/framework/sinatra/transaction_namer.rb +54 -0
- data/lib/one_apm/inst/http_clients/curb.rb +189 -0
- data/lib/one_apm/inst/http_clients/excon.rb +70 -0
- data/lib/one_apm/inst/http_clients/excon/connection.rb +31 -0
- data/lib/one_apm/inst/http_clients/excon/middleware.rb +55 -0
- data/lib/one_apm/inst/http_clients/httpclient.rb +44 -0
- data/lib/one_apm/inst/http_clients/net.rb +34 -0
- data/lib/one_apm/inst/http_clients/typhoeus.rb +76 -0
- data/lib/one_apm/inst/nosql/memcache.rb +134 -0
- data/lib/one_apm/inst/nosql/mongo.rb +126 -0
- data/lib/one_apm/inst/nosql/mongo_moped.rb +85 -0
- data/lib/one_apm/inst/nosql/redis.rb +83 -0
- data/lib/one_apm/inst/orm/active_record.rb +99 -0
- data/lib/one_apm/inst/orm/active_record_4.rb +28 -0
- data/lib/one_apm/inst/orm/data_mapper.rb +180 -0
- data/lib/one_apm/inst/orm/sequel.rb +47 -0
- data/lib/one_apm/inst/rack.rb +38 -0
- data/lib/one_apm/inst/rack/rack.rb +44 -0
- data/lib/one_apm/inst/rack/rack_builder.rb +51 -0
- data/lib/one_apm/inst/rails/action_controller.rb +118 -0
- data/lib/one_apm/inst/rails/action_web_service.rb +44 -0
- data/lib/one_apm/inst/rails/errors.rb +43 -0
- data/lib/one_apm/inst/rails3/action_controller.rb +172 -0
- data/lib/one_apm/inst/rails3/errors.rb +43 -0
- data/lib/one_apm/inst/rails4/action_controller.rb +27 -0
- data/lib/one_apm/inst/rails4/action_controller_subscriber.rb +121 -0
- data/lib/one_apm/inst/rails4/action_view.rb +23 -0
- data/lib/one_apm/inst/rails4/action_view_subscriber.rb +93 -0
- data/lib/one_apm/inst/rails4/active_record_subscriber.rb +96 -0
- data/lib/one_apm/inst/rails4/errors.rb +42 -0
- data/lib/one_apm/inst/rails_middleware.rb +40 -0
- data/lib/one_apm/inst/support/evented_subscriber.rb +98 -0
- data/lib/one_apm/inst/support/ignore_actions.rb +39 -0
- data/lib/one_apm/inst/support/queue_time.rb +76 -0
- data/lib/one_apm/inst/transaction_base.rb +405 -0
- data/lib/one_apm/logger/agent_logger.rb +206 -0
- data/lib/one_apm/logger/audit_logger.rb +78 -0
- data/lib/one_apm/logger/memory_logger.rb +50 -0
- data/lib/one_apm/logger/null_logger.rb +19 -0
- data/lib/one_apm/metrics/metric_data.rb +72 -0
- data/lib/one_apm/metrics/metric_spec.rb +82 -0
- data/lib/one_apm/metrics/stats.rb +173 -0
- data/lib/one_apm/probe.rb +16 -0
- data/lib/one_apm/probe/framework_loader.rb +53 -0
- data/lib/one_apm/probe/instance_methods.rb +105 -0
- data/lib/one_apm/probe/instrumentation.rb +60 -0
- data/lib/one_apm/rack/browser_monitoring.rb +144 -0
- data/lib/one_apm/rack/middleware_base.rb +27 -0
- data/lib/one_apm/rack/middleware_hooks.rb +17 -0
- data/lib/one_apm/rack/middleware_tracing.rb +81 -0
- data/lib/one_apm/rack/middleware_wrapper.rb +86 -0
- data/lib/one_apm/support/chained_call.rb +15 -0
- data/lib/one_apm/support/coerce.rb +81 -0
- data/lib/one_apm/support/collection_helper.rb +79 -0
- data/lib/one_apm/support/dotted_hash.rb +45 -0
- data/lib/one_apm/support/encoders.rb +34 -0
- data/lib/one_apm/support/environment_report.rb +127 -0
- data/lib/one_apm/support/event_buffer.rb +82 -0
- data/lib/one_apm/support/event_buffer/sampled_buffer.rb +45 -0
- data/lib/one_apm/support/event_buffer/sized_buffer.rb +21 -0
- data/lib/one_apm/support/event_buffer/synthetics_event_buffer.rb +40 -0
- data/lib/one_apm/support/helper.rb +49 -0
- data/lib/one_apm/support/hostname.rb +13 -0
- data/lib/one_apm/support/http_clients/curb_wrappers.rb +65 -0
- data/lib/one_apm/support/http_clients/excon_wrappers.rb +63 -0
- data/lib/one_apm/support/http_clients/httpclient_wrappers.rb +61 -0
- data/lib/one_apm/support/http_clients/net_http_wrappers.rb +48 -0
- data/lib/one_apm/support/http_clients/typhoeus_wrappers.rb +73 -0
- data/lib/one_apm/support/http_clients/uri_util.rb +39 -0
- data/lib/one_apm/support/json_marshaller.rb +68 -0
- data/lib/one_apm/support/json_wrapper.rb +130 -0
- data/lib/one_apm/support/language_support.rb +142 -0
- data/lib/one_apm/support/library_detection.rb +119 -0
- data/lib/one_apm/support/local_environment.rb +196 -0
- data/lib/one_apm/support/marshaller.rb +62 -0
- data/lib/one_apm/support/method_tracer.rb +334 -0
- data/lib/one_apm/support/method_tracer/helpers.rb +92 -0
- data/lib/one_apm/support/method_tracer/traced_method_stack.rb +103 -0
- data/lib/one_apm/support/obfuscator.rb +47 -0
- data/lib/one_apm/support/okjson.rb +601 -0
- data/lib/one_apm/support/parameter_filtering.rb +35 -0
- data/lib/one_apm/support/rules_engine.rb +56 -0
- data/lib/one_apm/support/rules_engine/replacement_rule.rb +80 -0
- data/lib/one_apm/support/rules_engine/segment_terms_rule.rb +46 -0
- data/lib/one_apm/support/server.rb +11 -0
- data/lib/one_apm/support/supported_versions.rb +257 -0
- data/lib/one_apm/support/system_info.rb +211 -0
- data/lib/one_apm/support/timer_lib.rb +29 -0
- data/lib/one_apm/support/version_number.rb +51 -0
- data/lib/one_apm/support/vm.rb +30 -0
- data/lib/one_apm/support/vm/jruby_vm.rb +38 -0
- data/lib/one_apm/support/vm/monotonic_gc_profiler.rb +43 -0
- data/lib/one_apm/support/vm/mri_vm.rb +85 -0
- data/lib/one_apm/support/vm/rubinius_vm.rb +129 -0
- data/lib/one_apm/support/vm/snapshot.rb +18 -0
- data/lib/one_apm/transaction.rb +336 -0
- data/lib/one_apm/transaction/class_methods.rb +132 -0
- data/lib/one_apm/transaction/instance_helpers.rb +82 -0
- data/lib/one_apm/transaction/metric_constants.rb +42 -0
- data/lib/one_apm/transaction/sample_buffer/force_persist_sample_buffer.rb +21 -0
- data/lib/one_apm/transaction/sample_buffer/slowest_sample_buffer.rb +21 -0
- data/lib/one_apm/transaction/sample_buffer/synthetics_sample_buffer.rb +21 -0
- data/lib/one_apm/transaction/sample_buffer/transaction_sample_buffer.rb +101 -0
- data/lib/one_apm/transaction/sample_buffer/xray_sample_buffer.rb +60 -0
- data/lib/one_apm/transaction/segment.rb +193 -0
- data/lib/one_apm/transaction/segment_summary.rb +51 -0
- data/lib/one_apm/transaction/thread_local_access.rb +73 -0
- data/lib/one_apm/transaction/transaction_analysis.rb +78 -0
- data/lib/one_apm/transaction/transaction_apdex.rb +20 -0
- data/lib/one_apm/transaction/transaction_cpu.rb +22 -0
- data/lib/one_apm/transaction/transaction_finish_append.rb +67 -0
- data/lib/one_apm/transaction/transaction_ignore.rb +33 -0
- data/lib/one_apm/transaction/transaction_jruby_functions.rb +40 -0
- data/lib/one_apm/transaction/transaction_metrics.rb +53 -0
- data/lib/one_apm/transaction/transaction_name.rb +90 -0
- data/lib/one_apm/transaction/transaction_namer.rb +49 -0
- data/lib/one_apm/transaction/transaction_sample.rb +204 -0
- data/lib/one_apm/transaction/transaction_sample_builder.rb +168 -0
- data/lib/one_apm/transaction/transaction_state.rb +149 -0
- data/lib/one_apm/transaction/transaction_summary.rb +28 -0
- data/lib/one_apm/transaction/transaction_synthetics.rb +40 -0
- data/lib/one_apm/transaction/transaction_timings.rb +54 -0
- data/lib/one_apm/version.rb +13 -0
- data/lib/oneapm_rpm.rb +16 -0
- data/lib/sequel/extensions/oneapm_instrumentation.rb +84 -0
- data/lib/sequel/plugins/oneapm_instrumentation.rb +66 -0
- data/oneapm.yml +135 -0
- data/oneapm_rpm.gemspec +58 -0
- metadata +474 -0
@@ -0,0 +1,62 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module OneApm
|
4
|
+
module Support
|
5
|
+
class Marshaller
|
6
|
+
|
7
|
+
# Used to wrap errors reported to agent by the collector
|
8
|
+
class CollectorError < StandardError; end
|
9
|
+
|
10
|
+
def parsed_error(error)
|
11
|
+
error_type = error['error_type']
|
12
|
+
error_message = error['message']
|
13
|
+
|
14
|
+
exception = case error_type
|
15
|
+
when 'OneApm::LicenseException'
|
16
|
+
OneApm::LicenseException.new(error_message)
|
17
|
+
when 'OneApm::ForceRestartException'
|
18
|
+
OneApm::ForceRestartException.new(error_message)
|
19
|
+
when 'OneApm::ForceDisconnectException'
|
20
|
+
OneApm::ForceDisconnectException.new(error_message)
|
21
|
+
else
|
22
|
+
OneApm::Support::Marshaller::CollectorError.new("#{error['error_type']}: #{error['message']}")
|
23
|
+
end
|
24
|
+
|
25
|
+
exception
|
26
|
+
end
|
27
|
+
|
28
|
+
def prepare(data, options={})
|
29
|
+
encoder = options[:encoder] || default_encoder
|
30
|
+
if data.respond_to?(:to_collector_array)
|
31
|
+
data.to_collector_array(encoder)
|
32
|
+
elsif data.kind_of?(Array)
|
33
|
+
data.map { |element| prepare(element, options) }
|
34
|
+
else
|
35
|
+
data
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def default_encoder
|
40
|
+
Encoders::Identity
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.human_readable?
|
44
|
+
false
|
45
|
+
end
|
46
|
+
|
47
|
+
protected
|
48
|
+
|
49
|
+
def return_value(data)
|
50
|
+
if data.respond_to?(:has_key?)
|
51
|
+
if data.has_key?('exception')
|
52
|
+
raise parsed_error(data['exception'])
|
53
|
+
elsif data.has_key?('return_value')
|
54
|
+
return data['return_value']
|
55
|
+
end
|
56
|
+
end
|
57
|
+
::OneApm::Agent.logger.debug("Unexpected response from collector: #{data}")
|
58
|
+
nil
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,334 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'one_apm/probe'
|
4
|
+
require 'one_apm/support/method_tracer/helpers'
|
5
|
+
|
6
|
+
module OneApm
|
7
|
+
module Support
|
8
|
+
# This module contains class methods added to support installing custom
|
9
|
+
# metric tracers and executing for individual metrics.
|
10
|
+
#
|
11
|
+
# == Examples
|
12
|
+
#
|
13
|
+
# When the agent initializes, it extends Module with these methods.
|
14
|
+
# However if you want to use the API in code that might get loaded
|
15
|
+
# before the agent is initialized you will need to require
|
16
|
+
# this file:
|
17
|
+
#
|
18
|
+
# require 'one_apm/support/method_tracer'
|
19
|
+
# class A
|
20
|
+
# include OneApm::Support::MethodTracer
|
21
|
+
# def process
|
22
|
+
# ...
|
23
|
+
# end
|
24
|
+
# add_method_tracer :process
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
# To instrument a class method:
|
28
|
+
#
|
29
|
+
# require 'one_apm/support/method_tracer'
|
30
|
+
# class An
|
31
|
+
# def self.process
|
32
|
+
# ...
|
33
|
+
# end
|
34
|
+
# class << self
|
35
|
+
# include OneApm::Support::MethodTracer
|
36
|
+
# add_method_tracer :process
|
37
|
+
# end
|
38
|
+
# end
|
39
|
+
#
|
40
|
+
# @api public
|
41
|
+
#
|
42
|
+
|
43
|
+
module MethodTracer
|
44
|
+
def self.included clazz
|
45
|
+
clazz.extend ClassMethods
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.extended clazz
|
49
|
+
clazz.extend ClassMethods
|
50
|
+
end
|
51
|
+
|
52
|
+
# Trace a given block with stats and keep track of the caller.
|
53
|
+
# See OneApm::Support::MethodTracer::ClassMethods#add_method_tracer for a description of the arguments.
|
54
|
+
# +metric_names+ is either a single name or an array of metric names.
|
55
|
+
# If more than one metric is passed, the +produce_metric+ option only applies to the first. The
|
56
|
+
# others are always recorded. Only the first metric is pushed onto the scope stack.
|
57
|
+
#
|
58
|
+
# Generally you pass an array of metric names if you want to record the metric under additional
|
59
|
+
# categories, but generally this *should never ever be done*. Most of the time you can aggregate
|
60
|
+
# on the server.
|
61
|
+
#
|
62
|
+
# @api public
|
63
|
+
#
|
64
|
+
def trace_execution_scoped(metric_names, options={}) #THREAD_LOCAL_ACCESS
|
65
|
+
OneApm::Support::MethodTracer::Helpers.trace_execution_scoped(metric_names, options) do
|
66
|
+
# Using an implicit block avoids object allocation for a &block param
|
67
|
+
yield
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# Trace a given block with stats assigned to the given metric_name. It does not
|
72
|
+
# provide scoped measurements, meaning whatever is being traced will not 'blame the
|
73
|
+
# Controller'--that is to say appear in the breakdown chart.
|
74
|
+
# This is code is inlined in #add_method_tracer.
|
75
|
+
# * <tt>metric_names</tt> is a single name or an array of names of metrics
|
76
|
+
#
|
77
|
+
# @api public
|
78
|
+
#
|
79
|
+
def trace_execution_unscoped(metric_names, options={}) #THREAD_LOCAL_ACCESS
|
80
|
+
return yield unless OneApm::Agent.tl_is_execution_traced?
|
81
|
+
t0 = Time.now
|
82
|
+
begin
|
83
|
+
yield
|
84
|
+
ensure
|
85
|
+
duration = (Time.now - t0).to_f # for some reason this is 3 usec faster than Time - Time
|
86
|
+
OneApm::Agent.instance.stats_engine.tl_record_unscoped_metrics(metric_names, duration)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# Defines methods used at the class level, for adding instrumentation
|
91
|
+
# @api public
|
92
|
+
module ClassMethods
|
93
|
+
# contains methods refactored out of the #add_method_tracer method
|
94
|
+
module AddMethodTracer
|
95
|
+
ALLOWED_KEYS = [:force, :metric, :push_scope, :code_header, :code_footer].freeze
|
96
|
+
|
97
|
+
DEPRECATED_KEYS = [:force, :scoped_metric_only, :deduct_call_time_from_parent].freeze
|
98
|
+
|
99
|
+
# raises an error when the
|
100
|
+
# OneApm::Support::MethodTracer::ClassMethods#add_method_tracer
|
101
|
+
# method is called with improper keys. This aids in
|
102
|
+
# debugging new instrumentation by failing fast
|
103
|
+
def check_for_illegal_keys!(method_name, options)
|
104
|
+
unrecognized_keys = options.keys - ALLOWED_KEYS
|
105
|
+
deprecated_keys = options.keys & DEPRECATED_KEYS
|
106
|
+
|
107
|
+
if unrecognized_keys.any?
|
108
|
+
raise "Unrecognized options when adding method tracer to #{method_name}: " +
|
109
|
+
unrecognized_keys.join(', ')
|
110
|
+
end
|
111
|
+
|
112
|
+
if deprecated_keys.any?
|
113
|
+
OneApm::Agent.logger.warn("Deprecated options when adding method tracer to #{method_name}: "+
|
114
|
+
deprecated_keys.join(', '))
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# validity checking - add_method_tracer must receive either
|
119
|
+
# push scope or metric, or else it would record no
|
120
|
+
# data. Raises an error if this is the case
|
121
|
+
def check_for_push_scope_and_metric(options)
|
122
|
+
unless options[:push_scope] || options[:metric]
|
123
|
+
raise "Can't add a tracer where push_scope is false and metric is false"
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
DEFAULT_SETTINGS = {:push_scope => true, :metric => true, :code_header => "", :code_footer => "" }.freeze
|
128
|
+
|
129
|
+
# Checks the provided options to make sure that they make
|
130
|
+
# sense. Raises an error if the options are incorrect to
|
131
|
+
# assist with debugging, so that errors occur at class
|
132
|
+
# construction time rather than instrumentation run time
|
133
|
+
def validate_options(method_name, options)
|
134
|
+
unless options.is_a?(Hash)
|
135
|
+
raise TypeError.new("Error adding method tracer to #{method_name}: provided options must be a Hash")
|
136
|
+
end
|
137
|
+
check_for_illegal_keys!(method_name, options)
|
138
|
+
options = DEFAULT_SETTINGS.merge(options)
|
139
|
+
check_for_push_scope_and_metric(options)
|
140
|
+
options
|
141
|
+
end
|
142
|
+
|
143
|
+
# Default to the class where the method is defined.
|
144
|
+
#
|
145
|
+
# Example:
|
146
|
+
# Foo.default_metric_name_code('bar') #=> "Custom/#{Foo.name}/bar"
|
147
|
+
def default_metric_name_code(method_name)
|
148
|
+
"Custom/#{self.name}/#{method_name.to_s}"
|
149
|
+
end
|
150
|
+
|
151
|
+
# Checks to see if the method we are attempting to trace
|
152
|
+
# actually exists or not. #add_method_tracer can't do
|
153
|
+
# anything if the method doesn't exist.
|
154
|
+
def oneapm_method_exists?(method_name)
|
155
|
+
exists = method_defined?(method_name) || private_method_defined?(method_name)
|
156
|
+
::OneApm::Agent.logger.error("Did not trace #{self.name}##{method_name} because that method does not exist") unless exists
|
157
|
+
exists
|
158
|
+
end
|
159
|
+
|
160
|
+
# Checks to see if we have already traced a method with a
|
161
|
+
# given metric by checking to see if the traced method
|
162
|
+
# exists. Warns the user if methods are being double-traced
|
163
|
+
# to help with debugging custom instrumentation.
|
164
|
+
def traced_method_exists?(method_name, metric_name_code)
|
165
|
+
exists = method_defined?(_traced_method_name(method_name, metric_name_code))
|
166
|
+
::OneApm::Agent.logger.error("Attempt to trace a method twice with the same metric: Method = #{method_name}, Metric Name = #{metric_name_code}") if exists
|
167
|
+
exists
|
168
|
+
end
|
169
|
+
|
170
|
+
# Returns a code snippet to be eval'd that skips tracing
|
171
|
+
# when the agent is not tracing execution. turns
|
172
|
+
# instrumentation into effectively one method call overhead
|
173
|
+
# when the agent is disabled
|
174
|
+
def assemble_code_header(method_name, metric_name_code, options)
|
175
|
+
header = "return #{_untraced_method_name(method_name, metric_name_code)}(*args, &block) unless OneApm::Agent.tl_is_execution_traced?\n"
|
176
|
+
header += options[:code_header].to_s
|
177
|
+
header
|
178
|
+
end
|
179
|
+
|
180
|
+
# returns an eval-able string that contains the traced
|
181
|
+
# method code used if the agent is not creating a scope for
|
182
|
+
# use in scoped metrics.
|
183
|
+
def method_without_push_scope(method_name, metric_name_code, options)
|
184
|
+
"def #{_traced_method_name(method_name, metric_name_code)}(*args, &block)
|
185
|
+
#{assemble_code_header(method_name, metric_name_code, options)}
|
186
|
+
t0 = Time.now
|
187
|
+
begin
|
188
|
+
#{_untraced_method_name(method_name, metric_name_code)}(*args, &block)\n
|
189
|
+
ensure
|
190
|
+
duration = (Time.now - t0).to_f
|
191
|
+
OneApm::Agent.record_metric(\"#{metric_name_code}\", duration)
|
192
|
+
#{options[:code_footer]}
|
193
|
+
end
|
194
|
+
end"
|
195
|
+
end
|
196
|
+
|
197
|
+
# returns an eval-able string that contains the tracing code
|
198
|
+
# for a fully traced metric including scoping
|
199
|
+
def method_with_push_scope(method_name, metric_name_code, options)
|
200
|
+
"def #{_traced_method_name(method_name, metric_name_code)}(*args, &block)
|
201
|
+
#{options[:code_header]}
|
202
|
+
result = ::OneApm::Support::MethodTracer::Helpers.trace_execution_scoped(\"#{metric_name_code}\",
|
203
|
+
:metric => #{options[:metric]}) do
|
204
|
+
#{_untraced_method_name(method_name, metric_name_code)}(*args, &block)
|
205
|
+
end
|
206
|
+
#{options[:code_footer]}
|
207
|
+
result
|
208
|
+
end"
|
209
|
+
end
|
210
|
+
|
211
|
+
# Decides which code snippet we should be eval'ing in this
|
212
|
+
# context, based on the options.
|
213
|
+
def code_to_eval(method_name, metric_name_code, options)
|
214
|
+
options = validate_options(method_name, options)
|
215
|
+
if options[:push_scope]
|
216
|
+
method_with_push_scope(method_name, metric_name_code, options)
|
217
|
+
else
|
218
|
+
method_without_push_scope(method_name, metric_name_code, options)
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
include AddMethodTracer
|
223
|
+
|
224
|
+
# Add a method tracer to the specified method.
|
225
|
+
#
|
226
|
+
# By default, this will cause invocations of the traced method to be
|
227
|
+
# recorded in transaction traces, and in a metric named after the class
|
228
|
+
# and method. It will also make the method show up in transaction-level
|
229
|
+
# breakdown charts and tables.
|
230
|
+
#
|
231
|
+
# === Overriding the metric name
|
232
|
+
#
|
233
|
+
# +metric_name_code+ is a string that is eval'd to get the name of the
|
234
|
+
# metric associated with the call, so if you want to use interpolation
|
235
|
+
# evaluated at call time, then single quote the value like this:
|
236
|
+
#
|
237
|
+
# add_method_tracer :foo, 'Custom/#{self.class.name}/foo'
|
238
|
+
#
|
239
|
+
# This would name the metric according to the class of the runtime
|
240
|
+
# intance, as opposed to the class where +foo+ is defined.
|
241
|
+
#
|
242
|
+
# If not provided, the metric name will be <tt>Custom/ClassName/method_name</tt>.
|
243
|
+
#
|
244
|
+
# @param [Symbol] method_name the name of the method to trace
|
245
|
+
# @param [String] metric_name_code the metric name to record calls to
|
246
|
+
# the traced method under. This may be either a static string, or Ruby
|
247
|
+
# code to be evaluated at call-time in order to determine the metric
|
248
|
+
# name dynamically.
|
249
|
+
# @param [Hash] options additional options controlling how the method is
|
250
|
+
# traced.
|
251
|
+
# @option options [Boolean] :push_scope (true) If false, the traced method will
|
252
|
+
# not appear in transaction traces or breakdown charts, and it will
|
253
|
+
# only be visible in custom dashboards.
|
254
|
+
# @option options [Boolean] :metric (true) If false, the traced method will
|
255
|
+
# only appear in transaction traces, but no metrics will be recorded
|
256
|
+
# for it.
|
257
|
+
# @option options [String] :code_header ('') Ruby code to be inserted and run
|
258
|
+
# before the tracer begins timing.
|
259
|
+
# @option options [String] :code_footer ('') Ruby code to be inserted and run
|
260
|
+
# after the tracer stops timing.
|
261
|
+
#
|
262
|
+
# @example
|
263
|
+
# add_method_tracer :foo
|
264
|
+
#
|
265
|
+
# # With a custom metric name
|
266
|
+
# add_method_tracer :foo, 'Custom/#{self.class.name}/foo'
|
267
|
+
#
|
268
|
+
# # Instrument foo only for custom dashboards (not in transaction
|
269
|
+
# # traces or breakdown charts)
|
270
|
+
# add_method_tracer :foo, 'Custom/foo', :push_scope => false
|
271
|
+
#
|
272
|
+
# # Instrument foo in transaction traces only
|
273
|
+
# add_method_tracer :foo, 'Custom/foo', :metric => false
|
274
|
+
#
|
275
|
+
# @api public
|
276
|
+
#
|
277
|
+
def add_method_tracer(method_name, metric_name_code=nil, options = {})
|
278
|
+
return unless oneapm_method_exists?(method_name)
|
279
|
+
metric_name_code ||= default_metric_name_code(method_name)
|
280
|
+
return if traced_method_exists?(method_name, metric_name_code)
|
281
|
+
|
282
|
+
traced_method = code_to_eval(method_name, metric_name_code, options)
|
283
|
+
|
284
|
+
visibility = OneApm::Helper.instance_method_visibility self, method_name
|
285
|
+
|
286
|
+
class_eval traced_method, __FILE__, __LINE__
|
287
|
+
alias_method _untraced_method_name(method_name, metric_name_code), method_name
|
288
|
+
alias_method method_name, _traced_method_name(method_name, metric_name_code)
|
289
|
+
send visibility, method_name
|
290
|
+
send visibility, _traced_method_name(method_name, metric_name_code)
|
291
|
+
::OneApm::Agent.logger.debug("Traced method: class = #{self.name},"+
|
292
|
+
"method = #{method_name}, "+
|
293
|
+
"metric = '#{metric_name_code}'")
|
294
|
+
end
|
295
|
+
|
296
|
+
# For tests only because tracers must be removed in reverse-order
|
297
|
+
# from when they were added, or else other tracers that were added to the same method
|
298
|
+
# may get removed as well.
|
299
|
+
def remove_method_tracer(method_name, metric_name_code) # :nodoc:
|
300
|
+
return unless Agent.config[:agent_enabled]
|
301
|
+
if method_defined? "#{_traced_method_name(method_name, metric_name_code)}"
|
302
|
+
alias_method method_name, "#{_untraced_method_name(method_name, metric_name_code)}"
|
303
|
+
undef_method "#{_traced_method_name(method_name, metric_name_code)}"
|
304
|
+
::OneApm::Agent.logger.debug("removed method tracer #{method_name} #{metric_name_code}\n")
|
305
|
+
else
|
306
|
+
raise "No tracer for '#{metric_name_code}' on method '#{method_name}'"
|
307
|
+
end
|
308
|
+
end
|
309
|
+
private
|
310
|
+
|
311
|
+
# given a method and a metric, this method returns the
|
312
|
+
# untraced alias of the method name
|
313
|
+
def _untraced_method_name(method_name, metric_name)
|
314
|
+
"#{_sanitize_name(method_name)}_without_trace_#{_sanitize_name(metric_name)}"
|
315
|
+
end
|
316
|
+
|
317
|
+
# given a method and a metric, this method returns the traced
|
318
|
+
# alias of the method name
|
319
|
+
def _traced_method_name(method_name, metric_name)
|
320
|
+
"#{_sanitize_name(method_name)}_with_trace_#{_sanitize_name(metric_name)}"
|
321
|
+
end
|
322
|
+
|
323
|
+
# makes sure that method names do not contain characters that
|
324
|
+
# might break the interpreter, for example ! or ? characters
|
325
|
+
# that are not allowed in the middle of method names
|
326
|
+
def _sanitize_name(name)
|
327
|
+
name.to_s.tr_s('^a-zA-Z0-9', '_')
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
331
|
+
# @!parse extend ClassMethods
|
332
|
+
end
|
333
|
+
end
|
334
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module OneApm
|
4
|
+
module Support
|
5
|
+
module MethodTracer
|
6
|
+
module Helpers
|
7
|
+
MAX_ALLOWED_METRIC_DURATION = 1_000_000_000 # roughly 31 years
|
8
|
+
|
9
|
+
extend self
|
10
|
+
|
11
|
+
# helper for logging errors to the oneapm_agent.log
|
12
|
+
# properly. Logs the error at error level
|
13
|
+
def log_errors(code_area)
|
14
|
+
yield
|
15
|
+
rescue => e
|
16
|
+
::OneApm::Agent.logger.error("Caught exception in #{code_area}.", e)
|
17
|
+
end
|
18
|
+
|
19
|
+
def trace_execution_scoped_header(state, t0)
|
20
|
+
log_errors(:trace_execution_scoped_header) do
|
21
|
+
stack = state.traced_method_stack
|
22
|
+
stack.push_frame(state, :method_tracer, t0)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def record_metrics(state, first_name, other_names, duration, exclusive, options)
|
27
|
+
record_scoped_metric = options.has_key?(:scoped_metric) ? options[:scoped_metric] : true
|
28
|
+
stat_engine = OneApm::Agent.instance.stats_engine
|
29
|
+
if record_scoped_metric
|
30
|
+
stat_engine.record_scoped_and_unscoped_metrics(state, first_name, other_names, duration, exclusive)
|
31
|
+
else
|
32
|
+
metrics = [first_name].concat(other_names)
|
33
|
+
stat_engine.record_unscoped_metrics(state, metrics, duration, exclusive)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def trace_execution_scoped_footer(state, t0, first_name, metric_names, expected_frame, options, t1=Time.now.to_f)
|
38
|
+
log_errors(:trace_method_execution_footer) do
|
39
|
+
if expected_frame
|
40
|
+
stack = state.traced_method_stack
|
41
|
+
create_metrics = options.has_key?(:metric) ? options[:metric] : true
|
42
|
+
frame = stack.pop_frame(state, expected_frame, first_name, t1, create_metrics)
|
43
|
+
if create_metrics
|
44
|
+
duration = t1 - t0
|
45
|
+
exclusive = duration - frame.children_time
|
46
|
+
|
47
|
+
if duration < MAX_ALLOWED_METRIC_DURATION
|
48
|
+
if duration < 0
|
49
|
+
::OneApm::Agent.logger.log_once(:warn, "metric_duration_negative:#{first_name}",
|
50
|
+
"Metric #{first_name} has negative duration: #{duration} s")
|
51
|
+
end
|
52
|
+
|
53
|
+
if exclusive < 0
|
54
|
+
::OneApm::Agent.logger.log_once(:warn, "metric_exclusive_negative:#{first_name}",
|
55
|
+
"Metric #{first_name} has negative exclusive time: duration = #{duration} s, child_time = #{frame.children_time}")
|
56
|
+
end
|
57
|
+
|
58
|
+
record_metrics(state, first_name, metric_names, duration, exclusive, options)
|
59
|
+
else
|
60
|
+
::OneApm::Agent.logger.log_once(:warn, "too_huge_metric:#{first_name}",
|
61
|
+
"Ignoring metric #{first_name} with unacceptably large duration: #{duration} s")
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def trace_execution_scoped(metric_names, options={}) #THREAD_LOCAL_ACCESS
|
69
|
+
state = OneApm::TransactionState.tl_get
|
70
|
+
return yield unless state.is_execution_traced?
|
71
|
+
|
72
|
+
metric_names = Array(metric_names)
|
73
|
+
first_name = metric_names.shift
|
74
|
+
return yield unless first_name
|
75
|
+
|
76
|
+
additional_metrics_callback = options[:additional_metrics_callback]
|
77
|
+
start_time = Time.now.to_f
|
78
|
+
expected_scope = trace_execution_scoped_header(state, start_time)
|
79
|
+
|
80
|
+
begin
|
81
|
+
result = yield
|
82
|
+
metric_names += Array(additional_metrics_callback.call) if additional_metrics_callback
|
83
|
+
result
|
84
|
+
ensure
|
85
|
+
trace_execution_scoped_footer(state, start_time, first_name, metric_names, expected_scope, options)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|