newrelic_rpm 8.5.0 → 8.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +28 -1
  3. data/LICENSE +0 -6
  4. data/README.md +16 -18
  5. data/THIRD_PARTY_NOTICES.md +14 -199
  6. data/lib/new_relic/agent/agent.rb +21 -0
  7. data/lib/new_relic/agent/agent_logger.rb +7 -0
  8. data/lib/new_relic/agent/audit_logger.rb +4 -0
  9. data/lib/new_relic/agent/configuration/default_source.rb +72 -14
  10. data/lib/new_relic/agent/configuration/event_harvest_config.rb +4 -2
  11. data/lib/new_relic/agent/configuration/server_source.rb +1 -0
  12. data/lib/new_relic/agent/hostname.rb +16 -10
  13. data/lib/new_relic/agent/instrumentation/active_support_logger/chain.rb +23 -0
  14. data/lib/new_relic/agent/instrumentation/active_support_logger/instrumentation.rb +20 -0
  15. data/lib/new_relic/agent/instrumentation/active_support_logger/prepend.rb +12 -0
  16. data/lib/new_relic/agent/instrumentation/active_support_logger.rb +24 -0
  17. data/lib/new_relic/agent/instrumentation/curb/chain.rb +1 -1
  18. data/lib/new_relic/agent/instrumentation/curb/prepend.rb +1 -1
  19. data/lib/new_relic/agent/instrumentation/logger/instrumentation.rb +18 -18
  20. data/lib/new_relic/agent/instrumentation/logger.rb +4 -3
  21. data/lib/new_relic/agent/linking_metadata.rb +45 -0
  22. data/lib/new_relic/agent/local_log_decorator.rb +37 -0
  23. data/lib/new_relic/agent/log_event_aggregator.rb +234 -0
  24. data/lib/new_relic/agent/log_priority.rb +20 -0
  25. data/lib/new_relic/agent/new_relic_service.rb +14 -7
  26. data/lib/new_relic/agent/pipe_service.rb +4 -0
  27. data/lib/new_relic/agent/samplers/memory_sampler.rb +6 -1
  28. data/lib/new_relic/agent/transaction.rb +11 -0
  29. data/lib/new_relic/agent.rb +4 -14
  30. data/lib/new_relic/helper.rb +40 -0
  31. data/lib/new_relic/version.rb +1 -1
  32. data/lib/tasks/config.rake +3 -3
  33. data/newrelic.yml +20 -4
  34. data/newrelic_rpm.gemspec +1 -0
  35. data/test/agent_helper.rb +18 -4
  36. metadata +24 -3
  37. data/ROADMAP.md +0 -24
