newrelic_rpm 3.8.0.218 → 3.8.1.221

Sign up to get free protection for your applications and to get access to all the features.
Files changed (125) hide show
  1. data.tar.gz.sig +0 -0
  2. data/CHANGELOG +32 -0
  3. data/README.md +4 -7
  4. data/Rakefile +3 -0
  5. data/lib/new_relic/agent.rb +3 -7
  6. data/lib/new_relic/agent/agent.rb +4 -14
  7. data/lib/new_relic/agent/agent_logger.rb +19 -11
  8. data/lib/new_relic/agent/autostart.rb +1 -1
  9. data/lib/new_relic/agent/configuration/default_source.rb +25 -12
  10. data/lib/new_relic/agent/configuration/manager.rb +14 -7
  11. data/lib/new_relic/agent/configuration/yaml_source.rb +39 -8
  12. data/lib/new_relic/agent/cross_app_monitor.rb +9 -7
  13. data/lib/new_relic/agent/cross_app_tracing.rb +6 -6
  14. data/lib/new_relic/agent/datastores/mongo.rb +6 -7
  15. data/lib/new_relic/agent/datastores/mongo/metric_translator.rb +32 -13
  16. data/lib/new_relic/agent/datastores/mongo/statement_formatter.rb +4 -3
  17. data/lib/new_relic/agent/error_collector.rb +2 -2
  18. data/lib/new_relic/agent/instrumentation/action_controller_subscriber.rb +10 -69
  19. data/lib/new_relic/agent/instrumentation/action_view_subscriber.rb +5 -7
  20. data/lib/new_relic/agent/instrumentation/active_record_subscriber.rb +2 -2
  21. data/lib/new_relic/agent/instrumentation/controller_instrumentation.rb +77 -93
  22. data/lib/new_relic/agent/instrumentation/evented_subscriber.rb +1 -1
  23. data/lib/new_relic/agent/instrumentation/mongo.rb +26 -42
  24. data/lib/new_relic/agent/instrumentation/rubyprof.rb +1 -1
  25. data/lib/new_relic/agent/instrumentation/sinatra.rb +4 -1
  26. data/lib/new_relic/agent/javascript_instrumentor.rb +15 -6
  27. data/lib/new_relic/agent/method_tracer.rb +41 -92
  28. data/lib/new_relic/agent/request_sampler.rb +0 -1
  29. data/lib/new_relic/agent/rules_engine.rb +36 -12
  30. data/lib/new_relic/agent/shim_agent.rb +0 -1
  31. data/lib/new_relic/agent/sql_sampler.rb +8 -15
  32. data/lib/new_relic/agent/stats_engine.rb +2 -6
  33. data/lib/new_relic/agent/stats_engine/metric_stats.rb +8 -2
  34. data/lib/new_relic/agent/stats_engine/stats_hash.rb +1 -1
  35. data/lib/new_relic/agent/supported_versions.rb +1 -1
  36. data/lib/new_relic/agent/traced_method_stack.rb +87 -0
  37. data/lib/new_relic/agent/transaction.rb +277 -107
  38. data/lib/new_relic/agent/transaction_sample_builder.rb +2 -2
  39. data/lib/new_relic/agent/transaction_sampler.rb +18 -27
  40. data/lib/new_relic/agent/transaction_state.rb +15 -40
  41. data/lib/new_relic/control/instance_methods.rb +8 -4
  42. data/lib/new_relic/recipes.rb +3 -3
  43. data/lib/new_relic/transaction_sample.rb +3 -7
  44. data/lib/new_relic/version.rb +1 -1
  45. data/lib/sequel/extensions/newrelic_instrumentation.rb +3 -3
  46. data/newrelic_rpm.gemspec +15 -9
  47. data/test/agent_helper.rb +71 -36
  48. data/test/environments/norails/Gemfile +2 -0
  49. data/test/environments/rails21/Gemfile +2 -0
  50. data/test/environments/rails22/Gemfile +2 -0
  51. data/test/environments/rails23/Gemfile +2 -0
  52. data/test/environments/rails30/Gemfile +2 -0
  53. data/test/environments/rails31/Gemfile +2 -0
  54. data/test/environments/rails32/Gemfile +2 -0
  55. data/test/environments/rails40/Gemfile +2 -0
  56. data/test/environments/rails41/Gemfile +1 -0
  57. data/test/helpers/mongo_metric_builder.rb +1 -1
  58. data/test/multiverse/suites/agent_only/audit_log_test.rb +2 -2
  59. data/test/multiverse/suites/agent_only/cross_application_tracing_test.rb +9 -1
  60. data/test/multiverse/suites/agent_only/encoding_handling_test.rb +1 -1
  61. data/test/multiverse/suites/agent_only/logging_test.rb +2 -2
  62. data/test/multiverse/suites/agent_only/marshaling_test.rb +8 -9
  63. data/test/multiverse/suites/agent_only/rum_instrumentation_test.rb +14 -1
  64. data/test/multiverse/suites/agent_only/set_transaction_name_test.rb +30 -13
  65. data/test/multiverse/suites/agent_only/thread_profiling_test.rb +9 -8
  66. data/test/multiverse/suites/agent_only/transaction_ignoring_test.rb +43 -0
  67. data/test/multiverse/suites/config_file_loading/config_file_loading_test.rb +77 -5
  68. data/test/multiverse/suites/excon/excon_test.rb +1 -1
  69. data/test/multiverse/suites/mongo/Envfile +4 -7
  70. data/test/multiverse/suites/mongo/helpers/mongo_operation_tests.rb +55 -16
  71. data/test/multiverse/suites/mongo/helpers/mongo_server.rb +6 -4
  72. data/test/multiverse/suites/rails/bad_instrumentation_test.rb +2 -2
  73. data/test/multiverse/suites/rails/error_tracing_test.rb +3 -1
  74. data/test/multiverse/suites/rails/request_statistics_test.rb +14 -14
  75. data/test/multiverse/suites/rails/transaction_ignoring_test.rb +45 -0
  76. data/test/multiverse/suites/sequel/sequel_instrumentation_test.rb +1 -1
  77. data/test/new_relic/agent/agent/connect_test.rb +4 -4
  78. data/test/new_relic/agent/agent_logger_test.rb +32 -0
  79. data/test/new_relic/agent/configuration/manager_test.rb +12 -4
  80. data/test/new_relic/agent/configuration/yaml_source_test.rb +2 -2
  81. data/test/new_relic/agent/cross_app_monitor_test.rb +6 -2
  82. data/test/new_relic/agent/cross_app_tracing_test.rb +5 -5
  83. data/test/new_relic/agent/datastores/mongo/statement_formatter_test.rb +7 -6
  84. data/test/new_relic/agent/error_collector_test.rb +1 -2
  85. data/test/new_relic/agent/instrumentation/action_controller_subscriber_test.rb +24 -11
  86. data/test/new_relic/agent/instrumentation/action_view_subscriber_test.rb +2 -2
  87. data/test/new_relic/agent/instrumentation/active_record_subscriber_test.rb +12 -17
  88. data/test/new_relic/agent/instrumentation/controller_instrumentation_test.rb +43 -32
  89. data/test/new_relic/agent/instrumentation/net_instrumentation_test.rb +3 -4
  90. data/test/new_relic/agent/instrumentation/task_instrumentation_test.rb +36 -20
  91. data/test/new_relic/agent/javascript_instrumentor_test.rb +1 -2
  92. data/test/new_relic/agent/method_tracer/class_methods/add_method_tracer_test.rb +15 -26
  93. data/test/new_relic/agent/method_tracer/instance_methods/trace_execution_scoped_test.rb +66 -103
  94. data/test/new_relic/agent/method_tracer_test.rb +2 -2
  95. data/test/new_relic/agent/mock_scope_listener.rb +3 -6
  96. data/test/new_relic/agent/pipe_channel_manager_test.rb +4 -4
  97. data/test/new_relic/agent/rules_engine_test.rb +13 -0
  98. data/test/new_relic/agent/sql_sampler_test.rb +8 -10
  99. data/test/new_relic/agent/stats_engine/metric_stats_test.rb +18 -0
  100. data/test/new_relic/agent/stats_engine_test.rb +0 -173
  101. data/test/new_relic/agent/threading/agent_thread_test.rb +27 -26
  102. data/test/new_relic/agent/traced_method_stack_test.rb +139 -0
  103. data/test/new_relic/agent/transaction_sample_builder_test.rb +2 -12
  104. data/test/new_relic/agent/transaction_sampler_test.rb +98 -107
  105. data/test/new_relic/agent/transaction_state_test.rb +52 -61
  106. data/test/new_relic/agent/transaction_test.rb +209 -140
  107. data/test/new_relic/agent_test.rb +3 -2
  108. data/test/new_relic/control_test.rb +10 -9
  109. data/test/new_relic/fake_collector.rb +34 -2
  110. data/test/new_relic/http_client_test_cases.rb +0 -5
  111. data/test/new_relic/license_test.rb +4 -2
  112. data/test/new_relic/local_environment_test.rb +14 -28
  113. data/test/new_relic/multiverse_helpers.rb +2 -2
  114. data/test/new_relic/rack/developer_mode_test.rb +4 -5
  115. data/test/new_relic/transaction_ignoring_test_cases.rb +104 -0
  116. data/test/new_relic/transaction_sample_test.rb +14 -7
  117. data/test/performance/lib/performance/instrumentation/gc_stats.rb +6 -3
  118. data/test/performance/suites/transaction_tracing.rb +4 -1
  119. data/test/test_helper.rb +31 -60
  120. data/ui/views/newrelic/show_sample.rhtml +1 -1
  121. metadata +46 -101
  122. metadata.gz.sig +0 -0
  123. data/lib/new_relic/agent/stats_engine/transactions.rb +0 -114
  124. data/lib/new_relic/agent/transaction/pop.rb +0 -52
  125. data/test/new_relic/agent/transaction/pop_test.rb +0 -79
