newrelic_rpm 3.6.0.74.beta → 3.6.0.78

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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