ghazel-newrelic_rpm 3.1.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (255) hide show
  1. data/CHANGELOG +525 -0
  2. data/LICENSE +37 -0
  3. data/README.rdoc +179 -0
  4. data/bin/mongrel_rpm +33 -0
  5. data/bin/newrelic +13 -0
  6. data/bin/newrelic_cmd +5 -0
  7. data/cert/cacert.pem +118 -0
  8. data/cert/oldsite.pem +28 -0
  9. data/cert/site.pem +27 -0
  10. data/install.rb +9 -0
  11. data/lib/conditional_vendored_dependency_detection.rb +3 -0
  12. data/lib/conditional_vendored_metric_parser.rb +5 -0
  13. data/lib/new_relic/agent.rb +423 -0
  14. data/lib/new_relic/agent/agent.rb +1040 -0
  15. data/lib/new_relic/agent/beacon_configuration.rb +61 -0
  16. data/lib/new_relic/agent/browser_monitoring.rb +83 -0
  17. data/lib/new_relic/agent/busy_calculator.rb +91 -0
  18. data/lib/new_relic/agent/chained_call.rb +13 -0
  19. data/lib/new_relic/agent/error_collector.rb +203 -0
  20. data/lib/new_relic/agent/instrumentation.rb +9 -0
  21. data/lib/new_relic/agent/instrumentation/active_merchant.rb +21 -0
  22. data/lib/new_relic/agent/instrumentation/acts_as_solr.rb +62 -0
  23. data/lib/new_relic/agent/instrumentation/authlogic.rb +13 -0
  24. data/lib/new_relic/agent/instrumentation/controller_instrumentation.rb +413 -0
  25. data/lib/new_relic/agent/instrumentation/data_mapper.rb +232 -0
  26. data/lib/new_relic/agent/instrumentation/delayed_job_instrumentation.rb +44 -0
  27. data/lib/new_relic/agent/instrumentation/memcache.rb +56 -0
  28. data/lib/new_relic/agent/instrumentation/merb/controller.rb +36 -0
  29. data/lib/new_relic/agent/instrumentation/merb/errors.rb +23 -0
  30. data/lib/new_relic/agent/instrumentation/metric_frame.rb +326 -0
  31. data/lib/new_relic/agent/instrumentation/metric_frame/pop.rb +84 -0
  32. data/lib/new_relic/agent/instrumentation/net.rb +23 -0
  33. data/lib/new_relic/agent/instrumentation/passenger_instrumentation.rb +34 -0
  34. data/lib/new_relic/agent/instrumentation/queue_time.rb +210 -0
  35. data/lib/new_relic/agent/instrumentation/rack.rb +98 -0
  36. data/lib/new_relic/agent/instrumentation/rails/action_controller.rb +83 -0
  37. data/lib/new_relic/agent/instrumentation/rails/action_web_service.rb +36 -0
  38. data/lib/new_relic/agent/instrumentation/rails/active_record_instrumentation.rb +108 -0
  39. data/lib/new_relic/agent/instrumentation/rails/errors.rb +36 -0
  40. data/lib/new_relic/agent/instrumentation/rails3/action_controller.rb +60 -0
  41. data/lib/new_relic/agent/instrumentation/rails3/active_record_instrumentation.rb +112 -0
  42. data/lib/new_relic/agent/instrumentation/rails3/errors.rb +31 -0
  43. data/lib/new_relic/agent/instrumentation/sinatra.rb +54 -0
  44. data/lib/new_relic/agent/instrumentation/sunspot.rb +23 -0
  45. data/lib/new_relic/agent/instrumentation/unicorn_instrumentation.rb +16 -0
  46. data/lib/new_relic/agent/method_tracer.rb +422 -0
  47. data/lib/new_relic/agent/sampler.rb +50 -0
  48. data/lib/new_relic/agent/samplers/cpu_sampler.rb +58 -0
  49. data/lib/new_relic/agent/samplers/delayed_job_lock_sampler.rb +40 -0
  50. data/lib/new_relic/agent/samplers/memory_sampler.rb +144 -0
  51. data/lib/new_relic/agent/samplers/object_sampler.rb +26 -0
  52. data/lib/new_relic/agent/shim_agent.rb +29 -0
  53. data/lib/new_relic/agent/stats_engine.rb +24 -0
  54. data/lib/new_relic/agent/stats_engine/metric_stats.rb +182 -0
  55. data/lib/new_relic/agent/stats_engine/samplers.rb +89 -0
  56. data/lib/new_relic/agent/stats_engine/transactions.rb +184 -0
  57. data/lib/new_relic/agent/transaction_sample_builder.rb +101 -0
  58. data/lib/new_relic/agent/transaction_sampler.rb +378 -0
  59. data/lib/new_relic/agent/worker_loop.rb +81 -0
  60. data/lib/new_relic/collection_helper.rb +69 -0
  61. data/lib/new_relic/command.rb +85 -0
  62. data/lib/new_relic/commands/deployments.rb +105 -0
  63. data/lib/new_relic/commands/install.rb +91 -0
  64. data/lib/new_relic/control.rb +48 -0
  65. data/lib/new_relic/control/class_methods.rb +47 -0
  66. data/lib/new_relic/control/configuration.rb +157 -0
  67. data/lib/new_relic/control/frameworks/external.rb +16 -0
  68. data/lib/new_relic/control/frameworks/merb.rb +30 -0
  69. data/lib/new_relic/control/frameworks/rails.rb +151 -0
  70. data/lib/new_relic/control/frameworks/rails3.rb +70 -0
  71. data/lib/new_relic/control/frameworks/ruby.rb +42 -0
  72. data/lib/new_relic/control/frameworks/sinatra.rb +23 -0
  73. data/lib/new_relic/control/instance_methods.rb +154 -0
  74. data/lib/new_relic/control/instrumentation.rb +81 -0
  75. data/lib/new_relic/control/logging_methods.rb +88 -0
  76. data/lib/new_relic/control/profiling.rb +25 -0
  77. data/lib/new_relic/control/server_methods.rb +104 -0
  78. data/lib/new_relic/data_serialization.rb +84 -0
  79. data/lib/new_relic/delayed_job_injection.rb +38 -0
  80. data/lib/new_relic/histogram.rb +91 -0
  81. data/lib/new_relic/local_environment.rb +391 -0
  82. data/lib/new_relic/merbtasks.rb +6 -0
  83. data/lib/new_relic/metric_data.rb +46 -0
  84. data/lib/new_relic/metric_spec.rb +73 -0
  85. data/lib/new_relic/metrics.rb +9 -0
  86. data/lib/new_relic/noticed_error.rb +24 -0
  87. data/lib/new_relic/rack/browser_monitoring.rb +68 -0
  88. data/lib/new_relic/rack/developer_mode.rb +267 -0
  89. data/lib/new_relic/rack/metric_app.rb +65 -0
  90. data/lib/new_relic/rack/mongrel_rpm.ru +28 -0
  91. data/lib/new_relic/rack/newrelic.yml +27 -0
  92. data/lib/new_relic/rack_app.rb +6 -0
  93. data/lib/new_relic/recipes.rb +73 -0
  94. data/lib/new_relic/stats.rb +378 -0
  95. data/lib/new_relic/timer_lib.rb +27 -0
  96. data/lib/new_relic/transaction_analysis.rb +76 -0
  97. data/lib/new_relic/transaction_analysis/segment_summary.rb +47 -0
  98. data/lib/new_relic/transaction_sample.rb +272 -0
  99. data/lib/new_relic/transaction_sample/composite_segment.rb +27 -0
  100. data/lib/new_relic/transaction_sample/fake_segment.rb +9 -0
  101. data/lib/new_relic/transaction_sample/segment.rb +251 -0
  102. data/lib/new_relic/transaction_sample/summary_segment.rb +21 -0
  103. data/lib/new_relic/url_rule.rb +14 -0
  104. data/lib/new_relic/version.rb +55 -0
  105. data/lib/newrelic_rpm.rb +49 -0
  106. data/lib/tasks/all.rb +4 -0
  107. data/lib/tasks/install.rake +7 -0
  108. data/lib/tasks/tests.rake +19 -0
  109. data/newrelic.yml +265 -0
  110. data/newrelic_rpm.gemspec +330 -0
  111. data/recipes/newrelic.rb +6 -0
  112. data/test/active_record_fixtures.rb +77 -0
  113. data/test/config/newrelic.yml +48 -0
  114. data/test/config/test_control.rb +48 -0
  115. data/test/new_relic/agent/agent/connect_test.rb +316 -0
  116. data/test/new_relic/agent/agent/start_test.rb +340 -0
  117. data/test/new_relic/agent/agent/start_worker_thread_test.rb +155 -0
  118. data/test/new_relic/agent/agent_test.rb +173 -0
  119. data/test/new_relic/agent/agent_test_controller.rb +77 -0
  120. data/test/new_relic/agent/agent_test_controller_test.rb +366 -0
  121. data/test/new_relic/agent/apdex_from_server_test.rb +9 -0
  122. data/test/new_relic/agent/beacon_configuration_test.rb +104 -0
  123. data/test/new_relic/agent/browser_monitoring_test.rb +274 -0
  124. data/test/new_relic/agent/busy_calculator_test.rb +81 -0
  125. data/test/new_relic/agent/error_collector/notice_error_test.rb +257 -0
  126. data/test/new_relic/agent/error_collector_test.rb +168 -0
  127. data/test/new_relic/agent/instrumentation/active_record_instrumentation_test.rb +522 -0
  128. data/test/new_relic/agent/instrumentation/controller_instrumentation_test.rb +36 -0
  129. data/test/new_relic/agent/instrumentation/instrumentation_test.rb +11 -0
  130. data/test/new_relic/agent/instrumentation/metric_frame/pop_test.rb +179 -0
  131. data/test/new_relic/agent/instrumentation/metric_frame_test.rb +50 -0
  132. data/test/new_relic/agent/instrumentation/net_instrumentation_test.rb +84 -0
  133. data/test/new_relic/agent/instrumentation/queue_time_test.rb +387 -0
  134. data/test/new_relic/agent/instrumentation/rack_test.rb +35 -0
  135. data/test/new_relic/agent/instrumentation/task_instrumentation_test.rb +184 -0
  136. data/test/new_relic/agent/memcache_instrumentation_test.rb +107 -0
  137. data/test/new_relic/agent/method_tracer/class_methods/add_method_tracer_test.rb +164 -0
  138. data/test/new_relic/agent/method_tracer/instance_methods/trace_execution_scoped_test.rb +234 -0
  139. data/test/new_relic/agent/method_tracer_test.rb +386 -0
  140. data/test/new_relic/agent/mock_scope_listener.rb +23 -0
  141. data/test/new_relic/agent/rpm_agent_test.rb +149 -0
  142. data/test/new_relic/agent/sampler_test.rb +19 -0
  143. data/test/new_relic/agent/shim_agent_test.rb +20 -0
  144. data/test/new_relic/agent/stats_engine/metric_stats/harvest_test.rb +150 -0
  145. data/test/new_relic/agent/stats_engine/metric_stats_test.rb +81 -0
  146. data/test/new_relic/agent/stats_engine/samplers_test.rb +99 -0
  147. data/test/new_relic/agent/stats_engine_test.rb +185 -0
  148. data/test/new_relic/agent/transaction_sample_builder_test.rb +195 -0
  149. data/test/new_relic/agent/transaction_sampler_test.rb +945 -0
  150. data/test/new_relic/agent/worker_loop_test.rb +66 -0
  151. data/test/new_relic/collection_helper_test.rb +127 -0
  152. data/test/new_relic/command/deployments_test.rb +68 -0
  153. data/test/new_relic/control/class_methods_test.rb +62 -0
  154. data/test/new_relic/control/logging_methods_test.rb +157 -0
  155. data/test/new_relic/control_test.rb +195 -0
  156. data/test/new_relic/data_serialization_test.rb +70 -0
  157. data/test/new_relic/local_environment_test.rb +72 -0
  158. data/test/new_relic/metric_data_test.rb +125 -0
  159. data/test/new_relic/metric_spec_test.rb +95 -0
  160. data/test/new_relic/rack/all_test.rb +11 -0
  161. data/test/new_relic/rack/browser_monitoring_test.rb +84 -0
  162. data/test/new_relic/rack/developer_mode_test.rb +43 -0
  163. data/test/new_relic/stats_test.rb +426 -0
  164. data/test/new_relic/transaction_analysis/segment_summary_test.rb +77 -0
  165. data/test/new_relic/transaction_analysis_test.rb +121 -0
  166. data/test/new_relic/transaction_sample/composite_segment_test.rb +35 -0
  167. data/test/new_relic/transaction_sample/fake_segment_test.rb +17 -0
  168. data/test/new_relic/transaction_sample/segment_test.rb +454 -0
  169. data/test/new_relic/transaction_sample/summary_segment_test.rb +31 -0
  170. data/test/new_relic/transaction_sample_subtest_test.rb +56 -0
  171. data/test/new_relic/transaction_sample_test.rb +170 -0
  172. data/test/new_relic/version_number_test.rb +89 -0
  173. data/test/test_contexts.rb +29 -0
  174. data/test/test_helper.rb +144 -0
  175. data/ui/helpers/developer_mode_helper.rb +351 -0
  176. data/ui/helpers/google_pie_chart.rb +49 -0
  177. data/ui/views/layouts/newrelic_default.rhtml +47 -0
  178. data/ui/views/newrelic/_explain_plans.rhtml +27 -0
  179. data/ui/views/newrelic/_sample.rhtml +20 -0
  180. data/ui/views/newrelic/_segment.rhtml +28 -0
  181. data/ui/views/newrelic/_segment_limit_message.rhtml +1 -0
  182. data/ui/views/newrelic/_segment_row.rhtml +12 -0
  183. data/ui/views/newrelic/_show_sample_detail.rhtml +24 -0
  184. data/ui/views/newrelic/_show_sample_sql.rhtml +24 -0
  185. data/ui/views/newrelic/_show_sample_summary.rhtml +3 -0
  186. data/ui/views/newrelic/_sql_row.rhtml +16 -0
  187. data/ui/views/newrelic/_stack_trace.rhtml +15 -0
  188. data/ui/views/newrelic/_table.rhtml +12 -0
  189. data/ui/views/newrelic/explain_sql.rhtml +43 -0
  190. data/ui/views/newrelic/file/images/arrow-close.png +0 -0
  191. data/ui/views/newrelic/file/images/arrow-open.png +0 -0
  192. data/ui/views/newrelic/file/images/blue_bar.gif +0 -0
  193. data/ui/views/newrelic/file/images/file_icon.png +0 -0
  194. data/ui/views/newrelic/file/images/gray_bar.gif +0 -0
  195. data/ui/views/newrelic/file/images/new-relic-rpm-desktop.gif +0 -0
  196. data/ui/views/newrelic/file/images/new_relic_rpm_desktop.gif +0 -0
  197. data/ui/views/newrelic/file/images/textmate.png +0 -0
  198. data/ui/views/newrelic/file/javascript/jquery-1.4.2.js +6240 -0
  199. data/ui/views/newrelic/file/javascript/transaction_sample.js +120 -0
  200. data/ui/views/newrelic/file/stylesheets/style.css +490 -0
  201. data/ui/views/newrelic/index.rhtml +71 -0
  202. data/ui/views/newrelic/sample_not_found.rhtml +2 -0
  203. data/ui/views/newrelic/show_sample.rhtml +80 -0
  204. data/ui/views/newrelic/show_source.rhtml +3 -0
  205. data/ui/views/newrelic/threads.rhtml +53 -0
  206. data/vendor/gems/dependency_detection-0.0.1.build/LICENSE +19 -0
  207. data/vendor/gems/dependency_detection-0.0.1.build/README +0 -0
  208. data/vendor/gems/dependency_detection-0.0.1.build/lib/dependency_detection.rb +57 -0
  209. data/vendor/gems/dependency_detection-0.0.1.build/lib/dependency_detection/version.rb +3 -0
  210. data/vendor/gems/metric_parser-0.1.0.pre1/LICENSE +0 -0
  211. data/vendor/gems/metric_parser-0.1.0.pre1/README +0 -0
  212. data/vendor/gems/metric_parser-0.1.0.pre1/lib/metric_parser.rb +1 -0
  213. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser.rb +64 -0
  214. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/action_mailer.rb +14 -0
  215. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/active_merchant.rb +31 -0
  216. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/active_record.rb +33 -0
  217. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/apdex.rb +89 -0
  218. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/background_transaction.rb +7 -0
  219. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/client.rb +46 -0
  220. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/controller.rb +67 -0
  221. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/controller_cpu.rb +43 -0
  222. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/controller_ext.rb +17 -0
  223. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/database.rb +48 -0
  224. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/database_pool.rb +24 -0
  225. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/dot_net.rb +28 -0
  226. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/dot_net_parser.rb +17 -0
  227. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/errors.rb +11 -0
  228. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/external.rb +55 -0
  229. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/frontend.rb +40 -0
  230. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/gc.rb +20 -0
  231. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/hibernate_session.rb +7 -0
  232. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/java.rb +31 -0
  233. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/java_parser.rb +17 -0
  234. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/jsp.rb +34 -0
  235. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/jsp_tag.rb +7 -0
  236. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/mem_cache.rb +55 -0
  237. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/metric_parser.rb +122 -0
  238. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/orm.rb +27 -0
  239. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/other_transaction.rb +40 -0
  240. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/servlet.rb +7 -0
  241. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/servlet_context_listener.rb +7 -0
  242. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/servlet_filter.rb +7 -0
  243. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/solr.rb +27 -0
  244. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/solr_request_handler.rb +15 -0
  245. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/spring.rb +54 -0
  246. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/spring_controller.rb +6 -0
  247. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/spring_view.rb +6 -0
  248. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/struts_action.rb +20 -0
  249. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/struts_result.rb +20 -0
  250. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/version.rb +5 -0
  251. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/view.rb +66 -0
  252. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/web_frontend.rb +18 -0
  253. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/web_service.rb +14 -0
  254. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/web_transaction.rb +133 -0
  255. metadata +399 -0