@@ -0,0 +1,234 @@
1
+ # encoding: utf-8
2
+ # This file is distributed under New Relic's license terms.
3
+ # See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
4
+
5
+ require 'new_relic/agent/event_aggregator'
6
+ require 'new_relic/agent/log_priority'
7
+
8
+ module NewRelic
9
+ module Agent
10
+ class LogEventAggregator < EventAggregator
11
+ # Per-message keys
12
+ LEVEL_KEY = "log.level".freeze
13
+ MESSAGE_KEY = "message".freeze
14
+ TIMESTAMP_KEY = "timestamp".freeze
15
+ PRIORITY_KEY = "priority".freeze
16
+
17
+ # Metric keys
18
+ LINES = "Logging/lines".freeze
19
+ DROPPED_METRIC = "Logging/Forwarding/Dropped".freeze
20
+ SEEN_METRIC = "Supportability/Logging/Forwarding/Seen".freeze
21
+ SENT_METRIC = "Supportability/Logging/Forwarding/Sent".freeze
22
+ OVERALL_SUPPORTABILITY_FORMAT = "Supportability/Logging/Ruby/Logger/%s".freeze
23
+ METRICS_SUPPORTABILITY_FORMAT = "Supportability/Logging/Metrics/Ruby/%s".freeze
24
+ FORWARDING_SUPPORTABILITY_FORMAT = "Supportability/Logging/Forwarding/Ruby/%s".freeze
25
+ DECORATING_SUPPORTABILITY_FORMAT = "Supportability/Logging/LocalDecorating/Ruby/%s".freeze
26
+ MAX_BYTES = 32768 # 32 * 1024 bytes (32 kibibytes)
27
+
28
+ named :LogEventAggregator
29
+ buffer_class PrioritySampledBuffer
30
+
31
+ capacity_key :'application_logging.forwarding.max_samples_stored'
32
+ enabled_key :'application_logging.enabled'
33
+
34
+ # Config keys
35
+ OVERALL_ENABLED_KEY = :'application_logging.enabled'
36
+ METRICS_ENABLED_KEY = :'application_logging.metrics.enabled'
37
+ FORWARDING_ENABLED_KEY = :'application_logging.forwarding.enabled'
38
+ DECORATING_ENABLED_KEY = :'application_logging.local_decorating.enabled'
39
+
40
+ def initialize(events)
41
+ super(events)
42
+ @counter_lock = Mutex.new
43
+ @seen = 0
44
+ @seen_by_severity = Hash.new(0)
45
+ @high_security = NewRelic::Agent.config[:high_security]
46
+ @instrumentation_logger_enabled = NewRelic::Agent::Instrumentation::Logger.enabled?
47
+ register_for_done_configuring(events)
48
+ end
49
+
50
+ def capacity
51
+ @buffer.capacity
52
+ end
53
+
54
+ def record(formatted_message, severity)
55
+ return unless enabled?
56
+
57
+ severity = "UNKNOWN" if severity.nil? || severity.empty?
58
+
59
+ if NewRelic::Agent.config[METRICS_ENABLED_KEY]
60
+ @counter_lock.synchronize do
61
+ @seen += 1
62
+ @seen_by_severity[severity] += 1
63
+ end
64
+ end
65
+
66
+ return if formatted_message.nil? || formatted_message.empty?
67
+ return unless NewRelic::Agent.config[:'application_logging.forwarding.enabled']
68
+ return if @high_security
69
+
70
+ txn = NewRelic::Agent::Transaction.tl_current
71
+ priority = LogPriority.priority_for(txn)
72
+
73
+ if txn
74
+ return txn.add_log_event(create_event(priority, formatted_message, severity))
75
+ else
76
+ return @lock.synchronize do
77
+ @buffer.append(priority: priority) do
78
+ create_event(priority, formatted_message, severity)
79
+ end
80
+ end
81
+ end
82
+ rescue
83
+ nil
84
+ end
85
+
86
+ def record_batch txn, logs
87
+ # Ensure we have the same shared priority
88
+ priority = LogPriority.priority_for(txn)
89
+ logs.each do |log|
90
+ log.first[PRIORITY_KEY] = priority
91
+ end
92
+
93
+ @lock.synchronize do
94
+ logs.each do |log|
95
+ @buffer.append(event: log)
96
+ end
97
+ end
98
+ end
99
+
100
+ def create_event priority, formatted_message, severity
101
+ formatted_message = truncate_message(formatted_message)
102
+
103
+ event = LinkingMetadata.append_trace_linking_metadata({
104
+ LEVEL_KEY => severity,
105
+ MESSAGE_KEY => formatted_message,
106
+ TIMESTAMP_KEY => Process.clock_gettime(Process::CLOCK_REALTIME) * 1000
107
+ })
108
+
109
+ [
110
+ {
111
+ PrioritySampledBuffer::PRIORITY_KEY => priority
112
+ },
113
+ event
114
+ ]
115
+ end
116
+
117
+ # Because our transmission format (MELT) is different than historical
118
+ # agent payloads, extract the munging here to keep the service focused
119
+ # on the general harvest + transmit instead of the format.
120
+ #
121
+ # Payload shape matches the publicly documented MELT format.
122
+ # https://docs.newrelic.com/docs/logs/log-api/introduction-log-api
123
+ #
124
+ # We have to keep the aggregated payloads in a separate shape, though, to
125
+ # work with the priority sampling buffers
126
+ def self.payload_to_melt_format(data)
127
+ common_attributes = LinkingMetadata.append_service_linking_metadata({})
128
+
129
+ # To save on unnecessary data transmission, trim the entity.type
130
+ # sent by classic logs-in-context
131
+ common_attributes.delete(ENTITY_TYPE_KEY)
132
+
133
+ _, items = data
134
+ payload = [{
135
+ common: {attributes: common_attributes},
136
+ logs: items.map(&:last)
137
+ }]
138
+
139
+ return [payload, items.size]
140
+ end
141
+
142
+ def harvest!
143
+ record_customer_metrics()
144
+ super
145
+ end
146
+
147
+ def reset!
148
+ @counter_lock.synchronize do
149
+ @seen = 0
150
+ @seen_by_severity.clear
151
+ end
152
+ super
153
+ end
154
+
155
+ def enabled?
156
+ @enabled && @instrumentation_logger_enabled
157
+ end
158
+
159
+ private
160
+
161
+ # We record once-per-connect metrics for enabled/disabled state at the
162
+ # point we consider the configuration stable (i.e. once we've gotten SSC)
163
+ def register_for_done_configuring(events)
164
+ events.subscribe(:server_source_configuration_added) do
165
+ @high_security = NewRelic::Agent.config[:high_security]
166
+
167
+ record_configuration_metric(OVERALL_SUPPORTABILITY_FORMAT, OVERALL_ENABLED_KEY)
168
+ record_configuration_metric(METRICS_SUPPORTABILITY_FORMAT, METRICS_ENABLED_KEY)
169
+ record_configuration_metric(FORWARDING_SUPPORTABILITY_FORMAT, FORWARDING_ENABLED_KEY)
170
+ record_configuration_metric(DECORATING_SUPPORTABILITY_FORMAT, DECORATING_ENABLED_KEY)
171
+ end
172
+ end
173
+
174
+ def record_configuration_metric(format, key)
175
+ state = NewRelic::Agent.config[key]
176
+ label = if !enabled?
177
+ "disabled"
178
+ else
179
+ state ? "enabled" : "disabled"
180
+ end
181
+ NewRelic::Agent.increment_metric(format % label)
182
+ end
183
+
184
+ def after_harvest metadata
185
+ dropped_count = metadata[:seen] - metadata[:captured]
186
+ note_dropped_events(metadata[:seen], dropped_count)
187
+ record_supportability_metrics(metadata[:seen], metadata[:captured], dropped_count)
188
+ end
189
+
190
+ # To avoid paying the cost of metric recording on every line, we hold
191
+ # these until harvest before recording them
192
+ def record_customer_metrics
193
+ return unless enabled?
194
+ return unless NewRelic::Agent.config[:'application_logging.metrics.enabled']
195
+
196
+ @counter_lock.synchronize do
197
+ return unless @seen > 0
198
+
199
+ NewRelic::Agent.increment_metric(LINES, @seen)
200
+ @seen_by_severity.each do |(severity, count)|
201
+ NewRelic::Agent.increment_metric(line_metric_name_by_severity(severity), count)
202
+ end
203
+
204
+ @seen = 0
205
+ @seen_by_severity.clear
206
+ end
207
+ end
208
+
209
+ def line_metric_name_by_severity(severity)
210
+ @line_metrics ||= {}
211
+ @line_metrics[severity] ||= "Logging/lines/#{severity}".freeze
212
+ end
213
+
214
+ def note_dropped_events total_count, dropped_count
215
+ if dropped_count > 0
216
+ NewRelic::Agent.logger.warn("Dropped #{dropped_count} log events out of #{total_count}.")
217
+ end
218
+ end
219
+
220
+ def record_supportability_metrics total_count, captured_count, dropped_count
221
+ return unless total_count > 0
222
+
223
+ NewRelic::Agent.increment_metric(DROPPED_METRIC, dropped_count)
224
+ NewRelic::Agent.increment_metric(SEEN_METRIC, total_count)
225
+ NewRelic::Agent.increment_metric(SENT_METRIC, captured_count)
226
+ end
227
+
228
+ def truncate_message(message)
229
+ return message if message.bytesize <= MAX_BYTES
230
+ message.byteslice(0...MAX_BYTES)
231
+ end
232
+ end
233
+ end
234
+ end
@@ -0,0 +1,20 @@
1
+ # encoding: utf-8
2
+ # This file is distributed under New Relic's license terms.
3
+ # See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
4
+
5
+ require 'new_relic/agent/event_aggregator'
6
+
7
+ # Stateless calculation of priority for a given log event
8
+ module NewRelic
9
+ module Agent
10
+ module LogPriority
11
+ extend self
12
+
13
+ def priority_for(txn)
14
+ return txn.priority if txn
15
+
16
+ rand.round(NewRelic::PRIORITY_PRECISION)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -177,18 +177,25 @@ module NewRelic
177
177
  :item_count => items.size)
