newrelic_rpm 8.6.0 → 8.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +83 -3
  3. data/README.md +1 -1
  4. data/Rakefile +1 -1
  5. data/docker-compose.yml +1 -1
  6. data/lib/new_relic/agent/agent.rb +5 -2
  7. data/lib/new_relic/agent/autostart.rb +13 -10
  8. data/lib/new_relic/agent/configuration/default_source.rb +144 -42
  9. data/lib/new_relic/agent/configuration/environment_source.rb +2 -0
  10. data/lib/new_relic/agent/instrumentation/action_controller_subscriber.rb +2 -2
  11. data/lib/new_relic/agent/instrumentation/active_merchant.rb +14 -0
  12. data/lib/new_relic/agent/instrumentation/acts_as_solr.rb +10 -0
  13. data/lib/new_relic/agent/instrumentation/authlogic.rb +10 -0
  14. data/lib/new_relic/agent/instrumentation/data_mapper.rb +12 -0
  15. data/lib/new_relic/agent/instrumentation/delayed_job_instrumentation.rb +19 -0
  16. data/lib/new_relic/agent/instrumentation/rack/helpers.rb +2 -0
  17. data/lib/new_relic/agent/instrumentation/rainbows_instrumentation.rb +11 -0
  18. data/lib/new_relic/agent/instrumentation/sidekiq.rb +15 -0
  19. data/lib/new_relic/agent/instrumentation/sinatra.rb +21 -11
  20. data/lib/new_relic/agent/instrumentation/sunspot.rb +10 -0
  21. data/lib/new_relic/agent/instrumentation/thread/chain.rb +24 -0
  22. data/lib/new_relic/agent/instrumentation/thread/instrumentation.rb +27 -0
  23. data/lib/new_relic/agent/instrumentation/thread/prepend.rb +22 -0
  24. data/lib/new_relic/agent/instrumentation/thread.rb +20 -0
  25. data/lib/new_relic/agent/pipe_service.rb +1 -2
  26. data/lib/new_relic/agent/stats.rb +48 -23
  27. data/lib/new_relic/agent/tracer.rb +14 -1
  28. data/lib/new_relic/agent/transaction/abstract_segment.rb +2 -1
  29. data/lib/new_relic/agent/transaction/tracing.rb +8 -3
  30. data/lib/new_relic/agent/transaction.rb +24 -4
  31. data/lib/new_relic/agent/transaction_error_primitive.rb +2 -0
  32. data/lib/new_relic/agent/transaction_metrics.rb +5 -4
  33. data/lib/new_relic/agent/vm/mri_vm.rb +13 -1
  34. data/lib/new_relic/control/instrumentation.rb +31 -0
  35. data/lib/new_relic/dependency_detection.rb +1 -1
  36. data/lib/new_relic/language_support.rb +17 -0
  37. data/lib/new_relic/local_environment.rb +2 -0
  38. data/lib/new_relic/supportability_helper.rb +1 -0
  39. data/lib/new_relic/traced_thread.rb +36 -0
  40. data/lib/new_relic/version.rb +1 -1
  41. data/lib/tasks/config.rake +11 -3
  42. data/newrelic.yml +12 -1
  43. data/newrelic_rpm.gemspec +5 -2
  44. metadata +12 -3
@@ -13,6 +13,16 @@ DependencyDetection.defer do
13
13
 
14
14
  executes do
15
15
  ::NewRelic::Agent.logger.info 'Installing Authlogic instrumentation'
16
+ deprecation_msg = 'The instrumentation for Authlogic is deprecated. ' \
17
+ 'It will be removed in version 9.0.0.' \
18
+
19
+ ::NewRelic::Agent.logger.log_once(
20
+ :warn,
21
+ :deprecated_authlogic,
22
+ deprecation_msg
23
+ )
24
+
25
+ ::NewRelic::Agent.record_metric("Supportability/Deprecated/Authlogic", 1)
16
26
  end
