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,187 @@
1
+ require 'new_relic/language_support'
2
+
3
+ module NewRelic
4
+ module Agent
5
+ class StatsEngine
6
+ # Handles methods related to actual Metric collection
7
+ module MetricStats
8
+ # A simple mutex-synchronized hash to make sure our statistics
9
+ # are internally consistent even in truly-threaded rubies like JRuby
10
+ class SynchronizedHash < ::Hash
11
+ include NewRelic::LanguageSupport::SynchronizedHash
12
+
13
+ def initialize
14
+ @lock = Mutex.new
15
+ end
16
+
17
+ def []=(*args)
18
+ @lock.synchronize { super }
19
+ end
20
+
21
+ def clear(*args)
22
+ @lock.synchronize { super }
23
+ end
24
+
25
+ def delete(*args)
26
+ @lock.synchronize { super }
27
+ end
28
+
29
+ def delete_if(*args)
30
+ @lock.synchronize { super }
31
+ end
32
+ end
33
+
34
+ # Returns all of the metric names of all the stats in the engine
35
+ def metrics
36
+ stats_hash.keys.map(&:to_s)
37
+ end
38
+
39
+ # a simple accessor for looking up a stat with no scope -
40
+ # returns a new stats object if no stats object for that
41
+ # metric exists yet
42
+ def get_stats_no_scope(metric_name)
43
+ stats_hash[NewRelic::MetricSpec.new(metric_name, '')] ||= NewRelic::MethodTraceStats.new
44
+ end
45
+
46
+ # This version allows a caller to pass a stat class to use
47
+ def get_custom_stats(metric_name, stat_class)
48
+ stats_hash[NewRelic::MetricSpec.new(metric_name)] ||= stat_class.new
49
+ end
50
+
51
+ # If use_scope is true, two chained metrics are created, one with scope and one without
52
+ # If scoped_metric_only is true, only a scoped metric is created (used by rendering metrics which by definition are per controller only)
53
+ def get_stats(metric_name, use_scope = true, scoped_metric_only = false, scope = nil)
54
+ scope ||= scope_name if use_scope
55
+ if scoped_metric_only
56
+ spec = NewRelic::MetricSpec.new metric_name, scope
57
+ stats = stats_hash[spec] ||= NewRelic::MethodTraceStats.new
58
+ else
59
+ stats = stats_hash[NewRelic::MetricSpec.new(metric_name)] ||= NewRelic::MethodTraceStats.new
60
+ if scope && scope != metric_name
61
+ spec = NewRelic::MetricSpec.new metric_name, scope
62
+ stats = stats_hash[spec] ||= NewRelic::ScopedMethodTraceStats.new(stats)
63
+ end
64
+ end
65
+ stats
66
+ end
67
+
68
+ # Returns a stat if one exists, otherwise returns nil. If you
69
+ # want auto-initialization, use one of get_stats or get_stats_no_scope
70
+ def lookup_stats(metric_name, scope_name = '')
71
+ stats_hash[NewRelic::MetricSpec.new(metric_name, scope_name)]
72
+ end
73
+
74
+ # This module was extracted from the harvest method and should
75
+ # be refactored
76
+ module Harvest
77
+
78
+ # merge data from previous harvests into this stats engine -
79
+ # takes into account the case where there are new stats for
80
+ # that metric, and the case where there is no current data
81
+ # for that metric
82
+ def merge_data(metric_data_hash)
83
+ metric_data_hash.each do |metric_spec, metric_data|
84
+ new_data = lookup_stats(metric_spec.name, metric_spec.scope)
85
+ if new_data
86
+ new_data.merge!(metric_data.stats)
87
+ else
88
+ stats_hash[metric_spec] = metric_data.stats
89
+ end
90
+ end
91
+ end
92
+
93
+ private
94
+ def get_stats_hash_from(engine_or_hash)
95
+ if engine_or_hash.is_a?(StatsEngine)
96
+ engine_or_hash.stats_hash
97
+ else
98
+ engine_or_hash
99
+ end
100
+ end
101
+
102
+ def coerce_to_metric_spec(metric_spec)
103
+ if metric_spec.is_a?(NewRelic::MetricSpec)
104
+ metric_spec
105
+ else
106
+ NewRelic::MetricSpec.new(metric_spec)
107
+ end
108
+ end
109
+
110
+ def clone_and_reset_stats(metric_spec, stats)
111
+ if stats.nil?
112
+ raise "Nil stats for #{metric_spec.name} (#{metric_spec.scope})"
113
+ end
114
+
115
+ stats_copy = stats.clone
116
+ stats.reset
117
+ stats_copy
118
+ end
119
+
120
+ # if the previous timeslice data has not been reported (due to an error of some sort)
121
+ # then we need to merge this timeslice with the previously accumulated - but not sent
122
+ # data
123
+ def merge_old_data!(metric_spec, stats, old_data)
124
+ metric_data = old_data[metric_spec]
125
+ stats.merge!(metric_data.stats) unless metric_data.nil?
126
+ end
127
+
128
+ def add_data_to_send_unless_empty(data, stats, metric_spec, id)
129
+ # don't bother collecting and reporting stats that have
130
+ # zero-values for this timeslice. significant
131
+ # performance boost and storage savings.
132
+ return if stats.is_reset?
133
+ data[metric_spec] = NewRelic::MetricData.new((id ? nil : metric_spec), stats, id)
134
+ end
135
+
136
+ def merge_stats(other_engine_or_hash, metric_ids)
137
+ old_data = get_stats_hash_from(other_engine_or_hash)
138
+
139
+ timeslice_data = {}
140
+ stats_hash.each do | metric_spec, stats |
141
+
142
+ metric_spec = coerce_to_metric_spec(metric_spec)
143
+ stats_copy = clone_and_reset_stats(metric_spec, stats)
144
+ merge_old_data!(metric_spec, stats_copy, old_data)
145
+ add_data_to_send_unless_empty(timeslice_data, stats_copy, metric_spec, metric_ids[metric_spec])
146
+ end
147
+ timeslice_data
148
+ end
149
+
150
+ end
151
+ include Harvest
152
+
153
+ # Harvest the timeslice data. First recombine current statss
154
+ # with any previously
155
+ # unsent metrics, clear out stats cache, and return the current
156
+ # stats.
157
+ # ---
158
+ # Note: this is not synchronized. There is still some risk in this and
159
+ # we will revisit later to see if we can make this more robust without
160
+ # sacrificing efficiency.
161
+ # +++
162
+ def harvest_timeslice_data(previous_timeslice_data, metric_ids)
163
+
164
+ poll harvest_samplers
165
+ merge_stats(previous_timeslice_data, metric_ids)
166
+ end
167
+
168
+ # Remove all stats. For test code only.
169
+ def clear_stats
170
+ @stats_hash = SynchronizedHash.new
171
+ NewRelic::Agent::BusyCalculator.reset
172
+ end
173
+
174
+ # Reset each of the stats, such as when a new passenger instance starts up.
175
+ def reset_stats
176
+ stats_hash.values.each { |s| s.reset }
177
+ end
178
+
179
+ # returns a memoized SynchronizedHash that holds the actual
180
+ # instances of Stats keyed off their MetricName
181
+ def stats_hash
182
+ @stats_hash ||= SynchronizedHash.new
183
+ end
184
+ end
185
+ end
186
+ end
187
+ end
@@ -0,0 +1,95 @@
1
+ module NewRelic
2
+ module Agent
3
+ class StatsEngine
4
+ module Shim # :nodoc:
5
+ def add_sampler(*args); end
6
+ def add_harvest_sampler(*args); end
7
+ def start_sampler_thread(*args); end
8
+ end
9
+
10
+ # Contains statistics engine extensions to support the concept of samplers
11
+ module Samplers
12
+
13
+ # By default a sampler polls on harvest time, once a minute. However you can
14
+ # override #use_harvest_sampler? to return false and it will sample
15
+ # every POLL_PERIOD seconds on a background thread.
16
+ POLL_PERIOD = 20
17
+
18
+ # starts the sampler thread which runs periodically, rather than
19
+ # at harvest time. This is deprecated, and should not actually
20
+ # be used - mo threads mo problems
21
+ #
22
+ # returns unless there are actually periodic samplers to run
23
+ def start_sampler_thread
24
+
25
+ return if @sampler_thread && @sampler_thread.alive?
26
+
27
+ # start up a thread that will periodically poll for metric samples
28
+ return if periodic_samplers.empty?
29
+
30
+ @sampler_thread = Thread.new do
31
+ while true do
32
+ begin
33
+ sleep POLL_PERIOD
34
+ poll periodic_samplers
35
+ end
36
+ end
37
+ end
38
+ @sampler_thread['newrelic_label'] = 'Sampler Tasks'
39
+ end
40
+
41
+ private
42
+
43
+ def add_sampler_to(sampler_array, sampler)
44
+ raise "Sampler #{sampler.inspect} is already registered. Don't call add_sampler directly anymore." if sampler_array.include?(sampler)
45
+ sampler_array << sampler
46
+ sampler.stats_engine = self
47
+ end
48
+
49
+ def log_added_sampler(type, sampler)
50
+ log.debug "Adding #{type} sampler: #{sampler.inspect}"
51
+ end
52
+
53
+ public
54
+
55
+ # Add an instance of Sampler to be invoked about every 10 seconds on a background
56
+ # thread.
57
+ def add_sampler(sampler)
58
+ add_sampler_to(periodic_samplers, sampler)
59
+ log_added_sampler('periodic', sampler)
60
+ end
61
+
62
+ # Add a sampler to be invoked just before each harvest.
63
+ def add_harvest_sampler(sampler)
64
+ add_sampler_to(harvest_samplers, sampler)
65
+ log_added_sampler('harvest-time', sampler)
66
+ end
67
+
68
+ private
69
+
70
+ # Call poll on each of the samplers. Remove
71
+ # the sampler if it raises.
72
+ def poll(samplers)
73
+ samplers.delete_if do |sampled_item|
74
+ begin
75
+ sampled_item.poll
76
+ false # it's okay. don't delete it.
77
+ rescue Exception => e
78
+ log.error "Removing #{sampled_item} from list"
79
+ log.error e
80
+ log.debug e.backtrace.to_s
81
+ true # remove the sampler
82
+ end
83
+ end
84
+ end
85
+
86
+ def harvest_samplers
87
+ @harvest_samplers ||= []
88
+ end
89
+ def periodic_samplers
90
+ @periodic_samplers ||= []
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,208 @@
1
+ # -*- coding: utf-8 -*-
2
+ module NewRelic
3
+ module Agent
4
+ class StatsEngine
5
+ # A simple stack element that tracks the current name and length
6
+ # of the executing stack
7
+ class ScopeStackElement
8
+ attr_reader :name, :deduct_call_time_from_parent
9
+ attr_accessor :children_time
10
+ def initialize(name, deduct_call_time)
11
+ @name = name
12
+ @deduct_call_time_from_parent = deduct_call_time
13
+ @children_time = 0
14
+ end
15
+ end
16
+
17
+ # Handles pushing and popping elements onto an internal stack that
18
+ # tracks where time should be allocated in Transaction Traces
19
+ module Transactions
20
+
21
+ # Defines methods that stub out the stats engine methods
22
+ # when the agent is disabled
23
+ module Shim # :nodoc:
24
+ def start_transaction(*args); end
25
+ def end_transaction; end
26
+ def push_scope(*args); end
27
+ def transaction_sampler=(*args); end
28
+ def sql_sampler=(*args); end
29
+ def scope_name=(*args); end
30
+ def scope_name; end
31
+ def pop_scope(*args); end
32
+ end
33
+
34
+ # add a new transaction sampler, unless we're currently in a
35
+ # transaction (then we fail)
36
+ def transaction_sampler= sampler
37
+ fail "Can't add a scope listener midflight in a transaction" if scope_stack.any?
38
+ @transaction_sampler = sampler
39
+ end
40
+
41
+ # removes a transaction sampler
42
+ def remove_transaction_sampler(l)
43
+ @transaction_sampler = nil
44
+ end
45
+
46
+ def sql_sampler= sampler
47
+ fail "Can't add a scope listener midflight in a transaction" if scope_stack.any?
48
+ @sql_sampler = sampler
49
+ end
50
+
51
+ def remove_sql_sampler(l)
52
+ @sql_sampler = nil
53
+ end
54
+
55
+ # Pushes a scope onto the transaction stack - this generates a
56
+ # TransactionSample::Segment at the end of transaction execution
57
+ def push_scope(metric, time = Time.now.to_f, deduct_call_time_from_parent = true)
58
+
59
+ stack = scope_stack
60
+ if collecting_gc?
61
+ if stack.empty?
62
+ # reset the gc time so we only include gc time spent during this call
63
+ @last_gc_timestamp = gc_time
64
+ @last_gc_count = gc_collections
65
+ else
66
+ capture_gc_time
67
+ end
68
+ end
69
+ @transaction_sampler.notice_push_scope metric, time if @transaction_sampler
70
+ scope = ScopeStackElement.new(metric, deduct_call_time_from_parent)
71
+ stack.push scope
72
+ scope
73
+ end
74
+
75
+ # Pops a scope off the transaction stack - this updates the
76
+ # transaction sampler that we've finished execution of a traced method
77
+ def pop_scope(expected_scope, duration, time=Time.now.to_f)
78
+ capture_gc_time if collecting_gc?
79
+ stack = scope_stack
80
+ scope = stack.pop
81
+ fail "unbalanced pop from blame stack, got #{scope ? scope.name : 'nil'}, expected #{expected_scope ? expected_scope.name : 'nil'}" if scope != expected_scope
82
+
83
+ if !stack.empty?
84
+ if scope.deduct_call_time_from_parent
85
+ stack.last.children_time += duration
86
+ else
87
+ stack.last.children_time += scope.children_time
88
+ end
89
+ end
90
+ @transaction_sampler.notice_pop_scope(scope.name, time) if @transaction_sampler
91
+ scope
92
+ end
93
+
94
+ # Returns the latest ScopeStackElement
95
+ def peek_scope
96
+ scope_stack.last
97
+ end
98
+
99
+ # set the name of the transaction for the current thread, which will be used
100
+ # to define the scope of all traced methods called on this thread until the
101
+ # scope stack is empty.
102
+ #
103
+ # currently the transaction name is the name of the controller action that
104
+ # is invoked via the dispatcher, but conceivably we could use other transaction
105
+ # names in the future if the traced application does more than service http request
106
+ # via controller actions
107
+ def scope_name=(transaction)
108
+ Thread::current[:newrelic_scope_name] = transaction
109
+ Thread::current[:newrelic_most_recent_transaction] = transaction
110
+ end
111
+
112
+ # Returns the current scope name from the thread local
113
+ def scope_name
114
+ Thread::current[:newrelic_scope_name]
115
+ end
116
+
117
+ # Start a new transaction, unless one is already in progress
118
+ def start_transaction(name = nil)
119
+ Thread::current[:newrelic_scope_stack] ||= []
120
+ self.scope_name = name if name
121
+ end
122
+
123
+ # Try to clean up gracefully, otherwise we leave things hanging around on thread locals.
124
+ # If it looks like a transaction is still in progress, then maybe this is an inner transaction
125
+ # and is ignored.
126
+ #
127
+ def end_transaction
128
+ stack = scope_stack
129
+
130
+ if stack && stack.empty?
131
+ Thread::current[:newrelic_scope_stack] = nil
132
+ Thread::current[:newrelic_scope_name] = nil
133
+ end
134
+ end
135
+
136
+ private
137
+
138
+ # Make sure we don't do this in a multi-threaded environment
139
+ def collecting_gc?
140
+ if !defined?(@@collecting_gc)
141
+ @@collecting_gc = false
142
+ if !NewRelic::Control.instance.multi_threaded?
143
+ @@collecting_gc = true if GC.respond_to?(:time) && GC.respond_to?(:collections) # 1.8.x
144
+ @@collecting_gc = true if defined?(GC::Profiler) && GC::Profiler.enabled? # 1.9.2
145
+ end
146
+ end
147
+ @@collecting_gc
148
+ end
149
+
150
+ # The total number of times the garbage collector has run since
151
+ # profiling was enabled
152
+ def gc_collections
153
+ if GC.respond_to?(:count)
154
+ GC.count
155
+ elsif GC.respond_to?(:collections)
156
+ GC.collections
157
+ end
158
+ end
159
+
160
+ # The total amount of time taken by garbage collection since
161
+ # profiling was enabled
162
+ def gc_time
163
+ if GC.respond_to?(:time)
164
+ GC.time
165
+ elsif defined?(GC::Profiler) && GC::Profiler.respond_to?(:total_time)
166
+ # The 1.9 profiler returns a time in usec
167
+ GC::Profiler.total_time * 1000000.0
168
+ end
169
+ end
170
+
171
+ # Assumes collecting_gc?
172
+ def capture_gc_time
173
+ # Skip this if we are already in this segment
174
+ return if !scope_stack.empty? && scope_stack.last.name == "GC/cumulative"
175
+ num_calls = gc_collections - @last_gc_count
176
+ elapsed = (gc_time - @last_gc_timestamp).to_f
177
+ @last_gc_timestamp = gc_time
178
+ @last_gc_count = gc_collections
179
+
180
+ if defined?(GC::Profiler)
181
+ GC::Profiler.clear
182
+ @last_gc_timestamp = 0
183
+ end
184
+
185
+ if num_calls > 0
186
+ # µs to seconds
187
+ elapsed = elapsed / 1000000.0
188
+ # Allocate the GC time to a scope as if the GC just ended
189
+ # right now.
190
+ time = Time.now.to_f
191
+ gc_scope = push_scope("GC/cumulative", time - elapsed)
192
+ # GC stats are collected into a blamed metric which allows
193
+ # us to show the stats controller by controller
194
+ gc_stats = NewRelic::Agent.get_stats(gc_scope.name, true)
195
+ gc_stats.record_multiple_data_points(elapsed, num_calls)
196
+ pop_scope(gc_scope, elapsed, time)
197
+ end
198
+ end
199
+
200
+ # Returns the current scope stack, memoized to a thread local variable
201
+ def scope_stack
202
+ Thread::current[:newrelic_scope_stack] ||= []
203
+ end
204
+
205
+ end
206
+ end
207
+ end
208
+ end
@@ -0,0 +1,25 @@
1
+ require 'new_relic/agent/stats_engine/metric_stats'
2
+ require 'new_relic/agent/stats_engine/samplers'
3
+ require 'new_relic/agent/stats_engine/transactions'
4
+
5
+ module NewRelic
6
+ module Agent
7
+ # This class handles all the statistics gathering for the agent
8
+ class StatsEngine
9
+ include MetricStats
10
+ include Samplers
11
+ include Transactions
12
+
13
+ def initialize
14
+ # Makes the unit tests happy
15
+ Thread::current[:newrelic_scope_stack] = nil
16
+ start_sampler_thread
17
+ end
18
+
19
+ def log
20
+ NewRelic::Control.instance.log
21
+ end
22
+
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,101 @@
1
+ require 'new_relic/collection_helper'
2
+ require 'new_relic/transaction_sample'
3
+ require 'new_relic/control'
4
+ require 'new_relic/agent/instrumentation/metric_frame'
5
+ module NewRelic
6
+ module Agent
7
+ # a builder is created with every sampled transaction, to dynamically
8
+ # generate the sampled data. It is a thread-local object, and is not
9
+ # accessed by any other thread so no need for synchronization.
10
+ class TransactionSampleBuilder
11
+ attr_reader :current_segment, :sample
12
+
13
+ include NewRelic::CollectionHelper
14
+
15
+ def initialize(time=Time.now)
16
+ @sample = NewRelic::TransactionSample.new(time.to_f)
17
+ @sample_start = time.to_f
18
+ @current_segment = @sample.root_segment
19
+ end
20
+
21
+ def sample_id
22
+ @sample.sample_id
23
+ end
24
+ def ignored?
25
+ @ignore || @sample.params[:path].nil?
26
+ end
27
+ def ignore_transaction
28
+ @ignore = true
29
+ end
30
+ def trace_entry(metric_name, time)
31
+ segment = @sample.create_segment(time.to_f - @sample_start, metric_name)
32
+ @current_segment.add_called_segment(segment)
33
+ @current_segment = segment
34
+ end
35
+
36
+ def trace_exit(metric_name, time)
37
+ if metric_name != @current_segment.metric_name
38
+ fail "unbalanced entry/exit: #{metric_name} != #{@current_segment.metric_name}"
39
+ end
40
+ @current_segment.end_trace(time.to_f - @sample_start)
41
+ @current_segment = @current_segment.parent_segment
42
+ end
43
+
44
+ def finish_trace(time)
45
+ # This should never get called twice, but in a rare case that we can't reproduce in house it does.
46
+ # log forensics and return gracefully
47
+ if @sample.frozen?
48
+ log = NewRelic::Control.instance.log
49
+ log.error "Unexpected double-freeze of Transaction Trace Object: \n#{@sample.to_s}"
50
+ return
51
+ end
52
+ @sample.root_segment.end_trace(time.to_f - @sample_start)
53
+ @sample.params[:custom_params] = normalize_params(NewRelic::Agent::Instrumentation::MetricFrame.custom_parameters)
54
+ @sample.freeze
55
+ @current_segment = nil
56
+ end
57
+
58
+ def scope_depth
59
+ depth = -1 # have to account for the root
60
+ current = @current_segment
61
+
62
+ while(current)
63
+ depth += 1
64
+ current = current.parent_segment
65
+ end
66
+
67
+ depth
68
+ end
69
+
70
+ def freeze
71
+ @sample.freeze unless sample.frozen?
72
+ end
73
+
74
+ def set_profile(profile)
75
+ @sample.profile = profile
76
+ end
77
+
78
+ def set_transaction_info(path, uri, params)
79
+ @sample.params[:path] = path
80
+
81
+ if NewRelic::Control.instance.capture_params
82
+ params = normalize_params params
83
+
84
+ @sample.params[:request_params].merge!(params)
85
+ @sample.params[:request_params].delete :controller
86
+ @sample.params[:request_params].delete :action
87
+ end
88
+ @sample.params[:uri] ||= uri || params[:uri]
89
+ end
90
+
91
+ def set_transaction_cpu_time(cpu_time)
92
+ @sample.params[:cpu_time] = cpu_time
93
+ end
94
+
95
+ def sample
96
+ @sample
97
+ end
98
+
99
+ end
100
+ end
101
+ end