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.
- checksums.yaml +4 -4
- data/.yardopts +2 -0
- data/CHANGELOG.md +8 -0
- data/lib/new_relic/agent.rb +1 -0
- data/lib/new_relic/agent/agent.rb +19 -12
- data/lib/new_relic/agent/configuration/default_source.rb +7 -0
- data/lib/new_relic/agent/distributed_trace_monitor.rb +29 -0
- data/lib/new_relic/agent/distributed_trace_payload.rb +223 -0
- data/lib/new_relic/agent/distributed_trace_priority_sampled_buffer.rb +72 -0
- data/lib/new_relic/agent/external.rb +137 -0
- data/lib/new_relic/agent/http_clients/abstract_request.rb +31 -0
- data/lib/new_relic/agent/http_clients/curb_wrappers.rb +3 -1
- data/lib/new_relic/agent/http_clients/excon_wrappers.rb +3 -1
- data/lib/new_relic/agent/http_clients/http_rb_wrappers.rb +3 -1
- data/lib/new_relic/agent/http_clients/httpclient_wrappers.rb +3 -1
- data/lib/new_relic/agent/http_clients/net_http_wrappers.rb +3 -1
- data/lib/new_relic/agent/http_clients/typhoeus_wrappers.rb +3 -1
- data/lib/new_relic/agent/sql_sampler.rb +2 -2
- data/lib/new_relic/agent/throughput_monitor.rb +59 -0
- data/lib/new_relic/agent/transaction.rb +59 -15
- data/lib/new_relic/agent/transaction/abstract_segment.rb +17 -5
- data/lib/new_relic/agent/transaction/distributed_tracing.rb +121 -0
- data/lib/new_relic/agent/transaction/external_request_segment.rb +183 -19
- data/lib/new_relic/agent/transaction/segment.rb +2 -3
- data/lib/new_relic/agent/transaction/trace.rb +14 -5
- data/lib/new_relic/agent/transaction/trace_builder.rb +58 -0
- data/lib/new_relic/agent/transaction/trace_node.rb +34 -32
- data/lib/new_relic/agent/transaction/tracing.rb +4 -9
- data/lib/new_relic/agent/transaction_error_primitive.rb +18 -0
- data/lib/new_relic/agent/transaction_event_aggregator.rb +14 -0
- data/lib/new_relic/agent/transaction_event_primitive.rb +14 -0
- data/lib/new_relic/agent/transaction_event_recorder.rb +9 -1
- data/lib/new_relic/agent/transaction_sampler.rb +12 -66
- data/lib/new_relic/agent/transaction_state.rb +1 -3
- data/lib/new_relic/supportability_helper.rb +5 -0
- data/lib/new_relic/version.rb +1 -1
- data/newrelic.yml +1 -1
- data/newrelic_rpm.gemspec +1 -1
- data/test/agent_helper.rb +11 -3
- metadata +10 -4
- data/lib/new_relic/agent/transaction_sample_builder.rb +0 -138
@@ -37,12 +37,13 @@ module NewRelic
|
|
37
37
|
transaction.segment_complete self
|
38
38
|
end
|
39
39
|
rescue => e
|
40
|
-
# This rescue block was added for the benefit of this test:
|
41
|
-
# test/multiverse/suites/bare/standalone_instrumentation_test.rb
|
42
|
-
# See the top of the test for details.
|
43
40
|
NewRelic::Agent.logger.error "Exception finishing segment: #{name}", e
|
44
41
|
end
|
45
42
|
|
43
|
+
def finished?
|
44
|
+
!!@end_time
|
45
|
+
end
|
46
|
+
|
46
47
|
def record_metrics?
|
47
48
|
@record_metrics
|
48
49
|
end
|
@@ -55,8 +56,15 @@ module NewRelic
|
|
55
56
|
@record_on_finish
|
56
57
|
end
|
57
58
|
|
58
|
-
def
|
59
|
-
|
59
|
+
def finalize
|
60
|
+
unless finished?
|
61
|
+
finish
|
62
|
+
NewRelic::Agent.logger.warn "Segment: #{name} was unfinished at " \
|
63
|
+
"the end of transaction. Timing information for " \
|
64
|
+
"#{transaction.best_name} may be inaccurate."
|
65
|
+
# @todo: we should record a supportability metric here
|
66
|
+
end
|
67
|
+
record_metrics if record_metrics?
|
60
68
|
end
|
61
69
|
|
62
70
|
def params
|
@@ -78,6 +86,10 @@ module NewRelic
|
|
78
86
|
|
79
87
|
protected
|
80
88
|
|
89
|
+
def record_metrics
|
90
|
+
raise NotImplementedError, "Subclasses must implement record_metrics"
|
91
|
+
end
|
92
|
+
|
81
93
|
def child_complete segment
|
82
94
|
if segment.record_metrics?
|
83
95
|
self.children_time += segment.duration
|
@@ -0,0 +1,121 @@
|
|
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/distributed_trace_payload'
|
6
|
+
|
7
|
+
module NewRelic
|
8
|
+
module Agent
|
9
|
+
class Transaction
|
10
|
+
module DistributedTracing
|
11
|
+
attr_accessor :distributed_trace_payload
|
12
|
+
|
13
|
+
def distributed_trace?
|
14
|
+
!!distributed_trace_payload
|
15
|
+
end
|
16
|
+
|
17
|
+
def create_distributed_trace_payload url = nil
|
18
|
+
return unless Agent.config[:'distributed_tracing.enabled']
|
19
|
+
self.order += 1
|
20
|
+
DistributedTracePayload.for_transaction self, url
|
21
|
+
end
|
22
|
+
|
23
|
+
LBRACE = "{".freeze
|
24
|
+
|
25
|
+
def accept_distributed_trace_payload transport_type, payload
|
26
|
+
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
|
46
|
+
|
47
|
+
self.sampled = payload.sampled unless payload.sampled.nil?
|
48
|
+
|
49
|
+
true
|
50
|
+
rescue => e
|
51
|
+
NewRelic::Agent.increment_metric "Supportability/DistributedTracing/AcceptFailure/Unknown"
|
52
|
+
NewRelic::Agent.logger.warn "Failed to accept distributed trace payload", e
|
53
|
+
false
|
54
|
+
end
|
55
|
+
|
56
|
+
def distributed_tracing_trip_id
|
57
|
+
if distributed_trace_payload
|
58
|
+
distributed_trace_payload.trip_id
|
59
|
+
else
|
60
|
+
guid
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
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
|
77
|
+
end
|
78
|
+
|
79
|
+
def depth
|
80
|
+
if distributed_trace_payload
|
81
|
+
distributed_trace_payload.depth
|
82
|
+
else
|
83
|
+
1
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def order
|
88
|
+
@order ||= 0
|
89
|
+
end
|
90
|
+
|
91
|
+
attr_writer :order
|
92
|
+
|
93
|
+
def append_distributed_tracing_info payload
|
94
|
+
return unless Agent.config[:'distributed_tracing.enabled']
|
95
|
+
if distributed_trace_payload
|
96
|
+
distributed_trace_payload.assign_intrinsics self, payload
|
97
|
+
elsif order > 0
|
98
|
+
DistributedTracePayload.assign_initial_intrinsics self, payload
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def assign_distributed_tracing_intrinsics
|
103
|
+
return unless Agent.config[:'distributed_tracing.enabled']
|
104
|
+
DistributedTracePayload::INTRINSIC_KEYS.each do |key|
|
105
|
+
next unless value = @payload[key]
|
106
|
+
attributes.add_intrinsic_attribute key, value
|
107
|
+
end
|
108
|
+
nil
|
109
|
+
end
|
110
|
+
|
111
|
+
# This method returns transport_duration in seconds. Transport duration
|
112
|
+
# is stored in milliseconds on the payload, but it's needed in seconds
|
113
|
+
# for metrics and intrinsics.
|
114
|
+
def transport_duration
|
115
|
+
return unless distributed_trace_payload
|
116
|
+
(start_time.to_f * 1000 - distributed_trace_payload.timestamp) / 1000
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -8,12 +8,17 @@ require 'new_relic/agent/http_clients/uri_util'
|
|
8
8
|
module NewRelic
|
9
9
|
module Agent
|
10
10
|
class Transaction
|
11
|
+
|
12
|
+
#
|
13
|
+
# This class represents an external segment in a transaction trace.
|
14
|
+
#
|
15
|
+
# @api public
|
11
16
|
class ExternalRequestSegment < Segment
|
12
17
|
attr_reader :library, :uri, :procedure
|
13
18
|
|
14
19
|
NR_SYNTHETICS_HEADER = 'X-NewRelic-Synthetics'.freeze
|
15
20
|
|
16
|
-
def initialize library, uri, procedure
|
21
|
+
def initialize library, uri, procedure # :nodoc:
|
17
22
|
@library = library
|
18
23
|
@uri = HTTPClients::URIUtil.parse_and_normalize_url(uri)
|
19
24
|
@procedure = procedure
|
@@ -22,20 +27,25 @@ module NewRelic
|
|
22
27
|
super()
|
23
28
|
end
|
24
29
|
|
25
|
-
def name
|
30
|
+
def name # :nodoc:
|
26
31
|
@name ||= "External/#{host}/#{library}/#{procedure}"
|
27
32
|
end
|
28
33
|
|
29
|
-
def host
|
34
|
+
def host # :nodoc:
|
30
35
|
@host_header || uri.host
|
31
36
|
end
|
32
37
|
|
33
|
-
# This method
|
34
|
-
#
|
35
|
-
# header is used it
|
38
|
+
# This method adds New Relic request headers to a given request made to an
|
39
|
+
# external API and checks to see if a host header is used for the request.
|
40
|
+
# If a host header is used, it updates the segment name to match the host
|
41
|
+
# header.
|
42
|
+
#
|
43
|
+
# @param [NewRelic::Agent::HTTPClients::AbstractRequest] request the request
|
44
|
+
# object (must belong to a subclass of NewRelic::Agent::HTTPClients::AbstractRequest)
|
45
|
+
#
|
46
|
+
# @api public
|
36
47
|
def add_request_headers request
|
37
48
|
process_host_header request
|
38
|
-
|
39
49
|
synthetics_header = transaction && transaction.raw_synthetics_header
|
40
50
|
insert_synthetics_header request, synthetics_header if synthetics_header
|
41
51
|
|
@@ -43,14 +53,22 @@ module NewRelic
|
|
43
53
|
|
44
54
|
transaction_state.is_cross_app_caller = true
|
45
55
|
txn_guid = transaction_state.request_guid
|
46
|
-
trip_id = transaction && transaction.cat_trip_id
|
47
|
-
path_hash = transaction && transaction.cat_path_hash
|
56
|
+
trip_id = transaction && transaction.cat_trip_id
|
57
|
+
path_hash = transaction && transaction.cat_path_hash
|
48
58
|
|
49
59
|
CrossAppTracing.insert_request_headers request, txn_guid, trip_id, path_hash
|
60
|
+
insert_distributed_trace_header request
|
50
61
|
rescue => e
|
51
62
|
NewRelic::Agent.logger.error "Error in add_request_headers", e
|
52
63
|
end
|
53
|
-
|
64
|
+
|
65
|
+
# This method extracts app data from an external response if present. If
|
66
|
+
# a valid cross-app ID is found, the name of the segment is updated to
|
67
|
+
# reflect the cross-process ID and transaction name.
|
68
|
+
#
|
69
|
+
# @param [Hash] response a hash of response headers
|
70
|
+
#
|
71
|
+
# @api public
|
54
72
|
def read_response_headers response
|
55
73
|
return unless record_metrics? && CrossAppTracing.cross_app_enabled?
|
56
74
|
return unless CrossAppTracing.response_has_crossapp_header?(response)
|
@@ -69,22 +87,103 @@ module NewRelic
|
|
69
87
|
NewRelic::Agent.logger.error "Error in read_response_headers", e
|
70
88
|
end
|
71
89
|
|
72
|
-
def cross_app_request?
|
90
|
+
def cross_app_request? # :nodoc:
|
73
91
|
!!@app_data
|
74
92
|
end
|
75
93
|
|
76
|
-
def cross_process_id
|
94
|
+
def cross_process_id # :nodoc:
|
77
95
|
@app_data && @app_data[0]
|
78
96
|
end
|
79
97
|
|
80
|
-
def transaction_guid
|
98
|
+
def transaction_guid # :nodoc:
|
81
99
|
@app_data && @app_data[5]
|
82
100
|
end
|
83
101
|
|
84
|
-
def cross_process_transaction_name
|
102
|
+
def cross_process_transaction_name # :nodoc:
|
85
103
|
@app_data && @app_data[1]
|
86
104
|
end
|
87
105
|
|
106
|
+
EXTERNAL_TRANSACTION_PREFIX = 'ExternalTransaction/'.freeze
|
107
|
+
SLASH = '/'.freeze
|
108
|
+
APP_DATA_KEY = 'NewRelicAppData'.freeze
|
109
|
+
|
110
|
+
# Obtain an obfuscated +String+ suitable for delivery across public networks that identifies this application
|
111
|
+
# and transaction to another application which is also running a New Relic agent. This +String+ can be processed
|
112
|
+
# by +process_request_metadata+ on the receiving application.
|
113
|
+
#
|
114
|
+
# @return [String] obfuscated request metadata to send
|
115
|
+
#
|
116
|
+
# @api public
|
117
|
+
#
|
118
|
+
def get_request_metadata
|
119
|
+
NewRelic::Agent.record_api_supportability_metric(:get_request_metadata)
|
120
|
+
return unless CrossAppTracing.cross_app_enabled?
|
121
|
+
|
122
|
+
if transaction
|
123
|
+
|
124
|
+
# build hash of CAT metadata
|
125
|
+
#
|
126
|
+
rmd = {
|
127
|
+
NewRelicID: NewRelic::Agent.config[:cross_process_id],
|
128
|
+
NewRelicTransaction: [
|
129
|
+
transaction.guid,
|
130
|
+
false,
|
131
|
+
transaction.cat_trip_id,
|
132
|
+
transaction.cat_path_hash
|
133
|
+
]
|
134
|
+
}
|
135
|
+
|
136
|
+
# flag cross app in the state so transaction knows to add bits to paylaod
|
137
|
+
#
|
138
|
+
transaction.state.is_cross_app_caller = true
|
139
|
+
|
140
|
+
# add Synthetics header if we have it
|
141
|
+
#
|
142
|
+
rmd[:NewRelicSynthetics] = transaction.raw_synthetics_header if transaction.raw_synthetics_header
|
143
|
+
|
144
|
+
# obfuscate the generated request metadata JSON
|
145
|
+
#
|
146
|
+
obfuscator.obfuscate ::JSON.dump(rmd)
|
147
|
+
|
148
|
+
end
|
149
|
+
rescue => e
|
150
|
+
NewRelic::Agent.logger.error "error during get_request_metadata", e
|
151
|
+
end
|
152
|
+
|
153
|
+
# Process obfuscated +String+ sent from a called application that is also running a New Relic agent and
|
154
|
+
# save information in current transaction for inclusion in a trace. This +String+ is generated by
|
155
|
+
# +get_response_metadata+ on the receiving application.
|
156
|
+
#
|
157
|
+
# @param response_metadata [String] received obfuscated response metadata
|
158
|
+
#
|
159
|
+
# @api public
|
160
|
+
#
|
161
|
+
def process_response_metadata response_metadata
|
162
|
+
NewRelic::Agent.record_api_supportability_metric(:process_response_metadata)
|
163
|
+
if transaction
|
164
|
+
app_data = ::JSON.parse(obfuscator.deobfuscate(response_metadata))[APP_DATA_KEY]
|
165
|
+
|
166
|
+
# validate cross app id
|
167
|
+
#
|
168
|
+
if Array === app_data and CrossAppTracing.trusted_valid_cross_app_id? app_data[0]
|
169
|
+
@app_data = app_data
|
170
|
+
update_segment_name
|
171
|
+
else
|
172
|
+
NewRelic::Agent.logger.error "error processing response metadata: invalid/non-trusted ID"
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
nil
|
177
|
+
rescue => e
|
178
|
+
NewRelic::Agent.logger.error "error during process_response_metadata", e
|
179
|
+
end
|
180
|
+
|
181
|
+
def record_metrics
|
182
|
+
add_unscoped_metrics
|
183
|
+
record_distributed_tracing_metrics if Agent.config[:'distributed_tracing.enabled']
|
184
|
+
super
|
185
|
+
end
|
186
|
+
|
88
187
|
private
|
89
188
|
|
90
189
|
def insert_synthetics_header request, header
|
@@ -106,19 +205,25 @@ module NewRelic
|
|
106
205
|
end
|
107
206
|
end
|
108
207
|
|
208
|
+
X_NEWRELIC_TRACE_HEADER = "X-NewRelic-Trace".freeze
|
209
|
+
|
210
|
+
def insert_distributed_trace_header request
|
211
|
+
return unless Agent.config[:'distributed_tracing.enabled']
|
212
|
+
payload = transaction.create_distributed_trace_payload uri
|
213
|
+
request[X_NEWRELIC_TRACE_HEADER] = payload.http_safe
|
214
|
+
end
|
215
|
+
|
109
216
|
EXTERNAL_ALL = "External/all".freeze
|
110
217
|
|
111
|
-
def
|
112
|
-
|
218
|
+
def add_unscoped_metrics
|
219
|
+
@unscoped_metrics = [ EXTERNAL_ALL,
|
113
220
|
"External/#{host}/all",
|
114
221
|
suffixed_rollup_metric
|
115
222
|
]
|
116
223
|
|
117
224
|
if cross_app_request?
|
118
|
-
|
225
|
+
@unscoped_metrics << "ExternalApp/#{host}/#{cross_process_id}/all"
|
119
226
|
end
|
120
|
-
|
121
|
-
metrics
|
122
227
|
end
|
123
228
|
|
124
229
|
EXTERNAL_ALL_WEB = "External/allWeb".freeze
|
@@ -132,6 +237,61 @@ module NewRelic
|
|
132
237
|
end
|
133
238
|
end
|
134
239
|
|
240
|
+
ALL_SUFFIX = "all".freeze
|
241
|
+
ALL_WEB_SUFFIX = "allWeb".freeze
|
242
|
+
ALL_OTHER_SUFFIX = "allOther".freeze
|
243
|
+
|
244
|
+
def transaction_type_suffix
|
245
|
+
if Transaction.recording_web_transaction?
|
246
|
+
ALL_WEB_SUFFIX
|
247
|
+
else
|
248
|
+
ALL_OTHER_SUFFIX
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
def record_distributed_tracing_metrics
|
253
|
+
add_caller_by_duration_metrics
|
254
|
+
record_transport_duration_metrics
|
255
|
+
record_errors_by_caller_metrics
|
256
|
+
end
|
257
|
+
|
258
|
+
DURATION_BY_CALLER_UNKOWN_PREFIX = "DurationByCaller/Unknown/Unknown/Unknown/Unknown".freeze
|
259
|
+
|
260
|
+
def add_caller_by_duration_metrics
|
261
|
+
prefix = if transaction.distributed_trace?
|
262
|
+
payload = transaction.distributed_trace_payload
|
263
|
+
"DurationByCaller/#{payload.caller_type}/#{payload.caller_account_id}/#{payload.caller_app_id}/transport"
|
264
|
+
else
|
265
|
+
DURATION_BY_CALLER_UNKOWN_PREFIX
|
266
|
+
end
|
267
|
+
|
268
|
+
@unscoped_metrics << "#{prefix}/#{ALL_SUFFIX}"
|
269
|
+
@unscoped_metrics << "#{prefix}/#{transaction_type_suffix}"
|
270
|
+
end
|
271
|
+
|
272
|
+
def record_transport_duration_metrics
|
273
|
+
return unless transaction.distributed_trace?
|
274
|
+
payload = transaction.distributed_trace_payload
|
275
|
+
prefix = "TransportDuration/#{payload.caller_type}/#{payload.caller_account_id}/#{payload.caller_app_id}/transport"
|
276
|
+
metric_cache.record_unscoped "#{prefix}/#{ALL_SUFFIX}", transaction.transport_duration
|
277
|
+
metric_cache.record_unscoped "#{prefix}/#{transaction_type_suffix}", transaction.transport_duration
|
278
|
+
end
|
279
|
+
|
280
|
+
ERRORS_BY_CALLER_UNKOWN_PREFIX = "ErrorsByCaller/Unknown/Unknown/Unknown/Unknown".freeze
|
281
|
+
|
282
|
+
def record_errors_by_caller_metrics
|
283
|
+
return unless transaction.exceptions.size > 0
|
284
|
+
prefix = if transaction.distributed_trace?
|
285
|
+
payload = transaction.distributed_trace_payload
|
286
|
+
"ErrorsByCaller/#{payload.caller_type}/#{payload.caller_account_id}/#{payload.caller_app_id}/transport"
|
287
|
+
else
|
288
|
+
ERRORS_BY_CALLER_UNKOWN_PREFIX
|
289
|
+
end
|
290
|
+
|
291
|
+
NewRelic::Agent.increment_metric "#{prefix}/#{ALL_SUFFIX}"
|
292
|
+
NewRelic::Agent.increment_metric "#{prefix}/#{transaction_type_suffix}"
|
293
|
+
end
|
294
|
+
|
135
295
|
def update_segment_name
|
136
296
|
if @app_data
|
137
297
|
@name = "ExternalTransaction/#{host}/#{cross_process_id}/#{cross_process_transaction_name}"
|
@@ -139,6 +299,10 @@ module NewRelic
|
|
139
299
|
@name = "External/#{host}/#{library}/#{procedure}"
|
140
300
|
end
|
141
301
|
end
|
302
|
+
|
303
|
+
def obfuscator
|
304
|
+
CrossAppTracing.obfuscator
|
305
|
+
end
|
142
306
|
end
|
143
307
|
end
|
144
308
|
end
|