newrelic_rpm 4.8.0.341 → 5.0.0.342

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. checksums.yaml +5 -5
  2. data/.travis.yml +27 -0
  3. data/CHANGELOG.md +39 -0
  4. data/config.dot +0 -3
  5. data/lib/new_relic/agent/{throughput_monitor.rb → adaptive_sampler.rb} +22 -13
  6. data/lib/new_relic/agent/agent.rb +3 -4
  7. data/lib/new_relic/agent/configuration/default_source.rb +22 -17
  8. data/lib/new_relic/agent/configuration/high_security_source.rb +0 -2
  9. data/lib/new_relic/agent/database.rb +5 -0
  10. data/lib/new_relic/agent/database/explain_plan_helpers.rb +11 -0
  11. data/lib/new_relic/agent/distributed_trace_monitor.rb +4 -2
  12. data/lib/new_relic/agent/distributed_trace_payload.rb +88 -119
  13. data/lib/new_relic/agent/external.rb +14 -0
  14. data/lib/new_relic/agent/heap.rb +140 -0
  15. data/lib/new_relic/agent/instrumentation/bunny.rb +5 -1
  16. data/lib/new_relic/agent/instrumentation/rails5/action_cable.rb +5 -1
  17. data/lib/new_relic/agent/messaging.rb +10 -0
  18. data/lib/new_relic/agent/new_relic_service.rb +43 -23
  19. data/lib/new_relic/agent/priority_sampled_buffer.rb +68 -0
  20. data/lib/new_relic/agent/supported_versions.rb +1 -1
  21. data/lib/new_relic/agent/transaction.rb +14 -8
  22. data/lib/new_relic/agent/transaction/distributed_tracing.rb +113 -55
  23. data/lib/new_relic/agent/transaction/external_request_segment.rb +17 -11
  24. data/lib/new_relic/agent/transaction/message_broker_segment.rb +12 -4
  25. data/lib/new_relic/agent/transaction_error_primitive.rb +2 -8
  26. data/lib/new_relic/agent/transaction_event_aggregator.rb +16 -13
  27. data/lib/new_relic/agent/transaction_event_primitive.rb +5 -3
  28. data/lib/new_relic/agent/transaction_event_recorder.rb +3 -11
  29. data/lib/new_relic/recipes/capistrano3.rb +5 -2
  30. data/lib/new_relic/version.rb +2 -2
  31. data/newrelic_rpm.gemspec +3 -2
  32. metadata +38 -9
  33. data/lib/new_relic/agent/distributed_trace_priority_sampled_buffer.rb +0 -72
@@ -53,6 +53,15 @@ module NewRelic
53
53
  #
54
54
  def process_request_metadata request_metadata
55
55
  NewRelic::Agent.record_api_supportability_metric(:process_request_metadata)
56
+
57
+ if NewRelic::Agent.config[:'distributed_tracing.enabled']
58
+ NewRelic::Agent.logger.log_once(
59
+ :warn,
60
+ :process_request_metadata_noop,
61
+ 'Skipping process_request_metadata because distributed tracing is enabled')
62
+ return
63
+ end
64
+
56
65
  return unless CrossAppTracing.cross_app_enabled?
57
66
 
58
67
  state = NewRelic::Agent::TransactionState.tl_get
@@ -100,6 +109,11 @@ module NewRelic
100
109
  NewRelic::Agent.record_api_supportability_metric(:get_response_metadata)
101
110
  return unless CrossAppTracing.cross_app_enabled?
102
111
 
112
+ if NewRelic::Agent.config[:'distributed_tracing.enabled']
113
+ ::NewRelic::Agent.logger.warn('Skipping get_response_metadata because distributed tracing is enabled')
114
+ return
115
+ end
116
+
103
117
  state = NewRelic::Agent::TransactionState.tl_get
104
118
  if transaction = state.current_transaction and state.client_cross_app_id
105
119
 
