newrelic_rpm 4.8.0.341 → 5.1.0.344

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +5 -5
  2. data/.travis.yml +27 -0
  3. data/CHANGELOG.md +93 -0
  4. data/README.md +0 -3
  5. data/config.dot +0 -3
  6. data/lib/new_relic/agent/{throughput_monitor.rb → adaptive_sampler.rb} +22 -13
  7. data/lib/new_relic/agent/agent.rb +3 -4
  8. data/lib/new_relic/agent/configuration/default_source.rb +41 -19
  9. data/lib/new_relic/agent/configuration/high_security_source.rb +0 -2
  10. data/lib/new_relic/agent/database/explain_plan_helpers.rb +11 -0
  11. data/lib/new_relic/agent/database.rb +5 -0
  12. data/lib/new_relic/agent/distributed_trace_monitor.rb +4 -2
  13. data/lib/new_relic/agent/distributed_trace_payload.rb +85 -119
  14. data/lib/new_relic/agent/error_collector.rb +17 -1
  15. data/lib/new_relic/agent/external.rb +14 -0
  16. data/lib/new_relic/agent/heap.rb +140 -0
  17. data/lib/new_relic/agent/instrumentation/active_record_5.rb +5 -0
  18. data/lib/new_relic/agent/instrumentation/active_record_prepend.rb +14 -1
  19. data/lib/new_relic/agent/instrumentation/bunny.rb +5 -1
  20. data/lib/new_relic/agent/instrumentation/grape.rb +33 -29
  21. data/lib/new_relic/agent/instrumentation/rails5/action_cable.rb +5 -1
  22. data/lib/new_relic/agent/instrumentation/resque.rb +17 -36
  23. data/lib/new_relic/agent/instrumentation/sequel.rb +0 -1
  24. data/lib/new_relic/agent/javascript_instrumentor.rb +2 -2
  25. data/lib/new_relic/agent/messaging.rb +10 -0
  26. data/lib/new_relic/agent/method_tracer.rb +23 -18
  27. data/lib/new_relic/agent/new_relic_service.rb +43 -23
  28. data/lib/new_relic/agent/priority_sampled_buffer.rb +68 -0
  29. data/lib/new_relic/agent/supported_versions.rb +1 -1
  30. data/lib/new_relic/agent/transaction/attributes.rb +1 -0
  31. data/lib/new_relic/agent/transaction/distributed_tracing.rb +173 -55
  32. data/lib/new_relic/agent/transaction/external_request_segment.rb +14 -64
  33. data/lib/new_relic/agent/transaction/message_broker_segment.rb +12 -4
  34. data/lib/new_relic/agent/transaction.rb +15 -8
  35. data/lib/new_relic/agent/transaction_error_primitive.rb +2 -8
  36. data/lib/new_relic/agent/transaction_event_aggregator.rb +16 -13
  37. data/lib/new_relic/agent/transaction_event_primitive.rb +5 -3
  38. data/lib/new_relic/agent/transaction_event_recorder.rb +3 -11
  39. data/lib/new_relic/agent.rb +27 -0
  40. data/lib/new_relic/build.rb +2 -2
  41. data/lib/new_relic/recipes/capistrano3.rb +5 -2
  42. data/lib/new_relic/version.rb +2 -2
  43. data/newrelic_rpm.gemspec +3 -2
  44. metadata +39 -9
  45. data/lib/new_relic/agent/distributed_trace_priority_sampled_buffer.rb +0 -72
@@ -22,52 +22,33 @@ DependencyDetection.defer do
22
22
  end
23
23
 
24
24
  executes do
25
- module Resque
26
- module Plugins
27
- module NewRelicInstrumentation
28
- include NewRelic::Agent::Instrumentation::ControllerInstrumentation
25
+ module ::Resque
26
+ class Job
27
+ include NewRelic::Agent::Instrumentation::ControllerInstrumentation
29
28
 
