newrelic_rpm 3.6.0.74.beta → 3.6.0.78

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. data.tar.gz.sig +1 -2
  2. data/CHANGELOG +18 -0
  3. data/Gemfile +3 -3
  4. data/Rakefile +8 -0
  5. data/lib/new_relic/agent/agent.rb +4 -9
  6. data/lib/new_relic/agent/agent_logger.rb +1 -2
  7. data/lib/new_relic/agent/audit_logger.rb +7 -3
  8. data/lib/new_relic/agent/busy_calculator.rb +3 -3
  9. data/lib/new_relic/agent/configuration/server_source.rb +23 -10
  10. data/lib/new_relic/agent/cross_app_monitor.rb +1 -0
  11. data/lib/new_relic/agent/database.rb +2 -0
  12. data/lib/new_relic/agent/error_collector.rb +1 -1
  13. data/lib/new_relic/agent/instrumentation/active_record.rb +14 -13
  14. data/lib/new_relic/agent/instrumentation/active_record_helper.rb +72 -0
  15. data/lib/new_relic/agent/instrumentation/rails4/action_controller.rb +0 -76
  16. data/lib/new_relic/agent/instrumentation/rails4/action_view.rb +138 -0
  17. data/lib/new_relic/agent/instrumentation/rails4/active_record.rb +108 -0
  18. data/lib/new_relic/agent/null_logger.rb +15 -0
  19. data/lib/new_relic/agent/stats.rb +1 -0
  20. data/lib/new_relic/agent/stats_engine/transactions.rb +4 -4
  21. data/lib/new_relic/control/frameworks/rails.rb +0 -37
  22. data/lib/new_relic/control/frameworks/rails3.rb +0 -17
  23. data/lib/new_relic/control/instance_methods.rb +1 -2
  24. data/lib/new_relic/environment_report.rb +159 -0
  25. data/lib/new_relic/helper.rb +4 -0
  26. data/lib/new_relic/local_environment.rb +0 -161
  27. data/lib/newrelic_rpm.rb +1 -1
  28. data/test/multiverse/lib/multiverse/suite.rb +7 -0
  29. data/test/multiverse/suites/active_record/Envfile +0 -1
  30. data/test/multiverse/suites/agent_only/key_transactions_test.rb +22 -12
  31. data/test/multiverse/suites/rails/Envfile +8 -1
  32. data/test/multiverse/suites/rails/app.rb +7 -2
  33. data/test/multiverse/suites/rails/error_tracing_test.rb +28 -16
  34. data/test/multiverse/suites/rails/view_instrumentation_test.rb +8 -2
  35. data/test/new_relic/agent/agent/connect_test.rb +5 -8
  36. data/test/new_relic/agent/agent/start_test.rb +3 -1
  37. data/test/new_relic/agent/agent_logger_test.rb +8 -8
  38. data/test/new_relic/agent/agent_test.rb +0 -6
  39. data/test/new_relic/agent/agent_test_controller_test.rb +18 -14
  40. data/test/new_relic/agent/audit_logger_test.rb +12 -0
  41. data/test/new_relic/agent/configuration/server_source_test.rb +48 -0
  42. data/test/new_relic/agent/cross_app_monitor_test.rb +8 -0
  43. data/test/new_relic/agent/instrumentation/action_view_subscriber_test.rb +254 -0
  44. data/test/new_relic/agent/instrumentation/active_record_helper_test.rb +52 -0
  45. data/test/new_relic/agent/instrumentation/active_record_instrumentation_test.rb +32 -51
  46. data/test/new_relic/agent/instrumentation/active_record_subscriber_test.rb +132 -0
  47. data/test/new_relic/agent/instrumentation/net_instrumentation_test.rb +20 -0
  48. data/test/new_relic/agent_test.rb +0 -8
  49. data/test/new_relic/control_test.rb +1 -2
  50. data/test/new_relic/dispatcher_test.rb +1 -5
  51. data/test/new_relic/environment_report_test.rb +89 -0
  52. data/test/new_relic/license_test.rb +1 -3
  53. data/test/new_relic/load_test.rb +5 -1
  54. data/test/new_relic/local_environment_test.rb +0 -27
  55. data/test/new_relic/rack/browser_monitoring_test.rb +1 -0
  56. data/test/script/ci.sh +19 -18
  57. data/test/test_helper.rb +15 -3
  58. metadata +42 -3
  59. metadata.gz.sig +0 -0