17
27
 
18
28
  executes do
@@ -15,6 +15,18 @@ DependencyDetection.defer do
15
15
  executes do
16
16
  ::NewRelic::Agent.logger.info 'Installing DataMapper instrumentation'
17
17
  require 'new_relic/agent/datastores/metric_helper'
18
+
19
+ deprecation_msg = 'The instrumentation for DataMapper is deprecated. ' \
20
+ 'It will be removed in version 9.0.0.' \
21
+ 'Visit https://docs.newrelic.com/docs/apm/agents/ruby-agent/getting-started/ruby-agent-requirements-supported-frameworks ' \
22
+ 'to learn about supported gems.'
23
+
24
+ ::NewRelic::Agent.logger.log_once(
25
+ :warn,
26
+ :deprecated_datamapper,
27
+ deprecation_msg
28
+ )
29
+ ::NewRelic::Agent.record_metric("Supportability/Deprecated/DataMapper", 1)
18
30
  end
19
31
 
20
32
  executes do
@@ -93,4 +93,23 @@ DependencyDetection.defer do
93
93
  chain_instrument ::NewRelic::Agent::Instrumentation::DelayedJob::Chain
94
94
  end
95
95
  end
96
+
97
+ executes do
98
+ next unless delayed_job_version < Gem::Version.new('4.1.0')
99
+ deprecation_msg = 'Instrumentation for DelayedJob versions below 4.1.0 is deprecated.' \
100
+ 'It will stop being monitored in version 9.0.0. ' \
101
+ 'Please upgrade your DelayedJob version to continue receiving full support. ' \
102
+
103
+ ::NewRelic::Agent.logger.log_once(
104
+ :warn,
105
+ :deprecated_delayed_job_version,
106
+ deprecation_msg
107
+ )
108
+
109
+ ::NewRelic::Agent.record_metric("Supportability/Deprecated/DelayedJob", 1)
110
+ end
111
+
112
+ def delayed_job_version
113
+ Gem.loaded_specs['delayed_job'].version if Gem.loaded_specs['delayed_job']
114
+ end
96
115
  end
@@ -20,6 +20,8 @@ module NewRelic::Agent::Instrumentation
20
20
  return false unless defined? ::Puma::Const::PUMA_VERSION
21
21
 
22
22
  version = Gem::Version.new(::Puma::Const::PUMA_VERSION)
23
+ # TODO: MAJOR VERSION - update min_version to 3.9.0
24
+ # min_version = Gem::Version.new('3.9.0')
23
25
  min_version = Gem::Version.new('2.12.0')
24
26
  version >= min_version
25
27
  end
@@ -12,6 +12,17 @@ DependencyDetection.defer do
12
12
  executes do
13
13
  ::NewRelic::Agent.logger.info 'Installing Rainbows instrumentation'
14
14
  ::NewRelic::Agent.logger.info 'Detected Rainbows, please see additional documentation: https://newrelic.com/docs/troubleshooting/im-using-unicorn-and-i-dont-see-any-data'
15
+
16
+ deprecation_msg = 'The dispatcher rainbows is deprecated. It will be removed ' \
17
+ 'in version 9.0.0. Please use a supported dispatcher instead. ' \
18
+ 'Visit https://docs.newrelic.com/docs/apm/agents/ruby-agent/getting-started/ruby-agent-requirements-supported-frameworks for options.'
19
+
20
+ ::NewRelic::Agent.logger.log_once(
21
+ :warn,
22
+ :deprecated_rainbows_dispatcher,
23
+ deprecation_msg
24
+ )
25
+ ::NewRelic::Agent.record_metric("Supportability/Deprecated/Rainbows", 1)
15
26
  end
16
27
 
17
28
  executes do
@@ -99,4 +99,19 @@ DependencyDetection.defer do
99
99
  end
100
100
  end
101
101
  end
