newrelic_rpm 3.7.0.177 → 3.7.1.180

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. data.tar.gz.sig +0 -0
  2. data/CHANGELOG +28 -1
  3. data/lib/new_relic/agent.rb +1 -2
  4. data/lib/new_relic/agent/agent.rb +28 -10
  5. data/lib/new_relic/agent/agent_logger.rb +4 -3
  6. data/lib/new_relic/agent/audit_logger.rb +5 -8
  7. data/lib/new_relic/agent/configuration/default_source.rb +24 -0
  8. data/lib/new_relic/agent/cross_app_tracing.rb +21 -15
  9. data/lib/new_relic/agent/datastores/mongo.rb +25 -0
  10. data/lib/new_relic/agent/datastores/mongo/metric_generator.rb +25 -0
  11. data/lib/new_relic/agent/datastores/mongo/metric_translator.rb +189 -0
  12. data/lib/new_relic/agent/datastores/mongo/obfuscator.rb +39 -0
  13. data/lib/new_relic/agent/datastores/mongo/statement_formatter.rb +52 -0
  14. data/lib/new_relic/agent/harvester.rb +55 -0
  15. data/lib/new_relic/agent/instrumentation/mongo.rb +139 -0
  16. data/lib/new_relic/agent/instrumentation/net.rb +6 -11
  17. data/lib/new_relic/agent/supported_versions.rb +9 -5
  18. data/lib/new_relic/agent/transaction_sampler.rb +4 -0
  19. data/lib/new_relic/version.rb +1 -1
  20. data/lib/tasks/versions.rake +1 -1
  21. data/test/agent_helper.rb +4 -0
  22. data/test/environments/norails/Gemfile +3 -0
  23. data/test/environments/rails40/Gemfile +5 -1
  24. data/test/flaky_proxy/Gemfile +3 -0
  25. data/test/flaky_proxy/README.md +82 -0
  26. data/test/flaky_proxy/lib/flaky_proxy.rb +22 -0
  27. data/test/flaky_proxy/lib/flaky_proxy/connection.rb +45 -0
  28. data/test/flaky_proxy/lib/flaky_proxy/http_message.rb +105 -0
  29. data/test/flaky_proxy/lib/flaky_proxy/proxy.rb +42 -0
  30. data/test/flaky_proxy/lib/flaky_proxy/rule.rb +75 -0
  31. data/test/flaky_proxy/lib/flaky_proxy/rule_set.rb +37 -0
  32. data/test/flaky_proxy/lib/flaky_proxy/server.rb +22 -0
  33. data/test/flaky_proxy/script/flaky_proxy +39 -0
  34. data/test/helpers/exceptions.rb +16 -0
  35. data/test/helpers/mongo_metric_builder.rb +29 -0
  36. data/test/multiverse/lib/multiverse/suite.rb +1 -0
  37. data/test/multiverse/suites/curb/curb_test.rb +0 -1
  38. data/test/multiverse/suites/deferred_instrumentation/sinatra_test.rb +4 -3
  39. data/test/multiverse/suites/excon/excon_test.rb +0 -1
  40. data/test/multiverse/suites/httpclient/httpclient_test.rb +0 -1
  41. data/test/multiverse/suites/mongo/Envfile +66 -0
  42. data/test/multiverse/suites/mongo/config/newrelic.yml +19 -0
  43. data/test/multiverse/suites/mongo/mongo_instrumentation_test.rb +418 -0
  44. data/test/multiverse/suites/mongo/mongo_unsupported_version_test.rb +36 -0
  45. data/test/multiverse/suites/net_http/net_http_test.rb +2 -4
  46. data/test/multiverse/suites/rails/Envfile +4 -4
  47. data/test/multiverse/suites/rails/config/newrelic.yml +1 -1
  48. data/test/multiverse/suites/rails/error_tracing_test.rb +7 -7
  49. data/test/multiverse/suites/sidekiq/Envfile +1 -1
  50. data/test/multiverse/suites/sidekiq/sidekiq_instrumentation_test.rb +0 -1
  51. data/test/multiverse/suites/sinatra/sinatra_classic_test.rb +5 -3
  52. data/test/multiverse/suites/sinatra/sinatra_modular_test.rb +5 -3
  53. data/test/multiverse/suites/typhoeus/typhoeus_test.rb +0 -1
  54. data/test/new_relic/agent/agent_logger_test.rb +9 -1
  55. data/test/new_relic/agent/agent_test.rb +66 -1
  56. data/test/new_relic/agent/agent_test_controller.rb +1 -2
  57. data/test/new_relic/agent/audit_logger_test.rb +12 -4
  58. data/test/new_relic/agent/configuration/orphan_configuration_test.rb +11 -2
  59. data/test/new_relic/agent/cpu_sampler_test.rb +1 -0
  60. data/test/new_relic/agent/cross_app_tracing_test.rb +60 -0
  61. data/test/new_relic/agent/datastores/mongo/metric_generator_test.rb +43 -0
  62. data/test/new_relic/agent/datastores/mongo/metric_translator_test.rb +301 -0
  63. data/test/new_relic/agent/datastores/mongo/obfuscator_test.rb +91 -0
  64. data/test/new_relic/agent/datastores/mongo/statement_formatter_test.rb +71 -0
  65. data/test/new_relic/agent/harvester_test.rb +85 -0
  66. data/test/new_relic/agent/transaction_sampler_test.rb +5 -0
  67. data/test/new_relic/agent/worker_loop_test.rb +3 -5
  68. data/test/new_relic/http_client_test_cases.rb +65 -81
  69. data/test/new_relic/noticed_error_test.rb +14 -16
  70. data/test/performance/lib/performance.rb +1 -0
  71. data/test/performance/lib/performance/console_reporter.rb +6 -2
  72. data/test/performance/lib/performance/instrumentor.rb +1 -15
  73. data/test/performance/lib/performance/platform.rb +35 -0
  74. data/test/performance/lib/performance/test_case.rb +16 -1
  75. data/test/performance/suites/marshalling.rb +73 -0
  76. metadata +48 -19
  77. metadata.gz.sig +1 -2
