Class: OneApm::Agent::CrossAppMonitor

Inherits:
InboundRequestMonitor show all
Defined in:
lib/one_apm/agent/cross_app/cross_app_monitor.rb

Constant Summary collapse

ONEAPM_ID_HEADER =
'X-OneApm-ID'
ONEAPM_TXN_HEADER =
'X-OneApm-Transaction'
ONEAPM_APPDATA_HEADER =
'X-OneApm-App-Data'
ONEAPM_ID_HEADER_KEY =
'HTTP_X_ONEAPM_ID'.freeze
ONEAPM_TXN_HEADER_KEY =
'HTTP_X_ONEAPM_TRANSACTION'.freeze
OA_CONTENT_LENGTH_HEADER_KEY =
'HTTP_CONTENT_LENGTH'.freeze

Instance Attribute Summary

Attributes inherited from InboundRequestMonitor

#obfuscator

Instance Method Summary collapse

Methods inherited from InboundRequestMonitor

#deserialize_header, #initialize, #setup_obfuscator

Constructor Details

This class inherits a constructor from OneApm::Agent::InboundRequestMonitor

Instance Method Details

#build_payload(state, timings, content_length) ⇒ Object



126
127
128
129
130
131
132
133
134
135
136
# File 'lib/one_apm/agent/cross_app/cross_app_monitor.rb', line 126

def build_payload(state, timings, content_length)
  payload = [
    OneApm::Manager.config[:cross_process_id],
    timings.transaction_name,
    timings.queue_time_in_seconds.to_f,
    timings.app_time_in_seconds.to_f,
    content_length,
    state.request_guid
  ]
  payload = obfuscator.obfuscate(OneApm::JSONWrapper.dump(payload))
end

#clear_client_cross_app_id(state) ⇒ Object



59
60
61
# File 'lib/one_apm/agent/cross_app/cross_app_monitor.rb', line 59

def clear_client_cross_app_id(state)
  state.client_cross_app_id = nil
end

#client_referring_transaction_guid(state) ⇒ Object



69
70
71
72
# File 'lib/one_apm/agent/cross_app/cross_app_monitor.rb', line 69

def client_referring_transaction_guid(state)
  info = state.referring_transaction_info or return nil
  return info[0]
end

#client_referring_transaction_path_hash(state) ⇒ Object



84
85
86
87
# File 'lib/one_apm/agent/cross_app/cross_app_monitor.rb', line 84

def client_referring_transaction_path_hash(state)
  info = state.referring_transaction_info or return nil
  return info[3].is_a?(String) && info[3]
end

#client_referring_transaction_record_flag(state) ⇒ Object



74
75
76
77
# File 'lib/one_apm/agent/cross_app/cross_app_monitor.rb', line 74

def client_referring_transaction_record_flag(state)
  info = state.referring_transaction_info or return nil
  return info[1]
end

#client_referring_transaction_trip_id(state) ⇒ Object



79
80
81
82
# File 'lib/one_apm/agent/cross_app/cross_app_monitor.rb', line 79

def client_referring_transaction_trip_id(state)
  info = state.referring_transaction_info or return nil
  return info[2].is_a?(String) && info[2]
end

#content_length_from_request(request) ⇒ Object



165
166
167
# File 'lib/one_apm/agent/cross_app/cross_app_monitor.rb', line 165

def content_length_from_request(request)
  request[OA_CONTENT_LENGTH_HEADER_KEY] || -1
end

#cross_app_enabled?Boolean

Returns:

  • (Boolean)


109
110
111
# File 'lib/one_apm/agent/cross_app/cross_app_monitor.rb', line 109

def cross_app_enabled?
  OneApm::Agent::CrossAppTracing.cross_app_enabled?
end

#decoded_id(request) ⇒ Object



158
159
160
161
162
163
# File 'lib/one_apm/agent/cross_app/cross_app_monitor.rb', line 158

def decoded_id(request)
  encoded_id = request[ONEAPM_ID_HEADER_KEY]
  return "" if encoded_id.nil?

  obfuscator.deobfuscate(encoded_id)
end

#hash_transaction_name(identifier) ⇒ Object



169
170
171
# File 'lib/one_apm/agent/cross_app/cross_app_monitor.rb', line 169

def hash_transaction_name(identifier)
  Digest::MD5.digest(identifier).unpack("@12N").first & 0xffffffff
end

#insert_response_header(state, request_headers, response_headers) ⇒ Object



89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/one_apm/agent/cross_app/cross_app_monitor.rb', line 89

def insert_response_header(state, request_headers, response_headers)
  unless state.client_cross_app_id.nil?
    txn = state.current_transaction
    unless txn.nil?
      txn.freeze_name_and_execute_if_not_ignored do
        timings = state.timings
        content_length = content_length_from_request(request_headers)

        set_response_headers(state, response_headers, timings, content_length)
        set_metrics(state.client_cross_app_id, timings)
      end
    end
    clear_client_cross_app_id(state)
  end
