newrelic_rpm 3.8.1.221 → 3.9.0.229

Sign up to get free protection for your applications and to get access to all the features.
Files changed (288) hide show
  1. data.tar.gz.sig +0 -0
  2. data/.gitignore +1 -0
  3. data/.yardopts +2 -0
  4. data/CHANGELOG +95 -0
  5. data/README.md +9 -3
  6. data/Rakefile +6 -0
  7. data/lib/new_relic/agent.rb +37 -52
  8. data/lib/new_relic/agent/agent.rb +32 -64
  9. data/lib/new_relic/agent/agent_logger.rb +3 -2
  10. data/lib/new_relic/agent/audit_logger.rb +2 -1
  11. data/lib/new_relic/agent/busy_calculator.rb +10 -8
  12. data/lib/new_relic/agent/configuration.rb +0 -13
  13. data/lib/new_relic/agent/configuration/default_source.rb +27 -20
  14. data/lib/new_relic/agent/configuration/manager.rb +101 -27
  15. data/lib/new_relic/agent/cross_app_monitor.rb +43 -50
  16. data/lib/new_relic/agent/cross_app_tracing.rb +13 -12
  17. data/lib/new_relic/agent/error_collector.rb +31 -35
  18. data/lib/new_relic/agent/harvester.rb +5 -1
  19. data/lib/new_relic/agent/hostname.rb +17 -0
  20. data/lib/new_relic/agent/http_clients/curb_wrappers.rb +1 -1
  21. data/lib/new_relic/agent/http_clients/typhoeus_wrappers.rb +1 -1
  22. data/lib/new_relic/agent/http_clients/uri_util.rb +13 -0
  23. data/lib/new_relic/agent/instrumentation/action_controller_subscriber.rb +18 -32
  24. data/lib/new_relic/agent/instrumentation/action_view_subscriber.rb +15 -15
  25. data/lib/new_relic/agent/instrumentation/active_merchant.rb +1 -1
  26. data/lib/new_relic/agent/instrumentation/active_record.rb +6 -4
  27. data/lib/new_relic/agent/instrumentation/active_record_helper.rb +3 -2
  28. data/lib/new_relic/agent/instrumentation/active_record_subscriber.rb +18 -20
  29. data/lib/new_relic/agent/instrumentation/controller_instrumentation.rb +79 -93
  30. data/lib/new_relic/agent/instrumentation/curb.rb +3 -3
  31. data/lib/new_relic/agent/instrumentation/data_mapper.rb +5 -4
  32. data/lib/new_relic/agent/instrumentation/middleware_proxy.rb +96 -0
  33. data/lib/new_relic/agent/instrumentation/middleware_tracing.rb +69 -0
  34. data/lib/new_relic/agent/instrumentation/net.rb +1 -1
  35. data/lib/new_relic/agent/instrumentation/queue_time.rb +21 -13
  36. data/lib/new_relic/agent/instrumentation/rack.rb +85 -74
  37. data/lib/new_relic/agent/instrumentation/rails3/action_controller.rb +3 -1
  38. data/lib/new_relic/agent/instrumentation/rails_middleware.rb +39 -0
  39. data/lib/new_relic/agent/instrumentation/rubyprof.rb +3 -3
  40. data/lib/new_relic/agent/instrumentation/sidekiq.rb +28 -5
  41. data/lib/new_relic/agent/instrumentation/sinatra.rb +4 -4
  42. data/lib/new_relic/agent/instrumentation/typhoeus.rb +4 -2
  43. data/lib/new_relic/agent/javascript_instrumentor.rb +34 -30
  44. data/lib/new_relic/agent/memory_logger.rb +12 -12
  45. data/lib/new_relic/agent/method_tracer.rb +34 -74
  46. data/lib/new_relic/agent/new_relic_service.rb +1 -1
  47. data/lib/new_relic/agent/pipe_channel_manager.rb +3 -3
  48. data/lib/new_relic/agent/request_sampler.rb +10 -11
  49. data/lib/new_relic/agent/samplers/vm_sampler.rb +6 -6
  50. data/lib/new_relic/agent/shim_agent.rb +2 -1
  51. data/lib/new_relic/agent/sql_sampler.rb +52 -27
  52. data/lib/new_relic/agent/stats.rb +24 -10
  53. data/lib/new_relic/agent/stats_engine/gc_profiler.rb +5 -17
  54. data/lib/new_relic/agent/stats_engine/metric_stats.rb +106 -58
  55. data/lib/new_relic/agent/stats_engine/stats_hash.rb +20 -24
  56. data/lib/new_relic/agent/supported_versions.rb +3 -1
  57. data/lib/new_relic/agent/threading/agent_thread.rb +42 -11
  58. data/lib/new_relic/agent/threading/backtrace_service.rb +3 -7
  59. data/lib/new_relic/agent/threading/thread_profile.rb +2 -2
  60. data/lib/new_relic/agent/traced_method_stack.rb +28 -18
  61. data/lib/new_relic/agent/transaction.rb +249 -196
  62. data/lib/new_relic/agent/transaction_metrics.rb +57 -0
  63. data/lib/new_relic/agent/transaction_sample_builder.rb +10 -7
  64. data/lib/new_relic/agent/transaction_sampler.rb +81 -45
  65. data/lib/new_relic/agent/transaction_state.rb +38 -49
  66. data/lib/new_relic/agent/vm/monotonic_gc_profiler.rb +15 -18
  67. data/lib/new_relic/agent/vm/rubinius_vm.rb +4 -2
  68. data/lib/new_relic/cli/commands/deployments.rb +3 -2
  69. data/lib/new_relic/control/frameworks/ruby.rb +2 -3
  70. data/lib/new_relic/control/frameworks/sinatra.rb +0 -7
  71. data/lib/new_relic/control/instance_methods.rb +3 -5
  72. data/lib/new_relic/json_wrapper.rb +2 -0
  73. data/lib/new_relic/language_support.rb +1 -1
  74. data/lib/new_relic/local_environment.rb +0 -16
  75. data/lib/new_relic/metric_spec.rb +10 -38
  76. data/lib/new_relic/noticed_error.rb +16 -11
  77. data/lib/new_relic/rack/agent_hooks.rb +4 -10
  78. data/lib/new_relic/rack/agent_middleware.rb +31 -0
  79. data/lib/new_relic/rack/browser_monitoring.rb +7 -13
  80. data/lib/new_relic/rack/developer_mode.rb +16 -59
  81. data/lib/new_relic/rack/error_collector.rb +16 -54
  82. data/lib/new_relic/recipes.rb +8 -101
  83. data/lib/new_relic/recipes/capistrano3.rb +66 -0
  84. data/lib/new_relic/recipes/capistrano_legacy.rb +98 -0
  85. data/lib/new_relic/transaction_sample.rb +6 -54
  86. data/lib/new_relic/transaction_sample/composite_segment.rb +1 -1
  87. data/lib/new_relic/transaction_sample/segment.rb +12 -4
  88. data/lib/new_relic/transaction_sample/summary_segment.rb +1 -1
  89. data/lib/new_relic/version.rb +2 -2
  90. data/lib/newrelic_rpm.rb +1 -1
  91. data/lib/sequel/extensions/newrelic_instrumentation.rb +19 -19
  92. data/lib/tasks/tests.rake +20 -1
  93. data/lib/tasks/versions.html.erb +0 -4
  94. data/lib/tasks/versions.rake +4 -3
  95. data/newrelic.yml +4 -12
  96. data/newrelic_rpm.gemspec +1 -1
  97. data/test/agent_helper.rb +146 -44
  98. data/test/config/newrelic.yml +0 -1
  99. data/test/environments/norails/Gemfile +1 -1
  100. data/test/environments/rails21/Gemfile +1 -1
  101. data/test/environments/rails22/Gemfile +1 -1
  102. data/test/environments/rails23/Gemfile +1 -1
  103. data/test/environments/rails30/Gemfile +1 -1
  104. data/test/environments/rails31/Gemfile +1 -1
  105. data/test/environments/rails32/Gemfile +1 -1
  106. data/test/environments/rails40/Gemfile +1 -1
  107. data/test/environments/rails41/Gemfile +1 -1
  108. data/test/multiverse/lib/multiverse/runner.rb +13 -1
  109. data/test/multiverse/lib/multiverse/suite.rb +26 -9
  110. data/test/multiverse/suites/active_record/config/newrelic.yml +0 -1
  111. data/test/multiverse/suites/activemerchant/Envfile +18 -1
  112. data/test/multiverse/suites/agent_only/audit_log_test.rb +4 -3
  113. data/test/multiverse/suites/agent_only/collector_exception_handling_test.rb +35 -0
  114. data/test/multiverse/suites/agent_only/config/newrelic.yml +0 -1
  115. data/test/multiverse/suites/agent_only/cross_application_tracing_test.rb +1 -0
  116. data/test/multiverse/suites/agent_only/encoding_handling_test.rb +3 -2
  117. data/test/multiverse/suites/agent_only/exclusive_time_test.rb +178 -0
  118. data/test/multiverse/suites/agent_only/logging_test.rb +10 -6
  119. data/test/multiverse/suites/agent_only/marshaling_test.rb +11 -9
  120. data/test/multiverse/suites/agent_only/script/loading.rb +1 -1
  121. data/test/multiverse/suites/agent_only/service_timeout_test.rb +5 -1
  122. data/test/multiverse/suites/agent_only/transaction_ignoring_test.rb +2 -1
  123. data/test/multiverse/suites/agent_only/xray_sessions_test.rb +9 -9
  124. data/test/multiverse/suites/capistrano/Capfile +26 -0
  125. data/test/multiverse/suites/capistrano/Envfile +18 -0
  126. data/test/multiverse/suites/capistrano/config/deploy.rb +10 -0
  127. data/test/multiverse/suites/capistrano/config/deploy/production.rb +9 -0
  128. data/test/multiverse/suites/capistrano/config/newrelic.yml +21 -0
  129. data/test/multiverse/suites/capistrano/deployment_test.rb +47 -0
  130. data/test/multiverse/suites/capistrano2/Capfile +4 -0
  131. data/test/multiverse/suites/capistrano2/Envfile +4 -0
  132. data/test/multiverse/suites/capistrano2/config/deploy.rb +19 -0
  133. data/test/multiverse/suites/capistrano2/config/newrelic.yml +21 -0
  134. data/test/multiverse/suites/capistrano2/deployment_test.rb +38 -0
  135. data/test/multiverse/suites/curb/Envfile +10 -1
  136. data/test/multiverse/suites/curb/config/newrelic.yml +0 -1
  137. data/test/multiverse/suites/datamapper/config/newrelic.yml +0 -1
  138. data/test/multiverse/suites/deferred_instrumentation/config/newrelic.yml +0 -1
  139. data/test/multiverse/suites/excon/config/newrelic.yml +0 -1
  140. data/test/multiverse/suites/httpclient/config/newrelic.yml +0 -1
  141. data/test/multiverse/suites/mongo/config/newrelic.yml +0 -1
  142. data/test/multiverse/suites/net_http/config/newrelic.yml +0 -1
  143. data/test/multiverse/suites/padrino/config/newrelic.yml +0 -1
  144. data/test/multiverse/suites/rack/Envfile +25 -0
  145. data/test/multiverse/suites/rack/example_app.rb +50 -0
  146. data/test/multiverse/suites/rack/nested_non_rack_app_test.rb +66 -0
  147. data/test/multiverse/suites/rack/rack_auto_instrumentation_test.rb +143 -0
  148. data/test/multiverse/suites/rack/rack_unsupported_version_test.rb +45 -0
  149. data/test/multiverse/suites/rack/url_map_test.rb +120 -0
  150. data/test/multiverse/suites/rails/Envfile +10 -0
  151. data/test/multiverse/suites/rails/app.rb +28 -63
  152. data/test/multiverse/suites/rails/bad_instrumentation_test.rb +2 -4
  153. data/test/multiverse/suites/rails/config/newrelic.yml +1 -2
  154. data/test/multiverse/suites/rails/dummy.txt +1 -0
  155. data/test/multiverse/suites/rails/error_tracing_test.rb +46 -31
  156. data/test/multiverse/suites/rails/gc_instrumentation_test.rb +0 -1
  157. data/test/multiverse/suites/rails/ignore_test.rb +9 -3
  158. data/test/multiverse/suites/rails/middleware_instrumentation_test.rb +41 -0
  159. data/test/multiverse/suites/rails/middlewares.rb +19 -0
  160. data/test/multiverse/suites/rails/parameter_capture_test.rb +169 -0
  161. data/test/multiverse/suites/rails/queue_time_test.rb +14 -4
  162. data/test/multiverse/suites/rails/rails2_app/app/controllers/application.rb +7 -0
  163. data/test/multiverse/suites/rails/rails2_app/config/boot.rb +127 -0
  164. data/test/multiverse/suites/rails/rails2_app/config/database.yml +18 -0
  165. data/test/multiverse/suites/rails/rails2_app/config/environment.rb +16 -0
  166. data/test/multiverse/suites/rails/rails2_app/config/environments/development.rb +10 -0
  167. data/test/multiverse/suites/rails/rails2_app/config/initializers/load_newrelic_rpm.rb +9 -0
  168. data/test/multiverse/suites/rails/rails2_app/config/preinitializer.rb +25 -0
  169. data/test/multiverse/suites/rails/rails2_app/config/routes.rb +18 -0
  170. data/test/multiverse/suites/rails/rails2_app/db/schema.rb +5 -0
  171. data/test/multiverse/suites/rails/rails3_app/app_rails3_plus.rb +76 -0
  172. data/test/multiverse/suites/rails/request_statistics_test.rb +2 -4
  173. data/test/multiverse/suites/rails/transaction_ignoring_test.rb +3 -5
  174. data/test/multiverse/suites/rails/view_instrumentation_test.rb +73 -42
  175. data/test/multiverse/suites/resque/config/newrelic.yml +0 -1
  176. data/test/multiverse/suites/sequel/config/newrelic.yml +0 -1
  177. data/test/multiverse/suites/sidekiq/Envfile +4 -0
  178. data/test/multiverse/suites/sidekiq/after_suite.rb +7 -0
  179. data/test/multiverse/suites/sidekiq/config/newrelic.yml +0 -1
  180. data/test/multiverse/suites/sidekiq/sidekiq_instrumentation_test.rb +55 -34
  181. data/test/multiverse/suites/sidekiq/sidekiq_server.rb +30 -0
  182. data/test/multiverse/suites/sidekiq/test_worker.rb +60 -0
  183. data/test/multiverse/suites/sinatra/config/newrelic.yml +0 -1
  184. data/test/multiverse/suites/sinatra/ignoring_test.rb +33 -11
  185. data/test/multiverse/suites/sinatra/sinatra_metric_explosion_test.rb +3 -1
  186. data/test/multiverse/suites/typhoeus/Envfile +9 -0
  187. data/test/multiverse/suites/typhoeus/config/newrelic.yml +0 -1
  188. data/test/multiverse/suites/typhoeus/typhoeus_test.rb +10 -0
  189. data/test/multiverse/test/suite_examples/one/a/config/newrelic.yml +0 -1
  190. data/test/multiverse/test/suite_examples/one/b/config/newrelic.yml +0 -1
  191. data/test/new_relic/agent/agent/connect_test.rb +3 -10
  192. data/test/new_relic/agent/agent_logger_test.rb +24 -6
  193. data/test/new_relic/agent/agent_test.rb +7 -8
  194. data/test/new_relic/agent/agent_test_controller.rb +2 -2
  195. data/test/new_relic/agent/audit_logger_test.rb +5 -1
  196. data/test/new_relic/agent/busy_calculator_test.rb +1 -1
  197. data/test/new_relic/agent/configuration/manager_test.rb +68 -69
  198. data/test/new_relic/agent/cross_app_monitor_test.rb +32 -14
  199. data/test/new_relic/agent/cross_app_tracing_test.rb +2 -2
  200. data/test/new_relic/agent/error_collector/notice_error_test.rb +9 -33
  201. data/test/new_relic/agent/error_collector_test.rb +45 -14
  202. data/test/new_relic/agent/harvester_test.rb +9 -0
  203. data/test/new_relic/agent/hostname_test.rb +41 -0
  204. data/test/new_relic/agent/instrumentation/action_controller_subscriber_test.rb +27 -19
  205. data/test/new_relic/agent/instrumentation/active_record_subscriber_test.rb +1 -1
  206. data/test/new_relic/agent/instrumentation/active_record_test.rb +3 -3
  207. data/test/new_relic/agent/instrumentation/controller_instrumentation_test.rb +95 -14
  208. data/test/new_relic/agent/instrumentation/middleware_proxy_test.rb +189 -0
  209. data/test/new_relic/agent/instrumentation/net_instrumentation_test.rb +2 -2
  210. data/test/new_relic/agent/instrumentation/queue_time_test.rb +18 -1
  211. data/test/new_relic/agent/instrumentation/rack_test.rb +10 -1
  212. data/test/new_relic/agent/instrumentation/sinatra_test.rb +3 -1
  213. data/test/new_relic/agent/javascript_instrumentor_test.rb +28 -41
  214. data/test/new_relic/agent/memory_logger_test.rb +14 -0
  215. data/test/new_relic/agent/method_interrobang_test.rb +1 -1
  216. data/test/new_relic/agent/method_tracer/class_methods/add_method_tracer_test.rb +1 -30
  217. data/test/new_relic/agent/method_tracer/instance_methods/trace_execution_scoped_test.rb +5 -21
  218. data/test/new_relic/agent/method_tracer_test.rb +5 -4
  219. data/test/new_relic/agent/mock_scope_listener.rb +2 -2
  220. data/test/new_relic/agent/obfuscator_test.rb +1 -1
  221. data/test/new_relic/agent/pipe_channel_manager_test.rb +17 -5
  222. data/test/new_relic/agent/request_sampler_test.rb +16 -16
  223. data/test/new_relic/agent/rpm_agent_test.rb +23 -29
  224. data/test/new_relic/agent/sql_sampler_test.rb +39 -31
  225. data/test/new_relic/agent/stats_engine/metric_stats_test.rb +189 -117
  226. data/test/new_relic/agent/stats_engine_test.rb +1 -1
  227. data/test/new_relic/agent/stats_hash_test.rb +28 -1
  228. data/test/new_relic/agent/stats_test.rb +1 -12
  229. data/test/new_relic/agent/threading/agent_thread_test.rb +23 -9
  230. data/test/new_relic/agent/threading/backtrace_service_test.rb +33 -32
  231. data/test/new_relic/agent/threading/fake_thread.rb +4 -8
  232. data/test/new_relic/agent/threading/threaded_test_case.rb +4 -14
  233. data/test/new_relic/agent/traced_method_stack_test.rb +43 -27
  234. data/test/new_relic/agent/transaction_interrobang_test.rb +1 -1
  235. data/test/new_relic/agent/transaction_metrics_test.rb +113 -0
  236. data/test/new_relic/agent/transaction_sample_builder_test.rb +1 -61
  237. data/test/new_relic/agent/transaction_sampler_test.rb +176 -228
  238. data/test/new_relic/agent/transaction_state_test.rb +62 -26
  239. data/test/new_relic/agent/transaction_test.rb +198 -80
  240. data/test/new_relic/agent/vm/monotonic_gc_profiler_test.rb +4 -4
  241. data/test/new_relic/agent/vm/rubinius_vm_test.rb +68 -0
  242. data/test/new_relic/agent_test.rb +31 -27
  243. data/test/new_relic/cli/commands/deployments_test.rb +7 -2
  244. data/test/new_relic/control/instance_methods_test.rb +4 -4
  245. data/test/new_relic/control_test.rb +28 -22
  246. data/test/new_relic/dependency_detection_test.rb +14 -0
  247. data/test/new_relic/fake_external_server.rb +1 -0
  248. data/test/new_relic/fake_rpm_site.rb +35 -0
  249. data/test/new_relic/http_client_test_cases.rb +12 -3
  250. data/test/new_relic/json_wrapper_test.rb +5 -0
  251. data/test/new_relic/language_support_test.rb +7 -0
  252. data/test/new_relic/license_test.rb +11 -5
  253. data/test/new_relic/local_environment_test.rb +0 -18
  254. data/test/new_relic/metric_data_test.rb +2 -2
  255. data/test/new_relic/metric_spec_test.rb +4 -23
  256. data/test/new_relic/multiverse_helpers.rb +1 -3
  257. data/test/new_relic/noticed_error_test.rb +6 -22
  258. data/test/new_relic/rack/agent_hooks_test.rb +5 -1
  259. data/test/new_relic/rack/agent_middleware_test.rb +32 -0
  260. data/test/new_relic/rack/browser_monitoring_test.rb +14 -1
  261. data/test/new_relic/rack/developer_mode_helper_test.rb +0 -8
  262. data/test/new_relic/rack/developer_mode_test.rb +1 -1
  263. data/test/new_relic/rack/error_collector_test.rb +6 -30
  264. data/test/new_relic/transaction_sample/fake_segment_test.rb +2 -2
  265. data/test/new_relic/transaction_sample/segment_test.rb +47 -47
  266. data/test/new_relic/transaction_sample_test.rb +9 -8
  267. data/test/performance/lib/performance/instrumentation/stackprof.rb +11 -8
  268. data/test/performance/script/runner +13 -0
  269. data/test/performance/suites/config.rb +5 -2
  270. data/test/performance/suites/rack_middleware.rb +84 -0
  271. data/test/performance/suites/rum_autoinsertion.rb +1 -1
  272. data/test/performance/suites/thread_profiling.rb +1 -1
  273. data/test/test_helper.rb +12 -10
  274. data/ui/helpers/developer_mode_helper.rb +3 -43
  275. data/ui/views/layouts/newrelic_default.rhtml +2 -2
  276. data/ui/views/newrelic/_sample.rhtml +2 -2
  277. data/ui/views/newrelic/_sql_row.rhtml +11 -11
  278. data/ui/views/newrelic/index.rhtml +21 -22
  279. data/vendor/gems/dependency_detection-0.0.1.build/lib/dependency_detection.rb +15 -10
  280. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser.rb +2 -0
  281. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/middleware.rb +34 -0
  282. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/nested.rb +24 -0
  283. metadata +108 -31
  284. metadata.gz.sig +0 -0
  285. data/lib/new_relic/rack/transaction_reset.rb +0 -20
  286. data/test/multiverse/suites/rails/mongrel_queue_depth_test.rb +0 -42
  287. data/test/new_relic/rack/transaction_reset_test.rb +0 -35
  288. data/ui/views/newrelic/show_source.rhtml +0 -3
