newrelic_rpm 3.6.8.168 → 3.6.9.171

Sign up to get free protection for your applications and to get access to all the features.
Files changed (137) hide show
  1. data.tar.gz.sig +0 -0
  2. data/.gitignore +1 -0
  3. data/.yardopts +17 -0
  4. data/CHANGELOG +48 -0
  5. data/README.md +8 -6
  6. data/lib/new_relic/agent.rb +65 -17
  7. data/lib/new_relic/agent/agent.rb +42 -113
  8. data/lib/new_relic/agent/browser_monitoring.rb +9 -1
  9. data/lib/new_relic/agent/configuration/default_source.rb +12 -0
  10. data/lib/new_relic/agent/error_collector.rb +13 -6
  11. data/lib/new_relic/agent/instrumentation/active_record_subscriber.rb +5 -5
  12. data/lib/new_relic/agent/instrumentation/controller_instrumentation.rb +15 -0
  13. data/lib/new_relic/agent/instrumentation/curb.rb +2 -2
  14. data/lib/new_relic/agent/instrumentation/metric_frame.rb +2 -2
  15. data/lib/new_relic/agent/instrumentation/rack.rb +2 -0
  16. data/lib/new_relic/agent/instrumentation/resque.rb +9 -3
  17. data/lib/new_relic/agent/instrumentation/sidekiq.rb +5 -0
  18. data/lib/new_relic/agent/method_tracer.rb +45 -27
  19. data/lib/new_relic/agent/new_relic_service.rb +14 -6
  20. data/lib/new_relic/agent/pipe_service.rb +1 -1
  21. data/lib/new_relic/agent/request_sampler.rb +10 -7
  22. data/lib/new_relic/agent/rules_engine.rb +5 -0
  23. data/lib/new_relic/agent/samplers/object_sampler.rb +1 -1
  24. data/lib/new_relic/agent/sql_sampler.rb +4 -2
  25. data/lib/new_relic/agent/stats_engine.rb +3 -0
  26. data/lib/new_relic/agent/stats_engine/gc_profiler.rb +17 -7
  27. data/lib/new_relic/agent/stats_engine/metric_stats.rb +5 -7
  28. data/lib/new_relic/agent/stats_engine/stats_hash.rb +2 -0
  29. data/lib/new_relic/agent/supported_versions.rb +247 -0
  30. data/lib/new_relic/agent/threading/backtrace_service.rb +1 -1
  31. data/lib/new_relic/agent/threading/thread_profile.rb +2 -1
  32. data/lib/new_relic/agent/transaction.rb +7 -6
  33. data/lib/new_relic/agent/transaction/developer_mode_sample_buffer.rb +11 -5
  34. data/lib/new_relic/agent/transaction/force_persist_sample_buffer.rb +3 -3
  35. data/lib/new_relic/agent/transaction/slowest_sample_buffer.rb +3 -3
  36. data/lib/new_relic/agent/transaction/transaction_sample_buffer.rb +23 -4
  37. data/lib/new_relic/agent/transaction/xray_sample_buffer.rb +4 -4
  38. data/lib/new_relic/agent/transaction_sampler.rb +14 -18
  39. data/lib/new_relic/agent/worker_loop.rb +1 -0
  40. data/lib/new_relic/control.rb +1 -0
  41. data/lib/new_relic/control/instance_methods.rb +0 -1
  42. data/lib/new_relic/helper.rb +1 -2
  43. data/lib/new_relic/language_support.rb +12 -2
  44. data/lib/new_relic/local_environment.rb +12 -11
  45. data/lib/new_relic/rack.rb +9 -0
  46. data/lib/new_relic/rack/agent_hooks.rb +6 -0
  47. data/lib/new_relic/rack/browser_monitoring.rb +9 -2
  48. data/lib/new_relic/rack/developer_mode.rb +15 -1
  49. data/lib/new_relic/rack/error_collector.rb +7 -0
  50. data/lib/new_relic/recipes.rb +2 -0
  51. data/lib/new_relic/transaction_sample.rb +39 -48
  52. data/lib/new_relic/version.rb +1 -1
  53. data/lib/tasks/install.rake +44 -2
  54. data/lib/tasks/versions.html.erb +31 -0
  55. data/lib/tasks/versions.rake +52 -0
  56. data/lib/tasks/versions.txt.erb +14 -0
  57. data/newrelic_rpm.gemspec +4 -2
  58. data/test/agent_helper.rb +21 -1
  59. data/test/environments/lib/environments/runner.rb +19 -5
  60. data/test/environments/norails/Gemfile +4 -1
  61. data/test/environments/rails21/Gemfile +4 -6
  62. data/test/environments/rails21/Rakefile +4 -0
  63. data/test/environments/rails21/config/database.yml +2 -7
  64. data/test/environments/rails22/Gemfile +6 -13
  65. data/test/environments/rails22/Rakefile +4 -0
  66. data/test/environments/rails22/config/database.yml +2 -7
  67. data/test/environments/rails22/config/environment.rb +1 -1
  68. data/test/environments/rails23/Gemfile +3 -4
  69. data/test/environments/rails23/Rakefile +4 -0
  70. data/test/environments/rails23/config/database.yml +2 -7
  71. data/test/environments/rails30/Gemfile +2 -4
  72. data/test/environments/rails30/Rakefile +2 -0
  73. data/test/environments/rails30/config/database.yml +2 -7
  74. data/test/environments/rails31/Gemfile +2 -4
  75. data/test/environments/rails31/Rakefile +2 -0
  76. data/test/environments/rails31/config/database.yml +2 -7
  77. data/test/environments/rails32/Gemfile +2 -5
  78. data/test/environments/rails32/Rakefile +2 -0
  79. data/test/environments/rails32/config/database.yml +1 -1
  80. data/test/environments/rails40/Gemfile +7 -4
  81. data/test/environments/rails40/Rakefile +2 -0
  82. data/test/environments/rails40/config/database.yml +2 -7
  83. data/test/helpers/runtime_detection.rb +17 -0
  84. data/test/multiverse/lib/multiverse/suite.rb +20 -4
  85. data/test/multiverse/suites/agent_only/key_transactions_test.rb +1 -1
  86. data/test/multiverse/suites/agent_only/marshaling_test.rb +1 -1
  87. data/test/multiverse/suites/agent_only/thread_profiling_test.rb +32 -7
  88. data/test/multiverse/suites/agent_only/xray_sessions_test.rb +1 -0
  89. data/test/multiverse/suites/config_file_loading/config_file_loading_test.rb +4 -3
  90. data/test/multiverse/suites/curb/curb_test.rb +8 -0
  91. data/test/multiverse/suites/excon/excon_test.rb +8 -0
  92. data/test/multiverse/suites/httpclient/httpclient_test.rb +8 -0
  93. data/test/multiverse/suites/net_http/net_http_test.rb +8 -0
  94. data/test/multiverse/suites/padrino/Envfile +3 -2
  95. data/test/multiverse/suites/rails/gc_instrumentation_test.rb +17 -8
  96. data/test/multiverse/suites/resque/Envfile +3 -3
  97. data/test/multiverse/suites/resque/instrumentation_test.rb +47 -5
  98. data/test/multiverse/suites/sequel/Envfile +0 -3
  99. data/test/multiverse/suites/sequel/database.rb +53 -0
  100. data/test/{new_relic/agent/instrumentation/sequel_test.rb → multiverse/suites/sequel/sequel_instrumentation_test.rb} +12 -53
  101. data/test/multiverse/suites/sequel/{sequel_test.rb → sequel_safety_test.rb} +2 -17
  102. data/test/multiverse/suites/sidekiq/sidekiq_instrumentation_test.rb +50 -5
  103. data/test/multiverse/suites/sinatra/sinatra_metric_explosion_test.rb +15 -2
  104. data/test/multiverse/suites/typhoeus/typhoeus_test.rb +8 -0
  105. data/test/new_relic/agent/agent/connect_test.rb +3 -2
  106. data/test/new_relic/agent/agent_test.rb +89 -82
  107. data/test/new_relic/agent/browser_monitoring_test.rb +44 -1
  108. data/test/new_relic/agent/error_collector_test.rb +17 -20
  109. data/test/new_relic/agent/instrumentation/active_record_instrumentation_test.rb +13 -10
  110. data/test/new_relic/agent/instrumentation/active_record_subscriber_test.rb +16 -1
  111. data/test/new_relic/agent/instrumentation/task_instrumentation_test.rb +2 -2
  112. data/test/new_relic/agent/method_tracer/instance_methods/trace_execution_scoped_test.rb +1 -1
  113. data/test/new_relic/agent/new_relic_service_test.rb +78 -9
  114. data/test/new_relic/agent/pipe_channel_manager_test.rb +7 -9
  115. data/test/new_relic/agent/pipe_service_test.rb +4 -4
  116. data/test/new_relic/agent/request_sampler_test.rb +2 -2
  117. data/test/new_relic/agent/stats_engine/gc_profiler_test.rb +15 -35
  118. data/test/new_relic/agent/stats_engine/metric_stats_test.rb +15 -7
  119. data/test/new_relic/agent/stats_engine_test.rb +4 -3
  120. data/test/new_relic/agent/threading/backtrace_service_test.rb +2 -0
  121. data/test/new_relic/agent/threading/thread_profile_test.rb +19 -0
  122. data/test/new_relic/agent/transaction/developer_mode_sample_buffer_test.rb +4 -4
  123. data/test/new_relic/agent/transaction/force_persist_sample_buffer_test.rb +1 -1
  124. data/test/new_relic/agent/transaction_sampler_test.rb +60 -45
  125. data/test/new_relic/fake_collector.rb +37 -2
  126. data/test/new_relic/http_client_test_cases.rb +26 -1
  127. data/test/new_relic/language_support_test.rb +12 -31
  128. data/test/new_relic/local_environment_test.rb +6 -2
  129. data/test/new_relic/multiverse_helpers.rb +2 -5
  130. data/test/new_relic/transaction_sample_test.rb +57 -36
  131. data/test/performance/suites/config.rb +76 -0
  132. data/test/rum/no_html_and_no_header.result.html +3 -0
  133. data/test/rum/no_html_and_no_header.source.html +3 -0
  134. data/test/script/ci.sh +0 -2
  135. data/test/test_helper.rb +5 -0
  136. metadata +43 -26
  137. metadata.gz.sig +0 -0
