Class: NewRelic::Agent::CrossProcessMonitor
- Inherits:
-
Object
- Object
- NewRelic::Agent::CrossProcessMonitor
- Defined in:
- lib/new_relic/agent/cross_process_monitoring.rb
Constant Summary collapse
- THREAD_ID_KEY =
Because we aren’t in the right spot when our transaction actually starts, hold client_cross_process_id we get thread local until then.
:newrelic_client_cross_process_id
- NEWRELIC_ID_HEADER_KEYS =
%w{X-NewRelic-ID HTTP_X_NEWRELIC_ID X_NEWRELIC_ID}
- CONTENT_LENGTH_HEADER_KEYS =
%w{Content-Length HTTP_CONTENT_LENGTH CONTENT_LENGTH}
Instance Method Summary collapse
- #build_payload(timings, content_length) ⇒ Object
- #clear_client_cross_process_id ⇒ Object
- #client_cross_process_id ⇒ Object
- #content_length_from_request(request) ⇒ Object
- #decode_with_key(text) ⇒ Object
- #decoded_id(request) ⇒ Object
- #finish_setup(config) ⇒ Object
-
#initialize(events = nil) ⇒ CrossProcessMonitor
constructor
A new instance of CrossProcessMonitor.
- #insert_response_header(request_headers, response_headers) ⇒ Object
- #obfuscate_with_key(text) ⇒ Object
-
#register_event_listeners ⇒ Object
Expected sequence of events: :before_call will save our cross process request id to the thread :start_transaction will get called when a transaction starts up :after_call will write our response headers/metrics and clean up the thread.
- #save_client_cross_process_id(request_headers) ⇒ Object
- #set_error_custom_parameters(options) ⇒ Object
- #set_metrics(id, timings) ⇒ Object
- #set_response_headers(response_headers, timings, content_length) ⇒ Object
- #set_transaction_custom_parameters ⇒ Object
- #should_process_request(request_headers) ⇒ Object
-
#trusts?(request) ⇒ Boolean
Expects an ID of format “12#345”, and will only accept that!.
Constructor Details
#initialize(events = nil) ⇒ CrossProcessMonitor
Returns a new instance of CrossProcessMonitor.
8 9 10 11 12 13 14 15 16 17 18 |
# File 'lib/new_relic/agent/cross_process_monitoring.rb', line 8 def initialize(events = nil) # When we're starting up for real in the agent, we get passed the events # Other spots can pull from the agent, during startup the agent doesn't exist yet! events ||= Agent.instance.events @trusted_ids = [] events.subscribe(:finished_configuring) do finish_setup(Agent.config) register_event_listeners end end |
Instance Method Details
#build_payload(timings, content_length) ⇒ Object
101 102 103 104 105 106 107 108 109 |
# File 'lib/new_relic/agent/cross_process_monitoring.rb', line 101 def build_payload(timings, content_length) # FIXME The transaction name might not be properly encoded. use a json generator # For now we just handle quote characters by dropping them transaction_name = timings.transaction_name.gsub(/["']/, "") payload = %[["#{@cross_process_id}","#{transaction_name}",#{timings.queue_time_in_seconds},#{timings.app_time_in_seconds},#{content_length}] ] payload = obfuscate_with_key(payload) end |
#clear_client_cross_process_id ⇒ Object
62 63 64 |
# File 'lib/new_relic/agent/cross_process_monitoring.rb', line 62 def clear_client_cross_process_id NewRelic::Agent::AgentThread.current[THREAD_ID_KEY] = nil end |
#client_cross_process_id ⇒ Object
66 67 68 |
# File 'lib/new_relic/agent/cross_process_monitoring.rb', line 66 def client_cross_process_id NewRelic::Agent::AgentThread.current[THREAD_ID_KEY] end |
#content_length_from_request(request) ⇒ Object
144 145 146 |
# File 'lib/new_relic/agent/cross_process_monitoring.rb', line 144 def content_length_from_request(request) from_headers(request, CONTENT_LENGTH_HEADER_KEYS) || -1 end |
#decode_with_key(text) ⇒ Object
130 131 132 |
# File 'lib/new_relic/agent/cross_process_monitoring.rb', line 130 def decode_with_key(text) encode_with_key(Base64.decode64(text)) end |
#decoded_id(request) ⇒ Object
137 138 139 140 141 142 |
# File 'lib/new_relic/agent/cross_process_monitoring.rb', line 137 def decoded_id(request) encoded_id = from_headers(request, NEWRELIC_ID_HEADER_KEYS) return "" if encoded_id.nil? decode_with_key(encoded_id) end |
#finish_setup(config) ⇒ Object
20 21 22 23 24 25 |
# File 'lib/new_relic/agent/cross_process_monitoring.rb', line 20 def finish_setup(config) @cross_process_id = config[:cross_process_id] @encoding_key = config[:encoding_key] @encoding_bytes = get_bytes(@encoding_key) unless @encoding_key.nil? @trusted_ids = config[:trusted_account_ids] || [] end |
#insert_response_header(request_headers, response_headers) ⇒ Object
70 71 72 73 74 75 76 77 78 79 80 |
# File 'lib/new_relic/agent/cross_process_monitoring.rb', line 70 def insert_response_header(request_headers, response_headers) unless client_cross_process_id.nil? timings = NewRelic::Agent::BrowserMonitoring.timings content_length = content_length_from_request(request_headers) set_response_headers(response_headers, timings, content_length) set_metrics(client_cross_process_id, timings) clear_client_cross_process_id end end |
#obfuscate_with_key(text) ⇒ Object
126 127 128 |
# File 'lib/new_relic/agent/cross_process_monitoring.rb', line 126 def obfuscate_with_key(text) Base64.encode64(encode_with_key(text)).chomp end |
#register_event_listeners ⇒ Object
Expected sequence of events:
:before_call will save our cross process request id to the thread
:start_transaction will get called when a transaction starts up
:after_call will write our response headers/metrics and clean up the thread
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
# File 'lib/new_relic/agent/cross_process_monitoring.rb', line 31 def register_event_listeners NewRelic::Agent.logger.debug("Wiring up Cross Process monitoring to events after finished configuring") events = Agent.instance.events events.subscribe(:before_call) do |env| save_client_cross_process_id(env) end events.subscribe(:start_transaction) do |name| set_transaction_custom_parameters end events.subscribe(:after_call) do |env, (status_code, headers, body)| insert_response_header(env, headers) end events.subscribe(:notice_error) do |_, | set_error_custom_parameters() end end |
#save_client_cross_process_id(request_headers) ⇒ Object
56 57 58 59 60 |
# File 'lib/new_relic/agent/cross_process_monitoring.rb', line 56 def save_client_cross_process_id(request_headers) if should_process_request(request_headers) NewRelic::Agent::AgentThread.current[THREAD_ID_KEY] = decoded_id(request_headers) end end |
#set_error_custom_parameters(options) ⇒ Object
117 118 119 |
# File 'lib/new_relic/agent/cross_process_monitoring.rb', line 117 def set_error_custom_parameters() [:client_cross_process_id] = client_cross_process_id unless client_cross_process_id.nil? end |
#set_metrics(id, timings) ⇒ Object
121 122 123 124 |
# File 'lib/new_relic/agent/cross_process_monitoring.rb', line 121 def set_metrics(id, timings) metric = NewRelic::Agent.instance.stats_engine.get_stats_no_scope("ClientApplication/#{id}/all") metric.record_data_point(timings.app_time_in_seconds) end |
#set_response_headers(response_headers, timings, content_length) ⇒ Object
97 98 99 |
# File 'lib/new_relic/agent/cross_process_monitoring.rb', line 97 def set_response_headers(response_headers, timings, content_length) response_headers['X-NewRelic-App-Data'] = build_payload(timings, content_length) end |
#set_transaction_custom_parameters ⇒ Object
111 112 113 114 115 |
# File 'lib/new_relic/agent/cross_process_monitoring.rb', line 111 def set_transaction_custom_parameters # 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 NewRelic::Agent.add_custom_parameters(:client_cross_process_id => client_cross_process_id) unless client_cross_process_id.nil? end |
#should_process_request(request_headers) ⇒ Object
82 83 84 85 86 |
# File 'lib/new_relic/agent/cross_process_monitoring.rb', line 82 def should_process_request(request_headers) return Agent.config[:'cross_process.enabled'] && @cross_process_id && trusts?(request_headers) end |
#trusts?(request) ⇒ Boolean
Expects an ID of format “12#345”, and will only accept that!
89 90 91 92 93 94 95 |
# File 'lib/new_relic/agent/cross_process_monitoring.rb', line 89 def trusts?(request) id = decoded_id(request) split_id = id.match(/(\d+)#\d+/) return false if split_id.nil? @trusted_ids.include?(split_id.captures.first.to_i) end |