newrelic_rpm 4.5.0.337 → 4.6.0.338

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +2 -0
  3. data/CHANGELOG.md +8 -0
  4. data/lib/new_relic/agent.rb +1 -0
  5. data/lib/new_relic/agent/agent.rb +19 -12
  6. data/lib/new_relic/agent/configuration/default_source.rb +7 -0
  7. data/lib/new_relic/agent/distributed_trace_monitor.rb +29 -0
  8. data/lib/new_relic/agent/distributed_trace_payload.rb +223 -0
  9. data/lib/new_relic/agent/distributed_trace_priority_sampled_buffer.rb +72 -0
  10. data/lib/new_relic/agent/external.rb +137 -0
  11. data/lib/new_relic/agent/http_clients/abstract_request.rb +31 -0
  12. data/lib/new_relic/agent/http_clients/curb_wrappers.rb +3 -1
  13. data/lib/new_relic/agent/http_clients/excon_wrappers.rb +3 -1
  14. data/lib/new_relic/agent/http_clients/http_rb_wrappers.rb +3 -1
  15. data/lib/new_relic/agent/http_clients/httpclient_wrappers.rb +3 -1
  16. data/lib/new_relic/agent/http_clients/net_http_wrappers.rb +3 -1
  17. data/lib/new_relic/agent/http_clients/typhoeus_wrappers.rb +3 -1
  18. data/lib/new_relic/agent/sql_sampler.rb +2 -2
  19. data/lib/new_relic/agent/throughput_monitor.rb +59 -0
  20. data/lib/new_relic/agent/transaction.rb +59 -15
  21. data/lib/new_relic/agent/transaction/abstract_segment.rb +17 -5
  22. data/lib/new_relic/agent/transaction/distributed_tracing.rb +121 -0
  23. data/lib/new_relic/agent/transaction/external_request_segment.rb +183 -19
  24. data/lib/new_relic/agent/transaction/segment.rb +2 -3
  25. data/lib/new_relic/agent/transaction/trace.rb +14 -5
  26. data/lib/new_relic/agent/transaction/trace_builder.rb +58 -0
  27. data/lib/new_relic/agent/transaction/trace_node.rb +34 -32
  28. data/lib/new_relic/agent/transaction/tracing.rb +4 -9
  29. data/lib/new_relic/agent/transaction_error_primitive.rb +18 -0
  30. data/lib/new_relic/agent/transaction_event_aggregator.rb +14 -0
  31. data/lib/new_relic/agent/transaction_event_primitive.rb +14 -0
  32. data/lib/new_relic/agent/transaction_event_recorder.rb +9 -1
  33. data/lib/new_relic/agent/transaction_sampler.rb +12 -66
  34. data/lib/new_relic/agent/transaction_state.rb +1 -3
  35. data/lib/new_relic/supportability_helper.rb +5 -0
  36. data/lib/new_relic/version.rb +1 -1
  37. data/newrelic.yml +1 -1
  38. data/newrelic_rpm.gemspec +1 -1
  39. data/test/agent_helper.rb +11 -3
  40. metadata +10 -4
  41. data/lib/new_relic/agent/transaction_sample_builder.rb +0 -138
@@ -18,6 +18,8 @@ module NewRelic
18
18
  super name, start_time
19
19
  end
20
20
 
21
+ private
22
+
21
23
  def record_metrics
22
24
  if record_scoped_metric?
23
25
  metric_cache.record_scoped_and_unscoped name, duration, exclusive_duration
@@ -29,8 +31,6 @@ module NewRelic
29
31
  end
30
32
  end
31
33
 
32
- private
33
-
34
34
  def append_unscoped_metric metric
35
35
  if @unscoped_metrics
36
36
  if Array === @unscoped_metrics
@@ -48,7 +48,6 @@ module NewRelic
48
48
  end
49
49
 
50
50
  def segment_complete
51
- Agent.instance.transaction_sampler.add_node_parameters params if params?
52
51
  end
53
52
  end
54
53
  end
@@ -15,10 +15,12 @@ module NewRelic
15
15
  :attributes, :node_count, :finished, :threshold,
16
16
  :profile
17
17
 
18
+ ROOT = "ROOT".freeze
19
+
18
20
  def initialize(start_time)
19
21
  @start_time = start_time
20
22
  @node_count = 0