@@ -9,10 +9,10 @@ module NewRelic
9
9
  class Transaction
10
10
  class ForcePersistSampleBuffer < TransactionSampleBuffer
11
11
 
12
- MAX_SAMPLES = 10
12
+ CAPACITY = 10
13
13
 
14
- def max_samples
15
- MAX_SAMPLES
14
+ def capacity
15
+ CAPACITY
16
16
  end
17
17
 
18
18
  def allow_sample?(sample)
@@ -9,10 +9,10 @@ module NewRelic
9
9
  class Transaction
10
10
  class SlowestSampleBuffer < TransactionSampleBuffer
11
11
 
12
- MAX_SAMPLES = 1
12
+ CAPACITY = 1
13
13
 
14
- def max_samples
15
- MAX_SAMPLES
14
+ def capacity
15
+ CAPACITY
16
16
  end
17
17
 
18
18
  def allow_sample?(sample)
@@ -8,6 +8,7 @@ module NewRelic
8
8
  class TransactionSampleBuffer
9
9
  attr_reader :samples
10
10
 
11
+ SINGLE_BUFFER_MAX = 20
11
12
  NO_SAMPLES = [].freeze
12
13
 
13
14
  def initialize
@@ -53,17 +54,35 @@ module NewRelic
53
54
  end
