newrelic_rpm 3.6.4.122 → 3.6.5.130

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. data.tar.gz.sig +0 -0
  2. data/CHANGELOG +14 -1
  3. data/lib/new_relic/agent.rb +8 -7
  4. data/lib/new_relic/agent/agent.rb +2 -0
  5. data/lib/new_relic/agent/configuration/defaults.rb +1 -1
  6. data/lib/new_relic/agent/configuration/server_source.rb +2 -1
  7. data/lib/new_relic/agent/cross_app_tracing.rb +52 -26
  8. data/lib/new_relic/agent/event_listener.rb +1 -1
  9. data/lib/new_relic/agent/http_clients/excon_wrappers.rb +61 -0
  10. data/lib/new_relic/agent/http_clients/net_http_wrappers.rb +46 -0
  11. data/lib/new_relic/agent/http_clients/typhoeus_wrappers.rb +72 -0
  12. data/lib/new_relic/agent/{uri_util.rb → http_clients/uri_util.rb} +11 -17
  13. data/lib/new_relic/agent/instrumentation/action_controller_subscriber.rb +4 -11
  14. data/lib/new_relic/agent/instrumentation/active_record_subscriber.rb +1 -1
  15. data/lib/new_relic/agent/instrumentation/controller_instrumentation.rb +2 -2
  16. data/lib/new_relic/agent/instrumentation/excon.rb +72 -0
  17. data/lib/new_relic/agent/instrumentation/excon/connection.rb +33 -0
  18. data/lib/new_relic/agent/instrumentation/excon/middleware.rb +45 -0
  19. data/lib/new_relic/agent/instrumentation/net.rb +3 -1
  20. data/lib/new_relic/agent/instrumentation/typhoeus.rb +73 -0
  21. data/lib/new_relic/agent/method_tracer.rb +3 -6
  22. data/lib/new_relic/agent/new_relic_service.rb +1 -1
  23. data/lib/new_relic/agent/samplers/object_sampler.rb +1 -1
  24. data/lib/new_relic/agent/stats.rb +12 -10
  25. data/lib/new_relic/agent/stats_engine/metric_stats.rb +24 -6
  26. data/lib/new_relic/agent/stats_engine/stats_hash.rb +9 -11
  27. data/lib/new_relic/agent/transaction.rb +34 -33
  28. data/lib/new_relic/agent/transaction_sampler.rb +15 -6
  29. data/lib/new_relic/language_support.rb +8 -0
  30. data/lib/new_relic/local_environment.rb +10 -14
  31. data/lib/new_relic/version.rb +1 -1
  32. data/test/agent_helper.rb +43 -0
  33. data/test/config/test.cert.crt +14 -0
  34. data/test/config/test.cert.csr +11 -0
  35. data/test/config/test.cert.key +15 -0
  36. data/test/config/testing-privkey.pem +18 -0
  37. data/test/multiverse/lib/multiverse/color.rb +9 -3
  38. data/test/multiverse/lib/multiverse/suite.rb +11 -1
  39. data/test/multiverse/suites/agent_only/audit_log_test.rb +1 -0
  40. data/test/multiverse/suites/agent_only/config/newrelic.yml +4 -0
  41. data/test/multiverse/suites/agent_only/http_response_code_test.rb +1 -1
  42. data/test/multiverse/suites/agent_only/rename_rule_test.rb +2 -1
  43. data/test/multiverse/suites/agent_only/start_up_test.rb +3 -2
  44. data/test/multiverse/suites/config_file_loading/Envfile +2 -0
  45. data/test/multiverse/suites/excon/Envfile +15 -0
  46. data/test/multiverse/suites/excon/config/newrelic.yml +21 -0
  47. data/test/multiverse/suites/excon/excon_test.rb +60 -0
  48. data/test/multiverse/suites/net_http/Envfile +6 -0
  49. data/test/multiverse/suites/net_http/config/newrelic.yml +21 -0
  50. data/test/multiverse/suites/net_http/net_http_test.rb +102 -0
  51. data/test/multiverse/suites/rails/Envfile +1 -1
  52. data/test/multiverse/suites/rails/view_instrumentation_test.rb +6 -0
  53. data/test/multiverse/suites/resque/Envfile +0 -9
  54. data/test/multiverse/suites/resque/instrumentation_test.rb +21 -6
  55. data/test/multiverse/suites/typhoeus/Envfile +46 -0
  56. data/test/multiverse/suites/typhoeus/config/newrelic.yml +21 -0
  57. data/test/multiverse/suites/typhoeus/typhoeus_test.rb +77 -0
  58. data/test/new_relic/agent/agent_test_controller_test.rb +11 -10
  59. data/test/new_relic/agent/configuration/server_source_test.rb +23 -9
  60. data/test/new_relic/agent/database_test.rb +6 -0
  61. data/test/new_relic/agent/http_clients/uri_util_test.rb +64 -0
  62. data/test/new_relic/agent/instrumentation/net_instrumentation_test.rb +0 -406
  63. data/test/new_relic/agent/new_relic_service_test.rb +23 -19
  64. data/test/new_relic/agent/stats_engine/metric_stats_test.rb +29 -12
  65. data/test/new_relic/agent/stats_hash_test.rb +24 -3
  66. data/test/new_relic/agent/stats_test.rb +4 -4
  67. data/test/new_relic/agent/transaction/pop_test.rb +1 -1
  68. data/test/new_relic/agent/transaction_sampler_test.rb +18 -0
  69. data/test/new_relic/agent/transaction_test.rb +64 -69
  70. data/test/new_relic/agent_test.rb +20 -0
  71. data/test/new_relic/dependency_detection_test.rb +99 -0
  72. data/test/new_relic/evil_server.rb +56 -0
  73. data/test/new_relic/fake_collector.rb +12 -96
  74. data/test/new_relic/fake_external_server.rb +55 -0
  75. data/test/new_relic/fake_server.rb +97 -0
  76. data/test/new_relic/http_client_test_cases.rb +444 -0
  77. data/test/new_relic/language_support_test.rb +45 -0
  78. data/vendor/gems/dependency_detection-0.0.1.build/lib/dependency_detection.rb +25 -4
  79. metadata +37 -35
  80. metadata.gz.sig +0 -0
  81. data/test/multiverse/suites/resque/resque_setup.rb +0 -19
  82. data/test/new_relic/agent/uri_util_test.rb +0 -75
