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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +217 -1
- data/CONTRIBUTING.md +2 -2
- data/README.md +16 -20
- data/lib/boot/strap.rb +4 -3
- data/lib/new_relic/agent/agent.rb +4 -0
- data/lib/new_relic/agent/agent_helpers/connect.rb +3 -0
- data/lib/new_relic/agent/agent_helpers/harvest.rb +3 -0
- data/lib/new_relic/agent/agent_helpers/shutdown.rb +3 -0
- data/lib/new_relic/agent/agent_helpers/start_worker_thread.rb +1 -0
- data/lib/new_relic/agent/agent_helpers/startup.rb +7 -0
- data/lib/new_relic/agent/aws.rb +6 -0
- data/lib/new_relic/agent/configuration/default_source.rb +363 -31
- data/lib/new_relic/agent/configuration/environment_source.rb +5 -1
- data/lib/new_relic/agent/configuration/manager.rb +23 -0
- data/lib/new_relic/agent/configuration/yaml_source.rb +6 -1
- data/lib/new_relic/agent/database/obfuscation_helpers.rb +11 -11
- data/lib/new_relic/agent/database.rb +41 -1
- data/lib/new_relic/agent/distributed_tracing.rb +2 -2
- data/lib/new_relic/agent/health_check.rb +136 -0
- data/lib/new_relic/agent/instrumentation/active_merchant.rb +0 -13
- data/lib/new_relic/agent/instrumentation/active_record.rb +1 -8
- data/lib/new_relic/agent/instrumentation/active_record_helper.rb +5 -1
- data/lib/new_relic/agent/instrumentation/active_record_subscriber.rb +9 -16
- data/lib/new_relic/agent/instrumentation/active_support_broadcast_logger.rb +0 -2
- data/lib/new_relic/agent/instrumentation/active_support_logger.rb +0 -2
- data/lib/new_relic/agent/instrumentation/async_http.rb +1 -2
- data/lib/new_relic/agent/instrumentation/aws_sdk_firehose/chain.rb +21 -0
- data/lib/new_relic/agent/instrumentation/aws_sdk_firehose/instrumentation.rb +66 -0
- data/lib/new_relic/agent/instrumentation/aws_sdk_firehose/prepend.rb +15 -0
- data/lib/new_relic/agent/instrumentation/aws_sdk_firehose.rb +22 -0
- data/lib/new_relic/agent/instrumentation/aws_sdk_kinesis/chain.rb +21 -0
- data/lib/new_relic/agent/instrumentation/aws_sdk_kinesis/instrumentation.rb +91 -0
- data/lib/new_relic/agent/instrumentation/aws_sdk_kinesis/prepend.rb +15 -0
- data/lib/new_relic/agent/instrumentation/aws_sdk_kinesis.rb +22 -0
- data/lib/new_relic/agent/instrumentation/aws_sdk_lambda/chain.rb +33 -0
- data/lib/new_relic/agent/instrumentation/aws_sdk_lambda/instrumentation.rb +93 -0
- data/lib/new_relic/agent/instrumentation/aws_sdk_lambda/prepend.rb +23 -0
- data/lib/new_relic/agent/instrumentation/aws_sdk_lambda.rb +23 -0
- data/lib/new_relic/agent/instrumentation/aws_sqs.rb +0 -2
- data/lib/new_relic/agent/instrumentation/bunny.rb +3 -4
- data/lib/new_relic/agent/instrumentation/concurrent_ruby.rb +0 -2
- data/lib/new_relic/agent/instrumentation/curb.rb +3 -4
- data/lib/new_relic/agent/instrumentation/delayed_job_instrumentation.rb +0 -23
- data/lib/new_relic/agent/instrumentation/dynamodb/instrumentation.rb +1 -1
- data/lib/new_relic/agent/instrumentation/dynamodb.rb +0 -2
- data/lib/new_relic/agent/instrumentation/elasticsearch.rb +0 -2
- data/lib/new_relic/agent/instrumentation/ethon.rb +0 -4
- data/lib/new_relic/agent/instrumentation/excon.rb +0 -16
- data/lib/new_relic/agent/instrumentation/fiber.rb +0 -2
- data/lib/new_relic/agent/instrumentation/grape/instrumentation.rb +0 -3
- data/lib/new_relic/agent/instrumentation/grape.rb +1 -1
- data/lib/new_relic/agent/instrumentation/httpclient.rb +0 -1
- data/lib/new_relic/agent/instrumentation/httprb.rb +0 -1
- data/lib/new_relic/agent/instrumentation/httpx.rb +0 -4
- data/lib/new_relic/agent/instrumentation/logger.rb +1 -3
- data/lib/new_relic/agent/instrumentation/logstasher.rb +0 -2
- data/lib/new_relic/agent/instrumentation/memcache.rb +0 -1
- data/lib/new_relic/agent/instrumentation/opensearch/chain.rb +21 -0
- data/lib/new_relic/agent/instrumentation/opensearch/instrumentation.rb +66 -0
- data/lib/new_relic/agent/instrumentation/opensearch/prepend.rb +13 -0
- data/lib/new_relic/agent/instrumentation/opensearch.rb +23 -0
- data/lib/new_relic/agent/instrumentation/padrino.rb +3 -3
- data/lib/new_relic/agent/instrumentation/rake.rb +0 -1
- data/lib/new_relic/agent/instrumentation/rdkafka/chain.rb +72 -0
- data/lib/new_relic/agent/instrumentation/rdkafka/instrumentation.rb +70 -0
- data/lib/new_relic/agent/instrumentation/rdkafka/prepend.rb +67 -0
- data/lib/new_relic/agent/instrumentation/rdkafka.rb +25 -0
- data/lib/new_relic/agent/instrumentation/redis.rb +7 -6
- data/lib/new_relic/agent/instrumentation/resque.rb +7 -5
- data/lib/new_relic/agent/instrumentation/roda.rb +4 -4
- data/lib/new_relic/agent/instrumentation/ruby_kafka/chain.rb +55 -0
- data/lib/new_relic/agent/instrumentation/ruby_kafka/instrumentation.rb +67 -0
- data/lib/new_relic/agent/instrumentation/ruby_kafka/prepend.rb +60 -0
- data/lib/new_relic/agent/instrumentation/ruby_kafka.rb +25 -0
- data/lib/new_relic/agent/instrumentation/sidekiq/extensions/delayed_class.rb +1 -1
- data/lib/new_relic/agent/instrumentation/sidekiq.rb +0 -14
- data/lib/new_relic/agent/instrumentation/sinatra.rb +3 -19
- data/lib/new_relic/agent/instrumentation/thread.rb +0 -2
- data/lib/new_relic/agent/instrumentation/tilt.rb +0 -4
- data/lib/new_relic/agent/instrumentation/typhoeus.rb +0 -1
- data/lib/new_relic/agent/instrumentation/view_component/instrumentation.rb +11 -5
- data/lib/new_relic/agent/instrumentation/view_component.rb +0 -2
- data/lib/new_relic/agent/javascript_instrumentor.rb +2 -3
- data/lib/new_relic/agent/local_log_decorator.rb +12 -2
- data/lib/new_relic/agent/log_event_aggregator.rb +28 -2
- data/lib/new_relic/agent/messaging.rb +11 -5
- data/lib/new_relic/agent/new_relic_service.rb +8 -2
- data/lib/new_relic/agent/serverless_handler.rb +241 -12
- data/lib/new_relic/agent/serverless_handler_event_sources.json +155 -0
- data/lib/new_relic/agent/serverless_handler_event_sources.rb +49 -0
- data/lib/new_relic/agent/span_event_primitive.rb +4 -2
- data/lib/new_relic/agent/system_info.rb +14 -0
- data/lib/new_relic/agent/threading/backtrace_node.rb +10 -1
- data/lib/new_relic/agent/transaction/message_broker_segment.rb +3 -0
- data/lib/new_relic/agent/transaction/request_attributes.rb +13 -1
- data/lib/new_relic/agent/transaction/trace_context.rb +1 -1
- data/lib/new_relic/agent.rb +95 -2
- data/lib/new_relic/control/frameworks/grape.rb +14 -0
- data/lib/new_relic/control/frameworks/padrino.rb +14 -0
- data/lib/new_relic/control/frameworks/rails4.rb +1 -3
- data/lib/new_relic/dependency_detection.rb +11 -13
- data/lib/new_relic/environment_report.rb +2 -2
- data/lib/new_relic/helper.rb +15 -0
- data/lib/new_relic/language_support.rb +3 -1
- data/lib/new_relic/local_environment.rb +1 -4
- data/lib/new_relic/version.rb +1 -1
- data/lib/sequel/extensions/new_relic_instrumentation.rb +1 -1
- data/lib/tasks/helpers/newrelicyml.rb +73 -11
- data/lib/tasks/instrumentation_generator/instrumentation.thor +1 -1
- data/lib/tasks/instrumentation_generator/templates/dependency_detection.tt +11 -8
- data/newrelic.yml +224 -79
- data/test/agent_helper.rb +8 -1
- metadata +32 -6
@@ -52,6 +52,8 @@ module NewRelic
|
|
52
52
|
DATASTORE_CATEGORY = 'datastore'
|
53
53
|
CLIENT = 'client'
|
54
54
|
|
55
|
+
DB_STATEMENT_MAX_BYTES = 4096
|
56
|
+
|
55
57
|
# Builds a Hash of error attributes as well as the Span ID when
|
56
58
|
# an error is present. Otherwise, returns nil when no error present.
|
57
59
|
def error_attributes(segment)
|
@@ -114,9 +116,9 @@ module NewRelic
|
|
114
116
|
agent_attributes[DB_SYSTEM_KEY] = segment.product if allowed?(DB_SYSTEM_KEY)
|
115
117
|
|
116
118
|
if segment.sql_statement && allowed?(DB_STATEMENT_KEY)
|
117
|
-
agent_attributes[DB_STATEMENT_KEY] = truncate(segment.sql_statement.safe_sql,
|
119
|
+
agent_attributes[DB_STATEMENT_KEY] = truncate(segment.sql_statement.safe_sql, DB_STATEMENT_MAX_BYTES)
|
118
120
|
elsif segment.nosql_statement && allowed?(DB_STATEMENT_KEY)
|
119
|
-
agent_attributes[DB_STATEMENT_KEY] = truncate(segment.nosql_statement,
|
121
|
+
agent_attributes[DB_STATEMENT_KEY] = truncate(segment.nosql_statement, DB_STATEMENT_MAX_BYTES)
|
120
122
|
end
|
121
123
|
|
122
124
|
[intrinsics, custom_attributes(segment), agent_attributes.merge(agent_attributes(segment))]
|
@@ -19,6 +19,20 @@ module NewRelic
|
|
19
19
|
RbConfig::CONFIG['target_os']
|
20
20
|
end
|
21
21
|
|
22
|
+
def self.os_distribution
|
23
|
+
case
|
24
|
+
when darwin? then :darwin
|
25
|
+
when linux? then :linux
|
26
|
+
when bsd? then :bsd
|
27
|
+
when windows? then :windows
|
28
|
+
else ruby_os_identifier
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.windows?
|
33
|
+
!!(ruby_os_identifier[/mingw|mswin/i])
|
34
|
+
end
|
35
|
+
|
22
36
|
def self.darwin?
|
23
37
|
!!(ruby_os_identifier =~ /darwin/i)
|
24
38
|
end
|
@@ -125,7 +125,16 @@ module NewRelic
|
|
125
125
|
|
126
126
|
# Returns [filename, method, line number]
|
127
127
|
def parse_backtrace_frame(frame)
|
128
|
-
|
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,
|
@@ -24,7 +24,7 @@ module NewRelic
|
|
24
24
|
@referer = referer_from_request(request)
|
25
25
|
@accept = attribute_from_env(request, HTTP_ACCEPT_HEADER_KEY)
|
26
26
|
@content_length = content_length_from_request(request)
|
27
|
-
@content_type =
|
27
|
+
@content_type = content_type_attribute_from_request(request)
|
28
28
|
@host = attribute_from_request(request, :host)
|
29
29
|
@port = port_from_request(request)
|
30
30
|
@user_agent = attribute_from_request(request, :user_agent)
|
@@ -127,6 +127,18 @@ module NewRelic
|
|
127
127
|
end
|
128
128
|
end
|
129
129
|
|
130
|
+
def content_type_attribute_from_request(request)
|
131
|
+
# Rails 7.0 changed the behavior of `content_type`. We want just the MIME type, so use `media_type` if available.
|
132
|
+
# https://guides.rubyonrails.org/upgrading_ruby_on_rails.html#actiondispatch-request-content-type-now-returns-content-type-header-as-it-is
|
133
|
+
content_type = if request.respond_to?(:media_type)
|
134
|
+
:media_type
|
135
|
+
elsif request.respond_to?(:content_type)
|
136
|
+
:content_type
|
137
|
+
end
|
138
|
+
|
139
|
+
request.send(content_type) if content_type
|
140
|
+
end
|
141
|
+
|
130
142
|
def attribute_from_env(request, key)
|
131
143
|
if env = attribute_from_request(request, :env)
|
132
144
|
env[key]
|
@@ -95,7 +95,7 @@ module NewRelic
|
|
95
95
|
|
96
96
|
def create_trace_state_payload
|
97
97
|
unless Agent.config[:'distributed_tracing.enabled']
|
98
|
-
NewRelic::Agent.logger.warn('Not configured to create
|
98
|
+
NewRelic::Agent.logger.warn('Not configured to create W3C trace context payload')
|
99
99
|
return
|
100
100
|
end
|
101
101
|
|
data/lib/new_relic/agent.rb
CHANGED
@@ -85,6 +85,10 @@ module NewRelic
|
|
85
85
|
# An exception that forces an agent to stop reporting until its mongrel is restarted.
|
86
86
|
class ForceDisconnectException < StandardError; end
|
87
87
|
|
88
|
+
# Error handling for the automated custom instrumentation tracer logic
|
89
|
+
class AutomaticTracerParseException < StandardError; end
|
90
|
+
class AutomaticTracerTraceException < StandardError; end
|
91
|
+
|
88
92
|
# An exception that forces an agent to restart.
|
89
93
|
class ForceRestartException < StandardError
|
90
94
|
def message
|
@@ -109,6 +113,9 @@ module NewRelic
|
|
109
113
|
# placeholder name used when we cannot determine a transaction's name
|
110
114
|
UNKNOWN_METRIC = '(unknown)'.freeze
|
111
115
|
LLM_FEEDBACK_MESSAGE = 'LlmFeedbackMessage'
|
116
|
+
# give the observed app time to load the code that automatic tracers have
|
117
|
+
# been configured for
|
118
|
+
AUTOMATIC_TRACER_MAX_ATTEMPTS = 60 # 60 = try about twice a second for 30 seconds
|
112
119
|
|
113
120
|
attr_reader :error_group_callback
|
114
121
|
attr_reader :llm_token_count_callback
|
@@ -125,8 +132,8 @@ module NewRelic
|
|
125
132
|
def agent # :nodoc:
|
126
133
|
return @agent if @agent
|
127
134
|
|
128
|
-
NewRelic::Agent.logger.
|
129
|
-
NewRelic::Agent.logger.
|
135
|
+
NewRelic::Agent.logger.debug("Agent unavailable as it hasn't been started.")
|
136
|
+
NewRelic::Agent.logger.debug(caller.join("\n"))
|
130
137
|
nil
|
131
138
|
end
|
132
139
|
|
@@ -163,6 +170,92 @@ module NewRelic
|
|
163
170
|
end
|
164
171
|
end
|
165
172
|
|
173
|
+
# @api private
|
174
|
+
def self.add_automatic_method_tracers(arr)
|
175
|
+
return unless arr
|
176
|
+
return arr if arr.respond_to?(:empty?) && arr.empty?
|
177
|
+
|
178
|
+
arr = arr.split(/\s*,\s*/) if arr.is_a?(String)
|
179
|
+
|
180
|
+
add_tracers_once_methods_are_defined(arr.dup)
|
181
|
+
|
182
|
+
arr
|
183
|
+
end
|
184
|
+
|
185
|
+
# spawn a thread that will attempt to establish a tracer for each of the
|
186
|
+
# configured methods. the thread will continue to keep trying with each
|
187
|
+
# tracer until one of the following happens:
|
188
|
+
# - the tracer is successfully established
|
189
|
+
# - the configured method string couldn't be parsed
|
190
|
+
# - establishing a tracer for a successfully parsed string failed
|
191
|
+
# - the maximum number of attempts has been reached
|
192
|
+
# the thread will only be spawned once per agent initialization, to account
|
193
|
+
# for configuration reloading scenarios.
|
194
|
+
#
|
195
|
+
# @api private
|
196
|
+
def self.add_tracers_once_methods_are_defined(notations)
|
197
|
+
# this class method can be invoked multiple times at agent startup, so
|
198
|
+
# we return asap here instead of using a traditional memoization of
|
199
|
+
# waiting for the method's body to finish being executed
|
200
|
+
if defined?(@add_tracers_once_methods_are_defined)
|
201
|
+
return
|
202
|
+
else
|
203
|
+
@add_tracers_once_methods_are_defined = true
|
204
|
+
end
|
205
|
+
|
206
|
+
Thread.new do
|
207
|
+
AUTOMATIC_TRACER_MAX_ATTEMPTS.times do
|
208
|
+
notations.delete_if { |notation| prep_tracer_for(notation) }
|
209
|
+
|
210
|
+
break if notations.empty?
|
211
|
+
|
212
|
+
sleep 0.5
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
# returns `true` if the notation string has either been successfully
|
218
|
+
# processed or raised an error during processing. returns `false` if the
|
219
|
+
# string seems good but the (customer) code to be traced has not yet been
|
220
|
+
# loaded into the Ruby VM
|
221
|
+
#
|
222
|
+
# @api private
|
223
|
+
def self.prep_tracer_for(fully_qualified_method_notation)
|
224
|
+
delimiters = fully_qualified_method_notation.scan(/\.|#/)
|
225
|
+
raise AutomaticTracerParseException.new("Expected exactly one '.' or '#' delimiter.") unless delimiters.size == 1
|
226
|
+
|
227
|
+
delimiter = delimiters.first
|
228
|
+
namespace, method_name = fully_qualified_method_notation.split(delimiter)
|
229
|
+
unless namespace && !namespace.empty?
|
230
|
+
raise AutomaticTracerParseException.new("Nothing found to the left of the #{delimiter} delimiter.")
|
231
|
+
end
|
232
|
+
unless method_name && !method_name.empty?
|
233
|
+
raise AutomaticTracerParseException.new("Nothing found to the right of the #{delimiter} delimiter.")
|
234
|
+
end
|
235
|
+
|
236
|
+
begin
|
237
|
+
klass = ::NewRelic::LanguageSupport.constantize(namespace)
|
238
|
+
return false unless klass
|
239
|
+
|
240
|
+
klass_to_trace = delimiter.eql?('.') ? klass.singleton_class : klass
|
241
|
+
add_or_defer_method_tracer(klass_to_trace, method_name, nil, {})
|
242
|
+
rescue StandardError => e
|
243
|
+
raise AutomaticTracerTraceException.new("#{e.class} - #{e.message}")
|
244
|
+
end
|
245
|
+
|
246
|
+
true
|
247
|
+
rescue AutomaticTracerParseException => e
|
248
|
+
NewRelic::Agent.logger.error('Unable to parse out a usable method name to trace. Expected a valid, fully ' \
|
249
|
+
"qualified method notation. Got: '#{fully_qualified_method_notation}'. " \
|
250
|
+
"Error: #{e.message}")
|
251
|
+
true
|
252
|
+
rescue AutomaticTracerTraceException => e
|
253
|
+
NewRelic::Agent.logger.error('Unable to automatically apply a tracer to method ' \
|
254
|
+
"'#{fully_qualified_method_notation}'. Error: #{e.message}")
|
255
|
+
true
|
256
|
+
end
|
257
|
+
|
258
|
+
# @api private
|
166
259
|
def add_deferred_method_tracers_now
|
167
260
|
@tracer_lock.synchronize do
|
168
261
|
@tracer_queue.each do |receiver, method_name, metric_name, options|
|
@@ -0,0 +1,14 @@
|
|
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 'new_relic/control/frameworks/ruby'
|
6
|
+
module NewRelic
|
7
|
+
class Control
|
8
|
+
module Frameworks
|
9
|
+
# Contains basic control logic for Grape
|
10
|
+
class Grape < NewRelic::Control::Frameworks::Ruby
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,14 @@
|
|
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 'new_relic/control/frameworks/sinatra'
|
6
|
+
module NewRelic
|
7
|
+
class Control
|
8
|
+
module Frameworks
|
9
|
+
# Contains basic control logic for Padrino
|
10
|
+
class Padrino < NewRelic::Control::Frameworks::Sinatra
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -9,9 +9,7 @@ module NewRelic
|
|
9
9
|
module Frameworks
|
10
10
|
class Rails4 < NewRelic::Control::Frameworks::Rails3
|
11
11
|
def rails_gem_list
|
12
|
-
|
13
|
-
"#{gem.name} (#{gem.version})"
|
14
|
-
end
|
12
|
+
NewRelic::Helper.rubygems_specs.map { |gem| "#{gem.name} (#{gem.version})" }
|
15
13
|
end
|
16
14
|
|
17
15
|
def append_plugin_list
|
@@ -25,11 +25,9 @@ module DependencyDetection
|
|
25
25
|
|
26
26
|
def detect!
|
27
27
|
@items.each do |item|
|
28
|
-
if item.
|
29
|
-
|
30
|
-
|
31
|
-
item.configure_as_unsatisfied unless item.disabled_configured?
|
32
|
-
end
|
28
|
+
next if item.executed || item.disabled_configured?
|
29
|
+
|
30
|
+
item.dependencies_satisfied? ? item.execute : item.configure_as_unsatisfied
|
33
31
|
end
|
34
32
|
end
|
35
33
|
|
@@ -65,6 +63,13 @@ module DependencyDetection
|
|
65
63
|
end
|
66
64
|
|
67
65
|
def configure_as_unsatisfied
|
66
|
+
# TODO: currently using :unsatisfied for Padrino will clobber the value
|
67
|
+
# already set for Sinatra, so skip Padrino and circle back with a
|
68
|
+
# new Padrino specific solution in the future.
|
69
|
+
#
|
70
|
+
# https://github.com/newrelic/newrelic-ruby-agent/issues/2912
|
71
|
+
return if name == :padrino
|
72
|
+
|
68
73
|
NewRelic::Agent.config.instance_variable_get(:@cache)[config_key] = :unsatisfied
|
69
74
|
end
|
70
75
|
|
@@ -139,8 +144,6 @@ module DependencyDetection
|
|
139
144
|
!(disabled_configured? || deprecated_disabled_configured?)
|
140
145
|
end
|
141
146
|
|
142
|
-
# TODO: MAJOR VERSION
|
143
|
-
# will only return true if a disabled key is found and is truthy
|
144
147
|
def deprecated_disabled_configured?
|
145
148
|
return false if self.name.nil?
|
146
149
|
|
@@ -148,12 +151,7 @@ module DependencyDetection
|
|
148
151
|
return false unless ::NewRelic::Agent.config[key] == true
|
149
152
|
|
150
153
|
::NewRelic::Agent.logger.debug("Not installing #{self.name} instrumentation because of configuration #{key}")
|
151
|
-
|
152
|
-
"[DEPRECATED] configuration #{key} for #{self.name} will be removed in the next major release. " \
|
153
|
-
"Use `#{config_key}` with one of `#{VALID_CONFIG_VALUES.map(&:to_s).inspect}`"
|
154
|
-
)
|
155
|
-
|
156
|
-
return true
|
154
|
+
true
|
157
155
|
end
|
158
156
|
|
159
157
|
def config_key
|
@@ -44,7 +44,7 @@ module NewRelic
|
|
44
44
|
####################################
|
45
45
|
report_on('Gems') do
|
46
46
|
begin
|
47
|
-
|
47
|
+
NewRelic::Helper.rubygems_specs.map { |gem| "#{gem.name}(#{gem.version})" }
|
48
48
|
rescue
|
49
49
|
# There are certain rubygem, bundler, rails combinations (e.g. gem
|
50
50
|
# 1.6.2, rails 2.3, bundler 1.2.3) where the code above throws an error
|
@@ -67,7 +67,7 @@ module NewRelic
|
|
67
67
|
report_on('Physical Cores') { ::NewRelic::Agent::SystemInfo.num_physical_cores }
|
68
68
|
report_on('Arch') { ::NewRelic::Agent::SystemInfo.processor_arch }
|
69
69
|
report_on('OS version') { ::NewRelic::Agent::SystemInfo.os_version }
|
70
|
-
report_on('OS') { ::NewRelic::Agent::SystemInfo.
|
70
|
+
report_on('OS') { ::NewRelic::Agent::SystemInfo.os_distribution }
|
71
71
|
report_on('Database adapter') { ::NewRelic::Agent::DatabaseAdapter.value }
|
72
72
|
report_on('Framework') { Agent.config[:framework].to_s }
|
73
73
|
report_on('Dispatcher') { Agent.config[:dispatcher].to_s }
|
data/lib/new_relic/helper.rb
CHANGED
@@ -82,5 +82,20 @@ module NewRelic
|
|
82
82
|
File.exist?(executable_path) && File.file?(executable_path) && File.executable?(executable_path)
|
83
83
|
end
|
84
84
|
end
|
85
|
+
|
86
|
+
# Bundler version 2.5.12 deprecated all_specs and added installed_specs.
|
87
|
+
# To support newer Bundler versions, try to use installed_specs first,
|
88
|
+
# then fall back to all_specs.
|
89
|
+
# All callers expect this to be an array, so return an array if Bundler isn't defined
|
90
|
+
# @api private
|
91
|
+
def rubygems_specs
|
92
|
+
return [] unless defined?(Bundler)
|
93
|
+
|
94
|
+
if Bundler.rubygems.respond_to?(:installed_specs)
|
95
|
+
Bundler.rubygems.installed_specs
|
96
|
+
else
|
97
|
+
Bundler.rubygems.all_specs
|
98
|
+
end
|
99
|
+
end
|
85
100
|
end
|
86
101
|
end
|
@@ -88,7 +88,9 @@ module NewRelic
|
|
88
88
|
end
|
89
89
|
|
90
90
|
def bundled_gem?(gem_name)
|
91
|
-
defined?(Bundler)
|
91
|
+
return false unless defined?(Bundler)
|
92
|
+
|
93
|
+
NewRelic::Helper.rubygems_specs.map(&:name).include?(gem_name)
|
92
94
|
rescue => e
|
93
95
|
::NewRelic::Agent.logger.info("Could not determine if third party #{gem_name} gem is installed", e)
|
94
96
|
false
|
@@ -142,10 +142,7 @@ module NewRelic
|
|
142
142
|
end
|
143
143
|
|
144
144
|
def check_for_falcon
|
145
|
-
|
146
|
-
NewRelic::LanguageSupport.object_space_usable?
|
147
|
-
|
148
|
-
@discovered_dispatcher = :falcon if find_class_in_object_space(::Falcon::Server)
|
145
|
+
@discovered_dispatcher = :falcon if defined?(::Falcon::Server) && File.basename($PROGRAM_NAME) == 'falcon'
|
149
146
|
end
|
150
147
|
|
151
148
|
def check_for_delayed_job
|
data/lib/new_relic/version.rb
CHANGED
@@ -79,7 +79,7 @@ module Sequel
|
|
79
79
|
|
80
80
|
THREAD_SAFE_CONNECTION_POOL_CLASSES = [
|
81
81
|
(defined?(::Sequel::ThreadedConnectionPool) && ::Sequel::ThreadedConnectionPool),
|
82
|
-
(defined?(::Sequel::TimedQueueConnectionPool) && RUBY_VERSION >= '3.
|
82
|
+
(defined?(::Sequel::TimedQueueConnectionPool) && RUBY_VERSION >= '3.2' && ::Sequel::TimedQueueConnectionPool)
|
83
83
|
].compact.freeze
|
84
84
|
|
85
85
|
def explainer_for(sql)
|
@@ -45,6 +45,34 @@ module NewRelicYML
|
|
45
45
|
|
46
46
|
HEADER
|
47
47
|
|
48
|
+
SECURITY_BEGIN = <<-SECURITY
|
49
|
+
# BEGIN security agent
|
50
|
+
#
|
51
|
+
# NOTE: At this time, the security agent is intended for use only within
|
52
|
+
# a dedicated security testing environment with data that can tolerate
|
53
|
+
# modification or deletion. The security agent is available as a
|
54
|
+
# separate Ruby gem, newrelic_security. It is recommended that this
|
55
|
+
# separate gem only be introduced to a security testing environment
|
56
|
+
# by leveraging Bundler grouping like so:
|
57
|
+
#
|
58
|
+
# # Gemfile
|
59
|
+
# gem 'newrelic_rpm' # New Relic APM observability agent
|
60
|
+
# gem 'newrelic-infinite_tracing' # New Relic Infinite Tracing
|
61
|
+
#
|
62
|
+
# group :security do
|
63
|
+
# gem 'newrelic_security', require: false # New Relic security agent
|
64
|
+
# end
|
65
|
+
#
|
66
|
+
# NOTE: All "security.*" configuration parameters are related only to the
|
67
|
+
# security agent, and all other configuration parameters that may
|
68
|
+
# have "security" in the name somewhere are related to the APM agent.
|
69
|
+
|
70
|
+
SECURITY
|
71
|
+
|
72
|
+
SECURITY_END = <<-SECURITY
|
73
|
+
# END security agent
|
74
|
+
SECURITY
|
75
|
+
|
48
76
|
FOOTER = <<~FOOTER
|
49
77
|
# Environment-specific settings are in this section.
|
50
78
|
# RAILS_ENV or RACK_ENV (as appropriate) is used to determine the environment.
|
@@ -67,16 +95,35 @@ module NewRelicYML
|
|
67
95
|
FOOTER
|
68
96
|
|
69
97
|
def self.get_configs(defaults)
|
70
|
-
|
98
|
+
agent_configs = {}
|
99
|
+
security_configs = {}
|
100
|
+
|
101
|
+
defaults.sort.each do |key, value|
|
71
102
|
next if CRITICAL.include?(key) || SKIP.include?(key)
|
72
103
|
|
73
104
|
next unless public_config?(value) && !deprecated?(value)
|
74
105
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
106
|
+
# TODO: OLD RUBIES < 2.6
|
107
|
+
# Remove `to_s`. `start_with?` doesn't accept symbols in Ruby <2.6
|
108
|
+
if key.to_s.start_with?('security.')
|
109
|
+
description, default = build_config(key, value)
|
110
|
+
security_configs[key] = {description: description, default: default}
|
111
|
+
next
|
112
|
+
end
|
113
|
+
|
114
|
+
description, default = build_config(key, value)
|
115
|
+
agent_configs[key] = {description: description, default: default}
|
79
116
|
end
|
117
|
+
|
118
|
+
[agent_configs, security_configs]
|
119
|
+
end
|
120
|
+
|
121
|
+
def self.build_config(key, value)
|
122
|
+
sanitized_description = sanitize_description(value[:description])
|
123
|
+
description = format_description(sanitized_description)
|
124
|
+
default = default_value(key, value)
|
125
|
+
|
126
|
+
[description, default]
|
80
127
|
end
|
81
128
|
|
82
129
|
def self.public_config?(value)
|
@@ -126,15 +173,30 @@ module NewRelicYML
|
|
126
173
|
end
|
127
174
|
end
|
128
175
|
|
129
|
-
def self.
|
130
|
-
|
131
|
-
|
176
|
+
def self.agent_configs_yml(agent_configs)
|
177
|
+
agent_yml = ''
|
178
|
+
agent_configs.each do |key, value|
|
179
|
+
agent_yml += "#{value[:description]}\n # #{key}: #{value[:default]}\n\n"
|
180
|
+
end
|
181
|
+
|
182
|
+
agent_yml
|
183
|
+
end
|
132
184
|
|
133
|
-
|
134
|
-
|
185
|
+
def self.security_configs_yml(security_configs)
|
186
|
+
security_yml = ''
|
187
|
+
security_configs.each do |key, value|
|
188
|
+
security_yml += "#{value[:description]}\n # #{key}: #{value[:default]}\n\n"
|
135
189
|
end
|
136
190
|
|
137
|
-
|
191
|
+
security_yml
|
192
|
+
end
|
193
|
+
|
194
|
+
def self.build_string(defaults)
|
195
|
+
agent_configs, security_configs = get_configs(defaults)
|
196
|
+
agent_string = agent_configs_yml(agent_configs)
|
197
|
+
security_string = security_configs_yml(security_configs)
|
198
|
+
|
199
|
+
agent_string + SECURITY_BEGIN + security_string + SECURITY_END + "\n"
|
138
200
|
end
|
139
201
|
|
140
202
|
# :nocov:
|
@@ -103,7 +103,7 @@ class Instrumentation < Thor
|
|
103
103
|
<<-CONFIG
|
104
104
|
:'instrumentation.#{snake_name}' => {
|
105
105
|
:default => 'auto',
|
106
|
-
:documentation_default => 'auto'
|
106
|
+
:documentation_default => 'auto',
|
107
107
|
:public => true,
|
108
108
|
:type => String,
|
109
109
|
:dynamic_name => true,
|
@@ -2,27 +2,30 @@
|
|
2
2
|
# See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
|
3
3
|
# frozen_string_literal: true
|
4
4
|
|
5
|
-
require_relative '<%= @snake_name.downcase %>/instrumentation'
|
6
|
-
require_relative '<%= @snake_name.downcase %>/chain'
|
7
|
-
require_relative '<%= @snake_name.downcase %>/prepend'
|
8
|
-
|
9
5
|
DependencyDetection.defer do
|
10
|
-
named :<%= @
|
6
|
+
named :<%= @snake_name %>
|
11
7
|
|
12
8
|
depends_on do
|
13
9
|
# The class that needs to be defined to prepend/chain onto. This can be used
|
14
10
|
# to determine whether the library is installed.
|
15
|
-
defined?(
|
11
|
+
defined?(<%= @class_name %>)
|
16
12
|
# Add any additional requirements to verify whether this instrumentation
|
17
13
|
# should be installed
|
18
14
|
end
|
19
15
|
|
20
16
|
executes do
|
21
|
-
|
17
|
+
require_relative '<%= @snake_name.downcase %>/instrumentation'
|
22
18
|
|
19
|
+
# prepend_instrument and chain_instrument call extract_supportability_name
|
20
|
+
# to get the library name for supportability metrics and info-level logging.
|
21
|
+
# This is done by spliting on the 2nd to last spot of the instrumented
|
22
|
+
# module. If this isn't how we want the name to appear, pass in the desired
|
23
|
+
# name as a third argument.
|
23
24
|
if use_prepend?
|
24
|
-
|
25
|
+
require_relative '<%= @snake_name.downcase %>/prepend'
|
26
|
+
prepend_instrument <%= @class_name %>, NewRelic::Agent::Instrumentation::<%= @class_name %>::Prepend
|
25
27
|
else
|
28
|
+
require_relative '<%= @snake_name.downcase %>/chain'
|
26
29
|
chain_instrument NewRelic::Agent::Instrumentation::<%= @class_name %>::Chain
|
27
30
|
end
|
28
31
|
end
|