@@ -44,7 +44,7 @@ module NewRelic
44
44
  end
45
45
  end
46
46
 
47
- def record(metric_specs, value=nil, aux=nil)
47
+ def record(metric_specs, value=nil, aux=nil, &blk)
48
48
  Array(metric_specs).each do |metric_spec|
49
49
  stats = nil
50
50
  begin
@@ -64,19 +64,7 @@ module NewRelic
64
64
  end
65
65
  end
66
66
 
67
- if block_given?
68
- yield stats
69
- else
70
- case value
71
- when Numeric
72
- aux ||= value
73
- stats.record_data_point(value, aux)
74
- when :apdex_s, :apdex_t, :apdex_f
75
- stats.record_apdex(value, aux)
76
- when NewRelic::Agent::Stats
77
- stats.merge!(value)
78
- end
79
- end
67
+ stats.record(value, aux, &blk)
80
68
  end
81
69
  end
82
70
 
@@ -91,13 +79,9 @@ module NewRelic
91
79
  if other.is_a?(StatsHash) && other.started_at < @started_at
92
80
  @started_at = other.started_at
93
81
  end
94
- other.each do |key,val|
82
+ other.each do |key, val|
95
83
  begin
96
- if self.has_key?(key)
97
- self[key].merge!(val)
98
- else
99
- self[key] = val
100
- end
84
+ merge_or_insert(key, val)
101
85
  rescue => err