@@ -0,0 +1,140 @@
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
+ module NewRelic
6
+ module Agent
7
+ # This class implements a min Heap. The first element is always the one with the
8
+ # lowest priority. It is a tree structure that is represented as an array. The
9
+ # relationship between between nodes in the tree and indices in the array are as
10
+ # follows:
11
+ #
12
+ # parent_index = (child_index - 1) / 2
13
+ # left_child_index = parent_index * 2 + 1
14
+ # right_child_index = parent_index * 2 + 2
15
+ #
16
+ # the root node is at index 0
17
+ # a node is a leaf node when its index >= length / 2
18
+ #
19
+
20
+ class Heap
21
+
22
+ # @param [Array] items an optional array of items to intialize the heap
23
+ #
24
+ # @param [Callable] priority_fn an optional priority function used to
25
+ # to compute the priority for an item. If it's not supplied priority
26
+ # will be computed using Comparable.
27
+ def initialize(items = nil, &priority_fn)
28
+ @items = []
29
+ @priority_fn = priority_fn || ->(x) { x }
30
+ items.each{ |item| push(item) } if items
31
+ end
32
+
33
+ def [](index)
34
+ @items[index]
35
+ end
36
+
37
+ def []=(index, value)
38
+ @items[index] = value
39
+ end
40
+
41
+ def fix(index)
42
+ parent_index = parent_index_for(index)
43
+
44
+ if in_range?(parent_index) && priority(parent_index) > priority(index)
45
+ heapify_up(index)
46
+ else
47
+ child_index = left_child_index_for(index)
48
+
49
+ return unless in_range?(child_index)
50
+
51
+ if right_sibling_smaller?(child_index)
52
+ child_index += 1
53
+ end
54
+
55
+ if priority(child_index) < priority(index)
56
+ heapify_down(index)
57
+ end
58
+ end
59
+ end
60
+
61
+ def push(item)
62
+ @items << item
63
+ heapify_up(size - 1)
64
+ end
65
+
66
+ alias_method :<<, :push
67
+
68
+ def pop
69
+ swap(0, size - 1)
70
+ item = @items.pop
71
+ heapify_down(0)
72
+ item
73
+ end
74
+
75
+ def size
76
+ @items.size
77
+ end
78
+
79
+ def empty?
80
+ @items.empty?
81
+ end
82
+
83
+ def to_a
84
+ @items
85
+ end
86
+
87
+ private
88
+
89
+ def priority(index)
90
+ @priority_fn.call(@items[index])
91
+ end
92
+
93
+ def parent_index_for child_index
94
+ (child_index - 1) / 2
95
+ end
96
+
97
+ def left_child_index_for parent_index
98
+ 2 * parent_index + 1
99
+ end
100
+
101
+ def right_sibling_smaller?(lchild_index)
102
+ in_range?(lchild_index + 1) && priority(lchild_index) > priority(lchild_index + 1)
103
+ end
104
+
105
+ def in_range?(index)
106
+ index >= 0 && index < size
107
+ end
108
+
109
+ def heapify_up(child_index)
110
+ return if child_index == 0
111
+
112
+ parent_index = parent_index_for(child_index)
113
+
114
+ if priority(child_index) < priority(parent_index)
115
+ swap(child_index, parent_index)
116
+ heapify_up(parent_index)
117
+ end
118
+ end
119
+
120
+ def heapify_down(parent_index)
121
+ child_index = left_child_index_for(parent_index)
122
+ return unless in_range?(child_index)
123
+
124
+ if right_sibling_smaller?(child_index)
125
+ child_index += 1
126
+ end
127
+
128
+ if priority(child_index) < priority(parent_index)
129
+ swap(parent_index, child_index)
130
+ heapify_down(child_index)
131
+ end
132
+ end
133
+
134
+ def swap(i, j)
135
+ @items[i], @items[j] = @items[j], @items[i]
136
+ end
137
+ end
138
+
139
+ end
140
+ end
@@ -24,7 +24,11 @@ DependencyDetection.defer do
24
24
  def publish payload, opts = {}
25
25
  begin
26
26
  destination = NewRelic::Agent::Instrumentation::Bunny.exchange_name(name)
27
- opts[:headers] ||= {} if NewRelic::Agent::CrossAppTracing.cross_app_enabled?
27
+
28
+ tracing_enabled =
29
+ NewRelic::Agent::CrossAppTracing.cross_app_enabled? ||
30
+ NewRelic::Agent.config[:'distributed_tracing.enabled']
31
+ opts[:headers] ||= {} if tracing_enabled
28
32
 
