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
@@ -92,14 +92,18 @@ module NewRelic
|
|
92
92
|
elsif type == Symbol
|
93
93
|
self[config_key] = value.to_sym
|
94
94
|
elsif type == Array
|
95
|
-
self[config_key] =
|
95
|
+
self[config_key] = if DEFAULTS[config_key].key?(:transform)
|
96
|
+
DEFAULTS[config_key][:transform].call(value)
|
97
|
+
else
|
98
|
+
value.split(/\s*,\s*/)
|
99
|
+
end
|
96
100
|
elsif type == NewRelic::Agent::Configuration::Boolean
|
97
101
|
if /false|off|no/i.match?(value)
|
98
102
|
self[config_key] = false
|
99
103
|
elsif !value.nil?
|
100
104
|
self[config_key] = true
|
101
105
|
end
|
102
|
-
|
106
|
+
elsif !serverless?
|
103
107
|
::NewRelic::Agent.logger.info("#{environment_key} does not have a corresponding configuration setting (#{config_key} does not exist).")
|
104
108
|
::NewRelic::Agent.logger.info('Run `rake newrelic:config:docs` or visit https://docs.newrelic.com/docs/apm/agents/ruby-agent/configuration/ruby-agent-configuration to see a list of available configuration settings.')
|
105
109
|
self[config_key] = value
|
@@ -114,6 +118,14 @@ module NewRelic
|
|
114
118
|
def collect_new_relic_environment_variable_keys
|
115
119
|
ENV.keys.select { |key| key.match(SUPPORTED_PREFIXES) }
|
116
120
|
end
|
121
|
+
|
122
|
+
# we can't rely on the :'serverless_mode.enabled' config parameter being
|
123
|
+
# set yet to signify serverless mode given that we're in the midst of
|
124
|
+
# building the config but we can always rely on the env var being set
|
125
|
+
# by the Lambda layer
|
126
|
+
def serverless?
|
127
|
+
NewRelic::Agent::ServerlessHandler.env_var_set?
|
128
|
+
end
|
117
129
|
end
|
118
130
|
end
|
119
131
|
end
|
@@ -34,6 +34,7 @@ module NewRelic
|
|
34
34
|
def initialize
|
35
35
|
reset_to_defaults
|
36
36
|
@callbacks = Hash.new { |hash, key| hash[key] = [] }
|
37
|
+
@lock = Mutex.new
|
37
38
|
end
|
38
39
|
|
39
40
|
def add_config_for_testing(source, level = 0)
|
@@ -137,7 +138,14 @@ module NewRelic
|
|
137
138
|
end
|
138
139
|
|
139
140
|
def evaluate_and_apply_transformations(key, value)
|
140
|
-
|
141
|
+
evaluated = evaluate_procs(value)
|
142
|
+
default = enforce_allowlist(key, evaluated)
|
143
|
+
return default if default
|
144
|
+
|
145
|
+
boolean = enforce_boolean(key, value)
|
146
|
+
evaluated = boolean if [true, false].include?(boolean)
|
147
|
+
|
148
|
+
apply_transformations(key, evaluated)
|
141
149
|
end
|
142
150
|
|
143
151
|
def apply_transformations(key, value)
|
@@ -145,7 +153,7 @@ module NewRelic
|
|
145
153
|
begin
|
146
154
|
transform.call(value)
|
147
155
|
rescue => e
|
148
|
-
|
156
|
+
NewRelic::Agent.logger.error("Error applying transformation for #{key}, pre-transform value was: #{value}.", e)
|
149
157
|
raise e
|
150
158
|
end
|
151
159
|
else
|
@@ -153,8 +161,33 @@ module NewRelic
|
|
153
161
|
end
|
154
162
|
end
|
155
163
|
|
164
|
+
def enforce_allowlist(key, value)
|
165
|
+
return unless allowlist = default_source.allowlist_for(key)
|
166
|
+
return if allowlist.include?(value)
|
167
|
+
|
168
|
+
default = default_source.default_for(key)
|
169
|
+
NewRelic::Agent.logger.warn "Invalid value '#{value}' for #{key}, applying default value of '#{default}'"
|
170
|
+
default
|
171
|
+
end
|
172
|
+
|
173
|
+
def enforce_boolean(key, value)
|
174
|
+
type = default_source.value_from_defaults(key, :type)
|
175
|
+
return unless type == Boolean
|
176
|
+
|
177
|
+
bool_value = default_source.boolean_for(key, value)
|
178
|
+
return bool_value unless bool_value.nil?
|
179
|
+
|
180
|
+
default = default_source.default_for(key)
|
181
|
+
NewRelic::Agent.logger.warn "Invalid value '#{value}' for #{key}, applying default value of '#{default}'"
|
182
|
+
default
|
183
|
+
end
|
184
|
+
|
156
185
|
def transform_from_default(key)
|
157
|
-
|
186
|
+
default_source.transform_for(key)
|
187
|
+
end
|
188
|
+
|
189
|
+
def default_source
|
190
|
+
NewRelic::Agent::Configuration::DefaultSource
|
158
191
|
end
|
159
192
|
|
160
193
|
def register_callback(key, &proc)
|
@@ -213,7 +246,7 @@ module NewRelic
|
|
213
246
|
begin
|
214
247
|
thawed_layer[k] = instance_eval(&v) if v.respond_to?(:call)
|
215
248
|
rescue => e
|
216
|
-
|
249
|
+
NewRelic::Agent.logger.debug("#{e.class.name} : #{e.message} - when accessing config key #{k}")
|
217
250
|
thawed_layer[k] = nil
|
218
251
|
end
|
219
252
|
thawed_layer.delete(:config)
|
@@ -364,9 +397,19 @@ module NewRelic
|
|
364
397
|
def reset_cache
|
365
398
|
return new_cache unless defined?(@cache) && @cache
|
366
399
|
|
367
|
-
|
368
|
-
|
369
|
-
|
400
|
+
# Modifying the @cache hash under JRuby - even with a `synchronize do`
|
401
|
+
# block and a `Hash#dup` operation - has been known to cause issues
|
402
|
+
# with JRuby for concurrent access of the hash while it is being
|
403
|
+
# modified. The hash really only needs to be modified for the benefit
|
404
|
+
# of the security agent, so if JRuby is in play and the security agent
|
405
|
+
# is not, don't attempt to modify the hash at all and return early.
|
406
|
+
return new_cache if NewRelic::LanguageSupport.jruby? && !Agent.config[:'security.agent.enabled']
|
407
|
+
|
408
|
+
@lock.synchronize do
|
409
|
+
preserved = @cache.dup.select { |_k, v| DEPENDENCY_DETECTION_VALUES.include?(v) }
|
410
|
+
new_cache
|
411
|
+
preserved.each { |k, v| @cache[k] = v }
|
412
|
+
end
|
370
413
|
|
371
414
|
@cache
|
372
415
|
end
|
@@ -380,7 +423,7 @@ module NewRelic
|
|
380
423
|
# is expensive enough that we don't want to do it unless we're
|
381
424
|
# actually going to be logging the message based on our current log
|
382
425
|
# level, so use a `do` block.
|
383
|
-
|
426
|
+
NewRelic::Agent.logger.debug do
|
384
427
|
hash = flattened.delete_if { |k, _h| DEFAULTS.fetch(k, {}).fetch(:exclude_from_reported_settings, false) }
|
385
428
|
"Updating config (#{direction}) from #{source.class}. Results: #{hash.inspect}"
|
386
429
|
end
|
@@ -7,6 +7,8 @@ require 'new_relic/agent/configuration/dotted_hash'
|
|
7
7
|
module NewRelic
|
8
8
|
module Agent
|
9
9
|
module Configuration
|
10
|
+
# The Language Security Policy Source gives customers the ability to
|
11
|
+
# configure high security mode settings.
|
10
12
|
class SecurityPolicySource < DottedHash
|
11
13
|
class << self
|
12
14
|
def enabled?(option)
|
@@ -147,6 +149,15 @@ module NewRelic
|
|
147
149
|
permitted_fn: nil
|
148
150
|
}
|
149
151
|
],
|
152
|
+
'ai_monitoring' => [
|
153
|
+
{
|
154
|
+
option: :'ai_monitoring.enabled',
|
155
|
+
supported: true,
|
156
|
+
enabled_fn: method(:enabled?),
|
157
|
+
disabled_value: false,
|
158
|
+
permitted_fn: nil
|
159
|
+
}
|
160
|
+
],
|
150
161
|
'allow_raw_exception_messages' => [
|
151
162
|
{
|
152
163
|
option: :'strip_exception_messages.enabled',
|
@@ -24,7 +24,7 @@ module NewRelic
|
|
24
24
|
:host => local_host,
|
25
25
|
:display_host => Agent.config[:'process_host.display_name'],
|
26
26
|
:app_name => Agent.config[:app_name],
|
27
|
-
:language =>
|
27
|
+
:language => LANGUAGE,
|
28
28
|
:labels => Agent.config.parsed_labels,
|
29
29
|
:agent_version => NewRelic::VERSION::STRING,
|
30
30
|
:environment => @environment_report,
|
@@ -14,6 +14,9 @@ module NewRelic
|
|
14
14
|
TIMESTAMP = 'timestamp'.freeze
|
15
15
|
PRIORITY = 'priority'.freeze
|
16
16
|
EVENT_TYPE_REGEX = /^[a-zA-Z0-9:_ ]+$/.freeze
|
17
|
+
MAX_ATTRIBUTE_COUNT = 64
|
18
|
+
MAX_ATTRIBUTE_SIZE = 4095
|
19
|
+
MAX_NAME_SIZE = 255
|
17
20
|
|
18
21
|
named :CustomEventAggregator
|
19
22
|
capacity_key :'custom_insights_events.max_samples_stored'
|
@@ -49,10 +52,33 @@ module NewRelic
|
|
49
52
|
{TYPE => type,
|
50
53
|
TIMESTAMP => Process.clock_gettime(Process::CLOCK_REALTIME).to_i,
|
51
54
|
PRIORITY => priority},
|
52
|
-
|
55
|
+
create_custom_event_attributes(type, attributes)
|
53
56
|
]
|
54
57
|
end
|
55
58
|
|
59
|
+
def create_custom_event_attributes(type, attributes)
|
60
|
+
result = AttributeProcessing.flatten_and_coerce(attributes)
|
61
|
+
|
62
|
+
if result.size > MAX_ATTRIBUTE_COUNT
|
63
|
+
NewRelic::Agent.logger.warn("Custom event attributes are limited to #{MAX_ATTRIBUTE_COUNT}. Discarding #{result.size - MAX_ATTRIBUTE_COUNT} attributes")
|
64
|
+
result = result.first(MAX_ATTRIBUTE_COUNT)
|
65
|
+
end
|
66
|
+
|
67
|
+
result.each_with_object({}) do |(key, val), new_result|
|
68
|
+
# name is limited to 255
|
69
|
+
if key.is_a?(String) && key.length > MAX_NAME_SIZE
|
70
|
+
key = key[0, MAX_NAME_SIZE]
|
71
|
+
end
|
72
|
+
|
73
|
+
# value is limited to 4095 except for LLM content-related events
|
74
|
+
if val.is_a?(String) && val.length > MAX_ATTRIBUTE_SIZE
|
75
|
+
val = val[0, MAX_ATTRIBUTE_SIZE] unless NewRelic::Agent::LLM.exempt_event_attribute?(type, key)
|
76
|
+
end
|
77
|
+
|
78
|
+
new_result[key] = val
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
56
82
|
def after_initialize
|
57
83
|
@type_strings = Hash.new { |hash, key| hash[key] = key.to_s.freeze }
|
58
84
|
end
|
@@ -7,17 +7,17 @@ module NewRelic
|
|
7
7
|
module Database
|
8
8
|
module ObfuscationHelpers
|
9
9
|
COMPONENTS_REGEX_MAP = {
|
10
|
-
:
|
11
|
-
:
|
12
|
-
:
|
13
|
-
:
|
14
|
-
:
|
15
|
-
:
|
16
|
-
:
|
17
|
-
:
|
18
|
-
:
|
19
|
-
:
|
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,6 +277,7 @@ 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
|
244
281
|
|
245
282
|
def symbolized_adapter(adapter)
|
246
283
|
if adapter.start_with?(POSTGRES_PREFIX) || adapter == POSTGIS_PREFIX
|
@@ -253,6 +290,8 @@ module NewRelic
|
|
253
290
|
:mysql2
|
254
291
|
elsif adapter.start_with?(SQLITE_PREFIX)
|
255
292
|
:sqlite
|
293
|
+
elsif adapter == TRILOGY_PREFIX
|
294
|
+
:trilogy
|
256
295
|
else
|
257
296
|
adapter.to_sym
|
258
297
|
end
|
@@ -35,7 +35,7 @@ module NewRelic
|
|
35
35
|
|
36
36
|
class << self
|
37
37
|
def for_transaction(transaction)
|
38
|
-
return nil unless connected?
|
38
|
+
return nil unless Agent.instance.connected?
|
39
39
|
|
40
40
|
payload = new
|
41
41
|
payload.version = VERSION
|
@@ -101,10 +101,6 @@ module NewRelic
|
|
101
101
|
transaction.current_segment.guid
|
102
102
|
end
|
103
103
|
end
|
104
|
-
|
105
|
-
def connected?
|
106
|
-
Agent.instance.connected?
|
107
|
-
end
|
108
104
|
end
|
109
105
|
|
110
106
|
attr_accessor :version,
|
@@ -110,6 +110,29 @@ module NewRelic
|
|
110
110
|
false
|
111
111
|
end
|
112
112
|
|
113
|
+
# Neither ignored nor expected errors impact apdex.
|
114
|
+
#
|
115
|
+
# Ignored errors are checked via `#error_is_ignored?`
|
116
|
+
# Expected errors are checked in 2 separate ways:
|
117
|
+
# 1. The presence of an `expected: true` attribute key/value pair in the
|
118
|
+
# options hash, which will be set if that key/value pair was used in
|
119
|
+
# the `notice_error` public API.
|
120
|
+
# 2. By calling `#expected?` which in turn calls `ErrorFilter#expected?`
|
121
|
+
# which checks for 3 things:
|
122
|
+
# - A match for user-defined HTTP status codes to expect
|
123
|
+
# - A match for user-defined error classes to expect
|
124
|
+
# - A match for user-defined error messages to expect
|
125
|
+
def error_affects_apdex?(error, options)
|
126
|
+
return false if error_is_ignored?(error)
|
127
|
+
return false if options[:expected]
|
128
|
+
|
129
|
+
!expected?(error, ::NewRelic::Agent::Tracer.state.current_transaction&.http_response_code)
|
130
|
+
rescue => e
|
131
|
+
NewRelic::Agent.logger.error("Could not determine if error '#{error}' should impact Apdex - " \
|
132
|
+
"#{e.class}: #{e.message}. Defaulting to 'true' (it should impact Apdex).")
|
133
|
+
true
|
134
|
+
end
|
135
|
+
|
113
136
|
# Calling instance_variable_set on a wrapped Java object in JRuby will
|
114
137
|
# generate a warning unless that object's class has already been marked
|
115
138
|
# as persistent, so we skip tagging of exception objects that are actually
|
@@ -214,7 +237,10 @@ module NewRelic
|
|
214
237
|
end
|
215
238
|
|
216
239
|
def notice_segment_error(segment, exception, options = {})
|
217
|
-
|
240
|
+
status_code = process_http_status_code(exception, options)
|
241
|
+
return if skip_notice_error?(exception, status_code)
|
242
|
+
|
243
|
+
options.merge!(segment.llm_event.error_attributes(exception)) if segment.llm_event
|
218
244
|
|
219
245
|
segment.set_noticed_error(create_noticed_error(exception, options))
|
220
246
|
exception
|
@@ -225,15 +251,13 @@ module NewRelic
|
|
225
251
|
|
226
252
|
# See NewRelic::Agent.notice_error for options and commentary
|
227
253
|
def notice_error(exception, options = {}, span_id = nil)
|
228
|
-
|
229
|
-
transaction = state.current_transaction
|
230
|
-
status_code = transaction&.http_response_code
|
231
|
-
|
254
|
+
status_code = process_http_status_code(exception, options)
|
232
255
|
return if skip_notice_error?(exception, status_code)
|
233
256
|
|
234
257
|
tag_exception(exception)
|
235
258
|
|
236
|
-
|
259
|
+
state = ::NewRelic::Agent::Tracer.state
|
260
|
+
if options[:expected]
|
237
261
|
increment_expected_error_count!(state, exception)
|
238
262
|
else
|
239
263
|
increment_error_count!(state, exception, options)
|
@@ -241,10 +265,8 @@ module NewRelic
|
|
241
265
|
|
242
266
|
noticed_error = create_noticed_error(exception, options)
|
243
267
|
error_trace_aggregator.add_to_error_queue(noticed_error)
|
244
|
-
|
245
|
-
|
246
|
-
span_id ||= transaction&.current_segment ? transaction.current_segment.guid : nil
|
247
|
-
error_event_aggregator.record(noticed_error, payload, span_id)
|
268
|
+
span_id ||= state.current_transaction&.current_segment&.guid
|
269
|
+
error_event_aggregator.record(noticed_error, state.current_transaction&.payload, span_id)
|
248
270
|
exception
|
249
271
|
rescue => e
|
250
272
|
::NewRelic::Agent.logger.warn("Failure when capturing error '#{exception}':", e)
|
@@ -334,6 +356,13 @@ module NewRelic
|
|
334
356
|
def error_group_callback
|
335
357
|
NewRelic::Agent.error_group_callback
|
336
358
|
end
|
359
|
+
|
360
|
+
def process_http_status_code(exception, options)
|
361
|
+
status_code = ::NewRelic::Agent::Tracer.state.current_transaction&.http_response_code
|
362
|
+
options[:expected] = true if !options[:expected] && @error_filter.expected?(exception, status_code)
|
363
|
+
|
364
|
+
status_code
|
365
|
+
end
|
337
366
|
end
|
338
367
|
end
|
339
368
|
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
|
-
|
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
|
@@ -70,18 +70,7 @@ module NewRelic
|
|
70
70
|
end
|
71
71
|
|
72
72
|
def get_explain_plan(statement)
|
73
|
-
|
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)
|
@@ -5,9 +5,13 @@
|
|
5
5
|
module NewRelic::Agent::Instrumentation
|
6
6
|
module ActiveSupportBroadcastLogger
|
7
7
|
def record_one_broadcast_with_new_relic(*args)
|
8
|
-
broadcasts[1..-1]
|
9
|
-
|
10
|
-
|
8
|
+
if broadcasts && broadcasts[1..-1]
|
9
|
+
broadcasts[1..-1].each { |broadcasted_logger| broadcasted_logger.instance_variable_set(:@skip_instrumenting, true) }
|
10
|
+
yield
|
11
|
+
broadcasts.each { |broadcasted_logger| broadcasted_logger.instance_variable_set(:@skip_instrumenting, false) }
|
12
|
+
else
|
13
|
+
NewRelic::Agent.logger.error('Error recording broadcasted logger')
|
14
|
+
end
|
11
15
|
end
|
12
16
|
end
|
13
17
|
end
|
@@ -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
|
@@ -10,13 +10,14 @@ DependencyDetection.defer do
|
|
10
10
|
named :async_http
|
11
11
|
|
12
12
|
depends_on do
|
13
|
-
defined?(Async::HTTP) &&
|
13
|
+
defined?(Async::HTTP) &&
|
14
|
+
Gem::Version.new(Async::HTTP::VERSION) >= Gem::Version.new('0.59.0') &&
|
15
|
+
!defined?(Traces::Backend::NewRelic) # defined in the traces-backend-newrelic gem
|
14
16
|
end
|
15
17
|
|
16
18
|
executes do
|
17
|
-
NewRelic::Agent.logger.info('Installing async_http instrumentation')
|
18
|
-
|
19
19
|
require 'async/http/internet'
|
20
|
+
|
20
21
|
if use_prepend?
|
21
22
|
prepend_instrument Async::HTTP::Internet, NewRelic::Agent::Instrumentation::AsyncHttp::Prepend
|
22
23
|
else
|
@@ -0,0 +1,33 @@
|
|
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 'instrumentation'
|
6
|
+
|
7
|
+
module NewRelic::Agent::Instrumentation
|
8
|
+
module AwsSdkLambda::Chain
|
9
|
+
def self.instrument!
|
10
|
+
::Aws::Lambda::Client.class_eval do
|
11
|
+
include NewRelic::Agent::Instrumentation::AwsSdkLambda
|
12
|
+
|
13
|
+
alias_method(:invoke_without_new_relic, :invoke)
|
14
|
+
|
15
|
+
def invoke(*args)
|
16
|
+
invoke_with_new_relic(*args) { invoke_without_new_relic(*args) }
|
17
|
+
end
|
18
|
+
|
19
|
+
alias_method(:invoke_async_without_new_relic, :invoke_async)
|
20
|
+
|
21
|
+
def invoke_async(*args)
|
22
|
+
invoke_async_with_new_relic(*args) { invoke_async_without_new_relic(*args) }
|
23
|
+
end
|
24
|
+
|
25
|
+
alias_method(:invoke_with_response_stream_without_new_relic, :invoke_with_response_stream)
|
26
|
+
|
27
|
+
def invoke_with_response_stream(*args)
|
28
|
+
invoke_with_response_stream_with_new_relic(*args) { invoke_with_response_stream_without_new_relic(*args) }
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|