102
86
  NewRelic::Agent.instance.error_collector. \
103
87
  notice_agent_error(StatsMergerError.new(key, self.fetch(key, nil), val, err))
@@ -106,10 +90,22 @@ module NewRelic
106
90
  self
107
91
  end
108
92
 
109
- def resolve_scopes!(resolved_scope)
110
- placeholder = StatsEngine::SCOPE_PLACEHOLDER.to_s
111
- each_pair do |spec, stats|
112
- spec.scope = resolved_scope if spec.scope == placeholder
93
+ def merge_transaction_metrics!(txn_metrics, scope)
94
+ txn_metrics.each_unscoped do |name, stats|
95
+ spec = NewRelic::MetricSpec.new(name)
96
+ merge_or_insert(spec, stats)
97
+ end
98
+ txn_metrics.each_scoped do |name, stats|
99
+ spec = NewRelic::MetricSpec.new(name, scope)
100
+ merge_or_insert(spec, stats)
101
+ end
102
+ end
103
+
104
+ def merge_or_insert(metric_spec, stats)
105
+ if self.has_key?(metric_spec)
106
+ self[metric_spec].merge!(stats)
107
+ else
108
+ self[metric_spec] = stats
113
109
  end
114
110
  end
115
111
  end
@@ -108,7 +108,8 @@ module NewRelic
108
108
  :rack =>