@@ -0,0 +1,39 @@
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 Datastores
8
+ module Mongo
9
+ module Obfuscator
10
+
11
+ WHITELIST = [:operation].freeze
12
+
13
+ def self.obfuscate_statement(source, whitelist=WHITELIST)
14
+ obfuscated = {}
15
+ source.each do |key, value|
16
+ if whitelist.include?(key)
17
+ obfuscated[key] = value
18
+ else
19
+ obfuscated[key] = obfuscate_value(value, whitelist)
20
+ end
21
+ end
22
+
23
+ obfuscated
24
+ end
25
+
26
+ def self.obfuscate_value(value, whitelist)
27
+ if value.is_a?(Hash)
28
+ obfuscate_statement(value, whitelist)
29
+ elsif value.is_a?(Array)
30
+ value.map {|v| obfuscate_value(v, whitelist)}
31
+ else
32
+ '?'
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,52 @@
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
+ require 'new_relic/agent/datastores/mongo/obfuscator'
6
+
7
+ module NewRelic
8
+ module Agent
9
+ module Datastores
10
+ module Mongo
11
+ module StatementFormatter
12
+
13
+ PLAINTEXT_KEYS = [
14
+ :database,
15
+ :collection,
16
+ :operation,
17
+ :fields,
18
+ :skip,
19
+ :limit,
20
+ :order
21
+ ]
22
+
23
+ OBFUSCATE_KEYS = [
24
+ :selector
25
+ ]
26
+
27
+ def self.format(statement)
28
+ return nil unless NewRelic::Agent.config[:'mongo.capture_queries']
29
+
30
+ result = {}
31
+ PLAINTEXT_KEYS.each do |key|
32
+ result[key] = statement[key] if statement.key?(key)
33
+ end
34
+
35
+ OBFUSCATE_KEYS.each do |key|
36
+ if statement.key?(key)
37
+ obfuscated = obfuscate(statement[key])
38
+ result[key] = obfuscated if obfuscated
39
+ end
40
+ end
41
+ result
42
+ end
43
+
44
+ def self.obfuscate(statement)
45
+ statement = Obfuscator.obfuscate_statement(statement) if NewRelic::Agent.config[:'mongo.obfuscate_queries']
46
+ statement
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,55 @@
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
+ class Harvester
8
+ attr_accessor :starting_pid
9
+
10
+ # Inject target for after_fork call to avoid spawning thread in tests
11
+ def initialize(events, after_forker=NewRelic::Agent)
12
+ @starting_pid = Process.pid
13
+ @after_forker = after_forker
14
+ @lock = Mutex.new
15
+
16
+ if events
17
+ events.subscribe(:start_transaction, &method(:on_transaction))
18
+ end
19
+ end
20
+
21
+ def on_transaction(*_)
22
+ return unless restart_in_children_enabled? && needs_restart?
23
+
24
+ needs_thread_start = false
25
+ @lock.synchronize do
26
+ needs_thread_start = needs_restart?
27
+ mark_started
28
+ end
29
+
30
+ if needs_thread_start
31
+ restart_harvest_thread
32
+ end
33
+ end
34
+
35
+ def mark_started(pid = Process.pid)
36
+ @starting_pid = pid
37
+ end
38
+
39
+ def needs_restart?(pid = Process.pid)
40
+ @starting_pid != pid
41
+ end
42
+
43
+ def restart_in_children_enabled?
44
+ NewRelic::Agent.config[:restart_thread_in_children]
45
+ end
46
+
47
+ def restart_harvest_thread
48
+ # Daemonize reports thread as still alive when it isn't... whack!
49
+ NewRelic::Agent.instance.instance_variable_set(:@worker_thread, nil)
50
+ @after_forker.after_fork(:force_reconnect => true)
51
+ end
52
+
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,139 @@
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
+ DependencyDetection.defer do
6
+ named :mongo
7
+
8
+ depends_on do
9
+ if defined?(::Mongo) && defined?(::Mongo::Logging)
10
+ true
11
+ else
12
+ if defined?(::Mongo)
13
+ NewRelic::Agent.logger.info 'Mongo instrumentation requires Mongo::Logging'
14
+ end
15
+
16
+ false
17
+ end
18
+ end
19
+
20
+ depends_on do
21
+ require 'new_relic/agent/datastores/mongo'
22
+ NewRelic::Agent::Datastores::Mongo.is_supported_version?
23
+ end
24
+
25
+ executes do
26
+ NewRelic::Agent.logger.info 'Installing Mongo instrumentation'
27
+ install_mongo_instrumentation
28
+ end
29
+
30
+ def install_mongo_instrumentation
31
+ instrument_mongo_logging
32
+ instrument_save
33
+ instrument_ensure_index
34
+ end
35
+
36
+ def instrument_mongo_logging
37
+ ::Mongo::Logging.class_eval do
38
+ include NewRelic::Agent::MethodTracer
39
+ require 'new_relic/agent/datastores/mongo/metric_generator'
40
+ require 'new_relic/agent/datastores/mongo/statement_formatter'
41
+
42
+ def instrument_with_new_relic_trace(name, payload = {}, &block)
43
+ metrics = NewRelic::Agent::Datastores::Mongo::MetricGenerator.generate_metrics_for(name, payload)
44
+
45
+ trace_execution_scoped(metrics) do
46
+ t0 = Time.now
47
+ result = instrument_without_new_relic_trace(name, payload, &block)
48
+
49
+ payload[:operation] = name
50
+ statement = NewRelic::Agent::Datastores::Mongo::StatementFormatter.format(payload)
51
+ if statement
52
+ NewRelic::Agent.instance.transaction_sampler.notice_nosql_statement(statement, (Time.now - t0).to_f)
53
+ end
54
+
55
+ result
56
+ end
57
+ end
58
+
59
+ ::Mongo::Collection.class_eval { include Mongo::Logging; }
60
+ ::Mongo::Connection.class_eval { include Mongo::Logging; }
61
+ ::Mongo::Cursor.class_eval { include Mongo::Logging; }
62
+
63
+ alias_method :instrument_without_new_relic_trace, :instrument
64
+ alias_method :instrument, :instrument_with_new_relic_trace
65
+ end
66
+ end
67
+
68
+ def instrument_save
69
+ ::Mongo::Collection.class_eval do
70
+ include NewRelic::Agent::MethodTracer
71
+ require 'new_relic/agent/datastores/mongo/metric_generator'
72
+ require 'new_relic/agent/datastores/mongo/statement_formatter'
73
+
74
+ def save_with_new_relic_trace(doc, opts = {}, &block)
75
+ metrics = NewRelic::Agent::Datastores::Mongo::MetricGenerator.generate_metrics_for(:save, { :collection => self.name })
76
+
77
+ trace_execution_scoped(metrics) do
78
+ t0 = Time.now
79
+
80
+ transaction_state = NewRelic::Agent::TransactionState.get
81
+ transaction_state.push_traced(false)
82
+
83
+ begin
84
+ result = save_without_new_relic_trace(doc, opts, &block)
85
+ ensure
86
+ transaction_state.pop_traced
87
+ end
88
+
89
+ doc[:operation] = :save
90
+ statement = NewRelic::Agent::Datastores::Mongo::StatementFormatter.format(doc)
91
+ if statement
92
+ NewRelic::Agent.instance.transaction_sampler.notice_nosql_statement(statement, (Time.now - t0).to_f)
93
+ end
94
+
95
+ result
96
+ end
97
+ end
98
+
99
+ alias_method :save_without_new_relic_trace, :save
100
+ alias_method :save, :save_with_new_relic_trace
101
+ end
102
+ end
103
+
104
+ def instrument_ensure_index
105
+ ::Mongo::Collection.class_eval do
106
+ include NewRelic::Agent::MethodTracer
107
+ require 'new_relic/agent/datastores/mongo/metric_generator'
108
+ require 'new_relic/agent/datastores/mongo/statement_formatter'
109
+
110
+ def ensure_index_with_new_relic_trace(spec, opts = {}, &block)
111
+ metrics = NewRelic::Agent::Datastores::Mongo::MetricGenerator.generate_metrics_for(:ensureIndex, { :collection => self.name })
112
+
113
+ trace_execution_scoped(metrics) do
114
+ t0 = Time.now
115
+
116
+ transaction_state = NewRelic::Agent::TransactionState.get
117
+ transaction_state.push_traced(false)
118
+
119
+ begin
120
+ result = save_without_new_relic_trace(spec, opts, &block)
121
+ ensure
122
+ transaction_state.pop_traced
123
+ end
124
+
125
+ spec[:operation] = :ensureIndex
126
+ statement = NewRelic::Agent::Datastores::Mongo::StatementFormatter.format(spec)
127
+ if statement
128
+ NewRelic::Agent.instance.transaction_sampler.notice_nosql_statement(statement, (Time.now - t0).to_f)
129
+ end
130
+
131
+ result
132
+ end
133
+ end
134
+
135
+ alias_method :ensure_index_without_new_relic_trace, :ensure_index
136
+ alias_method :ensure_index, :ensure_index_with_new_relic_trace
137
+ end
138
+ end
139
+ end
@@ -17,20 +17,15 @@ DependencyDetection.defer do
17
17
 