@@ -10,13 +10,7 @@ module NewRelic
10
10
  # No version constant in < 2.0 versions of Mongo :(
11
11
  defined?(::Mongo) &&
12
12
  defined?(::Mongo::MongoClient) &&
13
- !is_version2? &&
14
- !is_version_1_10_or_later?
15
- end
16
-
17
- def self.is_version_1_10_or_later?
18
- # Again, no VERSION constant in 1.x, so we have to rely on constant checks
19
- defined?(::Mongo::CollectionOperationWriter)
13
+ !is_version2?
20
14
  end
21
15
 
22
16
  # At present we explicitly don't support version 2.x of the driver yet
@@ -24,6 +18,11 @@ module NewRelic
24
18
  defined?(::Mongo::VERSION) &&
25
19
  NewRelic::VersionNumber.new(::Mongo::VERSION) > NewRelic::VersionNumber.new("2.0.0")
26
20
  end
21
+
22
+ def self.is_version_1_10_or_later?
23
+ # Again, no VERSION constant in 1.x, so we have to rely on constant checks
24
+ defined?(::Mongo::CollectionOperationWriter)
25
+ end
27
26
  end
28
27
  end
29
28
  end
@@ -12,21 +12,27 @@ module NewRelic
12
12
  def self.metrics_for(name, payload, request_type = :web)