178
178
  end
179
179
 
180
+ def log_event_data(data)
181
+ payload, size = LogEventAggregator.payload_to_melt_format(data)
182
+ invoke_remote(:log_event_data, payload, :item_count => size)
183
+ end
184
+
180
185
  def error_event_data(data)
181
186
  metadata, items = data
182
- invoke_remote(:error_event_data, [@agent_id, *data], :item_count => items.size)
187
+ response = invoke_remote(:error_event_data, [@agent_id, *data], :item_count => items.size)
183
188
  NewRelic::Agent.record_metric("Supportability/Events/TransactionError/Sent", :count => items.size)
184
189
  NewRelic::Agent.record_metric("Supportability/Events/TransactionError/Seen", :count => metadata[:events_seen])
190
+ response
185
191
  end
186
192
 
187
193
  def span_event_data(data)
188
194
  metadata, items = data
189
- invoke_remote(:span_event_data, [@agent_id, *data], :item_count => items.size)
195
+ response = invoke_remote(:span_event_data, [@agent_id, *data], :item_count => items.size)
190
196
  NewRelic::Agent.record_metric("Supportability/Events/SpanEvents/Sent", :count => items.size)
191
197
  NewRelic::Agent.record_metric("Supportability/Events/SpanEvents/Seen", :count => metadata[:events_seen])