109
109
  {
110
110
  :type => :web,
111
- :supported => [">= 1.0.0"],
111
+ :supported => [">= 1.1.0"],
112
+ :deprecated => ["~>1.0.0"],
112
113
  :url => "https://rubygems.org/gems/rack",
113
114
  :feed => "https://rubygems.org/gems/rack/versions.atom"
114
115
  },
@@ -242,6 +243,7 @@ module NewRelic
242
243
  :activemerchant =>
243
244
  {
244
245
  :type => :other,
246
+ :supported => [ ">= 1.25.0"],
245
247
  :url => "https://rubygems.org/gems/activemerchant",
246
248
  :feed => "https://rubygems.org/gems/activemerchant/versions.atom"
247
249
  },
@@ -5,23 +5,43 @@
5
5
  module NewRelic
6
6
  module Agent
7
7
  module Threading
8
+ class AgentThread
8
9
 
9
- class AgentThread < ::Thread
10
- def initialize(label)
10
+ def self.create(label, &blk)
11
11
  ::NewRelic::Agent.logger.debug("Creating New Relic thread: #{label}")
12
- self[:newrelic_label] = label
13
- super
12
+ wrapped_blk = Proc.new do
13
+ begin
14
+ blk.call
15
+ rescue => e
16
+ ::NewRelic::Agent.logger.debug("Thread #{label} exited with error", e)
17
+ ensure
18
+ ::NewRelic::Agent.logger.debug("Exiting New Relic thread: #{label}")
19
+ end
20
+ end
21
+
22
+ thread = backing_thread_class.new(&wrapped_blk)
23
+ thread[:newrelic_label] = label
24
+ thread
14
25
  end
15
26
 
16
- def self.bucket_thread(thread, profile_agent_code)
27
+ # Simplifies testing if we don't directly use ::Thread.list, so keep
28
+ # the accessor for it here on AgentThread to use and stub.
29
+ def self.list
30
+ backing_thread_class.list
31
+ end
32
+
33
+ def self.bucket_thread(thread, profile_agent_code) #THREAD_LOCAL_ACCESS
17
34
  if thread.key?(:newrelic_label)
18
35
  profile_agent_code ? :agent : :ignore
19
- elsif TransactionState.in_background_transaction?(thread)
20
- :background
21
- elsif TransactionState.in_request_transaction?(thread)
22
- :request
23
36
  else
24
- :other
37
+ state = TransactionState.tl_state_for(thread)
38
+ if state.in_background_transaction?
39
+ :background
40
+ elsif state.in_request_transaction?
41
+ :request
42
+ else
43
+ :other
44
+ end
25
45
  end
26
46
  end
27
47
 
@@ -35,8 +55,19 @@ module NewRelic
35
55
  bt.reject! { |t| t.include?('/newrelic_rpm-') } unless profile_agent_code
36
56
  bt
37
57
  end
38
- end
39
58
 
59
+ # To allow tests to swap out Thread for a synchronous alternative,
60
+ # surface the backing class we'll use from the class level.
61
+ @backing_thread_class = ::Thread
62
+
63
+ def self.backing_thread_class
64
+ @backing_thread_class
65
+ end
66
+
67
+ def self.backing_thread_class=(clazz)
68
+ @backing_thread_class = clazz
69
+ end
70
+ end
40
71
  end
41
72
  end
42
73
  end
@@ -128,13 +128,9 @@ module NewRelic
128
128
  return if @running || !self.class.is_supported?
129
129
 
130
130
  @running = true
131
- self.worker_thread = AgentThread.new('Backtrace Service') do
132
- begin
133
- # Not passing period because we expect it's already been set.
134
- self.worker_loop.run(&method(:poll))
135
- ensure
136
- NewRelic::Agent.logger.debug("Exiting New Relic thread: Backtrace Service")
137
- end
131
+ self.worker_thread = AgentThread.create('Backtrace Service') do
132
+ # Not passing period because we expect it's already been set.
133
+ self.worker_loop.run(&method(:poll))
138
134
  end
139
135
  end
140
136
 
@@ -78,11 +78,11 @@ module NewRelic
78
78
  end
79
79
  end
80
80
 
81
- def truncate_to_node_count!(count_to_keep)
81
+ def truncate_to_node_count!(count_to_keep) #THREAD_LOCAL_ACCESS
82
82
  all_nodes = @traces.values.map { |n| n.flatten }.flatten
83
83
 
84
84
  NewRelic::Agent.instance.stats_engine.
85
- record_supportability_metric_count("ThreadProfiler/NodeCount", all_nodes.size)
85
+ tl_record_supportability_metric_count("ThreadProfiler/NodeCount", all_nodes.size)
86
86
 
87
87
  all_nodes.sort!
88
88
  nodes_to_prune = Set.new(all_nodes[count_to_keep..-1] || [])
@@ -5,12 +5,11 @@
5
5
  module NewRelic
6
6
  module Agent
7
7
  class TracedMethodFrame
8
- attr_reader :deduct_call_time_from_parent, :tag
9
- attr_accessor :name, :start_time, :children_time, :type
10
- def initialize(tag, start_time, deduct_call_time)
8
+ attr_reader :tag
9
+ attr_accessor :name, :start_time, :children_time, :category
10
+ def initialize(tag, start_time)
11
11
  @tag = tag
12
12
  @start_time = start_time
13
- @deduct_call_time_from_parent = deduct_call_time
14
13
  @children_time = 0
15
14
  end
16
15
  end
@@ -24,14 +23,16 @@ module NewRelic
24
23
  @stack = []
25
24
  end
26
25
 
27
- def self.push_frame(tag, time = Time.now.to_f, deduct_call_time_from_parent = true)
28
- stack = NewRelic::Agent::TransactionState.get.traced_method_stack
29
- stack.push_frame(tag, time, deduct_call_time_from_parent)
26
+ def self.tl_push_frame(tag, time = Time.now.to_f)
27
+ state = NewRelic::Agent::TransactionState.tl_get
28
+ stack = state.traced_method_stack
29
+ stack.push_frame(state, tag, time)
30
30
  end
31
31
 
32
- def self.pop_frame(expected_frame, name, time=Time.now.to_f)
33
- stack = NewRelic::Agent::TransactionState.get.traced_method_stack
34
- stack.pop_frame(expected_frame, name, time)
32
+ def self.tl_pop_frame(expected_frame, name, time, deduct_call_time_from_parent=true)
33
+ state = NewRelic::Agent::TransactionState.tl_get
34
+ stack = state.traced_method_stack
35
+ stack.pop_frame(state, expected_frame, name, time, deduct_call_time_from_parent)
35
36
  end
36
37
 
37
38
  # Pushes a frame onto the transaction stack - this generates a
@@ -41,9 +42,9 @@ module NewRelic
41
42
  #