13
13
  payload ||= {}
14
14
 
15
- database = payload[:database]
16
- collection = payload[:collection]
17
-
18
- if collection_in_selector?(collection, payload)
15
+ if collection_in_selector?(payload)
19
16
  command_key = command_key_from_selector(payload)
20
17
  name = get_name_from_selector(command_key, payload)
21
18
  collection = get_collection_from_selector(command_key, payload)
19
+ else
20
+ collection = payload[:collection]
22
21
  end
23
22
 
23
+ # The 1.10.0 version of the mongo driver renamed 'remove' to
24
+ # 'delete', but for metric consistency with previous versions we
25
+ # want to keep it as 'remove'.
26
+ name = 'remove' if name.to_s == 'delete'
27
+
24
28
  if self.find_one?(name, payload)
25
29
  name = 'findOne'
26
30
  elsif self.find_and_remove?(name, payload)
27
31
  name = 'findAndRemove'
28
32
  elsif self.find_and_modify?(name, payload)
29
33
  name = 'findAndModify'
34
+ elsif self.create_indexes?(name, payload)
35
+ name = 'createIndexes'
30
36
  elsif self.create_index?(name, payload)
31
37
  name = 'createIndex'
32
38
  collection = self.collection_name_from_index(payload)
@@ -44,9 +50,7 @@ module NewRelic
44
50
  collection = collection_name_from_rename_selector(payload)
45
51
  end
46
52
 
47
- metrics = build_metrics(name, collection, request_type)
48
-
49
- metrics
53
+ build_metrics(name, collection, request_type)
50
54
  end
