newrelic_rpm 9.12.0 → 9.17.0

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 (114) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +217 -1
  3. data/CONTRIBUTING.md +2 -2
  4. data/README.md +16 -20
  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 +6 -0
  13. data/lib/new_relic/agent/configuration/default_source.rb +363 -31
  14. data/lib/new_relic/agent/configuration/environment_source.rb +5 -1
  15. data/lib/new_relic/agent/configuration/manager.rb +23 -0
  16. data/lib/new_relic/agent/configuration/yaml_source.rb +6 -1
  17. data/lib/new_relic/agent/database/obfuscation_helpers.rb +11 -11
  18. data/lib/new_relic/agent/database.rb +41 -1
  19. data/lib/new_relic/agent/distributed_tracing.rb +2 -2
  20. data/lib/new_relic/agent/health_check.rb +136 -0
  21. data/lib/new_relic/agent/instrumentation/active_merchant.rb +0 -13
  22. data/lib/new_relic/agent/instrumentation/active_record.rb +1 -8
  23. data/lib/new_relic/agent/instrumentation/active_record_helper.rb +5 -1
  24. data/lib/new_relic/agent/instrumentation/active_record_subscriber.rb +9 -16
  25. data/lib/new_relic/agent/instrumentation/active_support_broadcast_logger.rb +0 -2
  26. data/lib/new_relic/agent/instrumentation/active_support_logger.rb +0 -2
  27. data/lib/new_relic/agent/instrumentation/async_http.rb +1 -2
  28. data/lib/new_relic/agent/instrumentation/aws_sdk_firehose/chain.rb +21 -0
  29. data/lib/new_relic/agent/instrumentation/aws_sdk_firehose/instrumentation.rb +66 -0
  30. data/lib/new_relic/agent/instrumentation/aws_sdk_firehose/prepend.rb +15 -0
  31. data/lib/new_relic/agent/instrumentation/aws_sdk_firehose.rb +22 -0
  32. data/lib/new_relic/agent/instrumentation/aws_sdk_kinesis/chain.rb +21 -0
  33. data/lib/new_relic/agent/instrumentation/aws_sdk_kinesis/instrumentation.rb +91 -0
  34. data/lib/new_relic/agent/instrumentation/aws_sdk_kinesis/prepend.rb +15 -0
  35. data/lib/new_relic/agent/instrumentation/aws_sdk_kinesis.rb +22 -0
  36. data/lib/new_relic/agent/instrumentation/aws_sdk_lambda/chain.rb +33 -0
  37. data/lib/new_relic/agent/instrumentation/aws_sdk_lambda/instrumentation.rb +93 -0
  38. data/lib/new_relic/agent/instrumentation/aws_sdk_lambda/prepend.rb +23 -0
  39. data/lib/new_relic/agent/instrumentation/aws_sdk_lambda.rb +23 -0
  40. data/lib/new_relic/agent/instrumentation/aws_sqs.rb +0 -2
  41. data/lib/new_relic/agent/instrumentation/bunny.rb +3 -4
  42. data/lib/new_relic/agent/instrumentation/concurrent_ruby.rb +0 -2
  43. data/lib/new_relic/agent/instrumentation/curb.rb +3 -4
  44. data/lib/new_relic/agent/instrumentation/delayed_job_instrumentation.rb +0 -23
  45. data/lib/new_relic/agent/instrumentation/dynamodb/instrumentation.rb +1 -1
  46. data/lib/new_relic/agent/instrumentation/dynamodb.rb +0 -2
  47. data/lib/new_relic/agent/instrumentation/elasticsearch.rb +0 -2
  48. data/lib/new_relic/agent/instrumentation/ethon.rb +0 -4
  49. data/lib/new_relic/agent/instrumentation/excon.rb +0 -16
  50. data/lib/new_relic/agent/instrumentation/fiber.rb +0 -2
  51. data/lib/new_relic/agent/instrumentation/grape/instrumentation.rb +0 -3
  52. data/lib/new_relic/agent/instrumentation/grape.rb +1 -1
  53. data/lib/new_relic/agent/instrumentation/httpclient.rb +0 -1
  54. data/lib/new_relic/agent/instrumentation/httprb.rb +0 -1
  55. data/lib/new_relic/agent/instrumentation/httpx.rb +0 -4
  56. data/lib/new_relic/agent/instrumentation/logger.rb +1 -3
  57. data/lib/new_relic/agent/instrumentation/logstasher.rb +0 -2
  58. data/lib/new_relic/agent/instrumentation/memcache.rb +0 -1
  59. data/lib/new_relic/agent/instrumentation/opensearch/chain.rb +21 -0
  60. data/lib/new_relic/agent/instrumentation/opensearch/instrumentation.rb +66 -0
  61. data/lib/new_relic/agent/instrumentation/opensearch/prepend.rb +13 -0
  62. data/lib/new_relic/agent/instrumentation/opensearch.rb +23 -0
  63. data/lib/new_relic/agent/instrumentation/padrino.rb +3 -3
  64. data/lib/new_relic/agent/instrumentation/rake.rb +0 -1
  65. data/lib/new_relic/agent/instrumentation/rdkafka/chain.rb +72 -0
  66. data/lib/new_relic/agent/instrumentation/rdkafka/instrumentation.rb +70 -0
  67. data/lib/new_relic/agent/instrumentation/rdkafka/prepend.rb +67 -0
  68. data/lib/new_relic/agent/instrumentation/rdkafka.rb +25 -0
  69. data/lib/new_relic/agent/instrumentation/redis.rb +7 -6
  70. data/lib/new_relic/agent/instrumentation/resque.rb +7 -5
  71. data/lib/new_relic/agent/instrumentation/roda.rb +4 -4
  72. data/lib/new_relic/agent/instrumentation/ruby_kafka/chain.rb +55 -0
  73. data/lib/new_relic/agent/instrumentation/ruby_kafka/instrumentation.rb +67 -0
  74. data/lib/new_relic/agent/instrumentation/ruby_kafka/prepend.rb +60 -0
  75. data/lib/new_relic/agent/instrumentation/ruby_kafka.rb +25 -0
  76. data/lib/new_relic/agent/instrumentation/sidekiq/extensions/delayed_class.rb +1 -1
  77. data/lib/new_relic/agent/instrumentation/sidekiq.rb +0 -14
  78. data/lib/new_relic/agent/instrumentation/sinatra.rb +3 -19
  79. data/lib/new_relic/agent/instrumentation/thread.rb +0 -2
  80. data/lib/new_relic/agent/instrumentation/tilt.rb +0 -4
  81. data/lib/new_relic/agent/instrumentation/typhoeus.rb +0 -1
  82. data/lib/new_relic/agent/instrumentation/view_component/instrumentation.rb +11 -5
  83. data/lib/new_relic/agent/instrumentation/view_component.rb +0 -2
  84. data/lib/new_relic/agent/javascript_instrumentor.rb +2 -3
  85. data/lib/new_relic/agent/local_log_decorator.rb +12 -2
  86. data/lib/new_relic/agent/log_event_aggregator.rb +28 -2
  87. data/lib/new_relic/agent/messaging.rb +11 -5
  88. data/lib/new_relic/agent/new_relic_service.rb +8 -2
  89. data/lib/new_relic/agent/serverless_handler.rb +241 -12
  90. data/lib/new_relic/agent/serverless_handler_event_sources.json +155 -0
  91. data/lib/new_relic/agent/serverless_handler_event_sources.rb +49 -0
  92. data/lib/new_relic/agent/span_event_primitive.rb +4 -2
  93. data/lib/new_relic/agent/system_info.rb +14 -0
  94. data/lib/new_relic/agent/threading/backtrace_node.rb +10 -1
  95. data/lib/new_relic/agent/transaction/message_broker_segment.rb +3 -0
  96. data/lib/new_relic/agent/transaction/request_attributes.rb +13 -1
  97. data/lib/new_relic/agent/transaction/trace_context.rb +1 -1
  98. data/lib/new_relic/agent.rb +95 -2
  99. data/lib/new_relic/control/frameworks/grape.rb +14 -0
  100. data/lib/new_relic/control/frameworks/padrino.rb +14 -0
  101. data/lib/new_relic/control/frameworks/rails4.rb +1 -3
  102. data/lib/new_relic/dependency_detection.rb +11 -13
  103. data/lib/new_relic/environment_report.rb +2 -2
  104. data/lib/new_relic/helper.rb +15 -0
  105. data/lib/new_relic/language_support.rb +3 -1
  106. data/lib/new_relic/local_environment.rb +1 -4
  107. data/lib/new_relic/version.rb +1 -1
  108. data/lib/sequel/extensions/new_relic_instrumentation.rb +1 -1
  109. data/lib/tasks/helpers/newrelicyml.rb +73 -11
  110. data/lib/tasks/instrumentation_generator/instrumentation.thor +1 -1
  111. data/lib/tasks/instrumentation_generator/templates/dependency_detection.tt +11 -8
  112. data/newrelic.yml +224 -79
  113. data/test/agent_helper.rb +8 -1
  114. metadata +32 -6
