dolores_rpm 3.2.0.2

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 (257) hide show
  1. data/CHANGELOG +559 -0
  2. data/LICENSE +64 -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/dolores_rpm-3.3.4.fork.gem +0 -0
  11. data/install.rb +9 -0
  12. data/lib/conditional_vendored_dependency_detection.rb +3 -0
  13. data/lib/conditional_vendored_metric_parser.rb +5 -0
  14. data/lib/new_relic/agent/agent.rb +1311 -0
  15. data/lib/new_relic/agent/beacon_configuration.rb +110 -0
  16. data/lib/new_relic/agent/browser_monitoring.rb +102 -0
  17. data/lib/new_relic/agent/busy_calculator.rb +99 -0
  18. data/lib/new_relic/agent/chained_call.rb +13 -0
  19. data/lib/new_relic/agent/database.rb +203 -0
  20. data/lib/new_relic/agent/error_collector.rb +251 -0
  21. data/lib/new_relic/agent/instrumentation/active_merchant.rb +27 -0
  22. data/lib/new_relic/agent/instrumentation/acts_as_solr.rb +68 -0
  23. data/lib/new_relic/agent/instrumentation/authlogic.rb +19 -0
  24. data/lib/new_relic/agent/instrumentation/controller_instrumentation.rb +424 -0
  25. data/lib/new_relic/agent/instrumentation/data_mapper.rb +57 -0
  26. data/lib/new_relic/agent/instrumentation/delayed_job_instrumentation.rb +52 -0
  27. data/lib/new_relic/agent/instrumentation/memcache.rb +80 -0
  28. data/lib/new_relic/agent/instrumentation/merb/controller.rb +41 -0
  29. data/lib/new_relic/agent/instrumentation/merb/errors.rb +29 -0
  30. data/lib/new_relic/agent/instrumentation/metric_frame/pop.rb +80 -0
  31. data/lib/new_relic/agent/instrumentation/metric_frame.rb +332 -0
  32. data/lib/new_relic/agent/instrumentation/net.rb +29 -0
  33. data/lib/new_relic/agent/instrumentation/passenger_instrumentation.rb +36 -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 +114 -0
  37. data/lib/new_relic/agent/instrumentation/rails/action_web_service.rb +42 -0
  38. data/lib/new_relic/agent/instrumentation/rails/active_record_instrumentation.rb +115 -0
  39. data/lib/new_relic/agent/instrumentation/rails/errors.rb +42 -0
  40. data/lib/new_relic/agent/instrumentation/rails3/action_controller.rb +118 -0
  41. data/lib/new_relic/agent/instrumentation/rails3/active_record_instrumentation.rb +122 -0
  42. data/lib/new_relic/agent/instrumentation/rails3/errors.rb +37 -0
  43. data/lib/new_relic/agent/instrumentation/sinatra.rb +58 -0
  44. data/lib/new_relic/agent/instrumentation/sunspot.rb +29 -0
  45. data/lib/new_relic/agent/instrumentation/unicorn_instrumentation.rb +21 -0
  46. data/lib/new_relic/agent/instrumentation.rb +9 -0
  47. data/lib/new_relic/agent/method_tracer.rb +528 -0
  48. data/lib/new_relic/agent/sampler.rb +50 -0
  49. data/lib/new_relic/agent/samplers/cpu_sampler.rb +58 -0
  50. data/lib/new_relic/agent/samplers/delayed_job_lock_sampler.rb +40 -0
  51. data/lib/new_relic/agent/samplers/memory_sampler.rb +144 -0
  52. data/lib/new_relic/agent/samplers/object_sampler.rb +26 -0
  53. data/lib/new_relic/agent/shim_agent.rb +29 -0
  54. data/lib/new_relic/agent/sql_sampler.rb +267 -0
  55. data/lib/new_relic/agent/stats_engine/metric_stats.rb +187 -0
  56. data/lib/new_relic/agent/stats_engine/samplers.rb +95 -0
  57. data/lib/new_relic/agent/stats_engine/transactions.rb +208 -0
  58. data/lib/new_relic/agent/stats_engine.rb +25 -0
  59. data/lib/new_relic/agent/transaction_sample_builder.rb +101 -0
  60. data/lib/new_relic/agent/transaction_sampler.rb +397 -0
  61. data/lib/new_relic/agent/worker_loop.rb +89 -0
  62. data/lib/new_relic/agent.rb +454 -0
  63. data/lib/new_relic/collection_helper.rb +75 -0
  64. data/lib/new_relic/command.rb +85 -0
  65. data/lib/new_relic/commands/deployments.rb +105 -0
  66. data/lib/new_relic/commands/install.rb +80 -0
  67. data/lib/new_relic/control/class_methods.rb +53 -0
  68. data/lib/new_relic/control/configuration.rb +202 -0
  69. data/lib/new_relic/control/frameworks/external.rb +16 -0
  70. data/lib/new_relic/control/frameworks/merb.rb +31 -0
  71. data/lib/new_relic/control/frameworks/rails.rb +164 -0
  72. data/lib/new_relic/control/frameworks/rails3.rb +75 -0
  73. data/lib/new_relic/control/frameworks/ruby.rb +42 -0
  74. data/lib/new_relic/control/frameworks/sinatra.rb +20 -0
  75. data/lib/new_relic/control/frameworks.rb +10 -0
  76. data/lib/new_relic/control/instance_methods.rb +179 -0
  77. data/lib/new_relic/control/instrumentation.rb +100 -0
  78. data/lib/new_relic/control/logging_methods.rb +143 -0
  79. data/lib/new_relic/control/profiling.rb +25 -0
  80. data/lib/new_relic/control/server_methods.rb +114 -0
  81. data/lib/new_relic/control.rb +46 -0
  82. data/lib/new_relic/data_serialization.rb +157 -0
  83. data/lib/new_relic/delayed_job_injection.rb +46 -0
  84. data/lib/new_relic/language_support.rb +69 -0
  85. data/lib/new_relic/local_environment.rb +414 -0
  86. data/lib/new_relic/merbtasks.rb +6 -0
  87. data/lib/new_relic/metric_data.rb +51 -0
  88. data/lib/new_relic/metric_spec.rb +75 -0
  89. data/lib/new_relic/metrics.rb +9 -0
  90. data/lib/new_relic/noticed_error.rb +24 -0
  91. data/lib/new_relic/rack/browser_monitoring.rb +68 -0
  92. data/lib/new_relic/rack/developer_mode.rb +268 -0
  93. data/lib/new_relic/recipes.rb +73 -0
  94. data/lib/new_relic/stats.rb +388 -0
  95. data/lib/new_relic/timer_lib.rb +27 -0
  96. data/lib/new_relic/transaction_analysis/segment_summary.rb +49 -0
  97. data/lib/new_relic/transaction_analysis.rb +77 -0
  98. data/lib/new_relic/transaction_sample/composite_segment.rb +27 -0
  99. data/lib/new_relic/transaction_sample/fake_segment.rb +9 -0
  100. data/lib/new_relic/transaction_sample/segment.rb +201 -0
  101. data/lib/new_relic/transaction_sample/summary_segment.rb +21 -0
  102. data/lib/new_relic/transaction_sample.rb +245 -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/recipes/newrelic.rb +6 -0
  111. data/test/active_record_fixtures.rb +77 -0
  112. data/test/config/newrelic.yml +48 -0
  113. data/test/config/test_control.rb +48 -0
  114. data/test/new_relic/agent/agent/connect_test.rb +410 -0
  115. data/test/new_relic/agent/agent/start_test.rb +255 -0
  116. data/test/new_relic/agent/agent/start_worker_thread_test.rb +153 -0
  117. data/test/new_relic/agent/agent_test.rb +139 -0
  118. data/test/new_relic/agent/agent_test_controller.rb +77 -0
  119. data/test/new_relic/agent/agent_test_controller_test.rb +363 -0
  120. data/test/new_relic/agent/apdex_from_server_test.rb +9 -0
  121. data/test/new_relic/agent/beacon_configuration_test.rb +108 -0
  122. data/test/new_relic/agent/browser_monitoring_test.rb +278 -0
  123. data/test/new_relic/agent/busy_calculator_test.rb +81 -0
  124. data/test/new_relic/agent/database_test.rb +162 -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 +175 -0
  127. data/test/new_relic/agent/instrumentation/active_record_instrumentation_test.rb +538 -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 +172 -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 +143 -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/sql_sampler_test.rb +160 -0
  145. data/test/new_relic/agent/stats_engine/metric_stats/harvest_test.rb +150 -0
  146. data/test/new_relic/agent/stats_engine/metric_stats_test.rb +82 -0
  147. data/test/new_relic/agent/stats_engine/samplers_test.rb +99 -0
  148. data/test/new_relic/agent/stats_engine_test.rb +185 -0
  149. data/test/new_relic/agent/transaction_sample_builder_test.rb +195 -0
  150. data/test/new_relic/agent/transaction_sampler_test.rb +955 -0
  151. data/test/new_relic/agent/worker_loop_test.rb +66 -0
  152. data/test/new_relic/agent_test.rb +175 -0
  153. data/test/new_relic/collection_helper_test.rb +149 -0
  154. data/test/new_relic/command/deployments_test.rb +68 -0
  155. data/test/new_relic/control/class_methods_test.rb +62 -0
  156. data/test/new_relic/control/configuration_test.rb +72 -0
  157. data/test/new_relic/control/logging_methods_test.rb +185 -0
  158. data/test/new_relic/control_test.rb +254 -0
  159. data/test/new_relic/data_serialization_test.rb +208 -0
  160. data/test/new_relic/delayed_job_injection_test.rb +16 -0
  161. data/test/new_relic/local_environment_test.rb +72 -0
  162. data/test/new_relic/metric_data_test.rb +125 -0
  163. data/test/new_relic/metric_spec_test.rb +95 -0
  164. data/test/new_relic/rack/all_test.rb +11 -0
  165. data/test/new_relic/rack/browser_monitoring_test.rb +84 -0
  166. data/test/new_relic/rack/developer_mode_helper_test.rb +141 -0
  167. data/test/new_relic/rack/developer_mode_test.rb +43 -0
  168. data/test/new_relic/stats_test.rb +426 -0
  169. data/test/new_relic/transaction_analysis/segment_summary_test.rb +91 -0
  170. data/test/new_relic/transaction_analysis_test.rb +121 -0
  171. data/test/new_relic/transaction_sample/composite_segment_test.rb +35 -0
  172. data/test/new_relic/transaction_sample/fake_segment_test.rb +17 -0
  173. data/test/new_relic/transaction_sample/segment_test.rb +389 -0
  174. data/test/new_relic/transaction_sample/summary_segment_test.rb +31 -0
  175. data/test/new_relic/transaction_sample_subtest_test.rb +56 -0
  176. data/test/new_relic/transaction_sample_test.rb +164 -0
  177. data/test/new_relic/version_number_test.rb +89 -0
  178. data/test/test_contexts.rb +29 -0
  179. data/test/test_helper.rb +154 -0
  180. data/ui/helpers/developer_mode_helper.rb +357 -0
  181. data/ui/helpers/google_pie_chart.rb +48 -0
  182. data/ui/views/layouts/newrelic_default.rhtml +47 -0
  183. data/ui/views/newrelic/_explain_plans.rhtml +27 -0
  184. data/ui/views/newrelic/_sample.rhtml +20 -0
  185. data/ui/views/newrelic/_segment.rhtml +28 -0
  186. data/ui/views/newrelic/_segment_limit_message.rhtml +1 -0
  187. data/ui/views/newrelic/_segment_row.rhtml +12 -0
  188. data/ui/views/newrelic/_show_sample_detail.rhtml +24 -0
  189. data/ui/views/newrelic/_show_sample_sql.rhtml +24 -0
  190. data/ui/views/newrelic/_show_sample_summary.rhtml +3 -0
  191. data/ui/views/newrelic/_sql_row.rhtml +16 -0
  192. data/ui/views/newrelic/_stack_trace.rhtml +15 -0
  193. data/ui/views/newrelic/_table.rhtml +12 -0
  194. data/ui/views/newrelic/explain_sql.rhtml +43 -0
  195. data/ui/views/newrelic/file/images/arrow-close.png +0 -0
  196. data/ui/views/newrelic/file/images/arrow-open.png +0 -0
  197. data/ui/views/newrelic/file/images/blue_bar.gif +0 -0
  198. data/ui/views/newrelic/file/images/file_icon.png +0 -0
  199. data/ui/views/newrelic/file/images/gray_bar.gif +0 -0
  200. data/ui/views/newrelic/file/images/new-relic-rpm-desktop.gif +0 -0
  201. data/ui/views/newrelic/file/images/new_relic_rpm_desktop.gif +0 -0
  202. data/ui/views/newrelic/file/images/textmate.png +0 -0
  203. data/ui/views/newrelic/file/javascript/jquery-1.4.2.js +6240 -0
  204. data/ui/views/newrelic/file/javascript/transaction_sample.js +120 -0
  205. data/ui/views/newrelic/file/stylesheets/style.css +490 -0
  206. data/ui/views/newrelic/index.rhtml +71 -0
  207. data/ui/views/newrelic/sample_not_found.rhtml +2 -0
  208. data/ui/views/newrelic/show_sample.rhtml +80 -0
  209. data/ui/views/newrelic/show_source.rhtml +3 -0
  210. data/ui/views/newrelic/threads.rhtml +53 -0
  211. data/vendor/gems/dependency_detection-0.0.1.build/LICENSE +5 -0
  212. data/vendor/gems/dependency_detection-0.0.1.build/lib/dependency_detection/version.rb +3 -0
  213. data/vendor/gems/dependency_detection-0.0.1.build/lib/dependency_detection.rb +62 -0
  214. data/vendor/gems/metric_parser-0.1.0.pre1/lib/metric_parser.rb +1 -0
  215. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/action_mailer.rb +14 -0
  216. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/active_merchant.rb +31 -0
  217. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/active_record.rb +33 -0
  218. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/apdex.rb +89 -0
  219. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/background_transaction.rb +7 -0
  220. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/client.rb +46 -0
  221. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/controller.rb +67 -0
  222. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/controller_cpu.rb +43 -0
  223. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/controller_ext.rb +17 -0
  224. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/database.rb +48 -0
  225. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/database_pool.rb +24 -0
  226. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/dot_net.rb +28 -0
  227. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/dot_net_parser.rb +17 -0
  228. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/errors.rb +11 -0
  229. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/external.rb +55 -0
  230. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/frontend.rb +40 -0
  231. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/gc.rb +20 -0
  232. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/hibernate_session.rb +7 -0
  233. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/java.rb +31 -0
  234. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/java_parser.rb +17 -0
  235. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/jsp.rb +34 -0
  236. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/jsp_tag.rb +7 -0
  237. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/mem_cache.rb +55 -0
  238. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/metric_parser.rb +122 -0
  239. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/orm.rb +27 -0
  240. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/other_transaction.rb +40 -0
  241. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/servlet.rb +7 -0
  242. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/servlet_context_listener.rb +7 -0
  243. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/servlet_filter.rb +7 -0
  244. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/solr.rb +27 -0
  245. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/solr_request_handler.rb +15 -0
  246. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/spring.rb +54 -0
  247. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/spring_controller.rb +6 -0
  248. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/spring_view.rb +6 -0
  249. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/struts_action.rb +20 -0
  250. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/struts_result.rb +20 -0
  251. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/version.rb +5 -0
  252. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/view.rb +70 -0
  253. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/web_frontend.rb +18 -0
  254. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/web_service.rb +14 -0
  255. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/web_transaction.rb +133 -0
  256. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser.rb +64 -0
  257. metadata +398 -0
