module Mmtrix::Agent::CrossAppTracing
Constants
- APPDATA_TXN_GUID_INDEX
The index of the transaction GUID in the appdata header of responses
- NR_APPDATA_HEADER
The cross app response header for “outgoing” calls
- NR_ID_HEADER
The cross app id header for “outgoing” calls
- NR_SYNTHETICS_HEADER
The cross app synthetics header
- NR_TXN_HEADER
The cross app transaction header for “outgoing” calls
Public Instance Methods
Extract any custom parameters from response
if it’s cross-application and add them to the current TT node.
# File lib/mmtrix/agent/cross_app_tracing.rb, line 203 def add_cat_transaction_trace_parameters( response ) appdata = extract_appdata( response ) transaction_sampler.add_node_parameters( \ :transaction_guid => appdata[APPDATA_TXN_GUID_INDEX] ) end
# File lib/mmtrix/agent/cross_app_tracing.rb, line 192 def add_transaction_trace_parameters(request, response) filtered_uri = ::Mmtrix::Agent::HTTPClients::URIUtil.filter_uri(request.uri) transaction_sampler.add_node_parameters(:uri => filtered_uri) if response && response_is_crossapp?(response) add_cat_transaction_trace_parameters(response) end end
Check the given id
to ensure it conforms to the format of a cross-application ID. Raises an Mmtrix::Agent::CrossAppTracing::Error
if it doesn’t.
# File lib/mmtrix/agent/cross_app_tracing.rb, line 322 def check_crossapp_id( id ) id =~ /\A\d+#\d+\z/ or raise Mmtrix::Agent::CrossAppTracing::Error, "malformed cross application ID %p" % [ id ] end
Check the given name
to ensure it conforms to the format of a valid transaction name.
# File lib/mmtrix/agent/cross_app_tracing.rb, line 331 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
Return an Array of metrics used for every response.
# File lib/mmtrix/agent/cross_app_tracing.rb, line 235 def common_metrics( request ) metrics = [ "External/all" ] metrics << "External/#{request.host}/all" if Mmtrix::Agent::Transaction.recording_web_transaction? metrics << "External/allWeb" else metrics << "External/allOther" end return metrics end
Return true
if cross app tracing is enabled in the config.
# File lib/mmtrix/agent/cross_app_tracing.rb, line 138 def cross_app_enabled? valid_cross_process_id? && valid_encoding_key? && cross_application_tracer_enabled? end
Fetcher for the cross app encoding key. Raises a Mmtrix::Agent::CrossAppTracing::Error
if the key isn’t configured.
# File lib/mmtrix/agent/cross_app_tracing.rb, line 158 def cross_app_encoding_key Mmtrix::Agent.config[:encoding_key] or raise Mmtrix::Agent::CrossAppTracing::Error, "No encoding_key set." end
# File lib/mmtrix/agent/cross_app_tracing.rb, line 152 def cross_application_tracer_enabled? Mmtrix::Agent.config[:"cross_application_tracer.enabled"] || Mmtrix::Agent.config[:cross_application_tracing] end
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> ]
# File lib/mmtrix/agent/cross_app_tracing.rb, line 288 def extract_appdata( response ) appdata = response[NR_APPDATA_HEADER] or raise Mmtrix::Agent::CrossAppTracing::Error, "Can't derive metrics for response: no #{NR_APPDATA_HEADER} header!" decoded_appdata = obfuscator.deobfuscate( appdata ) decoded_appdata.set_encoding( ::Encoding::UTF_8 ) if decoded_appdata.respond_to?( :set_encoding ) return Mmtrix::JSONWrapper.load( decoded_appdata ) end
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
# File lib/mmtrix/agent/cross_app_tracing.rb, line 97 def finish_trace(state, t0, node, request, response) unless t0 Mmtrix::Agent.logger.error("HTTP request trace finished without start time. This is probably an agent bug.") return end 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_scoped_and_unscoped_metrics( state, scoped_metric, metrics, duration) # If we don't have node, something failed during start_trace so # the current node isn't the HTTP call it should have been. if node node.name = scoped_metric add_transaction_trace_parameters(request, response) end end ensure # If we have a node, always pop the traced method stack to avoid # an inconsistent state, which prevents tracing of whole transaction. if node stack = state.traced_method_stack stack.pop_frame(state, node, scoped_metric, t1) end end rescue Mmtrix::Agent::CrossAppTracing::Error => err Mmtrix::Agent.logger.debug "while cross app tracing", err rescue => err Mmtrix::Agent.logger.error "Uncaught exception while finishing an HTTP request trace", err end
Inject the X-Process header into the outgoing request
.
# File lib/mmtrix/agent/cross_app_tracing.rb, line 168 def inject_request_headers(state, request) cross_app_id = Mmtrix::Agent.config[:cross_process_id] or raise Mmtrix::Agent::CrossAppTracing::Error, "no cross app ID configured" state.is_cross_app_caller = true txn_guid = state.request_guid txn = state.current_transaction if txn trip_id = txn.cat_trip_id(state) path_hash = txn.cat_path_hash(state) if txn.raw_synthetics_header request[NR_SYNTHETICS_HEADER] = txn.raw_synthetics_header end end txn_data = Mmtrix::JSONWrapper.dump([txn_guid, false, trip_id, path_hash]) request[NR_ID_HEADER] = obfuscator.obfuscate(cross_app_id) request[NR_TXN_HEADER] = obfuscator.obfuscate(txn_data) rescue Mmtrix::Agent::CrossAppTracing::Error => err Mmtrix::Agent.logger.debug "Not injecting x-process header", err end
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.
# File lib/mmtrix/agent/cross_app_tracing.rb, line 214 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 Mmtrix::Agent.logger.debug "%p while fetching x-process metrics: %s" % [ err.class, err.message ] metrics.concat metrics_for_regular_request( request ) end else metrics.concat metrics_for_regular_request( request ) end return metrics end
Return the set of metric objects appropriate for the given cross app response
.
# File lib/mmtrix/agent/cross_app_tracing.rb, line 263 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 ) metrics = [] metrics << "ExternalApp/#{request.host}/#{xp_id}/all" metrics << "ExternalTransaction/#{request.host}/#{xp_id}/#{txn_name}" return metrics end
Return the set of metric objects appropriate for the given (non-cross app) request
.
# File lib/mmtrix/agent/cross_app_tracing.rb, line 303 def metrics_for_regular_request( request ) metrics = [] metrics << "External/#{request.host}/#{request.type}/#{request.method}" return metrics end
# File lib/mmtrix/agent/cross_app_tracing.rb, line 163 def obfuscator @obfuscator ||= Mmtrix::Agent::Obfuscator.new(cross_app_encoding_key) end
Returns true
if Cross Application Tracing is enabled, and the given response
has the appropriate headers.
# File lib/mmtrix/agent/cross_app_tracing.rb, line 251 def response_is_crossapp?( response ) return false unless cross_app_enabled? unless response[NR_APPDATA_HEADER] return false end return true end
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 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
This method returns the transaction node if it was sucessfully pushed.
# File lib/mmtrix/agent/cross_app_tracing.rb, line 70 def start_trace(state, t0, request) inject_request_headers(state, request) if cross_app_enabled? stack = state.traced_method_stack node = stack.push_frame(state, :http_request, t0) return node rescue => err Mmtrix::Agent.logger.error "Uncaught exception while tracing HTTP request", err return nil rescue Exception => e Mmtrix::Agent.logger.debug "Unexpected exception raised while tracing HTTP request", e raise e end
Fetch a reference to the stats engine.
# File lib/mmtrix/agent/cross_app_tracing.rb, line 312 def stats_engine Mmtrix::Agent.instance.stats_engine end
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.
# File lib/mmtrix/agent/cross_app_tracing.rb, line 38 def tl_trace_http_request(request) state = Mmtrix::Agent::TransactionState.tl_get return yield unless state.is_execution_traced? # It's important to set t0 outside the ensured block, otherwise there's # a race condition if we raise after begin but before t0's set. t0 = Time.now begin node = start_trace(state, t0, request) response = yield ensure finish_trace(state, t0, node, request, response) end return response end
# File lib/mmtrix/agent/cross_app_tracing.rb, line 316 def transaction_sampler Mmtrix::Agent.instance.transaction_sampler end
# File lib/mmtrix/agent/cross_app_tracing.rb, line 144 def valid_cross_process_id? Mmtrix::Agent.config[:cross_process_id] && Mmtrix::Agent.config[:cross_process_id].length > 0 end
# File lib/mmtrix/agent/cross_app_tracing.rb, line 148 def valid_encoding_key? Mmtrix::Agent.config[:encoding_key] && Mmtrix::Agent.config[:encoding_key].length > 0 end