data.tar.gz.sig CHANGED
Binary file
data/CHANGELOG CHANGED
@@ -1,5 +1,18 @@
1
1
  # New Relic Ruby Agent Release Notes #
2
2
 
3
+ ## v3.6.5 ##
4
+
5
+ * Rails 4.0 Support
6
+
7
+ The Ruby agent is all set for the recent general release of Rails 4.0! We've
8
+ been tracking the RC's, and that work paid off. Versions 3.6.5 and 3.6.4 of
9
+ the Ruby agent should work fine with Rails 4.0.0.
10
+
11
+ * Excon and Typhoeus support
12
+
13
+ The Ruby agent now supports the Excon and Typhoeus HTTP libraries! For more
14
+ details see https://newrelic.com/docs/ruby/ruby-http-clients.
15
+
3
16
  ## v3.6.4 ##
4
17
 
5
18
  * Exception Whitelist
@@ -21,7 +34,7 @@
21
34
  generation of the agent's EnvironmentReport on startup from a background
22
35
  thread to the main thread.
23
36
 
24
- ## 3.6.3 ##
37
+ ## v3.6.3 ##
25
38
 
26
39
  * Better Sinatra Support
27
40
 
@@ -98,7 +98,7 @@ module NewRelic
98
98
  require 'new_relic/agent/transaction_info'
99
99
  require 'new_relic/agent/configuration'
100
100
  require 'new_relic/agent/rules_engine'
101
- require 'new_relic/agent/uri_util'
101
+ require 'new_relic/agent/http_clients/uri_util'
102
102
  require 'new_relic/agent/system_info'