54
55
 
55
56
  def full?
56
- @samples.length >= max_samples
57
+ @samples.length >= max_capacity
57
58
  end
58
59
 
59
- # Our default truncation strategy is to keep max_samples worth of the
60
- # longest samples. Override this method for alternate behavior.
60
+ # Capacity is the desired number of samples a buffer will hold. This
61
+ # can be user dictated via config if a feature wants.
62
+ #
63
+ # This value will be forcibly capped by the max_capacity
64
+ def capacity
65
+ raise NotImplementedError.new("TransactionSampleBuffer subclasses must provide a capacity override")
66
+ end
67
+
68
+ # Apply hard upper limit to the capacity to prevent users from
69
+ # consuming too much memory buffering TT's.
70
+ #
71
+ # A typical buffer should NOT override this method (although we do for
72
+ # odd things like dev-mode)
73
+ def max_capacity
74
+ capacity > SINGLE_BUFFER_MAX ? SINGLE_BUFFER_MAX : capacity
75
+ end
76
+
77
+ # Our default truncation strategy is to keep max_capacity
78
+ # worth of the longest samples. Override this method for alternate
79
+ # behavior.
61
80
  #
62
81
  # This doesn't use the more convenient #last and #sort_by to avoid
63
82
  # additional array allocations (and abundant alliteration)
