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.
Files changed (234) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +30 -0
  3. data/.rubocop.yml +725 -0
  4. data/Gemfile +3 -0
  5. data/Guardfile +7 -0
  6. data/LICENSE +1 -0
  7. data/README.md +3 -0
  8. data/config/cert/cacert.pem +1177 -0
  9. data/config/database.yml +5 -0
  10. data/lib/initializers/goliath.rb +11 -0
  11. data/lib/initializers/other.rb +1 -0
  12. data/lib/initializers/rails.rb +15 -0
  13. data/lib/one_apm/agent.rb +253 -0
  14. data/lib/one_apm/agent/agent.rb +283 -0
  15. data/lib/one_apm/agent/agent/connect.rb +175 -0
  16. data/lib/one_apm/agent/agent/container_data_manager.rb +218 -0
  17. data/lib/one_apm/agent/agent/forkable_dispatcher_functions.rb +96 -0
  18. data/lib/one_apm/agent/agent/helpers.rb +45 -0
  19. data/lib/one_apm/agent/agent/start.rb +226 -0
  20. data/lib/one_apm/agent/agent/start_worker_thread.rb +148 -0
  21. data/lib/one_apm/agent/busy_calculator.rb +115 -0
  22. data/lib/one_apm/agent/cross_app/cross_app_monitor.rb +181 -0
  23. data/lib/one_apm/agent/cross_app/cross_app_tracing.rb +336 -0
  24. data/lib/one_apm/agent/database.rb +308 -0
  25. data/lib/one_apm/agent/database/active_record_helper.rb +80 -0
  26. data/lib/one_apm/agent/database/obfuscation_helpers.rb +76 -0
  27. data/lib/one_apm/agent/database/obfuscator.rb +78 -0
  28. data/lib/one_apm/agent/database/postgres_explain_obfuscator.rb +45 -0
  29. data/lib/one_apm/agent/datastores.rb +175 -0
  30. data/lib/one_apm/agent/datastores/metric_helper.rb +83 -0
  31. data/lib/one_apm/agent/datastores/mongo.rb +27 -0
  32. data/lib/one_apm/agent/datastores/mongo/metric_translator.rb +189 -0
  33. data/lib/one_apm/agent/datastores/mongo/obfuscator.rb +37 -0
  34. data/lib/one_apm/agent/datastores/mongo/statement_formatter.rb +51 -0
  35. data/lib/one_apm/agent/event/event_listener.rb +40 -0
  36. data/lib/one_apm/agent/event/event_loop.rb +191 -0
  37. data/lib/one_apm/agent/event/worker_loop.rb +97 -0
  38. data/lib/one_apm/agent/harvester.rb +48 -0
  39. data/lib/one_apm/agent/inbound_request_monitor.rb +30 -0
  40. data/lib/one_apm/agent/javascript_instrumentor.rb +186 -0
  41. data/lib/one_apm/agent/pipe/pipe_channel_manager.rb +275 -0
  42. data/lib/one_apm/agent/pipe/pipe_service.rb +81 -0
  43. data/lib/one_apm/agent/sampler.rb +55 -0
  44. data/lib/one_apm/agent/sampler_collection.rb +65 -0
  45. data/lib/one_apm/agent/samplers/cpu_sampler.rb +49 -0
  46. data/lib/one_apm/agent/samplers/delayed_job_sampler.rb +109 -0
  47. data/lib/one_apm/agent/samplers/memory_sampler.rb +144 -0
  48. data/lib/one_apm/agent/samplers/object_sampler.rb +22 -0
  49. data/lib/one_apm/agent/samplers/vm_sampler.rb +124 -0
  50. data/lib/one_apm/agent/synthetics_monitor.rb +48 -0
  51. data/lib/one_apm/agent/threading/agent_thread.rb +74 -0
  52. data/lib/one_apm/agent/threading/backtrace_node.rb +133 -0
  53. data/lib/one_apm/agent/threading/backtrace_service.rb +259 -0
  54. data/lib/one_apm/agent/threading/thread_profile.rb +155 -0
  55. data/lib/one_apm/collector/collector/helper.rb +139 -0
  56. data/lib/one_apm/collector/collector/http_connection.rb +254 -0
  57. data/lib/one_apm/collector/collector/server_methods.rb +71 -0
  58. data/lib/one_apm/collector/collector_service.rb +123 -0
  59. data/lib/one_apm/collector/commands/agent_command.rb +17 -0
  60. data/lib/one_apm/collector/commands/thread_profiler_session.rb +108 -0
  61. data/lib/one_apm/collector/commands/xray_session.rb +53 -0
  62. data/lib/one_apm/collector/commands/xray_session_collection.rb +156 -0
  63. data/lib/one_apm/collector/containers/agent_command_router.rb +153 -0
  64. data/lib/one_apm/collector/containers/custom_event_aggregator.rb +94 -0
  65. data/lib/one_apm/collector/containers/error_collector.rb +349 -0
  66. data/lib/one_apm/collector/containers/sql_sampler.rb +331 -0
  67. data/lib/one_apm/collector/containers/stats_engine.rb +34 -0
  68. data/lib/one_apm/collector/containers/transaction_event_aggregator.rb +249 -0
  69. data/lib/one_apm/collector/containers/transaction_sampler.rb +352 -0
  70. data/lib/one_apm/collector/containers/utilization_data.rb +36 -0
  71. data/lib/one_apm/collector/stats_engine/gc_profiler.rb +106 -0
  72. data/lib/one_apm/collector/stats_engine/metric_stats.rb +243 -0
  73. data/lib/one_apm/collector/stats_engine/stats_hash.rb +105 -0
  74. data/lib/one_apm/configuration.rb +429 -0
  75. data/lib/one_apm/configuration/autostart.rb +41 -0
  76. data/lib/one_apm/configuration/default_source.rb +1026 -0
  77. data/lib/one_apm/configuration/environment_source.rb +113 -0
  78. data/lib/one_apm/configuration/high_security_source.rb +56 -0
  79. data/lib/one_apm/configuration/manual_source.rb +13 -0
  80. data/lib/one_apm/configuration/server_source.rb +60 -0
  81. data/lib/one_apm/configuration/yaml_source.rb +134 -0
  82. data/lib/one_apm/errors/agent_errors.rb +26 -0
  83. data/lib/one_apm/errors/internal_agent_error.rb +16 -0
  84. data/lib/one_apm/errors/noticed_error.rb +79 -0
  85. data/lib/one_apm/frameworks/external.rb +15 -0
  86. data/lib/one_apm/frameworks/rails.rb +103 -0
  87. data/lib/one_apm/frameworks/rails3.rb +37 -0
  88. data/lib/one_apm/frameworks/rails4.rb +21 -0
  89. data/lib/one_apm/frameworks/ruby.rb +21 -0
  90. data/lib/one_apm/frameworks/sinatra.rb +12 -0
  91. data/lib/one_apm/inst/3rd/active_merchant.rb +35 -0
  92. data/lib/one_apm/inst/3rd/acts_as_solr.rb +70 -0
  93. data/lib/one_apm/inst/3rd/authlogic.rb +23 -0
  94. data/lib/one_apm/inst/3rd/sunspot.rb +31 -0
  95. data/lib/one_apm/inst/background_job/active_job.rb +88 -0
  96. data/lib/one_apm/inst/background_job/delayed_job.rb +52 -0
  97. data/lib/one_apm/inst/background_job/delayed_job_injection.rb +8 -0
  98. data/lib/one_apm/inst/background_job/resque.rb +107 -0
  99. data/lib/one_apm/inst/background_job/sidekiq.rb +64 -0
  100. data/lib/one_apm/inst/dispatcher/passenger.rb +25 -0
  101. data/lib/one_apm/inst/dispatcher/rainbows.rb +23 -0
  102. data/lib/one_apm/inst/framework/grape.rb +94 -0
  103. data/lib/one_apm/inst/framework/padrino.rb +30 -0
  104. data/lib/one_apm/inst/framework/sinatra.rb +185 -0
  105. data/lib/one_apm/inst/framework/sinatra/ignorer.rb +50 -0
  106. data/lib/one_apm/inst/framework/sinatra/transaction_namer.rb +54 -0
  107. data/lib/one_apm/inst/http_clients/curb.rb +189 -0
  108. data/lib/one_apm/inst/http_clients/excon.rb +70 -0
  109. data/lib/one_apm/inst/http_clients/excon/connection.rb +31 -0
  110. data/lib/one_apm/inst/http_clients/excon/middleware.rb +55 -0
  111. data/lib/one_apm/inst/http_clients/httpclient.rb +44 -0
  112. data/lib/one_apm/inst/http_clients/net.rb +34 -0
  113. data/lib/one_apm/inst/http_clients/typhoeus.rb +76 -0
  114. data/lib/one_apm/inst/nosql/memcache.rb +134 -0
  115. data/lib/one_apm/inst/nosql/mongo.rb +126 -0
  116. data/lib/one_apm/inst/nosql/mongo_moped.rb +85 -0
  117. data/lib/one_apm/inst/nosql/redis.rb +83 -0
  118. data/lib/one_apm/inst/orm/active_record.rb +99 -0
  119. data/lib/one_apm/inst/orm/active_record_4.rb +28 -0
  120. data/lib/one_apm/inst/orm/data_mapper.rb +180 -0
  121. data/lib/one_apm/inst/orm/sequel.rb +47 -0
  122. data/lib/one_apm/inst/rack.rb +38 -0
  123. data/lib/one_apm/inst/rack/rack.rb +44 -0
  124. data/lib/one_apm/inst/rack/rack_builder.rb +51 -0
  125. data/lib/one_apm/inst/rails/action_controller.rb +118 -0
  126. data/lib/one_apm/inst/rails/action_web_service.rb +44 -0
  127. data/lib/one_apm/inst/rails/errors.rb +43 -0
  128. data/lib/one_apm/inst/rails3/action_controller.rb +172 -0
  129. data/lib/one_apm/inst/rails3/errors.rb +43 -0
  130. data/lib/one_apm/inst/rails4/action_controller.rb +27 -0
  131. data/lib/one_apm/inst/rails4/action_controller_subscriber.rb +121 -0
  132. data/lib/one_apm/inst/rails4/action_view.rb +23 -0
  133. data/lib/one_apm/inst/rails4/action_view_subscriber.rb +93 -0
  134. data/lib/one_apm/inst/rails4/active_record_subscriber.rb +96 -0
  135. data/lib/one_apm/inst/rails4/errors.rb +42 -0
  136. data/lib/one_apm/inst/rails_middleware.rb +40 -0
  137. data/lib/one_apm/inst/support/evented_subscriber.rb +98 -0
  138. data/lib/one_apm/inst/support/ignore_actions.rb +39 -0
  139. data/lib/one_apm/inst/support/queue_time.rb +76 -0
  140. data/lib/one_apm/inst/transaction_base.rb +405 -0
  141. data/lib/one_apm/logger/agent_logger.rb +206 -0
  142. data/lib/one_apm/logger/audit_logger.rb +78 -0
  143. data/lib/one_apm/logger/memory_logger.rb +50 -0
  144. data/lib/one_apm/logger/null_logger.rb +19 -0
  145. data/lib/one_apm/metrics/metric_data.rb +72 -0
  146. data/lib/one_apm/metrics/metric_spec.rb +82 -0
  147. data/lib/one_apm/metrics/stats.rb +173 -0
  148. data/lib/one_apm/probe.rb +16 -0
  149. data/lib/one_apm/probe/framework_loader.rb +53 -0
  150. data/lib/one_apm/probe/instance_methods.rb +105 -0
  151. data/lib/one_apm/probe/instrumentation.rb +60 -0
  152. data/lib/one_apm/rack/browser_monitoring.rb +144 -0
  153. data/lib/one_apm/rack/middleware_base.rb +27 -0
  154. data/lib/one_apm/rack/middleware_hooks.rb +17 -0
  155. data/lib/one_apm/rack/middleware_tracing.rb +81 -0
  156. data/lib/one_apm/rack/middleware_wrapper.rb +86 -0
  157. data/lib/one_apm/support/chained_call.rb +15 -0
  158. data/lib/one_apm/support/coerce.rb +81 -0
  159. data/lib/one_apm/support/collection_helper.rb +79 -0
  160. data/lib/one_apm/support/dotted_hash.rb +45 -0
  161. data/lib/one_apm/support/encoders.rb +34 -0
  162. data/lib/one_apm/support/environment_report.rb +127 -0
  163. data/lib/one_apm/support/event_buffer.rb +82 -0
  164. data/lib/one_apm/support/event_buffer/sampled_buffer.rb +45 -0
  165. data/lib/one_apm/support/event_buffer/sized_buffer.rb +21 -0
  166. data/lib/one_apm/support/event_buffer/synthetics_event_buffer.rb +40 -0
  167. data/lib/one_apm/support/helper.rb +49 -0
  168. data/lib/one_apm/support/hostname.rb +13 -0
  169. data/lib/one_apm/support/http_clients/curb_wrappers.rb +65 -0
  170. data/lib/one_apm/support/http_clients/excon_wrappers.rb +63 -0
  171. data/lib/one_apm/support/http_clients/httpclient_wrappers.rb +61 -0
  172. data/lib/one_apm/support/http_clients/net_http_wrappers.rb +48 -0
  173. data/lib/one_apm/support/http_clients/typhoeus_wrappers.rb +73 -0
  174. data/lib/one_apm/support/http_clients/uri_util.rb +39 -0
  175. data/lib/one_apm/support/json_marshaller.rb +68 -0
  176. data/lib/one_apm/support/json_wrapper.rb +130 -0
  177. data/lib/one_apm/support/language_support.rb +142 -0
  178. data/lib/one_apm/support/library_detection.rb +119 -0
  179. data/lib/one_apm/support/local_environment.rb +196 -0
  180. data/lib/one_apm/support/marshaller.rb +62 -0
  181. data/lib/one_apm/support/method_tracer.rb +334 -0
  182. data/lib/one_apm/support/method_tracer/helpers.rb +92 -0
  183. data/lib/one_apm/support/method_tracer/traced_method_stack.rb +103 -0
  184. data/lib/one_apm/support/obfuscator.rb +47 -0
  185. data/lib/one_apm/support/okjson.rb +601 -0
  186. data/lib/one_apm/support/parameter_filtering.rb +35 -0
  187. data/lib/one_apm/support/rules_engine.rb +56 -0
  188. data/lib/one_apm/support/rules_engine/replacement_rule.rb +80 -0
  189. data/lib/one_apm/support/rules_engine/segment_terms_rule.rb +46 -0
  190. data/lib/one_apm/support/server.rb +11 -0
  191. data/lib/one_apm/support/supported_versions.rb +257 -0
  192. data/lib/one_apm/support/system_info.rb +211 -0
  193. data/lib/one_apm/support/timer_lib.rb +29 -0
  194. data/lib/one_apm/support/version_number.rb +51 -0
  195. data/lib/one_apm/support/vm.rb +30 -0
  196. data/lib/one_apm/support/vm/jruby_vm.rb +38 -0
  197. data/lib/one_apm/support/vm/monotonic_gc_profiler.rb +43 -0
  198. data/lib/one_apm/support/vm/mri_vm.rb +85 -0
  199. data/lib/one_apm/support/vm/rubinius_vm.rb +129 -0
  200. data/lib/one_apm/support/vm/snapshot.rb +18 -0
  201. data/lib/one_apm/transaction.rb +336 -0
  202. data/lib/one_apm/transaction/class_methods.rb +132 -0
  203. data/lib/one_apm/transaction/instance_helpers.rb +82 -0
  204. data/lib/one_apm/transaction/metric_constants.rb +42 -0
  205. data/lib/one_apm/transaction/sample_buffer/force_persist_sample_buffer.rb +21 -0
  206. data/lib/one_apm/transaction/sample_buffer/slowest_sample_buffer.rb +21 -0
  207. data/lib/one_apm/transaction/sample_buffer/synthetics_sample_buffer.rb +21 -0
  208. data/lib/one_apm/transaction/sample_buffer/transaction_sample_buffer.rb +101 -0
  209. data/lib/one_apm/transaction/sample_buffer/xray_sample_buffer.rb +60 -0
  210. data/lib/one_apm/transaction/segment.rb +193 -0
  211. data/lib/one_apm/transaction/segment_summary.rb +51 -0
  212. data/lib/one_apm/transaction/thread_local_access.rb +73 -0
  213. data/lib/one_apm/transaction/transaction_analysis.rb +78 -0
  214. data/lib/one_apm/transaction/transaction_apdex.rb +20 -0
  215. data/lib/one_apm/transaction/transaction_cpu.rb +22 -0
  216. data/lib/one_apm/transaction/transaction_finish_append.rb +67 -0
  217. data/lib/one_apm/transaction/transaction_ignore.rb +33 -0
  218. data/lib/one_apm/transaction/transaction_jruby_functions.rb +40 -0
  219. data/lib/one_apm/transaction/transaction_metrics.rb +53 -0
  220. data/lib/one_apm/transaction/transaction_name.rb +90 -0
  221. data/lib/one_apm/transaction/transaction_namer.rb +49 -0
  222. data/lib/one_apm/transaction/transaction_sample.rb +204 -0
  223. data/lib/one_apm/transaction/transaction_sample_builder.rb +168 -0
  224. data/lib/one_apm/transaction/transaction_state.rb +149 -0
  225. data/lib/one_apm/transaction/transaction_summary.rb +28 -0
  226. data/lib/one_apm/transaction/transaction_synthetics.rb +40 -0
  227. data/lib/one_apm/transaction/transaction_timings.rb +54 -0
  228. data/lib/one_apm/version.rb +13 -0
  229. data/lib/oneapm_rpm.rb +16 -0
  230. data/lib/sequel/extensions/oneapm_instrumentation.rb +84 -0
  231. data/lib/sequel/plugins/oneapm_instrumentation.rb +66 -0
  232. data/oneapm.yml +135 -0
  233. data/oneapm_rpm.gemspec +58 -0
  234. 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