@@ -0,0 +1,80 @@
1
+ require 'new_relic/agent/instrumentation'
2
+ module NewRelic
3
+ module Agent
4
+ module Instrumentation
5
+ class MetricFrame
6
+ module Pop
7
+
8
+ def clear_thread_metric_frame!
9
+ Thread.current[:newrelic_metric_frame] = nil
10
+ end
11
+
12
+ def set_new_scope!(metric)
13
+ agent.stats_engine.scope_name = metric
14
+ end
15
+
16
+ def log_underflow
17
+ NewRelic::Agent.logger.error "Underflow in metric frames: #{caller.join("\n ")}"
18
+ end
19
+
20
+ def notice_scope_empty
21
+ transaction_sampler.notice_scope_empty
22
+ sql_sampler.notice_scope_empty
23
+ end
24
+
25
+ def record_transaction_cpu
26
+ burn = cpu_burn
27
+ transaction_sampler.notice_transaction_cpu_time(burn) if burn
28
+ end
29
+
30
+ def normal_cpu_burn
31
+ return unless @process_cpu_start
32
+ process_cpu - @process_cpu_start
33
+ end
34
+
35
+ def jruby_cpu_burn
36
+ return unless @jruby_cpu_start
37
+ burn = (jruby_cpu_time - @jruby_cpu_start)
38
+ record_jruby_cpu_burn(burn)
39
+ burn
40
+ end
41
+
42
+ # we need to do this here because the normal cpu sampler
43
+ # process doesn't work on JRuby. See the cpu_sampler.rb file
44
+ # to understand where cpu is recorded for non-jruby processes
45
+ def record_jruby_cpu_burn(burn)
46
+ NewRelic::Agent.get_stats_no_scope(NewRelic::Metrics::USER_TIME).record_data_point(burn)
47
+ end
48
+
49
+ def cpu_burn
50
+ normal_cpu_burn || jruby_cpu_burn
51
+ end
52
+
53
+ def end_transaction!
54
+ agent.stats_engine.end_transaction
55
+ end
56
+
57
+ def notify_transaction_sampler(web_transaction)
58
+ record_transaction_cpu
59
+ notice_scope_empty
60
+ end
61
+
62
+ def traced?
63
+ NewRelic::Agent.is_execution_traced?
64
+ end
65
+
66
+ def handle_empty_path_stack(metric)
67
+ raise 'path stack not empty' unless @path_stack.empty?
68
+ notify_transaction_sampler(metric.is_web_transaction?) if traced?
69
+ end_transaction!
70
+ clear_thread_metric_frame!
71
+ end
72
+
73
+ def current_stack_metric
74
+ metric_name
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,332 @@
1
+ require 'new_relic/agent/instrumentation/metric_frame/pop'
2
+
3
+ # A struct holding the information required to measure a controller
4
+ # action. This is put on the thread local. Handles the issue of
5
+ # re-entrancy, or nested action calls.
6
+ #
7
+ # This class is not part of the public API. Avoid making calls on it directly.
8
+ #
9
+ module NewRelic
10
+ module Agent
11
+ module Instrumentation
12
+ class MetricFrame
13
+ # helper module refactored out of the `pop` method
14
+ include Pop
15
+
16
+ attr_accessor :start # A Time instance for the start time, never nil
17
+ attr_accessor :apdex_start # A Time instance used for calculating the apdex score, which
18
+ # might end up being @start, or it might be further upstream if
19
+ # we can find a request header for the queue entry time
20
+ attr_accessor :exception,
21
+ :filtered_params, :force_flag,
22
+ :jruby_cpu_start, :process_cpu_start, :database_metric_name
23
+
24
+ # Give the current metric frame a request context. Use this to
25
+ # get the URI and referer. The request is interpreted loosely
26
+ # as a Rack::Request or an ActionController::AbstractRequest.
27
+ attr_accessor :request
28
+
29
+
30
+ @@check_server_connection = false
31
+ def self.check_server_connection=(value)
32
+ @@check_server_connection = value
33
+ end
34
+ # Return the currently active metric frame, or nil. Call with +true+
35
+ # to create a new metric frame if one is not already on the thread.
36
+ def self.current(create_if_empty=nil)
37
+ f = Thread.current[:newrelic_metric_frame]
38
+ return f if f || !create_if_empty
39
+
40
+ # Reconnect to the server if necessary. This is only done
41
+ # for old versions of passenger that don't implement an explicit after_fork
42
+ # event.
43
+ agent.after_fork(:keep_retrying => false) if @@check_server_connection
44
+
45
+ Thread.current[:newrelic_metric_frame] = new
46
+ end
47
+
48
+ # This is the name of the model currently assigned to database
49
+ # measurements, overriding the default.
50
+ def self.database_metric_name
51
+ current && current.database_metric_name
52
+ end
53
+
54
+ def self.referer
55
+ current && current.referer
56
+ end
57
+
58
+ def self.agent
59
+ NewRelic::Agent.instance
60
+ end
61
+
62
+ @@java_classes_loaded = false
63
+
64
+ if defined? JRuby
65
+ begin
66
+ require 'java'
67
+ include_class 'java.lang.management.ManagementFactory'
68
+ include_class 'com.sun.management.OperatingSystemMXBean'
69
+ @@java_classes_loaded = true
70
+ rescue Exception => e
71
+ end
72
+ end
73
+
74
+ attr_reader :depth
75
+
76
+ def initialize
77
+ Thread.current[:newrelic_start_time] = @start = Time.now
78
+ @path_stack = [] # stack of [controller, path] elements
79
+ @jruby_cpu_start = jruby_cpu_time
80
+ @process_cpu_start = process_cpu
81
+ end
82
+
83
+ def agent
84
+ NewRelic::Agent.instance
85
+ end
86
+
87
+ def transaction_sampler
88
+ agent.transaction_sampler
89
+ end
90
+
91
+ def sql_sampler
92
+ agent.sql_sampler
93
+ end
94
+
95
+ private :agent
96
+ private :transaction_sampler
97
+ private :sql_sampler
98
+
99
+ # Indicate that we are entering a measured controller action or task.
100
+ # Make sure you unwind every push with a pop call.
101
+ def push(m)
102
+ transaction_sampler.notice_first_scope_push(start)
103
+ sql_sampler.notice_first_scope_push(start)
104
+ @path_stack.push NewRelic::MetricParser::MetricParser.for_metric_named(m)
105
+ end
106
+
107
+ # Indicate that you don't want to keep the currently saved transaction
108
+ # information
109
+ def self.abort_transaction!
110
+ current.abort_transaction! if current
111
+ end
112
+
113
+ # For the current web transaction, return the path of the URI minus the host part and query string, or nil.
114
+ def uri
115
+ @uri ||= self.class.uri_from_request(@request) unless @request.nil?
116
+ end
117
+
118
+ # For the current web transaction, return the full referer, minus the host string, or nil.
119
+ def referer
120
+ @referer ||= self.class.referer_from_request(@request)
121
+ end
122
+
123
+ # Call this to ensure that the current transaction is not saved
124
+ def abort_transaction!
125
+ transaction_sampler.ignore_transaction
126
+ end
127
+ # This needs to be called after entering the call to trace the
128
+ # controller action, otherwise the controller action blames
129
+ # itself. It gets reset in the normal #pop call.
130
+ def start_transaction
131
+ agent.stats_engine.start_transaction metric_name
132
+ # Only push the transaction context info once, on entry:
133
+ if @path_stack.size == 1
134
+ transaction_sampler.notice_transaction(metric_name, uri, filtered_params)
135
+ sql_sampler.notice_transaction(metric_name, uri, filtered_params)
136
+ end
137
+ end
138
+
139
+ def current_metric
140
+ @path_stack.last
141
+ end
142
+
143
+ # Return the path, the part of the metric after the category
144
+ def path
145
+ @path_stack.last.last
146
+ end
147
+
148
+ # Unwind one stack level. It knows if it's back at the outermost caller and
149
+ # does the appropriate wrapup of the context.
150
+ def pop
151
+ metric = @path_stack.pop
152
+ log_underflow if metric.nil?
153
+ if @path_stack.empty?
154
+ handle_empty_path_stack(metric)
155
+ else
156
+ set_new_scope!(current_stack_metric)
157
+ end
158
+ end
159
+
160
+ # If we have an active metric frame, notice the error and increment the error metric.
161
+ # Options:
162
+ # * <tt>:request</tt> => Request object to get the uri and referer
163
+ # * <tt>:uri</tt> => The request path, minus any request params or query string.
164
+ # * <tt>:referer</tt> => The URI of the referer
165
+ # * <tt>:metric</tt> => The metric name associated with the transaction
166
+ # * <tt>:request_params</tt> => Request parameters, already filtered if necessary
167
+ # * <tt>:custom_params</tt> => Custom parameters
168
+ # Anything left over is treated as custom params
169
+
170
+ def self.notice_error(e, options={})
171
+ request = options.delete(:request)
172
+ if request
173
+ options[:referer] = referer_from_request(request)
174
+ options[:uri] = uri_from_request(request)
175
+ end
176
+ if current
177
+ current.notice_error(e, options)
178
+ else
179
+ agent.error_collector.notice_error(e, options)
180
+ end
181
+ end
182
+
183
+ # Do not call this. Invoke the class method instead.
184
+ def notice_error(e, options={}) # :nodoc:
185
+ params = custom_parameters
186
+ options[:referer] = referer if referer
187
+ options[:request_params] = filtered_params if filtered_params
188
+ options[:uri] = uri if uri
189
+ options[:metric] = metric_name
190
+ options.merge!(custom_parameters)
191
+ if exception != e
192
+ result = agent.error_collector.notice_error(e, options)
193
+ self.exception = result if result
194
+ end
195
+ end
196
+
197
+ # Add context parameters to the metric frame. This information will be passed in to errors
198
+ # and transaction traces. Keys and Values should be strings, numbers or date/times.
199
+ def self.add_custom_parameters(p)
200
+ current.add_custom_parameters(p) if current
201
+ end
202
+
203
+ def self.custom_parameters
204
+ (current && current.custom_parameters) ? current.custom_parameters : {}
205
+ end
206
+
207
+ def record_apdex()
208
+ return unless recording_web_transaction? && NewRelic::Agent.is_execution_traced?
209
+ t = Time.now
210
+ self.class.record_apdex(current_metric, t - start, t - apdex_start, !exception.nil?)
211
+ end
212
+
213
+ def metric_name
214
+ return nil if @path_stack.empty?
215
+ current_metric.name
216
+ end
217
+
218
+ # Return the array of metrics to record for the current metric frame.
219
+ def recorded_metrics
220
+ metrics = [ metric_name ]
221
+ metrics += current_metric.summary_metrics if @path_stack.size == 1
222
+ metrics
223
+ end
224
+
225
+ # Yield to a block that is run with a database metric name context. This means
226
+ # the Database instrumentation will use this for the metric name if it does not
227
+ # otherwise know about a model. This is re-entrant.
228
+ #
229
+ # * <tt>model</tt> is the DB model class
230
+ # * <tt>method</tt> is the name of the finder method or other method to identify the operation with.
231
+ #
232
+ def with_database_metric_name(model, method)
233
+ previous = @database_metric_name
234
+ model_name = case model
235
+ when Class
236
+ model.name
237
+ when String
238
+ model
239
+ else
240
+ model.to_s
241
+ end
242
+ @database_metric_name = "ActiveRecord/#{model_name}/#{method}"
243
+ yield
244
+ ensure
245
+ @database_metric_name=previous
246
+ end
247
+
248
+ def custom_parameters
249
+ @custom_parameters ||= {}
250
+ end
251
+
252
+ def add_custom_parameters(p)
253
+ custom_parameters.merge!(p)
254
+ end
255
+
256
+ def self.recording_web_transaction?
257
+ c = Thread.current[:newrelic_metric_frame]
258
+ if c
259
+ c.recording_web_transaction?
260
+ end
261
+ end
262
+
263
+ def recording_web_transaction?
264
+ current_metric && current_metric.is_web_transaction?
265
+ end
266
+
267
+ def is_web_transaction?(metric)
268
+ 0 == metric.index("Controller")
269
+ end
270
+
271
+ # Make a safe attempt to get the referer from a request object, generally successful when
272
+ # it's a Rack request.
273
+ def self.referer_from_request(request)
274
+ if request && request.respond_to?(:referer)
275
+ request.referer.to_s.split('?').first
276
+ end
277
+ end
278
+
279
+ # Make a safe attempt to get the URI, without the host and query string.
280
+ def self.uri_from_request(request)
281
+ approximate_uri = case
282
+ when request.respond_to?(:fullpath) then request.fullpath
283
+ when request.respond_to?(:path) then request.path
284
+ when request.respond_to?(:request_uri) then request.request_uri
285
+ when request.respond_to?(:uri) then request.uri
286
+ when request.respond_to?(:url) then request.url
287
+ end
288
+ return approximate_uri[%r{^(https?://.*?)?(/[^?]*)}, 2] || '/' if approximate_uri # '
289
+ end
290
+
291
+ def self.record_apdex(current_metric, action_duration, total_duration, is_error)
292
+ summary_stat = agent.stats_engine.get_custom_stats("Apdex", NewRelic::ApdexStats)
293
+ controller_stat = agent.stats_engine.get_custom_stats(current_metric.apdex_metric_path, NewRelic::ApdexStats)
294
+ update_apdex(summary_stat, total_duration, is_error)
295
+ update_apdex(controller_stat, action_duration, is_error)
296
+ end
297
+
298
+ # Record an apdex value for the given stat. when `failed`
299
+ # the apdex should be recorded as a failure regardless of duration.
300
+ def self.update_apdex(stat, duration, failed)
301
+ duration = duration.to_f
302
+ apdex_t = NewRelic::Control.instance.apdex_t
303
+ case
304
+ when failed
305
+ stat.record_apdex_f
306
+ when duration <= apdex_t
307
+ stat.record_apdex_s
308
+ when duration <= 4 * apdex_t
309
+ stat.record_apdex_t
310
+ else
311
+ stat.record_apdex_f
312
+ end
313
+ end
314
+
315
+ private
316
+
317
+ def process_cpu
318
+ return nil if defined? JRuby
319
+ p = Process.times
320
+ p.stime + p.utime
321
+ end
322
+
323
+ def jruby_cpu_time # :nodoc:
324
+ return nil unless @@java_classes_loaded
325
+ threadMBean = ManagementFactory.getThreadMXBean()
326
+ java_utime = threadMBean.getCurrentThreadUserTime() # ns
327
+ -1 == java_utime ? 0.0 : java_utime/1e9
328
+ end
329
+ end
330
+ end
331
+ end
332
+ end
@@ -0,0 +1,29 @@
1
+ DependencyDetection.defer do
2
+ @name = :net
3
+
4
+ depends_on do
5
+ defined?(Net) && defined?(Net::HTTP)
6
+ end
7
+
8
+ executes do
9
+ NewRelic::Agent.logger.debug 'Installing Net instrumentation'
10
+ end
11
+
12
+ executes do
13
+ Net::HTTP.class_eval do
14
+ def request_with_newrelic_trace(*args, &block)
15
+ metrics = ["External/#{@address}/Net::HTTP/#{args[0].method}", "External/#{@address}/all", "External/all"]
16
+ if NewRelic::Agent::Instrumentation::MetricFrame.recording_web_transaction?
17
+ metrics << "External/allWeb"
18
+ else
19
+ metrics << "External/allOther"
20
+ end
21
+ self.class.trace_execution_scoped metrics do
22
+ request_without_newrelic_trace(*args, &block)
23
+ end
24
+ end
25
+ alias request_without_newrelic_trace request
26
+ alias request request_with_newrelic_trace
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,36 @@
1
+ DependencyDetection.defer do
2
+ @name = :passenger
3
+
4
+ depends_on do
5
+ defined?(PhusionPassenger)
6
+ end
7
+
8
+ executes do
9
+ NewRelic::Agent.logger.debug "Installing Passenger event hooks."
10
+
11
+ PhusionPassenger.on_event(:stopping_worker_process) do
12
+ NewRelic::Agent.logger.debug "Passenger stopping this process, shutdown the agent."
13
+ NewRelic::Agent.instance.shutdown
14
+ end
15
+
16
+ PhusionPassenger.on_event(:starting_worker_process) do |forked|
17
+ # We want to reset the stats from the stats engine in case any carried
18
+ # over into the spawned process. Don't clear them in case any were
19
+ # cached. We do this even in conservative spawning.
20
+ NewRelic::Agent.after_fork(:force_reconnect => true)
21
+ end
22
+ end
23
+ end
24
+
25
+ DependencyDetection.defer do
26
+ depends_on do
27
+ defined?(::Passenger) && defined?(::Passenger::AbstractServer) || defined?(::IN_PHUSION_PASSENGER)
28
+ end
29
+
30
+ executes do
31
+ # We're on an older version of passenger
32
+ NewRelic::Agent.logger.warn "An older version of Phusion Passenger has been detected. We recommend using at least release 2.1.1."
33
+
34
+ NewRelic::Agent::Instrumentation::MetricFrame.check_server_connection = true
35
+ end
36
+ end
@@ -0,0 +1,210 @@
1
+ module NewRelic
2
+ module Agent
3
+ module Instrumentation
4
+ module QueueTime
5
+ unless defined?(MAIN_HEADER)
6
+ MAIN_HEADER = 'HTTP_X_REQUEST_START'
7
+ MIDDLEWARE_HEADER = 'HTTP_X_MIDDLEWARE_START'
8
+ QUEUE_HEADER = 'HTTP_X_QUEUE_START'
9
+ ALT_QUEUE_HEADER = 'HTTP_X_QUEUE_TIME'
10
+ HEROKU_QUEUE_HEADER = 'HTTP_X_HEROKU_QUEUE_WAIT_TIME'
11
+ APP_HEADER = 'HTTP_X_APPLICATION_START'
12
+
13
+ HEADER_REGEX = /([^\s\/,(t=)]+)? ?t=([0-9]+)/
14
+ SERVER_METRIC = 'WebFrontend/WebServer/'
15
+ MIDDLEWARE_METRIC = 'Middleware/'
16
+ # no individual queue metric - more than one queue?!
17
+ ALL_SERVER_METRIC = 'WebFrontend/WebServer/all'
18
+ ALL_MIDDLEWARE_METRIC = 'Middleware/all'
19
+ ALL_QUEUE_METRIC = 'WebFrontend/QueueTime'
20
+ end
21
+
22
+ def parse_frontend_headers(headers)
23
+ # these methods add internal state, so we dup so other parts
24
+ # of the app don't have to worry about it.
25
+ # May have performance implications with very large env hashes
26
+ env = headers.dup
27
+ add_end_time_header(Time.now, env)
28
+ middleware_start = parse_middleware_time_from(env)
29
+ queue_start = parse_queue_time_from(env)
30
+ server_start = parse_server_time_from(env)
31
+ # returned for the controller instrumentation
32
+ [middleware_start, queue_start, server_start].min
33
+ end
34
+
35
+ private
36
+
37
+ # main method to extract server time info from env hash,
38
+ # records individual server metrics and one roll-up for all servers
39
+ def parse_server_time_from(env)
40
+ end_time = parse_end_time(env)
41
+ matches = get_matches_from_header(MAIN_HEADER, env)
42
+
43
+ record_individual_server_stats(end_time, matches)
44
+ record_rollup_server_stat(end_time, matches)
45
+ end
46
+
47
+ def parse_middleware_time_from(env)
48
+ end_time = parse_end_time(env)
49
+ matches = get_matches_from_header(MIDDLEWARE_HEADER, env)
50
+
51
+ record_individual_middleware_stats(end_time, matches)
52
+ oldest_time = record_rollup_middleware_stat(end_time, matches)
53
+ # notice this bit: we reset the end time to the earliest
54
+ # middleware tag so that other frontend metrics don't
55
+ # include this time.
56
+ add_end_time_header(oldest_time, env)
57
+ oldest_time
58
+ end
59
+
60
+ def parse_queue_time_from(env)
61
+ oldest_time = nil
62
+ end_time = parse_end_time(env)
63
+ alternate_length = check_for_alternate_queue_length(env)
64
+ if alternate_length
65
+ # skip all that fancy-dan stuff
66
+ NewRelic::Agent.get_stats(ALL_QUEUE_METRIC).trace_call(alternate_length)
67
+ oldest_time = (end_time - alternate_length) # should be a time
68
+ else
69
+ matches = get_matches_from_header(QUEUE_HEADER, env)
70
+ oldest_time = record_rollup_queue_stat(end_time, matches)
71
+ end
72
+ # notice this bit: we reset the end time to the earliest
73
+ # queue tag or the start time minus the queue time so that
74
+ # other frontend metrics don't include this time.
75
+ add_end_time_header(oldest_time, env)
76
+ oldest_time
77
+ end
78
+
79
+ def check_for_alternate_queue_length(env)
80
+ heroku_length = check_for_heroku_queue_length(env)
81
+ return heroku_length if heroku_length
82
+ header = env[ALT_QUEUE_HEADER]
83
+ return nil unless header
84
+ (header.gsub('t=', '').to_i / 1_000_000.0)
85
+ end
86
+
87
+ def check_for_heroku_queue_length(env)
88
+ header = env[HEROKU_QUEUE_HEADER]
89
+ return nil unless header
90
+ (header.gsub(/[^0-9]/, '').to_i / 1_000.0)
91
+ end
92
+
93
+ def get_matches_from_header(header, env)
94
+ return [] if env.nil?
95
+ get_matches(env[header]).map do |name, time|
96
+ convert_to_name_time_pair(name, time)
97
+ end
98
+ end
99
+
100
+ def get_matches(string)
101
+ string.to_s.scan(HEADER_REGEX)
102
+ end
103
+
104
+ def convert_to_name_time_pair(name, time)
105
+ [name, convert_from_microseconds(time.to_i)]
106
+ end
107
+
108
+ def record_individual_stat_of_type(type, end_time, matches)
109
+ matches = matches.sort_by {|name, time| time }
110
+ matches.reverse!
111
+ matches.inject(end_time) {|end_time, pair|
112
+ name, time = pair
113
+ self.send(type, name, time, end_time) if name
114
+ time
115
+ }
116
+ end
117
+
118
+ # goes through the list of servers and records each one in
119
+ # reverse order, subtracting the time for each successive
120
+ # server from the earlier ones in the list.
121
+ # an example because it's complicated:
122
+ # start data:
123
+ # [['a', Time.at(1000)], ['b', Time.at(1001)]], start time: Time.at(1002)
124
+ # initial run: Time.at(1002), ['b', Time.at(1001)]
125
+ # next: Time.at(1001), ['a', Time.at(1000)]
126
+ # see tests for more
127
+ def record_individual_server_stats(end_time, matches) # (Time, [[String, Time]]) -> nil
128
+ record_individual_stat_of_type(:record_server_time_for, end_time, matches)
129
+ end
130
+
131
+ def record_individual_middleware_stats(end_time, matches)
132
+ record_individual_stat_of_type(:record_middleware_time_for, end_time, matches)
133
+ end
134
+
135
+ # records the total time for all servers in a rollup metric
136
+ def record_rollup_server_stat(end_time, matches) # (Time, [String, Time]) -> nil
137
+ record_rollup_stat_of_type(ALL_SERVER_METRIC, end_time, matches)
138
+ end
139
+
140
+ def record_rollup_middleware_stat(end_time, matches)
141
+ record_rollup_stat_of_type(ALL_MIDDLEWARE_METRIC, end_time, matches)
142
+ end
143
+
144
+ def record_rollup_queue_stat(end_time, matches)
145
+ record_rollup_stat_of_type(ALL_QUEUE_METRIC, end_time, matches)
146
+ end
147
+
148
+ def record_rollup_stat_of_type(metric, end_time, matches)
149
+ oldest_time = find_oldest_time(matches) || end_time
150
+ record_time_stat(metric, oldest_time, end_time)
151
+ oldest_time
152
+ end
153
+
154
+ # searches for the first server to touch a request
155
+ def find_oldest_time(matches) # [[String, Time]] -> Time
156
+ matches.map do |name, time|
157
+ time
158
+ end.min
159
+ end
160
+
161
+ # basically just assembles the metric name
162
+ def record_server_time_for(name, start_time, end_time) # (Maybe String, Time, Time) -> nil
163
+ record_time_stat(SERVER_METRIC + name, start_time, end_time) if name
164
+ end
165
+
166
+ def record_middleware_time_for(name, start_time, end_time)
167
+ record_time_stat(MIDDLEWARE_METRIC + name, start_time, end_time)
168
+ end
169
+
170
+ # Checks that the time is not negative, and does the actual
171
+ # data recording
172
+ def record_time_stat(name, start_time, end_time) # (String, Time, Time) -> nil
173
+ total_time = end_time - start_time
174
+ if total_time < 0
175
+ raise "should not provide an end time less than start time: #{end_time} is less than #{start_time}"
176
+ else
177
+ NewRelic::Agent.get_stats(name).trace_call(total_time)
178
+ end
179
+ end
180
+
181
+ def add_end_time_header(end_time, env) # (Time, Env) -> nil
182
+ return unless end_time
183
+ env[APP_HEADER] = "t=#{convert_to_microseconds(end_time)}"
184
+ end
185
+
186
+ def parse_end_time(env)
187
+ header = env[APP_HEADER]
188
+ return Time.now unless header
189
+ convert_from_microseconds(header.gsub('t=', '').to_i)
190
+ end
191
+
192
+ # convert a time to the value provided by the header, for convenience
193
+ def convert_to_microseconds(time) # Time -> Int
194
+ raise TypeError.new('Cannot convert a non-time into microseconds') unless time.is_a?(Time) || time.is_a?(Numeric)
195
+ return time if time.is_a?(Numeric)
196
+ (time.to_f * 1_000_000).to_i
197
+ end
198
+
199
+ # convert a time from the header value (time in microseconds)
200
+ # into a ruby time object
201
+ def convert_from_microseconds(int) # Int -> Time
202
+ raise TypeError.new('Cannot convert a non-number into a time') unless int.is_a?(Time) || int.is_a?(Numeric)
203
+ return int if int.is_a?(Time)
204
+ Time.at((int / 1_000_000.0))
205
+ end
206
+ end
207
+ end
208
+ end
209
+ end
210
+