102
+
103
+ executes do
104
+ next unless Gem::Version.new(Sidekiq::VERSION) < Gem::Version.new('5.0.0')
105
+ deprecation_msg = 'Instrumentation for Sidekiq versions below 5.0.0 is deprecated.' \
106
+ 'They will stop being monitored in version 9.0.0. ' \
107
+ 'Please upgrade your Sidekiq version to continue receiving full support. '
108
+
109
+ ::NewRelic::Agent.logger.log_once(
110
+ :warn,
111
+ :deprecated_sidekiq_version,
112
+ deprecation_msg
113
+ )
114
+
115
+ ::NewRelic::Agent.record_metric("Supportability/Deprecated/Sidekiq", 1)
116
+ end
102
117
  end
@@ -32,18 +32,28 @@ DependencyDetection.defer do
32
32
  end
33
33
 
34
34
  executes do
35
- if Sinatra::Base.respond_to?(:build)
36
- # These requires are inside an executes block because they require rack, and
37
- # we can't be sure that rack is available when this file is first required.
38
- require 'new_relic/rack/agent_hooks'
39
- require 'new_relic/rack/browser_monitoring'
40
- if use_prepend?
41
- prepend_instrument ::Sinatra::Base.singleton_class, NewRelic::Agent::Instrumentation::Sinatra::Build::Prepend
42
- else
43
- chain_instrument NewRelic::Agent::Instrumentation::Sinatra::Build::Chain
44
- end
35
+ # These requires are inside an executes block because they require rack, and
36
+ # we can't be sure that rack is available when this file is first required.
37
+ require 'new_relic/rack/agent_hooks'
38
+ require 'new_relic/rack/browser_monitoring'
39
+ if use_prepend?
40
+ prepend_instrument ::Sinatra::Base.singleton_class, NewRelic::Agent::Instrumentation::Sinatra::Build::Prepend
45
41
  else
46
- ::NewRelic::Agent.logger.info("Skipping auto-injection of middleware for Sinatra - requires Sinatra 1.2.1+")
42
+ chain_instrument NewRelic::Agent::Instrumentation::Sinatra::Build::Chain
47
43
  end
48
44
  end
45
+
46
+ executes do
47
+ next unless Gem::Version.new(Sinatra::VERSION) < Gem::Version.new('2.0.0')
48
+ deprecation_msg = 'The Ruby Agent is dropping support for Sinatra versions below 2.0.0 ' \
49
+ 'in version 9.0.0. Please upgrade your Sinatra version to continue receiving full compatibility. ' \
50
+
51
+ ::NewRelic::Agent.logger.log_once(
52
+ :warn,
53
+ :deprecated_sinatra_version,
54
+ deprecation_msg
55
+ )
56
+
57
+ ::NewRelic::Agent.record_metric("Supportability/Deprecated/Sinatra", 1)
58
+ end
49
59
  end
@@ -11,6 +11,16 @@ DependencyDetection.defer do
11
11
 
12
12
  executes do
13
13
  ::NewRelic::Agent.logger.info 'Installing Rails Sunspot instrumentation'
14
+ deprecation_msg = 'The instrumentation for Sunspot is deprecated.' \
15
+ ' It will be removed in version 9.0.0.' \
16
+
17
+ ::NewRelic::Agent.logger.log_once(
18
+ :warn,
19
+ :deprecated_sunspot,
20
+ deprecation_msg
21
+ )
22
+
23
+ ::NewRelic::Agent.record_metric("Supportability/Deprecated/Sunspot", 1)
14
24
  end
15
25
 
16
26
  executes do
