module OneApm::Agent::CrossAppTracing
Constants
- OA_APPDATA_HEADER
The cross app response header for “outgoing” calls
- OA_APPDATA_TXN_GUID_INDEX
The index of the transaction GUID in the appdata header of responses
- OA_ID_HEADER
The cross app id header for “outgoing” calls
- OA_SYNTHETICS_HEADER
The cross app synthetics header
- OA_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/one_apm/agent/cross_app/cross_app_tracing.rb, line 223 def add_cat_transaction_trace_parameters( response ) appdata = extract_appdata( response ) transaction_sampler.add_segment_parameters( \ :transaction_guid => appdata[OA_APPDATA_TXN_GUID_INDEX] ) end
# File lib/one_apm/agent/cross_app/cross_app_tracing.rb, line 213 def add_transaction_trace_parameters(request, response) filtered_uri = ::OneApm::Support::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
# File lib/one_apm/agent/cross_app/cross_app_tracing.rb, line 356 def check_cross_tier_id( id ) id =~ /\A\d+#\d+#\d+\z/ or raise OneApm::Agent::CrossAppTracing::Error, "malformed cross application ID %p" % [ id ] end
Check the given id
to ensure it conforms to the format of a cross-application ID. Raises an OneApm::Agent::CrossAppTracing::Error
if it doesn't.
# File lib/one_apm/agent/cross_app/cross_app_tracing.rb, line 350 def check_crossapp_id( id ) id =~ /\A\d+#\d+\z/ or raise OneApm::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/one_apm/agent/cross_app/cross_app_tracing.rb, line 365 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/one_apm/agent/cross_app/cross_app_tracing.rb, line 256 def common_metrics( request ) metrics = [ "External/all" ] metrics << "External/#{request.host}/all" if OneApm::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/one_apm/agent/cross_app/cross_app_tracing.rb, line 159 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 OneApm::Agent::CrossAppTracing::Error
if the key isn't configured.
# File lib/one_apm/agent/cross_app/cross_app_tracing.rb, line 179 def cross_app_encoding_key OneApm::Manager.config[:encoding_key] or raise OneApm::Agent::CrossAppTracing::Error, "No encoding_key set." end
# File lib/one_apm/agent/cross_app/cross_app_tracing.rb, line 370 def cross_app_run_time_block response, res_time run_time_block = nil if response && response_is_crossapp?( response ) xp_id, txn_name, _q_time, run_time, _req_len, _ = extract_appdata( response ) run_time_block = Proc.new do |stats| stats.call_count += 1 stats.total_call_time += res_time stats.total_exclusive_time += run_time stats.min_call_time = res_time stats.max_call_time = res_time stats.sum_of_squares += res_time**2 stats end end run_time_block end
# File lib/one_apm/agent/cross_app/cross_app_tracing.rb, line 173 def cross_application_tracer_enabled? OneApm::Manager.config[:"cross_application_tracer.enabled"] end
# File lib/one_apm/agent/cross_app/cross_app_tracing.rb, line 344 def external_rename_rules OneApm::Manager.agent.external_rename_rules 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/one_apm/agent/cross_app/cross_app_tracing.rb, line 308 def extract_appdata( response ) appdata = response[OA_APPDATA_HEADER] or raise OneApm::Agent::CrossAppTracing::Error, "Can't derive metrics for response: no #{OA_APPDATA_HEADER} header!" decoded_appdata = obfuscator.deobfuscate( appdata ) decoded_appdata.set_encoding( ::Encoding::UTF_8 ) if decoded_appdata.respond_to?( :set_encoding ) return OneApm::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/one_apm/agent/cross_app/cross_app_tracing.rb, line 98 def finish_trace(state, t0, segment, request, response) unless t0 OneApm::Manager.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, nil, &cross_app_run_time_block(response, duration)) # If we don't have segment, something failed during start_trace so # the current segment isn't the HTTP call it should have been. if segment segment.name = scoped_metric add_transaction_trace_parameters(request, response) end payload = { :name => name(request) , :scope => state.current_transaction.frozen_name, :apdex_perf_zone => 'S', :guid => OneApm::Helper.generate_guid, :duration => duration, :referring_transaction_guid => state.current_transaction.guid, :call_count => 1, :start_timestamp => t0.to_f, :request_url => request.uri, :bytes_sent => 0, :bytes_rec => 0, :has_trace => 0, :exec_time => duration, :call_type => 0 } transaction_event_aggregator.on_cross_app_transaction_finished(payload) end ensure # If we have a segment, always pop the traced method stack to avoid # an inconsistent state, which prevents tracing of whole transaction. if segment stack = state.traced_method_stack stack.pop_frame(state, segment, scoped_metric, t1) end end rescue OneApm::Agent::CrossAppTracing::Error => err OneApm::Manager.logger.debug "while cross app tracing", err rescue => err OneApm::Manager.logger.error "Uncaught exception while finishing an HTTP request trace", err end
Inject the X-Process header into the outgoing request
.
# File lib/one_apm/agent/cross_app/cross_app_tracing.rb, line 189 def inject_request_headers(state, request) cross_app_id = OneApm::Manager.config[:cross_process_id] or raise OneApm::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) request_host = request.host if request && request.respond_to?(:host) if txn.raw_synthetics_header request[OA_SYNTHETICS_HEADER] = txn.raw_synthetics_header end end txn_data = OneApm::JSONWrapper.dump([txn_guid, true, trip_id, path_hash, request_host]) request[OA_ID_HEADER] = obfuscator.obfuscate(cross_app_id) request[OA_TXN_HEADER] = obfuscator.obfuscate(txn_data) rescue OneApm::Agent::CrossAppTracing::Error => err OneApm::Manager.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/one_apm/agent/cross_app/cross_app_tracing.rb, line 234 def metrics_for( request, response ) metrics = common_metrics( request ) if response && response_is_crossapp?( response ) begin metrics.delete_if{|m| m == "External/#{request.host}/all"} metrics.concat metrics_for_crossapp_response( request, response ) rescue => err # Fall back to regular metrics if there's a problem with x-process metrics OneApm::Manager.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/one_apm/agent/cross_app/cross_app_tracing.rb, line 284 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/one_apm/agent/cross_app/cross_app_tracing.rb, line 323 def metrics_for_regular_request( request ) metrics = [] metrics << "External/#{request.host}/#{request.type}/#{request.method}" return metrics end
# File lib/one_apm/agent/cross_app/cross_app_tracing.rb, line 154 def name(request) external_rename_rules.rename(request) end
# File lib/one_apm/agent/cross_app/cross_app_tracing.rb, line 184 def obfuscator @obfuscator ||= OneApm::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/one_apm/agent/cross_app/cross_app_tracing.rb, line 272 def response_is_crossapp?( response ) return false unless cross_app_enabled? unless response[OA_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 beingcommunicated 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 segment if it was sucessfully pushed.
# File lib/one_apm/agent/cross_app/cross_app_tracing.rb, line 71 def start_trace(state, t0, request) inject_request_headers(state, request) if cross_app_enabled? stack = state.traced_method_stack segment = stack.push_frame(state, :http_request, t0) return segment rescue => err OneApm::Manager.logger.error "Uncaught exception while tracing HTTP request", err return nil rescue Exception => e OneApm::Manager.logger.debug "Unexpected exception raised while tracing HTTP request", e raise e end
Fetch a reference to the stats engine.
# File lib/one_apm/agent/cross_app/cross_app_tracing.rb, line 332 def stats_engine OneApm::Manager.agent.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/one_apm/agent/cross_app/cross_app_tracing.rb, line 38 def tl_trace_http_request(request) state = OneApm::TransactionState.tl_get return yield unless state.is_execution_traced? return yield if request['_SKIP_CAT_'] # 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 segment = start_trace(state, t0, request) response = yield ensure finish_trace(state, t0, segment, request, response) end return response end
# File lib/one_apm/agent/cross_app/cross_app_tracing.rb, line 340 def transaction_event_aggregator OneApm::Manager.agent.transaction_event_aggregator end
# File lib/one_apm/agent/cross_app/cross_app_tracing.rb, line 336 def transaction_sampler OneApm::Manager.agent.transaction_sampler end
# File lib/one_apm/agent/cross_app/cross_app_tracing.rb, line 165 def valid_cross_process_id? OneApm::Manager.config[:cross_process_id] && OneApm::Manager.config[:cross_process_id].length > 0 end
# File lib/one_apm/agent/cross_app/cross_app_tracing.rb, line 169 def valid_encoding_key? OneApm::Manager.config[:encoding_key] && OneApm::Manager.config[:encoding_key].length > 0 end