newrelic_rpm 9.10.2 → 9.12.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +91 -1
- data/README.md +1 -1
- data/lib/new_relic/agent/agent_logger.rb +1 -0
- data/lib/new_relic/agent/configuration/default_source.rb +114 -3
- data/lib/new_relic/agent/database/obfuscator.rb +1 -0
- data/lib/new_relic/agent/error_collector.rb +14 -10
- 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 +25 -0
- data/lib/new_relic/agent/instrumentation/bunny/instrumentation.rb +14 -0
- data/lib/new_relic/agent/instrumentation/elasticsearch/instrumentation.rb +1 -1
- 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 +27 -0
- 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/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 +5 -0
- data/lib/new_relic/agent/instrumentation/stripe_subscriber.rb +22 -1
- data/lib/new_relic/agent/local_log_decorator.rb +8 -1
- data/lib/new_relic/agent/log_event_aggregator.rb +91 -26
- data/lib/new_relic/control/instance_methods.rb +1 -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/rack/browser_monitoring.rb +11 -7
- data/lib/new_relic/version.rb +2 -2
- data/lib/tasks/config.rake +5 -2
- data/lib/tasks/gha.rake +31 -0
- data/lib/tasks/helpers/config.html.erb +1 -1
- data/lib/tasks/helpers/format.rb +1 -1
- data/lib/tasks/helpers/newrelicyml.rb +3 -2
- data/lib/tasks/instrumentation_generator/instrumentation.thor +1 -0
- data/newrelic.yml +202 -135
- data/newrelic_rpm.gemspec +2 -0
- data/test/agent_helper.rb +9 -0
- metadata +42 -3
@@ -0,0 +1,21 @@
|
|
1
|
+
# This file is distributed under New Relic's license terms.
|
2
|
+
# See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
|
3
|
+
# frozen_string_literal: true
|
4
|
+
|
5
|
+
module NewRelic::Agent::Instrumentation
|
6
|
+
module LogStasher::Chain
|
7
|
+
def self.instrument!
|
8
|
+
::LogStasher.singleton_class.class_eval do
|
9
|
+
include NewRelic::Agent::Instrumentation::LogStasher
|
10
|
+
|
11
|
+
alias_method(:build_logstash_event_without_new_relic, :build_logstash_event)
|
12
|
+
|
13
|
+
def build_logstash_event(*args)
|
14
|
+
build_logstash_event_with_new_relic(*args) do
|
15
|
+
build_logstash_event_without_new_relic(*args)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# This file is distributed under New Relic's license terms.
|
2
|
+
# See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
|
3
|
+
# frozen_string_literal: true
|
4
|
+
|
5
|
+
module NewRelic::Agent::Instrumentation
|
6
|
+
module LogStasher
|
7
|
+
INSTRUMENTATION_NAME = NewRelic::Agent.base_name(name)
|
8
|
+
|
9
|
+
def self.enabled?
|
10
|
+
NewRelic::Agent.config[:'instrumentation.logstasher'] != 'disabled'
|
11
|
+
end
|
12
|
+
|
13
|
+
def build_logstash_event_with_new_relic(*args)
|
14
|
+
logstasher_event = yield
|
15
|
+
log = logstasher_event.instance_variable_get(:@data)
|
16
|
+
|
17
|
+
::NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)
|
18
|
+
::NewRelic::Agent.agent.log_event_aggregator.record_logstasher_event(log)
|
19
|
+
::NewRelic::Agent::LocalLogDecorator.decorate(log)
|
20
|
+
|
21
|
+
logstasher_event
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# This file is distributed under New Relic's license terms.
|
2
|
+
# See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
|
3
|
+
# frozen_string_literal: true
|
4
|
+
|
5
|
+
module NewRelic::Agent::Instrumentation
|
6
|
+
module LogStasher::Prepend
|
7
|
+
include NewRelic::Agent::Instrumentation::LogStasher
|
8
|
+
|
9
|
+
def build_logstash_event(*args)
|
10
|
+
build_logstash_event_with_new_relic(*args) { super }
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,27 @@
|
|
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 'logstasher/instrumentation'
|
6
|
+
require_relative 'logstasher/chain'
|
7
|
+
require_relative 'logstasher/prepend'
|
8
|
+
|
9
|
+
DependencyDetection.defer do
|
10
|
+
named :logstasher
|
11
|
+
|
12
|
+
depends_on do
|
13
|
+
defined?(LogStasher) &&
|
14
|
+
Gem::Version.new(LogStasher::VERSION) >= Gem::Version.new('1.0.0') &&
|
15
|
+
NewRelic::Agent.config[:'application_logging.enabled']
|
16
|
+
end
|
17
|
+
|
18
|
+
executes do
|
19
|
+
NewRelic::Agent.logger.info('Installing LogStasher instrumentation')
|
20
|
+
|
21
|
+
if use_prepend?
|
22
|
+
prepend_instrument LogStasher.singleton_class, NewRelic::Agent::Instrumentation::LogStasher::Prepend
|
23
|
+
else
|
24
|
+
chain_instrument NewRelic::Agent::Instrumentation::LogStasher::Chain
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -13,6 +13,7 @@ module NewRelic
|
|
13
13
|
attr_accessor :_nr_deferred_detection_ran
|
14
14
|
end
|
15
15
|
builder_class._nr_deferred_detection_ran = false
|
16
|
+
NewRelic::Control::SecurityInterface.instance.wait = true
|
16
17
|
end
|
17
18
|
|
18
19
|
def deferred_dependency_check
|
@@ -21,6 +22,8 @@ module NewRelic
|
|
21
22
|
NewRelic::Agent.logger.info('Doing deferred dependency-detection before Rack startup')
|
22
23
|
DependencyDetection.detect!
|
23
24
|
self.class._nr_deferred_detection_ran = true
|
25
|
+
NewRelic::Control::SecurityInterface.instance.wait = false
|
26
|
+
NewRelic::Control::SecurityInterface.instance.init_agent
|
24
27
|
end
|
25
28
|
|
26
29
|
def check_for_late_instrumentation(app)
|
@@ -32,15 +32,19 @@ DependencyDetection.defer do
|
|
32
32
|
NewRelic::Agent::Instrumentation::ActionControllerSubscriber \
|
33
33
|
.subscribe(/^process_action.action_controller$/)
|
34
34
|
|
35
|
-
subs = %w[
|
35
|
+
subs = %w[exist_fragment?
|
36
|
+
expire_fragment
|
37
|
+
halted_callback
|
38
|
+
read_fragment
|
39
|
+
redirect_to
|
36
40
|
send_data
|
41
|
+
send_file
|
37
42
|
send_stream
|
38
|
-
|
39
|
-
|
40
|
-
unpermitted_parameters]
|
43
|
+
write_fragment
|
44
|
+
unpermitted_parameters].map { |s| Regexp.escape(s) }
|
41
45
|
|
42
46
|
# have to double escape period because its going from string -> regex
|
43
47
|
NewRelic::Agent::Instrumentation::ActionControllerOtherSubscriber \
|
44
|
-
.subscribe(Regexp.new("^(
|
48
|
+
.subscribe(Regexp.new("^(?:#{subs.join('|')})\\.action_controller$"))
|
45
49
|
end
|
46
50
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# This file is distributed under New Relic's license terms.
|
2
|
+
# See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
|
3
|
+
# frozen_string_literal: true
|
4
|
+
|
5
|
+
module NewRelic::Agent::Instrumentation
|
6
|
+
module RedisClient
|
7
|
+
module ClusterMiddleware
|
8
|
+
include NewRelic::Agent::Instrumentation::Redis
|
9
|
+
|
10
|
+
# Until we decide to move our Redis instrumentation entirely off patches
|
11
|
+
# keep the middleware instrumentation for the call and connect methods
|
12
|
+
# limited to the redis-clustering instrumentation.
|
13
|
+
#
|
14
|
+
# Redis's middleware option does not capture errors as high in the stack
|
15
|
+
# as our patches. Leaving the patches for call and connect on the main
|
16
|
+
# Redis gem limits the feature disparity our customers experience.
|
17
|
+
def call(*args, &block)
|
18
|
+
call_with_tracing(args[0]) { super }
|
19
|
+
end
|
20
|
+
|
21
|
+
def connect(*args, &block)
|
22
|
+
connect_with_tracing { super }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -9,14 +9,14 @@ module NewRelic::Agent::Instrumentation
|
|
9
9
|
INSTRUMENTATION_NAME = NewRelic::Agent.base_name(name)
|
10
10
|
|
11
11
|
def connect_with_tracing
|
12
|
-
with_tracing(Constants::CONNECT, database:
|
12
|
+
with_tracing(Constants::CONNECT, database: _nr_db) { yield }
|
13
13
|
end
|
14
14
|
|
15
15
|
def call_with_tracing(command, &block)
|
16
16
|
operation = command[0]
|
17
17
|
statement = ::NewRelic::Agent::Datastores::Redis.format_command(command)
|
18
18
|
|
19
|
-
with_tracing(operation, statement: statement, database:
|
19
|
+
with_tracing(operation, statement: statement, database: _nr_db) { yield }
|
20
20
|
end
|
21
21
|
|
22
22
|
# Used for Redis 4.x and 3.x
|
@@ -24,22 +24,15 @@ module NewRelic::Agent::Instrumentation
|
|
24
24
|
operation = pipeline.is_a?(::Redis::Pipeline::Multi) ? Constants::MULTI_OPERATION : Constants::PIPELINE_OPERATION
|
25
25
|
statement = ::NewRelic::Agent::Datastores::Redis.format_pipeline_commands(pipeline.commands)
|
26
26
|
|
27
|
-
with_tracing(operation, statement: statement, database:
|
27
|
+
with_tracing(operation, statement: statement, database: _nr_db) { yield }
|
28
28
|
end
|
29
29
|
|
30
30
|
# Used for Redis 5.x+
|
31
31
|
def call_pipelined_with_tracing(pipeline)
|
32
|
-
db = begin
|
33
|
-
_nr_redis_client_config.db
|
34
|
-
rescue StandardError => e
|
35
|
-
NewRelic::Agent.logger.error("Failed to determine configured Redis db value: #{e.class} - #{e.message}")
|
36
|
-
nil
|
37
|
-
end
|
38
|
-
|
39
32
|
operation = pipeline.flatten.include?('MULTI') ? Constants::MULTI_OPERATION : Constants::PIPELINE_OPERATION
|
40
33
|
statement = ::NewRelic::Agent::Datastores::Redis.format_pipeline_commands(pipeline)
|
41
34
|
|
42
|
-
with_tracing(operation, statement: statement, database:
|
35
|
+
with_tracing(operation, statement: statement, database: _nr_db) { yield }
|
43
36
|
end
|
44
37
|
|
45
38
|
private
|
@@ -94,5 +87,15 @@ module NewRelic::Agent::Instrumentation
|
|
94
87
|
config
|
95
88
|
end
|
96
89
|
end
|
90
|
+
|
91
|
+
def _nr_db
|
92
|
+
# db is a method on the Redis client in versions < 5.x
|
93
|
+
return db if respond_to?(:db)
|
94
|
+
# db is accessible through the RedisClient::Config object in versions > 5.x
|
95
|
+
return _nr_redis_client_config.db if _nr_redis_client_config.respond_to?(:db)
|
96
|
+
rescue StandardError => e
|
97
|
+
NewRelic::Agent.logger.debug("Failed to determine configured Redis db value: #{e.class} - #{e.message}")
|
98
|
+
nil
|
99
|
+
end
|
97
100
|
end
|
98
101
|
end
|
@@ -6,6 +6,9 @@ module NewRelic::Agent::Instrumentation
|
|
6
6
|
module RedisClient
|
7
7
|
module Middleware
|
8
8
|
# This module is used to instrument Redis 5.x+
|
9
|
+
#
|
10
|
+
# It only instruments call_pipelined because connect and call are accessed
|
11
|
+
# too late in the stack to capture all errors
|
9
12
|
include NewRelic::Agent::Instrumentation::Redis
|
10
13
|
|
11
14
|
def call_pipelined(*args, &block)
|
@@ -10,6 +10,7 @@ require_relative 'redis/chain'
|
|
10
10
|
require_relative 'redis/constants'
|
11
11
|
require_relative 'redis/prepend'
|
12
12
|
require_relative 'redis/middleware'
|
13
|
+
require_relative 'redis/cluster_middleware'
|
13
14
|
|
14
15
|
DependencyDetection.defer do
|
15
16
|
# Why not :redis? newrelic-redis used that name, so avoid conflicting
|
@@ -33,6 +34,10 @@ DependencyDetection.defer do
|
|
33
34
|
NewRelic::Agent.logger.info('Installing Redis Instrumentation')
|
34
35
|
if NewRelic::Agent::Instrumentation::Redis::Constants::HAS_REDIS_CLIENT
|
35
36
|
RedisClient.register(NewRelic::Agent::Instrumentation::RedisClient::Middleware)
|
37
|
+
|
38
|
+
if defined?(Redis::Cluster::Client)
|
39
|
+
return RedisClient.register(NewRelic::Agent::Instrumentation::RedisClient::ClusterMiddleware)
|
40
|
+
end
|
36
41
|
end
|
37
42
|
|
38
43
|
if use_prepend?
|
@@ -10,6 +10,7 @@ module NewRelic
|
|
10
10
|
EVENT_ATTRIBUTES = %i[http_status method num_retries path request_id].freeze
|
11
11
|
ATTRIBUTE_NAMESPACE = 'stripe.user_data'
|
12
12
|
ATTRIBUTE_FILTER_TYPES = %i[include exclude].freeze
|
13
|
+
PATH_PORTION_PATTERN = %r{^/([^/]+/[^/]+)(?:/|\z)}.freeze
|
13
14
|
|
14
15
|
def start_segment(event)
|
15
16
|
return unless is_execution_traced?
|
@@ -39,7 +40,27 @@ module NewRelic
|
|
39
40
|
end
|
40
41
|
|
41
42
|
def metric_name(event)
|
42
|
-
|
43
|
+
# Grab only the first 2 items from the slash (/) delimited event path.
|
44
|
+
# These items are the API version string and the category. Grabbing
|
45
|
+
# any more of the path will result in unique method names that will
|
46
|
+
# easily grow to be too numerous to sort through in the UI and
|
47
|
+
# possibly even violate default New Relic metric count thresholds.
|
48
|
+
# See newrelic/newrelic-ruby-agent#2654 and
|
49
|
+
# newrelic/newrelic-ruby-agent#2709 for more details.
|
50
|
+
#
|
51
|
+
# In Ruby v3.4 benchmarks, using regex to get at the first two path
|
52
|
+
# elements was seen as more performant than using String#split.
|
53
|
+
#
|
54
|
+
# Regex legend:
|
55
|
+
#
|
56
|
+
# ^ = starts with
|
57
|
+
# / = a literal '/'
|
58
|
+
# () = capture
|
59
|
+
# (?:) = don't capture
|
60
|
+
# [^/]+ = 1 or more characters that are not '/'
|
61
|
+
# /|\z = a literal '/' OR the end of the string
|
62
|
+
path_portion = event.path =~ PATH_PORTION_PATTERN ? Regexp.last_match(1) : NewRelic::UNKNOWN
|
63
|
+
"Stripe/#{path_portion}/#{event.method}"
|
43
64
|
end
|
44
65
|
|
45
66
|
def add_stripe_attributes(segment, event)
|
@@ -12,6 +12,12 @@ module NewRelic
|
|
12
12
|
return message unless decorating_enabled?
|
13
13
|
|
14
14
|
metadata = NewRelic::Agent.linking_metadata
|
15
|
+
|
16
|
+
if message.is_a?(Hash)
|
17
|
+
message.merge!(metadata) unless message.frozen?
|
18
|
+
return
|
19
|
+
end
|
20
|
+
|
15
21
|
formatted_metadata = " NR-LINKING|#{metadata[ENTITY_GUID_KEY]}|#{metadata[HOSTNAME_KEY]}|" \
|
16
22
|
"#{metadata[TRACE_ID_KEY]}|#{metadata[SPAN_ID_KEY]}|" \
|
17
23
|
"#{escape_entity_name(metadata[ENTITY_NAME_KEY])}|"
|
@@ -23,7 +29,8 @@ module NewRelic
|
|
23
29
|
|
24
30
|
def decorating_enabled?
|
25
31
|
NewRelic::Agent.config[:'application_logging.enabled'] &&
|
26
|
-
NewRelic::Agent::Instrumentation::Logger.enabled?
|
32
|
+
(NewRelic::Agent::Instrumentation::Logger.enabled? ||
|
33
|
+
NewRelic::Agent::Instrumentation::LogStasher.enabled?) &&
|
27
34
|
NewRelic::Agent.config[:'application_logging.local_decorating.enabled']
|
28
35
|
end
|
29
36
|
|
@@ -20,7 +20,8 @@ module NewRelic
|
|
20
20
|
DROPPED_METRIC = 'Logging/Forwarding/Dropped'.freeze
|
21
21
|
SEEN_METRIC = 'Supportability/Logging/Forwarding/Seen'.freeze
|
22
22
|
SENT_METRIC = 'Supportability/Logging/Forwarding/Sent'.freeze
|
23
|
-
|
23
|
+
LOGGER_SUPPORTABILITY_FORMAT = 'Supportability/Logging/Ruby/Logger/%s'.freeze
|
24
|
+
LOGSTASHER_SUPPORTABILITY_FORMAT = 'Supportability/Logging/Ruby/LogStasher/%s'.freeze
|
24
25
|
METRICS_SUPPORTABILITY_FORMAT = 'Supportability/Logging/Metrics/Ruby/%s'.freeze
|
25
26
|
FORWARDING_SUPPORTABILITY_FORMAT = 'Supportability/Logging/Forwarding/Ruby/%s'.freeze
|
26
27
|
DECORATING_SUPPORTABILITY_FORMAT = 'Supportability/Logging/LocalDecorating/Ruby/%s'.freeze
|
@@ -58,38 +59,71 @@ module NewRelic
|
|
58
59
|
end
|
59
60
|
|
60
61
|
def record(formatted_message, severity)
|
61
|
-
return unless
|
62
|
+
return unless logger_enabled?
|
62
63
|
|
63
64
|
severity = 'UNKNOWN' if severity.nil? || severity.empty?
|
65
|
+
increment_event_counters(severity)
|
66
|
+
|
67
|
+
return if formatted_message.nil? || formatted_message.empty?
|
68
|
+
return unless monitoring_conditions_met?(severity)
|
69
|
+
|
70
|
+
txn = NewRelic::Agent::Transaction.tl_current
|
71
|
+
priority = LogPriority.priority_for(txn)
|
64
72
|
|
65
|
-
if
|
66
|
-
|
67
|
-
|
68
|
-
|
73
|
+
return txn.add_log_event(create_event(priority, formatted_message, severity)) if txn
|
74
|
+
|
75
|
+
@lock.synchronize do
|
76
|
+
@buffer.append(priority: priority) do
|
77
|
+
create_event(priority, formatted_message, severity)
|
69
78
|
end
|
70
79
|
end
|
80
|
+
rescue
|
81
|
+
nil
|
82
|
+
end
|
71
83
|
|
72
|
-
|
73
|
-
return
|
74
|
-
|
75
|
-
|
84
|
+
def record_logstasher_event(log)
|
85
|
+
return unless logstasher_enabled?
|
86
|
+
|
87
|
+
# LogStasher logs do not inherently include a message key, so most logs are recorded.
|
88
|
+
# But when the key exists, we should not record the log if the message value is nil or empty.
|
89
|
+
return if log.key?('message') && (log['message'].nil? || log['message'].empty?)
|
90
|
+
|
91
|
+
severity = determine_severity(log)
|
92
|
+
increment_event_counters(severity)
|
93
|
+
|
94
|
+
return unless monitoring_conditions_met?(severity)
|
76
95
|
|
77
96
|
txn = NewRelic::Agent::Transaction.tl_current
|
78
97
|
priority = LogPriority.priority_for(txn)
|
79
98
|
|
80
|
-
if txn
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
create_event(priority, formatted_message, severity)
|
86
|
-
end
|
99
|
+
return txn.add_log_event(create_logstasher_event(priority, severity, log)) if txn
|
100
|
+
|
101
|
+
@lock.synchronize do
|
102
|
+
@buffer.append(priority: priority) do
|
103
|
+
create_logstasher_event(priority, severity, log)
|
87
104
|
end
|
88
105
|
end
|
89
106
|
rescue
|
90
107
|
nil
|
91
108
|
end
|
92
109
|
|
110
|
+
def monitoring_conditions_met?(severity)
|
111
|
+
!severity_too_low?(severity) && NewRelic::Agent.config[FORWARDING_ENABLED_KEY] && !@high_security
|
112
|
+
end
|
113
|
+
|
114
|
+
def determine_severity(log)
|
115
|
+
log['level'] ? log['level'].to_s.upcase : 'UNKNOWN'
|
116
|
+
end
|
117
|
+
|
118
|
+
def increment_event_counters(severity)
|
119
|
+
return unless NewRelic::Agent.config[METRICS_ENABLED_KEY]
|
120
|
+
|
121
|
+
@counter_lock.synchronize do
|
122
|
+
@seen += 1
|
123
|
+
@seen_by_severity[severity] += 1
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
93
127
|
def record_batch(txn, logs)
|
94
128
|
# Ensure we have the same shared priority
|
95
129
|
priority = LogPriority.priority_for(txn)
|
@@ -104,15 +138,17 @@ module NewRelic
|
|
104
138
|
end
|
105
139
|
end
|
106
140
|
|
107
|
-
def
|
108
|
-
|
109
|
-
|
110
|
-
event = LinkingMetadata.append_trace_linking_metadata({
|
141
|
+
def add_event_metadata(formatted_message, severity)
|
142
|
+
metadata = {
|
111
143
|
LEVEL_KEY => severity,
|
112
|
-
MESSAGE_KEY => formatted_message,
|
113
144
|
TIMESTAMP_KEY => Process.clock_gettime(Process::CLOCK_REALTIME) * 1000
|
114
|
-
}
|
145
|
+
}
|
146
|
+
metadata[MESSAGE_KEY] = formatted_message unless formatted_message.nil?
|
147
|
+
|
148
|
+
LinkingMetadata.append_trace_linking_metadata(metadata)
|
149
|
+
end
|
115
150
|
|
151
|
+
def create_prioritized_event(priority, event)
|
116
152
|
[
|
117
153
|
{
|
118
154
|
PrioritySampledBuffer::PRIORITY_KEY => priority
|
@@ -121,6 +157,31 @@ module NewRelic
|
|
121
157
|
]
|
122
158
|
end
|
123
159
|
|
160
|
+
def create_event(priority, formatted_message, severity)
|
161
|
+
formatted_message = truncate_message(formatted_message)
|
162
|
+
event = add_event_metadata(formatted_message, severity)
|
163
|
+
|
164
|
+
create_prioritized_event(priority, event)
|
165
|
+
end
|
166
|
+
|
167
|
+
def create_logstasher_event(priority, severity, log)
|
168
|
+
formatted_message = log['message'] ? truncate_message(log['message']) : nil
|
169
|
+
event = add_event_metadata(formatted_message, severity)
|
170
|
+
add_logstasher_event_attributes(event, log)
|
171
|
+
|
172
|
+
create_prioritized_event(priority, event)
|
173
|
+
end
|
174
|
+
|
175
|
+
def add_logstasher_event_attributes(event, log)
|
176
|
+
log_copy = log.dup
|
177
|
+
# Delete previously reported attributes
|
178
|
+
log_copy.delete('message')
|
179
|
+
log_copy.delete('level')
|
180
|
+
log_copy.delete('@timestamp')
|
181
|
+
|
182
|
+
event['attributes'] = log_copy
|
183
|
+
end
|
184
|
+
|
124
185
|
def add_custom_attributes(custom_attributes)
|
125
186
|
attributes.add_custom_attributes(custom_attributes)
|
126
187
|
end
|
@@ -166,10 +227,14 @@ module NewRelic
|
|
166
227
|
super
|
167
228
|
end
|
168
229
|
|
169
|
-
def
|
230
|
+
def logger_enabled?
|
170
231
|
@enabled && @instrumentation_logger_enabled
|
171
232
|
end
|
172
233
|
|
234
|
+
def logstasher_enabled?
|
235
|
+
@enabled && NewRelic::Agent::Instrumentation::LogStasher.enabled?
|
236
|
+
end
|
237
|
+
|
173
238
|
private
|
174
239
|
|
175
240
|
# We record once-per-connect metrics for enabled/disabled state at the
|
@@ -177,8 +242,8 @@ module NewRelic
|
|
177
242
|
def register_for_done_configuring(events)
|
178
243
|
events.subscribe(:server_source_configuration_added) do
|
179
244
|
@high_security = NewRelic::Agent.config[:high_security]
|
180
|
-
|
181
|
-
record_configuration_metric(
|
245
|
+
record_configuration_metric(LOGGER_SUPPORTABILITY_FORMAT, OVERALL_ENABLED_KEY)
|
246
|
+
record_configuration_metric(LOGSTASHER_SUPPORTABILITY_FORMAT, OVERALL_ENABLED_KEY)
|
182
247
|
record_configuration_metric(METRICS_SUPPORTABILITY_FORMAT, METRICS_ENABLED_KEY)
|
183
248
|
record_configuration_metric(FORWARDING_SUPPORTABILITY_FORMAT, FORWARDING_ENABLED_KEY)
|
184
249
|
record_configuration_metric(DECORATING_SUPPORTABILITY_FORMAT, DECORATING_ENABLED_KEY)
|
@@ -0,0 +1,57 @@
|
|
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 'singleton'
|
6
|
+
|
7
|
+
module NewRelic
|
8
|
+
class Control
|
9
|
+
class SecurityInterface
|
10
|
+
include Singleton
|
11
|
+
|
12
|
+
attr_accessor :wait
|
13
|
+
|
14
|
+
SUPPORTABILITY_PREFIX_SECURITY = 'Supportability/Ruby/SecurityAgent/Enabled/'
|
15
|
+
SUPPORTABILITY_PREFIX_SECURITY_AGENT = 'Supportability/Ruby/SecurityAgent/Agent/Enabled/'
|
16
|
+
ENABLED = 'enabled'
|
17
|
+
DISABLED = 'disabled'
|
18
|
+
|
19
|
+
def agent_started?
|
20
|
+
(@agent_started ||= false) == true
|
21
|
+
end
|
22
|
+
|
23
|
+
def waiting?
|
24
|
+
(@wait ||= false) == true
|
25
|
+
end
|
26
|
+
|
27
|
+
def init_agent
|
28
|
+
return if agent_started? || waiting?
|
29
|
+
|
30
|
+
record_supportability_metrics
|
31
|
+
|
32
|
+
if Agent.config[:'security.agent.enabled'] && !Agent.config[:high_security]
|
33
|
+
Agent.logger.info('Invoking New Relic security module')
|
34
|
+
require 'newrelic_security'
|
35
|
+
|
36
|
+
@agent_started = true
|
37
|
+
else
|
38
|
+
Agent.logger.info('New Relic Security is completely disabled by one of the user-provided configurations: `security.agent.enabled` or `high_security`. Not loading security capabilities.')
|
39
|
+
Agent.logger.info("high_security = #{Agent.config[:high_security]}")
|
40
|
+
Agent.logger.info("security.agent.enabled = #{Agent.config[:'security.agent.enabled']}")
|
41
|
+
end
|
42
|
+
rescue LoadError
|
43
|
+
Agent.logger.info('New Relic security agent not found - skipping')
|
44
|
+
rescue StandardError => exception
|
45
|
+
Agent.logger.error("Exception in New Relic security module loading: #{exception} #{exception.backtrace}")
|
46
|
+
end
|
47
|
+
|
48
|
+
def record_supportability_metrics
|
49
|
+
Agent.config[:'security.agent.enabled'] ? security_agent_metric(ENABLED) : security_agent_metric(DISABLED)
|
50
|
+
end
|
51
|
+
|
52
|
+
def security_agent_metric(setting)
|
53
|
+
NewRelic::Agent.record_metric_once(SUPPORTABILITY_PREFIX_SECURITY_AGENT + setting)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
data/lib/new_relic/control.rb
CHANGED
@@ -8,7 +8,6 @@ require 'new_relic/local_environment'
|
|
8
8
|
require 'new_relic/language_support'
|
9
9
|
require 'new_relic/helper'
|
10
10
|
|
11
|
-
require 'singleton'
|
12
11
|
require 'erb'
|
13
12
|
require 'socket'
|
14
13
|
require 'net/https'
|
@@ -18,6 +17,7 @@ require 'new_relic/control/server_methods'
|
|
18
17
|
require 'new_relic/control/instrumentation'
|
19
18
|
require 'new_relic/control/class_methods'
|
20
19
|
require 'new_relic/control/instance_methods'
|
20
|
+
require 'new_relic/control/security_interface'
|
21
21
|
|
22
22
|
require 'new_relic/agent'
|
23
23
|
require 'new_relic/delayed_job_injection'
|
@@ -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'
|
23
|
+
CONTENT_TYPE = 'Content-Type'
|
24
|
+
CONTENT_DISPOSITION = 'Content-Disposition'
|
25
|
+
CONTENT_LENGTH = 'Content-Length'
|
26
26
|
ATTACHMENT = /attachment/.freeze
|
27
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
|
@@ -120,7 +120,11 @@ module NewRelic
|
|
120
120
|
end
|
121
121
|
|
122
122
|
def streaming?(env, headers)
|
123
|
-
#
|
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.
|
124
128
|
return true if headers && headers['Transfer-Encoding'] == 'chunked'
|
125
129
|
|
126
130
|
defined?(ActionController::Live) &&
|
data/lib/new_relic/version.rb
CHANGED
data/lib/tasks/config.rake
CHANGED
@@ -23,13 +23,16 @@ namespace :newrelic do
|
|
23
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
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>"
|
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>"
|
27
29
|
}
|
28
30
|
|
29
31
|
NAME_OVERRIDES = {
|
30
32
|
'slow_sql' => 'Slow SQL [#slow-sql]',
|
31
33
|
'custom_insights_events' => 'Custom Events [#custom-events]',
|
32
|
-
'ai_monitoring' => 'AI Monitoring [#ai-monitoring]'
|
34
|
+
'ai_monitoring' => 'AI Monitoring [#ai-monitoring]',
|
35
|
+
'security_agent' => 'Security Agent [#security-agent]'
|
33
36
|
}
|
34
37
|
|
35
38
|
desc 'Describe available New Relic configuration settings'
|