newrelic_rpm 5.5.0.348 → 6.2.0.354

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.travis.yml +36 -84
  4. data/.yardopts +1 -0
  5. data/CHANGELOG.md +149 -0
  6. data/README.md +1 -1
  7. data/config.dot +1 -0
  8. data/lib/new_relic/agent/agent.rb +15 -11
  9. data/lib/new_relic/agent/attribute_filter.rb +77 -17
  10. data/lib/new_relic/agent/configuration/default_source.rb +71 -1
  11. data/lib/new_relic/agent/configuration/security_policy_source.rb +14 -0
  12. data/lib/new_relic/agent/configuration/server_source.rb +2 -1
  13. data/lib/new_relic/agent/cross_app_monitor.rb +3 -3
  14. data/lib/new_relic/agent/datastores/metric_helper.rb +1 -2
  15. data/lib/new_relic/agent/datastores.rb +6 -8
  16. data/lib/new_relic/agent/distributed_trace_monitor.rb +1 -2
  17. data/lib/new_relic/agent/distributed_trace_payload.rb +7 -11
  18. data/lib/new_relic/agent/error_collector.rb +4 -6
  19. data/lib/new_relic/agent/external.rb +6 -4
  20. data/lib/new_relic/agent/hostname.rb +8 -0
  21. data/lib/new_relic/agent/instrumentation/action_cable_subscriber.rb +8 -5
  22. data/lib/new_relic/agent/instrumentation/action_controller_subscriber.rb +12 -8
  23. data/lib/new_relic/agent/instrumentation/action_view_subscriber.rb +1 -1
  24. data/lib/new_relic/agent/instrumentation/active_job.rb +6 -7
  25. data/lib/new_relic/agent/instrumentation/active_record.rb +2 -2
  26. data/lib/new_relic/agent/instrumentation/active_record_subscriber.rb +3 -3
  27. data/lib/new_relic/agent/instrumentation/active_storage.rb +23 -0
  28. data/lib/new_relic/agent/instrumentation/active_storage_subscriber.rb +59 -0
  29. data/lib/new_relic/agent/instrumentation/acts_as_solr.rb +1 -1
  30. data/lib/new_relic/agent/instrumentation/bunny.rb +16 -12
  31. data/lib/new_relic/agent/instrumentation/controller_instrumentation.rb +9 -3
  32. data/lib/new_relic/agent/instrumentation/curb.rb +18 -5
  33. data/lib/new_relic/agent/instrumentation/data_mapper.rb +1 -1
  34. data/lib/new_relic/agent/instrumentation/evented_subscriber.rb +1 -1
  35. data/lib/new_relic/agent/instrumentation/excon/connection.rb +1 -1
  36. data/lib/new_relic/agent/instrumentation/grape.rb +17 -4
  37. data/lib/new_relic/agent/instrumentation/middleware_proxy.rb +1 -1
  38. data/lib/new_relic/agent/instrumentation/middleware_tracing.rb +11 -4
  39. data/lib/new_relic/agent/instrumentation/net.rb +1 -1
  40. data/lib/new_relic/agent/instrumentation/rake.rb +2 -3
  41. data/lib/new_relic/agent/instrumentation/sequel.rb +1 -1
  42. data/lib/new_relic/agent/instrumentation/typhoeus.rb +3 -3
  43. data/lib/new_relic/agent/javascript_instrumentor.rb +1 -1
  44. data/lib/new_relic/agent/messaging.rb +9 -8
  45. data/lib/new_relic/agent/method_tracer_helpers.rb +2 -2
  46. data/lib/new_relic/agent/new_relic_service/json_marshaller.rb +0 -1
  47. data/lib/new_relic/agent/new_relic_service/marshaller.rb +5 -26
  48. data/lib/new_relic/agent/new_relic_service.rb +69 -25
  49. data/lib/new_relic/agent/span_event_primitive.rb +26 -16
  50. data/lib/new_relic/agent/sql_sampler.rb +3 -3
  51. data/lib/new_relic/agent/stats_engine.rb +2 -2
  52. data/lib/new_relic/agent/synthetics_monitor.rb +1 -2
  53. data/lib/new_relic/agent/system_info.rb +5 -0
  54. data/lib/new_relic/agent/threading/agent_thread.rb +1 -1
  55. data/lib/new_relic/agent/tracer.rb +462 -0
  56. data/lib/new_relic/agent/transaction/abstract_segment.rb +1 -1
  57. data/lib/new_relic/agent/transaction/datastore_segment.rb +0 -2
  58. data/lib/new_relic/agent/transaction/distributed_tracing.rb +1 -2
  59. data/lib/new_relic/agent/transaction/trace_node.rb +4 -2
  60. data/lib/new_relic/agent/transaction/tracing.rb +0 -99
  61. data/lib/new_relic/agent/transaction.rb +43 -88
  62. data/lib/new_relic/agent/transaction_time_aggregator.rb +49 -25
  63. data/lib/new_relic/agent/utilization_data.rb +36 -1
  64. data/lib/new_relic/agent.rb +8 -4
  65. data/lib/new_relic/build.rb +2 -2
  66. data/lib/new_relic/control/frameworks/rails6.rb +14 -0
  67. data/lib/new_relic/latest_changes.rb +2 -2
  68. data/lib/new_relic/rack/agent_middleware.rb +1 -1
  69. data/lib/new_relic/version.rb +2 -2
  70. data/newrelic_rpm.gemspec +2 -9
  71. data/test/agent_helper.rb +2 -2
  72. metadata +13 -39
  73. data/lib/new_relic/agent/transaction_state.rb +0 -186