30
- def around_perform_with_monitoring(*args)
31
- begin
32
- perform_action_with_newrelic_trace(
33
- :name => 'perform',
34
- :class_name => self.name,
35
- :category => 'OtherTransaction/ResqueJob') do
29
+ alias_method :perform_without_instrumentation, :perform
36
30
 
37
- NewRelic::Agent::Transaction.merge_untrusted_agent_attributes(args, :'job.resque.args',
38
- NewRelic::Agent::AttributeFilter::DST_NONE)
31
+ def perform
32
+ begin
33
+ perform_action_with_newrelic_trace(
34
+ :name => 'perform',
35
+ :class_name => self.payload_class,
36
+ :category => 'OtherTransaction/ResqueJob') do
39
37
 
40
- yield(*args)
41
- end
42
- ensure
43
- NewRelic::Agent.agent.flush_pipe_data
44
- end
45
- end
46
- end
47
- end
48
- end
38
+ NewRelic::Agent::Transaction.merge_untrusted_agent_attributes(
39
+ args,
40
+ :'job.resque.args',
41
+ NewRelic::Agent::AttributeFilter::DST_NONE)
49
42
 
50
- module NewRelic
51
- module Agent
52
- module Instrumentation
53
- module ResqueInstrumentationInstaller
54
- def payload_class
55
- klass = super
56
- klass.instance_eval do
57
- extend ::Resque::Plugins::NewRelicInstrumentation
58
- end
43
+ perform_without_instrumentation
59
44
  end
45
+ ensure
46
+ NewRelic::Agent.agent.flush_pipe_data
60
47
  end
61
48
  end
62
49
  end
63
50
  end
64
51
 
65
- ::Resque::Job.class_eval do
66
- def self.new(*args)
67
- super(*args).extend NewRelic::Agent::Instrumentation::ResqueInstrumentationInstaller
68
- end
69
- end
70
-
71
52
  if NewRelic::LanguageSupport.can_fork?
72
53
  ::Resque.before_first_fork do
73
54
  NewRelic::Agent.manual_start(:dispatcher => :resque,
@@ -39,7 +39,6 @@ DependencyDetection.defer do
39
39
  end
40
40
 
41
41
  Sequel::Model.plugin :newrelic_instrumentation
42
-
43
42
  else
44
43
 
45
44
  NewRelic::Agent.logger.info "Sequel instrumentation requires at least version 3.37.0."
@@ -104,7 +104,7 @@ module NewRelic
104
104
  end
105
105
 
106
106
  def browser_timing_loader
107
- html_safe_if_needed("\n<script type=\"text/javascript\">#{Agent.config[:js_agent_loader]}</script>")
107
+ html_safe_if_needed("\n<script>#{Agent.config[:js_agent_loader]}</script>")
108
108
  end
109
109
 
110
110
  def browser_timing_config(state)
@@ -114,7 +114,7 @@ module NewRelic
114
114
  txn.freeze_name_and_execute_if_not_ignored do
115
115
  data = data_for_js_agent(state)
116
116
  json = ::JSON.dump(data)
117
- return html_safe_if_needed("\n<script type=\"text/javascript\">window.NREUM||(NREUM={});NREUM.info=#{json}</script>")
117
+ return html_safe_if_needed("\n<script>window.NREUM||(NREUM={});NREUM.info=#{json}</script>")
118
118
  end
119
119
 
120
120
  ''
@@ -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
@@ -291,7 +291,27 @@ module NewRelic
291
291
  #
292
292
  # @api public
293
293
  #
294
- def add_method_tracer(method_name, metric_name_code=nil, options = {})
294
+ def add_method_tracer(method_name, metric_name_code = nil, options = {})
295
+ ::NewRelic::Agent.add_or_defer_method_tracer(self, method_name, metric_name_code, options)
296
+ end
297
+
298
+ # For tests only because tracers must be removed in reverse-order
299
+ # from when they were added, or else other tracers that were added to the same method
300
+ # may get removed as well.
301
+ def remove_method_tracer(method_name, metric_name_code) # :nodoc:
302
+ return unless Agent.config[:agent_enabled]
303
+ if method_defined? "#{_traced_method_name(method_name, metric_name_code)}"
304
+ alias_method method_name, "#{_untraced_method_name(method_name, metric_name_code)}"
305
+ undef_method "#{_traced_method_name(method_name, metric_name_code)}"
306
+ ::NewRelic::Agent.logger.debug("removed method tracer #{method_name} #{metric_name_code}\n")
307
+ else
308
+ raise "No tracer for '#{metric_name_code}' on method '#{method_name}'"
309
+ end
310
+ end
311
+
312
+ private
313
+
314
+ def _add_method_tracer_now(method_name, metric_name_code, options)
295
315
  NewRelic::Agent.record_api_supportability_metric(:add_method_tracer)
296
316
 
297
317
  return unless newrelic_method_exists?(method_name)
@@ -308,24 +328,9 @@ module NewRelic
308
328
  send visibility, method_name
309
329
  send visibility, _traced_method_name(method_name, metric_name_code)
310
330
  ::NewRelic::Agent.logger.debug("Traced method: class = #{derived_class_name},"+
311
- "method = #{method_name}, "+
312
- "metric = '#{metric_name_code}'")
313
- end
314
-
315
- # For tests only because tracers must be removed in reverse-order
316
- # from when they were added, or else other tracers that were added to the same method
317
- # may get removed as well.
318
- def remove_method_tracer(method_name, metric_name_code) # :nodoc:
319
- return unless Agent.config[:agent_enabled]
320
- if method_defined? "#{_traced_method_name(method_name, metric_name_code)}"
321
- alias_method method_name, "#{_untraced_method_name(method_name, metric_name_code)}"
322
- undef_method "#{_traced_method_name(method_name, metric_name_code)}"
323
- ::NewRelic::Agent.logger.debug("removed method tracer #{method_name} #{metric_name_code}\n")
324
- else
325
- raise "No tracer for '#{metric_name_code}' on method '#{method_name}'"
326
- end
331
+ "method = #{method_name}, "+
332
+ "metric = '#{metric_name_code}'")
327
333
  end