198
+ response
192
199
  end
193
200
 
194
201
  # We do not compress if content is smaller than 64kb. There are
@@ -454,7 +461,7 @@ module NewRelic
454
461
  def handle_serialization_error(method, e)
455
462
  NewRelic::Agent.increment_metric("Supportability/serialization_failure")
456
463
  NewRelic::Agent.increment_metric("Supportability/serialization_failure/#{method}")
457
- msg = "Failed to serialize #{method} data using #{@marshaller.class.to_s}: #{e.inspect}"
464
+ msg = "Failed to serialize #{method} data using #{@marshaller.class}: #{e.inspect}"
458
465
  error = SerializationError.new(msg)
459
466
  error.set_backtrace(e.backtrace)
460
467
  raise error
@@ -464,11 +471,11 @@ module NewRelic
464
471
  serialize_time = serialize_finish_ts && (serialize_finish_ts - start_ts)
465
472
  request_duration = response_check_ts && (response_check_ts - request_send_ts)
466
473
  if request_duration
467
- NewRelic::Agent.record_metric("Supportability/Agent/Collector/#{method.to_s}/Duration", request_duration)
474
+ NewRelic::Agent.record_metric("Supportability/Agent/Collector/#{method}/Duration", request_duration)
468
475
  end
469
476
  if serialize_time
470
477
  NewRelic::Agent.record_metric("Supportability/invoke_remote_serialize", serialize_time)
471
- NewRelic::Agent.record_metric("Supportability/invoke_remote_serialize/#{method.to_s}", serialize_time)
478
+ NewRelic::Agent.record_metric("Supportability/invoke_remote_serialize/#{method}", serialize_time)
472
479
  end
473
480
  end
474
481
 
@@ -482,8 +489,8 @@ module NewRelic
482
489
  # of items as arguments.
483
490
  def record_size_supportability_metrics(method, size_bytes, item_count)
