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-BlueWare-ID'
ONEAPM_TXN_HEADER =
'X-BlueWare-Transaction'
ONEAPM_APPDATA_HEADER =
'X-BlueWare-App-Data'
ONEAPM_ID_HEADER_KEY =
'HTTP_X_BLUEWARE_ID'.freeze
ONEAPM_TXN_HEADER_KEY =
'HTTP_X_BLUEWARE_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



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

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



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

def clear_client_cross_app_id(state)
  state.client_cross_app_id = nil
end

#client_referring_transaction_guid(state) ⇒ Object



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

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



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

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



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

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



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

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



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

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

#cross_app_enabled?Boolean

Returns:

  • (Boolean)


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

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

#decoded_id(request) ⇒ Object



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

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



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

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

#insert_response_header(state, request_headers, response_headers) ⇒ Object



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

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



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

def on_finished_configuring(events)
  register_event_listeners(events)
end

#path_hash(txn_name, seed) ⇒ Object



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

def path_hash(txn_name, seed)
  rotated    = ((seed << 1) | (seed >> 31)) & 0xffffffff
  app_name   = OneApm::Manager.config.app_name
  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


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
54
# File 'lib/one_apm/agent/cross_app/cross_app_monitor.rb', line 29

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



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

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



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

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



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

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



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

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



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

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



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

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



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

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)


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

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