51
55
 
52
56
  def self.build_metrics(name, collection, request_type = :web)
@@ -70,8 +74,8 @@ module NewRelic
70
74
  "Datastore/instance/MongoDB/#{host}:#{port}/#{database}"
71
75
  end
72
76
 
73
- def self.collection_in_selector?(collection, payload)
74
- collection == '$cmd' && payload[:selector]
77
+ def self.collection_in_selector?(payload)
78
+ payload[:collection] == '$cmd' && payload[:selector]
75
79
  end
76
80
 
77
81
  NAMES_IN_SELECTOR = [
@@ -84,6 +88,7 @@ module NewRelic
84
88
 
85
89
  :distinct,
86
90
 
91
+ :createIndexes,
87
92
  :deleteIndexes,
88
93
  :reIndex,
89
94
 
@@ -131,6 +136,10 @@ module NewRelic
131
136
  name == :findandmodify && payload[:selector] && payload[:selector][:remove]
132
137
  end
133
138
 
139
+ def self.create_indexes?(name, paylod)
140
+ name == :createIndexes
141
+ end
142
+
134
143
  def self.create_index?(name, payload)
135
144
  name == :insert && payload[:collection] == "system.indexes"
136
145
  end
@@ -156,11 +165,21 @@ module NewRelic
156
165
  end
157
166
 
158
167
  def self.collection_name_from_index(payload)
159
- if payload[:documents] && payload[:documents].first[:ns]
160
- payload[:documents].first[:ns].split('.').last
161
- else
162
- 'system.indexes'
168
+ if payload[:documents]
169
+ if payload[:documents].is_a?(Array)
170
+ # mongo gem versions pre 1.10.0
171
+ document = payload[:documents].first
172
+ else
173
+ # mongo gem versions 1.10.0 and later
174
+ document = payload[:documents]
175
+ end
176
+
177
+ if document && document[:ns]
178
+ return document[:ns].split('.').last
179
+ end
163
180
  end
181
+
182
+ 'system.indexes'
164
183
  end
165
184
 
166
185
  def self.collection_name_from_group_selector(payload)
@@ -24,16 +24,17 @@ module NewRelic
24
24
  :selector
25
25
  ]
26
26
 
27
- def self.format(statement)
27
+ def self.format(statement, operation)
28
28
  return nil unless NewRelic::Agent.config[:'mongo.capture_queries']
29
29
 
30
- result = {}
30
+ result = { :operation => operation }
31
+
31
32
  PLAINTEXT_KEYS.each do |key|
32
33
  result[key] = statement[key] if statement.key?(key)
33
34
  end
34
35
 
35
36
  OBFUSCATE_KEYS.each do |key|
36
- if statement.key?(key)
37
+ if statement.key?(key) && statement[key]
37
38
  obfuscated = obfuscate(statement[key])
38
39
  result[key] = obfuscated if obfuscated
39
40
  end
@@ -110,7 +110,7 @@ module NewRelic
110
110
  if options[:metric] && options[:metric] != ::NewRelic::Agent::UNKNOWN_METRIC
111
111
  "Errors/#{options[:metric]}"
112
112
  else
113
- if txn = TransactionState.get.transaction
113
+ if txn = TransactionState.get.most_recent_transaction
114
114
  "Errors/#{txn.name}"
115
115
  end
116
116
  end
@@ -265,7 +265,7 @@ module NewRelic
265
265
  return if should_exit_notice_error?(exception)
266
266
  increment_error_count!(exception, options)
267
267
  NewRelic::Agent.instance.events.notify(:notice_error, exception, options)
268
- action_path = fetch_from_options(options, :metric, "")
268
+ action_path = fetch_from_options(options, :metric, "")
269
269
  exception_options = error_params_from_options(options).merge(exception_info(exception))
270
270
  add_to_error_queue(NewRelic::NoticedError.new(action_path, exception_options, exception))
271
271
  exception
@@ -42,14 +42,7 @@ module NewRelic
42
42
  event = pop_event(id)
43
43
  event.payload.merge!(payload)
44
44
 
45
- set_enduser_ignore if event.enduser_ignored?
46
-
47
45
  if NewRelic::Agent.is_execution_traced? && !event.ignored?
48
- event.finalize_metric_name!
49
- record_queue_time(event)
50
- record_metrics(event)
51
- record_apdex(event)
52
- record_instance_busy(event)
53
46
  stop_transaction(event)