328
- private
329
334
 
330
335
  # given a method and a metric, this method returns the
331
336
  # untraced alias of the method name
@@ -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",
@@ -57,6 +57,7 @@ module NewRelic
57
57
  end
58
58
 
59
59
  def merge_custom_attributes(other)
60
+ return unless Agent.config[:'custom_attributes.enabled']
60
61
  return if other.empty?
61
62
  AttributeProcessing.flatten_and_coerce(other) do |k, v|
62
63
  add_custom_attribute(k, v)
@@ -14,92 +14,79 @@ module NewRelic
14
14
  !!distributed_trace_payload
15
15
  end
16
16
 
17
- def create_distributed_trace_payload url = nil
17
+ SUPPORTABILITY_CREATE_PAYLOAD_SUCCESS = "Supportability/DistributedTrace/CreatePayload/Success".freeze
18
+ SUPPORTABILITY_CREATE_PAYLOAD_EXCEPTION = "Supportability/DistributedTrace/CreatePayload/Exception".freeze
19
+
20
+ def create_distributed_trace_payload
18
21
  return unless Agent.config[:'distributed_tracing.enabled']
19
- self.order += 1
20
- DistributedTracePayload.for_transaction self, url
22
+ self.distributed_trace_payload_created = true
23
+ payload = DistributedTracePayload.for_transaction self
24
+ NewRelic::Agent.increment_metric SUPPORTABILITY_CREATE_PAYLOAD_SUCCESS
25
+ payload
26
+ rescue => e
27
+ NewRelic::Agent.increment_metric SUPPORTABILITY_CREATE_PAYLOAD_EXCEPTION
28
+ NewRelic::Agent.logger.warn "Failed to create distributed trace payload", e
29
+ nil
21
30
  end
22
31
 