@@ -7,17 +7,17 @@ module NewRelic
7
7
  module Database
8
8
  module ObfuscationHelpers
9
9
  COMPONENTS_REGEX_MAP = {
10
- :single_quotes => /'(?:[^']|'')*?(?:\\'.*|'(?!'))/,
11
- :double_quotes => /"(?:[^"]|"")*?(?:\\".*|"(?!"))/,
12
- :dollar_quotes => /(\$(?!\d)[^$]*?\$).*?(?:\1|$)/,
13
- :uuids => /\{?(?:[0-9a-fA-F]\-*){32}\}?/,
14
- :numeric_literals => /-?\b(?:[0-9]+\.)?[0-9]+([eE][+-]?[0-9]+)?\b/,
15
- :boolean_literals => /\b(?:true|false|null)\b/i,
16
- :hexadecimal_literals => /0x[0-9a-fA-F]+/,
17
- :comments => /(?:#|--).*?(?=\r|\n|$)/i,
18
- :multi_line_comments => /\/\*(?:[^\/]|\/[^*])*?(?:\*\/|\/\*.*)/,
19
- :oracle_quoted_strings => /q'\[.*?(?:\]'|$)|q'\{.*?(?:\}'|$)|q'\<.*?(?:\>'|$)|q'\(.*?(?:\)'|$)/
20
- }
10
+ single_quotes: /'(?:[^']|'')*?(?:\\'.*|'(?!'))/,
11
+ double_quotes: /"(?:[^"]|"")*?(?:\\".*|"(?!"))/,
12
+ dollar_quotes: /(\$(?!\d)[^$]*?\$).*?(?:\1|$)/,
13
+ uuids: /\{?(?:[0-9a-fA-F]-*){32}\}?/,
14
+ numeric_literals: /-?\b(?:[0-9]+\.)?[0-9]+([eE][+-]?[0-9]+)?\b/,
15
+ boolean_literals: /\b(?:true|false|null)\b/i,
16
+ hexadecimal_literals: /0x[0-9a-fA-F]+/,
17
+ comments: /(?:#|--).*?(?=\r|\n|$)/i,
18
+ multi_line_comments: %r{/\*.*?\*/}m,
19
+ oracle_quoted_strings: /q'\[.*?(?:\]'|$)|q'\{.*?(?:\}'|$)|q'<.*?(?:>'|$)|q'\(.*?(?:\)'|$)/
20
+ }.freeze
21
21
 