end

#on_finished_configuring(events) ⇒ Object



21
22
23
# File 'lib/one_apm/agent/cross_app/cross_app_monitor.rb', line 21

def on_finished_configuring(events)
  register_event_listeners(events)
end

#path_hash(txn_name, seed) ⇒ Object



173
174
175
176
177
178
# File 'lib/one_apm/agent/cross_app/cross_app_monitor.rb', line 173

def path_hash(txn_name, seed)
  rotated    = ((seed << 1) | (seed >> 31)) & 0xffffffff
  app_name   = OneApm::Manager.config.app_names.first
  identifier = "#{app_name};#{txn_name}"
  sprintf("%08x", rotated ^ hash_transaction_name(identifier))
end

#register_event_listeners(events) ⇒ Object

Expected sequence of events:

:before_call will save our cross application request id to the thread
:after_call will write our response headers/metrics and clean up the thread


28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/one_apm/agent/cross_app/cross_app_monitor.rb', line 28

def register_event_listeners(events)
  OneApm::Manager.logger.
    debug("Wiring up Cross Application Tracing to events after finished configuring")

  events.subscribe(:before_call) do |env|
    if should_process_request(env)
      state = OneApm::TransactionState.tl_get

      save_client_cross_app_id(state, env)
      save_referring_transaction_info(state, env)
      set_transaction_custom_parameters(state)
    end
  end

  events.subscribe(:after_call) do |env, (_status_code, headers, _body)|
    state = OneApm::TransactionState.tl_get

    insert_response_header(state, env, headers)
  end

  events.subscribe(:notice_error) do |_, options|
    state = OneApm::TransactionState.tl_get

    set_error_custom_parameters(state, options)
  end
end

#save_client_cross_app_id(state, request_headers) ⇒ Object



55
56
57
# File 'lib/one_apm/agent/cross_app/cross_app_monitor.rb', line 55

def save_client_cross_app_id(state, request_headers)
  state.client_cross_app_id = decoded_id(request_headers)
end

#save_referring_transaction_info(state, request_headers) ⇒ Object



63
64
65
66
67
# File 'lib/one_apm/agent/cross_app/cross_app_monitor.rb', line 63

def save_referring_transaction_info(state, request_headers)
  txn_header = request_headers[ONEAPM_TXN_HEADER_KEY] or return
  txn_info = deserialize_header(txn_header, ONEAPM_TXN_HEADER)
  state.referring_transaction_info = txn_info
end

#set_error_custom_parameters(state, options) ⇒ Object



149
150
151
# File 'lib/one_apm/agent/cross_app/cross_app_monitor.rb', line 149

def set_error_custom_parameters(state, options)
  options[:client_cross_process_id] = state.client_cross_app_id if state.client_cross_app_id
end

#set_metrics(id, timings) ⇒ Object



153
154
155
156
# File 'lib/one_apm/agent/cross_app/cross_app_monitor.rb', line 153

def set_metrics(id, timings)
  metric_name = "ClientApplication/#{id}/all"
  OneApm::Manager.record_metric(metric_name, timings.app_time_in_seconds)
end

#set_response_headers(state, response_headers, timings, content_length) ⇒ Object



122
123
124
# File 'lib/one_apm/agent/cross_app/cross_app_monitor.rb', line 122

def set_response_headers(state, response_headers, timings, content_length)
  response_headers[ONEAPM_APPDATA_HEADER] = build_payload(state, timings, content_length)
end

#set_transaction_custom_parameters(state) ⇒ Object



138
139
140
141
142
143
144
145
146
147
# File 'lib/one_apm/agent/cross_app/cross_app_monitor.rb', line 138

def set_transaction_custom_parameters(state)
  # We expect to get the before call to set the id (if we have it) before
  # this, and then write our custom parameter when the transaction starts
  OneApm::Manager.add_custom_parameters(:client_cross_process_id => state.client_cross_app_id) if state.client_cross_app_id

  referring_guid = client_referring_transaction_guid(state)
  if referring_guid
    OneApm::Manager.add_custom_parameters(:referring_transaction_guid => referring_guid)
  end
end

#should_process_request(request_headers) ⇒ Object



105
106
107
# File 'lib/one_apm/agent/cross_app/cross_app_monitor.rb', line 105

def should_process_request(request_headers)
  return cross_app_enabled? && trusts?(request_headers)
end

#trusts?(request) ⇒ Boolean

Expects an ID of format “12#345”, and will only accept that!

Returns:

  • (Boolean)


114
115
116
117
118
119
120
# File 'lib/one_apm/agent/cross_app/cross_app_monitor.rb', line 114

def trusts?(request)
  id = decoded_id(request)
  split_id = id.match(/(\d+)#\d+/)
  return false if split_id.nil?

  OneApm::Manager.config[:trusted_account_ids].include?(split_id.captures.first.to_i)
end