@@ -0,0 +1,24 @@
1
+ # encoding: utf-8
2
+ # This file is distributed under New Relic's license terms.
3
+ # See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
4
+
5
+ require_relative 'instrumentation'
6
+
7
+ module NewRelic::Agent::Instrumentation
8
+ module MonitoredThread
9
+ module Chain
10
+ def self.instrument!
11
+ ::Thread.class_eval do
12
+ include NewRelic::Agent::Instrumentation::MonitoredThread
13
+
14
+ alias_method :initialize_without_new_relic, :initialize
15
+
16
+ def initialize(*args, &block)
17
+ traced_block = add_thread_tracing(*args, &block)
18
+ initialize_with_newrelic_tracing { initialize_without_new_relic(*args, &traced_block) }
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,27 @@
1
+ # encoding: utf-8
2
+ # This file is distributed under New Relic's license terms.
3
+ # See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
4
+
5
+ module NewRelic
6
+ module Agent
7
+ module Instrumentation
8
+ module MonitoredThread
9
+ attr_reader :nr_parent_thread_id
10
+
11
+ def initialize_with_newrelic_tracing
12
+ @nr_parent_thread_id = ::Thread.current.object_id
13
+ yield
14
+ end
15
+
16
+ def add_thread_tracing(*args, &block)
17
+ return block if skip_tracing?
18
+ NewRelic::Agent::Tracer.thread_block_with_current_transaction(*args, &block)
19
+ end
20
+
21
+ def skip_tracing?
22
+ !NewRelic::Agent.config[:'instrumentation.thread.tracing']
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,22 @@
1
+ # encoding: utf-8
2
+ # This file is distributed under New Relic's license terms.
3
+ # See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
4
+
5
+ require_relative 'instrumentation'
6
+
7
+ module NewRelic
8
+ module Agent
9
+ module Instrumentation
10
+ module MonitoredThread
11
+ module Prepend
12
+ include NewRelic::Agent::Instrumentation::MonitoredThread
13
+
14
+ def initialize(*args, &block)
15
+ traced_block = add_thread_tracing(*args, &block)
16
+ initialize_with_newrelic_tracing { super(*args, &traced_block) }
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,20 @@
1
+ # encoding: utf-8
2
+ # This file is distributed under New Relic's license terms.
3
+ # See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
4
+
5
+ require_relative 'thread/chain'
6
+ require_relative 'thread/prepend'
7
+
8
+ DependencyDetection.defer do
9
+ named :thread
10
+
11
+ executes do
12
+ ::NewRelic::Agent.logger.info 'Installing Thread Instrumentation'
13
+
14
+ if use_prepend?
15
+ prepend_instrument ::Thread, ::NewRelic::Agent::Instrumentation::MonitoredThread::Prepend
16
+ else
17
+ chain_instrument ::NewRelic::Agent::Instrumentation::MonitoredThread::Chain
18
+ end
19
+ end
20
+ end
@@ -10,8 +10,7 @@ module NewRelic
10
10
 
11
11
  def initialize(channel_id)
12
12
  @channel_id = channel_id
13
- @collector = NewRelic::Control::Server.new(:name => 'parent',
14
- :port => 0)
13
+ @collector = NewRelic::Control::Server.new({name: 'parent', port: 0})
15
14
  @pipe = NewRelic::Agent::PipeChannelManager.channels[@channel_id]
16
15
  if @pipe && @pipe.parent_pid != $$
17
16
  @pipe.after_fork_in_child
@@ -4,6 +4,8 @@
4
4
  module NewRelic
5
5
  module Agent
6
6
  class Stats
7
+ SKIP_MARSHALLING = [:@lock]
8
+
7
9
  attr_accessor :call_count
8
10
  attr_accessor :min_call_time
9
11
  attr_accessor :max_call_time
@@ -12,6 +14,7 @@ module NewRelic
12
14
  attr_accessor :sum_of_squares
13
15
 
14
16
  def initialize
17
+ @lock = Mutex.new
15
18
  reset
16
19
  end
17
20
 
@@ -34,12 +37,14 @@ module NewRelic
34
37
  end
35
38
 
36
39
  def merge!(other)
