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
-
.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(http) ⇒ 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:. -
.extract_custom_parameters(response) ⇒ Object
Extract any custom parameters from
response
if it’s cross-application and add them to the current TT node. -
.finish_trace(t0, segment, request, response, http) ⇒ Object
Finish tracing the HTTP
request
that started att0
with the information inresponse
and the givenhttp
connection. -
.get_metric(metric_name) ⇒ Object
Convenience function for fetching the metric associated with
metric_name
. -
.get_scoped_metric(metric_name) ⇒ Object
Convenience function for fetching the scoped metric associated with
metric_name
. -
.inject_request_headers(request) ⇒ Object
Inject the X-Process header into the outgoing
request
. -
.metrics_for(http, request, response) ⇒ Object
Return the set of metrics (NewRelic::MethodTraceStats objects) that correspond to the given
request
andresponse
. -
.metrics_for_crossapp_response(http, response) ⇒ Object
Return the set of metric objects appropriate for the given cross app
response
. -
.metrics_for_regular_response(http, request, response) ⇒ Object
Return the set of metric objects appropriate for the given (non-cross app)
response
. -
.response_is_crossapp?(response) ⇒ Boolean
Returns
true
if Cross Application Tracing is enabled, and the givenresponse
has the appropriate headers. -
.start_trace(http, request) ⇒ Object
Set up the necessary state for cross-application tracing before the given
request
goes out on the specifiedhttp
connection. -
.stats_engine ⇒ Object
Fetch a reference to the stats engine.
-
.trace_http_request(http, request) ⇒ Object
Send the given
request
, adding metrics appropriate to the response when it comes back.
Methods included from NewRelic::Agent::CrossAppMonitor::EncodingFunctions
decode_with_key, encode_with_key, obfuscate_with_key
Class Method Details
.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.
264 265 266 267 268 |
# File 'lib/new_relic/agent/cross_app_tracing.rb', line 264 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.
273 274 275 276 |
# File 'lib/new_relic/agent/cross_app_tracing.rb', line 273 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(http) ⇒ Object
Return an Array of metrics used for every response.
162 163 164 165 166 167 168 169 170 171 172 173 |
# File 'lib/new_relic/agent/cross_app_tracing.rb', line 162 def common_metrics( http ) metrics = [ "External/all" ] metrics << "External/#{http.address}/all" if NewRelic::Agent::Instrumentation::MetricFrame.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.
96 97 98 99 100 |
# File 'lib/new_relic/agent/cross_app_tracing.rb', line 96 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.
105 106 107 108 |
# File 'lib/new_relic/agent/cross_app_tracing.rb', line 105 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>
]
219 220 221 222 223 224 225 226 227 228 229 230 |
# File 'lib/new_relic/agent/cross_app_tracing.rb', line 219 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 |
.extract_custom_parameters(response) ⇒ Object
Extract any custom parameters from response
if it’s cross-application and add them to the current TT node.
129 130 131 132 133 134 135 |
# File 'lib/new_relic/agent/cross_app_tracing.rb', line 129 def extract_custom_parameters( response ) appdata = extract_appdata( response ) sampler = NewRelic::Agent.instance.transaction_sampler sampler.add_segment_parameters( :transaction_guid => appdata[APPDATA_TXN_GUID_INDEX] ) end |
.finish_trace(t0, segment, request, response, http) ⇒ Object
Finish tracing the HTTP request
that started at t0
with the information in response
and the given http
connection.
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
# File 'lib/new_relic/agent/cross_app_tracing.rb', line 64 def finish_trace( t0, segment, request, response, http ) t1 = Time.now duration = t1.to_f - t0.to_f begin if request && response && http # Figure out which metrics we need to report based on the request and response # The last (most-specific) one is scoped. metrics = metrics_for( http, request, response ) scoped_metric = metrics.pop # Report the metrics metrics.each { |metric| get_metric(metric).trace_call(duration) } get_scoped_metric( scoped_metric ).trace_call( duration ) # Add TT custom parameters stats_engine.rename_scope_segment( scoped_metric ) extract_custom_parameters( response ) if response_is_crossapp?( 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, duration, 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 |
.get_metric(metric_name) ⇒ Object
Convenience function for fetching the metric associated with metric_name
.
244 245 246 |
# File 'lib/new_relic/agent/cross_app_tracing.rb', line 244 def get_metric( metric_name ) stats_engine.get_stats_no_scope( metric_name ) end |
.get_scoped_metric(metric_name) ⇒ Object
Convenience function for fetching the scoped metric associated with metric_name
.
250 251 252 253 |
# File 'lib/new_relic/agent/cross_app_tracing.rb', line 250 def get_scoped_metric( metric_name ) # Default is to use the metric_name itself as the scope, which is what we want stats_engine.get_stats( metric_name ) end |
.inject_request_headers(request) ⇒ Object
Inject the X-Process header into the outgoing request
.
112 113 114 115 116 117 118 119 120 121 122 123 124 |
# File 'lib/new_relic/agent/cross_app_tracing.rb', line 112 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::TransactionInfo.get.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(http, request, response) ⇒ Object
Return the set of metrics (NewRelic::MethodTraceStats objects) that correspond to the given request
and response
.
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 |
# File 'lib/new_relic/agent/cross_app_tracing.rb', line 140 def metrics_for( http, request, response ) metrics = common_metrics( http ) if response_is_crossapp?( response ) begin metrics.concat metrics_for_crossapp_response( http, 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_response( http, request, response ) end else NewRelic::Agent.logger.debug "Response doesn't have CAT headers." metrics.concat metrics_for_regular_response( http, request, response ) end return metrics end |
.metrics_for_crossapp_response(http, response) ⇒ Object
Return the set of metric objects appropriate for the given cross app response
.
192 193 194 195 196 197 198 199 200 201 202 203 204 205 |
# File 'lib/new_relic/agent/cross_app_tracing.rb', line 192 def metrics_for_crossapp_response( http, 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/#{http.address}/#{xp_id}/all" metrics << "ExternalTransaction/#{http.address}/#{xp_id}/#{txn_name}" return metrics end |
.metrics_for_regular_response(http, request, response) ⇒ Object
Return the set of metric objects appropriate for the given (non-cross app) response
.
235 236 237 238 239 240 |
# File 'lib/new_relic/agent/cross_app_tracing.rb', line 235 def metrics_for_regular_response( http, request, response ) metrics = [] metrics << "External/#{http.address}/Net::HTTP/#{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.
178 179 180 181 182 183 184 185 186 187 |
# File 'lib/new_relic/agent/cross_app_tracing.rb', line 178 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(http, request) ⇒ Object
Set up the necessary state for cross-application tracing before the given request
goes out on the specified http
connection.
48 49 50 51 52 53 54 55 56 57 58 59 |
# File 'lib/new_relic/agent/cross_app_tracing.rb', line 48 def start_trace( http, request ) inject_request_headers( request ) if cross_app_enabled? # Create a segment and time the call t0 = Time.now segment = stats_engine.push_scope( "External/#{http.address}/all", 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.
257 258 259 |
# File 'lib/new_relic/agent/cross_app_tracing.rb', line 257 def stats_engine NewRelic::Agent.instance.stats_engine end |
.trace_http_request(http, request) ⇒ Object
Send the given request
, adding metrics appropriate to the response when it comes back.
32 33 34 35 36 37 38 39 40 41 42 43 |
# File 'lib/new_relic/agent/cross_app_tracing.rb', line 32 def trace_http_request( http, request ) return yield unless NewRelic::Agent.is_execution_traced? t0, segment = start_trace( http, request ) begin response = yield ensure finish_trace( t0, segment, request, response, http ) if t0 end return response end |