@@ -34,13 +34,16 @@ module NewRelic
34
34
  #
35
35
  # @api public
36
36
  def start_segment(library: nil, uri: nil, procedure: nil)
37
+ Deprecator.deprecate 'External.start_segment',
38
+ 'Tracer#start_external_request_segment'
39
+
37
40
  raise ArgumentError, 'Argument `library` is required' if library.nil?
38
41
  raise ArgumentError, 'Argument `uri` is required' if uri.nil?
39
42
  raise ArgumentError, 'Argument `procedure` is required' if procedure.nil?
40
43
 
41
44
  ::NewRelic::Agent.record_api_supportability_metric(:start_segment)
42
45
 
43
- ::NewRelic::Agent::Transaction.start_external_request_segment(
46
+ ::NewRelic::Agent::Tracer.start_external_request_segment(
44
47
  library: library,
45
48
  uri: uri,
46
49
  procedure: procedure
@@ -64,7 +67,7 @@ module NewRelic
64
67
  NewRelic::Agent.record_api_supportability_metric(:process_request_metadata)
65
68
  return unless CrossAppTracing.cross_app_enabled?
66
69
 
67
- state = NewRelic::Agent::TransactionState.tl_get
70
+ state = NewRelic::Agent::Tracer.state
68
71
  if transaction = state.current_transaction
69
72
  rmd = ::JSON.parse obfuscator.deobfuscate(request_metadata)
70
73
 
@@ -109,8 +112,7 @@ module NewRelic
109
112
  NewRelic::Agent.record_api_supportability_metric(:get_response_metadata)
110
113
  return unless CrossAppTracing.cross_app_enabled?
111
114
 
112
- state = NewRelic::Agent::TransactionState.tl_get
113
- return unless (transaction = state.current_transaction)
115
+ return unless (transaction = Tracer.current_transaction)
114
116
  return unless (cross_app_payload = transaction.cross_app_payload)
115
117
 
116
118
  # must freeze the name since we're responding with it
@@ -1,6 +1,7 @@
1
1
  # encoding: utf-8
2
2
  # This file is distributed under New Relic's license terms.
3
3
  # See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details.
4
+ require 'socket'
4
5
 
5
6
  module NewRelic
6
7
  module Agent
@@ -16,6 +17,13 @@ module NewRelic
16
17
  end
17
18
  end
18
19
 
20
+ def self.get_fqdn
21
+ %x[hostname -f].chomp!
22
+ rescue => e
23
+ NewRelic::Agent.logger.debug "Unable to determine fqdn #{e}"
24
+ nil
25
+ end
26
+
19
27
  def self.heroku_dyno_name_prefix(dyno_name)
20
28
  get_dyno_prefixes.find do |dyno_prefix|
21
29
  dyno_name.start_with?(dyno_prefix + ".")
@@ -27,7 +27,7 @@ module NewRelic
27
27
  event = super
28
28
  notice_error payload if payload.key? :exception
29
29
  if event.name == PERFORM_ACTION
30
- finish_transaction
30
+ finish_transaction event
31
31
  else
32
32
  stop_recording_metrics event
33
33
  end
@@ -38,15 +38,18 @@ module NewRelic
38
38
  private
39
39
 
40
40
  def start_transaction event
41
- Transaction.start(state, :action_cable, :transaction_name => transaction_name_from_event(event))
41
+ event.payload[:finishable] = Tracer.start_transaction_or_segment(
42
+ name: transaction_name_from_event(event),
43
+ category: :action_cable
44
+ )
42
45
  end
43
46
 
44
- def finish_transaction
45
- Transaction.stop(state)
47
+ def finish_transaction event
48
+ (finishable = event.payload[:finishable]) && finishable.finish
46
49
  end
47
50
 
48
51
  def start_recording_metrics event
49
- event.payload[:segment] = Transaction.start_segment name: metric_name_from_event(event)
52
+ event.payload[:segment] = Tracer.start_segment name: metric_name_from_event(event)
50
53
  end
51
54
 
52
55
  def stop_recording_metrics event
@@ -43,17 +43,21 @@ module NewRelic
43
43
  end
44
44
 
45
45
  def start_transaction(event)
46
- Transaction.start(state, :controller,
47
- :request => event.request,
48
- :filtered_params => filter(event.payload[:params]),
49
- :apdex_start_time => event.queue_start,
50
- :transaction_name => event.metric_name,
51
- :ignore_apdex => event.apdex_ignored?,
52
- :ignore_enduser => event.enduser_ignored?)
46
+ event.payload[:finishable] = Tracer.start_transaction_or_segment(
47
+ name: event.metric_name,
48
+ category: :controller,
49
+ options: {
50
+ request: event.request,
51
+ filtered_params: filter(event.payload[:params]),
52
+ apdex_start_time: event.queue_start,
53
+ ignore_apdex: event.apdex_ignored?,
54
+ ignore_enduser: event.enduser_ignored?
55
+ }
56
+ )
53
57
  end
54
58
 
55
59
  def stop_transaction(event)
56
- Transaction.stop(state)
60
+ (finishable = event.payload[:finishable]) && finishable.finish
57
61
  end
58
62
 
59
63
  def filter(params)
@@ -14,7 +14,7 @@ module NewRelic
14
14
  event = RenderEvent.new(name, Time.now, nil, id, payload)
15
15
  push_event(event)
16
16
  if state.is_execution_traced? && event.recordable?
17
- event.segment = NewRelic::Agent::Transaction.start_segment name: event.metric_name
17
+ event.segment = NewRelic::Agent::Tracer.start_segment name: event.metric_name
18
18
  end
19
19
  rescue => e
20
20
  log_notification_error(e, name, 'start')
@@ -39,7 +39,7 @@ module NewRelic
39
39
  end
40
40
 
41
41
  def self.perform(job, block)
42
- state = ::NewRelic::Agent::TransactionState.tl_get
42
+ state = ::NewRelic::Agent::Tracer.state
43
43
  txn = state.current_transaction
44
44
 
45
45
  # Don't nest transactions if we're already in a web transaction.
@@ -52,7 +52,7 @@ module NewRelic
52
52
  transaction_category)
53
53
  block.call
54
54
  else
55
- run_in_transaction(state, job, block)
55
+ run_in_transaction(job, block)
56
56
  end
57
57
  end
58
58
 
@@ -62,11 +62,10 @@ module NewRelic
62
62
  end
63
63
  end
64
64
 
65
- def self.run_in_transaction(state, job, block)
66
- ::NewRelic::Agent::Transaction.wrap(state,
67
- transaction_name_for_job(job),
68
- :other,
69
- &block)
65
+ def self.run_in_transaction(job, block)
66
+ ::NewRelic::Agent::Tracer.in_transaction(name: transaction_name_for_job(job),
67
+ category: :other,
68
+ &block)
70
69
  end
71
70
 
72
71
  def self.transaction_category
@@ -44,7 +44,7 @@ module NewRelic
44
44
  end
45
45
 
46
46
  def log_with_newrelic_instrumentation(*args, &block) #THREAD_LOCAL_ACCESS
47
- state = NewRelic::Agent::TransactionState.tl_get
47
+ state = NewRelic::Agent::Tracer.state
48
48
 
49
49
  if !state.is_execution_traced?
50
50
  return log_without_newrelic_instrumentation(*args, &block)
@@ -67,7 +67,7 @@ module NewRelic
67
67
  database = @config && @config[:database]
68
68
  end
69
69
 
70
- segment = NewRelic::Agent::Transaction.start_datastore_segment(
70
+ segment = NewRelic::Agent::Tracer.start_datastore_segment(
71
71
  product: product,
72
72
  operation: operation,
73
73
  collection: collection,
@@ -31,7 +31,7 @@ module NewRelic
31
31
  # we don't expect this to be called more than once, but we're being
32
32
  # defensive.
33
33
  return if defined?(cached?)
34
- if ::ActiveRecord::VERSION::STRING >= "5.1.0"
34
+ if defined?(::ActiveRecord) && ::ActiveRecord::VERSION::STRING >= "5.1.0"
35
35
  def cached?(payload)
36
36
  payload.fetch(:cached, false)
37
37
  end
@@ -114,7 +114,7 @@ module NewRelic
114
114
  database = @config && @config[:database]
115
115
  end
116
116
 
117
- segment = Transaction.start_datastore_segment product: product,
117
+ segment = Tracer.start_datastore_segment product: product,
118
118
  operation: operation,
119
119
  collection: collection,
120
120
  host: host,
@@ -130,7 +130,7 @@ module NewRelic
130
130
  end
131
131
 
132
132
  def state
133
- @state ||= NewRelic::Agent::TransactionState.tl_get
133
+ @state ||= NewRelic::Agent::Tracer.state
134
134
  end
135
135
 
136
136
  def sql
@@ -0,0 +1,23 @@
1
+ # encoding: utf-8
2
+ # This file is distributed under New Relic's license terms.
3
+ # See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details.
4
+
5
+ require 'new_relic/agent/instrumentation/active_storage_subscriber'
6
+
7
+ DependencyDetection.defer do
8
+ named :active_storage
9
+
10
+ depends_on do
11
+ defined?(::ActiveStorage) &&
12
+ !NewRelic::Agent::Instrumentation::ActiveStorageSubscriber.subscribed?
13
+ end
14
+
15
+ executes do
16
+ ::NewRelic::Agent.logger.info 'Installing ActiveStorage 5 instrumentation'
17
+ end
18
+
19
+ executes do
20
+ ActiveSupport::Notifications.subscribe(/\.active_storage$/,
21
+ NewRelic::Agent::Instrumentation::ActiveStorageSubscriber.new)
22
+ end
23
+ end
@@ -0,0 +1,59 @@
1
+ # encoding: utf-8
2
+ # This file is distributed under New Relic's license terms.
3
+ # See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details.
4
+ require 'new_relic/agent/instrumentation/evented_subscriber'
5
+
6
+ module NewRelic
7
+ module Agent
8
+ module Instrumentation
9
+ class ActiveStorageSubscriber < EventedSubscriber
10
+ def start name, id, payload
11
+ return unless state.is_execution_traced?
12
+ start_segment name, id, payload
13
+ rescue => e
14
+ log_notification_error e, name, 'start'
15
+ end
16
+
17
+ def finish name, id, payload
18
+ return unless state.is_execution_traced?
19
+ finish_segment id
20
+ rescue => e
21
+ log_notification_error e, name, 'finish'
22
+ end
23
+
24
+ def start_segment name, id, payload
25
+ segment = Tracer.start_segment name: metric_name(name, payload)
26
+ segment.params[:key] = payload[:key]
27
+ segment.params[:exist] = payload[:exist] if payload.key? :exist
28
+ event_stack[id].push segment
29
+ end
30
+
31
+ def finish_segment id
32
+ segment = event_stack[id].pop
33
+ segment.finish if segment
34
+ end
35
+
36
+ def metric_name name, payload
37
+ service = payload[:service]
38
+ method = method_from_name name
39
+ "Ruby/ActiveStorage/#{service}Service/#{method}"
40
+ end
41
+
42
+ PATTERN = /\Aservice_([^\.]*)\.active_storage\z/
43
+ UNKNOWN = "unknown".freeze
44
+
45
+ METHOD_NAME_MAPPING = Hash.new do |h, k|
46
+ if PATTERN =~ k
47
+ h[k] = $1
48
+ else
49
+ h[k] = UNKNOWN
50
+ end
51
+ end
52
+
53
+ def method_from_name name
54
+ METHOD_NAME_MAPPING[name]
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -13,7 +13,7 @@ module NewRelic
13
13
  begin
14
14
  parse_query_without_newrelic(*args)
15
15
  ensure
16
- return unless txn = ::NewRelic::Agent::TransactionState.tl_get.current_transaction
16
+ return unless txn = ::NewRelic::Agent::Tracer.current_transaction
17
17
  txn.current_segment.params[:statement] = ::NewRelic::Agent::Database.truncate_query(args.first.inspect) rescue nil
18
18
  end
19
19
  end
@@ -57,19 +57,23 @@ DependencyDetection.defer do
57
57
  def pop(opts = {:manual_ack => false}, &block)
58
58
  t0 = Time.now
59
59
  msg = pop_without_new_relic opts, &block
60
+ delivery_info, message_properties, _payload = msg
60
61
 
61
62
  begin
62
- exchange_name = NewRelic::Agent::Instrumentation::Bunny.exchange_name(msg.first.exchange)
63
-
64
- segment = NewRelic::Agent::Messaging.start_amqp_consume_segment(
65
- library: NewRelic::Agent::Instrumentation::Bunny::LIBRARY,
66
- destination_name: exchange_name,
67
- delivery_info: msg[0],
68
- message_properties: msg[1],
69
- exchange_type: channel.exchanges[msg.first.exchange].type,
70
- queue_name: name,
71
- start_time: t0
72
- )
63
+ if delivery_info
64
+ exchange_name = NewRelic::Agent::Instrumentation::Bunny.exchange_name(delivery_info.exchange)
65
+ exchange_type = NewRelic::Agent::Instrumentation::Bunny.exchange_type(delivery_info, channel)
66
+
67
+ segment = NewRelic::Agent::Messaging.start_amqp_consume_segment(
68
+ library: NewRelic::Agent::Instrumentation::Bunny::LIBRARY,
69
+ destination_name: exchange_name,
70
+ delivery_info: delivery_info,
71
+ message_properties: message_properties,
72
+ exchange_type: exchange_type,
73
+ queue_name: name,
74
+ start_time: t0
75
+ )
76
+ end
73
77
 
74
78
  rescue => e
75
79
  NewRelic::Agent.logger.error "Error starting message broker segment in Bunny::Queue#pop", e
@@ -85,7 +89,7 @@ DependencyDetection.defer do
85
89
  def purge *args
86
90
  begin
87
91
  type = server_named? ? :temporary_queue : :queue
88
- segment = NewRelic::Agent::Transaction.start_message_broker_segment(
92
+ segment = NewRelic::Agent::Tracer.start_message_broker_segment(
89
93
  action: :purge,
90
94
  library: NewRelic::Agent::Instrumentation::Bunny::LIBRARY,
91
95
  destination_type: type,
@@ -233,7 +233,9 @@ module NewRelic
233
233
  category ||= (txn && txn.category)
234
234
  case category
235
235
  when :controller then ::NewRelic::Agent::Transaction::CONTROLLER_PREFIX
236
+ when :web then ::NewRelic::Agent::Transaction::CONTROLLER_PREFIX
236
237
  when :task then ::NewRelic::Agent::Transaction::TASK_PREFIX
238
+ when :background then ::NewRelic::Agent::Transaction::TASK_PREFIX
237
239
  when :rack then ::NewRelic::Agent::Transaction::RACK_PREFIX
238
240
  when :uri then ::NewRelic::Agent::Transaction::CONTROLLER_PREFIX
239
241
  when :sinatra then ::NewRelic::Agent::Transaction::SINATRA_PREFIX
@@ -344,7 +346,7 @@ module NewRelic
344
346
  #
345
347
  def perform_action_with_newrelic_trace(*args, &block) #THREAD_LOCAL_ACCESS
346
348
  NewRelic::Agent.record_api_supportability_metric(:perform_action_with_newrelic_trace)
347
- state = NewRelic::Agent::TransactionState.tl_get
349
+ state = NewRelic::Agent::Tracer.state
348
350
  request = newrelic_request(args)
349
351
  queue_start_time = detect_queue_start_time(request)
350
352
 
@@ -364,7 +366,11 @@ module NewRelic
364
366
  txn_options = create_transaction_options(trace_options, category, state, queue_start_time)
365
367
 
366
368
  begin
367
- Transaction.start(state, category, txn_options)
369
+ finishable = Tracer.start_transaction_or_segment(
370
+ name: txn_options[:transaction_name],
371
+ category: category,
372
+ options: txn_options
373
+ )
368
374
 
369
375
  begin
370
376
  yield
@@ -374,7 +380,7 @@ module NewRelic
374
380
  end
375
381
 
376
382
  ensure
377
- Transaction.stop(state)
383
+ finishable.finish if finishable
378
384
  end
379
385
  end
380
386
 
@@ -112,13 +112,9 @@ DependencyDetection.defer do
112
112
  alias_method :add_without_newrelic, :add
113
113
  alias_method :add, :add_with_newrelic
114
114
 
115
-
116
115
  # Trace as an External/Multiple call if the first request isn't serial.
117
116
  def perform_with_newrelic(&blk)
118
- return perform_without_newrelic if
119
- self.requests.first &&
120
- self.requests.first.respond_to?(:_nr_serial) &&
121
- self.requests.first._nr_serial
117
+ return perform_without_newrelic if first_request_is_serial?
122
118
 
123
119
  trace_execution_scoped("External/Multiple/Curb::Multi/perform") do
124
120
  perform_without_newrelic(&blk)
@@ -198,6 +194,23 @@ DependencyDetection.defer do
198
194
  request._nr_instrumented = false
199
195
  end
200
196
 
197
+ private
198
+
199
+ def first_request_is_serial?
200
+ return false unless (first = self.requests.first)
201
+
202
+ # Before curb 0.9.8, requests was an array of Curl::Easy
203
+ # instances. Starting with 0.9.8, it's a Hash where the
204
+ # values are Curl::Easy instances.
205
+ #
206
+ # So, requests.first will either be an_obj or [a_key, an_obj].
207
+ # We need to handle either case.
208
+ #
209
+ first = first[-1] if first.is_a?(Array)
210
+
211
+ first.respond_to?(:_nr_serial) && first._nr_serial
212
+ end
213
+
201
214
  end # class Curl::Multi
202
215
 
203
216
  end
@@ -185,7 +185,7 @@ module NewRelic
185
185
  # hooked with tracers, ensuring that notice_sql attaches this SQL to
186
186
  # the proper call scope.
187
187
  def log(msg) #THREAD_LOCAL_ACCESS
188
- state = NewRelic::Agent::TransactionState.tl_get
188
+ state = NewRelic::Agent::Tracer.state
189
189
  return unless state.is_execution_traced?
190
190
 
191
191
  txn = state.current_transaction
@@ -60,7 +60,7 @@ module NewRelic
60
60
  end
61
61
 
62
62
  def state
63
- NewRelic::Agent::TransactionState.tl_get
63
+ NewRelic::Agent::Tracer.state
64
64
  end
65
65
  end
66
66