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. -
.inject_request_headers(request) ⇒ Object
Inject the X-Process header into the outgoing
request
. -
.metrics_for(http, request, response) ⇒ Object
Return the set of metric names 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.
252 253 254 255 256 |
# File 'lib/new_relic/agent/cross_app_tracing.rb', line 252 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.
261 262 263 264 |
# File 'lib/new_relic/agent/cross_app_tracing.rb', line 261 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.
163 164 165 166 167 168 169 170 171 172 173 174 |
# File 'lib/new_relic/agent/cross_app_tracing.rb', line 163 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.
97 98 99 100 101 |
# File 'lib/new_relic/agent/cross_app_tracing.rb', line 97 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.
106 107 108 109 |
# File 'lib/new_relic/agent/cross_app_tracing.rb', line 106 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>
]
220 221 222 223 224 225 226 227 228 229 230 231 |
# File 'lib/new_relic/agent/cross_app_tracing.rb', line 220 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.
130 131 132 133 134 135 136 |
# File 'lib/new_relic/agent/cross_app_tracing.rb', line 130 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.
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 93 |
# File 'lib/new_relic/agent/cross_app_tracing.rb', line 66 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 stats_engine.record_metrics(metrics, duration) stats_engine.record_metrics(scoped_metric, duration, :scoped => true) # 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 |
.inject_request_headers(request) ⇒ Object
Inject the X-Process header into the outgoing request
.
113 114 115 116 117 118 119 120 121 122 123 124 125 |
# File 'lib/new_relic/agent/cross_app_tracing.rb', line 113 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 metric names that correspond to the given request
and response
.
141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 |
# File 'lib/new_relic/agent/cross_app_tracing.rb', line 141 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
.
193 194 195 196 197 198 199 200 201 202 203 204 205 206 |
# File 'lib/new_relic/agent/cross_app_tracing.rb', line 193 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
.
236 237 238 239 240 241 |
# File 'lib/new_relic/agent/cross_app_tracing.rb', line 236 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.
179 180 181 182 183 184 185 186 187 188 |
# File 'lib/new_relic/agent/cross_app_tracing.rb', line 179 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.
50 51 52 53 54 55 56 57 58 59 60 61 |
# File 'lib/new_relic/agent/cross_app_tracing.rb', line 50 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.
245 246 247 |
# File 'lib/new_relic/agent/cross_app_tracing.rb', line 245 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.
34 35 36 37 38 39 40 41 42 43 44 45 |
# File 'lib/new_relic/agent/cross_app_tracing.rb', line 34 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 |