37
- @min_call_time = other.min_call_time if min_time_less?(other)
38
- @max_call_time = other.max_call_time if other.max_call_time > max_call_time
39
- @total_call_time += other.total_call_time
40
- @total_exclusive_time += other.total_exclusive_time
41
- @sum_of_squares += other.sum_of_squares
42
- @call_count += other.call_count
40
+ @lock.synchronize do
41
+ @min_call_time = other.min_call_time if min_time_less?(other)
42
+ @max_call_time = other.max_call_time if other.max_call_time > max_call_time
43
+ @total_call_time += other.total_call_time
44
+ @total_exclusive_time += other.total_exclusive_time
45
+ @sum_of_squares += other.sum_of_squares
46
+ @call_count += other.call_count
47
+ end
43
48
  self
44
49
  end
45
50
 
@@ -78,13 +83,15 @@ module NewRelic
78
83
  # will aggregate all data points collected over a specified period and upload
79
84
  # its data to the NewRelic server
80
85
  def record_data_point(value, exclusive_time = value)
81
- @call_count += 1
82
- @total_call_time += value
83
- @min_call_time = value if value < @min_call_time || @call_count == 1
84
- @max_call_time = value if value > @max_call_time
85
- @total_exclusive_time += exclusive_time
86
-
87
- @sum_of_squares += (value * value)
86
+ @lock.synchronize do
87
+ @call_count += 1
88
+ @total_call_time += value
89
+ @min_call_time = value if value < @min_call_time || @call_count == 1
90
+ @max_call_time = value if value > @max_call_time
91
+ @total_exclusive_time += exclusive_time
92
+
93
+ @sum_of_squares += (value * value)
94
+ end
88
95
  self
89
96
  end
90
97
 
@@ -92,7 +99,7 @@ module NewRelic
92
99
 
93
100
  # increments the call_count by one
94
101
  def increment_count(value = 1)
95
- @call_count += value
102
+ @lock.synchronize { @call_count += value }
96
103
  end
97
104
 
98
105
  # Concerned about implicit usage of inspect relying on stats format, so
@@ -122,17 +129,35 @@ module NewRelic
122
129
  alias_method :apdex_f, :total_exclusive_time
123
130
 
124
131
  def record_apdex(bucket, apdex_t)
125
- case bucket
126
- when :apdex_s then @call_count += 1
127
- when :apdex_t then @total_call_time += 1
128
- when :apdex_f then @total_exclusive_time += 1
132
+ @lock.synchronize do
133
+ case bucket
134
+ when :apdex_s then @call_count += 1
135
+ when :apdex_t then @total_call_time += 1
136
+ when :apdex_f then @total_exclusive_time += 1
137
+ end
138
+ if apdex_t
139
+ @min_call_time = apdex_t
140
+ @max_call_time = apdex_t
141
+ else
142
+ ::NewRelic::Agent.logger.warn("Attempted to set apdex_t to #{apdex_t.inspect}, backtrace = #{caller.join("\n")}")
143
+ end
129
144
  end
130
- if apdex_t
131
- @min_call_time = apdex_t
132
- @max_call_time = apdex_t
133
- else
134
- ::NewRelic::Agent.logger.warn("Attempted to set apdex_t to #{apdex_t.inspect}, backtrace = #{caller.join("\n")}")
145
+ end
146
+
147
+ # Override marshalling methods to exclude @lock from being included in marshalled data
148
+ def marshal_dump
149
+ instance_variables.each_with_object({}) do |name, instance_copy|
150
+ next if SKIP_MARSHALLING.include?(name)
151
+ instance_copy[name] = instance_variable_get(name)
152
+ end
153
+ end
154
+
155
+ def marshal_load(marshalled_data)
156
+ marshalled_data.each do |name, value|
157
+ instance_variable_set(name, value) unless SKIP_MARSHALLING.include?(name)
135
158
  end
159
+ # since the lock is excluded when marshalling, create a new lock when loading marshalled data
160
+ @lock = Mutex.new
136
161
  end
137
162
 
138
163
  protected
