class NewRelic::Rack::BrowserMonitoring
This middleware is used by the agent for the Real user monitoring (RUM) feature, and will usually be automatically injected in the middleware chain. If automatic injection is not working, you may manually use it in your middleware chain instead.
@api public
Constants
- ALREADY_INSTRUMENTED_KEY
- ATTACHMENT
- BODY_START
- CHARSET_RE
- CONTENT_DISPOSITION
- CONTENT_TYPE
- GT
- HEAD_START
- SCAN_LIMIT
The maximum number of bytes of the response body that we will examine in order to look for a RUM insertion point.
- TEXT_HTML
- X_UA_COMPATIBLE_RE
Public Instance Methods
autoinstrument_source(response, headers, js_to_inject)
click to toggle source
# File lib/new_relic/rack/browser_monitoring.rb, line 77 def autoinstrument_source(response, headers, js_to_inject) source = gather_source(response) close_old_response(response) return nil unless source # Only scan the first 50k (roughly) then give up. beginning_of_source = source[0..SCAN_LIMIT] if body_start = find_body_start(beginning_of_source) meta_tag_positions = [ find_x_ua_compatible_position(beginning_of_source), find_charset_position(beginning_of_source) ].compact if !meta_tag_positions.empty? insertion_index = meta_tag_positions.max else insertion_index = find_end_of_head_open(beginning_of_source) || body_start end if insertion_index source = source[0...insertion_index] << js_to_inject << source[insertion_index..-1] else NewRelic::Agent.logger.debug "Skipping RUM instrumentation. Could not properly determine location to inject script." end else msg = "Skipping RUM instrumentation. Unable to find <body> tag in first #{SCAN_LIMIT} bytes of document." NewRelic::Agent.logger.log_once(:warn, :rum_insertion_failure, msg) NewRelic::Agent.logger.debug(msg) end source rescue => e NewRelic::Agent.logger.debug "Skipping RUM instrumentation on exception.", e nil end
close_old_response(response)
click to toggle source
Per “The Response > The Body” section of Rack spec, we should close if our response is able. rack.rubyforge.org/doc/SPEC.html
# File lib/new_relic/rack/browser_monitoring.rb, line 124 def close_old_response(response) if response.respond_to?(:close) response.close end end
find_body_start(beginning_of_source)
click to toggle source
# File lib/new_relic/rack/browser_monitoring.rb, line 130 def find_body_start(beginning_of_source) beginning_of_source.index(BODY_START) end
find_charset_position(beginning_of_source)
click to toggle source
# File lib/new_relic/rack/browser_monitoring.rb, line 139 def find_charset_position(beginning_of_source) match = CHARSET_RE.match(beginning_of_source) match.end(0) if match end
find_end_of_head_open(beginning_of_source)
click to toggle source
# File lib/new_relic/rack/browser_monitoring.rb, line 144 def find_end_of_head_open(beginning_of_source) head_open = beginning_of_source.index(HEAD_START) beginning_of_source.index(GT, head_open) + 1 if head_open end
find_x_ua_compatible_position(beginning_of_source)
click to toggle source
# File lib/new_relic/rack/browser_monitoring.rb, line 134 def find_x_ua_compatible_position(beginning_of_source) match = X_UA_COMPATIBLE_RE.match(beginning_of_source) match.end(0) if match end
gather_source(response)
click to toggle source
# File lib/new_relic/rack/browser_monitoring.rb, line 116 def gather_source(response) source = nil response.each {|fragment| source ? (source << fragment.to_s) : (source = fragment.to_s)} source end
is_attachment?(headers)
click to toggle source
# File lib/new_relic/rack/browser_monitoring.rb, line 64 def is_attachment?(headers) headers[CONTENT_DISPOSITION] && headers[CONTENT_DISPOSITION].include?(ATTACHMENT) end
is_html?(headers)
click to toggle source
# File lib/new_relic/rack/browser_monitoring.rb, line 60 def is_html?(headers) headers[CONTENT_TYPE] && headers[CONTENT_TYPE].include?(TEXT_HTML) end
is_streaming?(env)
click to toggle source
# File lib/new_relic/rack/browser_monitoring.rb, line 68 def is_streaming?(env) return false unless defined?(ActionController::Live) env['action_controller.instance'].class.included_modules.include?(ActionController::Live) end
should_instrument?(env, status, headers)
click to toggle source
# File lib/new_relic/rack/browser_monitoring.rb, line 51 def should_instrument?(env, status, headers) NewRelic::Agent.config[:'browser_monitoring.auto_instrument'] && status == 200 && !env[ALREADY_INSTRUMENTED_KEY] && is_html?(headers) && !is_attachment?(headers) && !is_streaming?(env) end
traced_call(env)
click to toggle source
# File lib/new_relic/rack/browser_monitoring.rb, line 30 def traced_call(env) result = @app.call(env) # [status, headers, response] js_to_inject = NewRelic::Agent.browser_timing_header if (js_to_inject != "") && should_instrument?(env, result[0], result[1]) response_string = autoinstrument_source(result[2], result[1], js_to_inject) env[ALREADY_INSTRUMENTED_KEY] = true if response_string response = Rack::Response.new(response_string, result[0], result[1]) response.finish else result end else result end end