21
- @root_node = NewRelic::Agent::Transaction::TraceNode.new(0.0, "ROOT")
23
+ @root_node = NewRelic::Agent::Transaction::TraceNode.new(ROOT, 0.0, nil, nil, nil)
22
24
  @prepared = false
23
25
  end
24
26
 
@@ -27,7 +29,12 @@ module NewRelic
27
29
  end
28
30
 
29
31
  def count_nodes
30
- self.node_count
32
+ node_count = 0
33
+ each_node do |node|
34
+ next if node == root_node
35
+ node_count +=1
36
+ end
37
+ node_count
31
38
  end
32
39
 
33
40
  def duration
@@ -51,7 +58,7 @@ module NewRelic
51
58
  def create_node(time_since_start, metric_name = nil)
52
59
  raise FinishedTraceError.new "Can't create additional node for finished trace." if self.finished
53
60
  self.node_count += 1
54
- NewRelic::Agent::Transaction::TraceNode.new(time_since_start, metric_name)
61
+ NewRelic::Agent::Transaction::TraceNode.new(metric_name, time_since_start)
55
62
  end
56
63
 
57
64
  def each_node(&block)
@@ -111,6 +118,8 @@ module NewRelic
111
118
  @root_node.each_node_with_nest_tracking(&block)
112
119
  end
113
120
 
121
+ EMPTY_HASH = {}.freeze
122
+
114
123
  def trace_tree
115
124
  destination = NewRelic::Agent::AttributeFilter::DST_TRANSACTION_TRACER
116
125
 
@@ -120,8 +129,8 @@ module NewRelic
120
129
 
