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,40 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'one_apm/rack/middleware_wrapper'
|
4
|
+
|
5
|
+
LibraryDetection.defer do
|
6
|
+
named :rails_middleware
|
7
|
+
|
8
|
+
depends_on do
|
9
|
+
defined?(::Rails) && ::Rails::VERSION::MAJOR.to_i >= 3
|
10
|
+
end
|
11
|
+
|
12
|
+
depends_on do
|
13
|
+
!::OneApm::Agent.config[:disable_rack_middleware]
|
14
|
+
end
|
15
|
+
|
16
|
+
executes do
|
17
|
+
::OneApm::Agent.logger.info("Installing Rails 3+ middleware instrumentation")
|
18
|
+
|
19
|
+
module ActionDispatch
|
20
|
+
class MiddlewareStack
|
21
|
+
class Middleware
|
22
|
+
|
23
|
+
def build_with_one_apm(app)
|
24
|
+
# MiddlewareWrapper.wrap guards against double-wrapping here.
|
25
|
+
# We need to instrument the innermost app (usually a RouteSet),
|
26
|
+
# which will never itself be the return value from #build, but will
|
27
|
+
# instead be the initial value of the app argument.
|
28
|
+
wrapped_app = ::OneApm::Rack::MiddlewareWrapper.wrap(app)
|
29
|
+
result = build_without_one_apm(wrapped_app)
|
30
|
+
::OneApm::Rack::MiddlewareWrapper.wrap(result)
|
31
|
+
end
|
32
|
+
|
33
|
+
alias_method :build_without_one_apm, :build
|
34
|
+
alias_method :build, :build_with_one_apm
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module OneApm
|
4
|
+
module Agent
|
5
|
+
module Instrumentation
|
6
|
+
class EventedSubscriber
|
7
|
+
def initialize
|
8
|
+
@queue_key = ['OneApm', self.class.name, object_id].join('-')
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.subscribed?
|
12
|
+
# TODO: need to talk to Rails core about an API for this,
|
13
|
+
# rather than digging through Listener ivars
|
14
|
+
ActiveSupport::Notifications.notifier.instance_variable_get(:@subscribers) \
|
15
|
+
.find{|s| s.instance_variable_get(:@delegate).class == self }
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.subscribe(pattern)
|
19
|
+
if !subscribed?
|
20
|
+
ActiveSupport::Notifications.subscribe(pattern, new)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def start(name, id, payload)
|
25
|
+
event = ActiveSupport::Notifications::Event.new(name, Time.now, nil, id, payload)
|
26
|
+
push_event(event)
|
27
|
+
return event
|
28
|
+
end
|
29
|
+
|
30
|
+
def finish(name, id, payload)
|
31
|
+
pop_event(id)
|
32
|
+
end
|
33
|
+
|
34
|
+
def log_notification_error(error, name, event_type)
|
35
|
+
# These are important enough failures that we want the backtraces
|
36
|
+
# logged at error level, hence the explicit log_exception call.
|
37
|
+
OneApm::Agent.logger.error("Error during #{event_type} callback for event '#{name}':")
|
38
|
+
OneApm::Agent.logger.log_exception(:error, error)
|
39
|
+
end
|
40
|
+
|
41
|
+
def push_event(event)
|
42
|
+
parent = event_stack[event.transaction_id].last
|
43
|
+
if parent && event.respond_to?(:parent=)
|
44
|
+
event.parent = parent
|
45
|
+
parent << event
|
46
|
+
end
|
47
|
+
event_stack[event.transaction_id].push event
|
48
|
+
end
|
49
|
+
|
50
|
+
def pop_event(transaction_id)
|
51
|
+
event = event_stack[transaction_id].pop
|
52
|
+
event.end = Time.now
|
53
|
+
return event
|
54
|
+
end
|
55
|
+
|
56
|
+
def event_stack
|
57
|
+
Thread.current[@queue_key] ||= Hash.new {|h,id| h[id] = [] }
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Taken from ActiveSupport::Notifications::Event, pasted here
|
62
|
+
# with a couple minor additions so we don't have a hard
|
63
|
+
# dependency on ActiveSupport::Notifications.
|
64
|
+
#
|
65
|
+
# Represents an intrumentation event, provides timing and metric
|
66
|
+
# name information useful when recording metrics.
|
67
|
+
class Event
|
68
|
+
attr_reader :name, :time, :transaction_id, :payload, :children
|
69
|
+
attr_accessor :end, :parent, :frame
|
70
|
+
|
71
|
+
def initialize(name, start, ending, transaction_id, payload)
|
72
|
+
@name = name
|
73
|
+
@payload = payload.dup
|
74
|
+
@time = start
|
75
|
+
@transaction_id = transaction_id
|
76
|
+
@end = ending
|
77
|
+
@children = []
|
78
|
+
end
|
79
|
+
|
80
|
+
def metric_name
|
81
|
+
raise NotImplementedError
|
82
|
+
end
|
83
|
+
|
84
|
+
def duration
|
85
|
+
self.end - time
|
86
|
+
end
|
87
|
+
|
88
|
+
def <<(event)
|
89
|
+
@children << event
|
90
|
+
end
|
91
|
+
|
92
|
+
def parent_of?(event)
|
93
|
+
@children.include? event
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module OneApm
|
4
|
+
module Agent
|
5
|
+
module Instrumentation
|
6
|
+
module IgnoreActions
|
7
|
+
def self.is_filtered?(key, klass, action_name)
|
8
|
+
# We'll walk the superclass chain and see if
|
9
|
+
# any class says 'yes, filter this one'.
|
10
|
+
|
11
|
+
while klass.respond_to? :oneapm_read_attr
|
12
|
+
ignore_actions = klass.oneapm_read_attr(key)
|
13
|
+
|
14
|
+
should_filter = case ignore_actions
|
15
|
+
when Hash
|
16
|
+
only_actions = Array(ignore_actions[:only])
|
17
|
+
except_actions = Array(ignore_actions[:except])
|
18
|
+
action_name = action_name.to_sym
|
19
|
+
|
20
|
+
only_actions.include?(action_name) || (!except_actions.empty? && !except_actions.include?(action_name))
|
21
|
+
else
|
22
|
+
!!ignore_actions
|
23
|
+
end
|
24
|
+
|
25
|
+
return true if should_filter
|
26
|
+
|
27
|
+
# Nothing so far says we should filter,
|
28
|
+
# so keep checking up the superclass chain.
|
29
|
+
klass = klass.superclass
|
30
|
+
end
|
31
|
+
|
32
|
+
# Getting here means that no class filtered this.
|
33
|
+
false
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module OneApm
|
4
|
+
module Agent
|
5
|
+
module Instrumentation
|
6
|
+
# https://oneapm.com/docs/features/tracking-front-end-time
|
7
|
+
# Record queue time metrics based on any of three headers
|
8
|
+
# which can be set on the request.
|
9
|
+
module QueueTime
|
10
|
+
unless defined?(REQUEST_START_HEADER)
|
11
|
+
REQUEST_START_HEADER = 'HTTP_X_REQUEST_START'.freeze
|
12
|
+
QUEUE_START_HEADER = 'HTTP_X_QUEUE_START'.freeze
|
13
|
+
MIDDLEWARE_START_HEADER = 'HTTP_X_MIDDLEWARE_START'.freeze
|
14
|
+
ALL_QUEUE_METRIC = 'WebFrontend/QueueTime'.freeze
|
15
|
+
# any timestamps before this are thrown out and the parser
|
16
|
+
# will try again with a larger unit (2000/1/1 UTC)
|
17
|
+
EARLIEST_ACCEPTABLE_TIME = Time.at(946684800)
|
18
|
+
|
19
|
+
CANDIDATE_HEADERS = [
|
20
|
+
REQUEST_START_HEADER,
|
21
|
+
QUEUE_START_HEADER,
|
22
|
+
MIDDLEWARE_START_HEADER
|
23
|
+
].freeze
|
24
|
+
|
25
|
+
DIVISORS = [1_000_000, 1_000, 1]
|
26
|
+
end
|
27
|
+
|
28
|
+
module_function
|
29
|
+
|
30
|
+
def parse_frontend_timestamp(headers, now=Time.now)
|
31
|
+
earliest = nil
|
32
|
+
|
33
|
+
CANDIDATE_HEADERS.each do |header|
|
34
|
+
if headers[header]
|
35
|
+
parsed = parse_timestamp(timestamp_string_from_header_value(headers[header]))
|
36
|
+
if parsed && (!earliest || parsed < earliest)
|
37
|
+
earliest = parsed
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
if earliest && earliest > now
|
43
|
+
OneApm::Agent.logger.debug("Negative queue time detected, treating as zero: start=#{earliest.to_f} > now=#{now.to_f}")
|
44
|
+
earliest = now
|
45
|
+
end
|
46
|
+
|
47
|
+
earliest
|
48
|
+
end
|
49
|
+
|
50
|
+
def timestamp_string_from_header_value(value)
|
51
|
+
case value
|
52
|
+
when /^\s*([\d+\.]+)\s*$/ then $1
|
53
|
+
# following regexp intentionally unanchored to handle
|
54
|
+
# (ie ignore) leading server names
|
55
|
+
when /t=([\d+\.]+)/ then $1
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def parse_timestamp(string)
|
60
|
+
DIVISORS.each do |divisor|
|
61
|
+
begin
|
62
|
+
t = Time.at(string.to_f / divisor)
|
63
|
+
return t if t > EARLIEST_ACCEPTABLE_TIME
|
64
|
+
rescue RangeError
|
65
|
+
# On Ruby versions built with a 32-bit time_t, attempting to
|
66
|
+
# instantiate a Time object in the far future raises a RangeError,
|
67
|
+
# in which case we know we've chosen the wrong divisor.
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
nil
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,405 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'one_apm/transaction'
|
4
|
+
require 'one_apm/transaction/transaction_namer'
|
5
|
+
require 'one_apm/inst/support/queue_time'
|
6
|
+
require 'one_apm/inst/support/ignore_actions'
|
7
|
+
|
8
|
+
module OneApm
|
9
|
+
module Agent
|
10
|
+
module Instrumentation
|
11
|
+
#
|
12
|
+
# OneApm instrumentation for transactions
|
13
|
+
#
|
14
|
+
# * controller actions
|
15
|
+
# * background tasks
|
16
|
+
# * external web calls
|
17
|
+
#
|
18
|
+
# see
|
19
|
+
# * add_transaction_tracer
|
20
|
+
# * perform_action_with_oneapm_trace
|
21
|
+
#
|
22
|
+
module TransactionBase
|
23
|
+
|
24
|
+
def self.included(clazz)
|
25
|
+
clazz.extend(ClassMethods)
|
26
|
+
end
|
27
|
+
|
28
|
+
module Shim
|
29
|
+
module ClassMethodsShim
|
30
|
+
def oneapm_ignore(*args); end
|
31
|
+
def oneapm_ignore_apdex(*args); end
|
32
|
+
def oneapm_ignore_enduser(*args); end
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.included(clazz)
|
36
|
+
clazz.extend(ClassMethodsShim)
|
37
|
+
end
|
38
|
+
def oneapm_notice_error(*args); end
|
39
|
+
def one_apm_trace_controller_action(*args); yield; end
|
40
|
+
def perform_action_with_oneapm_trace(*args); yield; end
|
41
|
+
end
|
42
|
+
|
43
|
+
OA_DO_NOT_TRACE_KEY = :'@do_not_trace' unless defined?(OA_DO_NOT_TRACE_KEY )
|
44
|
+
OA_IGNORE_APDEX_KEY = :'@ignore_apdex' unless defined?(OA_IGNORE_APDEX_KEY )
|
45
|
+
OA_IGNORE_ENDUSER_KEY = :'@ignore_enduser' unless defined?(OA_IGNORE_ENDUSER_KEY)
|
46
|
+
OA_DEFAULT_OPTIONS = {}.freeze unless defined?(OA_DEFAULT_OPTIONS )
|
47
|
+
|
48
|
+
module ClassMethods
|
49
|
+
|
50
|
+
# Have OneApm ignore actions in this controller.
|
51
|
+
# Specify the actions as hash options using :except and :only.
|
52
|
+
# If no actions are specified, all actions are ignored.
|
53
|
+
#
|
54
|
+
# @api public
|
55
|
+
#
|
56
|
+
def oneapm_ignore(options = {})
|
57
|
+
oneapm_ignore_aspect(OA_DO_NOT_TRACE_KEY, options)
|
58
|
+
end
|
59
|
+
|
60
|
+
# Have OneApm omit apdex measurements on the given actions.
|
61
|
+
# Typically used for actions that are not user facing or that skew your overall apdex measurement.
|
62
|
+
# Accepts :except and :only options.
|
63
|
+
#
|
64
|
+
# @api public
|
65
|
+
#
|
66
|
+
def oneapm_ignore_apdex(options = {})
|
67
|
+
oneapm_ignore_aspect(OA_IGNORE_APDEX_KEY, options)
|
68
|
+
end
|
69
|
+
|
70
|
+
# Have OneApm skip install javascript_instrumentation
|
71
|
+
#
|
72
|
+
# @api public
|
73
|
+
#
|
74
|
+
def oneapm_ignore_enduser(options = {})
|
75
|
+
oneapm_ignore_aspect(OA_IGNORE_ENDUSER_KEY, options)
|
76
|
+
end
|
77
|
+
|
78
|
+
def oneapm_ignore_aspect(property, options = {})
|
79
|
+
if options.empty?
|
80
|
+
oneapm_write_attr property, true
|
81
|
+
elsif !options.is_a?(Hash)
|
82
|
+
::OneApm::Agent.logger.error "oneapm_#{property} takes an optional hash with :only and :except lists of actions (illegal argument type '#{options.class}')"
|
83
|
+
else
|
84
|
+
oneapm_write_attr property, options
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def oneapm_write_attr(attr_name, value)
|
89
|
+
instance_variable_set(attr_name, value)
|
90
|
+
end
|
91
|
+
|
92
|
+
def oneapm_read_attr(attr_name)
|
93
|
+
instance_variable_get(attr_name)
|
94
|
+
end
|
95
|
+
|
96
|
+
# Add transaction tracing to the given method.
|
97
|
+
# This will treat the given method as a main entrypoint for instrumentation,
|
98
|
+
# just like controller actions are treated by default.
|
99
|
+
# Useful especially for background tasks.
|
100
|
+
#
|
101
|
+
# Example for background job:
|
102
|
+
# class Job
|
103
|
+
# include OneApm::Agent::Instrumentation::TransactionBase
|
104
|
+
# def run(task)
|
105
|
+
# ...
|
106
|
+
# end
|
107
|
+
#
|
108
|
+
# # Instrument run so tasks show up under task.name.
|
109
|
+
# # Note single quoting to defer eval to runtime.
|
110
|
+
# add_transaction_tracer :run, :name => '#{args[0].name}'
|
111
|
+
# end
|
112
|
+
#
|
113
|
+
# Here's an example of a controller that uses a dispatcher
|
114
|
+
# action to invoke operations which you want treated as top
|
115
|
+
# level actions, so they aren't all lumped into the invoker
|
116
|
+
# action.
|
117
|
+
#
|
118
|
+
# MyController < ActionController::Base
|
119
|
+
# include OneApm::Agent::Instrumentation::TransactionBase
|
120
|
+
# # dispatch the given op to the method given by the service parameter.
|
121
|
+
# def invoke_operation
|
122
|
+
# op = params['operation']
|
123
|
+
# send op
|
124
|
+
# end
|
125
|
+
# # Ignore the invoker to avoid double counting
|
126
|
+
# oneapm_ignore :only => 'invoke_operation'
|
127
|
+
# # Instrument the operations:
|
128
|
+
# add_transaction_tracer :print
|
129
|
+
# add_transaction_tracer :show
|
130
|
+
# add_transaction_tracer :forward
|
131
|
+
# end
|
132
|
+
#
|
133
|
+
# Here's an example of how to pass contextual information into the transaction
|
134
|
+
# so it will appear in transaction traces:
|
135
|
+
#
|
136
|
+
# class Job
|
137
|
+
# include OneApm::Agent::Instrumentation::TransactionBase
|
138
|
+
# def process(account)
|
139
|
+
# ...
|
140
|
+
# end
|
141
|
+
# # Include the account name in the transaction details. Note the single
|
142
|
+
# # quotes to defer eval until call time.
|
143
|
+
# add_transaction_tracer :process, :params => '{ :account_name => args[0].name }'
|
144
|
+
# end
|
145
|
+
#
|
146
|
+
# See OneApm::Agent::Instrumentation::TransactionBase#perform_action_with_oneapm_trace
|
147
|
+
# for the full list of available options.
|
148
|
+
#
|
149
|
+
# @api public
|
150
|
+
#
|
151
|
+
def add_transaction_tracer(method, options = {})
|
152
|
+
options[:name] ||= method.to_s
|
153
|
+
|
154
|
+
argument_list = generate_argument_list(options)
|
155
|
+
traced_method, punctuation = parse_punctuation(method)
|
156
|
+
with_method_name, without_method_name = build_method_names(traced_method, punctuation)
|
157
|
+
|
158
|
+
if already_added_transaction_tracer?(self, with_method_name)
|
159
|
+
::OneApm::Agent.logger.warn("Transaction tracer already in place for class = #{self.name}, method = #{method.to_s}, skipping")
|
160
|
+
return
|
161
|
+
end
|
162
|
+
|
163
|
+
class_eval <<-EOC
|
164
|
+
def #{with_method_name}(*args, &block)
|
165
|
+
perform_action_with_oneapm_trace(#{argument_list.join(',')}) do
|
166
|
+
#{without_method_name}(*args, &block)
|
167
|
+
end
|
168
|
+
end
|
169
|
+
EOC
|
170
|
+
|
171
|
+
visibility = OneApm::Helper.instance_method_visibility self, method
|
172
|
+
|
173
|
+
alias_method without_method_name, method.to_s
|
174
|
+
alias_method method.to_s, with_method_name
|
175
|
+
|
176
|
+
send visibility, method
|
177
|
+
send visibility, with_method_name
|
178
|
+
|
179
|
+
::OneApm::Agent.logger.debug("Traced transaction: class = #{self.name}, method = #{method.to_s}, options = #{options.inspect}")
|
180
|
+
end
|
181
|
+
|
182
|
+
def parse_punctuation(method)
|
183
|
+
[method.to_s.sub(/([?!=])$/, ''), $1]
|
184
|
+
end
|
185
|
+
|
186
|
+
def generate_argument_list(options)
|
187
|
+
options.map do |key, value|
|
188
|
+
value = if value.is_a?(Symbol)
|
189
|
+
value.inspect
|
190
|
+
elsif key == :params
|
191
|
+
value.to_s
|
192
|
+
else
|
193
|
+
%Q["#{value.to_s}"]
|
194
|
+
end
|
195
|
+
|
196
|
+
%Q[:#{key} => #{value}]
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
def build_method_names(traced_method, punctuation)
|
201
|
+
[
|
202
|
+
"#{traced_method.to_s}_with_oneapm_transaction_trace#{punctuation}",
|
203
|
+
"#{traced_method.to_s}_without_oneapm_transaction_trace#{punctuation}"
|
204
|
+
]
|
205
|
+
end
|
206
|
+
|
207
|
+
def already_added_transaction_tracer?(target, with_method_name)
|
208
|
+
if OneApm::Helper.instance_methods_include?(target, with_method_name)
|
209
|
+
true
|
210
|
+
else
|
211
|
+
false
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
# Yield to the given block with OneApm tracing. Used by
|
217
|
+
# default instrumentation on controller actions in Rails.
|
218
|
+
# But it can also be used in custom instrumentation of controller
|
219
|
+
# methods and background tasks.
|
220
|
+
#
|
221
|
+
# This is the method invoked by instrumentation added by the
|
222
|
+
# <tt>ClassMethods#add_transaction_tracer</tt>.
|
223
|
+
#
|
224
|
+
# Here's a more verbose version of the example shown in
|
225
|
+
# <tt>ClassMethods#add_transaction_tracer</tt> using this method instead of
|
226
|
+
# #add_transaction_tracer.
|
227
|
+
#
|
228
|
+
# Below is a controller with an +invoke_operation+ action which
|
229
|
+
# dispatches to more specific operation methods based on a
|
230
|
+
# parameter (very dangerous, btw!). With this instrumentation,
|
231
|
+
# the +invoke_operation+ action is ignored but the operation
|
232
|
+
# methods show up in OneApm as if they were first class controller
|
233
|
+
# actions
|
234
|
+
#
|
235
|
+
# MyController < ActionController::Base
|
236
|
+
# include OneApm::Agent::Instrumentation::TransactionBase
|
237
|
+
# # dispatch the given op to the method given by the service parameter.
|
238
|
+
# def invoke_operation
|
239
|
+
# op = params['operation']
|
240
|
+
# perform_action_with_oneapm_trace(:name => op) do
|
241
|
+
# send op, params['message']
|
242
|
+
# end
|
243
|
+
# end
|
244
|
+
# # Ignore the invoker to avoid double counting
|
245
|
+
# oneapm_ignore :only => 'invoke_operation'
|
246
|
+
# end
|
247
|
+
#
|
248
|
+
#
|
249
|
+
# When invoking this method explicitly as in the example above, pass in a
|
250
|
+
# block to measure with some combination of options:
|
251
|
+
#
|
252
|
+
# * <tt>:category => :controller</tt> indicates that this is a
|
253
|
+
# controller action and will appear with all the other actions. This
|
254
|
+
# is the default.
|
255
|
+
# * <tt>:category => :task</tt> indicates that this is a
|
256
|
+
# background task and will show up in OneApm with other background
|
257
|
+
# tasks instead of in the controllers list
|
258
|
+
# * <tt>:category => :middleware</tt> if you are instrumenting a rack
|
259
|
+
# middleware call. The <tt>:name</tt> is optional, useful if you
|
260
|
+
# have more than one potential transaction in the #call.
|
261
|
+
# * <tt>:category => :uri</tt> indicates that this is a
|
262
|
+
# web transaction whose name is a normalized URI, where 'normalized'
|
263
|
+
# means the URI does not have any elements with data in them such
|
264
|
+
# as in many REST URIs.
|
265
|
+
# * <tt>:name => action_name</tt> is used to specify the action
|
266
|
+
# name used as part of the metric name
|
267
|
+
# * <tt>:params => {...}</tt> to provide information about the context
|
268
|
+
# of the call, used in transaction trace display, for example:
|
269
|
+
# <tt>:params => { :account => @account.name, :file => file.name }</tt>
|
270
|
+
# These are treated similarly to request parameters in web transactions.
|
271
|
+
#
|
272
|
+
# Seldomly used options:
|
273
|
+
#
|
274
|
+
# * <tt>:class_name => aClass.name</tt> is used to override the name
|
275
|
+
# of the class when used inside the metric name. Default is the
|
276
|
+
# current class.
|
277
|
+
# * <tt>:path => metric_path</tt> is *deprecated* in the public API. It
|
278
|
+
# allows you to set the entire metric after the category part. Overrides
|
279
|
+
# all the other options.
|
280
|
+
# * <tt>:request => Rack::Request#new(env)</tt> is used to pass in a
|
281
|
+
# request object that may respond to uri and referer.
|
282
|
+
#
|
283
|
+
# @api public
|
284
|
+
#
|
285
|
+
def perform_action_with_oneapm_trace(*args, &block) #THREAD_LOCAL_ACCESS
|
286
|
+
state = OneApm::TransactionState.tl_get
|
287
|
+
state.request = oneapm_request(args)
|
288
|
+
|
289
|
+
skip_tracing = do_not_trace? || !state.is_execution_traced?
|
290
|
+
|
291
|
+
if skip_tracing
|
292
|
+
state.current_transaction.ignore! if state.current_transaction
|
293
|
+
OneApm::Agent.disable_all_tracing { return yield }
|
294
|
+
end
|
295
|
+
|
296
|
+
# This method has traditionally taken a variable number of arguments, but the
|
297
|
+
# only one that is expected / used is a single options hash. We are preserving
|
298
|
+
# the *args method signature to ensure backwards compatibility.
|
299
|
+
|
300
|
+
trace_options = args.last.is_a?(Hash) ? args.last : OA_DEFAULT_OPTIONS
|
301
|
+
category = trace_options[:category] || :controller
|
302
|
+
txn_options = create_transaction_options(trace_options, category, state)
|
303
|
+
|
304
|
+
begin
|
305
|
+
txn = Transaction.start(state, category, txn_options)
|
306
|
+
|
307
|
+
begin
|
308
|
+
yield
|
309
|
+
rescue => e
|
310
|
+
OneApm::Agent.notice_error(e)
|
311
|
+
raise
|
312
|
+
end
|
313
|
+
|
314
|
+
ensure
|
315
|
+
if txn
|
316
|
+
txn.ignore_apdex! if ignore_apdex?
|
317
|
+
txn.ignore_enduser! if ignore_enduser?
|
318
|
+
end
|
319
|
+
Transaction.stop(state)
|
320
|
+
end
|
321
|
+
end
|
322
|
+
|
323
|
+
protected
|
324
|
+
|
325
|
+
def oneapm_request(args)
|
326
|
+
opts = args.first
|
327
|
+
# passed as a parameter to add_transaction_tracer
|
328
|
+
if opts.respond_to?(:keys) && opts.respond_to?(:[]) && opts[:request]
|
329
|
+
opts[:request]
|
330
|
+
# in a Rails app
|
331
|
+
elsif self.respond_to?(:request)
|
332
|
+
self.request
|
333
|
+
end
|
334
|
+
end
|
335
|
+
|
336
|
+
# Should be implemented in the dispatcher class
|
337
|
+
def oneapm_response_code; end
|
338
|
+
|
339
|
+
def oneapm_request_headers(state)
|
340
|
+
request = state.request
|
341
|
+
if request
|
342
|
+
if request.respond_to?(:headers)
|
343
|
+
request.headers
|
344
|
+
elsif request.respond_to?(:env)
|
345
|
+
request.env
|
346
|
+
end
|
347
|
+
end
|
348
|
+
end
|
349
|
+
|
350
|
+
# overrideable method to determine whether to trace an action
|
351
|
+
# or not - you may override this in your controller and supply
|
352
|
+
# your own logic for ignoring transactions.
|
353
|
+
def do_not_trace?
|
354
|
+
_is_filtered?(OA_DO_NOT_TRACE_KEY)
|
355
|
+
end
|
356
|
+
|
357
|
+
# overrideable method to determine whether to trace an action
|
358
|
+
# for purposes of apdex measurement - you can use this to
|
359
|
+
# ignore things like api calls or other fast non-user-facing
|
360
|
+
# actions
|
361
|
+
def ignore_apdex?
|
362
|
+
_is_filtered?(OA_IGNORE_APDEX_KEY)
|
363
|
+
end
|
364
|
+
|
365
|
+
def ignore_enduser?
|
366
|
+
_is_filtered?(OA_IGNORE_ENDUSER_KEY)
|
367
|
+
end
|
368
|
+
|
369
|
+
private
|
370
|
+
|
371
|
+
def create_transaction_options(trace_options, category, state)
|
372
|
+
txn_options = {}
|
373
|
+
txn_options[:request] = trace_options[:request]
|
374
|
+
txn_options[:request] ||= request if respond_to?(:request)
|
375
|
+
# params should have been filtered before calling perform_action_with_oneapm_trace
|
376
|
+
txn_options[:filtered_params] = trace_options[:params]
|
377
|
+
txn_options[:transaction_name] = OneApm::TransactionNamer.name_for(nil, self, category, trace_options)
|
378
|
+
txn_options[:apdex_start_time] = detect_queue_start_time(state)
|
379
|
+
txn_options
|
380
|
+
end
|
381
|
+
|
382
|
+
# Filter out a request if it matches one of our parameters for
|
383
|
+
# ignoring it - the key is either OA_DO_NOT_TRACE_KEY or OA_IGNORE_APDEX_KEY
|
384
|
+
def _is_filtered?(key)
|
385
|
+
name = if respond_to?(:action_name)
|
386
|
+
action_name
|
387
|
+
else
|
388
|
+
:'[action_name_missing]'
|
389
|
+
end
|
390
|
+
|
391
|
+
OneApm::Agent::Instrumentation::IgnoreActions.is_filtered?(
|
392
|
+
key,
|
393
|
+
self.class,
|
394
|
+
name)
|
395
|
+
end
|
396
|
+
|
397
|
+
def detect_queue_start_time(state)
|
398
|
+
headers = oneapm_request_headers(state)
|
399
|
+
|
400
|
+
QueueTime.parse_frontend_timestamp(headers) if headers
|
401
|
+
end
|
402
|
+
end
|
403
|
+
end
|
404
|
+
end
|
405
|
+
end
|