484
491
  metrics = [
485
- "Supportability/invoke_remote_size",
486
- "Supportability/invoke_remote_size/#{method.to_s}"
492
+ "Supportability/Ruby/Collector/Output/Bytes",
493
+ "Supportability/Ruby/Collector/#{method}/Output/Bytes"
487
494
  ]
488
495
  # we may not have an item count, in which case, just record 0 for the exclusive time
489
496
  item_count ||= 0
@@ -61,6 +61,10 @@ module NewRelic
61
61
  write_to_pipe(:sql_trace_data, sql) if sql
62
62
  end
63
63
 
64
+ def log_event_data(logs)
65
+ write_to_pipe(:log_event_data, logs) if logs
66
+ end
67
+
64
68
  def shutdown
65
69
  @pipe.close if @pipe
66
70
  end
@@ -3,6 +3,7 @@
3
3
  # See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
4
4
 
5
5
  require 'new_relic/agent/sampler'
6
+ require 'new_relic/helper'
6
7
 
7
8
  module NewRelic
8
9
  module Agent
@@ -46,7 +47,11 @@ module NewRelic
46
47
 
47
48
  def self.platform
48
49
  if RUBY_PLATFORM =~ /java/
49
- %x(uname -s).downcase
50
+ begin
51
+ NewRelic::Helper.run_command('uname -s').downcase
52
+ rescue NewRelic::CommandRunFailedError, NewRelic::CommandExecutableNotFoundError
53
+ 'unknown'
54
+ end
50
55
  else
51
56
  RUBY_PLATFORM.downcase
52
57
  end
@@ -77,6 +77,7 @@ module NewRelic
77
77
 
78
78
  attr_reader :guid,
79
79
  :metrics,
80
+ :logs,
80
81
  :gc_start_snapshot,
81
82
  :category,
82
83
  :attributes,
@@ -236,6 +237,7 @@ module NewRelic
236
237
 
237
238
  @exceptions = {}
238
239
  @metrics = TransactionMetrics.new
240
+ @logs = PrioritySampledBuffer.new(NewRelic::Agent.instance.log_event_aggregator.capacity)
239
241
  @guid = NewRelic::Agent::GuidGenerator.generate_guid
240
242
 
241
243
  @ignore_this_transaction = false
@@ -533,6 +535,7 @@ module NewRelic
533
535
 
534
536
  record_exceptions
535
537
  record_transaction_event
538
+ record_log_events
536
539
  merge_metrics
537
540
  send_transaction_finished_event
538
541
  end
@@ -732,6 +735,10 @@ module NewRelic
732
735
  agent.transaction_event_recorder.record payload
733
736
  end
734
737
 
738
+ def record_log_events
739
+ agent.log_event_aggregator.record_batch self, @logs.to_a
740
+ end
741
+
735
742
  def queue_time
736
743
  @apdex_start ? @start_time - @apdex_start : 0
737
744
  end
@@ -826,6 +833,10 @@ module NewRelic
826
833
  attributes.merge_custom_attributes(p)
827
834
  end
828
835
 
836
+ def add_log_event(event)
837
+ logs.append(event: event)
838
+ end
839
+
829
840
  def recording_web_transaction?
830
841
  web_category?(@category)
831
842
  end
@@ -59,6 +59,8 @@ module NewRelic
59
59
  require 'new_relic/agent/logging'
60
60
  require 'new_relic/agent/distributed_tracing'
61
61
  require 'new_relic/agent/attribute_processing'
62
+ require 'new_relic/agent/linking_metadata'
63
+ require 'new_relic/agent/local_log_decorator'
62
64
 
63
65
  require 'new_relic/agent/instrumentation/controller_instrumentation'
64
66
 
@@ -726,20 +728,8 @@ module NewRelic
726
728
  # @api public
727
729
  def linking_metadata
728
730
  metadata = Hash.new