103
103
 
104
104
  require 'new_relic/agent/instrumentation/controller_instrumentation'
@@ -186,12 +186,13 @@ module NewRelic
186
186
  def record_metric(metric_name, value)
187
187
  if value.is_a?(Hash)
188
188
  stats = NewRelic::Agent::Stats.new
189
- stats.call_count = value[:count]
190
- stats.total_call_time = value[:total]
191
- stats.total_exclusive_time = value[:total]
192
- stats.min_call_time = value[:min]
193
- stats.max_call_time = value[:max]
194
- stats.sum_of_squares = value[:sum_of_squares]
189
+
190
+ stats.call_count = value[:count] if value[:count]
191
+ stats.total_call_time = value[:total] if value[:total]
192
+ stats.total_exclusive_time = value[:total] if value[:total]
193
+ stats.min_call_time = value[:min] if value[:min]
194
+ stats.max_call_time = value[:max] if value[:max]
195
+ stats.sum_of_squares = value[:sum_of_squares] if value[:sum_of_squares]
195
196
  value = stats
196
197
  end
197
198
  agent.stats_engine.record_metrics(metric_name, value)
@@ -53,6 +53,7 @@ module NewRelic
53
53
  @obfuscator = lambda {|sql| NewRelic::Agent::Database.default_sql_obfuscator(sql) }
54
54
 
55
55
  # FIXME: temporary work around for RUBY-839
56
+ # This should be handled with a configuration callback
56
57
  if Agent.config[:monitor_mode]
57
58
  @service = NewRelic::Agent::NewRelicService.new
58
59
  end
@@ -92,6 +93,7 @@ module NewRelic
92
93
  # cross application tracing ids and encoding
93
94
  attr_reader :cross_process_id
94
95
  attr_reader :cross_app_encoding_bytes
96
+ attr_reader :cross_app_monitor
95
97
  # service for communicating with collector
96
98
  attr_accessor :service
97
99
  # Global events dispatcher. This will provides our primary mechanism
@@ -148,7 +148,7 @@ module NewRelic
148
148
 
149
149
  :marshaller => Proc.new { NewRelic::Agent::NewRelicService::JsonMarshaller.is_supported? ? 'json' : 'pruby' },
150
150
 
151
- :'request_sampler.enabled' => false,
151
+ :'request_sampler.enabled' => true,
152
152
  :'request_sampler.sample_rate_ms' => 50
153
153
  ].freeze
154
154
  end
@@ -32,7 +32,8 @@ module NewRelic
32
32
  gated_features = {
33
33
  :'transaction_tracer.enabled' => 'collect_traces',
34
34
  :'slow_sql.enabled' => 'collect_traces',
35
- :'error_collector.enabled' => 'collect_errors'
35
+ :'error_collector.enabled' => 'collect_errors',
36
+ :'request_sampler.enabled' => 'collect_analytics_events'
36
37
  }
37
38
  gated_features.each do |feature, gate_key|
38
39
  if server_config.has_key?(gate_key)
@@ -31,14 +31,18 @@ module NewRelic
31
31
 
32
32
  # Send the given +request+, adding metrics appropriate to the
33
33
  # response when it comes back.
34
- def trace_http_request( http, request )
34
+ #
35
+ # See the documentation for +start_trace+ for an explanation of what
36
+ # +request+ should look like.
37
+ #
38
+ def trace_http_request( request )
35
39
  return yield unless NewRelic::Agent.is_execution_traced?
36
40
 
37
- t0, segment = start_trace( http, request )
41
+ t0, segment = start_trace( request )
38
42
  begin
39
43
  response = yield
40
44
  ensure
41
- finish_trace( t0, segment, request, response, http ) if t0
45
+ finish_trace( t0, segment, request, response ) if t0
42
46
  end
43
47
 
44
48
  return response
@@ -46,8 +50,19 @@ module NewRelic
46
50
 
47
51
 
48
52
  # Set up the necessary state for cross-application tracing before the