22
22
  DIALECT_COMPONENTS = {
23
23
  :fallback => COMPONENTS_REGEX_MAP.keys,
@@ -90,6 +90,42 @@ module NewRelic
90
90
  ConnectionManager.instance.get_connection(config, &connector)
91
91
  end
92
92
 
93
+ def explain_this(statement, use_execute = false)
94
+ if supports_with_connection?
95
+ explain_this_using_with_connection(statement)
96
+ else
97
+ explain_this_using_adapter_connection(statement, use_execute)
98
+ end
99
+ rescue => e
100
+ NewRelic::Agent.logger.error("Couldn't fetch the explain plan for statement: #{e}")
101
+ end
102
+
103
+ def explain_this_using_with_connection(statement)
104
+ ::ActiveRecord::Base.with_connection do |conn|
105
+ conn.exec_query("EXPLAIN #{statement.sql}", "Explain #{statement.name}", statement.binds)
106
+ end
107
+ end
108
+
109
+ def explain_this_using_adapter_connection(statement, use_execute)
110
+ connection = get_connection(statement.config) do
111
+ ::ActiveRecord::Base.send(:"#{statement.config[:adapter]}_connection", statement.config)
112
+ end
113
+
114
+ if use_execute
115
+ connection.execute("EXPLAIN #{statement.sql}")
116
+ else
117
+ connection.exec_query("EXPLAIN #{statement.sql}", "Explain #{statement.name}", statement.binds)
118
+ end
119
+ end
120
+
121
+ # ActiveRecord v7.2.0 introduced with_connection
122
+ def supports_with_connection?
123
+ return @supports_with_connection if defined?(@supports_with_connection)
124
+
125
+ @supports_with_connection = defined?(::ActiveRecord::VERSION::STRING) &&
126
+ Gem::Version.new(ActiveRecord::VERSION::STRING) >= Gem::Version.new('7.2.0')
127
+ end
128
+
93
129
  def close_connections
94
130
  ConnectionManager.instance.close_connections
95
131
  end
@@ -241,9 +277,11 @@ module NewRelic
241
277
  MYSQL_PREFIX = 'mysql'.freeze
242
278
  MYSQL2_PREFIX = 'mysql2'.freeze
243
279
  SQLITE_PREFIX = 'sqlite'.freeze
280
+ TRILOGY_PREFIX = 'trilogy'.freeze
281
+ REDSHIFT_PREFIX = 'redshift'.freeze
244
282
 
245
283
  def symbolized_adapter(adapter)
246
- if adapter.start_with?(POSTGRES_PREFIX) || adapter == POSTGIS_PREFIX
284
+ if adapter.start_with?(POSTGRES_PREFIX) || adapter == POSTGIS_PREFIX || adapter == REDSHIFT_PREFIX
247
285
  :postgres
248
286
  elsif adapter == MYSQL_PREFIX
249
287
  :mysql
@@ -253,6 +291,8 @@ module NewRelic
253
291
  :mysql2
254
292
  elsif adapter.start_with?(SQLITE_PREFIX)
255
293
  :sqlite
294
+ elsif adapter == TRILOGY_PREFIX
295
+ :trilogy
256
296
  else
257
297
  adapter.to_sym
258
298
  end
@@ -45,7 +45,7 @@ module NewRelic
45
45
  record_api_supportability_metric(:insert_distributed_trace_headers)
46
46
 
47
47
  unless Agent.config[:'distributed_tracing.enabled']
48
- NewRelic::Agent.logger.warn('Not configured to insert distributed trace headers')
48
+ NewRelic::Agent.logger.debug('Not configured to insert distributed trace headers')
49
49
  return nil
50
50
  end
51
51
 
@@ -99,7 +99,7 @@ module NewRelic
99
99
  record_api_supportability_metric(:accept_distributed_trace_headers)
100
100
 
101
101
  unless Agent.config[:'distributed_tracing.enabled']
102
- NewRelic::Agent.logger.warn('Not configured to accept distributed trace headers')
102
+ NewRelic::Agent.logger.debug('Not configured to accept distributed trace headers')
103
103
  return nil
104
104
  end
105
105
 
@@ -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
@@ -35,17 +35,4 @@ DependencyDetection.defer do
35
35
  end
36
36
  end
37
37
  end
38
-
39
- executes do
40
- next unless Gem::Version.new(ActiveMerchant::VERSION) < Gem::Version.new('1.65.0')
41
-
42
- deprecation_msg = 'The Ruby agent is dropping support for ActiveMerchant versions below 1.65.0 ' \
43
- 'in version 9.0.0. Please upgrade your ActiveMerchant version to continue receiving full support. ' \
44
-
45
- NewRelic::Agent.logger.log_once(
46
- :warn,
47
- :deprecated_active_merchant_version,
48
- deprecation_msg
49
- )
50
- end
51
38
  end
@@ -9,14 +9,7 @@ module NewRelic
9
9
  module Instrumentation
10
10
  module ActiveRecord
11
11
  EXPLAINER = lambda do |statement|
12
- connection = NewRelic::Agent::Database.get_connection(statement.config) do
13
- ::ActiveRecord::Base.send("#{statement.config[:adapter]}_connection",
14
- statement.config)
15
- end
16
- # the following line needs else branch coverage
17
- if connection && connection.respond_to?(:execute) # rubocop:disable Style/SafeNavigation
18
- return connection.execute("EXPLAIN #{statement.sql}")
19
- end
12
+ NewRelic::Agent::Database.explain_this(statement, true)
20
13
  end
21
14
 
22
15
  def self.insert_instrumentation
@@ -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 = {
@@ -70,18 +70,7 @@ module NewRelic
70
70
  end
71
71
 
72
72
  def get_explain_plan(statement)
73
- connection = NewRelic::Agent::Database.get_connection(statement.config) do
74
- ::ActiveRecord::Base.send("#{statement.config[:adapter]}_connection",
75
- statement.config)
76
- end
77
- # the following line needs else branch coverage
78
- if connection && connection.respond_to?(:exec_query) # rubocop:disable Style/SafeNavigation
79
- return connection.exec_query("EXPLAIN #{statement.sql}",
80
- "Explain #{statement.name}",
81
- statement.binds)
82
- end
83
- rescue => e
84
- NewRelic::Agent.logger.debug("Couldn't fetch the explain plan for #{statement} due to #{e}")
73
+ NewRelic::Agent::Database.explain_this(statement)
85
74
  end
86
75
 
87
76
  def active_record_config(payload)
@@ -127,10 +116,14 @@ module NewRelic
127
116
  port_path_or_id = nil
128
117
  database = nil
129
118
 
130
- if ActiveRecordHelper::InstanceIdentification.supported_adapter?(config)
131
- host = ActiveRecordHelper::InstanceIdentification.host(config)
132
- port_path_or_id = ActiveRecordHelper::InstanceIdentification.port_path_or_id(config)
133
- 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]}")
134
127
  end