18
18
  executes do
19
19
  class Net::HTTP
20
- # Instrument outgoing HTTP requests
21
- #
22
- # If request is called when not the connection isn't started, request
23
- # will call back into itself (via a start block).
24
- #
25
- # Don't tracing until the inner call then to avoid double-counting.
26
20
  def request_with_newrelic_trace(request, *args, &block)
27
- if started?
28
- wrapped_request = NewRelic::Agent::HTTPClients::NetHTTPRequest.new(self, request)
29
- NewRelic::Agent::CrossAppTracing.trace_http_request( wrapped_request ) do
21
+ wrapped_request = NewRelic::Agent::HTTPClients::NetHTTPRequest.new(self, request)
22
+
23
+ NewRelic::Agent::CrossAppTracing.trace_http_request( wrapped_request ) do
24
+ # RUBY-1244 Disable further tracing in request to avoid double
25
+ # counting if connection wasn't started (which calls request again).
26
+ NewRelic::Agent.disable_all_tracing do
30
27
  request_without_newrelic_trace( request, *args, &block )
31
28
  end
32
- else
33
- request_without_newrelic_trace( request, *args, &block )
34
29
  end
35
30
  end
36
31
 
@@ -32,12 +32,9 @@ module NewRelic
32
32
  {
33
33
  :type => :ruby,
34
34
  :name => "Rubinius",
35
- :experimental=> ["~> 2.1.1"],
35
+ :supported => ["~> 2.2.1"],
36
36
  :url => "http://rubini.us",
37
- :feed => "http://rubini.us/feed/atom.xml",
38
- :notes => [
39
- "Support for Rubinius 2.x is considered experimental.",
40
- "Consider using the latest for best results, and watch http://docs.newrelic.com/docs/ruby/rubinius for updates."]
37
+ :feed => "http://rubini.us/feed/atom.xml"
41
38
  },
