Module: OneApm::Agent::CrossAppTracing
- Defined in:
- lib/one_apm/agent/cross_app/cross_app_tracing.rb
Defined Under Namespace
Classes: Error
Constant Summary collapse
- OA_APPDATA_HEADER =
The cross app response header for “outgoing” calls
'X-BlueWare-App-Data'
- OA_ID_HEADER =
The cross app id header for “outgoing” calls
'X-BlueWare-ID'
- OA_TXN_HEADER =
The cross app transaction header for “outgoing” calls
'X-BlueWare-Transaction'
- OA_SYNTHETICS_HEADER =
The cross app synthetics header
'X-BlueWare-Synthetics'
- OA_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_cross_tier_id(id) ⇒ 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
Fetcher for the cross app encoding key.
- .cross_app_run_time_block(response, res_time) ⇒ Object
- .cross_application_tracer_enabled? ⇒ Boolean
- .external_rename_rules ⇒ Object
-
.extract_appdata(response) ⇒ Object
Extract x-process application data from the specified
response
and return it as an array of the form:. -
.finish_trace(state, t0, segment, request, response) ⇒ Object
Finish tracing the HTTP
request
that started att0
with the information inresponse
and the givenhttp
connection. -
.inject_request_headers(state, 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
. - .name(request) ⇒ Object
- .obfuscator ⇒ Object
-
.response_is_crossapp?(response) ⇒ Boolean
Returns
true
if Cross Application Tracing is enabled, and the givenresponse
has the appropriate headers. -
.start_trace(state, t0, 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.
-
.tl_trace_http_request(request) ⇒ Object
Send the given
request
, adding metrics appropriate to the response when it comes back. - .transaction_event_aggregator ⇒ Object
- .transaction_sampler ⇒ Object
- .valid_cross_process_id? ⇒ Boolean
- .valid_encoding_key? ⇒ Boolean
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.
223 224 225 226 227 |
# 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 |
.add_transaction_trace_parameters(request, response) ⇒ Object
213 214 215 216 217 218 219 |
# 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 |
.check_cross_tier_id(id) ⇒ Object
356 357 358 359 360 |
# 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_crossapp_id(id) ⇒ Object
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.
350 351 352 353 354 |
# 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_transaction_name(name) ⇒ Object
Check the given name
to ensure it conforms to the format of a valid transaction name.
365 366 367 368 |
# 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 |
.common_metrics(request) ⇒ Object
Return an Array of metrics used for every response.
256 257 258 259 260 261 262 263 264 265 266 267 |
# 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 |
.cross_app_enabled? ⇒ Boolean
Return true
if cross app tracing is enabled in the config.
159 160 161 162 163 |
# 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 |
.cross_app_encoding_key ⇒ Object
Fetcher for the cross app encoding key. Raises a OneApm::Agent::CrossAppTracing::Error if the key isn’t configured.
179 180 181 182 |
# 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 |
.cross_app_run_time_block(response, res_time) ⇒ Object
370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 |
# 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 |
.cross_application_tracer_enabled? ⇒ Boolean
173 174 175 |
# 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 |
.external_rename_rules ⇒ Object
344 345 346 |
# 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_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>
]
308 309 310 311 312 313 314 315 316 317 318 |
# 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_trace(state, 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
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 |
# 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_request_headers(state, request) ⇒ Object
Inject the X-Process header into the outgoing request
.
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 |
# 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 |
.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.
234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 |
# 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. ] metrics.concat metrics_for_regular_request( request ) end else 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
.
284 285 286 287 288 289 290 291 292 293 294 |
# 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 |
.metrics_for_regular_request(request) ⇒ Object
Return the set of metric objects appropriate for the given (non-cross app) request
.
323 324 325 326 327 328 |
# 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 |
.name(request) ⇒ Object
154 155 156 |
# File 'lib/one_apm/agent/cross_app/cross_app_tracing.rb', line 154 def name(request) external_rename_rules.rename(request) end |
.obfuscator ⇒ Object
184 185 186 |
# 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 |
.response_is_crossapp?(response) ⇒ Boolean
Returns true
if Cross Application Tracing is enabled, and the given response
has the appropriate headers.
272 273 274 275 276 277 278 279 |
# 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 |
.start_trace(state, t0, 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 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.
71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
# 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 |
.stats_engine ⇒ Object
Fetch a reference to the stats engine.
332 333 334 |
# File 'lib/one_apm/agent/cross_app/cross_app_tracing.rb', line 332 def stats_engine OneApm::Manager.agent.stats_engine end |
.tl_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 50 51 52 53 54 |
# 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 |
.transaction_event_aggregator ⇒ Object
340 341 342 |
# File 'lib/one_apm/agent/cross_app/cross_app_tracing.rb', line 340 def transaction_event_aggregator OneApm::Manager.agent.transaction_event_aggregator end |
.transaction_sampler ⇒ Object
336 337 338 |
# File 'lib/one_apm/agent/cross_app/cross_app_tracing.rb', line 336 def transaction_sampler OneApm::Manager.agent.transaction_sampler end |