121
130
  [
122
131
  NewRelic::Coerce.float(self.start_time),
123
- {},
124
- {},
132
+ EMPTY_HASH,
133
+ EMPTY_HASH,
125
134
  self.root_node.to_array,
126
135
  {
127
136
  'agentAttributes' => agent_attributes,
@@ -0,0 +1,58 @@
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/helper'
6
+ require 'new_relic/agent/transaction/trace'
7
+ require 'new_relic/agent/transaction/trace_node'
8
+
9
+ module NewRelic
10
+ module Agent
11
+ class Transaction
12
+ module TraceBuilder
13
+ extend self
14
+
15
+ def build_trace transaction
16
+ trace = Trace.new transaction.start_time
17
+ trace.root_node.exit_timestamp = transaction.end_time - transaction.start_time
18
+ copy_attributes transaction, trace
19
+ first, *rest = transaction.segments
20
+ relationship_map = rest.group_by { |s| s.parent }
21
+ trace.root_node.children << process_segments(transaction, first, trace.root_node, relationship_map)
22
+ trace
23
+ end
24
+
25
+ private
26
+
27
+ # recursively builds a transaction trace from the flat list of segments
28
+ def process_segments transaction, segment, parent, relationship_map
29
+ current = create_trace_node transaction, segment, parent
30
+
31
+ if children = relationship_map[segment]
32
+ current.children = children.map! do |child|
33
+ process_segments transaction, child, current, relationship_map
34
+ end
35
+ end
36
+
37
+ current
38
+ end
39
+
40
+ def create_trace_node transaction, segment, parent
41
+ relative_start = segment.start_time - transaction.start_time
42
+ relative_end = segment.end_time - transaction.start_time
43
+ TraceNode.new segment.name, relative_start, relative_end, segment.params, parent
44
+ end
45
+
46
+ def copy_attributes transaction, trace
47
+ trace.transaction_name = transaction.best_name
48
+ trace.uri = transaction.request_path
49
+ trace.guid = transaction.guid
50
+ trace.attributes = transaction.attributes
51
+ trace.threshold = transaction.threshold
52
+ trace.xray_session_id = transaction.xray_session_id
53
+ trace.finished = true
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -9,20 +9,21 @@ module NewRelic
9
9
  attr_reader :entry_timestamp
10
10
  # The exit timestamp will be relative except for the outermost sample which will
11
11
  # have a timestamp.
12
- attr_reader :exit_timestamp
12
+ attr_accessor :exit_timestamp
13
+
13
14
  attr_reader :parent_node
14
15
 
15
- attr_accessor :metric_name, :params
16
+ attr_accessor :metric_name
16
17
 
17
18
  UNKNOWN_NODE_NAME = '<unknown>'.freeze
18
19
 
19
- def initialize(timestamp, metric_name)
20
- @entry_timestamp = timestamp
20
+ def initialize(metric_name, relative_start, relative_end=nil, params=nil, parent=nil)
21
+ @entry_timestamp = relative_start
21
22
  @metric_name = metric_name || UNKNOWN_NODE_NAME
22
- @exit_timestamp = nil
23
- @called_nodes = nil
24
- @params = {}
25
- @parent_node = nil
23
+ @exit_timestamp = relative_end
24
+ @children = nil
25
+ @params = params
26
+ @parent_node = parent
26
27
  end
27
28
 
28
29
  # sets the final timestamp on a node to indicate the exit
@@ -31,33 +32,31 @@ module NewRelic
31
32
  @exit_timestamp = timestamp
32
33
  end
33
34
 
34
- def add_called_node(s)
35
- @called_nodes ||= []
36
- @called_nodes << s
37
- s.parent_node = self
38
- end
39
-
40
35
  def to_s
41
36
  to_debug_str(0)
42
37
  end
43
38
 
39
+ EMPTY_HASH = {}.freeze
40
+ EMPTY_ARRAY = [].freeze
41
+
44
42
  def to_array
43
+ params = @params ? @params : EMPTY_HASH
45
44
  [ NewRelic::Helper.time_to_millis(@entry_timestamp),
46
45
  NewRelic::Helper.time_to_millis(@exit_timestamp),
47
46
  NewRelic::Coerce.string(@metric_name),
48
47
  params ] +
49
- [ (@called_nodes ? @called_nodes.map{|s| s.to_array} : []) ]
48
+ [ (@children ? @children.map{|s| s.to_array} : EMPTY_ARRAY) ]
50
49
  end
51
50
 
52
51
  def path_string
53
- "#{metric_name}[#{called_nodes.collect {|node| node.path_string }.join('')}]"
52
+ "#{metric_name}[#{children.collect {|node| node.path_string }.join('')}]"
54
53
  end
55
54
 
56
55
  def to_s_compact
57
56
  str = ""
58
57
  str << metric_name
59
- if called_nodes.any?
60
- str << "{#{called_nodes.map { | cs | cs.to_s_compact }.join(",")}}"
58
+ if children.any?
59
+ str << "{#{children.map { | cs | cs.to_s_compact }.join(",")}}"
61
60
  end
62
61
  str
63
62
  end
@@ -71,7 +70,7 @@ module NewRelic
71
70
  s << "#{tab} -#{'%-16s' % k}: #{v.to_s[0..80]}\n"
72
71
  end
73
72
  end
74
- called_nodes.each do |cs|
73
+ children.each do |cs|
75
74
  s << cs.to_debug_str(depth + 1)
76
75
  end
77
76
  s << tab + "<< "
@@ -83,10 +82,12 @@ module NewRelic
83
82
  s << " #{metric_name}\n"
84
83
  end
85
84
 
86
- def called_nodes
87
- @called_nodes || []
85
+ def children
86
+ @children ||= []
88
87
  end
89
88
 
89
+ attr_writer :children
90
+
90
91
  # return the total duration of this node
91
92
  def duration
92
93
  (@exit_timestamp - @entry_timestamp).to_f
@@ -97,7 +98,7 @@ module NewRelic
97
98
  def exclusive_duration
98
99
  d = duration
99
100
 
100
- called_nodes.each do |node|
101
+ children.each do |node|
101
102
  d -= node.duration
102
103
  end
103
104
  d
@@ -105,10 +106,16 @@ module NewRelic
105
106
 
106
107
  def count_nodes
107
108
  count = 1
108
- called_nodes.each { | node | count += node.count_nodes }
109
+ children.each { | node | count += node.count_nodes }
109
110
  count
110
111
  end
111
112
 
113
+ def params
114
+ @params ||= {}
115
+ end
116
+
117
+ attr_writer :params
118
+
112
119
  def []=(key, value)
113
120
  # only create a parameters field if a parameter is set; this will save
114
121
  # bandwidth etc as most nodes have no parameters
@@ -124,8 +131,8 @@ module NewRelic
124
131
  def each_node(&block)
125
132
  block.call self
126
133
 
127
- if @called_nodes
128
- @called_nodes.each do |node|
134
+ if @children
135
+ @children.each do |node|
129
136
  node.each_node(&block)
130
137
  end
131
138
  end
@@ -137,8 +144,8 @@ module NewRelic
137
144
  summary = block.call self
138
145
  summary.current_nest_count += 1 if summary
139
146
 
140
- if @called_nodes
141
- @called_nodes.each do |node|
147
+ if @children
148
+ @children.each do |node|
142
149
  node.each_node_with_nest_tracking(&block)
143
150
  end
144
151
  end
@@ -159,11 +166,6 @@ module NewRelic
159
166
  NewRelic::Agent::Database.obfuscate_sql(params[:sql].sql)
160
167
  end
161
168
 
162
- def called_nodes=(nodes)
163
- @called_nodes = nodes
164
- end
165
-
166
- protected
167
169
  def parent_node=(s)
168
170
  @parent_node = s
169
171
  end
@@ -88,25 +88,20 @@ module NewRelic
88
88
  segment.transaction = self
89
89
  segment.parent = current_segment
90
90
  @current_segment = segment
91
- if @segments.length < Agent.config[:'transaction_tracer.limit_segments']
91
+ if @segments.length < segment_limit
92
92
  @segments << segment
93
- transaction_sampler.notice_push_frame state, segment.start_time if transaction_sampler_enabled?
94
93
  else
95
94
  segment.record_on_finish = true
95
+ ::NewRelic::Agent.logger.debug("Segment limit of #{segment_limit} reached, ceasing collection.")
96
96
  end
97
97
  end
98
98
 
99
99
  def segment_complete segment
100
100
  @current_segment = segment.parent
101
- if transaction_sampler_enabled? && !segment.record_on_finish?
102
- transaction_sampler.notice_pop_frame state, segment.name, segment.end_time
103
- end
104
101
  end
105
102
 
106
- private
107
-
108
- def transaction_sampler_enabled?
109
- Agent.config[:'transaction_tracer.enabled']
103
+ def segment_limit
104
+ Agent.config[:'transaction_tracer.limit_segments']
110
105
  end
111
106
  end
112
107
  end
@@ -8,6 +8,7 @@
8
8
  # the transaction event aggregator and the synthetics container.
9
9
 
10
10
  require 'new_relic/agent/payload_metric_mapping'
11
+ require 'new_relic/agent/distributed_trace_payload'
11
12
 
12
13
  module NewRelic
13
14
  module Agent
@@ -22,6 +23,7 @@ module NewRelic
22
23
  PORT_KEY = 'port'.freeze
23
24
  NAME_KEY = 'transactionName'.freeze
24
25
  DURATION_KEY = 'duration'.freeze
26
+ SAMPLED_KEY = 'nr.sampled'.freeze
25
27
  GUID_KEY = 'nr.transactionGuid'.freeze
26
28
  REFERRING_TRANSACTION_GUID_KEY = 'nr.referringTransactionGuid'.freeze
27
29
  SYNTHETICS_RESOURCE_ID_KEY = "nr.syntheticsResourceId".freeze
@@ -49,8 +51,10 @@ module NewRelic
49
51
  if payload
50
52
  attrs[NAME_KEY] = payload[:name]
51
53
  attrs[DURATION_KEY] = payload[:duration]
54
+ attrs[SAMPLED_KEY] = payload[:'nr.sampled'] if Agent.config[:'distributed_tracing.enabled']
52
55
  append_synthetics payload, attrs
53
56
  append_cat payload, attrs
57
+ append_distributed_trace_intrinsics payload, attrs
54
58
  PayloadMetricMapping.append_mapped_metrics payload[:metrics], attrs
55
59
  end
56
60
 
@@ -67,6 +71,20 @@ module NewRelic
67
71
  sample[GUID_KEY] = payload[:guid] if payload[:guid]
68
72
  sample[REFERRING_TRANSACTION_GUID_KEY] = payload[:referring_transaction_guid] if payload[:referring_transaction_guid]
69
73
  end
74
+
75
+ OTHER_GUID_KEY = "nr.guid".freeze
76
+
77
+ def append_distributed_trace_intrinsics payload, sample
78
+ return unless Agent.config[:'distributed_tracing.enabled']
79
+ DistributedTracePayload::INTRINSIC_KEYS.each do |key|
80
+ value = payload[key]
81
+ sample[key] = value unless value.nil?
82
+ end
83
+ # guid has a different name for transaction events
84
+ if sample.key? OTHER_GUID_KEY
85
+ sample[GUID_KEY] = sample.delete OTHER_GUID_KEY
86
+ end
87
+ end
70
88
  end
71
89
  end
72
90
  end
@@ -6,6 +6,7 @@
6
6
  require 'newrelic_rpm' unless defined?( NewRelic )
7
7
  require 'new_relic/agent' unless defined?( NewRelic::Agent )
8
8
  require 'new_relic/agent/event_aggregator'
9
+ require 'new_relic/agent/distributed_trace_priority_sampled_buffer'
9
10
 
10
11
  module NewRelic
11
12
  module Agent
@@ -14,6 +15,7 @@ module NewRelic
14
15
  named :TransactionEventAggregator
15
16
  capacity_key :'analytics_events.max_samples_stored'
16
17
  enabled_key :'analytics_events.enabled'
18
+ buffer_class DistributedTracePrioritySampledBuffer
17
19
 
18
20
  def append event=nil, &blk
19
21
  raise ArgumentError, "Expected argument or block, but received both" if event && blk
@@ -25,6 +27,18 @@ module NewRelic
25
27
  end
26
28
  end
27
29
 
30
+ # events that are selected to be sampled have priority of other events (ie
31
+ # the ones passed to append)
32
+ def append_sampled event=nil, &blk
33
+ raise ArgumentError, "Expected argument or block, but received both" if event && blk
34
+ return unless enabled?
35
+
36
+ @lock.synchronize do
37
+ @buffer.append_sampled event, &blk
38
+ notify_if_full
39
+ end
40
+ end
41
+
28
42
  private
29
43
 
30
44
  def after_harvest metadata
@@ -8,6 +8,7 @@
8
8
  # the transaction event aggregator and the synthetics container.
9
9
 
10
10
  require 'new_relic/agent/payload_metric_mapping'
11
+ require 'new_relic/agent/distributed_trace_payload'
11
12
 
12
13
  module NewRelic
13
14
  module Agent
@@ -24,6 +25,7 @@ module NewRelic
24
25
  NAME_KEY = 'name'.freeze
25
26
  DURATION_KEY = 'duration'.freeze
26
27
  ERROR_KEY = 'error'.freeze
28
+ SAMPLED_KEY = 'nr.sampled'.freeze
27
29
  GUID_KEY = 'nr.guid'.freeze
28
30
  REFERRING_TRANSACTION_GUID_KEY = 'nr.referringTransactionGuid'.freeze
29
31
  CAT_TRIP_ID_KEY = 'nr.tripId'.freeze
@@ -35,6 +37,7 @@ module NewRelic
35
37
  SYNTHETICS_JOB_ID_KEY = "nr.syntheticsJobId".freeze
36
38
  SYNTHETICS_MONITOR_ID_KEY = "nr.syntheticsMonitorId".freeze
37
39
 
40
+
38
41
  # To avoid allocations when we have empty custom or agent attributes
39
42
  EMPTY_HASH = {}.freeze
40
43
 
@@ -47,8 +50,11 @@ module NewRelic
47
50
  ERROR_KEY => payload[:error]
48
51
  }
49
52
 
53
+ intrinsics[SAMPLED_KEY] = payload[:'nr.sampled'] if Agent.config[:'distributed_tracing.enabled']
54
+
50
55
  NewRelic::Agent::PayloadMetricMapping.append_mapped_metrics(payload[:metrics], intrinsics)
51
56
  append_optional_attributes(intrinsics, payload)
57
+ append_distributed_trace_intrinsics(intrinsics, payload)
52
58
 
53
59
  attributes = payload[:attributes]
54
60
 
@@ -78,6 +84,14 @@ module NewRelic
78
84
  end
79
85
  end
80
86
 
87
+ def append_distributed_trace_intrinsics(sample, payload)
88
+ return unless Agent.config[:'distributed_tracing.enabled']
89
+ DistributedTracePayload::INTRINSIC_KEYS.each do |key|
90
+ value = payload[key]
91
+ sample[key] = value unless value.nil?
92
+ end
93
+ end
94
+
81
95
  def optionally_append(sample_key, payload_key, sample, payload)
82
96
  if payload.include?(payload_key)
83
97
  sample[sample_key] = string(payload[payload_key])