42
39
 
43
40
  # App servers
@@ -141,6 +138,13 @@ module NewRelic
141
138
  :url => "https://rubygems.org/gems/sequel",
142
139
  :feed => "https://rubygems.org/gems/sequel/versions.atom"
143
140
  },
141
+ :mongo =>
142
+ {
143
+ :type => :database,
144
+ :supported => ["~>1.8.0", "~>1.9.0"],
145
+ :url => "https://rubygems.org/gems/mongo",
146
+ :feed => "https://rubygems.org/gems/mongo/versions.atom"
147
+ },
144
148
 
145
149
  # Background Jobs
146
150
  :resque =>
@@ -226,6 +226,10 @@ module NewRelic
226
226
  notice_extra_data(key, duration, :key)
227
227
  end
228
228
 
229
+ def notice_nosql_statement(statement, duration)
230
+ notice_extra_data(statement, duration, :statement)
231
+ end
232
+
229
233
  # Set parameters on the current segment.
230
234
  def add_segment_parameters( params )
231
235
  return unless builder
@@ -12,7 +12,7 @@ module NewRelic
12
12
 
13
13
  MAJOR = 3
14
14
  MINOR = 7
15
- TINY = 0
15
+ TINY = 1
16
16
 
17
17
  begin
18
18
  require File.join(File.dirname(__FILE__), 'build')