64
83
  def truncate_samples
65
84
  @samples.sort!{|a,b| a.duration <=> b.duration}
66
- @samples.slice!(0..-(max_samples + 1))
85
+ @samples.slice!(0..-(max_capacity + 1))
67
86
  end
68
87
 
69
88
  # When pushing a scope different sample buffers potentially want to
@@ -20,9 +20,9 @@ module NewRelic
20
20
  @enabled = new_value
21
21
  end
22
22
 
23
- @max_samples = NewRelic::Agent.config[:'xray_session.max_samples']
23
+ @capacity = NewRelic::Agent.config[:'xray_session.max_samples']
24
24
  NewRelic::Agent.config.register_callback(:'xray_session.max_samples') do |new_value|
25
- @max_samples = new_value
25
+ @capacity = new_value
26
26
  end
27
27
  end
28
28
 
@@ -30,8 +30,8 @@ module NewRelic
30
30
  @xray_session_collection ||= NewRelic::Agent.instance.agent_command_router.xray_session_collection
31
31
  end
32
32
 
33
- def max_samples
34
- @max_samples
33
+ def capacity
34
+ @capacity
35
35
  end
36
36
 
37
37
  def truncate_samples
@@ -235,31 +235,27 @@ module NewRelic
235
235
  end
236
236
 
237
237
  # Gather transaction traces that we'd like to transmit to the server.
238
- # choose_samples is responsible for determining the contents of that
239
- # transmission, along with limits and ordering.
240
- def harvest(previous=[])
241
- return [] if !enabled?
242
-
243
- # If no unsent transactions from last time, we explicitly pass nil!
244
- previous ||= []
238
+ def harvest
239
+ return [] unless enabled?
245
240
 
246
241
  @samples_lock.synchronize do
247
242
  @last_sample = nil
248
- choose_samples(previous)
243
+ harvest_from_sample_buffers
249
244
  end
250
245
  end
251
246
 
252
- # Runs previously untransmitted samples into buffers, then chooses what
253
- # to send based on each buffer's internal logic
254
- def choose_samples(previous_samples)
255
- append_previous_samples_to_buffers(previous_samples)
256
- harvest_from_sample_buffers
247
+ def merge!(previous)
248
+ @samples_lock.synchronize do
249
+ @sample_buffers.each do |buffer|
250
+ buffer.store_previous(previous)
251
+ end
252
+ end
257
253
  end
258
254
 