data.tar.gz.sig CHANGED
@@ -1,2 +1 @@
1
- ,Ŷ�Ĩ���)FlќU桚���se���.,�–���\=C9f1 ���b�����cN[w7��Fqm/2)ن���3�'���nY�����2 j2RtVgp���lO��؜�C�h���?@$�w_���h3Bf��
2
- m���}�s�w��׏���ܮ)`�uŸ��]q�� ���Zĥ"q��h�6��K��^�XT�l�Aʡ��y
1
+ !�3��ԑ#$��q{�+�ŎtjC����3(��O���H6KDHe ��b0��].}�2���`�TrIt��"��Q���}#Ŷ��Pq� ��w5Z&n��]NX�)ULxH��eL-)J ��l��>f��1@ͮ18#�t@j�9��u{��38���U�`&*�uaL<�^~a"��T{;���s$9 ���U ~PD���
data/CHANGELOG CHANGED
@@ -9,6 +9,18 @@
9
9
  Sidekiq jobs will automatically show up in the Background tasks on New Relic
10
10
  similar to Resque and Delayed::Job tasks.
11
11
 
12
+ * Improved thread safety
13
+
14
+ The primary metrics data structures in the Ruby agent are now thread safe.
15
+ This should provide better reliability for the agent under JRuby and threaded
16
+ scenarios such as Sidekiq or Puma.
17
+
18
+ * More robust environment report
19
+
20
+ The agent's analysis of the local environment (e.g. OS, Processors, loaded
21
+ gems) will now work in a wider variety of app environments, including
22
+ Sinatra.
23
+
12
24
  * Experimental Rainbows! support
13
25
 
14
26
  The Ruby agent now automatically detects and instruments the Rainbows! web
@@ -17,6 +29,12 @@
17
29
 
18
30
  Thanks to Joseph Chen for the contribution.
19
31
 
32
+ * Fix a potential file descriptor leak in Resque instrumentation
33
+
34
+ A file descriptor leak that occurred when DontPerform exceptions were used to
35
+ abort processing of a job has been fixed. This should allow the Resque
36
+ instrumentation work correctly with the resque-lonely_job gem.
37
+
20
38
  ## v3.5.8 ##
21
39
 
22
40
  * Key Transactions
data/Gemfile CHANGED
@@ -1,12 +1,12 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
3
  group :development do
4
- # require 0.9.2.2.
4
+ # require 0.9.6.
5
5
  # There's problems with the test task in rake 10
6
6
  # https://github.com/jimweirich/rake/issues/144
7
- gem 'rake', '0.9.2.2'
7
+ gem 'rake', '0.9.6'
8
8
  if RUBY_VERSION > '1.9.0'
9
- gem 'mocha', '~>0.13.0'
9
+ gem 'mocha', '~>0.13.0', :require => false
10
10
  else
11
11
  gem 'mocha', '~>0.12.0'
12
12
  end
data/Rakefile CHANGED
@@ -11,6 +11,14 @@ namespace :test do
11
11
  desc "Run all tests"
12
12
  task :all => %w{newrelic multiverse}
13
13
 
14
+ begin
15
+ require 'test_bisect'
16
+ TestBisect::BisectTask.new do |t|
17
+ t.test_task_name = 'test:newrelic'
18
+ end
19
+ rescue LoadError
20
+ end
21
+
14
22
  agent_home = File.expand_path(File.dirname(__FILE__))
15
23
 
16
24
  desc "Run functional test suite for newrelic"
@@ -15,6 +15,7 @@ require 'new_relic/agent/database'
15
15
  require 'new_relic/agent/thread_profiler'
16
16
  require 'new_relic/agent/event_listener'
17
17
  require 'new_relic/agent/cross_app_monitor'
18
+ require 'new_relic/environment_report'
18
19
 
19
20
  module NewRelic
20
21
  module Agent
@@ -44,7 +45,6 @@ module NewRelic
44
45
 