54
47
  else
55
48
  Agent.instance.pop_trace_execution_flag
@@ -58,59 +51,19 @@ module NewRelic
58
51
  log_notification_error(e, name, 'finish')
59
52
  end
60
53
 
61
- def set_enduser_ignore
62
- TransactionState.get.request_ignore_enduser = true
63
- end
64
-
65
- def record_metrics(event)
66
- controller_metric = MetricSpec.new(event.metric_name)
67
- txn = Transaction.current
68
- metrics = [ 'HttpDispatcher']
69
- if txn.has_parent?
70
- parent_metric = MetricSpec.new(event.metric_name, StatsEngine::MetricStats::SCOPE_PLACEHOLDER)
71
- record_metric_on_parent_transaction(parent_metric, event.duration)
72
- end
73
- metrics << controller_metric.dup
74
-
75
- Agent.instance.stats_engine.record_metrics(metrics, event.duration)
76
- end
77
-
78
- def record_metric_on_parent_transaction(metric, time)
79
- NewRelic::Agent::Transaction.parent.stats_hash.record(metric, time)
80
- end
81
-
82
- def record_apdex(event)
83
- return if event.apdex_ignored?
84
- Transaction.record_apdex(event.end, event.exception_encountered?)
85
- end
86
-
87
- def record_instance_busy(event)
88
- BusyCalculator.dispatcher_start(event.time)
89
- BusyCalculator.dispatcher_finish(event.end)
90
- end
91
-
92
- def record_queue_time(event)
93
- return unless event.queue_start
94
- return unless NewRelic::Agent::Transaction.current.root?
95
- QueueTime.record_frontend_metrics(event.queue_start, event.time)
96
- end
97
-
98
54
  def start_transaction(event)
99
- txn = Transaction.start(:controller,
100
- :request => event.request,
101
- :filtered_params => filter(event.payload[:params]))
102
- txn.apdex_start = (event.queue_start || event.time)
103
- txn.name = event.metric_name
104
-
105
- event.scope = Agent.instance.stats_engine \
106
- .push_scope(:action_controller, event.time)
55
+ Transaction.start(:controller,
56
+ :request => event.request,
57
+ :filtered_params => filter(event.payload[:params]),
58
+ :apdex_start_time => event.queue_start,
59
+ :transaction_name => event.metric_name)
107
60
  end
108
61
 
109
62
  def stop_transaction(event)
110
- Agent.instance.stats_engine \
111
- .pop_scope(event.scope, event.metric_name, event.end)
112
- ensure
113
- Transaction.stop
63
+ Transaction.stop(Time.now,
64
+ :exception_encountered => event.exception_encountered?,
65
+ :ignore_apdex => event.apdex_ignored?,
66
+ :ignore_enduser => event.enduser_ignored?)
114
67
  end
115
68
 
116
69
  def filter(params)
@@ -120,7 +73,7 @@ module NewRelic
120
73
  end
121
74
 
122
75
  class ControllerEvent < Event
123
- attr_accessor :parent, :scope
76
+ attr_accessor :parent
124
77
  attr_reader :queue_start, :request
125
78
 
126
79
  def initialize(name, start, ending, transaction_id, payload, request)
@@ -140,18 +93,6 @@ module NewRelic
140
93
  @metric_name || "Controller/#{metric_path}/#{metric_action}"
141
94
  end
142
95
 
143
- def finalize_metric_name!
144
- txn = NewRelic::Agent::Transaction.current
145
-
146
- # the event provides the default name but the transaction has the final say
147
- txn.name ||= metric_name
148
-
149
- # this applies the transaction name rules if not already applied
150
- txn.freeze_name
151
- @metric_name = txn.name
152
- return @metric_name
153
- end
154
-
155
96
  def metric_path
156
97
  @controller_class.controller_path
157
98
  end
@@ -14,8 +14,7 @@ module NewRelic
14
14
  push_event(event)
15
15
 
16
16
  if NewRelic::Agent.is_execution_traced? && event.recordable?
17
- event.scope = NewRelic::Agent.instance.stats_engine \
18
- .push_scope(:action_view, event.time)
17
+ event.frame = NewRelic::Agent::TracedMethodStack.push_frame(:action_view, event.time)
19
18
  end
20
19
  rescue => e