259
- # Previous samples are added to buffers to enforce limiting rules
260
- def append_previous_samples_to_buffers(previous_samples)
261
- @sample_buffers.each do |buffer|
262
- buffer.store_previous(previous_samples)
255
+ def count
256
+ @samples_lock.synchronize do
257
+ samples = @sample_buffers.inject([]) { |all, b| all.concat(b.samples) }
258
+ samples.uniq.size
263
259
  end
264
260
  end
265
261
 
@@ -268,7 +264,7 @@ module NewRelic
268
264
  # want a single level flatten anyway, but, as you probably already
269
265
  # know, Ruby 1.8.6 :/
270
266
  result = []
271
- @sample_buffers.each {|buffer| result.concat(buffer.harvest_samples)}
267
+ @sample_buffers.each { |buffer| result.concat(buffer.harvest_samples) }
272
268
  result.uniq
273
269
  end
274
270
 
@@ -11,6 +11,7 @@ module NewRelic
11
11
  class WorkerLoop
12
12
 
13
13
  attr_accessor :period, :propagate_errors
14
+ attr_reader :iterations
14
15
 
15
16
  # Optional argument :duration (in seconds) for how long the worker loop runs
16
17
  # or :limit (integer) for max number of iterations
@@ -6,6 +6,7 @@ require 'yaml'
6
6
  require 'conditional_vendored_metric_parser'
7
7
  require 'conditional_vendored_dependency_detection'
8
8
  require 'new_relic/local_environment'
9
+ require 'new_relic/language_support'
9
10
  require 'new_relic/helper'
10
11
 
11
12
  require 'singleton'
@@ -2,7 +2,6 @@
2
2
  # This file is distributed under New Relic's license terms.
3
3
  # See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details.
4
4
 
5
- require 'new_relic/language_support'
6
5
  require 'new_relic/agent/null_logger'
7
6
  require 'new_relic/agent/memory_logger'
8
7
  require 'new_relic/agent/agent_logger'
@@ -52,8 +52,7 @@ module NewRelic
52
52
 
53
53
  # Load the JSON library from the standard library.
54
54
  def self::load_stdlib_json
55
- # Don't even try to use 1.9.1's json.
56
- return false if RUBY_VERSION == '1.9.1'
55
+ return false unless NewRelic::LanguageSupport.stdlib_json_usable?
57
56
 
58
57
  require 'json'
59
58
  define_method( :json_dump, &::JSON.method(:dump) )
@@ -64,19 +64,29 @@ module NewRelic::LanguageSupport
64
64
  end
65
65
  end
66
66
 
67
- def object_space_enabled?
67
+ def object_space_usable?
68
68
  if defined?(::JRuby) && JRuby.respond_to?(:runtime)
69
69
  JRuby.runtime.is_object_space_enabled
70
+ elsif defined?(::ObjectSpace) && !rubinius?
71
+ true
70
72
  else
71
- defined?(::ObjectSpace) ? true : false
73
+ false
72
74
  end
73
75
  end
74
76
 
77
+ def rubinius?
78
+ defined?(RUBY_ENGINE) && RUBY_ENGINE == 'rbx'
79
+ end
80
+
75
81
  def using_version?(version)
76
82
  numbers = version.split('.')
77
83
  numbers == ::RUBY_VERSION.split('.')[0, numbers.size]
78
84
  end
79
85
 
86
+ def stdlib_json_usable?
87
+ RUBY_VERSION >= '1.9.2'
88
+ end
89
+
80
90
  def test_forkability
81
91
  child = Process.fork { exit! }
82
92
  # calling wait here doesn't seem like it should necessary, but it seems to
@@ -35,7 +35,7 @@ module NewRelic
35
35
  # Runs through all the objects in ObjectSpace to find the first one that
36
36
  # match the provided class
37
37
  def find_class_in_object_space(klass)
38
- if NewRelic::LanguageSupport.object_space_enabled?
38
+ if NewRelic::LanguageSupport.object_space_usable?
39
39
  ObjectSpace.each_object(klass) do |x|
40
40
  return x
