newrelic_rpm 9.7.0 → 9.16.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +376 -2
- data/README.md +17 -18
- data/Rakefile +1 -1
- data/lib/boot/strap.rb +101 -0
- data/lib/new_relic/agent/agent.rb +4 -1
- data/lib/new_relic/agent/agent_helpers/connect.rb +10 -8
- data/lib/new_relic/agent/agent_helpers/start_worker_thread.rb +1 -1
- data/lib/new_relic/agent/agent_helpers/startup.rb +2 -1
- data/lib/new_relic/agent/agent_logger.rb +3 -1
- data/lib/new_relic/agent/aws.rb +68 -0
- data/lib/new_relic/agent/configuration/default_source.rb +519 -23
- data/lib/new_relic/agent/configuration/environment_source.rb +14 -2
- data/lib/new_relic/agent/configuration/high_security_source.rb +1 -0
- data/lib/new_relic/agent/configuration/manager.rb +51 -8
- data/lib/new_relic/agent/configuration/security_policy_source.rb +11 -0
- data/lib/new_relic/agent/configuration/yaml_source.rb +2 -0
- data/lib/new_relic/agent/connect/request_builder.rb +1 -1
- data/lib/new_relic/agent/custom_event_aggregator.rb +27 -1
- data/lib/new_relic/agent/database/obfuscation_helpers.rb +11 -11
- data/lib/new_relic/agent/database/obfuscator.rb +1 -0
- data/lib/new_relic/agent/database.rb +39 -0
- data/lib/new_relic/agent/distributed_tracing/distributed_trace_payload.rb +1 -5
- data/lib/new_relic/agent/error_collector.rb +39 -10
- data/lib/new_relic/agent/harvester.rb +1 -1
- 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 +3 -0
- data/lib/new_relic/agent/instrumentation/active_record_subscriber.rb +1 -12
- data/lib/new_relic/agent/instrumentation/active_support_broadcast_logger/instrumentation.rb +7 -3
- 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 +4 -3
- 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/chain.rb +37 -0
- data/lib/new_relic/agent/instrumentation/aws_sqs/instrumentation.rb +67 -0
- data/lib/new_relic/agent/instrumentation/aws_sqs/prepend.rb +21 -0
- data/lib/new_relic/agent/instrumentation/aws_sqs.rb +23 -0
- data/lib/new_relic/agent/instrumentation/bunny/instrumentation.rb +14 -0
- data/lib/new_relic/agent/instrumentation/bunny.rb +3 -4
- data/lib/new_relic/agent/instrumentation/concurrent_ruby.rb +1 -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/chain.rb +27 -0
- data/lib/new_relic/agent/instrumentation/dynamodb/instrumentation.rb +64 -0
- data/lib/new_relic/agent/instrumentation/dynamodb/prepend.rb +19 -0
- data/lib/new_relic/agent/instrumentation/dynamodb.rb +23 -0
- data/lib/new_relic/agent/instrumentation/elasticsearch/instrumentation.rb +58 -8
- 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.rb +1 -1
- data/lib/new_relic/agent/instrumentation/grpc/client/instrumentation.rb +0 -1
- data/lib/new_relic/agent/instrumentation/grpc_server.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/chain.rb +21 -0
- data/lib/new_relic/agent/instrumentation/logstasher/instrumentation.rb +24 -0
- data/lib/new_relic/agent/instrumentation/logstasher/prepend.rb +13 -0
- data/lib/new_relic/agent/instrumentation/logstasher.rb +25 -0
- data/lib/new_relic/agent/instrumentation/memcache.rb +0 -1
- data/lib/new_relic/agent/instrumentation/net_http/instrumentation.rb +6 -0
- 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/rack/instrumentation.rb +3 -0
- data/lib/new_relic/agent/instrumentation/rails_notifications/action_controller.rb +9 -5
- 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/cluster_middleware.rb +26 -0
- data/lib/new_relic/agent/instrumentation/redis/instrumentation.rb +14 -11
- data/lib/new_relic/agent/instrumentation/redis/middleware.rb +3 -0
- data/lib/new_relic/agent/instrumentation/redis.rb +11 -5
- data/lib/new_relic/agent/instrumentation/resque.rb +0 -4
- 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/ruby_openai/chain.rb +36 -0
- data/lib/new_relic/agent/instrumentation/ruby_openai/instrumentation.rb +196 -0
- data/lib/new_relic/agent/instrumentation/ruby_openai/prepend.rb +20 -0
- data/lib/new_relic/agent/instrumentation/ruby_openai.rb +35 -0
- 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/stripe_subscriber.rb +22 -1
- 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 +13 -6
- 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/llm/chat_completion_message.rb +25 -0
- data/lib/new_relic/agent/llm/chat_completion_summary.rb +66 -0
- data/lib/new_relic/agent/llm/embedding.rb +60 -0
- data/lib/new_relic/agent/llm/llm_event.rb +95 -0
- data/lib/new_relic/agent/llm/response_headers.rb +80 -0
- data/lib/new_relic/agent/llm.rb +49 -0
- data/lib/new_relic/agent/local_log_decorator.rb +8 -1
- data/lib/new_relic/agent/log_event_aggregator.rb +120 -44
- data/lib/new_relic/agent/messaging.rb +11 -5
- data/lib/new_relic/agent/new_relic_service.rb +12 -2
- data/lib/new_relic/agent/serverless_handler.rb +400 -0
- 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 +8 -10
- data/lib/new_relic/agent/system_info.rb +14 -0
- data/lib/new_relic/agent/threading/agent_thread.rb +1 -2
- data/lib/new_relic/agent/tracer.rb +5 -5
- data/lib/new_relic/agent/transaction/abstract_segment.rb +1 -1
- data/lib/new_relic/agent/transaction/external_request_segment.rb +0 -10
- 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/transaction/tracing.rb +2 -2
- data/lib/new_relic/agent/transaction.rb +2 -6
- data/lib/new_relic/agent/transaction_error_primitive.rb +23 -19
- data/lib/new_relic/agent.rb +198 -10
- data/lib/new_relic/constants.rb +2 -0
- 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/control/instance_methods.rb +8 -0
- data/lib/new_relic/control/private_instance_methods.rb +4 -0
- data/lib/new_relic/control/security_interface.rb +57 -0
- data/lib/new_relic/control.rb +1 -1
- data/lib/new_relic/dependency_detection.rb +10 -5
- 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 +14 -10
- data/lib/new_relic/rack/browser_monitoring.rb +28 -12
- data/lib/new_relic/supportability_helper.rb +2 -0
- data/lib/new_relic/thread_local_storage.rb +31 -0
- data/lib/new_relic/version.rb +2 -2
- data/lib/sequel/extensions/new_relic_instrumentation.rb +3 -2
- data/lib/tasks/config.rake +8 -3
- data/lib/tasks/gha.rake +31 -0
- data/lib/tasks/helpers/config.html.erb +3 -2
- data/lib/tasks/helpers/format.rb +1 -1
- data/lib/tasks/helpers/newrelicyml.rb +76 -13
- data/lib/tasks/instrumentation_generator/instrumentation.thor +31 -22
- data/lib/tasks/instrumentation_generator/templates/chain.tt +0 -1
- data/lib/tasks/instrumentation_generator/templates/chain_method.tt +0 -1
- data/lib/tasks/instrumentation_generator/templates/dependency_detection.tt +11 -8
- data/lib/tasks/instrumentation_generator/templates/newrelic.yml.tt +1 -1
- data/newrelic.yml +387 -143
- data/newrelic_rpm.gemspec +2 -0
- data/test/agent_helper.rb +17 -2
- metadata +80 -3
@@ -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
|
|
@@ -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
|
@@ -60,21 +60,22 @@ module NewRelic
|
|
60
60
|
|
61
61
|
def discover_dispatcher
|
62
62
|
dispatchers = %w[
|
63
|
+
serverless
|
64
|
+
puma
|
65
|
+
sidekiq
|
66
|
+
falcon
|
67
|
+
delayed_job
|
68
|
+
unicorn
|
63
69
|
passenger
|
70
|
+
resque
|
64
71
|
torquebox
|
65
72
|
trinidad
|
66
73
|
glassfish
|
67
|
-
resque
|
68
|
-
sidekiq
|
69
|
-
delayed_job
|
70
|
-
puma
|
71
74
|
thin
|
72
75
|
mongrel
|
73
76
|
litespeed
|
74
|
-
unicorn
|
75
77
|
webrick
|
76
78
|
fastcgi
|
77
|
-
falcon
|
78
79
|
]
|
79
80
|
while dispatchers.any? && @discovered_dispatcher.nil?
|
80
81
|
send('check_for_' + (dispatchers.shift))
|
@@ -141,10 +142,7 @@ module NewRelic
|
|
141
142
|
end
|
142
143
|
|
143
144
|
def check_for_falcon
|
144
|
-
|
145
|
-
NewRelic::LanguageSupport.object_space_usable?
|
146
|
-
|
147
|
-
@discovered_dispatcher = :falcon if find_class_in_object_space(::Falcon::Server)
|
145
|
+
@discovered_dispatcher = :falcon if defined?(::Falcon::Server) && File.basename($PROGRAM_NAME) == 'falcon'
|
148
146
|
end
|
149
147
|
|
150
148
|
def check_for_delayed_job
|
@@ -198,6 +196,12 @@ module NewRelic
|
|
198
196
|
@discovered_dispatcher = :passenger
|
199
197
|
end
|
200
198
|
|
199
|
+
def check_for_serverless
|
200
|
+
return unless NewRelic::Agent.config[:'serverless_mode.enabled']
|
201
|
+
|
202
|
+
@discovered_dispatcher = :serverless
|
203
|
+
end
|
204
|
+
|
201
205
|
public
|
202
206
|
|
203
207
|
# outputs a human-readable description
|
@@ -20,15 +20,15 @@ module NewRelic
|
|
20
20
|
# examine in order to look for a RUM insertion point.
|
21
21
|
SCAN_LIMIT = 50_000
|
22
22
|
|
23
|
-
CONTENT_TYPE = 'Content-Type'
|
24
|
-
CONTENT_DISPOSITION = 'Content-Disposition'
|
25
|
-
CONTENT_LENGTH = 'Content-Length'
|
26
|
-
ATTACHMENT =
|
27
|
-
TEXT_HTML =
|
23
|
+
CONTENT_TYPE = 'Content-Type'
|
24
|
+
CONTENT_DISPOSITION = 'Content-Disposition'
|
25
|
+
CONTENT_LENGTH = 'Content-Length'
|
26
|
+
ATTACHMENT = /attachment/.freeze
|
27
|
+
TEXT_HTML = %r{text/html}.freeze
|
28
28
|
|
29
|
-
BODY_START = '<body'
|
30
|
-
HEAD_START = '<head'
|
31
|
-
GT = '>'
|
29
|
+
BODY_START = '<body'
|
30
|
+
HEAD_START = '<head'
|
31
|
+
GT = '>'
|
32
32
|
|
33
33
|
ALREADY_INSTRUMENTED_KEY = 'newrelic.browser_monitoring_already_instrumented'
|
34
34
|
CHARSET_RE = /<\s*meta[^>]+charset\s*=[^>]*>/im.freeze
|
@@ -38,7 +38,7 @@ module NewRelic
|
|
38
38
|
result = @app.call(env)
|
39
39
|
(status, headers, response) = result
|
40
40
|
|
41
|
-
js_to_inject = NewRelic::Agent.browser_timing_header
|
41
|
+
js_to_inject = NewRelic::Agent.browser_timing_header(nonce(env))
|
42
42
|
if (js_to_inject != NewRelic::EMPTY_STR) && should_instrument?(env, status, headers)
|
43
43
|
response_string = autoinstrument_source(response, js_to_inject)
|
44
44
|
if headers.key?(CONTENT_LENGTH)
|
@@ -58,6 +58,14 @@ module NewRelic
|
|
58
58
|
end
|
59
59
|
end
|
60
60
|
|
61
|
+
def nonce(env)
|
62
|
+
return unless NewRelic::Agent.config[:'browser_monitoring.content_security_policy_nonce']
|
63
|
+
return unless NewRelic::Agent.config[:framework] == :rails_notifications
|
64
|
+
return unless defined?(ActionDispatch::ContentSecurityPolicy::Request)
|
65
|
+
|
66
|
+
env[ActionDispatch::ContentSecurityPolicy::Request::NONCE]
|
67
|
+
end
|
68
|
+
|
61
69
|
def should_instrument?(env, status, headers)
|
62
70
|
NewRelic::Agent.config[:'browser_monitoring.auto_instrument'] &&
|
63
71
|
status == 200 &&
|
@@ -65,6 +73,10 @@ module NewRelic
|
|
65
73
|
html?(headers) &&
|
66
74
|
!attachment?(headers) &&
|
67
75
|
!streaming?(env, headers)
|
76
|
+
rescue StandardError => e
|
77
|
+
NewRelic::Agent.logger.error('RUM instrumentation applicability check failed on exception:' \
|
78
|
+
"#{e.class} - #{e.message}")
|
79
|
+
false
|
68
80
|
end
|
69
81
|
|
70
82
|
private
|
@@ -100,15 +112,19 @@ module NewRelic
|
|
100
112
|
|
101
113
|
def html?(headers)
|
102
114
|
# needs else branch coverage
|
103
|
-
headers[CONTENT_TYPE]
|
115
|
+
headers[CONTENT_TYPE]&.match?(TEXT_HTML)
|
104
116
|
end
|
105
117
|
|
106
118
|
def attachment?(headers)
|
107
|
-
headers[CONTENT_DISPOSITION]&.
|
119
|
+
headers[CONTENT_DISPOSITION]&.match?(ATTACHMENT)
|
108
120
|
end
|
109
121
|
|
110
122
|
def streaming?(env, headers)
|
111
|
-
#
|
123
|
+
# Up until version 8.0, Rails would set 'Transfer-Encoding' to 'chunked'
|
124
|
+
# to trigger the desired HTTP/1.1 based streaming functionality in Rack.
|
125
|
+
# With version v8.0+, Rails assumes that the web server will be using
|
126
|
+
# Rack v3+ or an equally modern alternative and simply leaves the
|
127
|
+
# streaming behavior up to them.
|
112
128
|
return true if headers && headers['Transfer-Encoding'] == 'chunked'
|
113
129
|
|
114
130
|
defined?(ActionController::Live) &&
|
@@ -43,9 +43,11 @@ module NewRelic
|
|
43
43
|
:process_response_metadata,
|
44
44
|
:record_custom_event,
|
45
45
|
:record_metric,
|
46
|
+
:record_llm_feedback_event,
|
46
47
|
:recording_web_transaction?,
|
47
48
|
:require_test_helper,
|
48
49
|
:set_error_group_callback,
|
50
|
+
:set_llm_token_count_callback,
|
49
51
|
:set_segment_callback,
|
50
52
|
:set_sql_obfuscator,
|
51
53
|
:set_transaction_name,
|
@@ -0,0 +1,31 @@
|
|
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 ThreadLocalStorage
|
7
|
+
def self.get(thread, key)
|
8
|
+
if Agent.config[:thread_local_tracer_state]
|
9
|
+
thread.thread_variable_get(key)
|
10
|
+
else
|
11
|
+
thread[key]
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.set(thread, key, value)
|
16
|
+
if Agent.config[:thread_local_tracer_state]
|
17
|
+
thread.thread_variable_set(key, value)
|
18
|
+
else
|
19
|
+
thread[key] = value
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.[](key)
|
24
|
+
get(::Thread.current, key)
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.[]=(key, value)
|
28
|
+
set(::Thread.current, key, value)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/new_relic/version.rb
CHANGED
@@ -78,8 +78,9 @@ module Sequel
|
|
78
78
|
end
|
79
79
|
|
80
80
|
THREAD_SAFE_CONNECTION_POOL_CLASSES = [
|
81
|
-
(defined?(::Sequel::ThreadedConnectionPool) && ::Sequel::ThreadedConnectionPool)
|
82
|
-
|
81
|
+
(defined?(::Sequel::ThreadedConnectionPool) && ::Sequel::ThreadedConnectionPool),
|
82
|
+
(defined?(::Sequel::TimedQueueConnectionPool) && RUBY_VERSION >= '3.2' && ::Sequel::TimedQueueConnectionPool)
|
83
|
+
].compact.freeze
|
83
84
|
|
84
85
|
def explainer_for(sql)
|
85
86
|
proc do |*|
|
data/lib/tasks/config.rake
CHANGED
@@ -20,14 +20,19 @@ namespace :newrelic do
|
|
20
20
|
ATTRIBUTES => '[Attributes](/docs/features/agent-attributes) are key-value pairs containing information that determines the properties of an event or transaction. These key-value pairs can be viewed within transaction traces in APM, traced errors in APM, transaction events in dashboards, and page views in dashboards. You can customize exactly which attributes will be sent to each of these destinations',
|
21
21
|
'transaction_tracer' => 'The [transaction traces](/docs/apm/traces/transaction-traces/transaction-traces) feature collects detailed information from a selection of transactions, including a summary of the calling sequence, a breakdown of time spent, and a list of SQL queries and their query plans (on mysql and postgresql). Available features depend on your New Relic subscription level.',
|
22
22
|
'error_collector' => "The agent collects and reports all uncaught exceptions by default. These configuration options allow you to customize the error collection.\n\nFor information on ignored and expected errors, [see this page on Error Analytics in APM](/docs/agents/manage-apm-agents/agent-data/manage-errors-apm-collect-ignore-or-mark-expected/). To set expected errors via the `NewRelic::Agent.notice_error` Ruby method, [consult the Ruby agent API](/docs/agents/ruby-agent/api-guides/sending-handled-errors-new-relic/).",
|
23
|
-
'browser_monitoring' => "The browser
|
23
|
+
'browser_monitoring' => "The <InlinePopover type='browser' /> [page load timing](/docs/browser/new-relic-browser/page-load-timing/page-load-timing-process) feature (sometimes referred to as real user monitoring or RUM) gives you insight into the performance real users are experiencing with your website. This is accomplished by measuring the time it takes for your users' browsers to download and render your web pages by injecting a small amount of JavaScript code into the header and footer of each page.",
|
24
24
|
'application_logging' => "The Ruby agent supports [APM logs in context](/docs/apm/new-relic-apm/getting-started/get-started-logs-context). For some tips on configuring logs for the Ruby agent, see [Configure Ruby logs in context](/docs/logs/logs-context/configure-logs-context-ruby).\n\nAvailable logging-related config options include:",
|
25
|
-
'analytics_events' => '[New Relic dashboards](/docs/query-your-data/explore-query-data/dashboards/introduction-new-relic-one-dashboards) is a resource to gather and visualize data about your software and what it says about your business. With it you can quickly and easily create real-time dashboards to get immediate answers about end-user experiences, clickstreams, mobile activities, and server transactions.'
|
25
|
+
'analytics_events' => '[New Relic dashboards](/docs/query-your-data/explore-query-data/dashboards/introduction-new-relic-one-dashboards) is a resource to gather and visualize data about your software and what it says about your business. With it you can quickly and easily create real-time dashboards to get immediate answers about end-user experiences, clickstreams, mobile activities, and server transactions.',
|
26
|
+
'ai_monitoring' => "This section includes Ruby agent configurations for setting up AI monitoring.\n\n<Callout variant='important'>You need to enable distributed tracing to capture trace and feedback data. It is turned on by default in Ruby agents 8.0.0 and higher.</Callout>",
|
27
|
+
'security_agent' => "[New Relic Interactive Application Security Testing](https://docs.newrelic.com/docs/iast/introduction/) (IAST) tests your applications for any exploitable vulnerability by replaying the generated HTTP request with vulnerable payloads.\n\n<Callout variant='important'>Run IAST with non-production deployments only to avoid exposing vulnerabilities on your production software. \
|
28
|
+
IAST mode requires Ruby agent version 9.12.0 or higher and the [newrelic_security](https://rubygems.org/gems/newrelic_security) gem. Security agent configurations are disabled by default.</Callout>"
|
26
29
|
}
|
27
30
|
|
28
31
|
NAME_OVERRIDES = {
|
29
32
|
'slow_sql' => 'Slow SQL [#slow-sql]',
|
30
|
-
'custom_insights_events' => 'Custom Events [#custom-events]'
|
33
|
+
'custom_insights_events' => 'Custom Events [#custom-events]',
|
34
|
+
'ai_monitoring' => 'AI Monitoring [#ai-monitoring]',
|
35
|
+
'security_agent' => 'Security Agent [#security-agent]'
|
31
36
|
}
|
32
37
|
|
33
38
|
desc 'Describe available New Relic configuration settings'
|
data/lib/tasks/gha.rake
ADDED
@@ -0,0 +1,31 @@
|
|
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 'yaml'
|
6
|
+
require_relative 'helpers/version_bump'
|
7
|
+
|
8
|
+
# gha = GitHub Actions
|
9
|
+
namespace :gha do
|
10
|
+
# See .github/versions.yml
|
11
|
+
desc 'Update 3rd party action versions across all workflows'
|
12
|
+
task :update_versions do
|
13
|
+
gh_dir = File.expand_path('../../../.github', __FILE__)
|
14
|
+
info = YAML.load_file(File.join(gh_dir, 'versions.yml'))
|
15
|
+
workflows = Dir.glob(File.join(gh_dir, 'workflows', '*.yml'))
|
16
|
+
workflows.each do |workflow|
|
17
|
+
original = File.read(workflow)
|
18
|
+
modified = original.dup
|
19
|
+
info.each do |action, settings|
|
20
|
+
modified.gsub!(/uses: #{action}.*$/, "uses: #{action}@#{settings[:sha]} # tag #{settings[:tag]}")
|
21
|
+
end
|
22
|
+
|
23
|
+
if original != modified
|
24
|
+
File.open(workflow, 'w') { |f| f.puts modified }
|
25
|
+
puts "Updated #{workflow} with changes"
|
26
|
+
else
|
27
|
+
puts "#{workflow} remains unchanged"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -10,6 +10,7 @@ redirects:
|
|
10
10
|
- /docs/ruby/ruby-agent-configuration
|
11
11
|
- /docs/agents/ruby-agent/installation-and-configuration/ruby-agent-configuration
|
12
12
|
- /docs/agents/ruby-agent/installation-configuration/ruby-agent-configuration
|
13
|
+
freshnessValidatedDate: never
|
13
14
|
---
|
14
15
|
|
15
16
|
<CONTRIBUTOR_NOTE>
|
@@ -70,12 +71,12 @@ The Ruby agent looks for the following environment variables, in this order, to
|
|
70
71
|
|
71
72
|
If the Ruby agent doesn't detect values for any of those environment variables, it will default the application environment to `development` and read from the `development` section of the `newrelic.yml` config file.
|
72
73
|
|
73
|
-
When running the Ruby agent in a Rails app, the agent first looks for the `NEW_RELIC_ENV` environment variable to
|
74
|
+
When running the Ruby agent in a Rails app, the agent first looks for the `NEW_RELIC_ENV` environment variable to find the application environment and which section of the `newrelic.yml` to use. If `NEW_RELIC_ENV` is not present, the agent uses the Rails environment (`RAILS_ENV`).
|
74
75
|
|
75
76
|
When you edit the config file, be sure to:
|
76
77
|
|
77
78
|
* Indent only with two spaces.
|
78
|
-
* Indent only where relevant, in
|
79
|
+
* Indent only where relevant, in sections such as **`error_collector`**.
|
79
80
|
|
80
81
|
If you do not indent correctly, the agent may throw an `Unable to parse configuration file` error on startup.
|
81
82
|
|
data/lib/tasks/helpers/format.rb
CHANGED
@@ -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)
|
@@ -105,8 +152,9 @@ module NewRelicYML
|
|
105
152
|
def self.format_description(description)
|
106
153
|
# remove leading and trailing whitespace
|
107
154
|
description.strip!
|
108
|
-
# wrap text after 80 characters
|
109
|
-
|
155
|
+
# wrap text after 80 characters, assuming we're at one tabstop's (two
|
156
|
+
# spaces') level of indentation already
|
157
|
+
description.gsub!(/(.{1,78})(\s+|\Z)/, "\\1\n")
|
110
158
|
# add hashtags to lines
|
111
159
|
description = description.split("\n").map { |line| " # #{line}" }.join("\n")
|
112
160
|
|
@@ -125,15 +173,30 @@ module NewRelicYML
|
|
125
173
|
end
|
126
174
|
end
|
127
175
|
|
128
|
-
def self.
|
129
|
-
|
130
|
-
|
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
|
131
184
|
|
132
|
-
|
133
|
-
|
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"
|
134
189
|
end
|
135
190
|
|
136
|
-
|
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"
|
137
200
|
end
|
138
201
|
|
139
202
|
# :nocov:
|
@@ -31,15 +31,16 @@ class Instrumentation < Thor
|
|
31
31
|
|
32
32
|
def scaffold(name)
|
33
33
|
@name = name
|
34
|
+
@snake_name = snake_name(@name)
|
34
35
|
@method = options[:method] if options[:method]
|
35
36
|
@args = options[:args] if options[:args]
|
36
37
|
@class_name = ::NewRelic::LanguageSupport.camelize(name)
|
37
|
-
base_path = "#{INSTRUMENTATION_ROOT}#{
|
38
|
+
base_path = "#{INSTRUMENTATION_ROOT}#{@snake_name}"
|
38
39
|
|
39
40
|
empty_directory(base_path)
|
40
41
|
create_instrumentation_files(base_path)
|
41
|
-
append_to_default_source(name)
|
42
|
-
append_to_newrelic_yml(name)
|
42
|
+
append_to_default_source(@name, @snake_name)
|
43
|
+
# append_to_newrelic_yml(@name, @snake_name) # This is now done on release, we don't need it anymore, but leaving it to be sure.
|
43
44
|
create_tests(name)
|
44
45
|
end
|
45
46
|
|
@@ -69,53 +70,61 @@ class Instrumentation < Thor
|
|
69
70
|
def create_tests(name)
|
70
71
|
@name = name
|
71
72
|
@instrumentation_method_global_erb_snippet = '<%= $instrumentation_method %>'
|
72
|
-
|
73
|
+
@snake_name = snake_name(@name)
|
74
|
+
base_path = "#{MULTIVERSE_SUITE_ROOT}#{@snake_name}"
|
73
75
|
empty_directory(base_path)
|
74
76
|
template('templates/Envfile.tt', "#{base_path}/Envfile")
|
75
|
-
template('templates/test.tt', "#{base_path}/#{@
|
77
|
+
template('templates/test.tt', "#{base_path}/#{@snake_name}_instrumentation_test.rb")
|
76
78
|
|
77
79
|
empty_directory("#{base_path}/config")
|
78
80
|
template('templates/newrelic.yml.tt', "#{base_path}/config/newrelic.yml")
|
79
81
|
end
|
80
82
|
|
81
|
-
def append_to_default_source(name)
|
83
|
+
def append_to_default_source(name, snake_name)
|
82
84
|
insert_into_file(
|
83
85
|
DEFAULT_SOURCE_LOCATION,
|
84
|
-
config_block(name
|
85
|
-
after: ":description => 'Controls auto-instrumentation of bunny at start-up.
|
86
|
+
config_block(name, snake_name),
|
87
|
+
after: ":description => 'Controls auto-instrumentation of bunny at start-up. May be one of: `auto`, `prepend`, `chain`, `disabled`.'
|
86
88
|
},\n"
|
87
89
|
)
|
88
90
|
end
|
89
91
|
|
90
|
-
def append_to_newrelic_yml(name)
|
92
|
+
def append_to_newrelic_yml(name, snake_name)
|
91
93
|
insert_into_file(
|
92
94
|
NEWRELIC_YML_LOCATION,
|
93
|
-
yaml_block(name),
|
95
|
+
yaml_block(name, snake_name),
|
94
96
|
after: "# instrumentation.bunny: auto\n"
|
95
97
|
)
|
96
98
|
end
|
97
99
|
|
98
|
-
def config_block(name)
|
99
|
-
<<~
|
100
|
-
|
101
|
-
|
102
|
-
:
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
100
|
+
def config_block(name, snake_name)
|
101
|
+
# Don't change to <<~
|
102
|
+
# We want to preserve the whitespace so the config is correctly indented
|
103
|
+
<<-CONFIG
|
104
|
+
:'instrumentation.#{snake_name}' => {
|
105
|
+
:default => 'auto',
|
106
|
+
:documentation_default => 'auto',
|
107
|
+
:public => true,
|
108
|
+
:type => String,
|
109
|
+
:dynamic_name => true,
|
110
|
+
:allowed_from_server => false,
|
111
|
+
:description => 'Controls auto-instrumentation of the #{name} library at start-up. May be one of `auto`, `prepend`, `chain`, `disabled`.'
|
112
|
+
},
|
108
113
|
CONFIG
|
109
114
|
end
|
110
115
|
|
111
|
-
def yaml_block(name)
|
116
|
+
def yaml_block(name, snake_name)
|
112
117
|
<<~HEREDOC
|
113
118
|
|
114
119
|
# Controls auto-instrumentation of #{name} at start-up.
|
115
120
|
# May be one of [auto|prepend|chain|disabled]
|
116
|
-
# instrumentation.#{
|
121
|
+
# instrumentation.#{snake_name}: auto
|
117
122
|
HEREDOC
|
118
123
|
end
|
124
|
+
|
125
|
+
def snake_name(name)
|
126
|
+
name.downcase.tr('-', '_')
|
127
|
+
end
|
119
128
|
end
|
120
129
|
|
121
130
|
Instrumentation.start(ARGV)
|
@@ -9,7 +9,6 @@ module NewRelic::Agent::Instrumentation
|
|
9
9
|
include NewRelic::Agent::Instrumentation::<%= @class_name %>
|
10
10
|
|
11
11
|
alias_method(:<%= @method.downcase %>_without_new_relic, :<%= @method.downcase %>)
|
12
|
-
alias_method(:<%= @method.downcase %>, :<%= @method.downcase %>_with_new_relic)
|
13
12
|
|
14
13
|
def <%= @method.downcase %><%= "(#{@args})" unless @args.empty? %>
|
15
14
|
<%= @method.downcase %>_with_new_relic<%= "(#{@args})" unless @args.empty? %> do
|
@@ -1,5 +1,4 @@
|
|
1
1
|
alias_method(:<%= @method.downcase %>_without_new_relic, :<%= @method.downcase %>)
|
2
|
-
alias_method(:<%= @method.downcase %>, :<%= @method.downcase %>_with_new_relic)
|
3
2
|
|
4
3
|
def <%= @method.downcase %><%= "(#{@args})" unless @args.empty? %>
|
5
4
|
<%= @method.downcase %>_with_new_relic<%= "(#{@args})" unless @args.empty? %> do
|