Module: NewRelic::Agent::CrossAppTracing
- Extended by:
- NewRelic::Agent::CrossAppMonitor::EncodingFunctions
- Defined in:
- lib/new_relic/agent/cross_app_tracing.rb
Defined Under Namespace
Classes: Error
Constant Summary collapse
- NR_APPDATA_HEADER =
The cross app response header for “outgoing” calls
'X-NewRelic-App-Data'
- NR_ID_HEADER =
The cross app id header for “outgoing” calls
'X-NewRelic-ID'
- NR_TXN_HEADER =
The cross app transaction header for “outgoing” calls
'X-NewRelic-Transaction'
- APPDATA_TXN_GUID_INDEX =
The index of the transaction GUID in the appdata header of responses
5
Class Method Summary collapse
-
.add_cat_transaction_trace_parameters(response) ⇒ Object
Extract any custom parameters from
response
if it’s cross-application and add them to the current TT node. - .add_transaction_trace_parameters(request, response) ⇒ Object
-
.check_crossapp_id(id) ⇒ Object
Check the given
id
to ensure it conforms to the format of a cross-application ID. -
.check_transaction_name(name) ⇒ Object
Check the given
name
to ensure it conforms to the format of a valid transaction name. -
.common_metrics(request) ⇒ Object
Return an Array of metrics used for every response.
-
.cross_app_enabled? ⇒ Boolean
Return
true
if cross app tracing is enabled in the config. -
.cross_app_encoding_key ⇒ Object
Memoized fetcher for the cross app encoding key.
-
.extract_appdata(response) ⇒ Object
Extract x-process application data from the specified
response
and return it as an array of the form:. -
.finish_trace(t0, segment, request, response) ⇒ Object
Finish tracing the HTTP
request
that started att0
with the information inresponse
and the givenhttp
connection. -
.inject_request_headers(request) ⇒ Object
Inject the X-Process header into the outgoing
request
. -
.metrics_for(request, response) ⇒ Object
Return the set of metric names that correspond to the given
request
andresponse
. -
.metrics_for_crossapp_response(request, response) ⇒ Object
Return the set of metric objects appropriate for the given cross app
response
. -
.metrics_for_regular_request(request) ⇒ Object
Return the set of metric objects appropriate for the given (non-cross app)
request
. -
.response_is_crossapp?(response) ⇒ Boolean
Returns
true
if Cross Application Tracing is enabled, and the givenresponse
has the appropriate headers. -
.start_trace(request) ⇒ Object
Set up the necessary state for cross-application tracing before the given
request
goes out. -
.stats_engine ⇒ Object
Fetch a reference to the stats engine.
-
.trace_http_request(request) ⇒ Object
Send the given
request
, adding metrics appropriate to the response when it comes back. - .transaction_sampler ⇒ Object
Methods included from NewRelic::Agent::CrossAppMonitor::EncodingFunctions
decode_with_key, encode_with_key, obfuscate_with_key
Class Method Details
.add_cat_transaction_trace_parameters(response) ⇒ Object
Extract any custom parameters from response
if it’s cross-application and add them to the current TT node.
162 163 164 165 166 |
# File 'lib/new_relic/agent/cross_app_tracing.rb', line 162 def add_cat_transaction_trace_parameters( response ) appdata = extract_appdata( response ) transaction_sampler.add_segment_parameters( \ :transaction_guid => appdata[APPDATA_TXN_GUID_INDEX] ) end |
.add_transaction_trace_parameters(request, response) ⇒ Object
151 152 153 154 155 156 157 |
# File 'lib/new_relic/agent/cross_app_tracing.rb', line 151 def add_transaction_trace_parameters(request, response) filtered_uri = ::NewRelic::Agent::HTTPClients::URIUtil.filter_uri(request.uri) transaction_sampler.add_segment_parameters(:uri => filtered_uri) if response && response_is_crossapp?( response ) add_cat_transaction_trace_parameters( response ) end end |
.check_crossapp_id(id) ⇒ Object
Check the given id
to ensure it conforms to the format of a cross-application ID. Raises an NewRelic::Agent::CrossAppTracing::Error if it doesn’t.
287 288 289 290 291 |
# File 'lib/new_relic/agent/cross_app_tracing.rb', line 287 def check_crossapp_id( id ) id =~ /\A\d+#\d+\z/ or raise NewRelic::Agent::CrossAppTracing::Error, "malformed cross application ID %p" % [ id ] end |
.check_transaction_name(name) ⇒ Object
Check the given name
to ensure it conforms to the format of a valid transaction name.
296 297 298 299 |
# File 'lib/new_relic/agent/cross_app_tracing.rb', line 296 def check_transaction_name( name ) # No-op -- apparently absolutely anything is a valid transaction name? # This is here for when that inevitably comes back to haunt us. end |
.common_metrics(request) ⇒ Object
Return an Array of metrics used for every response.
195 196 197 198 199 200 201 202 203 204 205 206 |
# File 'lib/new_relic/agent/cross_app_tracing.rb', line 195 def common_metrics( request ) metrics = [ "External/all" ] metrics << "External/#{request.host}/all" if NewRelic::Agent::Transaction.recording_web_transaction? metrics << "External/allWeb" else metrics << "External/allOther" end return metrics end |
.cross_app_enabled? ⇒ Boolean
Return true
if cross app tracing is enabled in the config.
121 122 123 124 125 |
# File 'lib/new_relic/agent/cross_app_tracing.rb', line 121 def cross_app_enabled? NewRelic::Agent.config[:cross_process_id] && (NewRelic::Agent.config[:"cross_application_tracer.enabled"] || NewRelic::Agent.config[:cross_application_tracing]) end |
.cross_app_encoding_key ⇒ Object
Memoized fetcher for the cross app encoding key. Raises a NewRelic::Agent::CrossAppTracing::Error if the key isn’t configured.
130 131 132 133 |
# File 'lib/new_relic/agent/cross_app_tracing.rb', line 130 def cross_app_encoding_key NewRelic::Agent.config[:encoding_key] or raise NewRelic::Agent::CrossAppTracing::Error, "No encoding_key set." end |
.extract_appdata(response) ⇒ Object
Extract x-process application data from the specified response
and return it as an array of the form:
[
<cross app ID>,
<transaction name>,
<queue time in seconds>,
<response time in seconds>,
<request content length in bytes>,
<transaction GUID>
]
252 253 254 255 256 257 258 259 260 261 262 263 |
# File 'lib/new_relic/agent/cross_app_tracing.rb', line 252 def extract_appdata( response ) appdata = response[NR_APPDATA_HEADER] or raise NewRelic::Agent::CrossAppTracing::Error, "Can't derive metrics for response: no #{NR_APPDATA_HEADER} header!" key = cross_app_encoding_key() decoded_appdata = decode_with_key( key, appdata ) decoded_appdata.set_encoding( ::Encoding::UTF_8 ) if decoded_appdata.respond_to?( :set_encoding ) return NewRelic.json_load( decoded_appdata ) end |
.finish_trace(t0, segment, request, response) ⇒ Object
Finish tracing the HTTP request
that started at t0
with the information in response
and the given http
connection.
The request
must conform to the same interface described in the documentation for start_trace
.
The response
must respond to the following methods:
-
[](key) - Reads response headers.
-
to_hash - Converts response headers to a Hash
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
# File 'lib/new_relic/agent/cross_app_tracing.rb', line 90 def finish_trace( t0, segment, request, response ) t1 = Time.now duration = t1.to_f - t0.to_f begin if request # Figure out which metrics we need to report based on the request and response # The last (most-specific) one is scoped. metrics = metrics_for( request, response ) scoped_metric = metrics.pop stats_engine.record_metrics(metrics, duration) stats_engine.record_metrics(scoped_metric, duration, :scoped => true) # Add TT custom parameters segment.name = scoped_metric add_transaction_trace_parameters(request, response) end ensure # We always need to pop the scope stack to avoid an inconsistent # state, which will prevent tracing of the whole transaction. stats_engine.pop_scope( segment, scoped_metric, t1 ) end rescue NewRelic::Agent::CrossAppTracing::Error => err NewRelic::Agent.logger.debug "while cross app tracing", err rescue => err NewRelic::Agent.logger.error "Uncaught exception while finishing an HTTP request trace", err end |
.inject_request_headers(request) ⇒ Object
Inject the X-Process header into the outgoing request
.
137 138 139 140 141 142 143 144 145 146 147 148 149 |
# File 'lib/new_relic/agent/cross_app_tracing.rb', line 137 def inject_request_headers( request ) key = cross_app_encoding_key() cross_app_id = NewRelic::Agent.config[:cross_process_id] or raise NewRelic::Agent::CrossAppTracing::Error, "no cross app ID configured" txn_guid = NewRelic::Agent::TransactionState.get.request_guid txn_data = NewRelic.json_dump([ txn_guid, false ]) request[ NR_ID_HEADER ] = obfuscate_with_key( key, cross_app_id ) request[ NR_TXN_HEADER ] = obfuscate_with_key( key, txn_data ) rescue NewRelic::Agent::CrossAppTracing::Error => err NewRelic::Agent.logger.debug "Not injecting x-process header", err end |
.metrics_for(request, response) ⇒ Object
Return the set of metric names that correspond to the given request
and response
. response
may be nil in the case that the request produced an error without ever receiving an HTTP response.
173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 |
# File 'lib/new_relic/agent/cross_app_tracing.rb', line 173 def metrics_for( request, response ) metrics = common_metrics( request ) if response && response_is_crossapp?( response ) begin metrics.concat metrics_for_crossapp_response( request, response ) rescue => err # Fall back to regular metrics if there's a problem with x-process metrics NewRelic::Agent.logger.debug "%p while fetching x-process metrics: %s" % [ err.class, err. ] metrics.concat metrics_for_regular_request( request ) end else NewRelic::Agent.logger.debug "Response doesn't have CAT headers." metrics.concat metrics_for_regular_request( request ) end return metrics end |
.metrics_for_crossapp_response(request, response) ⇒ Object
Return the set of metric objects appropriate for the given cross app response
.
225 226 227 228 229 230 231 232 233 234 235 236 237 238 |
# File 'lib/new_relic/agent/cross_app_tracing.rb', line 225 def metrics_for_crossapp_response( request, response ) xp_id, txn_name, q_time, r_time, req_len, _ = extract_appdata( response ) check_crossapp_id( xp_id ) check_transaction_name( txn_name ) NewRelic::Agent.logger.debug "CAT xp_id: %p, txn_name: %p." % [ xp_id, txn_name ] metrics = [] metrics << "ExternalApp/#{request.host}/#{xp_id}/all" metrics << "ExternalTransaction/#{request.host}/#{xp_id}/#{txn_name}" return metrics end |
.metrics_for_regular_request(request) ⇒ Object
Return the set of metric objects appropriate for the given (non-cross app) request
.
268 269 270 271 272 273 |
# File 'lib/new_relic/agent/cross_app_tracing.rb', line 268 def metrics_for_regular_request( request ) metrics = [] metrics << "External/#{request.host}/#{request.type}/#{request.method}" return metrics end |
.response_is_crossapp?(response) ⇒ Boolean
Returns true
if Cross Application Tracing is enabled, and the given response
has the appropriate headers.
211 212 213 214 215 216 217 218 219 220 |
# File 'lib/new_relic/agent/cross_app_tracing.rb', line 211 def response_is_crossapp?( response ) return false unless cross_app_enabled? unless response[NR_APPDATA_HEADER] NewRelic::Agent.logger.debug "Response doesn't have the %p header: %p" % [ NR_APPDATA_HEADER, response.to_hash ] return false end return true end |
.start_trace(request) ⇒ Object
Set up the necessary state for cross-application tracing before the given request
goes out.
The request
object passed in must respond to the following methods:
-
type - Return a String describing the underlying library being used
to make the request (e.g. 'Net::HTTP' or 'Typhoeus')
-
host - Return a String with the hostname or IP of the host being
communicated with.
-
method - Return a String with the HTTP method name used for this request
-
[](key) - Lookup an HTTP request header by name
-
[]=(key, val) - Set an HTTP request header by name
-
uri - Full URI of the request
65 66 67 68 69 70 71 72 73 74 75 76 |
# File 'lib/new_relic/agent/cross_app_tracing.rb', line 65 def start_trace( request ) inject_request_headers( request ) if cross_app_enabled? # Create a segment and time the call t0 = Time.now segment = stats_engine.push_scope( :net_http, t0 ) return t0, segment rescue => err NewRelic::Agent.logger.error "Uncaught exception while tracing HTTP request", err return nil end |
.stats_engine ⇒ Object
Fetch a reference to the stats engine.
277 278 279 |
# File 'lib/new_relic/agent/cross_app_tracing.rb', line 277 def stats_engine NewRelic::Agent.instance.stats_engine end |
.trace_http_request(request) ⇒ Object
Send the given request
, adding metrics appropriate to the response when it comes back.
See the documentation for start_trace
for an explanation of what request
should look like.
38 39 40 41 42 43 44 45 46 47 48 49 |
# File 'lib/new_relic/agent/cross_app_tracing.rb', line 38 def trace_http_request( request ) return yield unless NewRelic::Agent.is_execution_traced? t0, segment = start_trace( request ) begin response = yield ensure finish_trace( t0, segment, request, response ) if t0 end return response end |