41
41
  end
@@ -117,21 +117,21 @@ module NewRelic
117
117
  end
118
118
 
119
119
  def check_for_unicorn
120
- if (defined?(::Unicorn) && defined?(::Unicorn::HttpServer)) && NewRelic::LanguageSupport.object_space_enabled?
120
+ if (defined?(::Unicorn) && defined?(::Unicorn::HttpServer)) && NewRelic::LanguageSupport.object_space_usable?
121
121
  v = find_class_in_object_space(::Unicorn::HttpServer)
122
122
  @discovered_dispatcher = :unicorn if v
123
123
  end
124
124
  end
125
125
 
126
126
  def check_for_rainbows
127
- if (defined?(::Rainbows) && defined?(::Rainbows::HttpServer)) && NewRelic::LanguageSupport.object_space_enabled?
127
+ if (defined?(::Rainbows) && defined?(::Rainbows::HttpServer)) && NewRelic::LanguageSupport.object_space_usable?
128
128
  v = find_class_in_object_space(::Rainbows::HttpServer)
129
129
  @discovered_dispatcher = :rainbows if v
130
130
  end
131
131
  end
132
132
 
133
133
  def check_for_delayed_job
134
- if $0 =~ /delayed_job$/
134
+ if $0 =~ /delayed_job$/ || (File.basename($0) == 'rake' && ARGV.include?('jobs:work'))
135
135
  @discovered_dispatcher = :delayed_job
136
136
  end
137
137
  end
@@ -156,16 +156,17 @@ module NewRelic
156
156
  end
157
157
 
158
158
  def check_for_thin
159
- if defined?(::Thin) && defined?(::Thin::Server) && NewRelic::LanguageSupport.object_space_enabled?
160
- # This case covers the thin web dispatcher
161
- # Same issue as above- we assume only one instance per process
162
- ObjectSpace.each_object(Thin::Server) do |thin_dispatcher|
159
+ if defined?(::Thin) && defined?(::Thin::Server)
160
+ # If ObjectSpace is available, use it to search for a Thin::Server
161
+ # instance. Otherwise, just the presence of the constant is sufficient.
162
+ if NewRelic::LanguageSupport.object_space_usable?
163
+ ObjectSpace.each_object(Thin::Server) do |thin_dispatcher|
164
+ @discovered_dispatcher = :thin
165
+ end
166
+ else
163
167
  @discovered_dispatcher = :thin
164
168
  end
165
169
  end
166
- if defined?(::Thin) && defined?(::Thin::VERSION) && !@discovered_dispatcher
167
- @discovered_dispatcher = :thin
168
- end
169
170
  end
170
171
 
171
172
  def check_for_litespeed
@@ -2,7 +2,16 @@
2
2
  # This file is distributed under New Relic's license terms.
3
3
  # See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details.
4
4
 
5
+ # @api public
5
6
  module NewRelic
7
+ # This module contains Rack middlewares used by the Ruby agent.
8
+ #
9
+ # Generally, these middlewares should be injected automatically when starting
10
+ # your application. If automatic injection into the middleware chain is not
11
+ # working for some reason, you may also include them manually.
12
+ #
13
+ #
14
+ # @api public
6
15
  module Rack
7
16
  end
8
17
  end
@@ -5,6 +5,12 @@
5
5
  require 'new_relic/agent/event_listener'
6
6
 
7
7
  module NewRelic::Rack
8
+ # This middleware is used by the agent internally, and is usually injected
9
+ # automatically into the middleware chain. If automatic injection is not
10
+ # working, you may manually use it in your middleware chain instead.
11
+ #
12
+ # @api public
13
+ #
8
14
  class AgentHooks
9
15
  def initialize(app, options = {})
10
16
  @app = app
@@ -5,6 +5,13 @@
5
5
  require 'rack'
6
6
 
7
7
  module NewRelic::Rack
