newrelic_rpm 9.16.0 → 9.17.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/.build_ignore +0 -1
  3. data/CHANGELOG.md +51 -3
  4. data/CONTRIBUTING.md +2 -2
  5. data/lib/boot/strap.rb +4 -3
  6. data/lib/new_relic/agent/agent.rb +4 -0
  7. data/lib/new_relic/agent/agent_helpers/connect.rb +3 -0
  8. data/lib/new_relic/agent/agent_helpers/harvest.rb +3 -0
  9. data/lib/new_relic/agent/agent_helpers/shutdown.rb +3 -0
  10. data/lib/new_relic/agent/agent_helpers/start_worker_thread.rb +1 -0
  11. data/lib/new_relic/agent/agent_helpers/startup.rb +7 -0
  12. data/lib/new_relic/agent/aws.rb +54 -3
  13. data/lib/new_relic/agent/configuration/default_source.rb +52 -11
  14. data/lib/new_relic/agent/configuration/yaml_source.rb +6 -1
  15. data/lib/new_relic/agent/database.rb +5 -1
  16. data/lib/new_relic/agent/distributed_tracing.rb +2 -2
  17. data/lib/new_relic/agent/health_check.rb +136 -0
  18. data/lib/new_relic/agent/instrumentation/active_record_helper.rb +5 -1
  19. data/lib/new_relic/agent/instrumentation/active_record_subscriber.rb +8 -4
  20. data/lib/new_relic/agent/instrumentation/aws_sdk_firehose/chain.rb +21 -0
  21. data/lib/new_relic/agent/instrumentation/aws_sdk_firehose/instrumentation.rb +66 -0
  22. data/lib/new_relic/agent/instrumentation/aws_sdk_firehose/prepend.rb +15 -0
  23. data/lib/new_relic/agent/instrumentation/aws_sdk_firehose.rb +22 -0
  24. data/lib/new_relic/agent/instrumentation/aws_sdk_kinesis/chain.rb +21 -0
  25. data/lib/new_relic/agent/instrumentation/aws_sdk_kinesis/instrumentation.rb +91 -0
  26. data/lib/new_relic/agent/instrumentation/aws_sdk_kinesis/prepend.rb +15 -0
  27. data/lib/new_relic/agent/instrumentation/aws_sdk_kinesis.rb +22 -0
  28. data/lib/new_relic/agent/instrumentation/aws_sdk_lambda/instrumentation.rb +8 -9
  29. data/lib/new_relic/agent/instrumentation/dynamodb/instrumentation.rb +7 -1
  30. data/lib/new_relic/agent/instrumentation/grape/instrumentation.rb +0 -3
  31. data/lib/new_relic/agent/instrumentation/resque.rb +7 -1
  32. data/lib/new_relic/agent/instrumentation/sidekiq/extensions/delayed_class.rb +1 -1
  33. data/lib/new_relic/agent/local_log_decorator.rb +12 -2
  34. data/lib/new_relic/agent/new_relic_service.rb +8 -2
  35. data/lib/new_relic/agent/threading/backtrace_node.rb +10 -1
  36. data/lib/new_relic/agent/transaction/message_broker_segment.rb +3 -0
  37. data/lib/new_relic/agent.rb +2 -2
  38. data/lib/new_relic/dependency_detection.rb +1 -8
  39. data/lib/new_relic/version.rb +1 -1
  40. data/newrelic.yml +32 -26
  41. data/test/agent_helper.rb +7 -0
  42. metadata +12 -6