729
- metadata[ENTITY_NAME_KEY] = config[:app_name][0]
730
- metadata[ENTITY_TYPE_KEY] = ENTITY_TYPE
731
- metadata[HOSTNAME_KEY] = Hostname.get
732
-
733
- if entity_guid = config[:entity_guid]
734
- metadata[ENTITY_GUID_KEY] = entity_guid
735
- end
736
-
737
- if trace_id = Tracer.current_trace_id
738
- metadata[TRACE_ID_KEY] = trace_id
739
- end
740
- if span_id = Tracer.current_span_id
741
- metadata[SPAN_ID_KEY] = span_id
742
- end
731
+ LinkingMetadata.append_service_linking_metadata(metadata)
732
+ LinkingMetadata.append_trace_linking_metadata(metadata)
743
733
  metadata
744
734
  end
745
735
 
@@ -3,8 +3,12 @@
3
3
  # See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
4
4
 
5
5
  require 'new_relic/language_support'
6
+ require 'open3'
6
7
 
7
8
  module NewRelic
9
+ class CommandExecutableNotFoundError < StandardError; end
10
+ class CommandRunFailedError < StandardError; end
11
+
8
12
  # A singleton for shared generic helper methods
9
13
  module Helper
10
14
  extend self
@@ -40,5 +44,41 @@ module NewRelic
40
44
  def time_to_millis(time)
41
45
  (time.to_f * 1000).round
42
46
  end
47
+
48
+ def run_command(command)
49
+ executable = command.split(' ').first
50
+ unless executable_in_path?(executable)
51
+ raise NewRelic::CommandExecutableNotFoundError.new("Executable not found: '#{executable}'")
52
+ end
53
+
54
+ exception = nil
55
+ begin
56
+ output, status = Open3.capture2e(command)
57
+ rescue => exception
58
+ end
59
+
60
+ if exception || !status.success?
61
+ message = exception ? "#{exception.class} - #{exception.message}" : output
62
+ raise NewRelic::CommandRunFailedError.new("Failed to run command '#{command}': #{message}")
63
+ end
64
+
65
+ output.chomp
66
+ end
67
+
68
+ # TODO: Open3 defers the actual excecution of a binary to Process.spawn,
69
+ # which will raise an Errno::ENOENT exception for a file that
70
+ # cannot be found. We might want to take the time to evaluate
71
+ # relying on that Process.spawn behavior instead of checking for
72
+ # existence ourselves. We'd need to see what it does, how efficient
73
+ # it is, if it differs in functionality between Ruby versions and
74
+ # operating systems, etc.
75
+ def executable_in_path?(executable)
76
+ return false unless ENV['PATH']
77
+
78
+ ENV['PATH'].split(File::PATH_SEPARATOR).any? do |bin_path|
79
+ executable_path = File.join(bin_path, executable)
80
+ File.exist?(executable_path) && File.file?(executable_path) && File.executable?(executable_path)
81
+ end
82
+ end
43
83
  end
44
84
  end
@@ -6,7 +6,7 @@
6
6
  module NewRelic
7
7
  module VERSION # :nodoc:
8
8
  MAJOR = 8
9
- MINOR = 5
9
+ MINOR = 6
10
10
  TINY = 0
11
11
 
12
12
  STRING = "#{MAJOR}.#{MINOR}.#{TINY}"
@@ -11,8 +11,8 @@ namespace :newrelic do
11
11
  DISABLING => 'Use these settings to toggle instrumentation types during agent startup.',
12
12
  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',
13
13
  "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.',
14
- "error_collector" => 'The agent collects and reports all uncaught exceptions by default. These configuration options allow you to customize the error collection.',
15
- "browser_monitoring" => 'The Browser monitoring [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.',
14
+ "error_collector" => 'The agent collects and reports all uncaught exceptions by default. These configuration options allow you to customize the error collection.\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/).',
15
+ "browser_monitoring" => "The browser monitoring [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.",
16
16
  "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.'
17
17
  }
18
18
 
@@ -24,7 +24,7 @@ namespace :newrelic do
24
24
  config_hash = build_config_hash
25
25
  sections = flatten_config_hash(config_hash)
26
26
 