8
+ # This middleware is used by the agent for the Real user monitoring (RUM)
9
+ # feature, and will usually be automatically injected in the middleware chain.
10
+ # If automatic injection is not working, you may manually use it in your
11
+ # middleware chain instead.
12
+ #
13
+ # @api public
14
+ #
8
15
  class BrowserMonitoring
9
16
 
10
17
  def initialize(app, options = {})
@@ -76,9 +83,9 @@ module NewRelic::Rack
76
83
  # is really weird and we should punt.
77
84
  if head_pos && (head_pos < body_close)
78
85
  # rebuild the source
79
- source = source[0..(head_pos-1)] <<
86
+ source = source[0...head_pos] <<
80
87
  header <<
81
- source[head_pos..(body_close-1)] <<
88
+ source[head_pos...body_close] <<
82
89
  footer <<
83
90
  source[body_close..-1]
84
91
  else
@@ -11,6 +11,20 @@ require 'new_relic/collection_helper'
11
11
 
12
12
  module NewRelic
13
13
  module Rack
14
+ # This middleware provides the 'developer mode' feature of newrelic_rpm,
15
+ # which allows you to see data about local web transactions in development
16
+ # mode immediately without needing to send this data to New Relic's servers.
17
+ #
18
+ # Enabling developer mode has serious performance and security impact, and
19
+ # thus you should never use this middleware in a production or non-local
20
+ # environment.
21
+ #
22
+ # This middleware should be automatically inserted in most contexts, but if
23
+ # automatic middleware insertion fails, you may manually insert it into your
24
+ # middleware chain.
25
+ #
26
+ # @api public
27
+ #
14
28
  class DeveloperMode
15
29
 
16
30
  VIEW_PATH = File.expand_path('../../../../ui/views/', __FILE__)
@@ -141,7 +155,7 @@ module NewRelic
141
155
  body = render_without_layout(view, binding)
142
156
  end
143
157
  if add_rack_array
144
- ::Rack::Response.new(body).finish
158
+ ::Rack::Response.new(body, 200, {'Content-Type' => 'text/html'}).finish
145
159
  else
146
160
  body
147
161
  end
@@ -3,6 +3,13 @@
3
3
  # See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details.
4
4
 
5
5
  module NewRelic::Rack
6
+ # This middleware is used by the agent in order to capture exceptions that
7
+ # occur within your web application. It will normally be injected into the
8
+ # middleware chain automatically, but if automatic injection fails in your
9
+ # environment, you may alternately include it manually.
10
+ #
11
+ # @api public
12
+ #
6
13
  class ErrorCollector
7
14
  def initialize(app, options={})
8
15
  @app = app
@@ -12,6 +12,8 @@
12
12
  # The task will run on app servers except where no_release is true.
13
13
  # If it fails, it will not affect the task execution or do a rollback.
14
14
  #
15
+ # @api public
16
+ #
15
17
  make_notify_task = Proc.new do
16
18
 
17
19
  namespace :newrelic do
@@ -18,6 +18,7 @@ module NewRelic
18
18
  attr_accessor :params, :root_segment, :profile, :force_persist, :guid,
19
19
  :threshold, :finished, :xray_session_id
20
20
  attr_reader :root_segment, :params, :sample_id
21
+ attr_writer :prepared
21
22
 
22
23
  @@start_time = Time.now
23
24
 
@@ -29,11 +30,16 @@ module NewRelic
29
30
  @params = { :segment_count => -1, :request_params => {} }
30
31
  @segment_count = -1
31
32
  @root_segment = create_segment 0.0, "ROOT"
33
+ @prepared = false
32
34
 
33
35
  @guid = generate_guid
34
36
  NewRelic::Agent::TransactionState.get.request_guid = @guid
35
37
  end
36
38
 
39
+ def prepared?
40
+ @prepared
41
+ end
42
+
37
43
  def count_segments
38
44
  @segment_count
39
45
  end
@@ -182,21 +188,19 @@ module NewRelic
182
188
  #