@@ -405,6 +405,19 @@ module NewRelic
405
405
 
406
406
  alias_method :tl_clear, :clear_state
407
407
 
408
+ def thread_block_with_current_transaction(*args, &block)
409
+ current_txn = ::Thread.current[:newrelic_tracer_state].current_transaction if ::Thread.current[:newrelic_tracer_state]
410
+ Proc.new do
411
+ begin
412
+ NewRelic::Agent::Tracer.state.current_transaction = current_txn
413
+ segment = NewRelic::Agent::Tracer.start_segment(name: "Ruby/Thread/#{::Thread.current.object_id}")
414
+ block.call(*args) if block.respond_to?(:call)
415
+ ensure
416
+ segment.finish if segment
417
+ end
418
+ end
419
+ end
420
+
408
421
  private
409
422
 
410
423
  def start_and_add_segment segment, parent = nil
@@ -445,7 +458,7 @@ module NewRelic
445
458
  end
446
459
 
447
460
  # Current transaction stack
448
- attr_reader :current_transaction
461
+ attr_accessor :current_transaction
449
462
 
450
463
  # Execution tracing on current thread
451
464
  attr_accessor :untraced
@@ -20,13 +20,14 @@ module NewRelic
20
20
  # after its parent. We will use the optimized exclusive duration
21
21
  # calculation in all other cases.
22
22
  #
23
- attr_reader :start_time, :end_time, :duration, :exclusive_duration, :guid
23
+ attr_reader :start_time, :end_time, :duration, :exclusive_duration, :guid, :starting_thread_id
24
24
  attr_accessor :name, :parent, :children_time, :transaction, :transaction_name
25
25
  attr_writer :record_metrics, :record_scoped_metric, :record_on_finish
26
26
  attr_reader :noticed_error
27
27
 
28
28
  def initialize name = nil, start_time = nil
29
29
  @name = name
30
+ @starting_thread_id = ::Thread.current.object_id
30
31
  @transaction_name = nil
31
32
  @transaction = nil
32
33
  @guid = NewRelic::Agent::GuidGenerator.generate_guid
@@ -6,7 +6,7 @@ module NewRelic
6
6
  module Agent
7
7
  class Transaction
8
8
  module Tracing
9
- attr_reader :current_segment
9
+ attr_reader :current_segment_by_thread
10
10
 
11
11
  def async?
12
12
  @async ||= false
@@ -23,7 +23,7 @@ module NewRelic
23
23
  def add_segment segment, parent = nil
24
24
  segment.transaction = self
25
25
  segment.parent = parent || current_segment
26
- @current_segment = segment
26
+ set_current_segment segment
27
27
  if @segments.length < segment_limit
28
28
  @segments << segment
29
29
  else
@@ -34,7 +34,12 @@ module NewRelic
34
34
  end
35
35
 
36
36
  def segment_complete segment
37
- @current_segment = segment.parent
37
+ # if parent was in another thread, remove the current_segment entry for this thread
38
+ if segment.parent && segment.parent.starting_thread_id != ::Thread.current.object_id
39
+ remove_current_segment_by_thread_id(::Thread.current.object_id)
40
+ else
41
+ set_current_segment segment.parent
42
+ end
38
43
  end
39
44
 
40
45
  def segment_limit
@@ -217,7 +217,8 @@ module NewRelic
217
217
 
218
218
  def initialize(category, options)
219
219
  @nesting_max_depth = 0
220
- @current_segment = nil
220
+ @current_segment_by_thread = {}
221
+ @current_segment_lock = Mutex.new
221
222
  @segments = []
222
223
 
223
224
  self.default_name = options[:transaction_name]
@@ -261,6 +262,25 @@ module NewRelic
261
262
  end
262
263
  end
263
264
 