42
43
  # +tag+ should be a Symbol, and is only for debugging purposes to
43
44
  # identify this frame if the stack gets corrupted.
44
- def push_frame(tag, time = Time.now.to_f, deduct_call_time_from_parent = true)
45
- transaction_sampler.notice_push_frame(time) if sampler_enabled?
46
- frame = TracedMethodFrame.new(tag, time, deduct_call_time_from_parent)
45
+ def push_frame(state, tag, time = Time.now.to_f)
46
+ transaction_sampler.notice_push_frame(state, time) if sampler_enabled?
47
+ frame = TracedMethodFrame.new(tag, time)
47
48
  @stack.push frame
48
49
  frame
49
50
  end
@@ -55,20 +56,25 @@ module NewRelic
55
56
  # push_frame call.
56
57
  #
57
58
  # +name+ will be applied to the generated transaction trace segment.
58
- def pop_frame(expected_frame, name, time=Time.now.to_f)
59
+ def pop_frame(state, expected_frame, name, time, deduct_call_time_from_parent=true)
59
60
  frame = @stack.pop
60
61
  fail "unbalanced pop from blame stack, got #{frame ? frame.tag : 'nil'}, expected #{expected_frame ? expected_frame.tag : 'nil'}" if frame != expected_frame
61
62
 
63
+ note_children_time(frame, time, deduct_call_time_from_parent)
64
+
65
+ transaction_sampler.notice_pop_frame(state, name, time) if sampler_enabled?
66
+ frame.name = name
67
+ frame
68
+ end
69
+
70
+ def note_children_time(frame, time, deduct_call_time_from_parent)
62
71
  if !@stack.empty?
63
- if frame.deduct_call_time_from_parent
72
+ if deduct_call_time_from_parent
64
73
  @stack.last.children_time += (time - frame.start_time)
65
74
  else
66
75
  @stack.last.children_time += frame.children_time
67
76
  end
68
77
  end
69
- transaction_sampler.notice_pop_frame(name, time) if sampler_enabled?
70
- frame.name = name
71
- frame
72
78
  end
73
79
 
74
80
  def sampler_enabled?
@@ -79,6 +85,10 @@ module NewRelic
79
85
  Agent.instance.transaction_sampler
80
86
  end
81
87
 
88
+ def clear
89
+ @stack.clear
90
+ end
91
+
82
92
  def empty?
83
93
  @stack.empty?
84
94
  end
@@ -4,6 +4,7 @@
4
4
 
5
5
  require 'new_relic/agent/transaction_timings'
6
6
  require 'new_relic/agent/instrumentation/queue_time'
7
+ require 'new_relic/agent/transaction_metrics'
7
8
 
8
9
  module NewRelic
9
10
  module Agent
@@ -14,162 +15,167 @@ module NewRelic
14
15
  class Transaction
15
16
 
16
17
  # for nested transactions
17
- SUBTRANSACTION_PREFIX = 'Nested'.freeze
18
+ SUBTRANSACTION_PREFIX = 'Nested/'.freeze
19
+ CONTROLLER_PREFIX = 'Controller/'.freeze
20
+ MIDDLEWARE_PREFIX = 'Middleware/Rack/'.freeze
21
+ TASK_PREFIX = 'OtherTransaction/Background/'.freeze
22
+ RACK_PREFIX = 'Controller/Rack/'.freeze
23
+ SINATRA_PREFIX = 'Controller/Sinatra/'.freeze
18
24
 
19
- attr_accessor :start_time # A Time instance for the start time, never nil
20
- attr_accessor :apdex_start # A Time instance used for calculating the apdex score, which
21
- # might end up being @start, or it might be further upstream if
22
- # we can find a request header for the queue entry time
23
- attr_accessor :type, :exceptions, :filtered_params,
24
- :jruby_cpu_start, :process_cpu_start
25
+ CONTROLLER_MIDDLEWARE_PREFIX = 'Controller/Middleware/Rack'.freeze
25
26
 
26
- attr_reader :database_metric_name
27
+ NESTED_TRACE_STOP_OPTIONS = { :metric => true }.freeze
28
+ WEB_TRANSACTION_CATEGORIES = [:controller, :uri, :rack, :sinatra, :middleware].freeze
27
29
 
28
- attr_reader :guid
29
- attr_reader :stats_hash
30
- attr_reader :gc_start_snapshot
30
+ MIDDLEWARE_SUMMARY_METRICS = ['Middleware/all'.freeze].freeze
31
+ EMPTY_SUMMARY_METRICS = [].freeze
31
32
 
32
- attr_reader :name_from_child
33
+ # A Time instance for the start time, never nil
34
+ attr_accessor :start_time
33
35
 
34
- # Populated with the trace sample once this transaction is completed.
35
- attr_reader :transaction_trace
36
+ # A Time instance used for calculating the apdex score, which
37
+ # might end up being @start, or it might be further upstream if
38
+ # we can find a request header for the queue entry time
39
+ attr_accessor :apdex_start
40
+
41
+ attr_accessor :exceptions,
42
+ :filtered_params,
43
+ :jruby_cpu_start,
44
+ :process_cpu_start
36
45
 
37
46
  # Give the current transaction a request context. Use this to
38
47
  # get the URI and referer. The request is interpreted loosely
39
48
  # as a Rack::Request or an ActionController::AbstractRequest.
40
49
  attr_accessor :request
41
50
 
51
+ # This is the name of the model currently assigned to database
52
+ # measurements, overriding the default.
53
+ attr_reader :database_metric_name
54
+
55
+ attr_reader :guid,
56
+ :metrics,
57
+ :gc_start_snapshot,
58
+ :category,
59
+ :name_from_child
60
+
61
+ # Populated with the trace sample once this transaction is completed.
62
+ attr_reader :transaction_trace
63
+
42
64
  # Return the currently active transaction, or nil.
43
- def self.current
44
- TransactionState.get.current_transaction
65
+ def self.tl_current
66
+ TransactionState.tl_get.current_transaction
45
67
  end
46
68
 
47
- def self.set_default_transaction_name(name, options = {})
48
- txn = current
49
- name = make_transaction_name(name, options[:category])
69
+ def self.set_default_transaction_name(name, options = {}) #THREAD_LOCAL_ACCESS
70
+ txn = tl_current
71
+ name = txn.make_transaction_name(name, options[:category])
50
72
 
51
73
  if txn.frame_stack.empty?
52
- txn.default_name = name
53
- txn.type = options[:category] if options[:category]
74
+ txn.set_default_transaction_name(name, options)
54
75
  else
55
76
  txn.frame_stack.last.name = name
56
- txn.frame_stack.last.type = options[:category] if options[:category]
77
+ txn.frame_stack.last.category = options[:category] if options[:category]
57
78
  end
58
79
  end
59
80
 
60
- def self.set_overriding_transaction_name(name, options = {})
61
- txn = current
81
+ def self.set_overriding_transaction_name(name, options = {}) #THREAD_LOCAL_ACCESS
82
+ txn = tl_current
62
83
  return unless txn
63
84
 
64
- name = make_transaction_name(name, options[:category])
85
+ name = txn.make_transaction_name(name, options[:category])
65
86
 
66
87
  if txn.frame_stack.empty?
67
- txn.default_name = name
68
- txn.name_from_api = name
69
- txn.type = options[:category] if options[:category]
88
+ txn.set_overriding_transaction_name(name, options)
70
89
  else
71
90
  txn.frame_stack.last.name = name
72
- txn.frame_stack.last.type = options[:category] if options[:category]
91
+ txn.frame_stack.last.category = options[:category] if options[:category]
73
92
 
74
93
  # Parent transaction also takes this name, but only if they
75
94
  # are both/neither web transactions.
76
- child_is_web_type = transaction_type_is_web?(txn.frame_stack.last.type)
77
- txn_is_web_type = transaction_type_is_web?(txn.type)
95
+ child_is_web_category = transaction_category_is_web?(txn.frame_stack.last.category)
96
+ txn_is_web_category = transaction_category_is_web?(txn.category)
78
97
 
