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,155 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'set'
|
4
|
+
require 'one_apm/agent/event/worker_loop'
|
5
|
+
require 'one_apm/agent/threading/backtrace_node'
|
6
|
+
|
7
|
+
# Data structure for representing a thread profile
|
8
|
+
|
9
|
+
module OneApm
|
10
|
+
module Agent
|
11
|
+
module Threading
|
12
|
+
|
13
|
+
class ThreadProfile
|
14
|
+
|
15
|
+
attr_reader :profile_id,
|
16
|
+
:traces,
|
17
|
+
:sample_period,
|
18
|
+
:duration,
|
19
|
+
:poll_count,
|
20
|
+
:backtrace_count,
|
21
|
+
:failure_count,
|
22
|
+
:created_at,
|
23
|
+
:xray_id,
|
24
|
+
:command_arguments,
|
25
|
+
:profile_agent_code
|
26
|
+
attr_accessor :finished_at
|
27
|
+
|
28
|
+
def initialize(command_arguments={})
|
29
|
+
@command_arguments = command_arguments
|
30
|
+
@profile_id = command_arguments.fetch('profile_id', -1)
|
31
|
+
@duration = command_arguments.fetch('duration', 120)
|
32
|
+
@sample_period = command_arguments.fetch('sample_period', 0.1)
|
33
|
+
@profile_agent_code = command_arguments.fetch('profile_agent_code', false)
|
34
|
+
@xray_id = command_arguments.fetch('x_ray_id', nil)
|
35
|
+
@finished = false
|
36
|
+
|
37
|
+
@traces = {
|
38
|
+
:agent => BacktraceRoot.new,
|
39
|
+
:background => BacktraceRoot.new,
|
40
|
+
:other => BacktraceRoot.new,
|
41
|
+
:request => BacktraceRoot.new
|
42
|
+
}
|
43
|
+
|
44
|
+
@poll_count = 0
|
45
|
+
@backtrace_count = 0
|
46
|
+
@failure_count = 0
|
47
|
+
@unique_threads = []
|
48
|
+
|
49
|
+
@created_at = Time.now
|
50
|
+
end
|
51
|
+
|
52
|
+
def requested_period
|
53
|
+
@sample_period
|
54
|
+
end
|
55
|
+
|
56
|
+
def increment_poll_count
|
57
|
+
@poll_count += 1
|
58
|
+
end
|
59
|
+
|
60
|
+
def sample_count
|
61
|
+
xray? ? @backtrace_count : @poll_count
|
62
|
+
end
|
63
|
+
|
64
|
+
def xray?
|
65
|
+
!!@xray_id
|
66
|
+
end
|
67
|
+
|
68
|
+
def empty?
|
69
|
+
@backtrace_count == 0
|
70
|
+
end
|
71
|
+
|
72
|
+
def unique_thread_count
|
73
|
+
return 0 if @unique_threads.nil?
|
74
|
+
@unique_threads.length
|
75
|
+
end
|
76
|
+
|
77
|
+
def aggregate(backtrace, bucket, thread)
|
78
|
+
if backtrace.nil?
|
79
|
+
@failure_count += 1
|
80
|
+
else
|
81
|
+
@backtrace_count += 1
|
82
|
+
@traces[bucket].aggregate(backtrace)
|
83
|
+
@unique_threads << thread unless @unique_threads.include?(thread)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def convert_N_trace_nodes_to_arrays(count_to_keep) #THREAD_LOCAL_ACCESS
|
88
|
+
all_nodes = @traces.values.map { |n| n.flattened }.flatten
|
89
|
+
|
90
|
+
OneApm::Agent.instance.stats_engine.
|
91
|
+
tl_record_supportability_metric_count("ThreadProfiler/NodeCount", all_nodes.size)
|
92
|
+
|
93
|
+
all_nodes.sort! do |a, b|
|
94
|
+
# we primarily prefer higher runnable_count
|
95
|
+
comparison = b.runnable_count <=> a.runnable_count
|
96
|
+
# we secondarily prefer lower depth
|
97
|
+
comparison = a.depth <=> b.depth if comparison == 0
|
98
|
+
# it is thus impossible for any child to preceed their parent
|
99
|
+
comparison
|
100
|
+
end
|
101
|
+
|
102
|
+
all_nodes.each_with_index do |n, i|
|
103
|
+
break if i >= count_to_keep
|
104
|
+
n.mark_for_array_conversion
|
105
|
+
end
|
106
|
+
all_nodes.each_with_index do |n, i|
|
107
|
+
break if i >= count_to_keep
|
108
|
+
n.complete_array_conversion
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
THREAD_PROFILER_NODES = 20_000
|
113
|
+
|
114
|
+
include OneApm::Coerce
|
115
|
+
|
116
|
+
def generate_traces
|
117
|
+
convert_N_trace_nodes_to_arrays(THREAD_PROFILER_NODES)
|
118
|
+
|
119
|
+
{
|
120
|
+
"OTHER" => @traces[:other ].as_array,
|
121
|
+
"REQUEST" => @traces[:request ].as_array,
|
122
|
+
"AGENT" => @traces[:agent ].as_array,
|
123
|
+
"BACKGROUND" => @traces[:background].as_array
|
124
|
+
}
|
125
|
+
end
|
126
|
+
|
127
|
+
def to_collector_array(encoder)
|
128
|
+
encoded_trace_tree = encoder.encode(generate_traces, :skip_normalization => true)
|
129
|
+
result = [
|
130
|
+
int(self.profile_id),
|
131
|
+
float(self.created_at),
|
132
|
+
float(self.finished_at),
|
133
|
+
int(self.sample_count),
|
134
|
+
encoded_trace_tree,
|
135
|
+
int(self.unique_thread_count),
|
136
|
+
0 # runnable thread count, which we don't track
|
137
|
+
]
|
138
|
+
result << int(@xray_id) if xray?
|
139
|
+
result
|
140
|
+
end
|
141
|
+
|
142
|
+
def to_log_description
|
143
|
+
id = if xray?
|
144
|
+
"@xray_id: #{xray_id}"
|
145
|
+
else
|
146
|
+
"@profile_id: #{profile_id}"
|
147
|
+
end
|
148
|
+
|
149
|
+
"#<ThreadProfile:#{object_id} #{id} @command_arguments=#{@command_arguments.inspect}>"
|
150
|
+
end
|
151
|
+
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
@@ -0,0 +1,139 @@
|
|
1
|
+
module OneApm
|
2
|
+
module Collector
|
3
|
+
class CollectorService
|
4
|
+
module Helper
|
5
|
+
|
6
|
+
def handle_serialization_error(method, e)
|
7
|
+
OneApm::Agent.increment_metric("Supportability/serialization_failure")
|
8
|
+
OneApm::Agent.increment_metric("Supportability/serialization_failure/#{method}")
|
9
|
+
msg = "Failed to serialize #{method} data using #{@marshaller.class.to_s}: #{e.inspect}"
|
10
|
+
error = SerializationError.new(msg)
|
11
|
+
error.set_backtrace(e.backtrace)
|
12
|
+
raise error
|
13
|
+
end
|
14
|
+
|
15
|
+
def record_timing_supportability_metrics(method, start_ts, serialize_finish_ts)
|
16
|
+
serialize_time = serialize_finish_ts && (serialize_finish_ts - start_ts)
|
17
|
+
duration = (Time.now - start_ts).to_f
|
18
|
+
OneApm::Agent.record_metric("Supportability/invoke_remote", duration)
|
19
|
+
OneApm::Agent.record_metric("Supportability/invoke_remote/#{method.to_s}", duration)
|
20
|
+
if serialize_time
|
21
|
+
OneApm::Agent.record_metric("Supportability/invoke_remote_serialize", serialize_time)
|
22
|
+
OneApm::Agent.record_metric("Supportability/invoke_remote_serialize/#{method.to_s}", serialize_time)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# For these metrics, we use the following fields:
|
27
|
+
# call_count => number of times this remote method was invoked
|
28
|
+
# total_call_time => total size in bytes of payloads across all invocations
|
29
|
+
# total_exclusive_time => total size in items (e.g. unique metrics, traces, events, etc) across all invocations
|
30
|
+
#
|
31
|
+
# The last field doesn't make sense for all methods (e.g. get_agent_commands),
|
32
|
+
# so we omit it for those methods that don't really take collections
|
33
|
+
# of items as arguments.
|
34
|
+
def record_size_supportability_metrics(method, size_bytes, item_count)
|
35
|
+
metrics = [
|
36
|
+
"Supportability/invoke_remote_size",
|
37
|
+
"Supportability/invoke_remote_size/#{method.to_s}"
|
38
|
+
]
|
39
|
+
# we may not have an item count, in which case, just record 0 for the exclusive time
|
40
|
+
item_count ||= 0
|
41
|
+
OneApm::Agent.instance.stats_engine.tl_record_unscoped_metrics(metrics, size_bytes, item_count)
|
42
|
+
end
|
43
|
+
|
44
|
+
def log_and_return_response(response)
|
45
|
+
::OneApm::Agent.logger.debug "Received response, status: #{response.code}, encoding: '#{response['content-encoding']}'"
|
46
|
+
|
47
|
+
case response
|
48
|
+
when Net::HTTPSuccess
|
49
|
+
true # do nothing
|
50
|
+
when Net::HTTPUnauthorized
|
51
|
+
raise LicenseException, 'Invalid license key, please visit support.oneapm.com'
|
52
|
+
when Net::HTTPServiceUnavailable
|
53
|
+
raise ServerConnectionException, "Service unavailable (#{response.code}): #{response.message}"
|
54
|
+
when Net::HTTPGatewayTimeOut
|
55
|
+
raise ServerConnectionException, "Gateway timeout (#{response.code}): #{response.message}"
|
56
|
+
when Net::HTTPRequestEntityTooLarge
|
57
|
+
raise UnrecoverableServerException, '413 Request Entity Too Large'
|
58
|
+
when Net::HTTPUnsupportedMediaType
|
59
|
+
raise UnrecoverableServerException, '415 Unsupported Media Type'
|
60
|
+
else
|
61
|
+
raise ServerConnectionException, "Unexpected response from server (#{response.code}): #{response.message}"
|
62
|
+
end
|
63
|
+
|
64
|
+
response
|
65
|
+
end
|
66
|
+
|
67
|
+
def reset_metric_id_cache
|
68
|
+
@metric_id_cache = {}
|
69
|
+
end
|
70
|
+
|
71
|
+
# ===================== Helpers ==========================
|
72
|
+
# takes an array of arrays of spec and id, adds it into the
|
73
|
+
# metric cache so we can save the collector some work by
|
74
|
+
# sending integers instead of strings the next time around
|
75
|
+
def fill_metric_id_cache(pairs_of_specs_and_ids)
|
76
|
+
Array(pairs_of_specs_and_ids).each do |metric_spec_hash, metric_id|
|
77
|
+
metric_spec = MetricSpec.new(metric_spec_hash['name'],
|
78
|
+
metric_spec_hash['scope'])
|
79
|
+
metric_id_cache[metric_spec] = metric_id
|
80
|
+
end
|
81
|
+
rescue => e
|
82
|
+
# If we've gotten this far, we don't want this error to propagate and
|
83
|
+
# make this post appear to have been non-successful, which would trigger
|
84
|
+
# re-aggregation of the same metric data into the next post, so just log
|
85
|
+
OneApm::Agent.logger.error("Failed to fill metric ID cache from response, error details follow ", e)
|
86
|
+
end
|
87
|
+
|
88
|
+
# The collector wants to recieve metric data in a format that's different
|
89
|
+
# from how we store it internally, so this method handles the translation.
|
90
|
+
# It also handles translating metric names to IDs using our metric ID cache.
|
91
|
+
def build_metric_data_array(stats_hash)
|
92
|
+
metric_data_array = []
|
93
|
+
stats_hash.each do |metric_spec, stats|
|
94
|
+
# Omit empty stats as an optimization
|
95
|
+
unless stats.is_reset?
|
96
|
+
metric_id = metric_id_cache[metric_spec]
|
97
|
+
metric_data = if metric_id
|
98
|
+
OneApm::MetricData.new(nil, stats, metric_id)
|
99
|
+
else
|
100
|
+
OneApm::MetricData.new(metric_spec, stats, nil)
|
101
|
+
end
|
102
|
+
metric_data_array << metric_data
|
103
|
+
end
|
104
|
+
end
|
105
|
+
metric_data_array
|
106
|
+
end
|
107
|
+
|
108
|
+
def valid_to_marshal?(data)
|
109
|
+
@marshaller.dump(data)
|
110
|
+
true
|
111
|
+
rescue StandardError, SystemStackError => e
|
112
|
+
OneApm::Agent.logger.warn("Unable to marshal environment report on connect.", e)
|
113
|
+
false
|
114
|
+
end
|
115
|
+
|
116
|
+
# Sets the user agent for connections to the server, to
|
117
|
+
# conform with the HTTP spec and allow for debugging. Includes
|
118
|
+
# the ruby version and also zlib version if available since
|
119
|
+
# that may cause corrupt compression if there is a problem.
|
120
|
+
def user_agent
|
121
|
+
ruby_description = ''
|
122
|
+
# note the trailing space!
|
123
|
+
ruby_description << "(ruby #{::RUBY_VERSION} #{::RUBY_PLATFORM}) " if defined?(::RUBY_VERSION) && defined?(::RUBY_PLATFORM)
|
124
|
+
zlib_version = ''
|
125
|
+
zlib_version << "zlib/#{Zlib.zlib_version}" if defined?(::Zlib) && Zlib.respond_to?(:zlib_version)
|
126
|
+
"OneApm-RubyAgent/#{OneApm::VERSION::STRING} #{ruby_description}#{zlib_version}"
|
127
|
+
end
|
128
|
+
|
129
|
+
private
|
130
|
+
|
131
|
+
# A shorthand for OneApm::Probe.instance
|
132
|
+
def probe
|
133
|
+
OneApm::Probe.instance
|
134
|
+
end
|
135
|
+
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
@@ -0,0 +1,254 @@
|
|
1
|
+
module OneApm
|
2
|
+
module Collector
|
3
|
+
class CollectorService
|
4
|
+
module HttpConnection
|
5
|
+
|
6
|
+
# These include Errno connection errors, and all indicate that the
|
7
|
+
# underlying TCP connection may be in a bad state.
|
8
|
+
CONNECTION_ERRORS = [Timeout::Error, EOFError, SystemCallError, SocketError].freeze
|
9
|
+
|
10
|
+
def invoke_remote(method, payload = [], options = {})
|
11
|
+
start_ts = Time.now
|
12
|
+
|
13
|
+
data = nil
|
14
|
+
begin
|
15
|
+
data = @marshaller.dump(payload, options)
|
16
|
+
if method != :connect
|
17
|
+
data = data.gsub(/(Controller|HttpDispatcher)/, "WebTransaction")
|
18
|
+
end
|
19
|
+
rescue StandardError, SystemStackError => e
|
20
|
+
handle_serialization_error(method, e)
|
21
|
+
end
|
22
|
+
serialize_finish_ts = Time.now
|
23
|
+
|
24
|
+
data, encoding = compress_request_if_needed(data)
|
25
|
+
size = data.size
|
26
|
+
|
27
|
+
uri = remote_method_uri(method, @marshaller.format)
|
28
|
+
full_uri = "#{@collector}#{uri}"
|
29
|
+
|
30
|
+
@audit_logger.log_request(full_uri, payload, @marshaller)
|
31
|
+
response = send_request(:data => data,
|
32
|
+
:uri => uri,
|
33
|
+
:encoding => encoding,
|
34
|
+
:collector => @collector)
|
35
|
+
@marshaller.load(decompress_response(response))
|
36
|
+
ensure
|
37
|
+
record_timing_supportability_metrics(method, start_ts, serialize_finish_ts)
|
38
|
+
if size
|
39
|
+
record_size_supportability_metrics(method, size, options[:item_count])
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# The path on the server that we should post our data to
|
44
|
+
def remote_method_uri(method, format)
|
45
|
+
params = {'run_id' => @agent_id, 'marshal_format' => format}
|
46
|
+
uri = "/tpm/agent.do?PROTOCOL_VERSION=#{PROTOCOL_VERSION}&license_key=#{@license_key}&method=#{method}"
|
47
|
+
uri << '&' + params.map do |k,v|
|
48
|
+
next unless v
|
49
|
+
"#{k}=#{v}"
|
50
|
+
end.compact.join('&')
|
51
|
+
uri
|
52
|
+
end
|
53
|
+
|
54
|
+
# Posts to the specified server
|
55
|
+
#
|
56
|
+
# Options:
|
57
|
+
# - :uri => the path to request on the server (a misnomer of
|
58
|
+
# course)
|
59
|
+
# - :encoding => the encoding to pass to the server
|
60
|
+
# - :collector => a URI object that responds to the 'name' method
|
61
|
+
# and returns the name of the collector to
|
62
|
+
# contact
|
63
|
+
# - :data => the data to send as the body of the request
|
64
|
+
def send_request(opts)
|
65
|
+
request = Net::HTTP::Post.new(opts[:uri], 'CONTENT-ENCODING' => opts[:encoding], 'HOST' => opts[:collector].name)
|
66
|
+
request['user-agent'] = user_agent
|
67
|
+
request.content_type = "application/octet-stream"
|
68
|
+
request.body = opts[:data]
|
69
|
+
|
70
|
+
response = nil
|
71
|
+
attempts = 0
|
72
|
+
max_attempts = 2
|
73
|
+
|
74
|
+
begin
|
75
|
+
attempts += 1
|
76
|
+
conn = http_connection
|
77
|
+
::OneApm::Agent.logger.debug "Sending request to #{opts[:collector]}#{opts[:uri]}"
|
78
|
+
OneApm::TimerLib.timeout(@request_timeout) do
|
79
|
+
response = conn.request(request)
|
80
|
+
end
|
81
|
+
rescue *CONNECTION_ERRORS => e
|
82
|
+
close_shared_connection
|
83
|
+
if attempts < max_attempts
|
84
|
+
::OneApm::Agent.logger.debug("Retrying request to #{opts[:collector]}#{opts[:uri]} after #{e}")
|
85
|
+
retry
|
86
|
+
else
|
87
|
+
raise ServerConnectionException, "Recoverable error talking to #{@collector} after #{attempts} attempts: #{e}"
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
log_and_return_response response
|
92
|
+
end
|
93
|
+
|
94
|
+
def session(&block)
|
95
|
+
raise ArgumentError, "#{self.class}#shared_connection must be passed a block" unless block_given?
|
96
|
+
|
97
|
+
begin
|
98
|
+
t0 = Time.now
|
99
|
+
@in_session = true
|
100
|
+
if OneApm::Agent.config[:aggressive_keepalive]
|
101
|
+
session_with_keepalive(&block)
|
102
|
+
else
|
103
|
+
session_without_keepalive(&block)
|
104
|
+
end
|
105
|
+
rescue *CONNECTION_ERRORS => e
|
106
|
+
elapsed = Time.now - t0
|
107
|
+
raise OneApm::ServerConnectionException, "Recoverable error connecting to #{@collector} after #{elapsed} seconds: #{e}"
|
108
|
+
ensure
|
109
|
+
@in_session = false
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def session_with_keepalive(&block)
|
114
|
+
establish_shared_connection
|
115
|
+
block.call
|
116
|
+
end
|
117
|
+
|
118
|
+
def session_without_keepalive(&block)
|
119
|
+
begin
|
120
|
+
establish_shared_connection
|
121
|
+
block.call
|
122
|
+
ensure
|
123
|
+
close_shared_connection
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def http_connection
|
128
|
+
if @in_session
|
129
|
+
establish_shared_connection
|
130
|
+
else
|
131
|
+
create_http_connection
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def establish_shared_connection
|
136
|
+
unless @shared_tcp_connection
|
137
|
+
@shared_tcp_connection = create_and_start_http_connection
|
138
|
+
end
|
139
|
+
@shared_tcp_connection
|
140
|
+
end
|
141
|
+
|
142
|
+
def close_shared_connection
|
143
|
+
if @shared_tcp_connection
|
144
|
+
::OneApm::Agent.logger.debug("Closing shared TCP connection to #{@shared_tcp_connection.address}:#{@shared_tcp_connection.port}")
|
145
|
+
@shared_tcp_connection.finish if @shared_tcp_connection.started?
|
146
|
+
@shared_tcp_connection = nil
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def setup_connection_timeouts(conn)
|
151
|
+
# We use Timeout explicitly instead of this
|
152
|
+
conn.read_timeout = nil
|
153
|
+
|
154
|
+
if conn.respond_to?(:keep_alive_timeout) && OneApm::Agent.config[:aggressive_keepalive]
|
155
|
+
conn.keep_alive_timeout = OneApm::Agent.config[:keep_alive_timeout]
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def create_and_start_http_connection
|
160
|
+
conn = create_http_connection
|
161
|
+
start_connection(conn)
|
162
|
+
conn
|
163
|
+
end
|
164
|
+
|
165
|
+
def start_connection(conn)
|
166
|
+
OneApm::Agent.logger.debug("Opening TCP connection to #{conn.address}:#{conn.port}")
|
167
|
+
OneApm::TimerLib.timeout(@request_timeout) { conn.start }
|
168
|
+
conn
|
169
|
+
end
|
170
|
+
|
171
|
+
# Return the Net::HTTP with proxy configuration given the OneApm::Support::Server object.
|
172
|
+
def create_http_connection
|
173
|
+
# Proxy returns regular HTTP if @proxy_host is nil (the default)
|
174
|
+
http_class = Net::HTTP::Proxy(proxy_server.name, proxy_server.port,
|
175
|
+
proxy_server.user, proxy_server.password)
|
176
|
+
|
177
|
+
conn = http_class.new((@collector.ip || @collector.name), @collector.port)
|
178
|
+
setup_connection_for_ssl(conn) if Agent.config[:ssl]
|
179
|
+
setup_connection_timeouts(conn)
|
180
|
+
|
181
|
+
::OneApm::Agent.logger.debug("Created net/http handle to #{conn.address}:#{conn.port}")
|
182
|
+
conn
|
183
|
+
end
|
184
|
+
|
185
|
+
def setup_connection_for_ssl(conn)
|
186
|
+
# Jruby 1.6.8 requires a gem for full ssl support and will throw
|
187
|
+
# an error when use_ssl=(true) is called and jruby-openssl isn't
|
188
|
+
# installed
|
189
|
+
conn.use_ssl = true
|
190
|
+
conn.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
191
|
+
conn.cert_store = ssl_cert_store
|
192
|
+
rescue StandardError, LoadError
|
193
|
+
msg = "Agent is configured to use SSL, but SSL is not available in the environment. "
|
194
|
+
msg << "Either disable SSL in the agent configuration, or install SSL support."
|
195
|
+
raise UnrecoverableAgentException.new(msg)
|
196
|
+
end
|
197
|
+
|
198
|
+
def ssl_cert_store
|
199
|
+
path = cert_file_path
|
200
|
+
if !@ssl_cert_store || path != @cached_cert_store_path
|
201
|
+
::OneApm::Agent.logger.debug("Creating SSL certificate store from file at #{path}")
|
202
|
+
@ssl_cert_store = OpenSSL::X509::Store.new
|
203
|
+
@ssl_cert_store.add_file(path)
|
204
|
+
@cached_cert_store_path = path
|
205
|
+
end
|
206
|
+
@ssl_cert_store
|
207
|
+
end
|
208
|
+
|
209
|
+
# The path to the certificate file used to verify the SSL
|
210
|
+
# connection if verify_peer is enabled
|
211
|
+
def cert_file_path
|
212
|
+
if path_override = OneApm::Agent.config[:ca_bundle_path]
|
213
|
+
OneApm::Agent.logger.warn("Couldn't find CA bundle from configured ca_bundle_path: #{path_override}") unless File.exists? path_override
|
214
|
+
path_override
|
215
|
+
else
|
216
|
+
File.expand_path(File.join(probe.oneapm_root, 'config', 'cert', 'cacert.pem'))
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
# We do not compress if content is smaller than 64kb. There are
|
221
|
+
# problems with bugs in Ruby in some versions that expose us
|
222
|
+
# to a risk of segfaults if we compress aggressively.
|
223
|
+
def compress_request_if_needed(data)
|
224
|
+
encoding = 'identity'
|
225
|
+
if data.size > 64 * 1024
|
226
|
+
data = OneApm::Support::Encoders::Compressed.encode(data)
|
227
|
+
encoding = 'deflate'
|
228
|
+
end
|
229
|
+
check_post_size(data)
|
230
|
+
[data, encoding]
|
231
|
+
end
|
232
|
+
|
233
|
+
# Decompresses the response from the server, if it is gzip
|
234
|
+
# encoded, otherwise returns it verbatim
|
235
|
+
def decompress_response(response)
|
236
|
+
if response['content-encoding'] == 'gzip'
|
237
|
+
Zlib::GzipReader.new(StringIO.new(response.body)).read
|
238
|
+
else
|
239
|
+
response.body
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
# Raises an UnrecoverableServerException if the post_string is longer
|
244
|
+
# than the limit configured in the probe object
|
245
|
+
def check_post_size(post_string)
|
246
|
+
return if post_string.size < Agent.config[:post_size_limit]
|
247
|
+
::OneApm::Agent.logger.debug "Tried to send too much data: #{post_string.size} bytes"
|
248
|
+
raise UnrecoverableServerException.new('413 Request Entity Too Large')
|
249
|
+
end
|
250
|
+
|
251
|
+
end
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|