135
128
 
136
129
  segment = Tracer.start_datastore_segment(product: product,
@@ -12,8 +12,6 @@ DependencyDetection.defer do
12
12
  depends_on { defined?(ActiveSupport::BroadcastLogger) }
13
13
 
14
14
  executes do
15
- NewRelic::Agent.logger.info('Installing ActiveSupport::BroadcastLogger instrumentation')
16
-
17
15
  if use_prepend?
18
16
  prepend_instrument ActiveSupport::BroadcastLogger, NewRelic::Agent::Instrumentation::ActiveSupportBroadcastLogger::Prepend
19
17
  else
@@ -14,8 +14,6 @@ DependencyDetection.defer do
14
14
  end
15
15
 
16
16
  executes do
17
- NewRelic::Agent.logger.info('Installing ActiveSupport::Logger instrumentation')
18
-
19
17
  if use_prepend?
20
18
  # the only method currently instrumented is a class method
21
19
  prepend_instrument ActiveSupport::Logger.singleton_class, NewRelic::Agent::Instrumentation::ActiveSupportLogger::Prepend
@@ -16,9 +16,8 @@ DependencyDetection.defer do
16
16
  end
17
17
 
18
18
  executes do
19
- NewRelic::Agent.logger.info('Installing async_http instrumentation')
20
-
21
19
  require 'async/http/internet'
20
+
22
21
  if use_prepend?
23
22
  prepend_instrument Async::HTTP::Internet, NewRelic::Agent::Instrumentation::AsyncHttp::Prepend
24
23
  else
@@ -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