27
- puts build_erb(format).result(binding)
27
+ puts build_erb(format).result(binding).split("\n").map(&:rstrip).join("\n").gsub('. ', '. ')
28
28
  sections # silences unused warning to return this
29
29
  end
30
30
 
data/newrelic.yml CHANGED
@@ -26,6 +26,22 @@ common: &default_settings
26
26
  # All of the following configuration options are optional. Review them, and
27
27
  # uncomment or edit them if they appear relevant to your application needs.
28
28
 
29
+ # If `true`, all logging-related features for the agent can be enabled or disabled
30
+ # independently. If `false`, all logging-related features are disabled.
31
+ # application_logging.enabled: true
32
+
33
+ # If `true`, the agent captures log records emitted by this application.
34
+ # application_logging.forwarding.enabled: false
35
+
36
+ # Defines the maximum number of log records to buffer in memory at a time.
37
+ # application_logging.forwarding.max_samples_stored: 10000
38
+
39
+ # If `true`, the agent captures metrics related to logging for this application.
40
+ # application_logging.metrics.enabled: true
41
+
42
+ # If `true`, the agent decorates logs with metadata to link to entities, hosts, traces, and spans.
43
+ # application_logging.local_decorating.enabled: false
44
+
29
45
  # If true, enables transaction event sampling.
30
46
  # transaction_events.enabled: true
31
47
 
@@ -82,10 +98,6 @@ common: &default_settings
82
98
  # end.
83
99
  # browser_monitoring.attributes.include: []
84
100
 
85
- # If true, enables auto-injection of the JavaScript header for page load timing
86
- # (sometimes referred to as real user monitoring or RUM).
87
- # browser_monitoring.auto_instrument: false
88
-
89
101
  # This is true by default, this enables auto-injection of the JavaScript header
90
102
  # for page load timing (sometimes referred to as real user monitoring or RUM).
91
103
  # browser_monitoring.auto_instrument: true
@@ -317,6 +329,10 @@ common: &default_settings
317
329
  # May be one of [auto|prepend|chain|disabled].
318
330
  # instrumentation.logger: auto
319
331
 
332
+ # Controls auto-instrumentation of ActiveSupport::Logger at start up.
333
+ # May be one of [auto|prepend|chain|disabled].
334
+ # instrumentation.active_support.logger: auto
335
+
320
336
  # Controls auto-instrumentation of memcache-client gem for Memcache at start up.
321
337
  # May be one of [auto|prepend|chain|disabled].
322
338
  # instrumentation.memcache_client: auto
data/newrelic_rpm.gemspec CHANGED
@@ -52,6 +52,7 @@ https://github.com/newrelic/newrelic-ruby-agent/
52
52
  s.add_development_dependency 'rb-inotify', '0.9.10' # locked to support < Ruby 2.3 (and listen 3.0.8)
53
53
  s.add_development_dependency 'listen', '3.0.8' # locked to support < Ruby 2.3
54
54
  s.add_development_dependency 'minitest', '4.7.5'
55
+ s.add_development_dependency 'minitest-stub-const', '0.6'
55
56
  s.add_development_dependency 'mocha', '~> 1.9.0'
56
57
  s.add_development_dependency 'yard'
57
58
  s.add_development_dependency 'pry-nav', '~> 0.3.0'
data/test/agent_helper.rb CHANGED
@@ -218,16 +218,30 @@ end
218
218
  def assert_stats_has_values stats, expected_spec, expected_attrs
219
219
  expected_attrs.each do |attr, expected_value|
220
220
  actual_value = stats.send(attr)
221
+
222
+ msg = "Expected #{attr} for #{expected_spec} to be #{'~' unless attr == :call_count}#{expected_value}, " \
223
+ "got #{actual_value}.\nActual stats:\n#{dump_stats(stats)}"
224
+
221
225
  if attr == :call_count
222
- assert_equal(expected_value, actual_value,
223
- "Expected #{attr} for #{expected_spec} to be #{expected_value}, got #{actual_value}.\nActual stats:\n#{dump_stats(stats)}")
226
+ assert_stats_has_values_with_call_count(expected_value, actual_value, msg)
224
227
  else