23
- LBRACE = "{".freeze
32
+ SUPPORTABILITY_ACCEPT_PAYLOAD_SUCCESS = "Supportability/DistributedTrace/AcceptPayload/Success".freeze
33
+ SUPPORTABILITY_ACCEPT_PAYLOAD_EXCEPTION = "Supportability/DistributedTrace/AcceptPayload/Exception".freeze
24
34
 
25
- def accept_distributed_trace_payload transport_type, payload
35
+ def accept_distributed_trace_payload payload
26
36
  return unless Agent.config[:'distributed_tracing.enabled']
27
- if distributed_trace_payload
28
- NewRelic::Agent.increment_metric "Supportability/DistributedTracing/AcceptFailure/PayloadAlreadyAccepted"
29
- return false
30
- elsif self.order > 0
31
- NewRelic::Agent.increment_metric "Supportability/DistributedTracing/AcceptFailure/CreateDistributedTracePayload-before-AcceptDistributedTracePayload"
32
- return false
33
- elsif name_frozen?
34
- NewRelic::Agent.increment_metric "Supportability/DistributedTracing/AcceptFailure/BrowserAgentInjected"
35
- return false
36
- end
37
-
38
- payload = if payload.start_with? LBRACE
39
- DistributedTracePayload.from_json payload
40
- else
41
- DistributedTracePayload.from_http_safe payload
42
- end
43
-
44
- payload.caller_transport_type = transport_type
45
- self.distributed_trace_payload = payload
37
+ return false if check_payload_ignored(payload)
38
+ return false unless payload = decode_payload(payload)
39
+ return false unless check_valid_version(payload)
40
+ return false unless check_trusted_account(payload)
46
41
 
47
- self.sampled = payload.sampled unless payload.sampled.nil?
42
+ assign_payload_and_sampling_params(payload)
48
43
 
44
+ NewRelic::Agent.increment_metric SUPPORTABILITY_ACCEPT_PAYLOAD_SUCCESS
49
45
  true
50
46
  rescue => e
51
- NewRelic::Agent.increment_metric "Supportability/DistributedTracing/AcceptFailure/Unknown"
47
+ NewRelic::Agent.increment_metric SUPPORTABILITY_ACCEPT_PAYLOAD_EXCEPTION
52
48
  NewRelic::Agent.logger.warn "Failed to accept distributed trace payload", e
53
49
  false
54
50
  end
55
51
 
56
- def distributed_tracing_trip_id
52
+ def trace_id
57
53
  if distributed_trace_payload
58
- distributed_trace_payload.trip_id
54
+ distributed_trace_payload.trace_id
59
55
  else
60
56
  guid
61
57
  end
62
58
  end
63
59
 
64
- def parent_ids
65
- if distributed_trace_payload &&
66
- distributed_trace_payload.parent_ids &&
67
- distributed_trace_payload.parent_ids.last != guid
68
- inbound_ids = distributed_trace_payload.parent_ids.dup
69
- inbound_ids.push guid
70
- if inbound_ids.size > 3
71
- inbound_ids.shift
72
- end
73
- inbound_ids
74
- else
75
- [guid]
76
- end
60
+ def parent_id
61
+ # The payload comes from our parent transaction, so its ID
62
+ # is our parent ID.
63
+ #
64
+ distributed_trace_payload && distributed_trace_payload.id
77
65
  end
78
66
 
79
- def depth
80
- if distributed_trace_payload
81
- distributed_trace_payload.depth
82
- else
83
- 1
84
- end
67
+ def grandparent_id
68
+ # The payload comes from our parent transaction, so its
69
+ # parent ID is our grandparent ID.
70
+ #
71
+ distributed_trace_payload && distributed_trace_payload.parent_id
85
72
  end
86
73
 
87
- def order
88
- @order ||= 0
74
+ def distributed_trace_payload_created?
75
+ @distributed_trace_payload_created ||= false
89
76
  end
90
77
 
91
- attr_writer :order
78
+ attr_writer :distributed_trace_payload_created
92
79
 
93
- def append_distributed_tracing_info payload
80
+ def append_distributed_trace_info transaction_payload
94
81
  return unless Agent.config[:'distributed_tracing.enabled']