79
- if (child_is_web_type == txn_is_web_type)
98
+ if (child_is_web_category == txn_is_web_category)
80
99
  txn.name_from_api = name
81
100
  end
82
101
  end
83
102
  end
84
103
 
85
- def self.make_transaction_name(name, category=nil)
104
+ def make_transaction_name(name, category=nil)
86
105
  namer = Instrumentation::ControllerInstrumentation::TransactionNamer
87
- "#{namer.category_name(category)}/#{name}"
106
+ "#{namer.prefix_for_category(self, category)}#{name}"
88
107
  end
89
108
 
90
- def self.start(transaction_type, options)
91
- transaction_type ||= :controller
92
- txn = current
109
+ def self.start(state, category, options)
110
+ category ||= :controller
111
+ txn = state.current_transaction
93
112
 
94
113
  if txn
95
- _, nested_frame = NewRelic::Agent::MethodTracer::TraceExecutionScoped.trace_execution_scoped_header({:deduct_call_time_from_parent => true}, Time.now.to_f)
114
+ if options[:filtered_params] && !options[:filtered_params].empty?
115
+ txn.filtered_params = options[:filtered_params]
116
+ end
117
+
118
+ nested_frame = NewRelic::Agent::MethodTracer::TraceExecutionScoped.trace_execution_scoped_header(state, Time.now.to_f)
96
119
  nested_frame.name = options[:transaction_name]
97
- nested_frame.type = transaction_type
120
+ nested_frame.category = category
98
121
  txn.frame_stack << nested_frame
99
122
  else
100
- txn = Transaction.new(transaction_type, options)
101
- TransactionState.get.current_transaction = txn
102
- txn.start()
123
+ txn = Transaction.new(category, options)
124
+ state.reset(txn)
125
+ txn.start(state)
103
126
  end
104
127
 
105
128
  txn
106
129
  end
107
130
 
108
- def self.stop(end_time=Time.now, opts={})
109
- txn = current
131
+ def self.stop(state, end_time=Time.now)
132
+ txn = state.current_transaction
110
133
 
111
134
  if txn.frame_stack.empty?
112
- txn.stop(end_time, opts)
113
- TransactionState.get.current_transaction = nil
135
+ txn.stop(state, end_time)
136
+ state.reset
114
137
  else
115
138
  nested_frame = txn.frame_stack.pop
116
139
 
117
140
  # Parent transaction inherits the name of the first child
118
141
  # to complete, if they are both/neither web transactions.
119
- nested_is_web_type = transaction_type_is_web?(nested_frame.type)
120
- txn_is_web_type = transaction_type_is_web?(txn.type)
142
+ nested_is_web_category = transaction_category_is_web?(nested_frame.category)
143
+ txn_is_web_category = transaction_category_is_web?(txn.category)
121
144
 
122
- if (nested_is_web_type == txn_is_web_type)
145
+ if (nested_is_web_category == txn_is_web_category)
123
146
  # first child to finish wins
124
147
  txn.name_from_child ||= nested_frame.name
125
148
  end
126
149
 
150
+ nested_name = nested_transaction_name(nested_frame.name)
151
+
152
+ if nested_name.start_with?(MIDDLEWARE_PREFIX)
153
+ summary_metrics = MIDDLEWARE_SUMMARY_METRICS
154
+ else
155
+ summary_metrics = EMPTY_SUMMARY_METRICS
156
+ end
157
+
127
158
  NewRelic::Agent::MethodTracer::TraceExecutionScoped.trace_execution_scoped_footer(
159
+ state,
128
160
  nested_frame.start_time.to_f,
129
- nested_transaction_name(nested_frame.name),
130
- [],
161
+ nested_name,
162
+ summary_metrics,
131
163
  nested_frame,
132
- {:metric => true},
164
+ NESTED_TRACE_STOP_OPTIONS,
133
165
  end_time.to_f)
134
166
  end
135
167
 
136
168
  :transaction_stopped
137
169
  end
138
170
 
139
- def self.in_transaction?
140
- !TransactionState.get.current_transaction.nil?
141
- end
142
-
143
171
  def self.nested_transaction_name(name)
144
- if name =~ /^Controller\//
145
- "#{SUBTRANSACTION_PREFIX}/#{name}"
172
+ if name.start_with?(CONTROLLER_PREFIX)
173
+ "#{SUBTRANSACTION_PREFIX}#{name}"
146
174
  else
147
175
  name
148
176
  end
149
177
  end
150
178
 
151
- def root?
152
- true
153
- end
154
-
155
- # This is the name of the model currently assigned to database
156
- # measurements, overriding the default.
157
- def self.database_metric_name
158
- current && current.database_metric_name
159
- end
160
-
161
- def self.referer
162
- current && current.referer
163
- end
164
-
165
- def self.agent
166
- NewRelic::Agent.instance
167
- end
168
-
169
- def self.freeze_name_and_execute_if_not_ignored
170
- self.current && self.current.freeze_name_and_execute_if_not_ignored { yield if block_given? }
171
- end
172
-
173
179
  @@java_classes_loaded = false
174
180
 
175
181
  if defined? JRuby
@@ -184,7 +190,7 @@ module NewRelic
184
190
 
185
191
  attr_reader :frame_stack
186
192
 
187
- def initialize(type, options)
193
+ def initialize(category, options)
188
194
  @frame_stack = []
189
195
 
190
196
  @default_name = Helper.correctly_encoded(options[:transaction_name])
@@ -192,7 +198,7 @@ module NewRelic
192
198
  @name_from_api = nil
193
199
  @frozen_name = nil
194
200
 
195
- @type = type
201
+ @category = category
196
202
  @start_time = Time.now
197
203
  @apdex_start = options[:apdex_start_time] || @start_time
198
204
  @jruby_cpu_start = jruby_cpu_time
@@ -201,10 +207,12 @@ module NewRelic
201
207
  @filtered_params = options[:filtered_params] || {}
202
208
  @request = options[:request]
203
209
  @exceptions = {}
204
- @stats_hash = StatsHash.new
210
+ @metrics = TransactionMetrics.new
205
211
  @guid = generate_guid
212
+
206
213
  @ignore_this_transaction = false
207
- TransactionState.get.most_recent_transaction = self
214
+ @ignore_apdex = false
215
+ @ignore_enduser = false
208
216
  end
209
217
 
210
218
  def noticed_error_ids
@@ -227,6 +235,24 @@ module NewRelic
227
235
  @name_from_api = Helper.correctly_encoded(name)
228
236
  end
229
237
 
238
+ def set_default_transaction_name(name, options)
239
+ self.default_name = name
240
+ @category = options[:category] if options[:category]
241
+ end
242
+
243
+ def set_overriding_transaction_name(name, options)
244
+ self.name_from_api = name
245
+ set_default_transaction_name(name, options)
246
+ end
247
+
248
+ def best_category
249
+ if frame_stack.empty?
250
+ category
251
+ else
252
+ frame_stack.last.category
253
+ end
254
+ end
255
+
230
256
  def best_name
231
257
  return @frozen_name if @frozen_name
232
258
  return @name_from_api if @name_from_api
@@ -246,13 +272,22 @@ module NewRelic
246
272
  (@name_from_api || @name_from_child || @default_name) ? true : false
247
273
  end
248
274
 
275
+ def promoted_transaction_name(name)
276
+ if name.start_with?(MIDDLEWARE_PREFIX)
277
+ "#{CONTROLLER_PREFIX}#{name}"
278
+ else
279
+ name
280
+ end
281
+ end
282
+
249
283
  def freeze_name_and_execute_if_not_ignored
250
284
  if !name_frozen?
251
- name = NewRelic::Agent.instance.transaction_rules.rename(best_name)
285
+ name = promoted_transaction_name(best_name)
286
+ name = NewRelic::Agent.instance.transaction_rules.rename(name)
252
287
  @name_frozen = true
253
288
 
254
289
  if name.nil?
255
- @ignore_this_transaction = true
290
+ ignore!
256
291
  @frozen_name = best_name
257
292
  else
258
293
  @frozen_name = name
@@ -274,26 +309,24 @@ module NewRelic
274
309
 
275
310
  # Indicate that we are entering a measured controller action or task.