225
- assert_in_delta(expected_value, actual_value, 0.0001,
226
- "Expected #{attr} for #{expected_spec} to be ~#{expected_value}, got #{actual_value}.\nActual stats:\n#{dump_stats(stats)}")
228
+ assert_in_delta(expected_value, actual_value, 0.0001, msg)
227
229
  end
228
230
  end
229
231
  end
230
232
 
233
+ def assert_stats_has_values_with_call_count(expected_value, actual_value, msg)
234
+ # >, <, >=, <= comparisons
235
+ if expected_value.to_s =~ /([<>]=?)\s*(\d+)/
236
+ operator = Regexp.last_match(1).to_sym
237
+ count = Regexp.last_match(2).to_i
238
+ assert_operator(actual_value, operator, count, msg)
239
+ # == comparison
240
+ else
241
+ assert_equal(expected_value, actual_value, msg)
242
+ end
243
+ end
244
+
231
245
  def assert_metrics_recorded expected
232
246
  expected = _normalize_metric_expectations(expected)
233
247
  expected.each do |specish, expected_attrs|
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: newrelic_rpm
3
3
  version: !ruby/object:Gem::Version
4
- version: 8.5.0
4
+ version: 8.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tanna McClure
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2022-02-24 00:00:00.000000000 Z
13
+ date: 2022-04-04 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rake
@@ -68,6 +68,20 @@ dependencies:
68
68
  - - '='
69
69
  - !ruby/object:Gem::Version
70
70
  version: 4.7.5
71
+ - !ruby/object:Gem::Dependency
72
+ name: minitest-stub-const
73
+ requirement: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - '='
76
+ - !ruby/object:Gem::Version
77
+ version: '0.6'
78
+ type: :development
79
+ prerelease: false
80
+ version_requirements: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - '='
83
+ - !ruby/object:Gem::Version
84
+ version: '0.6'
71
85
  - !ruby/object:Gem::Dependency
72
86
  name: mocha
73
87
  requirement: !ruby/object:Gem::Requirement
@@ -242,7 +256,6 @@ files:
242
256
  - Guardfile
243
257
  - LICENSE
244
258
  - README.md
245
- - ROADMAP.md
246
259
  - Rakefile
247
260
  - THIRD_PARTY_NOTICES.md
248
261
  - bin/mongrel_rpm
@@ -341,6 +354,10 @@ files:
341
354
  - lib/new_relic/agent/instrumentation/active_record_subscriber.rb
342
355
  - lib/new_relic/agent/instrumentation/active_storage.rb
343
356
  - lib/new_relic/agent/instrumentation/active_storage_subscriber.rb
357
+ - lib/new_relic/agent/instrumentation/active_support_logger.rb
358
+ - lib/new_relic/agent/instrumentation/active_support_logger/chain.rb
359
+ - lib/new_relic/agent/instrumentation/active_support_logger/instrumentation.rb
360
+ - lib/new_relic/agent/instrumentation/active_support_logger/prepend.rb
344
361
  - lib/new_relic/agent/instrumentation/acts_as_solr.rb
345
362
  - lib/new_relic/agent/instrumentation/authlogic.rb
346
363
  - lib/new_relic/agent/instrumentation/bunny.rb
@@ -441,7 +458,11 @@ files:
441
458
  - lib/new_relic/agent/instrumentation/typhoeus/prepend.rb
442
459
  - lib/new_relic/agent/internal_agent_error.rb
443
460
  - lib/new_relic/agent/javascript_instrumentor.rb
461
+ - lib/new_relic/agent/linking_metadata.rb
462
+ - lib/new_relic/agent/local_log_decorator.rb
463
+ - lib/new_relic/agent/log_event_aggregator.rb
444
464
  - lib/new_relic/agent/log_once.rb
465
+ - lib/new_relic/agent/log_priority.rb
445
466
  - lib/new_relic/agent/logging.rb
446
467
  - lib/new_relic/agent/memory_logger.rb
447
468
  - lib/new_relic/agent/messaging.rb