49
- # given +request+ goes out on the specified +http+ connection.
50
- def start_trace( http, request )
53
+ # given +request+ goes out.
54
+ #
55
+ # The +request+ object passed in must respond to the following methods:
56
+ #
57
+ # * type - Return a String describing the underlying library being used
58
+ # to make the request (e.g. 'Net::HTTP' or 'Typhoeus')
59
+ # * host - Return a String with the hostname or IP of the host being
60
+ # communicated with.
61
+ # * method - Return a String with the HTTP method name used for this request
62
+ # * [](key) - Lookup an HTTP request header by name
63
+ # * []=(key, val) - Set an HTTP request header by name
64
+ # * uri - Full URI of the request
65
+ def start_trace( request )
51
66
  inject_request_headers( request ) if cross_app_enabled?
52
67
 
53
68
  # Create a segment and time the call
@@ -63,15 +78,24 @@ module NewRelic
63
78
 
64
79
  # Finish tracing the HTTP +request+ that started at +t0+ with the information in
65
80
  # +response+ and the given +http+ connection.
66
- def finish_trace( t0, segment, request, response, http )
81
+ #
82
+ # The +request+ must conform to the same interface described in the documentation
83
+ # for +start_trace+.
84
+ #
85
+ # The +response+ must respond to the following methods:
86
+ #
87
+ # * [](key) - Reads response headers.
88
+ # * to_hash - Converts response headers to a Hash
89
+ #
90
+ def finish_trace( t0, segment, request, response )
67
91
  t1 = Time.now
68
92
  duration = t1.to_f - t0.to_f
69
93
 
70
94
  begin
71
- if request && response && http
95
+ if request
72
96
  # Figure out which metrics we need to report based on the request and response
73
97
  # The last (most-specific) one is scoped.
74
- metrics = metrics_for( http, request, response )
98
+ metrics = metrics_for( request, response )
75
99
  scoped_metric = metrics.pop
76
100
 
77
101
  stats_engine.record_metrics(metrics, duration)
@@ -79,7 +103,7 @@ module NewRelic
79
103
 
80
104
  # Add TT custom parameters
81
105
  segment.name = scoped_metric
82
- add_transaction_trace_parameters(http, request, response)
106
+ add_transaction_trace_parameters(request, response) if response
83
107
  end
84
108
  ensure
85
109
  # We always need to pop the scope stack to avoid an inconsistent
@@ -117,15 +141,15 @@ module NewRelic
117
141
  txn_guid = NewRelic::Agent::TransactionInfo.get.guid
118
142
  txn_data = NewRelic.json_dump([ txn_guid, false ])
119
143
 
120
- request[ NR_ID_HEADER ] = obfuscate_with_key( key, cross_app_id )
144
+ request[ NR_ID_HEADER ] = obfuscate_with_key( key, cross_app_id )
121
145
  request[ NR_TXN_HEADER ] = obfuscate_with_key( key, txn_data )
122
146
 
123
147
  rescue NewRelic::Agent::CrossAppTracing::Error => err
124
148
  NewRelic::Agent.logger.debug "Not injecting x-process header", err
125
149
  end
126
150
 
127
- def add_transaction_trace_parameters(http, request, response)
128
- filtered_uri = NewRelic::Agent::URIUtil.filtered_uri_for(http, request)
151
+ def add_transaction_trace_parameters(request, response)
152
+ filtered_uri = ::NewRelic::Agent::HTTPClients::URIUtil.filter_uri(request.uri)
129
153
  transaction_sampler.add_segment_parameters(:uri => filtered_uri)
130
154
  if response_is_crossapp?( response )
131
155
  add_cat_transaction_trace_parameters( response )
@@ -144,21 +168,23 @@ module NewRelic
144
168
 
145
169
  # Return the set of metric names that correspond to
146
170
  # the given +request+ and +response+.
147
- def metrics_for( http, request, response )
148
- metrics = common_metrics( http )
171
+ # +response+ may be nil in the case that the request produced an error
172
+ # without ever receiving an HTTP response.
173
+ def metrics_for( request, response )
174
+ metrics = common_metrics( request )
149
175
 