@@ -0,0 +1,136 @@
1
+ # This file is distributed under New Relic's license terms.
2
+ # See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
3
+ # frozen_string_literal: true
4
+
5
+ module NewRelic
6
+ module Agent
7
+ class HealthCheck
8
+ def initialize
9
+ @start_time = nano_time
10
+ @continue = true
11
+ @status = HEALTHY
12
+ # the following assignments may set @continue = false if they are invalid
13
+ set_enabled
14
+ set_delivery_location
15
+ set_frequency
16
+ end
17
+
18
+ HEALTHY = {healthy: true, last_error: 'NR-APM-000', message: 'Healthy'}.freeze
19
+ INVALID_LICENSE_KEY = {healthy: false, last_error: 'NR-APM-001', message: 'Invalid license key (HTTP status code 401)'}.freeze
20
+ MISSING_LICENSE_KEY = {healthy: false, last_error: 'NR-APM-002', message: 'License key missing in configuration'}.freeze
21
+ FORCED_DISCONNECT = {healthy: false, last_error: 'NR-APM-003', message: 'Forced disconnect received from New Relic (HTTP status code 410)'}.freeze
22
+ HTTP_ERROR = {healthy: false, last_error: 'NR-APM-004', message: 'HTTP error response code [%s] recevied from New Relic while sending data type [%s]'}.freeze
23
+ MISSING_APP_NAME = {healthy: false, last_error: 'NR-APM-005', message: 'Missing application name in agent configuration'}.freeze
24
+ APP_NAME_EXCEEDED = {healthy: false, last_error: 'NR-APM-006', message: 'The maximum number of configured app names (3) exceeded'}.freeze
25
+ PROXY_CONFIG_ERROR = {healthy: false, last_error: 'NR-APM-007', message: 'HTTP Proxy configuration error; response code [%s]'}.freeze
26
+ AGENT_DISABLED = {healthy: false, last_error: 'NR-APM-008', message: 'Agent is disabled via configuration'}.freeze
27
+ FAILED_TO_CONNECT = {healthy: false, last_error: 'NR-APM-009', message: 'Failed to connect to New Relic data collector'}.freeze
28
+ FAILED_TO_PARSE_CONFIG = {healthy: false, last_error: 'NR-APM-010', message: 'Agent config file is not able to be parsed'}.freeze
29
+ SHUTDOWN = {healthy: true, last_error: 'NR-APM-099', message: 'Agent has shutdown'}.freeze
30
+
31
+ def create_and_run_health_check_loop
32
+ return unless health_checks_enabled? && @continue
33
+
34
+ NewRelic::Agent.logger.debug('Agent Control health check conditions met. Starting health checks.')
35
+ NewRelic::Agent.record_metric('Supportability/AgentControl/Health/enabled', 1)
36
+
37
+ Thread.new do
38
+ while @continue
39
+ begin
40
+ sleep @frequency
41
+ write_file
42
+ @continue = false if @status == SHUTDOWN
43
+ rescue StandardError => e
44
+ NewRelic::Agent.logger.error("Aborting Agent Control health check. Error raised: #{e}")
45
+ @continue = false
46
+ end
47
+ end
48
+ end
49
+ end
50
+
51
+ def update_status(status, options = [])
52
+ return unless @continue
53
+
54
+ @status = status.dup
55
+ update_message(options) unless options.empty?
56
+ end
57
+
58
+ def healthy?
59
+ @status == HEALTHY
60
+ end
61
+
62
+ private
63
+
64
+ def set_enabled
65
+ @enabled = if ENV['NEW_RELIC_AGENT_CONTROL_ENABLED'] == 'true'
66
+ true
67
+ else
68
+ NewRelic::Agent.logger.debug('NEW_RELIC_AGENT_CONTROL_ENABLED not true, disabling health checks')
69
+ @continue = false
70
+ false
71
+ end
72
+ end
73
+
74
+ def set_delivery_location
75
+ @delivery_location = if ENV['NEW_RELIC_AGENT_CONTROL_HEALTH_DELIVERY_LOCATION']
76
+ # The spec states file paths for the delivery location will begin with file://
77
+ # This does not create a valid path in Ruby, so remove the prefix when present
78
+ ENV['NEW_RELIC_AGENT_CONTROL_HEALTH_DELIVERY_LOCATION']&.gsub('file://', '')
79
+ else
80
+ # The spec default is 'file:///newrelic/apm/health', but since we're just going to remove it anyway...
81
+ '/newrelic/apm/health'
82
+ end
83
+ end
84
+
85
+ def set_frequency
86
+ @frequency = ENV['NEW_RELIC_AGENT_CONTROL_HEALTH_FREQUENCY'] ? ENV['NEW_RELIC_AGENT_CONTROL_HEALTH_FREQUENCY'].to_i : 5
87
+
88
+ if @frequency <= 0
89
+ NewRelic::Agent.logger.debug('NEW_RELIC_AGENT_CONTROL_HEALTH_FREQUENCY zero or less, disabling health checks')
90
+ @continue = false
91
+ end
92
+ end
93
+
94
+ def contents
95
+ <<~CONTENTS
96
+ healthy: #{@status[:healthy]}
97
+ status: #{@status[:message]}#{last_error}
98
+ start_time_unix_nano: #{@start_time}
99
+ status_time_unix_nano: #{nano_time}
100
+ CONTENTS
101
+ end
102
+
103
+ def last_error
104
+ @status[:healthy] ? '' : "\nlast_error: #{@status[:last_error]}"
105
+ end
106
+
107
+ def nano_time
108
+ Process.clock_gettime(Process::CLOCK_REALTIME, :nanosecond)
109
+ end
110
+
111
+ def file_name
112
+ "health-#{NewRelic::Agent::GuidGenerator.generate_guid(32)}.yml"
113
+ end
114
+
115
+ def write_file
116
+ @file ||= "#{@delivery_location}/#{file_name}"
117
+
118
+ File.write(@file, contents)
119
+ rescue StandardError => e
120
+ NewRelic::Agent.logger.error("Agent Control health check raised an error while writing a file: #{e}")
121
+ @continue = false
122
+ end
123
+
124
+ def health_checks_enabled?
125
+ @enabled && @delivery_location && @frequency > 0
126
+ end
127
+
128
+ def update_message(options)
129
+ @status[:message] = sprintf(@status[:message], *options)
130
+ rescue StandardError => e
131
+ NewRelic::Agent.logger.debug("Error raised while updating Agent Control health check message: #{e}." \
132
+ "options = #{options}, @status[:message] = #{@status[:message]}")
133
+ end
134
+ end
135
+ end
136
+ end
@@ -170,6 +170,9 @@ module NewRelic
170
170
 