45
46
  @last_harvest_time = Time.now
46
47
  @obfuscator = lambda {|sql| NewRelic::Agent::Database.default_sql_obfuscator(sql) }
47
- @forked = false
48
48
 
49
49
  # FIXME: temporary work around for RUBY-839
50
50
  if Agent.config[:monitor_mode]
@@ -183,7 +183,6 @@ module NewRelic
183
183
  # connection, this tells me to only try it once so this method returns
184
184
  # quickly if there is some kind of latency with the server.
185
185
  def after_fork(options={})
186
- @forked = true
187
186
  Agent.config.apply_config(NewRelic::Agent::Configuration::ManualSource.new(options), 1)
188
187
 
189
188
  if channel_id = options[:report_to_channel]
@@ -213,10 +212,6 @@ module NewRelic
213
212
  @stats_engine.start_sampler_thread
214
213
  end
215
214
 
216
- def forked?
217
- @forked
218
- end
219
-
220
215
  # True if we have initialized and completed 'start'
221
216
  def started?
222
217
  @started
@@ -695,7 +690,7 @@ module NewRelic
695
690
  # Checks whether we should send environment info, and if so,
696
691
  # returns the snapshot from the local environment
697
692
  def environment_for_connect
698
- Agent.config[:send_environment_info] ? Control.instance.local_env.snapshot : []
693
+ Agent.config[:send_environment_info] ? Array(EnvironmentReport.new) : []
699
694
  end
700
695
 
701
696
  # Initializes the hash of settings that we send to the
@@ -745,7 +740,7 @@ module NewRelic
745
740
  end
746
741
 
747
742
  ::NewRelic::Agent.logger.debug "Server provided config: #{config_data.inspect}"
748
- server_config = NewRelic::Agent::Configuration::ServerSource.new(config_data)
743
+ server_config = NewRelic::Agent::Configuration::ServerSource.new(config_data, Agent.config)
749
744
  Agent.config.apply_config(server_config, 1)
750
745
  log_connection!(config_data) if @service
751
746
 
@@ -1030,7 +1025,7 @@ module NewRelic
1030
1025
  end
1031
1026
  raise e
1032
1027
  ensure
1033
- NewRelic::Agent::Database.close_connections unless forked?
1028
+ NewRelic::Agent::Database.close_connections
1034
1029
  duration = (Time.now - now).to_f
1035
1030
  @stats_engine.record_metrics('Supportability/Harvest', duration)
1036
1031
  end
@@ -82,8 +82,7 @@ module NewRelic
82
82
  end
83
83
 
84
84
  def create_null_logger
85
- null_path = ["/dev/null", "NUL"].detect{|f| File.exists?(f)}
86
- @log = ::Logger.new(null_path)
85
+ @log = NewRelic::Agent::NullLogger.new
87
86
  end
88
87
 
89
88
  def wants_stdout(config)
@@ -41,9 +41,13 @@ module NewRelic
41
41
 
42
42
  def setup_logger
43
43
  path = ensure_log_path
44
- @log = ::Logger.new(path || "/dev/null")
45
- @log.formatter = log_formatter
46
- ::NewRelic::Agent.logger.info("Audit log enabled at '#{path}'") if path
44
+ if path
45
+ @log = ::Logger.new(path)
46
+ @log.formatter = log_formatter
47
+ ::NewRelic::Agent.logger.info("Audit log enabled at '#{path}'")
48
+ else
49
+ @log = NewRelic::Agent::NullLogger.new
50
+ end
47
51
  end
48
52
 
49
53
  def ensure_log_path
@@ -18,7 +18,7 @@ module NewRelic
18
18
 
19
19
  # For testability, add accessors:
20
20
  attr_reader :harvest_start, :accumulator
21
-
21
+
22
22
  # sets up busy calculations based on the start and end of
23
23
  # transactions - used for a rough estimate of what percentage of
24
24
  # wall clock time is spent processing requests
@@ -30,7 +30,7 @@ module NewRelic
30
30
  @entrypoint_stack.push time
31
31
  end
32
32
  end
33
-
33
+
34
34
  # called when a transaction finishes, to add time to the
35
35
  # instance variable accumulator. this is harvested when we send