276
311
  # Make sure you unwind every push with a pop call.
277
- def start()
278
- return if !NewRelic::Agent.is_execution_traced?
312
+ def start(state)
313
+ return if !state.is_execution_traced?
279
314
 
280
- transaction_sampler.on_start_transaction(start_time, uri, filtered_params)
281
- sql_sampler.on_start_transaction(start_time, uri, filtered_params)
315
+ transaction_sampler.on_start_transaction(state, start_time, uri)
316
+ sql_sampler.on_start_transaction(state, start_time, uri)
282
317
  NewRelic::Agent.instance.events.notify(:start_transaction)
283
318
  NewRelic::Agent::BusyCalculator.dispatcher_start(start_time)
284
319
 
285
- @trace_options = {
286
- :metric => true,
287
- :scoped_metric => false,
288
- :deduct_call_time_from_parent => true
289
- }
290
- _, @expected_scope = NewRelic::Agent::MethodTracer::TraceExecutionScoped.trace_execution_scoped_header(@trace_options, start_time.to_f)
320
+ @trace_options = { :metric => true, :scoped_metric => false }
321
+ @expected_scope = NewRelic::Agent::MethodTracer::TraceExecutionScoped.trace_execution_scoped_header(state, start_time.to_f)
291
322
  end
292
323
 
293
324
  # Indicate that you don't want to keep the currently saved transaction
294
325
  # information
295
- def self.abort_transaction!
296
- current.abort_transaction! if current
326
+ def self.abort_transaction! #THREAD_LOCAL_ACCESS
327
+ state = NewRelic::Agent::TransactionState.tl_get
328
+ txn = state.current_transaction
329
+ txn.abort_transaction!(state) if txn
297
330
  end
298
331
 
299
332
  # For the current web transaction, return the path of the URI minus the host part and query string, or nil.
@@ -307,8 +340,8 @@ module NewRelic
307
340
  end
308
341
 
309
342
  # Call this to ensure that the current transaction is not saved
310
- def abort_transaction!
311
- transaction_sampler.ignore_transaction
343
+ def abort_transaction!(state)
344
+ transaction_sampler.ignore_transaction(state)
312
345
  end
313
346
 
314
347
  def summary_metrics
@@ -316,91 +349,102 @@ module NewRelic
316
349
  metric_parser.summary_metrics
317
350
  end
318
351
 
319
- def stop(end_time, opts)
320
- return if !NewRelic::Agent.is_execution_traced?
321
- freeze_name_and_execute_if_not_ignored
352
+ def needs_middleware_summary_metrics?(name)
353
+ name.start_with?(MIDDLEWARE_PREFIX)
354
+ end
322
355
 
323
- name = @frozen_name
324
- metrics = summary_metrics
356
+ def stop(state, end_time)
357
+ return if !state.is_execution_traced?
358
+ freeze_name_and_execute_if_not_ignored
325
359
 
326
360
  if @name_from_child
327
361
  name = Transaction.nested_transaction_name(@default_name)
328
- metrics << @frozen_name
329
362
  @trace_options[:scoped_metric] = true
363
+ else
364
+ name = @frozen_name
365
+ end
366
+
367
+ # These metrics are recorded here instead of in record_summary_metrics
368
+ # in order to capture the exclusive time associated with the outer-most
369
+ # TT node.
370
+ if needs_middleware_summary_metrics?(name)
371
+ summary_metrics_with_exclusive_time = MIDDLEWARE_SUMMARY_METRICS
372
+ else
373
+ summary_metrics_with_exclusive_time = EMPTY_SUMMARY_METRICS
330
374
  end
331
375
 
332
376
  NewRelic::Agent::MethodTracer::TraceExecutionScoped.trace_execution_scoped_footer(
377
+ state,
333
378
  start_time.to_f,
334
379
  name,
335
- metrics,
380
+ summary_metrics_with_exclusive_time,
336
381
  @expected_scope,
337
382
  @trace_options,
338
383
  end_time.to_f)
339
384
 
340
- log_underflow if @type.nil?
341
385
  NewRelic::Agent::BusyCalculator.dispatcher_finish(end_time)
342
386
 
343
- unless @ignore_this_transaction
344
- # these record metrics so need to be done before merging stats
387
+ commit!(state, end_time, name) unless @ignore_this_transaction
388
+ end
345
389
 
346
- # this one records metrics and wants to happen
347
- # before the transaction sampler is finished
348
- record_transaction_cpu
349
- gc_stop_snapshot = NewRelic::Agent::StatsEngine::GCProfiler.take_snapshot
350
- gc_delta = NewRelic::Agent::StatsEngine::GCProfiler.record_delta(
351
- gc_start_snapshot, gc_stop_snapshot)
352
- @transaction_trace = transaction_sampler.on_finishing_transaction(self, Time.now, gc_delta)
353
- sql_sampler.on_finishing_transaction(@frozen_name)
390
+ def commit!(state, end_time, outermost_segment_name)
391
+ record_transaction_cpu(state)
392
+ gc_stop_snapshot = NewRelic::Agent::StatsEngine::GCProfiler.take_snapshot
393
+ gc_delta = NewRelic::Agent::StatsEngine::GCProfiler.record_delta(
394
+ gc_start_snapshot, gc_stop_snapshot)
395
+ @transaction_trace = transaction_sampler.on_finishing_transaction(state, self, end_time, gc_delta)
396
+ sql_sampler.on_finishing_transaction(state, @frozen_name)
354
397
 
355
- record_apdex(end_time, opts[:exception_encountered]) unless opts[:ignore_apdex]
356
- NewRelic::Agent::Instrumentation::QueueTime.record_frontend_metrics(apdex_start, start_time) if queue_time > 0.0
357
- NewRelic::Agent::TransactionState.get.request_ignore_enduser = true if opts[:ignore_enduser]
398
+ record_summary_metrics(outermost_segment_name, end_time)
399
+ record_apdex(state, end_time) unless ignore_apdex?
400
+ NewRelic::Agent::Instrumentation::QueueTime.record_frontend_metrics(apdex_start, start_time) if queue_time > 0.0
358
401
 
359
- record_exceptions
360
- merge_stats_hash
402
+ record_exceptions
403
+ merge_metrics
361
404
 
362
- send_transaction_finished_event(start_time, end_time)
363
- end
405
+ send_transaction_finished_event(state, start_time, end_time)
406
+ end
407
+
408
+ # The summary metrics recorded by this method all end up with a duration
409
+ # equal to the transaction itself, and an exclusive time of zero.
410
+ def record_summary_metrics(outermost_segment_name, end_time)
411
+ metrics = summary_metrics
412
+ metrics << @frozen_name unless @frozen_name == outermost_segment_name
413
+ @metrics.record_unscoped(metrics, end_time - start_time, 0)
364
414
  end
365
415
 
366
416
  # This event is fired when the transaction is fully completed. The metric
367
417
  # values and sampler can't be successfully modified from this event.
368
- def send_transaction_finished_event(start_time, end_time)
418
+ def send_transaction_finished_event(state, start_time, end_time)
369
419
  payload = {
370
420
  :name => @frozen_name,
371
- :type => @type,
372
421
  :start_timestamp => start_time.to_f,
373
422
  :duration => end_time.to_f - start_time.to_f,
374
- :metrics => @stats_hash,
423
+ :metrics => @metrics,
375
424
  :custom_params => custom_parameters
376
425
  }
377
- append_guid_to(payload)
378
- append_referring_transaction_guid_to(payload)
426
+ append_guid_to(state, payload)
427
+ append_referring_transaction_guid_to(state, payload)
379
428
 
380
429
  agent.events.notify(:transaction_finished, payload)
381
430
  end
382
431
 
383
- def append_guid_to(payload)
384
- guid = NewRelic::Agent::TransactionState.get.request_guid_for_event
432
+ def append_guid_to(state, payload)
433
+ guid = state.request_guid_for_event
385
434
  if guid
386
435
  payload[:guid] = guid
387
436
  end
388
437
  end
389
438
 