265
+ def parent_thread_id
266
+ ::Thread.current.nr_parent_thread_id if ::Thread.current.respond_to?(:nr_parent_thread_id)
267
+ end
268
+
269
+ def current_segment
270
+ current_thread_id = ::Thread.current.object_id
271
+ return current_segment_by_thread[current_thread_id] if current_segment_by_thread[current_thread_id]
272
+ return current_segment_by_thread[parent_thread_id] if current_segment_by_thread[parent_thread_id]
273
+ current_segment_by_thread[@starting_thread_id]
274
+ end
275
+
276
+ def set_current_segment(new_segment)
277
+ @current_segment_lock.synchronize { current_segment_by_thread[::Thread.current.object_id] = new_segment }
278
+ end
279
+
280
+ def remove_current_segment_by_thread_id(id)
281
+ @current_segment_lock.synchronize { current_segment_by_thread.delete(id) }
282
+ end
283
+
264
284
  def distributed_tracer
265
285
  @distributed_tracer ||= DistributedTracer.new(self)
266
286
  end
@@ -719,9 +739,9 @@ module NewRelic
719
739
  # Do not call this. Invoke the class method instead.
720
740
  def notice_error(error, options = {}) # :nodoc:
721
741
  # Only the last error is kept
722
- if @current_segment
723
- @current_segment.notice_error error, expected: options[:expected]
724
- options[:span_id] = @current_segment.guid
742
+ if current_segment
743
+ current_segment.notice_error(error, expected: options[:expected])
744
+ options[:span_id] = current_segment.guid
725
745
  end
726
746
 
727
747
  if @exceptions[error]
@@ -63,6 +63,8 @@ module NewRelic
63
63
  append_cat payload, attrs
64
64
  DistributedTraceAttributes.copy_to_hash payload, attrs
65
65
  PayloadMetricMapping.append_mapped_metrics payload[:metrics], attrs
66
+ else
67
+ attrs[PRIORITY_KEY] = rand.round(NewRelic::PRIORITY_PRECISION)
66
68
  end
67
69
 
68
70
  attrs
@@ -13,6 +13,7 @@ module NewRelic
13
13
  DEFAULT_PROC = Proc.new { |hash, name| hash[name] = NewRelic::Agent::Stats.new }
14
14
 
15
15
  def initialize
16
+ @lock = Mutex.new
16
17
  @unscoped = Hash.new(&DEFAULT_PROC)
17
18
  @scoped = Hash.new(&DEFAULT_PROC)
18
19
  end
@@ -42,11 +43,11 @@ module NewRelic
42
43
  end
43
44
 
44
45
  def each_unscoped
45
- @unscoped.each { |name, stats| yield name, stats }
46
+ @lock.synchronize { @unscoped.each { |name, stats| yield name, stats } }
46
47
  end
47
48
 
48
49
  def each_scoped
49
- @scoped.each { |name, stats| yield name, stats }
50
+ @lock.synchronize { @scoped.each { |name, stats| yield name, stats } }
50
51
  end
51
52
 
52
53
  def _record_metrics(names, value, aux, target, &blk)
@@ -54,10 +55,10 @@ module NewRelic
54
55
  case names
55
56
  when Array
56
57
  names.each do |name|
57
- target[name].record(value, aux, &blk)
58
+ @lock.synchronize { target[name].record(value, aux, &blk) }
58
59
  end
59
60
  else
60
- target[names].record(value, aux, &blk)
61
+ @lock.synchronize { target[names].record(value, aux, &blk) }
61
62
  end
62
63
  end
63
64
  end
@@ -49,7 +49,19 @@ module NewRelic
49
49
  end
50
50
 
51
51
  if supports?(:constant_cache_invalidations)