36
36
  # data to the server
@@ -52,7 +52,7 @@ module NewRelic
52
52
  end
53
53
  end
54
54
  end
55
-
55
+
56
56
  # this returns the size of the entry point stack, which
57
57
  # determines how many transactions are running
58
58
  def busy_count
@@ -6,7 +6,7 @@ module NewRelic
6
6
  module Agent
7
7
  module Configuration
8
8
  class ServerSource < DottedHash
9
- def initialize(hash)
9
+ def initialize(hash, existing_config={})
10
10
  if hash['agent_config']
11
11
  if hash['agent_config']['transaction_tracer.transaction_threshold'] =~ /apdex_f/i
12
12
  # when value is "apdex_f" remove the config and defer to default
@@ -15,19 +15,32 @@ module NewRelic
15
15
  super(hash.delete('agent_config'))
16
16
  end
17
17
 
18
- string_map = [
19
- ['collect_traces', 'transaction_tracer.enabled'],
20
- ['collect_traces', 'slow_sql.enabled'],
21
- ['collect_errors', 'error_collector.enabled']
22
- ].each do |pair|
23
- hash[pair[1]] = hash[pair[0]] if hash[pair[0]] != nil
24
- end
25
-
26
18
  if hash['web_transactions_apdex']
27
19
  self[:web_transactions_apdex] = hash.delete('web_transactions_apdex')
28
20
  end
21
+ apply_feature_gates(hash, existing_config)
22
+
23
+ super(hash)
24
+ end
29
25
 
30
- super
26
+ # These feature gates are not intended to be bullet-proof, but only to
27
+ # avoid the overhead of collecting and transmitting additional data if
28
+ # the user's subscription level precludes its use. The server is the
29
+ # ultimate authority regarding subscription levels, so we expect it to
30
+ # do the real enforcement there.
31
+ def apply_feature_gates(hash, existing_config)
32
+ gated_features = {
33
+ :'transaction_tracer.enabled' => 'collect_traces',
34
+ :'slow_sql.enabled' => 'collect_traces',
35
+ :'error_collector.enabled' => 'collect_errors'
36
+ }
37
+ gated_features.each do |feature, gate_key|
38
+ unless hash[gate_key].nil?
39
+ existing_value = existing_config[feature]
40
+ allowed_by_server = hash[gate_key]
41
+ hash[feature] = (allowed_by_server && existing_value)
42
+ end
43
+ end
31
44
  end
32
45
  end
33
46
  end
@@ -47,6 +47,7 @@ module NewRelic
47
47
  key = key.bytes.to_a if key.respond_to?( :bytes )
48
48
 
49
49
  encoded = ""
50
+ encoded.force_encoding('binary') if encoded.respond_to?( :force_encoding )
50
51
  index = 0
51
52
  text.each_byte do |byte|
52
53
  encoded.concat((byte ^ key[index % key.length].to_i))
@@ -87,7 +87,9 @@ module NewRelic
87
87
  connection = get_connection(config)
88
88
  plan = nil
89
89
  if connection
90
+ start = Time.now
90
91
  plan = process_resultset(connection.execute("EXPLAIN #{statement}"))
92
+ ::NewRelic::Agent.record_metric("Supportability/Database/execute_explain_plan", Time.now - start)
91
93
  end
92
94
  return plan
93
95
  end
@@ -44,7 +44,7 @@ module NewRelic
44
44
  initialize_ignored_errors(ignore_errors)
45
45
  end
46
46
  end
47
-
47
+
48
48
  def initialize_ignored_errors(ignore_errors)
49
49
  @ignore.clear
50
50
  ignore_errors = ignore_errors.split(",") if ignore_errors.is_a? String
@@ -15,16 +15,16 @@ module NewRelic
15
15
  end
16
16
  end
17
17
  end
18
-
18
+
19
19
  def log_with_newrelic_instrumentation(*args, &block)
20
20
  if !NewRelic::Agent.is_execution_traced?
21
21
  return log_without_newrelic_instrumentation(*args, &block)
22
22
  end
23
-
23
+
24
24
  sql, name, binds = args
25
25
  metric = metric_for_name(NewRelic::Helper.correctly_encoded(name)) ||