150
- if response_is_crossapp?( response )
176
+ if response && response_is_crossapp?( response )
151
177
  begin
152
- metrics.concat metrics_for_crossapp_response( http, response )
178
+ metrics.concat metrics_for_crossapp_response( request, response )
153
179
  rescue => err
154
180
  # Fall back to regular metrics if there's a problem with x-process metrics
155
181
  NewRelic::Agent.logger.debug "%p while fetching x-process metrics: %s" %
156
182
  [ err.class, err.message ]
157
- metrics.concat metrics_for_regular_response( http, request, response )
183
+ metrics.concat metrics_for_regular_request( request )
158
184
  end
159
185
  else
160
186
  NewRelic::Agent.logger.debug "Response doesn't have CAT headers."
161
- metrics.concat metrics_for_regular_response( http, request, response )
187
+ metrics.concat metrics_for_regular_request( request )
162
188
  end
163
189
 
164
190
  return metrics
@@ -166,9 +192,9 @@ module NewRelic
166
192
 
167
193
 
168
194
  # Return an Array of metrics used for every response.
169
- def common_metrics( http )
195
+ def common_metrics( request )
170
196
  metrics = [ "External/all" ]
171
- metrics << "External/#{http.address}/all"
197
+ metrics << "External/#{request.host}/all"
172
198
 
173
199
  if NewRelic::Agent::Transaction.recording_web_transaction?
174
200
  metrics << "External/allWeb"
@@ -196,7 +222,7 @@ module NewRelic
196
222
 
197
223
  # Return the set of metric objects appropriate for the given cross app
198
224
  # +response+.
199
- def metrics_for_crossapp_response( http, response )
225
+ def metrics_for_crossapp_response( request, response )
200
226
  xp_id, txn_name, q_time, r_time, req_len, _ = extract_appdata( response )
201
227
 
202
228
  check_crossapp_id( xp_id )
@@ -205,8 +231,8 @@ module NewRelic
205
231
  NewRelic::Agent.logger.debug "CAT xp_id: %p, txn_name: %p." % [ xp_id, txn_name ]
206
232
 
207
233
  metrics = []
208
- metrics << "ExternalApp/#{http.address}/#{xp_id}/all"
209
- metrics << "ExternalTransaction/#{http.address}/#{xp_id}/#{txn_name}"
234
+ metrics << "ExternalApp/#{request.host}/#{xp_id}/all"
235
+ metrics << "ExternalTransaction/#{request.host}/#{xp_id}/#{txn_name}"
210
236
 
211
237
  return metrics
212
238
  end
@@ -238,10 +264,10 @@ module NewRelic
238
264
 
239
265
 
240
266
  # Return the set of metric objects appropriate for the given (non-cross app)
241
- # +response+.
242
- def metrics_for_regular_response( http, request, response )
267
+ # +request+.
268
+ def metrics_for_regular_request( request )
243
269
  metrics = []
244
- metrics << "External/#{http.address}/Net::HTTP/#{request.method}"
270
+ metrics << "External/#{request.host}/#{request.type}/#{request.method}"
245
271
 
246
272
  return metrics
247
273
  end
@@ -39,7 +39,7 @@ module NewRelic::Agent
39
39
  begin
40
40
  handler.call(*args)
41
41
  rescue => err
42
- NewRelic::Agent.logger.debug("Failure during notify for #{@event}", err)
42
+ NewRelic::Agent.logger.debug("Failure during notify for #{event}", err)
43
43
  end
44
44
  end
45
45
  end