@@ -33,7 +33,7 @@ namespace :newrelic do
33
33
  def write_versions(title, type, erb, suppress_versions = false)
34
34
  anchor = title.downcase.gsub(" ", "_")
35
35
  versions = versions_for_type(type)
36
- puts erb.result(binding)
36
+ puts erb.result(binding).gsub(/^ *$/, '')
37
37
  end
38
38
 
39
39
  VersionStruct = Struct.new(:name, :supported, :deprecated, :experimental, :notes)
data/test/agent_helper.rb CHANGED
@@ -205,6 +205,10 @@ unless defined?( assert_false )
205
205
  end
206
206
  end
207
207
 
208
+ unless defined? ( refute )
209
+ alias refute assert_false
210
+ end
211
+
208
212
  # Mock up a transaction for testing purposes, optionally specifying a name and
209
213
  # transaction type. The given block will be executed within the context of the
210
214
  # dummy transaction.
@@ -8,6 +8,9 @@ gem 'rack-test'
8
8
 
9
9
  platforms :rbx do
10
10
  gem "rubysl"
11
+ gem "rubysl-test-unit"
12
+ gem "rubysl-json"
13
+ gem "psych"
11
14
  gem "racc" # https://github.com/rubinius/rubinius/issues/2632
12
15
  end
13
16
 