21
20
  log_notification_error(e, name, 'start')
@@ -25,16 +24,15 @@ module NewRelic
25
24
  event = pop_event(id)
26
25
 
27
26
  if NewRelic::Agent.is_execution_traced? && event.recordable?
28
- scope = NewRelic::Agent.instance.stats_engine \
29
- .pop_scope(event.scope, event.metric_name, event.end)
30
- record_metrics(event, scope)
27
+ frame = NewRelic::Agent::TracedMethodStack.pop_frame(event.frame, event.metric_name, event.end)
28
+ record_metrics(event, frame)
31
29
  end
32
30
  rescue => e
33
31
  log_notification_error(e, name, 'finish')
34
32
  end
35
33
 
36
- def record_metrics(event, scope)
37
- exclusive = event.duration - scope.children_time
34
+ def record_metrics(event, frame)
35
+ exclusive = event.duration - frame.children_time
38
36
  metric_specs = [
39
37
  NewRelic::MetricSpec.new(event.metric_name),
40
38
  NewRelic::MetricSpec.new(event.metric_name, StatsEngine::MetricStats::SCOPE_PLACEHOLDER)
@@ -42,7 +42,7 @@ module NewRelic
42
42
  metric = base_metric(event)
43
43
 
44
44
  # enter transaction trace segment
45
- scope = NewRelic::Agent.instance.stats_engine.push_scope(:active_record, event.time)
45
+ frame = NewRelic::Agent::TracedMethodStack.push_frame(:active_record, event.time)
46
46
 
47
47
  NewRelic::Agent.instance.transaction_sampler \
48
48
  .notice_sql(event.payload[:sql], config,
@@ -55,7 +55,7 @@ module NewRelic
55
55
  &method(:get_explain_plan))
56
56
 
57
57
  # exit transaction trace segment
58
- NewRelic::Agent.instance.stats_engine.pop_scope(scope, metric, event.end)
58
+ NewRelic::Agent::TracedMethodStack.pop_frame(frame, metric, event.end)
59
59
  end
60
60
 
61
61
  def record_metrics(event)
@@ -147,60 +147,67 @@ module NewRelic
147
147
  def add_transaction_tracer(method, options={})
148
148
  # The metric path:
149
149
  options[:name] ||= method.to_s
150
- # create the argument list:
151
- options_arg = []
152
- options.each do |key, value|
153
- valuestr = case
154
- when value.is_a?(Symbol)
155
- value.inspect
156
- when key == :params
157
- value.to_s
158
- else
159
- %Q["#{value.to_s}"]
160
- end
161
- options_arg << %Q[:#{key} => #{valuestr}]
162
- end
163
- traced_method, punctuation = method.to_s.sub(/([?!=])$/, ''), $1
164
- visibility = NewRelic::Helper.instance_method_visibility self, method
165
150
 
166
- without_method_name = "#{traced_method.to_s}_without_newrelic_transaction_trace#{punctuation}"
167
- with_method_name = "#{traced_method.to_s}_with_newrelic_transaction_trace#{punctuation}"
151
+ argument_list = generate_argument_list(options)
152
+ traced_method, punctuation = parse_punctuation(method)
153
+ with_method_name, without_method_name = build_method_names(traced_method, punctuation)
168
154
 
169
- if NewRelic::Helper.instance_methods_include?(self, with_method_name)
155
+ if already_added_transaction_tracer?(self, with_method_name)
170
156
  ::NewRelic::Agent.logger.warn("Transaction tracer already in place for class = #{self.name}, method = #{method.to_s}, skipping")
171
157
  return
172
158
  end
173
159
 
174
160
  class_eval <<-EOC
175
- def #{traced_method.to_s}_with_newrelic_transaction_trace#{punctuation}(*args, &block)
176
- perform_action_with_newrelic_trace(#{options_arg.join(',')}) do
177
- #{traced_method.to_s}_without_newrelic_transaction_trace#{punctuation}(*args, &block)
161
+ def #{with_method_name}(*args, &block)
162
+ perform_action_with_newrelic_trace(#{argument_list.join(',')}) do
163
+ #{without_method_name}(*args, &block)
178
164
  end
179
165
  end
180
166
  EOC
167
+
168
+ visibility = NewRelic::Helper.instance_method_visibility self, method
169
+
181
170
  alias_method without_method_name, method.to_s
182
171
  alias_method method.to_s, with_method_name
183
172
  send visibility, method
184
173
  send visibility, with_method_name
185
174
  ::NewRelic::Agent.logger.debug("Traced transaction: class = #{self.name}, method = #{method.to_s}, options = #{options.inspect}")
186
175
  end
187
- end
188
176
 
189
- class TransactionNamer
190
- def initialize(traced_obj)
191
- @traced_obj = traced_obj
192
- if (@traced_obj.is_a?(Class) || @traced_obj.is_a?(Module))
193
- @traced_class_name = @traced_obj.name
194
- else
195
- @traced_class_name = @traced_obj.class.name
177
+ def parse_punctuation(method)
178
+ [method.to_s.sub(/([?!=])$/, ''), $1]
179
+ end
180
+
181
+ def generate_argument_list(options)
182
+ options.map do |key, value|
183
+ value = if value.is_a?(Symbol)
184
+ value.inspect
185
+ elsif key == :params
186
+ value.to_s
187
+ else
188
+ %Q["#{value.to_s}"]
189
+ end
190
+
191
+ %Q[:#{key} => #{value}]
196
192
  end
197
193
  end
198
194
 
199
- def name(options={})
200
- name = "#{category_name(options[:category])}/#{path_name(options)}"
195
+ def build_method_names(traced_method, punctuation)
196
+ [ "#{traced_method.to_s}_with_newrelic_transaction_trace#{punctuation}",
197
+ "#{traced_method.to_s}_without_newrelic_transaction_trace#{punctuation}" ]
198
+ end
199
+
200
+ def already_added_transaction_tracer?(target, with_method_name)
201
+ if NewRelic::Helper.instance_methods_include?(target, with_method_name)
202
+ true
203
+ else
204
+ false
205
+ end
201
206
  end
207
+ end
202
208
 
203
- def category_name(type = nil)
209
+ class TransactionNamer
210
+ def self.category_name(type = nil)
204
211
  type ||= Transaction.current && Transaction.current.type
205
212
  case type
206
213
  when :controller, nil then 'Controller'
@@ -213,6 +220,19 @@ module NewRelic
213
220
  end
214
221
  end
215
222
 
223
+ def initialize(traced_obj)
224
+ @traced_obj = traced_obj
225
+ if (@traced_obj.is_a?(Class) || @traced_obj.is_a?(Module))
226
+ @traced_class_name = @traced_obj.name
227
+ else
228
+ @traced_class_name = @traced_obj.class.name
229
+ end
230
+ end
231
+
232
+ def name(options={})
233
+ name = "#{self.class.category_name(options[:category])}/#{path_name(options)}"
234
+ end
235
+
216
236
  def path_name(options={})
217
237
  # if we have the path, use the path
218
238
  path = options[:path]
@@ -294,8 +314,6 @@ module NewRelic
294
314
  #
295
315
  # Seldomly used options:
296
316
  #
297
- # * <tt>:force => true</tt> indicates you should capture all
298
- # metrics even if the #newrelic_ignore directive was specified
299
317
  # * <tt>:class_name => aClass.name</tt> is used to override the name
300
318
  # of the class when used inside the metric name. Default is the
301
319
  # current class.
@@ -322,16 +340,19 @@ module NewRelic
322
340
  end
323
341
  end
324
342
 
325
- txn = _start_transaction(block_given? ? args : [])
343
+ # If a block was passed in, then the arguments represent options for
344
+ # the instrumentation, not app method arguments.
345
+ txn_options = create_transaction_options(block_given? ? args : [])
346
+ return yield unless NewRelic::Agent.is_execution_traced?
347
+
348
+ txn_options[:transaction_name] = TransactionNamer.new(self).name(txn_options)
349
+ txn_options[:apdex_start_time] = detect_queue_start_time
350
+
326
351
  begin
327
- options = { :force => txn.force_flag, :transaction => true }
328
- return yield if !(NewRelic::Agent.is_execution_traced? || options[:force])
329
- options[:metric] = true if options[:metric].nil?
330
- options[:deduct_call_time_from_parent] = true if options[:deduct_call_time_from_parent].nil?
331
- _, expected_scope = NewRelic::Agent::MethodTracer::TraceExecutionScoped.trace_execution_scoped_header(options, txn.start_time.to_f)
352
+ txn = Transaction.start(txn_options[:category], txn_options)
353
+ _record_queue_length
332
354
 
333
355
  begin
334
- NewRelic::Agent::BusyCalculator.dispatcher_start txn.start_time
335
356
  if block_given?
336
357
  yield
337
358
  else
@@ -343,28 +364,12 @@ module NewRelic
343
364
  end
344
365
 
345
366
  ensure
346
- end_time = Time.now
347
-
348
- txn.freeze_name
349
- metric_names = Array(recorded_metrics(txn))
350
- txn_name = metric_names.shift
351
-
352
- NewRelic::Agent::MethodTracer::TraceExecutionScoped.trace_execution_scoped_footer(txn.start_time.to_f, txn_name, metric_names, expected_scope, options, end_time.to_f)
353
- NewRelic::Agent::BusyCalculator.dispatcher_finish(end_time)
354
- txn.record_apdex(end_time) unless ignore_apdex?
355
- txn = Transaction.stop(txn_name, end_time)
356
-
357
- NewRelic::Agent::TransactionState.get.request_ignore_enduser = true if ignore_enduser?
367
+ Transaction.stop(Time.now,
368
+ :ignore_apdex => ignore_apdex?,
369
+ :ignore_enduser => ignore_enduser?)
358
370
  end
359
371
  end
360
372
 
361
- def recorded_metrics(txn)
362
- metric_parser = NewRelic::MetricParser::MetricParser.for_metric_named(txn.name)
363
- metrics = [txn.name]
364
- metrics += metric_parser.summary_metrics unless txn.has_parent?
365
- metrics
366
- end
367
-
368
373
  protected
369
374
 
370
375
  def newrelic_request(args)
@@ -417,32 +422,21 @@ module NewRelic
417
422
 
418
423
  private
419
424
 
420
- # Write a transaction onto a thread local if there isn't already one there.
421
- # If there is one, just update it.
422
- def _start_transaction(args) # :nodoc:
423
- # If a block was passed in, then the arguments represent options for the instrumentation,
424
- # not app method arguments.
425
- options = {}
426
- if args.any?
427
- if args.last.is_a?(Hash)
428
- options = args.pop
425
+ def create_transaction_options(txn_args)
426
+ txn_options = {}
427
+ if txn_args.any?
428
+ if txn_args.last.is_a?(Hash)
429
+ txn_options = txn_args.pop
429
430
  end
430
- available_params = options[:params] || {}
431
- options[:name] ||= args.first
431
+ available_params = txn_options[:params] || {}
432
+ txn_options[:name] ||= txn_args.first
432
433
  else
433
434
  available_params = self.respond_to?(:params) ? self.params : {}
434
435
  end
435
436
 
436
- options[:request] ||= self.request if self.respond_to? :request
437
- options[:filtered_params] = (respond_to? :filter_parameters) ? filter_parameters(available_params) : available_params
438
- category = options[:category] || :controller
439
- txn = Transaction.start(category, options)
440
- txn.name = TransactionNamer.new(self).name(options)
441
-
442
- txn.apdex_start = _detect_upstream_wait(txn)
443
- _record_queue_length
444
-
445
- return txn
437
+ txn_options[:request] ||= self.request if self.respond_to? :request
438
+ txn_options[:filtered_params] = (respond_to? :filter_parameters) ? filter_parameters(available_params) : available_params
439
+ txn_options
446
440
  end
447
441
 
448
442
  # Filter out a request if it matches one of our parameters for
@@ -473,20 +467,10 @@ module NewRelic
473
467
  end
474
468
  end
475
469
 
476
- # Return a Time instance representing the upstream start time.
477
- # now is a Time instance to fall back on if no other candidate
478
- # for the start time is found.
479
- def _detect_upstream_wait(txn)
480
- now = txn.start_time
481
- return now unless txn.root?
470
+ def detect_queue_start_time
482
471
  if newrelic_request_headers
483
- queue_start = QueueTime.parse_frontend_timestamp(newrelic_request_headers, now)
484
- QueueTime.record_frontend_metrics(queue_start, now) if queue_start
472
+ QueueTime.parse_frontend_timestamp(newrelic_request_headers)
485
473
  end
486
- queue_start || now
487
- rescue => e
488
- ::NewRelic::Agent.logger.error("Error detecting upstream wait time:", e)
489
- now
490
474
  end
491
475
  end
492
476
  end