183
189
  # :explain_sql : run EXPLAIN on all queries whose response times equal the value for this key
184
190
  # (for example :explain_sql => 2.0 would explain everything over 2 seconds. 0.0 would explain everything.)
185
- # :keep_backtraces : keep backtraces, significantly increasing size of trace (off by default)
186
191
  # :record_sql => [ :raw | :obfuscated] : copy over the sql, obfuscating if necessary
187
- def prepare_to_send(options={})
188
- sample = TransactionSample.new(@start_time, sample_id)
189
-
190
- sample.params.merge! self.params
191
- sample.guid = self.guid
192
- sample.force_persist = self.force_persist if self.force_persist
193
- sample.threshold = self.threshold
194
- sample.xray_session_id = self.xray_session_id
195
-
196
- build_segment_for_transfer(sample, @root_segment, sample.root_segment, options)
192
+ def prepare_to_send!(options={})
193
+ return self if @prepared
194
+
195
+ if options[:record_sql]
196
+ collect_explain_plans!(options[:explain_sql]) if options[:explain_sql]
197
+ prepare_sql_for_transmission!(options[:record_sql])
198
+ else
199
+ strip_sql!
200
+ end
197
201
 
198
- sample.root_segment.end_trace(@root_segment.exit_timestamp)
199
- sample
202
+ @prepared = true
203
+ self
200
204
  end
201
205
 
202
206
  def params=(params)
@@ -250,43 +254,30 @@ module NewRelic
250
254
  return time_delta
251
255
  end
252
256
 
253
- # see prepare_to_send for what we do with options
254
- #
255
- # This is badly in need of refactoring
256
- def build_segment_for_transfer(new_sample, source_segment, target_segment, options)
257
- source_segment.called_segments.each do |source_called_segment|
258
- target_called_segment = new_sample.create_segment(
259
- source_called_segment.entry_timestamp,
260
- source_called_segment.metric_name,
261
- source_called_segment.segment_id)
262
-
263
- target_segment.add_called_segment target_called_segment
264
- source_called_segment.params.each do |k,v|
265
- case k
266
- when :backtrace
267
- target_called_segment[k]=v if options[:keep_backtraces]
268
- when :sql
269
- # run an EXPLAIN on this sql if specified.
270
- if options[:record_sql] && options[:record_sql] &&
271
- options[:explain_sql] &&
272
- source_called_segment.duration > options[:explain_sql].to_f
273
- target_called_segment[:explain_plan] = source_called_segment.explain_sql
274
- end
275
-
276
- target_called_segment[:sql] = case options[:record_sql]
277
- when :raw then v
278
- when :obfuscated then NewRelic::Agent::Database.obfuscate_sql(v)
279
- else raise "Invalid value for record_sql: #{options[:record_sql]}"
280
- end.to_s if options[:record_sql]
281
- when :connection_config
282
- # don't copy it
283
- else
284
- target_called_segment[k]=v
285
- end
257
+ def strip_sql!
258
+ each_segment do |segment|
259
+ segment.params.delete(:sql)
260
+ end
261
+ end
262
+
263
+ def collect_explain_plans!(threshold)
264
+ each_segment do |segment|
265
+ if segment[:sql] && segment.duration > threshold
266
+ segment[:explain_plan] = segment.explain_sql
286
267
  end
268
+ end
269
+ end
287
270
 
288
- build_segment_for_transfer(new_sample, source_called_segment, target_called_segment, options)
289
- target_called_segment.end_trace(source_called_segment.exit_timestamp)
271
+ def prepare_sql_for_transmission!(strategy)
272
+ each_segment do |segment|
273
+ if segment[:sql]
274
+ segment[:sql] = case strategy
275
+ when :raw
276
+ segment[:sql].to_s
277
+ when :obfuscated
278
+ NewRelic::Agent::Database.obfuscate_sql(segment[:sql]).to_s
279
+ end
280
+ end
290
281
  end
291
282
  end
292
283
  end