171
171
  'sqlite3' => 'SQLite',
172
172
 
173
+ # https://rubygems.org/gems/trilogy
174
+ 'trilogy' => 'MySQL',
175
+
173
176
  # https://rubygems.org/gems/activerecord-jdbcpostgresql-adapter
174
177
  'jdbcmysql' => 'MySQL',
175
178
 
@@ -222,7 +225,8 @@ module NewRelic
222
225
 
223
226
  'postgresql' => :postgres,
224
227
  'jdbcpostgresql' => :postgres,
225
- 'postgis' => :postgres
228
+ 'postgis' => :postgres,
229
+ 'redshift' => :postgres
226
230
  }.freeze
227
231
 
228
232
  DATASTORE_DEFAULT_PORTS = {
@@ -116,10 +116,14 @@ module NewRelic
116
116
  port_path_or_id = nil
117
117
  database = nil
118
118
 
119
- if ActiveRecordHelper::InstanceIdentification.supported_adapter?(config)
120
- host = ActiveRecordHelper::InstanceIdentification.host(config)
121
- port_path_or_id = ActiveRecordHelper::InstanceIdentification.port_path_or_id(config)
122
- database = config && config[:database]
119
+ begin
120
+ if ActiveRecordHelper::InstanceIdentification.supported_adapter?(config)
121
+ host = ActiveRecordHelper::InstanceIdentification.host(config)
122
+ port_path_or_id = ActiveRecordHelper::InstanceIdentification.port_path_or_id(config)
123
+ database = config && config[:database]
124
+ end
125
+ rescue
126
+ NewRelic::Agent.logger.debug("Failed to retrieve ActiveRecord host, port, and database details for adapter: #{config && config[:adapter]}")
123
127
  end
124
128
 
125
129
  segment = Tracer.start_datastore_segment(product: product,
@@ -0,0 +1,21 @@
1
+ # This file is distributed under New Relic's license terms.
2
+ # See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
3
+ # frozen_string_literal: true
4
+
5
+ module NewRelic::Agent::Instrumentation
6
+ module Firehose::Chain
7
+ def self.instrument!
8
+ ::Aws::Firehose::Client.class_eval do
9
+ include NewRelic::Agent::Instrumentation::Firehose
10
+
11
+ NewRelic::Agent::Instrumentation::Firehose::INSTRUMENTED_METHODS.each do |method_name|
12
+ alias_method("#{method_name}_without_new_relic".to_sym, method_name.to_sym)
13
+
14
+ define_method(method_name) do |*args|
15
+ instrument_method_with_new_relic(method_name, *args) { send("#{method_name}_without_new_relic".to_sym, *args) }
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,66 @@
1
+ # This file is distributed under New Relic's license terms.
2
+ # See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
3
+ # frozen_string_literal: true
4
+
5
+ module NewRelic::Agent::Instrumentation
6
+ module Firehose
7
+ INSTRUMENTED_METHODS = %w[
8
+ create_delivery_stream
9
+ delete_delivery_stream
10
+ describe_delivery_stream
11
+ list_delivery_streams
12
+ list_tags_for_delivery_stream
13
+ put_record
14
+ put_record_batch
15
+ start_delivery_stream_encryption
16
+ stop_delivery_stream_encryption
17
+ tag_delivery_stream
18
+ untag_delivery_stream
19
+ update_destination
20
+ ].freeze
21
+
22
+ FIREHOSE = 'Firehose'
23
+ AWS_KINESIS_DELIVERY_STREAMS = 'aws_kinesis_delivery_streams'
24
+
25
+ def instrument_method_with_new_relic(method_name, *args)
26
+ return yield unless NewRelic::Agent::Tracer.tracing_enabled?
27
+
28
+ NewRelic::Agent.record_instrumentation_invocation(FIREHOSE)
29
+
30
+ params = args[0]
31
+ segment = NewRelic::Agent::Tracer.start_segment(name: get_segment_name(method_name, params))
32
+ arn = get_arn(params) if params
33
+ segment&.add_agent_attribute('cloud.resource_id', arn) if arn
34
+
35
+ begin
36
+ NewRelic::Agent::Tracer.capture_segment_error(segment) { yield }
37
+ ensure
38
+ segment&.add_agent_attribute('cloud.platform', AWS_KINESIS_DELIVERY_STREAMS)
39
+ segment&.finish
40
+ end
41
+ end
42
+
43
+ def get_segment_name(method_name, params)
44
+ stream_name = params&.dig(:delivery_stream_name)
45
+ return "#{FIREHOSE}/#{method_name}/#{stream_name}" if stream_name
46
+
47
+ "#{FIREHOSE}/#{method_name}"
48
+ rescue => e
49
+ NewRelic::Agent.logger.warn("Failed to create segment name: #{e}")
50
+ end
51
+
52
+ def nr_account_id
53
+ return @nr_account_id if defined?(@nr_account_id)
54
+
55
+ @nr_account_id = NewRelic::Agent::Aws.get_account_id(config)
56
+ end
57
+
58
+ def get_arn(params)
59
+ stream_arn = params&.dig(:delivery_stream_arn)
60
+ return stream_arn if stream_arn
61
+
62
+ stream_name = params&.dig(:delivery_stream_name)
63
+ NewRelic::Agent::Aws.create_arn(FIREHOSE.downcase, "deliverystream/#{stream_name}", config&.region, nr_account_id) if stream_name
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,15 @@
1
+ # This file is distributed under New Relic's license terms.
2
+ # See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
3
+ # frozen_string_literal: true
4
+
5
+ module NewRelic::Agent::Instrumentation
6
+ module Firehose::Prepend
7
+ include NewRelic::Agent::Instrumentation::Firehose
8
+
9
+ INSTRUMENTED_METHODS.each do |method_name|
10
+ define_method(method_name) do |*args|
11
+ instrument_method_with_new_relic(method_name, *args) { super(*args) }
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,22 @@
1
+ # This file is distributed under New Relic's license terms.
2
+ # See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
3
+ # frozen_string_literal: true
4
+
5
+ require_relative 'aws_sdk_firehose/instrumentation'
6
+ require_relative 'aws_sdk_firehose/chain'
7
+ require_relative 'aws_sdk_firehose/prepend'
8
+
9
+ DependencyDetection.defer do
10
+ named :aws_sdk_firehose
11
+
12
+ depends_on do
13
+ defined?(Aws::Firehose::Client)
14
+ end
15
+ executes do
16
+ if use_prepend?
17
+ prepend_instrument Aws::Firehose::Client, NewRelic::Agent::Instrumentation::Firehose::Prepend
18
+ else
19
+ chain_instrument NewRelic::Agent::Instrumentation::Firehose::Chain
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,21 @@
1
+ # This file is distributed under New Relic's license terms.
2
+ # See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
3
+ # frozen_string_literal: true
4
+
5
+ module NewRelic::Agent::Instrumentation
6
+ module Kinesis::Chain
7
+ def self.instrument!
8
+ ::Aws::Kinesis::Client.class_eval do
9
+ include NewRelic::Agent::Instrumentation::Kinesis
10
+
11
+ NewRelic::Agent::Instrumentation::Kinesis::INSTRUMENTED_METHODS.each do |method_name|
12
+ alias_method("#{method_name}_without_new_relic".to_sym, method_name.to_sym)
13
+
14
+ define_method(method_name) do |*args|
15
+ instrument_method_with_new_relic(method_name, *args) { send("#{method_name}_without_new_relic".to_sym, *args) }
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,91 @@
1
+ # This file is distributed under New Relic's license terms.
2
+ # See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
3
+ # frozen_string_literal: true
4
+
5
+ module NewRelic::Agent::Instrumentation
6
+ module Kinesis
7
+ INSTRUMENTED_METHODS = %w[
8
+ add_tags_to_stream
9
+ create_stream
10
+ decrease_stream_retention_period
11
+ delete_stream
12
+ describe_limits
13
+ describe_stream
14
+ disable_enhanced_monitoring
15
+ enable_enhanced_monitoring
16
+ get_records
17
+ get_shard_iterator
18
+ increase_stream_retention_period
19
+ list_streams
20
+ list_tags_for_stream
21
+ merge_shards
22
+ put_record
23
+ put_records
24
+ remove_tags_from_stream
25
+ split_shard
26
+ update_shard_count
27
+ ].freeze
28
+
29
+ KINESIS = 'Kinesis'
30
+ AWS_KINESIS_DATA_STREAMS = 'aws_kinesis_data_streams'
31
+ MESSAGE_BROKER_SEGMENT_METHODS = %w[put_record put_records get_records].freeze
32
+
33
+ def instrument_method_with_new_relic(method_name, *args)
34
+ return yield unless NewRelic::Agent::Tracer.tracing_enabled?
35
+
36
+ NewRelic::Agent.record_instrumentation_invocation(KINESIS)
37
+ params = args[0]
38
+ arn = get_arn(params) if params
39
+
40
+ if MESSAGE_BROKER_SEGMENT_METHODS.include?(method_name)
41
+ stream_name = get_stream_name(params, arn)
42
+ segment = NewRelic::Agent::Tracer.start_message_broker_segment(
43
+ action: method_name == 'get_records' ? :consume : :produce,
44
+ library: KINESIS,
45
+ destination_type: :stream,
46
+ destination_name: stream_name
47
+ )
48
+ else
49
+ segment = NewRelic::Agent::Tracer.start_segment(name: get_segment_name(method_name, params))
50
+ end
51
+
52
+ segment&.add_agent_attribute('cloud.resource_id', arn) if arn
53
+
54
+ begin
55
+ NewRelic::Agent::Tracer.capture_segment_error(segment) { yield }
56
+ ensure
57
+ segment&.add_agent_attribute('cloud.platform', AWS_KINESIS_DATA_STREAMS)
58
+ segment&.finish
59
+ end
60
+ end
61
+
62
+ def get_segment_name(method_name, params)
63
+ stream_name = params&.dig(:stream_name)
64
+ return "#{KINESIS}/#{method_name}/#{stream_name}" if stream_name
65
+
66
+ "#{KINESIS}/#{method_name}"
67
+ rescue => e
68
+ NewRelic::Agent.logger.warn("Failed to create segment name: #{e}")
69
+ end
70
+
71
+ def get_stream_name(params, arn)
72
+ params&.dig(:stream_name) || arn.split('/').last || 'unknown'
73
+ rescue => e
74
+ NewRelic::Agent.logger.warn("Failed to get stream name: #{e}")
75
+ end
76
+
77
+ def nr_account_id
78
+ return @nr_account_id if defined?(@nr_account_id)
79
+
80
+ @nr_account_id = NewRelic::Agent::Aws.get_account_id(config)
81
+ end
82
+
83
+ def get_arn(params)
84
+ stream_arn = params&.dig(:stream_arn)
85
+ return stream_arn if stream_arn
86
+
87
+ stream_name = params&.dig(:stream_name)
88
+ NewRelic::Agent::Aws.create_arn(KINESIS.downcase, "stream/#{stream_name}", config&.region, nr_account_id) if stream_name
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,15 @@
1
+ # This file is distributed under New Relic's license terms.
2
+ # See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
3
+ # frozen_string_literal: true
4
+
5
+ module NewRelic::Agent::Instrumentation
6
+ module Kinesis::Prepend
7
+ include NewRelic::Agent::Instrumentation::Kinesis
8
+
9
+ INSTRUMENTED_METHODS.each do |method_name|
10
+ define_method(method_name) do |*args|
11
+ instrument_method_with_new_relic(method_name, *args) { super(*args) }
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,22 @@
1
+ # This file is distributed under New Relic's license terms.
2
+ # See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
3
+ # frozen_string_literal: true
4
+
5
+ require_relative 'aws_sdk_kinesis/instrumentation'
6
+ require_relative 'aws_sdk_kinesis/chain'
7
+ require_relative 'aws_sdk_kinesis/prepend'
8
+
9
+ DependencyDetection.defer do
10
+ named :aws_sdk_kinesis
11
+
12
+ depends_on do
13
+ defined?(Aws::Kinesis::Client)
14
+ end
15
+ executes do
16
+ if use_prepend?
17
+ prepend_instrument Aws::Kinesis::Client, NewRelic::Agent::Instrumentation::Kinesis::Prepend
18
+ else
19
+ chain_instrument NewRelic::Agent::Instrumentation::Kinesis::Chain
20
+ end
21
+ end
22
+ end
@@ -63,10 +63,9 @@ module NewRelic::Agent::Instrumentation
63
63
  def generate_segment(action, options = {})
64
64
  function = function_name(options)
65
65
  region = aws_region
66
- account_id = aws_account_id
67
66
  arn = aws_arn(function, region)
68
67
  segment = NewRelic::Agent::Tracer.start_segment(name: "Lambda/#{action}/#{function}")
69
- segment.add_agent_attribute('cloud.account.id', account_id)
68
+ segment.add_agent_attribute('cloud.account.id', nr_account_id)
70
69
  segment.add_agent_attribute('cloud.platform', CLOUD_PLATFORM)
71
70
  segment.add_agent_attribute('cloud.region', region)
72
71
  segment.add_agent_attribute('cloud.resource_id', arn) if arn
@@ -77,18 +76,18 @@ module NewRelic::Agent::Instrumentation
77
76
  (options.fetch(:function_name, nil) if options.respond_to?(:fetch)) || NewRelic::UNKNOWN
78
77
  end
79
78
 
80
- def aws_account_id
81
- return unless self.respond_to?(:config)
82
-
83
- config&.account_id || NewRelic::Agent.config[:'cloud.aws.account_id']
84
- end
85
-
86
79
  def aws_region
87
80
  config&.region if self.respond_to?(:config)
88
81
  end
89
82
 
90
83
  def aws_arn(function, region)
91
- NewRelic::Agent::Aws.create_arn(AWS_SERVICE, "function:#{function}", region)
84
+ NewRelic::Agent::Aws.create_arn(AWS_SERVICE, "function:#{function}", region, nr_account_id)
85
+ end
86
+
87
+ def nr_account_id
88
+ return @nr_account_id if defined?(@nr_account_id)
89
+
90
+ @nr_account_id = NewRelic::Agent::Aws.get_account_id(config)
92
91
  end
93
92
  end
94
93
  end
@@ -49,10 +49,16 @@ module NewRelic::Agent::Instrumentation
49
49
  @nr_captured_request = yield
50
50
  end
51
51
 
52
+ def nr_account_id
53
+ return @nr_account_id if defined?(@nr_account_id)
54
+
55
+ @nr_account_id = NewRelic::Agent::Aws.get_account_id(config)
56
+ end
57
+
52
58
  def get_arn(params)
53
59
  return unless params[:table_name]
54
60
 
55
- NewRelic::Agent::Aws.create_arn(PRODUCT.downcase, "table/#{params[:table_name]}", config&.region)
61
+ NewRelic::Agent::Aws.create_arn(PRODUCT.downcase, "table/#{params[:table_name]}", config&.region, nr_account_id)
56
62
  end
57
63
  end
58
64
  end
@@ -56,10 +56,7 @@ module NewRelic::Agent::Instrumentation
56
56
 
57
57
  def name_transaction(route, class_name, version)
58
58
  txn_name = name_for_transaction(route, class_name, version)
59
- segment_name = "Middleware/Grape/#{class_name}/call"
60
59
  NewRelic::Agent::Transaction.set_default_transaction_name(txn_name, :grape)
61
- txn = NewRelic::Agent::Transaction.tl_current
62
- txn.segments.last.name = segment_name
63
60
  end
64
61
 
65
62
  def name_for_transaction(route, class_name, version)
@@ -19,7 +19,13 @@ DependencyDetection.defer do
19
19
  end
20
20
 
21
21
  executes do
22
- if NewRelic::Agent.config[:'resque.use_ruby_dns'] && NewRelic::Agent.config[:dispatcher] == :resque
22
+ if NewRelic::Agent.config[:'resque.use_ruby_dns'] &&
23
+ NewRelic::Agent.config[:dispatcher] == :resque &&
24
+ # resolv-replace is no longer part of the language in Ruby 3.4.
25
+ # we don't believe this lib is still necessary for Ruby 3.4 users.
26
+ # however, if we receive customer feedback to the contrary, we can find
27
+ # an alternate approach.
28
+ Gem::Version.new(RUBY_VERSION) < Gem::Version.new('3.4')
23
29
  NewRelic::Agent.logger.info('Requiring resolv-replace')
24
30
  require 'resolv'
25
31
  require 'resolv-replace'
@@ -6,7 +6,7 @@
6
6
  # Delayed extensions are disabled by default in Sidekiq 5 and 6 and
7
7
  # were removed entirely in Sidekiq 7.
8
8
  #
9
- # see https://github.com/mperham/sidekiq/issues/5076 for the discussion
9
+ # see https://github.com/sidekiq/sidekiq/issues/5076 for the discussion
10
10
  # of the removal, which includes mentions of alternatives
11
11
  if defined?(Sidekiq::VERSION) && Sidekiq::VERSION < '7.0.0'
12
12
  class Sidekiq::Extensions::DelayedClass
@@ -9,7 +9,7 @@ module NewRelic
9
9
  extend self
10
10
 
11
11
  def decorate(message)
12
- return message unless decorating_enabled?
12
+ return message if !decorating_enabled? || message.nil?
13
13
 
14
14
  metadata = NewRelic::Agent.linking_metadata
15
15
 
@@ -37,7 +37,17 @@ module NewRelic
37
37
  def escape_entity_name(entity_name)
38
38
  return unless entity_name
39
39
 
40
- URI::DEFAULT_PARSER.escape(entity_name)
40
+ # TODO: OLD RUBIES 3.3
41
+ # URI version 1.0 marked URI::RFC3986_PARSER.escape as obsolete,
42
+ # which URI::DEFAULT_PARSER is an alias for.
43
+ # URI version 1.0+ will ship with Ruby 3.4
44
+ # Once we drop support for Rubies below 3.4, we can use the
45
+ # URI::RFC2396 parser exclusively.
46
+ if Gem::Version.new(URI::VERSION) >= Gem::Version.new('1.0')
47
+ URI::RFC2396_PARSER.escape(entity_name)
48
+ else
49
+ URI::DEFAULT_PARSER.escape(entity_name)
50
+ end
41
51
  end
42
52
  end
43
53
  end
@@ -455,6 +455,8 @@ module NewRelic
455
455
  end
456
456
 
457
457
  def handle_error_response(response, endpoint)
458
+ NewRelic::Agent.agent.health_check.update_status(NewRelic::Agent::HealthCheck::HTTP_ERROR, [response.code, endpoint])
459
+
458
460
  case response
459
461
  when Net::HTTPRequestTimeOut,
460
462
  Net::HTTPTooManyRequests,
@@ -637,9 +639,13 @@ module NewRelic
637
639
  def send_request(opts)
638
640
  request = prep_request(opts)
639
641
  response = relay_request(request, opts)
640
- return response if response.is_a?(Net::HTTPSuccess) || response.is_a?(Net::HTTPAccepted)
641
642
 
642
- handle_error_response(response, opts[:endpoint])
643
+ if response.is_a?(Net::HTTPSuccess) || response.is_a?(Net::HTTPAccepted)
644
+ NewRelic::Agent.agent.health_check.update_status(NewRelic::Agent::HealthCheck::HEALTHY)
645
+ response
646
+ else
647
+ handle_error_response(response, opts[:endpoint])
648
+ end
643
649
  end
644
650
 
645
651
  def log_response(response)
@@ -125,7 +125,16 @@ module NewRelic
125
125
 
126
126
  # Returns [filename, method, line number]
127
127
  def parse_backtrace_frame(frame)
128
- frame =~ /([^:]*)(\:(\d+))?\:in `(.*)'/
128
+ # TODO: OLD RUBIES - Ruby 3.3
129
+ # The (?:`|') non-capturing group can be removed when the agent
130
+ # drops support for Ruby 3.3
131
+ # This group is used to capture the pre-Ruby 3.4.0 backtrace syntax.
132
+ # Example frame:
133
+ # Ruby 3.3.0 and below
134
+ # "irb.rb:69:in `catch'"
135
+ # Ruby 3.4.0+
136
+ # "irb.rb:69:in 'Kernel#catch'"
137
+ frame =~ /([^:]*)(\:(\d+))?\:in (?:`|')(.*)'/
129
138
  [$1, $4, $3] # sic
130
139
  end
131
140
  end
@@ -15,6 +15,7 @@ module NewRelic
15
15
  PRODUCE = 'Produce'.freeze
16
16
  QUEUE = 'Queue'.freeze
17
17
  PURGE = 'Purge'.freeze
18
+ STREAM = 'Stream'.freeze
18
19
  TEMP = 'Temp'.freeze
19
20
  TOPIC = 'Topic'.freeze
20
21
  UNKNOWN = 'Unknown'.freeze
@@ -22,6 +23,7 @@ module NewRelic
22
23
  DESTINATION_TYPES = [
23
24
  :exchange,
24
25
  :queue,
26
+ :stream,
25
27
  :topic,
26
28
  :temporary_queue,
27
29
  :temporary_topic,
@@ -37,6 +39,7 @@ module NewRelic
37
39
  TYPES = {
38
40
  exchange: EXCHANGE,
39
41
  temporary_queue: QUEUE,
42
+ stream: STREAM,
40
43
  queue: QUEUE,
41
44
  temporary_topic: TOPIC,
42
45
  topic: TOPIC,
@@ -132,8 +132,8 @@ module NewRelic
132
132
  def agent # :nodoc:
133
133
  return @agent if @agent
134
134
 
135
- NewRelic::Agent.logger.warn("Agent unavailable as it hasn't been started.")
136
- NewRelic::Agent.logger.warn(caller.join("\n"))
135
+ NewRelic::Agent.logger.debug("Agent unavailable as it hasn't been started.")
136
+ NewRelic::Agent.logger.debug(caller.join("\n"))
137
137
  nil
138
138
  end
139
139