@@ -0,0 +1,9 @@
1
+ if __FILE__ == $0 || $0 =~ /script\/plugin/
2
+ $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), 'lib'))
3
+ require 'new_relic/command'
4
+ begin
5
+ NewRelic::Command::Install.new(:quiet => true, :app_name => 'My Application').run
6
+ rescue NewRelic::Command::CommandFailure => e
7
+ $stderr.puts e.message
8
+ end
9
+ end
@@ -0,0 +1,3 @@
1
+ path = File.expand_path('../vendor/gems/dependency_detection-0.0.1.build/lib', File.dirname(__FILE__))
2
+ $LOAD_PATH << path unless $LOAD_PATH.include?(path)
3
+ require 'dependency_detection'
@@ -0,0 +1,5 @@
1
+ unless defined?(NewRelic) && defined?(NewRelic::MetricParser)
2
+ vendored_metric_parser = File.expand_path('../../vendor/gems/metric_parser-0.1.0.pre1/lib/', __FILE__)
3
+ $:.unshift vendored_metric_parser unless $:.include?(vendored_metric_parser)
4
+ require 'metric_parser'
5
+ end
@@ -0,0 +1,423 @@
1
+ require 'new_relic/control'
2
+ require 'new_relic/data_serialization'
3
+ # = New Relic Ruby Agent
4
+ #
5
+ # New Relic is a performance monitoring application for applications
6
+ # running in production. For more information on New Relic please visit
7
+ # http://www.newrelic.com.
8
+ #
9
+ # The New Relic Ruby Agent can be installed in Rails applications to
10
+ # gather runtime performance metrics, traces, and errors for display
11
+ # in a Developer Mode middleware (mapped to /newrelic in your application
12
+ # server) or for monitoring and analysis at http://rpm.newrelic.com
13
+ # with just about any Ruby application.
14
+ #
15
+ # == Getting Started
16
+ # For instructions on installation and setup, see
17
+ # the README[link:./files/README_rdoc.html] file.
18
+ #
19
+ # == Using with Rack/Metal
20
+ #
21
+ # To instrument Rack middlwares or Metal apps, refer to the docs in
22
+ # NewRelic::Agent::Instrumentation::Rack.
23
+ #
24
+ # == Ruby Agent API
25
+ #
26
+ # For details on the Ruby Agent API, refer to NewRelic::Agent.
27
+ #
28
+ # == Customizing the Ruby Agent
29
+ #
30
+ # For detailed information on customizing the Ruby Agent
31
+ # please visit our {support and documentation site}[http://support.newrelic.com].
32
+ #
33
+ module NewRelic
34
+ # == Ruby Agent APIs
35
+ # This module contains the public API methods for the Ruby Agent.
36
+ #
37
+ # For adding custom instrumentation to method invocations, refer to
38
+ # the docs in the class NewRelic::Agent::MethodTracer.
39
+ #
40
+ # For information on how to customize the controller
41
+ # instrumentation, or to instrument something other than Rails so
42
+ # that high level dispatcher actions or background tasks show up as
43
+ # first class operations in New Relic, refer to
44
+ # NewRelic::Agent::Instrumentation::ControllerInstrumentation and
45
+ # NewRelic::Agent::Instrumentation::ControllerInstrumentation::ClassMethods.
46
+ #
47
+ # Methods in this module as well as documented methods in
48
+ # NewRelic::Agent::MethodTracer and
49
+ # NewRelic::Agent::Instrumentation::ControllerInstrumentation are
50
+ # available to applications. When the agent is not enabled the
51
+ # method implementations are stubbed into no-ops to reduce overhead.
52
+ #
53
+ # Methods and classes in other parts of the agent are not guaranteed
54
+ # to be available between releases.
55
+ #
56
+ # Refer to the online docs at support.newrelic.com to see how to
57
+ # access the data collected by custom instrumentation, or e-mail
58
+ # support at New Relic for help.
59
+ module Agent
60
+ extend self
61
+
62
+ require 'new_relic/version'
63
+ require 'new_relic/local_environment'
64
+ require 'new_relic/stats'
65
+ require 'new_relic/metrics'
66
+ require 'new_relic/metric_spec'
67
+ require 'new_relic/metric_data'
68
+ require 'new_relic/collection_helper'
69
+ require 'new_relic/transaction_analysis'
70
+ require 'new_relic/transaction_sample'
71
+ require 'new_relic/url_rule'
72
+ require 'new_relic/noticed_error'
73
+ require 'new_relic/histogram'
74
+ require 'new_relic/timer_lib'
75
+
76
+ require 'new_relic/agent'
77
+ require 'new_relic/agent/chained_call'
78
+ require 'new_relic/agent/browser_monitoring'
79
+ require 'new_relic/agent/agent'
80
+ require 'new_relic/agent/shim_agent'
81
+ require 'new_relic/agent/method_tracer'
82
+ require 'new_relic/agent/worker_loop'
83
+ require 'new_relic/agent/stats_engine'
84
+ require 'new_relic/agent/transaction_sampler'
85
+ require 'new_relic/agent/error_collector'
86
+ require 'new_relic/agent/busy_calculator'
87
+ require 'new_relic/agent/sampler'
88
+
89
+ require 'new_relic/agent/instrumentation/controller_instrumentation'
90
+
91
+ require 'new_relic/agent/samplers/cpu_sampler'
92
+ require 'new_relic/agent/samplers/memory_sampler'
93
+ require 'new_relic/agent/samplers/object_sampler'
94
+ require 'new_relic/agent/samplers/delayed_job_lock_sampler'
95
+ require 'set'
96
+ require 'thread'
97
+ require 'resolv'
98
+
99
+ # An exception that is thrown by the server if the agent license is invalid.
100
+ class LicenseException < StandardError; end
101
+
102
+ # An exception that forces an agent to stop reporting until its mongrel is restarted.
103
+ class ForceDisconnectException < StandardError; end
104
+
105
+ # An exception that forces an agent to restart.
106
+ class ForceRestartException < StandardError; end
107
+
108
+ # Used to blow out of a periodic task without logging a an error, such as for routine
109
+ # failures.
110
+ class ServerConnectionException < StandardError; end
111
+
112
+ # Used for when a transaction trace or error report has too much
113
+ # data, so we reset the queue to clear the extra-large item
114
+ class PostTooBigException < ServerConnectionException; end
115
+
116
+ # Reserved for future use. Meant to represent a problem on the server side.
117
+ class ServerError < StandardError; end
118
+
119
+ class BackgroundLoadingError < StandardError; end
120
+
121
+ @agent = nil
122
+
123
+ # The singleton Agent instance. Used internally.
124
+ def agent #:nodoc:
125
+ raise "Plugin not initialized!" if @agent.nil?
126
+ @agent
127
+ end
128
+
129
+ def agent=(new_instance)#:nodoc:
130
+ @agent = new_instance
131
+ end
132
+
133
+ alias instance agent #:nodoc:
134
+
135
+ # Get or create a statistics gatherer that will aggregate numerical data
136
+ # under a metric name.
137
+ #
138
+ # +metric_name+ should follow a slash separated path convention. Application
139
+ # specific metrics should begin with "Custom/".
140
+ #
141
+ # Return a NewRelic::Stats that accepts data
142
+ # via calls to add_data_point(value).
143
+ def get_stats(metric_name, use_scope=false)
144
+ @agent.stats_engine.get_stats(metric_name, use_scope)
145
+ end
146
+
147
+ alias get_stats_no_scope get_stats
148
+
149
+ # Get the logger for the agent. Available after the agent has initialized.
150
+ # This sends output to the agent log file.
151
+ def logger
152
+ NewRelic::Control.instance.log
153
+ end
154
+
155
+ # Call this to manually start the Agent in situations where the Agent does
156
+ # not auto-start.
157
+ #
158
+ # When the app environment loads, so does the Agent. However, the
159
+ # Agent will only connect to the service if a web front-end is found. If
160
+ # you want to selectively monitor ruby processes that don't use
161
+ # web plugins, then call this method in your code and the Agent
162
+ # will fire up and start reporting to the service.
163
+ #
164
+ # Options are passed in as overrides for values in the
165
+ # newrelic.yml, such as app_name. In addition, the option +log+
166
+ # will take a logger that will be used instead of the standard
167
+ # file logger. The setting for the newrelic.yml section to use
168
+ # (ie, RAILS_ENV) can be overridden with an :env argument.
169
+ #
170
+ def manual_start(options={})
171
+ raise "Options must be a hash" unless Hash === options
172
+ NewRelic::Control.instance.init_plugin({ :agent_enabled => true, :sync_startup => true }.merge(options))
173
+ end
174
+
175
+ # Register this method as a callback for processes that fork
176
+ # jobs.
177
+ #
178
+ # If the master/parent connects to the agent prior to forking the
179
+ # agent in the forked process will use that agent_run. Otherwise
180
+ # the forked process will establish a new connection with the
181
+ # server.
182
+ #
183
+ # Use this especially when you fork the process to run background
184
+ # jobs or other work. If you are doing this with a web dispatcher
185
+ # that forks worker processes then you will need to force the
186
+ # agent to reconnect, which it won't do by default. Passenger and
187
+ # Unicorn are already handled, nothing special needed for them.
188
+ #
189
+ # Options:
190
+ # * <tt>:force_reconnect => true</tt> to force the spawned process to
191
+ # establish a new connection, such as when forking a long running process.
192
+ # The default is false--it will only connect to the server if the parent
193
+ # had not connected.
194
+ # * <tt>:keep_retrying => false</tt> if we try to initiate a new
195
+ # connection, this tells me to only try it once so this method returns
196
+ # quickly if there is some kind of latency with the server.
197
+ def after_fork(options={})
198
+ agent.after_fork(options)
199
+ end
200
+
201
+ # Clear out any unsent metric data.
202
+ def reset_stats
203
+ agent.reset_stats
204
+ end
205
+
206
+ # Shutdown the agent. Call this before exiting. Sends any queued data
207
+ # and kills the background thread.
208
+ def shutdown(options = {})
209
+ agent.shutdown(options)
210
+ end
211
+
212
+ def save_data
213
+ NewRelic::DataSerialization.read_and_write_to_file do |old_data|
214
+ agent.merge_data_from(old_data)
215
+ agent.serialize
216
+ end
217
+ end
218
+
219
+ def load_data
220
+ value = nil
221
+ NewRelic::DataSerialization.read_and_write_to_file do |old_data|
222
+ agent.merge_data_from(old_data)
223
+ value = {:metrics => agent.stats_engine.metrics.length, :traces => agent.unsent_traces_size, :errors => agent.unsent_errors_size}
224
+ nil # return nil so nothing is written to the file
225
+ end
226
+ value
227
+ end
228
+
229
+ # Add instrumentation files to the agent. The argument should be
230
+ # a glob matching ruby scripts which will be executed at the time
231
+ # instrumentation is loaded. Since instrumentation is not loaded
232
+ # when the agent is not running it's better to use this method to
233
+ # register instrumentation than just loading the files directly,
234
+ # although that probably also works.
235
+ def add_instrumentation(file_pattern)
236
+ NewRelic::Control.instance.add_instrumentation file_pattern
237
+ end
238
+
239
+ # This method sets the block sent to this method as a sql
240
+ # obfuscator. The block will be called with a single String SQL
241
+ # statement to obfuscate. The method must return the obfuscated
242
+ # String SQL. If chaining of obfuscators is required, use type =
243
+ # :before or :after
244
+ #
245
+ # type = :before, :replace, :after
246
+ #
247
+ # Example:
248
+ #
249
+ # NewRelic::Agent.set_sql_obfuscator(:replace) do |sql|
250
+ # my_obfuscator(sql)
251
+ # end
252
+ #
253
+ def set_sql_obfuscator(type = :replace, &block)
254
+ agent.set_sql_obfuscator type, &block
255
+ end
256
+
257
+
258
+ # This method sets the state of sql recording in the transaction
259
+ # sampler feature. Within the given block, no sql will be recorded
260
+ #
261
+ # usage:
262
+ #
263
+ # NewRelic::Agent.disable_sql_recording do
264
+ # ...
265
+ # end
266
+ #
267
+ def disable_sql_recording
268
+ state = agent.set_record_sql(false)
269
+ begin
270
+ yield
271
+ ensure
272
+ agent.set_record_sql(state)
273
+ end
274
+ end
275
+
276
+ # This method disables the recording of transaction traces in the given
277
+ # block. See also #disable_all_tracing
278
+ def disable_transaction_tracing
279
+ state = agent.set_record_tt(false)
280
+ begin
281
+ yield
282
+ ensure
283
+ agent.set_record_tt(state)
284
+ end
285
+ end
286
+
287
+ # Cancel the collection of the current transaction in progress, if
288
+ # any. Only affects the transaction started on this thread once
289
+ # it has started and before it has completed.
290
+ def abort_transaction!
291
+ # The class may not be loaded if the agent is disabled
292
+ if defined? NewRelic::Agent::Instrumentation::MetricFrame
293
+ NewRelic::Agent::Instrumentation::MetricFrame.abort_transaction!
294
+ end
295
+ end
296
+
297
+ # Yield to the block without collecting any metrics or traces in
298
+ # any of the subsequent calls. If executed recursively, will keep
299
+ # track of the first entry point and turn on tracing again after
300
+ # leaving that block. This uses the thread local
301
+ # +newrelic_untrace+
302
+ def disable_all_tracing
303
+ agent.push_trace_execution_flag(false)
304
+ yield
305
+ ensure
306
+ agent.pop_trace_execution_flag
307
+ end
308
+
309
+ # Check to see if we are capturing metrics currently on this thread.
310
+ def is_execution_traced?
311
+ Thread.current[:newrelic_untraced].nil? || Thread.current[:newrelic_untraced].last != false
312
+ end
313
+
314
+ def is_transaction_traced?
315
+ Thread::current[:record_tt] != false
316
+ end
317
+
318
+ def is_sql_recorded?
319
+ Thread::current[:record_sql] != false
320
+ end
321
+
322
+ # Set a filter to be applied to errors that the Ruby Agent will
323
+ # track. The block should evalute to the exception to track
324
+ # (which could be different from the original exception) or nil to
325
+ # ignore this exception.
326
+ #
327
+ # The block is yielded to with the exception to filter.
328
+ #
329
+ # Return the new block or the existing filter Proc if no block is passed.
330
+ #
331
+ def ignore_error_filter(&block)
332
+ agent.error_collector.ignore_error_filter(&block)
333
+ end
334
+
335
+ # Record the given error. It will be passed through the
336
+ # #ignore_error_filter if there is one.
337
+ #
338
+ # * <tt>exception</tt> is the exception which will be recorded. May also be
339
+ # an error message.
340
+ # Options:
341
+ # * <tt>:uri</tt> => The request path, minus any request params or query string.
342
+ # * <tt>:referer</tt> => The URI of the referer
343
+ # * <tt>:metric</tt> => The metric name associated with the transaction
344
+ # * <tt>:request_params</tt> => Request parameters, already filtered if necessary
345
+ # * <tt>:custom_params</tt> => Custom parameters
346
+ #
347
+ # Anything left over is treated as custom params.
348
+ #
349
+ def notice_error(exception, options={})
350
+ NewRelic::Agent::Instrumentation::MetricFrame.notice_error(exception, options)
351
+ end
352
+
353
+ # Add parameters to the current transaction trace (and traced error if any)
354
+ # on the call stack.
355
+ #
356
+ def add_custom_parameters(params)
357
+ NewRelic::Agent::Instrumentation::MetricFrame.add_custom_parameters(params)
358
+ end
359
+
360
+ # The #add_request_parameters method is aliased to #add_custom_parameters
361
+ # and is now deprecated.
362
+ alias add_request_parameters add_custom_parameters #:nodoc:
363
+
364
+ # Yield to a block that is run with a database metric name
365
+ # context. This means the Database instrumentation will use this
366
+ # for the metric name if it does not otherwise know about a model.
367
+ # This is re-entrant.
368
+ #
369
+ # * <tt>model</tt> is the DB model class
370
+ # * <tt>method</tt> is the name of the finder method or other
371
+ # method to identify the operation with.
372
+ def with_database_metric_name(model, method, &block)
373
+ if frame = NewRelic::Agent::Instrumentation::MetricFrame.current
374
+ frame.with_database_metric_name(model, method, &block)
375
+ else
376
+ yield
377
+ end
378
+ end
379
+
380
+ # Record a web transaction from an external source. This will
381
+ # process the response time, error, and score an apdex value.
382
+ #
383
+ # First argument is a float value, time in seconds. Option
384
+ # keys are strings.
385
+ #
386
+ # == Identifying the transaction
387
+ # * <tt>'uri' => uri</tt> to record the value for a given web request.
388
+ # If not provided, just record the aggregate dispatcher and apdex scores.
389
+ # * <tt>'metric' => metric_name</tt> to record with a general metric name
390
+ # like +OtherTransaction/Background/Class/method+. Ignored if +uri+ is
391
+ # provided.
392
+ #
393
+ # == Error options
394
+ # Provide one of the following:
395
+ # * <tt>'is_error' => true</tt> if an unknown error occurred
396
+ # * <tt>'error_message' => msg</tt> if an error message is available
397
+ # * <tt>'exception' => exception</tt> if a ruby exception is recorded
398
+ #
399
+ # == Misc options
400
+ # Additional information captured in errors
401
+ # * <tt>'referer' => referer_url</tt>
402
+ # * <tt>'request_params' => hash</tt> to record a set of name/value pairs as the
403
+ # request parameters.
404
+ # * <tt>'custom_params' => hash</tt> to record extra information in traced errors
405
+ #
406
+ def record_transaction(response_sec, options = {})
407
+ agent.record_transaction(response_sec, options)
408
+ end
409
+
410
+ # Returns a Javascript string which should be injected into the very top of the response body
411
+ #
412
+ def browser_timing_header
413
+ agent.browser_timing_header
414
+ end
415
+
416
+ # Returns a Javascript string which should be injected into the very bottom of the response body
417
+ #
418
+ def browser_timing_footer
419
+ agent.browser_timing_footer
420
+ end
421
+
422
+ end
423
+ end
@@ -0,0 +1,1040 @@
1
+ require 'socket'
2
+ require 'net/https'
3
+ require 'net/http'
4
+ require 'logger'
5
+ require 'zlib'
6
+ require 'stringio'
7
+ require 'new_relic/data_serialization'
8
+
9
+ module NewRelic
10
+ module Agent
11
+
12
+ # The Agent is a singleton that is instantiated when the plugin is
13
+ # activated. It collects performance data from ruby applications
14
+ # in realtime as the application runs, and periodically sends that
15
+ # data to the NewRelic server.
16
+ class Agent
17
+
18
+ # Specifies the version of the agent's communication protocol with
19
+ # the NewRelic hosted site.
20
+
21
+ PROTOCOL_VERSION = 8
22
+ # 14105: v8 (tag 2.10.3)
23
+ # (no v7)
24
+ # 10379: v6 (not tagged)
25
+ # 4078: v5 (tag 2.5.4)
26
+ # 2292: v4 (tag 2.3.6)
27
+ # 1754: v3 (tag 2.3.0)
28
+ # 534: v2 (shows up in 2.1.0, our first tag)
29
+
30
+
31
+ def initialize
32
+
33
+ @launch_time = Time.now
34
+
35
+ @metric_ids = {}
36
+ @histogram = NewRelic::Histogram.new(NewRelic::Control.instance.apdex_t / 10)
37
+ @stats_engine = NewRelic::Agent::StatsEngine.new
38
+ @transaction_sampler = NewRelic::Agent::TransactionSampler.new
39
+ @stats_engine.transaction_sampler = @transaction_sampler
40
+ @error_collector = NewRelic::Agent::ErrorCollector.new
41
+ @connect_attempts = 0
42
+
43
+ @request_timeout = NewRelic::Control.instance.fetch('timeout', 2 * 60)
44
+
45
+ @last_harvest_time = Time.now
46
+ @obfuscator = method(:default_sql_obfuscator)
47
+ end
48
+
49
+ module ClassMethods
50
+ # Should only be called by NewRelic::Control
51
+ def instance
52
+ @instance ||= self.new
53
+ end
54
+ end
55
+
56
+ module InstanceMethods
57
+
58
+ attr_reader :obfuscator
59
+ attr_reader :stats_engine
60
+ attr_reader :transaction_sampler
61
+ attr_reader :error_collector
62
+ attr_reader :record_sql
63
+ attr_reader :histogram
64
+ attr_reader :metric_ids
65
+ attr_reader :url_rules
66
+ attr_reader :beacon_configuration
67
+
68
+ def unsent_errors_size
69
+ @unsent_errors.length if @unsent_errors
70
+ end
71
+
72
+ def unsent_traces_size
73
+ @traces.length if @traces
74
+ end
75
+
76
+ def unsent_timeslice_data
77
+ @unsent_timeslice_data ||= {}
78
+ @unsent_timeslice_data.keys.length
79
+ end
80
+
81
+ def record_transaction(duration_seconds, options={})
82
+ is_error = options['is_error'] || options['error_message'] || options['exception']
83
+ metric = options['metric']
84
+ metric ||= options['uri'] # normalize this with url rules
85
+ raise "metric or uri arguments required" unless metric
86
+ metric_info = NewRelic::MetricParser::MetricParser.for_metric_named(metric)
87
+
88
+ if metric_info.is_web_transaction?
89
+ NewRelic::Agent::Instrumentation::MetricFrame.record_apdex(metric_info, duration_seconds, duration_seconds, is_error)
90
+ histogram.process(duration_seconds)
91
+ end
92
+ metrics = metric_info.summary_metrics
93
+
94
+ metrics << metric
95
+ metrics.each do |name|
96
+ stats = stats_engine.get_stats_no_scope(name)
97
+ stats.record_data_point(duration_seconds)
98
+ end
99
+
100
+ if is_error
101
+ if options['exception']
102
+ e = options['exception']
103
+ elsif options['error_message']
104
+ e = Exception.new options['error_message']
105
+ else
106
+ e = Exception.new 'Unknown Error'
107
+ end
108
+ error_collector.notice_error e, :uri => options['uri'], :metric => metric
109
+ end
110
+ # busy time ?
111
+ end
112
+
113
+ # This method should be called in a forked process after a fork.
114
+ # It assumes the parent process initialized the agent, but does
115
+ # not assume the agent started.
116
+ #
117
+ # The call is idempotent, but not re-entrant.
118
+ #
119
+ # * It clears any metrics carried over from the parent process
120
+ # * Restarts the sampler thread if necessary
121
+ # * Initiates a new agent run and worker loop unless that was done
122
+ # in the parent process and +:force_reconnect+ is not true
123
+ #
124
+ # Options:
125
+ # * <tt>:force_reconnect => true</tt> to force the spawned process to
126
+ # establish a new connection, such as when forking a long running process.
127
+ # The default is false--it will only connect to the server if the parent
128
+ # had not connected.
129
+ # * <tt>:keep_retrying => false</tt> if we try to initiate a new
130
+ # connection, this tells me to only try it once so this method returns
131
+ # quickly if there is some kind of latency with the server.
132
+ def after_fork(options={})
133
+
134
+ # @connected gets false after we fail to connect or have an error
135
+ # connecting. @connected has nil if we haven't finished trying to connect.
136
+ # or we didn't attempt a connection because this is the master process
137
+
138
+ # log.debug "Agent received after_fork notice in #$$: [#{control.agent_enabled?}; monitor=#{control.monitor_mode?}; connected: #{@connected.inspect}; thread=#{@worker_thread.inspect}]"
139
+ return if !control.agent_enabled? or
140
+ !control.monitor_mode? or
141
+ @connected == false or
142
+ @worker_thread && @worker_thread.alive?
143
+
144
+ log.info "Starting the worker thread in #$$ after forking."
145
+
146
+ # Clear out stats that are left over from parent process
147
+ reset_stats
148
+
149
+ # Don't ever check to see if this is a spawner. If we're in a forked process
150
+ # I'm pretty sure we're not also forking new instances.
151
+ start_worker_thread(options)
152
+ @stats_engine.start_sampler_thread
153
+ end
154
+
155
+ # True if we have initialized and completed 'start'
156
+ def started?
157
+ @started
158
+ end
159
+
160
+ # Return nil if not yet connected, true if successfully started
161
+ # and false if we failed to start.
162
+ def connected?
163
+ @connected
164
+ end
165
+
166
+ # Attempt a graceful shutdown of the agent.
167
+ def shutdown(options={})
168
+ run_loop_before_exit = options.fetch(:force_send, false)
169
+ return if not started?
170
+ if @worker_loop
171
+ @worker_loop.run_task if run_loop_before_exit
172
+ @worker_loop.stop
173
+
174
+ log.debug "Starting Agent shutdown"
175
+
176
+ # if litespeed, then ignore all future SIGUSR1 - it's
177
+ # litespeed trying to shut us down
178
+
179
+ if control.dispatcher == :litespeed
180
+ Signal.trap("SIGUSR1", "IGNORE")
181
+ Signal.trap("SIGTERM", "IGNORE")
182
+ end
183
+
184
+ begin
185
+ NewRelic::Agent.disable_all_tracing do
186
+ graceful_disconnect
187
+ end
188
+ rescue => e
189
+ log.error e
190
+ log.error e.backtrace.join("\n")
191
+ end
192
+ end
193
+ @started = nil
194
+ end
195
+
196
+ def start_transaction
197
+ @stats_engine.start_transaction
198
+ end
199
+
200
+ def end_transaction
201
+ @stats_engine.end_transaction
202
+ end
203
+
204
+ def set_record_sql(should_record)
205
+ prev = Thread::current[:record_sql]
206
+ Thread::current[:record_sql] = should_record
207
+ prev.nil? || prev
208
+ end
209
+
210
+ def set_record_tt(should_record)
211
+ prev = Thread::current[:record_tt]
212
+ Thread::current[:record_tt] = should_record
213
+ prev.nil? || prev
214
+ end
215
+ # Push flag indicating whether we should be tracing in this
216
+ # thread.
217
+ def push_trace_execution_flag(should_trace=false)
218
+ value = Thread.current[:newrelic_untraced]
219
+ if (value.nil?)
220
+ Thread.current[:newrelic_untraced] = []
221
+ end
222
+
223
+ Thread.current[:newrelic_untraced] << should_trace
224
+ end
225
+
226
+ # Pop the current trace execution status. Restore trace execution status
227
+ # to what it was before we pushed the current flag.
228
+ def pop_trace_execution_flag
229
+ Thread.current[:newrelic_untraced].pop if Thread.current[:newrelic_untraced]
230
+ end
231
+
232
+ def set_sql_obfuscator(type, &block)
233
+ if type == :before
234
+ @obfuscator = NewRelic::ChainedCall.new(block, @obfuscator)
235
+ elsif type == :after
236
+ @obfuscator = NewRelic::ChainedCall.new(@obfuscator, block)
237
+ elsif type == :replace
238
+ @obfuscator = block
239
+ else
240
+ fail "unknown sql_obfuscator type #{type}"
241
+ end
242
+ end
243
+
244
+ def log
245
+ NewRelic::Agent.logger
246
+ end
247
+
248
+ # Herein lies the corpse of the former 'start' method. May
249
+ # it's unmatched flog score rest in pieces.
250
+ module Start
251
+ def already_started?
252
+ if started?
253
+ control.log!("Agent Started Already!", :error)
254
+ true
255
+ end
256
+ end
257
+
258
+ def disabled?
259
+ !control.agent_enabled?
260
+ end
261
+
262
+ def log_dispatcher
263
+ dispatcher_name = control.dispatcher.to_s
264
+ return if log_if(dispatcher_name.empty?, :info, "No dispatcher detected.")
265
+ log.info "Dispatcher: #{dispatcher_name}"
266
+ end
267
+
268
+ def log_app_names
269
+ log.info "Application: #{control.app_names.join(", ")}"
270
+ end
271
+
272
+ def apdex_f
273
+ (4 * NewRelic::Control.instance.apdex_t).to_f
274
+ end
275
+
276
+ def apdex_f_threshold?
277
+ sampler_config.fetch('transaction_threshold', '') =~ /apdex_f/i
278
+ end
279
+
280
+ def set_sql_recording!
281
+ record_sql_config = sampler_config.fetch('record_sql', :obfuscated)
282
+ case record_sql_config.to_s
283
+ when 'off'
284
+ @record_sql = :off
285
+ when 'none'
286
+ @record_sql = :off
287
+ when 'false'
288
+ @record_sql = :off
289
+ when 'raw'
290
+ @record_sql = :raw
291
+ else
292
+ @record_sql = :obfuscated
293
+ end
294
+
295
+ log_sql_transmission_warning?
296
+ end
297
+
298
+ def log_sql_transmission_warning?
299
+ log_if((@record_sql == :raw), :warn, "Agent is configured to send raw SQL to the service")
300
+ end
301
+
302
+ def sampler_config
303
+ control.fetch('transaction_tracer', {})
304
+ end
305
+
306
+ # this entire method should be done on the transaction
307
+ # sampler object, rather than here. We should pass in the
308
+ # sampler config.
309
+ def config_transaction_tracer
310
+ @should_send_samples = @config_should_send_samples = sampler_config.fetch('enabled', true)
311
+ @should_send_random_samples = sampler_config.fetch('random_sample', false)
312
+ @explain_threshold = sampler_config.fetch('explain_threshold', 0.5).to_f
313
+ @explain_enabled = sampler_config.fetch('explain_enabled', true)
314
+ set_sql_recording!
315
+
316
+ # default to 2.0, string 'apdex_f' will turn into your
317
+ # apdex * 4
318
+ @slowest_transaction_threshold = sampler_config.fetch('transaction_threshold', 2.0).to_f
319
+ @slowest_transaction_threshold = apdex_f if apdex_f_threshold?
320
+ end
321
+
322
+ def connect_in_foreground
323
+ NewRelic::Agent.disable_all_tracing { connect(:keep_retrying => false) }
324
+ end
325
+
326
+ def using_rubinius?
327
+ RUBY_VERSION =~ /rubinius/i
328
+ end
329
+
330
+ def using_jruby?
331
+ defined?(JRuby)
332
+ end
333
+
334
+ def using_sinatra?
335
+ defined?(Sinatra::Application)
336
+ end
337
+
338
+ # we should not set an at_exit block if people are using
339
+ # these as they don't do standard at_exit behavior per MRI/YARV
340
+ def weird_ruby?
341
+ using_rubinius? || using_jruby? || using_sinatra?
342
+ end
343
+
344
+ def install_exit_handler
345
+ if control.send_data_on_exit && !weird_ruby?
346
+ # Our shutdown handler needs to run after other shutdown handlers
347
+ at_exit { at_exit { shutdown } }
348
+ end
349
+ end
350
+
351
+ def notify_log_file_location
352
+ log_file = NewRelic::Control.instance.log_file
353
+ log_if(log_file, :info, "Agent Log found in #{log_file}")
354
+ end
355
+
356
+ def log_version_and_pid
357
+ log.info "New Relic Ruby Agent #{NewRelic::VERSION::STRING} Initialized: pid = #{$$}"
358
+ end
359
+
360
+ def log_if(boolean, level, message)
361
+ self.log.send(level, message) if boolean
362
+ boolean
363
+ end
364
+
365
+ def log_unless(boolean, level, message)
366
+ self.log.send(level, message) unless boolean
367
+ boolean
368
+ end
369
+
370
+ def monitoring?
371
+ log_unless(control.monitor_mode?, :warn, "Agent configured not to send data in this environment - edit newrelic.yml to change this")
372
+ end
373
+
374
+ def has_license_key?
375
+ log_unless(control.license_key, :error, "No license key found. Please edit your newrelic.yml file and insert your license key.")
376
+ end
377
+
378
+ def has_correct_license_key?
379
+ has_license_key? && correct_license_length
380
+ end
381
+
382
+ def correct_license_length
383
+ key = control.license_key
384
+ log_unless((key.length == 40), :error, "Invalid license key: #{key}")
385
+ end
386
+
387
+ def using_forking_dispatcher?
388
+ log_if([:passenger, :unicorn].include?(control.dispatcher), :info, "Connecting workers after forking.")
389
+ end
390
+
391
+ def check_config_and_start_agent
392
+ return unless monitoring? && has_correct_license_key?
393
+ return if using_forking_dispatcher?
394
+ connect_in_foreground if control.sync_startup
395
+ start_worker_thread
396
+ install_exit_handler
397
+ end
398
+ end
399
+
400
+ include Start
401
+
402
+ def start
403
+ return if already_started? || disabled?
404
+ @started = true
405
+ @local_host = determine_host
406
+ log_dispatcher
407
+ log_app_names
408
+ config_transaction_tracer
409
+ check_config_and_start_agent
410
+ log_version_and_pid
411
+ notify_log_file_location
412
+ end
413
+
414
+ # Clear out the metric data, errors, and transaction traces. Reset the histogram data.
415
+ def reset_stats
416
+ @stats_engine.reset_stats
417
+ @unsent_errors = []
418
+ @traces = nil
419
+ @unsent_timeslice_data = {}
420
+ @last_harvest_time = Time.now
421
+ @launch_time = Time.now
422
+ @histogram = NewRelic::Histogram.new(NewRelic::Control.instance.apdex_t / 10)
423
+ end
424
+
425
+ private
426
+ def collector
427
+ @collector ||= control.server
428
+ end
429
+
430
+ module StartWorkerThread
431
+
432
+ def check_transaction_sampler_status
433
+ # disable transaction sampling if disabled by the server
434
+ # and we're not in dev mode
435
+ if control.developer_mode? || @should_send_samples
436
+ @transaction_sampler.enable
437
+ else
438
+ @transaction_sampler.disable
439
+ end
440
+ end
441
+
442
+ def log_worker_loop_start
443
+ log.info "Reporting performance data every #{@report_period} seconds."
444
+ log.debug "Running worker loop"
445
+ end
446
+
447
+ def create_and_run_worker_loop
448
+ @worker_loop = WorkerLoop.new
449
+ @worker_loop.run(@report_period) do
450
+ NewRelic::Agent.load_data
451
+ harvest_and_send_errors
452
+ harvest_and_send_slowest_sample
453
+ harvest_and_send_timeslice_data
454
+ end
455
+ end
456
+
457
+ def handle_force_restart(error)
458
+ log.info error.message
459
+ # disconnect and start over.
460
+ # clear the stats engine
461
+ reset_stats
462
+ @metric_ids = {}
463
+ @connected = nil
464
+ # Wait a short time before trying to reconnect
465
+ sleep 30
466
+ end
467
+
468
+ def handle_force_disconnect(error)
469
+ # when a disconnect is requested, stop the current thread, which
470
+ # is the worker thread that gathers data and talks to the
471
+ # server.
472
+ log.error "New Relic forced this agent to disconnect (#{error.message})"
473
+ disconnect
474
+ end
475
+
476
+ def handle_server_connection_problem(error)
477
+ log.error "Unable to establish connection with the server. Run with log level set to debug for more information."
478
+ log.debug("#{error.class.name}: #{error.message}\n#{error.backtrace.first}")
479
+ disconnect
480
+ end
481
+
482
+ def handle_other_error(error)
483
+ log.error "Terminating worker loop: #{error.class.name}: #{error.message}\n #{error.backtrace.join("\n ")}"
484
+ disconnect
485
+ end
486
+
487
+ def catch_errors
488
+ yield
489
+ rescue NewRelic::Agent::ForceRestartException => e
490
+ handle_force_restart(e)
491
+ retry
492
+ rescue NewRelic::Agent::ForceDisconnectException => e
493
+ handle_force_disconnect(e)
494
+ rescue NewRelic::Agent::ServerConnectionException => e
495
+ handle_server_connection_problem(e)
496
+ rescue Exception => e
497
+ handle_other_error(e)
498
+ end
499
+
500
+ def deferred_work!(connection_options)
501
+ catch_errors do
502
+ NewRelic::Agent.disable_all_tracing do
503
+ # We try to connect. If this returns false that means
504
+ # the server rejected us for a licensing reason and we should
505
+ # just exit the thread. If it returns nil
506
+ # that means it didn't try to connect because we're in the master.
507
+ connect(connection_options)
508
+ if @connected
509
+ check_transaction_sampler_status
510
+ log_worker_loop_start
511
+ create_and_run_worker_loop
512
+ else
513
+ log.debug "No connection. Worker thread ending."
514
+ end
515
+ end
516
+ end
517
+ end
518
+ end
519
+ include StartWorkerThread
520
+
521
+ # Try to launch the worker thread and connect to the server.
522
+ #
523
+ # See #connect for a description of connection_options.
524
+ def start_worker_thread(connection_options = {})
525
+ log.debug "Creating Ruby Agent worker thread."
526
+ @worker_thread = Thread.new do
527
+ deferred_work!(connection_options)
528
+ end # thread new
529
+ @worker_thread['newrelic_label'] = 'Worker Loop'
530
+ end
531
+
532
+ def control
533
+ NewRelic::Control.instance
534
+ end
535
+
536
+ module Connect
537
+ attr_accessor :connect_retry_period
538
+ attr_accessor :connect_attempts
539
+
540
+ def disconnect
541
+ @connected = false
542
+ true
543
+ end
544
+
545
+ def tried_to_connect?(options)
546
+ !(@connected.nil? || options[:force_reconnect])
547
+ end
548
+
549
+ def should_keep_retrying?(options)
550
+ @keep_retrying = (options[:keep_retrying].nil? || options[:keep_retrying])
551
+ end
552
+
553
+ def get_retry_period
554
+ return 600 if self.connect_attempts > 6
555
+ connect_attempts * 60
556
+ end
557
+
558
+ def increment_retry_period!
559
+ self.connect_retry_period=(get_retry_period)
560
+ end
561
+
562
+ def should_retry?
563
+ if @keep_retrying
564
+ self.connect_attempts=(connect_attempts + 1)
565
+ increment_retry_period!
566
+ log.info "Will re-attempt in #{connect_retry_period} seconds"
567
+ true
568
+ else
569
+ disconnect
570
+ false
571
+ end
572
+ end
573
+
574
+ def log_error(error)
575
+ log.error "Error establishing connection with New Relic Service at #{control.server}: #{error.message}"
576
+ log.debug error.backtrace.join("\n")
577
+ end
578
+
579
+ def handle_license_error(error)
580
+ log.error error.message
581
+ log.info "Visit NewRelic.com to obtain a valid license key, or to upgrade your account."
582
+ disconnect
583
+ end
584
+
585
+ def log_seed_token
586
+ if control.validate_seed
587
+ log.debug "Connecting with validation seed/token: #{control.validate_seed}/#{control.validate_token}"
588
+ end
589
+ end
590
+
591
+ def environment_for_connect
592
+ control['send_environment_info'] != false ? control.local_env.snapshot : []
593
+ end
594
+
595
+ def validate_settings
596
+ {
597
+ :seed => control.validate_seed,
598
+ :token => control.validate_token
599
+ }
600
+ end
601
+
602
+ def connect_settings
603
+ {
604
+ :pid => $$,
605
+ :host => @local_host,
606
+ :app_name => control.app_names,
607
+ :language => 'ruby',
608
+ :agent_version => NewRelic::VERSION::STRING,
609
+ :environment => environment_for_connect,
610
+ :settings => control.settings,
611
+ :validate => validate_settings
612
+ }
613
+ end
614
+ def connect_to_server
615
+ log_seed_token
616
+ connect_data = invoke_remote(:connect, connect_settings)
617
+ end
618
+
619
+ def configure_error_collector!(server_enabled)
620
+ # Ask for permission to collect error data
621
+ enabled = if error_collector.config_enabled && server_enabled
622
+ error_collector.enabled = true
623
+ else
624
+ error_collector.enabled = false
625
+ end
626
+ log.debug "Errors will #{enabled ? '' : 'not '}be sent to the New Relic service."
627
+ end
628
+
629
+ def enable_random_samples!(sample_rate)
630
+ sample_rate = 10 unless sample_rate.to_i > 0# a sane default for random sampling
631
+ @transaction_sampler.random_sampling = true
632
+ @transaction_sampler.sampling_rate = sample_rate
633
+ log.info "Transaction sampling enabled, rate = #{@transaction_sampler.sampling_rate}"
634
+ end
635
+
636
+
637
+ def configure_transaction_tracer!(server_enabled, sample_rate)
638
+ # Ask the server for permission to send transaction samples.
639
+ # determined by subscription license.
640
+ @should_send_samples = @config_should_send_samples && server_enabled
641
+
642
+ if @should_send_samples
643
+ # I don't think this is ever true, but...
644
+ enable_random_samples!(sample_rate) if @should_send_random_samples
645
+ log.debug "Transaction tracing threshold is #{@slowest_transaction_threshold} seconds."
646
+ else
647
+ log.debug "Transaction traces will not be sent to the New Relic service."
648
+ end
649
+ end
650
+
651
+ def set_collector_host!
652
+ host = invoke_remote(:get_redirect_host)
653
+ if host
654
+ @collector = control.server_from_host(host)
655
+ end
656
+ end
657
+
658
+ def query_server_for_configuration
659
+ set_collector_host!
660
+
661
+ finish_setup(connect_to_server)
662
+ end
663
+ def finish_setup(config_data)
664
+ @agent_id = config_data['agent_run_id']
665
+ @report_period = config_data['data_report_period']
666
+ @url_rules = config_data['url_rules']
667
+ @beacon_configuration = BeaconConfiguration.new(config_data)
668
+
669
+ log_connection!(config_data)
670
+ configure_transaction_tracer!(config_data['collect_traces'], config_data['sample_rate'])
671
+ configure_error_collector!(config_data['collect_errors'])
672
+ end
673
+
674
+ def log_connection!(config_data)
675
+ control.log! "Connected to NewRelic Service at #{@collector}"
676
+ log.debug "Agent Run = #{@agent_id}."
677
+ log.debug "Connection data = #{config_data.inspect}"
678
+ end
679
+ end
680
+ include Connect
681
+
682
+ def serialize
683
+ accumulator = []
684
+ accumulator[1] = harvest_transaction_traces if @transaction_sampler
685
+ accumulator[2] = harvest_errors if @error_collector
686
+ accumulator[0] = harvest_timeslice_data
687
+ accumulator
688
+ end
689
+
690
+ public :serialize
691
+
692
+ def merge_data_from(data)
693
+ metrics, transaction_traces, errors = data
694
+ @stats_engine.merge_data(metrics) if metrics
695
+ if transaction_traces
696
+ if @traces
697
+ @traces = @traces + transaction_traces
698
+ else
699
+ @traces = transaction_traces
700
+ end
701
+ end
702
+ if errors
703
+ if @unsent_errors
704
+ @unsent_errors = @unsent_errors + errors
705
+ else
706
+ @unsent_errors = errors
707
+ end
708
+ end
709
+ end
710
+
711
+ public :merge_data_from
712
+
713
+ # Connect to the server and validate the license. If successful,
714
+ # @connected has true when finished. If not successful, you can
715
+ # keep calling this. Return false if we could not establish a
716
+ # connection with the server and we should not retry, such as if
717
+ # there's a bad license key.
718
+ #
719
+ # Set keep_retrying=false to disable retrying and return asap, such as when
720
+ # invoked in the foreground. Otherwise this runs until a successful
721
+ # connection is made, or the server rejects us.
722
+ #
723
+ # * <tt>:keep_retrying => false</tt> to only try to connect once, and
724
+ # return with the connection set to nil. This ensures we may try again
725
+ # later (default true).
726
+ # * <tt>force_reconnect => true</tt> if you want to establish a new connection
727
+ # to the server before running the worker loop. This means you get a separate
728
+ # agent run and New Relic sees it as a separate instance (default is false).
729
+ def connect(options)
730
+ # Don't proceed if we already connected (@connected=true) or if we tried
731
+ # to connect and were rejected with prejudice because of a license issue
732
+ # (@connected=false), unless we're forced to by force_reconnect.
733
+ return if tried_to_connect?(options)
734
+
735
+ # wait a few seconds for the web server to boot, necessary in development
736
+ @connect_retry_period = should_keep_retrying?(options) ? 10 : 0
737
+
738
+ sleep connect_retry_period
739
+ log.debug "Connecting Process to New Relic: #$0"
740
+ query_server_for_configuration
741
+ @connected_pid = $$
742
+ @connected = true
743
+ rescue NewRelic::Agent::LicenseException => e
744
+ handle_license_error(e)
745
+ rescue Timeout::Error, StandardError => e
746
+ log_error(e)
747
+ if should_retry?
748
+ retry
749
+ else
750
+ disconnect
751
+ end
752
+ end
753
+
754
+ def determine_host
755
+ Socket.gethostname
756
+ end
757
+
758
+ def determine_home_directory
759
+ control.root
760
+ end
761
+
762
+ def is_application_spawner?
763
+ $0 =~ /ApplicationSpawner|^unicorn\S* master/
764
+ end
765
+
766
+ def harvest_timeslice_data(time=Time.now)
767
+ # this creates timeslices that are harvested below
768
+ NewRelic::Agent::BusyCalculator.harvest_busy
769
+
770
+ @unsent_timeslice_data ||= {}
771
+ @unsent_timeslice_data = @stats_engine.harvest_timeslice_data(@unsent_timeslice_data, @metric_ids)
772
+ @unsent_timeslice_data
773
+ end
774
+
775
+ def fill_metric_id_cache(pairs_of_specs_and_ids)
776
+ Array(pairs_of_specs_and_ids).each do |metric_spec, metric_id|
777
+ @metric_ids[metric_spec] = metric_id
778
+ end
779
+ end
780
+
781
+ def harvest_and_send_timeslice_data
782
+ now = Time.now
783
+ NewRelic::Agent.instance.stats_engine.get_stats_no_scope('Supportability/invoke_remote').record_data_point(0.0)
784
+ NewRelic::Agent.instance.stats_engine.get_stats_no_scope('Supportability/invoke_remote/metric_data').record_data_point(0.0)
785
+ harvest_timeslice_data(now)
786
+ begin
787
+ # In this version of the protocol, we get back an assoc array of spec to id.
788
+ metric_specs_and_ids = invoke_remote(:metric_data, @agent_id,
789
+ @last_harvest_time.to_f,
790
+ now.to_f,
791
+ @unsent_timeslice_data.values)
792
+
793
+ rescue Timeout::Error
794
+ # assume that the data was received. chances are that it was
795
+ metric_specs_and_ids = []
796
+ end
797
+
798
+ fill_metric_id_cache(metric_specs_and_ids)
799
+
800
+ log.debug "#{now}: sent #{@unsent_timeslice_data.length} timeslices (#{@agent_id}) in #{Time.now - now} seconds"
801
+
802
+ # if we successfully invoked this web service, then clear the unsent message cache.
803
+ @unsent_timeslice_data = {}
804
+ @last_harvest_time = now
805
+
806
+ # handle_messages
807
+
808
+ # note - exceptions are logged in invoke_remote. If an exception is encountered here,
809
+ # then the metric data is downsampled for another timeslices
810
+ end
811
+
812
+ def harvest_transaction_traces
813
+ @traces = @transaction_sampler.harvest(@traces, @slowest_transaction_threshold)
814
+ @traces
815
+ end
816
+
817
+ def harvest_and_send_slowest_sample
818
+ harvest_transaction_traces
819
+ unless @traces.empty?
820
+ now = Time.now
821
+ log.debug "Sending (#{@traces.length}) transaction traces"
822
+ begin
823
+ # take the traces and prepare them for sending across the
824
+ # wire. This includes gathering SQL explanations, stripping
825
+ # out stack traces, and normalizing SQL. note that we
826
+ # explain only the sql statements whose segments' execution
827
+ # times exceed our threshold (to avoid unnecessary overhead
828
+ # of running explains on fast queries.)
829
+ options = { :keep_backtraces => true }
830
+ options[:record_sql] = @record_sql unless @record_sql == :off
831
+ options[:explain_sql] = @explain_threshold if @explain_enabled
832
+ traces = @traces.collect {|trace| trace.prepare_to_send(options)}
833
+ invoke_remote :transaction_sample_data, @agent_id, traces
834
+ rescue PostTooBigException
835
+ # we tried to send too much data, drop the first trace and
836
+ # try again
837
+ retry if @traces.shift
838
+ end
839
+
840
+ log.debug "Sent slowest sample (#{@agent_id}) in #{Time.now - now} seconds"
841
+ end
842
+
843
+ # if we succeed sending this sample, then we don't need to keep
844
+ # the slowest sample around - it has been sent already and we
845
+ # can collect the next one
846
+ @traces = nil
847
+
848
+ # note - exceptions are logged in invoke_remote. If an
849
+ # exception is encountered here, then the slowest sample of is
850
+ # determined of the entire period since the last reported
851
+ # sample.
852
+ end
853
+
854
+ def harvest_errors
855
+ @unsent_errors = @error_collector.harvest_errors(@unsent_errors)
856
+ @unsent_errors
857
+ end
858
+
859
+ def harvest_and_send_errors
860
+ harvest_errors
861
+ if @unsent_errors && @unsent_errors.length > 0
862
+ log.debug "Sending #{@unsent_errors.length} errors"
863
+ begin
864
+ invoke_remote :error_data, @agent_id, @unsent_errors
865
+ rescue PostTooBigException
866
+ @unsent_errors.shift
867
+ retry
868
+ end
869
+ # if the remote invocation fails, then we never clear
870
+ # @unsent_errors, and therefore we can re-attempt to send on
871
+ # the next heartbeat. Note the error collector maxes out at
872
+ # 20 instances to prevent leakage
873
+ @unsent_errors = []
874
+ end
875
+ end
876
+
877
+ def compress_data(object)
878
+ dump = Marshal.dump(object)
879
+
880
+ # this checks to make sure mongrel won't choke on big uploads
881
+ check_post_size(dump)
882
+
883
+ # we currently optimize for CPU here since we get roughly a 10x
884
+ # reduction in message size with this, and CPU overhead is at a
885
+ # premium. For extra-large posts, we use the higher compression
886
+ # since otherwise it actually errors out.
887
+
888
+ dump_size = dump.size
889
+
890
+ # Compress if content is smaller than 64kb. There are problems
891
+ # with bugs in Ruby in some versions that expose us to a risk of
892
+ # segfaults if we compress aggressively.
893
+ return [dump, 'identity'] if dump_size < (64*1024)
894
+
895
+ # medium payloads get fast compression, to save CPU
896
+ # big payloads get all the compression possible, to stay under
897
+ # the 2,000,000 byte post threshold
898
+ compression = dump_size < 2000000 ? Zlib::BEST_SPEED : Zlib::BEST_COMPRESSION
899
+
900
+ [Zlib::Deflate.deflate(dump, compression), 'deflate']
901
+ end
902
+
903
+ def check_post_size(post_string)
904
+ # TODO: define this as a config option on the server side
905
+ return if post_string.size < control.post_size_limit
906
+ log.warn "Tried to send too much data: #{post_string.size} bytes"
907
+ raise PostTooBigException
908
+ end
909
+
910
+ def send_request(opts)
911
+ request = Net::HTTP::Post.new(opts[:uri], 'CONTENT-ENCODING' => opts[:encoding], 'HOST' => opts[:collector].name)
912
+ request['user-agent'] = user_agent
913
+ request.content_type = "application/octet-stream"
914
+ request.body = opts[:data]
915
+
916
+ log.debug "Connect to #{opts[:collector]}#{opts[:uri]}"
917
+
918
+ response = nil
919
+ http = control.http_connection(collector)
920
+ http.read_timeout = nil
921
+ begin
922
+ NewRelic::TimerLib.timeout(@request_timeout) do
923
+ response = http.request(request)
924
+ end
925
+ rescue Timeout::Error
926
+ log.warn "Timed out trying to post data to New Relic (timeout = #{@request_timeout} seconds)" unless @request_timeout < 30
927
+ raise
928
+ end
929
+ if response.is_a? Net::HTTPServiceUnavailable
930
+ raise NewRelic::Agent::ServerConnectionException, "Service unavailable (#{response.code}): #{response.message}"
931
+ elsif response.is_a? Net::HTTPGatewayTimeOut
932
+ log.debug("Timed out getting response: #{response.message}")
933
+ raise Timeout::Error, response.message
934
+ elsif response.is_a? Net::HTTPRequestEntityTooLarge
935
+ raise PostTooBigException
936
+ elsif !(response.is_a? Net::HTTPSuccess)
937
+ raise NewRelic::Agent::ServerConnectionException, "Unexpected response from server (#{response.code}): #{response.message}"
938
+ end
939
+ response
940
+ end
941
+
942
+ def decompress_response(response)
943
+ if response['content-encoding'] != 'gzip'
944
+ log.debug "Uncompressed content returned"
945
+ return response.body
946
+ end
947
+ log.debug "Decompressing return value"
948
+ i = Zlib::GzipReader.new(StringIO.new(response.body))
949
+ i.read
950
+ end
951
+
952
+ def check_for_exception(response)
953
+ dump = decompress_response(response)
954
+ value = Marshal.load(dump)
955
+ raise value if value.is_a? Exception
956
+ value
957
+ end
958
+
959
+ def remote_method_uri(method)
960
+ uri = "/agent_listener/#{PROTOCOL_VERSION}/#{control.license_key}/#{method}"
961
+ uri << "?run_id=#{@agent_id}" if @agent_id
962
+ uri
963
+ end
964
+
965
+ def user_agent
966
+ ruby_description = ''
967
+ # note the trailing space!
968
+ ruby_description << "(ruby #{::RUBY_VERSION} #{::RUBY_PLATFORM}) " if defined?(::RUBY_VERSION) && defined?(::RUBY_PLATFORM)
969
+ zlib_version = ''
970
+ zlib_version << "zlib/#{Zlib.zlib_version}" if defined?(::Zlib) && Zlib.respond_to?(:zlib_version)
971
+ "NewRelic-RubyAgent/#{NewRelic::VERSION::STRING} #{ruby_description}#{zlib_version}"
972
+ end
973
+
974
+ # send a message via post
975
+ def invoke_remote(method, *args)
976
+ now = Time.now
977
+ #determines whether to zip the data or send plain
978
+ post_data, encoding = compress_data(args)
979
+
980
+ response = send_request({:uri => remote_method_uri(method), :encoding => encoding, :collector => collector, :data => post_data})
981
+
982
+ # raises the right exception if the remote server tells it to die
983
+ return check_for_exception(response)
984
+ rescue NewRelic::Agent::ForceRestartException => e
985
+ log.info e.message
986
+ raise
987
+ rescue SystemCallError, SocketError => e
988
+ # These include Errno connection errors
989
+ raise NewRelic::Agent::ServerConnectionException, "Recoverable error connecting to the server: #{e}"
990
+ ensure
991
+ NewRelic::Agent.instance.stats_engine.get_stats_no_scope('Supportability/invoke_remote').record_data_point((Time.now - now).to_f)
992
+ NewRelic::Agent.instance.stats_engine.get_stats_no_scope('Supportability/invoke_remote/' + method.to_s).record_data_point((Time.now - now).to_f)
993
+ end
994
+
995
+ def graceful_disconnect
996
+ if @connected
997
+ begin
998
+ @request_timeout = 10
999
+ if NewRelic::DataSerialization.should_send_data?
1000
+ log.debug "Sending data to New Relic Service"
1001
+ NewRelic::Agent.load_data
1002
+ harvest_and_send_errors
1003
+ harvest_and_send_slowest_sample
1004
+ harvest_and_send_timeslice_data
1005
+ else
1006
+ log.debug "Serializing agent data to disk"
1007
+ NewRelic::Agent.save_data
1008
+ end
1009
+ if @connected_pid == $$
1010
+ log.debug "Sending New Relic service agent run shutdown message"
1011
+ invoke_remote :shutdown, @agent_id, Time.now.to_f
1012
+ else
1013
+ log.debug "This agent connected from parent process #{@connected_pid}--not sending shutdown"
1014
+ end
1015
+ log.debug "Graceful disconnect complete"
1016
+ rescue Timeout::Error, StandardError
1017
+ end
1018
+ else
1019
+ log.debug "Bypassing graceful disconnect - agent not connected"
1020
+ end
1021
+ end
1022
+ def default_sql_obfuscator(sql)
1023
+ sql = sql.dup
1024
+ # This is hardly readable. Use the unit tests.
1025
+ # remove single quoted strings:
1026
+ sql.gsub!(/'(.*?[^\\'])??'(?!')/, '?')
1027
+ # remove double quoted strings:
1028
+ sql.gsub!(/"(.*?[^\\"])??"(?!")/, '?')
1029
+ # replace all number literals
1030
+ sql.gsub!(/\d+/, "?")
1031
+ sql
1032
+ end
1033
+ end
1034
+
1035
+ extend ClassMethods
1036
+ include InstanceMethods
1037
+ include BrowserMonitoring
1038
+ end
1039
+ end
1040
+ end