390
- def append_referring_transaction_guid_to(payload)
391
- referring_guid = NewRelic::Agent.instance.cross_app_monitor.client_referring_transaction_guid
439
+ def append_referring_transaction_guid_to(state, payload)
440
+ referring_guid = NewRelic::Agent.instance.cross_app_monitor.client_referring_transaction_guid(state)
392
441
  if referring_guid
393
442
  payload[:referring_transaction_guid] = referring_guid
394
443
  end
395
444
  end
396
445
 
397
- def log_underflow
398
- NewRelic::Agent.logger.error "Underflow in transaction: #{caller.join("\n ")}"
399
- end
400
-
401
- def merge_stats_hash
402
- stats_hash.resolve_scopes!(best_name)
403
- NewRelic::Agent.instance.stats_engine.merge!(stats_hash)
446
+ def merge_metrics
447
+ NewRelic::Agent.instance.stats_engine.merge_transaction_metrics!(@metrics, best_name)
404
448
  end
405
449
 
406
450
  def record_exceptions
@@ -420,13 +464,14 @@ module NewRelic
420
464
  # * <tt>:custom_params</tt> => Custom parameters
421
465
  # Anything left over is treated as custom params
422
466
 
423
- def self.notice_error(e, options={})
467
+ def self.notice_error(e, options={}) #THREAD_LOCAL_ACCESS
424
468
  options = extract_request_options(options)
425
- if current
426
- current.notice_error(e, options)
469
+ state = NewRelic::Agent::TransactionState.tl_get
470
+ txn = state.current_transaction
471
+ if txn
472
+ txn.notice_error(e, options)
427
473
  else
428
- options = extract_finished_transaction_options(options)
429
- agent.error_collector.notice_error(e, options)
474
+ NewRelic::Agent.instance.error_collector.notice_error(e, options)
430
475
  end
431
476
  end
432
477
 
@@ -439,61 +484,41 @@ module NewRelic
439
484
  options
440
485
  end
441
486
 
442
- # If we aren't currently in a transaction, but found the remains of one
443
- # just finished in the TransactionState, use those custom params!
444
- def self.extract_finished_transaction_options(options)
445
- finished_txn = NewRelic::Agent::TransactionState.get.most_recent_transaction
446
- if finished_txn
447
- custom_params = options.fetch(:custom_params, {})
448
- custom_params.merge!(finished_txn.custom_parameters)
449
- options = options.merge(:custom_params => custom_params)
450
- options[:metric] = finished_txn.best_name
451
- end
452
- options
453
- end
454
487
 
455
488
  # Do not call this. Invoke the class method instead.
456
- def notice_error(e, options={}) # :nodoc:
489
+ def notice_error(error, options={}) # :nodoc:
457
490
  options[:referer] = referer if referer
458
- options[:request_params] = filtered_params if filtered_params
459
- options[:uri] = uri if uri
460
- options.merge!(custom_parameters)
461
- if !@exceptions.keys.include?(e)
462
- @exceptions[e] = options
463
- end
464
- end
465
491
 
466
- # Add context parameters to the transaction. This information will be passed in to errors
467
- # and transaction traces. Keys and Values should be strings, numbers or date/times.
468
- def self.add_custom_parameters(p)
469
- current.add_custom_parameters(p) if current
470
- end
492
+ if filtered_params && !filtered_params.empty?
493
+ options[:request_params] = filtered_params
494
+ end
471
495
 
472
- def self.custom_parameters
473
- (current && current.custom_parameters) ? current.custom_parameters : {}
474
- end
496
+ options[:uri] = uri if uri
497
+ options.merge!(custom_parameters)
475
498
 
476
- class << self
477
- alias_method :user_attributes, :custom_parameters
478
- alias_method :set_user_attributes, :add_custom_parameters
499
+ if @exceptions[error]
500
+ @exceptions[error].merge! options
501
+ else
502
+ @exceptions[error] = options
503
+ end
479
504
  end
480
505
 
481
- APDEX_METRIC_SPEC = NewRelic::MetricSpec.new('Apdex').freeze
506
+ APDEX_METRIC = 'Apdex'.freeze
482
507
 
483
- def record_apdex(end_time=Time.now, is_error=nil)
484
- return unless recording_web_transaction? && NewRelic::Agent.is_execution_traced?
508
+ def record_apdex(state, end_time=Time.now)
509
+ return unless recording_web_transaction? && state.is_execution_traced?
485
510
 
486
511
  freeze_name_and_execute_if_not_ignored do
487
512
  action_duration = end_time - start_time
488
513
  total_duration = end_time - apdex_start
489
- is_error = is_error.nil? ? !exceptions.empty? : is_error
514
+ is_error = !notable_exceptions.empty?
490
515
 
491
516
  apdex_bucket_global = self.class.apdex_bucket(total_duration, is_error, apdex_t)
492
517
  apdex_bucket_txn = self.class.apdex_bucket(action_duration, is_error, apdex_t)
493
518
 
494
- @stats_hash.record(APDEX_METRIC_SPEC, apdex_bucket_global, apdex_t)
495
- txn_apdex_metric = NewRelic::MetricSpec.new(@frozen_name.gsub(/^[^\/]+\//, 'Apdex/'))
496
- @stats_hash.record(txn_apdex_metric, apdex_bucket_txn, apdex_t)
519
+ @metrics.record_unscoped(APDEX_METRIC, apdex_bucket_global, apdex_t)
520
+ txn_apdex_metric = @frozen_name.gsub(/^[^\/]+\//, 'Apdex/')
521
+ @metrics.record_unscoped(txn_apdex_metric, apdex_bucket_txn, apdex_t)
497
522
  end
498
523
  end
499
524
 
@@ -549,16 +574,17 @@ module NewRelic
549
574
  #
550
575
  # @api public
551
576
  #
552
- def self.recording_web_transaction?
553
- self.current && self.current.recording_web_transaction?
577
+ def self.recording_web_transaction? #THREAD_LOCAL_ACCESS
578
+ txn = tl_current
579
+ txn && txn.recording_web_transaction?
554
580
  end
555
581
 
556
- def self.transaction_type_is_web?(type)
557
- [:controller, :uri, :rack, :sinatra].include?(type)
582
+ def self.transaction_category_is_web?(category)
583
+ WEB_TRANSACTION_CATEGORIES.include?(category)
558
584
  end
559
585
 
560
586
  def recording_web_transaction?
561
- self.class.transaction_type_is_web?(@type)
587
+ self.class.transaction_category_is_web?(@category)
562
588
  end
563
589
 
564
590
  # Make a safe attempt to get the referer from a request object, generally successful when
@@ -582,11 +608,6 @@ module NewRelic
582
608
  end
583
609
 
584
610
 
585
-
586
- def self.record_apdex(end_time, is_error)
587
- current && current.record_apdex(end_time, is_error)
588
- end
589
-
590
611
  def self.apdex_bucket(duration, failed, apdex_t)
591
612
  case
592
613
  when failed
@@ -614,9 +635,41 @@ module NewRelic
614
635
  jruby_cpu_time - @jruby_cpu_start
615
636
  end
616
637
 
617
- def record_transaction_cpu
638
+ def record_transaction_cpu(state)
618
639
  burn = cpu_burn
619
- transaction_sampler.notice_transaction_cpu_time(burn) if burn
640
+ if burn
641
+ transaction_sampler.notice_transaction_cpu_time(state, burn)
642
+ end
643
+ end
644
+
645
+ def ignore!
646
+ @ignore_this_transaction = true
647
+ end
648
+
649
+ def ignore?
650
+ @ignore_this_transaction
651
+ end
652
+
653
+ def ignore_apdex!
654
+ @ignore_apdex = true
655
+ end
656
+
657
+ def ignore_apdex?
658
+ @ignore_apdex
659
+ end
660
+
661
+ def ignore_enduser!
662
+ @ignore_enduser = true
663
+ end
664
+
665
+ def ignore_enduser?
666
+ @ignore_enduser
667
+ end
668
+
669
+ def notable_exceptions
670
+ @exceptions.keys.select do |exception|
671
+ !NewRelic::Agent.instance.error_collector.error_is_ignored?(exception)
672
+ end
620
673
  end
621
674
 
622
675
  private