29
33
  segment = NewRelic::Agent::Messaging.start_amqp_publish_segment(
30
34
  library: NewRelic::Agent::Instrumentation::Bunny::LIBRARY,
@@ -27,6 +27,10 @@ DependencyDetection.defer do
27
27
  ActiveSupport::Notifications.subscribe(/(perform_action|transmit)\.action_cable/,
28
28
  NewRelic::Agent::Instrumentation::ActionCableSubscriber.new)
29
29
 
30
- ::NewRelic::Agent::PrependSupportability.record_metrics_for(::ActionCable::Engine, ::ActionCable::RemoteConnections)
30
+ ActiveSupport.on_load(:action_cable) do
31
+ ::NewRelic::Agent::PrependSupportability.record_metrics_for(
32
+ ::ActionCable::Engine,
33
+ ::ActionCable::RemoteConnections)
34
+ end
31
35
  end
32
36
  end
@@ -364,7 +364,17 @@ module NewRelic
364
364
  transaction_name
365
365
  end
366
366
 
367
+ NEWRELIC_TRACE_KEY = "NewRelicTrace".freeze
368
+ RABBITMQ_TRANSPORT_TYPE = "RabbitMQ".freeze
369
+
367
370
  def consume_message_headers headers, transaction, state
371
+ if Agent.config[:'distributed_tracing.enabled']
372
+ payload = headers[NEWRELIC_TRACE_KEY]
373
+ if transaction.accept_distributed_trace_payload payload
374
+ transaction.distributed_trace_payload.caller_transport_type = RABBITMQ_TRANSPORT_TYPE
375
+ end
376
+ end
377
+
368
378
  if CrossAppTracing.cross_app_enabled? && CrossAppTracing.message_has_crossapp_request_header?(headers)
369
379
  decode_id headers[CrossAppTracing::NR_MESSAGE_BROKER_ID_HEADER], state
370
380
  decode_txn_info headers[CrossAppTracing::NR_MESSAGE_BROKER_TXN_HEADER], state
@@ -15,7 +15,8 @@ module NewRelic
15
15
  # Specifies the version of the agent's communication protocol with
16
16
  # the NewRelic hosted site.
17
17
 
18
- PROTOCOL_VERSION = 14
18
+ PROTOCOL_VERSION = 15
19
+
19
20
  # 1f147a42: v10 (tag 3.5.3.17)
20
21
  # cf0d1ff1: v9 (tag 3.5.0)
21
22
  # 14105: v8 (tag 2.10.3)
@@ -30,8 +31,8 @@ module NewRelic
30
31
  # underlying TCP connection may be in a bad state.
31
32
  CONNECTION_ERRORS = [Timeout::Error, EOFError, SystemCallError, SocketError].freeze
32
33
 
33
- attr_accessor :request_timeout, :agent_id
34
- attr_reader :collector, :marshaller
34
+ attr_accessor :request_timeout
35
+ attr_reader :collector, :marshaller, :agent_id
35
36
 
36
37
  def initialize(license_key=nil, collector=control.server)
37
38
  @license_key = license_key
@@ -41,18 +42,12 @@ module NewRelic
41
42
  @in_session = nil
42
43
  @agent_id = nil
43
44
  @shared_tcp_connection = nil
45
+ reset_remote_method_uris
44
46
 
45
47
  @audit_logger = ::NewRelic::Agent::AuditLogger.new
46
48
  Agent.config.register_callback(:'audit_log.enabled') do |enabled|
47
49
  @audit_logger.enabled = enabled
48
50
  end
49
- Agent.config.register_callback(:ssl) do |ssl|
50
- if !ssl
51
- ::NewRelic::Agent.logger.warn("Agent is configured not to use SSL when communicating with New Relic's servers")
52
- else
53
- ::NewRelic::Agent.logger.debug("Agent is configured to use SSL")
54
- end
55
- end
56
51
 
57
52
  Agent.config.register_callback(:marshaller) do |marshaller|
58
53
  if marshaller != 'json'
@@ -63,17 +58,26 @@ module NewRelic
63
58
  end
64
59
  end
65
60
 
61
+ def agent_id=(id)
62
+ # Remote URIs have the agent run ID in them, so we need to
63
+ # clear out our cached values whenever the run ID changes.
64
+ #
65
+ reset_remote_method_uris
66
+
67
+ @agent_id = id
68
+ end
69
+
66
70
  def connect(settings={})
67
- if host = get_redirect_host
71
+ if host = preconnect
68
72
  @collector = NewRelic::Control.instance.server_from_host(host)
69
73
  end
70
74
  response = invoke_remote(:connect, [settings])
71
- @agent_id = response['agent_run_id']
75
+ self.agent_id = response['agent_run_id']
72
76
  response
73
77
  end
74
78
 
75
- def get_redirect_host
76
- invoke_remote(:get_redirect_host)
79
+ def preconnect
80
+ invoke_remote(:preconnect)
77
81
  end
78
82
 
79
83
  def shutdown(time)
@@ -261,8 +265,7 @@ module NewRelic
261
265
  conn.verify_mode = OpenSSL::SSL::VERIFY_PEER
262
266
  conn.cert_store = ssl_cert_store
263
267
  rescue StandardError, LoadError
264
- msg = "Agent is configured to use SSL, but SSL is not available in the environment. "
265
- msg << "Either disable SSL in the agent configuration, or install SSL support."
268
+ msg = "SSL is not available in the environment; please install SSL support."
266
269
  raise UnrecoverableAgentException.new(msg)
267
270
  end
268
271
 
@@ -296,7 +299,7 @@ module NewRelic
296
299
  conn = Net::HTTP.new(@collector.name, @collector.port)
297
300
  end
298
301
 
299
- setup_connection_for_ssl(conn) if Agent.config[:ssl]
302
+ setup_connection_for_ssl(conn)
300
303
  setup_connection_timeouts(conn)
301
304
 
302
305
  ::NewRelic::Agent.logger.debug("Created net/http handle to #{conn.address}:#{conn.port}")
@@ -335,11 +338,28 @@ module NewRelic
335
338
  NewRelic::Control.instance
336
339
  end
337
340
 
338
- # The path on the server that we should post our data to
339
- def remote_method_uri(method, format)
340
- params = {'run_id' => @agent_id, 'marshal_format' => format}
341
- uri = "/agent_listener/#{PROTOCOL_VERSION}/#{license_key}/#{method}"
342
- uri << '?' + params.map do |k,v|
341
+ def remote_method_uri(method)
342
+ @remote_method_uris[method]
343
+ end
344
+
345
+ def reset_remote_method_uris
346
+ @remote_method_uris = Hash.new do |hash, remote_method|
347
+ hash[remote_method] = generate_remote_method_uri(remote_method)
348
+ end
349
+ end
350
+
351
+ def generate_remote_method_uri(method)
352
+ params = {
353
+ 'protocol_version' => PROTOCOL_VERSION,
354
+ 'license_key' => license_key,
355
+ 'run_id' => @agent_id,
356
+ 'method' => method,
357
+ 'marshal_format' => 'json', # Other formats are explicitly
358
+ # ruled out; see the initializer
359
+ }
360
+
361
+ uri = "/agent_listener/invoke_raw_method?"
362
+ uri << params.map do |k,v|
343
363
  next unless v
344
364
  "#{k}=#{v}"
345
365
  end.compact.join('&')
@@ -368,7 +388,7 @@ module NewRelic
368
388
  data, encoding = compress_request_if_needed(data)
369
389
  size = data.size
370
390
 
371
- uri = remote_method_uri(method, @marshaller.format)
391
+ uri = remote_method_uri(method)
372
392
  full_uri = "#{@collector}#{uri}"
373
393
 
374
394
  @audit_logger.log_request(full_uri, payload, @marshaller)
@@ -0,0 +1,68 @@
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/heap'
6
+
7
+ module NewRelic
8
+ module Agent
9
+ class PrioritySampledBuffer < SampledBuffer
10
+ PRIORITY_KEY = "priority".freeze
11
+
12
+ attr_reader :seen_lifetime, :captured_lifetime
13
+
14
+ def initialize(capacity)
15
+ super
16
+ @captured_lifetime = 0
17
+ @seen_lifetime = 0
18
+ end
19
+
20
+ # expects priority and a block, or an event as a hash with a `priority` key.
21
+ def append(priority: nil, event: nil, &blk)
22
+ increment_seen
23
+
24
+ if @seen == @capacity
25
+ @items = Heap.new(@items) { |x| priority_for(x) }
26
+ end
27
+
28
+ if full?
29
+ priority ||= priority_for(event)
30
+ if priority_for(@items[0]) < priority
31
+ @items[0] = event || blk.call
32
+ @items.fix(0)
33
+ end
34
+ else
35
+ @items << (event || blk.call)
36
+ @captured_lifetime += 1
37
+ end
38
+ end
39
+
40
+ alias_method :append_event, :append
41
+
42
+ def capacity=(new_capacity)
43
+ @capacity = new_capacity
44
+ old_items = @items.to_a
45
+ old_seen = @seen
46
+ reset!
47
+ old_items.each { |i| append(event: i) }
48
+ @seen = old_seen
49
+ end
50
+
51
+ def to_a
52
+ @items.to_a.dup
53
+ end
54
+
55
+ private
56
+
57
+ def increment_seen
58
+ @seen += 1
59
+ @seen_lifetime += 1
60
+ end
61
+
62
+ def priority_for(event)
63
+ event[0][PRIORITY_KEY]
64
+ end
65
+ end
66
+ end
67
+ end
68
+
@@ -11,7 +11,7 @@ module NewRelic
11
11
  {
12
12
  :type => :ruby,
13
13
  :name => "MRI",
14
- :supported => ["2.0.0", "~>2.1.0", "~>2.2.0", "~>2.3.0", "~>2.4.0"],
14
+ :supported => ["2.0.0", "~>2.1.0", "~>2.2.0", "~>2.3.0", "~>2.4.0", "~>2.5.0"],
15
15
  :deprecated => ["1.8.6", "1.8.7", "1.9.2", "1.9.3"],
16
16
  :url => "https://www.ruby-lang.org",
17
17
  :feed => "https://www.ruby-lang.org/en/feeds/news.rss",
@@ -58,7 +58,8 @@ module NewRelic
58
58
  :http_response_code,
59
59
  :response_content_length,
60
60
  :response_content_type,
61
- :sampled
61
+ :sampled,
62
+ :priority
62
63
 
63
64
  attr_reader :guid,
64
65
  :metrics,
@@ -283,11 +284,15 @@ module NewRelic
283
284
  @ignore_trace = false
284
285
 
285
286
  if Agent.config[:'distributed_tracing.enabled']
286
- @sampled = NewRelic::Agent.instance.throughput_monitor.sampled?
287
+ @sampled = NewRelic::Agent.instance.adaptive_sampler.sampled?
287
288
  else
288
289
  @sampled = nil
289
290
  end
290
291
 
292
+ # we will eventually add this behavior into the AdaptiveSampler (ThroughputMontor)
293
+ @priority = rand
294
+ @priority +=1 if @sampled
295
+
291
296
  @attributes = Attributes.new(NewRelic::Agent.instance.attribute_filter)
292
297
 
293
298
  merge_request_parameters(@filtered_params)
@@ -596,7 +601,7 @@ module NewRelic
596
601
  end
597
602
 
598
603
  def assign_intrinsics(state)
599
- attributes.add_intrinsic_attribute :'nr.sampled', sampled?
604
+ attributes.add_intrinsic_attribute :'sampled', sampled?
600
605
 
601
606
  if gc_time = calculate_gc_time
602
607
  attributes.add_intrinsic_attribute(:gc_time, gc_time)
@@ -612,8 +617,8 @@ module NewRelic
612
617
  attributes.add_intrinsic_attribute(:synthetics_monitor_id, synthetics_monitor_id)
613
618
  end
614
619
 
615
- if distributed_trace_payload || order > 0
616
- assign_distributed_tracing_intrinsics
620
+ if distributed_trace_payload || distributed_trace_payload_created?
621
+ assign_distributed_trace_intrinsics
617
622
  elsif state.is_cross_app?
618
623
  attributes.add_intrinsic_attribute(:trip_id, cat_trip_id)
619
624
  attributes.add_intrinsic_attribute(:path_hash, cat_path_hash)
@@ -648,13 +653,14 @@ module NewRelic
648
653
  :duration => duration,
649
654
  :metrics => @metrics,
650
655
  :attributes => @attributes,
651
- :error => false
656
+ :error => false,
657
+ :priority => @priority
652
658
  }
653
659
 
654
- @payload[:'nr.sampled'] = sampled? if Agent.config[:'distributed_tracing.enabled']
660
+ @payload[:'sampled'] = sampled? if Agent.config[:'distributed_tracing.enabled']
655
661
 
656
662
  append_cat_info(state, duration, @payload)
657
- append_distributed_tracing_info(@payload)
663
+ append_distributed_trace_info(@payload)
658
664
  append_apdex_perf_zone(duration, @payload)
659
665
  append_synthetics_to(state, @payload)
660
666
  append_referring_transaction_guid_to(state, @payload)