@@ -0,0 +1,61 @@
1
+ # encoding: utf-8
2
+ # This file is distributed under New Relic's license terms.
3
+ # See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details.
4
+
5
+ module NewRelic
6
+ module Agent
7
+ module HTTPClients
8
+ class ExconHTTPResponse
9
+ def initialize(response)
10
+ @response = response
11
+ # Since HTTP headers are case-insensitive, we normalize all of them to
12
+ # upper case here, and then also in our [](key) implementation.
13
+ @normalized_headers = {}
14
+ headers = response.respond_to?(:headers) ? response.headers : response[:headers]
15
+ (headers || {}).each do |key, val|
16
+ @normalized_headers[key.upcase] = val
17
+ end
18
+ end
19
+
20
+ def [](key)
21
+ @normalized_headers[key.upcase]
22
+ end
23
+
24
+ def to_hash
25
+ @normalized_headers.dup
26
+ end
27
+ end
28
+
29
+ class ExconHTTPRequest
30
+ def initialize(datum)
31
+ @datum = datum
32
+ end
33
+
34
+ def type
35
+ "Excon"
36
+ end
37
+
38
+ def host
39
+ @datum[:host]
40
+ end
41
+
42
+ def method
43
+ @datum[:method].to_s.upcase
44
+ end
45
+
46
+ def [](key)
47
+ @datum[:headers][key]
48
+ end
49
+
50
+ def []=(key, value)
51
+ @datum[:headers] ||= {}
52
+ @datum[:headers][key] = value
53
+ end
54
+
55
+ def uri
56
+ URI.parse("#{@datum[:scheme]}://#{@datum[:host]}:#{@datum[:port]}#{@datum[:path]}")
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,46 @@
1
+ # encoding: utf-8
2
+ # This file is distributed under New Relic's license terms.
3
+ # See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details.
4
+
5
+ module NewRelic
6
+ module Agent
7
+ module HTTPClients
8
+ class NetHTTPRequest
9
+ def initialize(connection, request)
10
+ @connection = connection
11
+ @request = request
12
+ end
13
+
14
+ def type
15
+ 'Net::HTTP'
16
+ end
17
+
18
+ def host
19
+ @connection.address
20
+ end
21
+
22
+ def method
23
+ @request.method
24
+ end
25
+
26
+ def [](key)
27
+ @request[key]
28
+ end
29
+
30
+ def []=(key, value)
31
+ @request[key] = value
32
+ end
33
+
34
+ def uri
35
+ case @request.path
36
+ when /^https?:\/\//
37
+ URI(@request.path)
38
+ else
39
+ scheme = @connection.use_ssl? ? 'https' : 'http'
40
+ URI("#{scheme}://#{@connection.address}:#{@connection.port}#{@request.path}")
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,72 @@
1
+ # encoding: utf-8
2
+ # This file is distributed under New Relic's license terms.
3
+ # See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details.
4
+
5
+ module NewRelic
6
+ module Agent
7
+ module HTTPClients
8
+ class TyphoeusHTTPResponse
9
+ def initialize(response)
10
+ @response = response
11
+ end
12
+
13
+ def [](key)
14
+ unless headers.nil?
15
+ result = headers[key]
16
+
17
+ # Typhoeus 0.5.3 has a bug where asking the headers hash for a
18
+ # non-existent header will return the hash itself, not what we want.
19
+ result == headers ? nil : result
20
+ end
21
+ end
22
+
23
+ def to_hash
24
+ hash = {}
25
+ headers.each do |(k,v)|
26
+ hash[k] = v
27
+ end
28
+ hash
29
+ end
30
+
31
+ private
32
+
33
+ def headers
34
+ @response.headers
35
+ end
36
+ end
37
+
38
+ class TyphoeusHTTPRequest
39
+ def initialize(request)
40
+ @request = request
41
+ @uri = URI.parse(request.url)
42
+ end
43
+
44
+ def type
45
+ "Typhoeus"
46
+ end
47
+
48
+ def host
49
+ @uri.host
50
+ end
51
+
52
+ def method
53
+ (@request.options[:method] || 'GET').to_s.upcase
54
+ end
55
+
56
+ def [](key)
57
+ return nil unless @request.options && @request.options[:headers]
58
+ @request.options[:headers][key]
59
+ end
60
+
61
+ def []=(key, value)
62
+ @request.options[:headers] ||= {}
63
+ @request.options[:headers][key] = value
64
+ end
65
+
66
+ def uri
67
+ @uri
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end