26
26
  metric_for_sql(NewRelic::Helper.correctly_encoded(sql))
27
-
27
+
28
28
  if !metric
29
29
  log_without_newrelic_instrumentation(*args, &block)
30
30
  else
@@ -44,15 +44,15 @@ module NewRelic
44
44
  end
45
45
  end
46
46
  end
47
-
47
+
48
48
  def remote_service_metric
49
49
  if @config && @config[:adapter]
50
50
  type = @config[:adapter].sub(/\d*/, '')
51
51
  host = @config[:host] || 'localhost'
52
52
  "RemoteService/sql/#{type}/#{host}"
53
- end
53
+ end
54
54
  end
55
-
55
+
56
56
  def metric_for_name(name)
57
57
  if name && (parts = name.split " ") && parts.size == 2
58
58
  model = parts.first
@@ -85,7 +85,7 @@ module NewRelic
85
85
  end
86
86
  metric
87
87
  end
88
-
88
+
89
89
  def rollup_metrics_for(metric)
90
90
  metrics = ["ActiveRecord/all"]
91
91
  metrics << "ActiveRecord/#{$1}" if metric =~ /ActiveRecord\/\w+\/(\w+)/
@@ -98,19 +98,20 @@ end
98
98
 
99
99
  DependencyDetection.defer do
100
100
  @name = :active_record
101
-
101
+
102
102
  depends_on do
103
- defined?(ActiveRecord) && defined?(ActiveRecord::Base)
103
+ defined?(::ActiveRecord) && (!defined?(::ActiveRecord::VERSION) ||
104
+ ::ActiveRecord::VERSION::MAJOR.to_i <= 3)
104
105
  end
105
106
 
106
107
  depends_on do
107
108
  !NewRelic::Agent.config[:disable_activerecord_instrumentation]
108
109
  end
109
-
110
+
110
111
  executes do
111
112
  ::NewRelic::Agent.logger.info 'Installing ActiveRecord instrumentation'
112
113
  end
113
-
114
+
114
115
  executes do
115
116
  if defined?(::Rails) && ::Rails::VERSION::MAJOR.to_i == 3
116
117
  Rails.configuration.after_initialize do
@@ -125,7 +126,7 @@ DependencyDetection.defer do
125
126
  ActiveRecord::ConnectionAdapters::AbstractAdapter.module_eval do
126
127
  include ::NewRelic::Agent::Instrumentation::ActiveRecord
127
128
  end
128
-
129
+
129
130
  ActiveRecord::Base.class_eval do
130
131
  class << self