52
- snap.constant_cache_invalidations = RubyVM.stat[:global_constant_state]
52
+ snap.constant_cache_invalidations = gather_constant_cache_invalidations
53
+ end
54
+ end
55
+
56
+ def gather_constant_cache_invalidations
57
+ # Ruby >= 3.2 uses :constant_cache
58
+ # see: https://github.com/ruby/ruby/pull/5433 and https://bugs.ruby-lang.org/issues/18589
59
+ # TODO: now that 3.2+ provides more granual cache invalidation data, should we report it instead of summing?
60
+ if RUBY_VERSION >= '3.2.0'
61
+ RubyVM.stat[:constant_cache].values.sum
62
+ # Ruby < 3.2 uses :global_constant_state
63
+ else
64
+ RubyVM.stat[:global_constant_state]
53
65
  end
54
66
  end
55
67
 
@@ -62,9 +62,40 @@ module NewRelic
62
62
  File.join(instrumentation_path, app.to_s, '*.rb')
63
63
  @instrumentation_files.each { |pattern| load_instrumentation_files pattern }
64
64
  DependencyDetection.detect!
65
+ ruby_22_deprecation
66
+ rails_32_deprecation
65
67
  ::NewRelic::Agent.logger.info "Finished instrumentation"
66
68
  end
67
69
  end
70
+
71
+ def rails_32_deprecation
72
+ return unless defined?(Rails::VERSION) && Gem::Version.new(Rails::VERSION::STRING) <= Gem::Version.new('3.2')
73
+ deprecation_msg = 'The Ruby Agent is dropping support for Rails 3.2 ' \
74
+ 'in a future major release. Please upgrade your Rails version to continue receiving support. ' \
75
+
76
+ Agent.logger.log_once(
77
+ :warn,
78
+ :deprecated_rails_version,
79
+ deprecation_msg
80
+ )
81
+
82
+ ::NewRelic::Agent.record_metric("Supportability/Deprecated/Rails32", 1)
83
+ end
84
+
85
+ def ruby_22_deprecation
86
+ return unless RUBY_VERSION <= '2.2.0'
87
+ deprecation_msg = 'The Ruby Agent is dropping support for Ruby 2.2 ' \
88
+ 'in version 9.0.0. Please upgrade your Ruby version to continue receiving support. ' \
89
+
90
+ ::NewRelic::Agent.logger.log_once(
91
+ :warn,
92
+ :deprecated_ruby_version,
93
+ deprecation_msg
94
+ )
95
+
96
+ ::NewRelic::Agent.record_metric("Supportability/Deprecated/Ruby22", 1)
97
+ end
98
+
68
99
  include Instrumentation
69
100
  end
70
101
  end
@@ -146,7 +146,7 @@ module DependencyDetection
146
146
  !(disabled_configured? || deprecated_disabled_configured?)
147
147
  end
148
148
 
149
- # TODO: Remove in 8.0
149
+ # TODO: MAJOR VERSION
150
150
  # will only return true if a disabled key is found and is truthy
151
151
  def deprecated_disabled_configured?
152
152
  return false if self.name.nil?
@@ -34,6 +34,23 @@ module NewRelic
34
34
  RUBY_ENGINE == 'jruby'
35
35
  end
36
36
 
37
+ # TODO: OLD RUBIES - RUBY_VERSION < 2.6
38
+ #
39
+ # Ruby 2.6 introduced an improved version of `Object.const_get` that
40
+ # respects the full namespace of the input and doesn't just grab the first
41
+ # constant matching the string to the right of the last '::'.
42
+ # Once we drop support for Ruby 2.5 and below, the only value this custom
43
+ # method will provide beyond `Object.const_get` itself is to automatically
44
+ # catch NameError.
45
+ #
46
+ # see: https://github.com/rails/rails/commit/7057ccf6565c1cb5354c1906880119276a9d15c0
47
+ #
48
+ # With Ruby 2.6+, this method can be defined like so:
49
+ # def constantize(constant_as_string_or_symbol)
50
+ # Object.const_get(constant_as_string_or_symbol)
51
+ # rescue NameError
52
+ # end
53
+ #
37
54
  def constantize(const_name)
38
55
  const_name.to_s.sub(/\A::/, '').split('::').inject(Object) do |namespace, name|
39
56
  begin