newrelic_rpm 4.8.0.341 → 5.0.0.342

Sign up to get free protection for your applications and to get access to all the features.
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)