@@ -14,7 +14,7 @@ platforms :jruby do
14
14
  gem "jruby-openssl"
15
15
  end
16
16
 
17
- platforms :mri_19, :mri_20, :rbx do
17
+ platforms :ruby do
18
18
  gem "mysql"
19
19
  gem "sqlite3-ruby"
20
20
  gem "sqlite3"
@@ -22,6 +22,10 @@ end
22
22
 
23
23
  platforms :rbx do
24
24
  gem "rubysl"
25
+ gem "rubysl-json"
26
+ # If we don't skip the require here, test-unit tries to install its at_exit
27
+ # hook and run when we run our rake task to create the test DB.
28
+ gem "rubysl-test-unit", :require => false
25
29
  gem "racc" # https://github.com/rubinius/rubinius/issues/2632
26
30
  end
27
31
 
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'http_parser.rb'
@@ -0,0 +1,82 @@
1
+ ## flaky_proxy
2
+
3
+ `flaky_proxy` helps you simulate failures in an HTTP service in a configurable
4
+ way, without modifying the underlying service.
5
+
6
+ ## Usage
7
+
8
+ ```
9
+ flaky_proxy -l 8888 -t 8081 rules
10
+ ```
11
+
12
+ Where `rules` is a file with the following content:
13
+
14
+ ```
15
+ match /foo/ { close }
16
+
17
+ match /bar/ do
18
+ close
19
+ pass
20
+ end
21
+
22
+ match /baz/ do
23
+ respond :status => 503
24
+ respond :body => '{ "error": "bad things happened" }'
25
+ pass
26
+ end
27
+
28
+ match /slowdown/ do
29
+ delay 10
30
+ end
31
+ ```
32
+
33
+ This will instruct `flaky_proxy` to listen for incoming connections on port
34
+ 8888, and forward HTTP requests recieved on this port on to the server running
35
+ on localhost on port 8081.
36
+
37
+ Incoming requests will be evaluated against the match rules specified in the
38
+ `rules` file that you pass to `flaky_proxy`. Each call to `match` takes a Regexp
39
+ and a block.
40
+
41
+ The Regexp is evaluated against the URL on the incoming request to
42
+ determine whether the rule matches a given request. Each request will be handled
43
+ by the *first* matching rule (or the default rule if no matches are found).
44
+
45
+ The block passed to `match` should contain a sequence of *actions* for handling
46
+ matching requests. Actions will be applied in sequence (the first matching
47
+ request will get the first action, the second will get the second, and so on).
48
+ Once all of the actions have been evaluated, the final action in the block will
49
+ continue to be used.
50
+
51
+ The `rules` file will be watched for changes automatically, and the rules will
52
+ be potentially reloaded each time the proxy accepts a new connection.
53
+
54
+ If the `rules` file is omitted, the all requests will be transparently proxied to the backend server.
55
+
56
+ ### Available Actions
57
+
58
+ #### pass
59
+
60
+ Pass the request on to the backend server without modification.
61
+
62
+ #### close
63
+
64
+ Close the TCP connection from the client before forwarding it on to the backend server.
65
+
66
+ #### respond(response_spec)
67
+
68
+ Respond to the client with a canned response, instead of forwarding the request on to the backend server. `response_spec` should be a `Hash` describing the canned response to be sent to the client. Recognized keys in the `response_spec` are:
69
+
70
+ * `:status` - A `Fixnum` with the HTTP status code. Default: 200.
71
+ * `:headers` - A `Hash` with response headers. Default: the `Content-Length` header will be automatically set based on the response body length.
72
+ * `:body` - A `String` containing the HTTP response body. Default = `''`.
73
+
74
+ #### delay(amount)
75
+
76
+ Delay for `amount` seconds before forwarding the request on to the backend server.
77
+
78
+ ## Caveats
79
+
80
+ * Totally single-threaded and non-evented, therefore cannot handle multiple client connections at once.
81
+ * Errors introduced in the rules file will likely the process to crash instead of just printing an error.
82
+ * Almost certainly doesn't handle string encodings correctly