131
132
  add_method_tracer(:find_by_sql, 'ActiveRecord/#{self.name}/find_by_sql',
@@ -133,6 +134,6 @@ DependencyDetection.defer do
133
134
  add_method_tracer(:transaction, 'ActiveRecord/#{self.name}/transaction',
134
135
  :metric => false)
135
136
  end
136
- end
137
+ end
137
138
  end
138
139
  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
+ module NewRelic
5
+ module Agent
6
+ module Instrumentation
7
+ module ActiveRecordHelper
8
+ module_function
9
+
10
+ def metric_for_name(name)
11
+ return unless name
12
+ parts = name.split(' ')
13
+ if parts.size == 2
14
+ model = parts.first
15
+ operation = parts.last.downcase
16
+ case operation
17
+ when 'load', 'count', 'exists'
18
+ op_name = 'find'
19
+ when 'indexes', 'columns'
20
+ op_name = nil # fall back to DirectSQL
21
+ when 'destroy', 'find', 'save', 'create'
22
+ op_name = operation
23
+ when 'update'
24
+ op_name = 'save'
25
+ else
26
+ if model == 'Join'
27
+ op_name = operation
28
+ end
29
+ end
30
+ "ActiveRecord/#{model}/#{op_name}" if op_name
31
+ end
32
+ end
33
+
34
+ def metric_for_sql(sql)
35
+ metric = NewRelic::Agent::Instrumentation::MetricFrame.database_metric_name
36
+ if metric.nil?
37
+ if sql =~ /^(select|update|insert|delete|show)/i
38
+ # Could not determine the model/operation so let's find a better
39
+ # metric. If it doesn't match the regex, it's probably a show
40
+ # command or some DDL which we'll ignore.
41
+ metric = "Database/SQL/#{$1.downcase}"
42
+ else
43
+ metric = "Database/SQL/other"
44
+ end
45
+ end
46
+ metric
47
+ end
48
+
49
+ # Given a metric name such as "ActiveRecord/model/action" this
50
+ # returns an array of rollup metrics:
51
+ # [ "ActiveRecord/all", "ActiveRecord/action" ]
52
+ # If the metric name is in the form of "ActiveRecord/action"
53
+ # this returns merely: [ "ActiveRecord/all" ]
54
+ def rollup_metrics_for(metric)
55
+ metrics = ["ActiveRecord/all"]
56
+ metrics << "ActiveRecord/#{$1}" if metric =~ /ActiveRecord\/[\w|\:]+\/(\w+)/
57
+ metrics
58
+ end
59
+
60
+ # Given a database adapter name and a database server host
61
+ # this returns a metric name in the form:
62
+ # "RemoteService/sql/adapter/host"
63
+ # Host defaults to "localhost".
64
+ def remote_service_metric(adapter, host)
65
+ host ||= 'localhost'
66
+ type = adapter.sub(/\d*/, '')
67
+ "RemoteService/sql/#{type}/#{host}"
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -40,31 +40,6 @@ module NewRelic
40
40
  end
41
41
 
42
42
  end
43
-
44
- module ActionView
45
- module NewRelic
46
- extend self
47
- def template_metric(identifier, options = {})
48
- if options[:file]
49
- "file"
50
- elsif identifier.nil?
51
- "(unknown)"
52
- elsif identifier.include? '/' # this is a filepath
53
- identifier.split('/')[-2..-1].join('/')
54
- else
55
- identifier
56
- end
57
- end
58
- def render_type(file_path)
59
- file = File.basename(file_path)
60
- if file.starts_with?('_')
61
- return 'Partial'
62
- else
63
- return 'Rendering'
64
- end
65
- end
66
- end
67
- end
68
43
  end
69
44
  end
70
45
  end
@@ -92,54 +67,3 @@ DependencyDetection.defer do
92
67
  end
93
68
  end
94
69
  end
95
-
96
- DependencyDetection.defer do
97
- @name = :rails40_view
98
-
99
- depends_on do
100
- defined?(::Rails) && ::Rails::VERSION::MAJOR.to_i == 4
101
- end
102
-
103
- depends_on do
104
- !NewRelic::Agent.config[:disable_view_instrumentation]
105
- end
106
-
107
- executes do
108
- ::NewRelic::Agent.logger.info 'Installing Rails 4 view instrumentation'
109
- end
110
-
111
- executes do
112
- ActionView::TemplateRenderer.class_eval do
113
- include NewRelic::Agent::MethodTracer
114
- # namespaced helper methods
115
-
116
- def render_with_newrelic(context, options)
117
- @details = extract_details(options) if respond_to? :extract_details, true
118
- identifier = determine_template(options) ? determine_template(options).identifier : nil
119
- str = "View/#{NewRelic::Agent::Instrumentation::Rails4::ActionView::NewRelic.template_metric(identifier, options)}/Rendering"
120
- trace_execution_scoped str do
121
- render_without_newrelic(context, options)
122
- end
123
- end
124
-
125
- alias_method :render_without_newrelic, :render
126
- alias_method :render, :render_with_newrelic
127
- end
128
-
129
- ActionView::PartialRenderer.class_eval do
130
- include NewRelic::Agent::MethodTracer
131
-
132
- def render_with_newrelic(*args, &block)
133
- setup(*args, &block)
134
- identifier = find_partial ? find_partial.identifier : nil
135
- str = "View/#{NewRelic::Agent::Instrumentation::Rails4::ActionView::NewRelic.template_metric(identifier)}/Partial"
136
- trace_execution_scoped str do
137
- render_without_newrelic(*args, &block)
138
- end
139
- end
140
-
141
- alias_method :render_without_newrelic, :render
142
- alias_method :render, :render_with_newrelic
143
- end
144
- end
145
- end