95
82
  if distributed_trace_payload
96
- distributed_trace_payload.assign_intrinsics self, payload
97
- elsif order > 0
98
- DistributedTracePayload.assign_initial_intrinsics self, payload
83
+ distributed_trace_payload.assign_intrinsics self, transaction_payload
84
+ elsif distributed_trace_payload_created?
85
+ DistributedTracePayload.assign_intrinsics_for_first_trace self, transaction_payload
99
86
  end
100
87
  end
101
88
 
102
- def assign_distributed_tracing_intrinsics
89
+ def assign_distributed_trace_intrinsics
103
90
  return unless Agent.config[:'distributed_tracing.enabled']
104
91
  DistributedTracePayload::INTRINSIC_KEYS.each do |key|
105
92
  next unless value = @payload[key]
@@ -115,7 +102,138 @@ module NewRelic
115
102
  return unless distributed_trace_payload
116
103
  (start_time.to_f * 1000 - distributed_trace_payload.timestamp) / 1000
117
104
  end
105
+
106
+ private
107
+
108
+ SUPPORTABILITY_CREATE_BEFORE_ACCEPT_PAYLOAD = "Supportability/DistributedTrace/AcceptPayload/Ignored/CreateBeforeAccept".freeze
109
+ SUPPORTABILITY_MULTIPLE_ACCEPT_PAYLOAD = "Supportability/DistributedTrace/AcceptPayload/Ignored/Multiple".freeze
110
+ SUPPORTABILITY_PAYLOAD_ACCEPT_IGNORED_NULL = "Supportability/DistributedTrace/AcceptPayload/Ignored/Null".freeze
111
+ SUPPORTABILITY_PAYLOAD_ACCEPT_IGNORED_BROWSER = "Supportability/DistributedTrace/AcceptPayload/Ignored/BrowserAgentInjected".freeze
112
+
113
+ def check_payload_ignored(payload)
114
+ if payload.nil?
115
+ NewRelic::Agent.increment_metric SUPPORTABILITY_PAYLOAD_ACCEPT_IGNORED_NULL
116
+ return true
117
+ elsif distributed_trace_payload
118
+ NewRelic::Agent.increment_metric SUPPORTABILITY_MULTIPLE_ACCEPT_PAYLOAD
119
+ return true
120
+ elsif distributed_trace_payload_created?
121
+ NewRelic::Agent.increment_metric SUPPORTABILITY_CREATE_BEFORE_ACCEPT_PAYLOAD
122
+ return true
123
+ end
124
+ false
125
+ end
126
+
127
+ SUPPORTABILITY_PAYLOAD_ACCEPT_IGNORED_PARSE_EXCEPTION = "Supportability/DistributedTrace/AcceptPayload/ParseException".freeze
128
+ LBRACE = "{".freeze
129
+
130
+ def decode_payload(payload)
131
+ if payload.start_with? LBRACE
132
+ DistributedTracePayload.from_json payload
133
+ else
134
+ DistributedTracePayload.from_http_safe payload
135
+ end
136
+ rescue => e
137
+ NewRelic::Agent.increment_metric SUPPORTABILITY_PAYLOAD_ACCEPT_IGNORED_PARSE_EXCEPTION
138
+ NewRelic::Agent.logger.warn "Error parsing distributed trace payload", e
139
+ nil
140
+ end
141
+
142
+ SUPPORTABILITY_PAYLOAD_ACCEPT_IGNORED_MAJOR_VERSION = "Supportability/DistributedTrace/AcceptPayload/Ignored/MajorVersion".freeze
143
+
144
+ def check_valid_version(payload)
145
+ if DistributedTracePayload.major_version_matches?(payload)
146
+ true
147
+ else
148
+ NewRelic::Agent.increment_metric SUPPORTABILITY_PAYLOAD_ACCEPT_IGNORED_MAJOR_VERSION
149
+ false
150
+ end
151
+ end
152
+
153
+ SUPPORTABILITY_PAYLOAD_ACCEPT_UNTRUSTED_ACCOUNT = "Supportability/DistributedTrace/AcceptPayload/Ignored/UntrustedAccount".freeze
154
+
155
+ def check_trusted_account(payload)
156
+ trusted_account_ids = NewRelic::Agent.config[:trusted_account_ids]
157
+ trusted = trusted_account_ids.include?(payload.parent_account_id.to_i)
158
+
159
+ unless trusted
160
+ NewRelic::Agent.increment_metric SUPPORTABILITY_PAYLOAD_ACCEPT_UNTRUSTED_ACCOUNT
161
+ return false
162
+ end
163
+
164
+ true
165
+ end
166
+
167
+ def assign_payload_and_sampling_params(payload)
168
+ self.distributed_trace_payload = payload
169
+
170
+ unless payload.sampled.nil?
171
+ self.sampled = payload.sampled
172
+ self.priority = payload.priority if payload.priority
173
+ end
174
+ end
175
+
176
+ ALL_SUFFIX = "all".freeze
177
+ ALL_WEB_SUFFIX = "allWeb".freeze
178
+ ALL_OTHER_SUFFIX = "allOther".freeze
179
+
180
+ def transaction_type_suffix
181
+ if Transaction.recording_web_transaction?
182
+ ALL_WEB_SUFFIX
183
+ else
184
+ ALL_OTHER_SUFFIX
185
+ end
186
+ end
187
+
188
+ def record_distributed_tracing_metrics
189
+ return unless Agent.config[:'distributed_tracing.enabled']
190
+
191
+ record_caller_by_duration_metrics
192
+ record_transport_duration_metrics
193
+ record_errors_by_caller_metrics
194
+ end
195
+
196
+ DURATION_BY_CALLER_UNKOWN_PREFIX = "DurationByCaller/Unknown/Unknown/Unknown/Unknown".freeze
197
+
198
+ def record_caller_by_duration_metrics
199
+ prefix = if distributed_trace?
200
+ payload = distributed_trace_payload
201
+ "DurationByCaller/#{payload.parent_type}/#{payload.parent_account_id}/#{payload.parent_app_id}/#{payload.caller_transport_type}"
202
+ else
203
+ DURATION_BY_CALLER_UNKOWN_PREFIX
204
+ end
205
+
206
+ metrics.record_unscoped "#{prefix}/#{ALL_SUFFIX}", duration
207
+ metrics.record_unscoped "#{prefix}/#{transaction_type_suffix}", duration
208
+ end
209
+
210
+ def record_transport_duration_metrics
211
+ return unless distributed_trace?
212
+
213
+ payload = distributed_trace_payload
214
+ prefix = "TransportDuration/#{payload.parent_type}/#{payload.parent_account_id}/#{payload.parent_app_id}/#{payload.caller_transport_type}"
215
+
216
+ metrics.record_unscoped "#{prefix}/#{ALL_SUFFIX}", transport_duration
217
+ metrics.record_unscoped "#{prefix}/#{transaction_type_suffix}", transport_duration
218
+ end
219
+
220
+ ERRORS_BY_CALLER_UNKOWN_PREFIX = "ErrorsByCaller/Unknown/Unknown/Unknown/Unknown".freeze
221
+
222
+ def record_errors_by_caller_metrics
223
+ return unless exceptions.size > 0
224
+
225
+ prefix = if distributed_trace?
226
+ payload = distributed_trace_payload
227
+ "ErrorsByCaller/#{payload.parent_type}/#{payload.parent_account_id}/#{payload.parent_app_id}/#{payload.caller_transport_type}"
228
+ else
229
+ ERRORS_BY_CALLER_UNKOWN_PREFIX
230
+ end
231
+
232
+ metrics.record_unscoped "#{prefix}/#{ALL_SUFFIX}", 1
233
+ metrics.record_unscoped "#{prefix}/#{transaction_type_suffix}", 1
234
+ end
118
235
  end
119
236
  end
120
237
  end
121
238
  end
239
+