Class: TingYun::BrowserMonitoring

Inherits:
AgentMiddleware show all
Defined in:
lib/ting_yun/middleware/browser_monitoring.rb

Constant Summary collapse

CONTENT_TYPE =
'Content-Type'.freeze
TEXT_HTML =
'text/html'.freeze
CONTENT_DISPOSITION =
'Content-Disposition'.freeze
ATTACHMENT =
'attachment'.freeze
SCAN_LIMIT =
64_000
TITLE_END =
'</title>'.freeze
TITLE_END_CAPITAL =
'</TITLE>'.freeze
HEAD_END =
'<head>'.freeze
HEAD_END_CAPITAL =
'<HEAD>'.freeze
GT =
'>'.freeze
ALREADY_INSTRUMENTED_KEY =
"tingyun.browser_monitoring_already_instrumented"

Constants included from Instrumentation::MiddlewareTracing

Instrumentation::MiddlewareTracing::TXN_STARTED_KEY

Instance Attribute Summary

Attributes inherited from AgentMiddleware

#category, #target, #transaction_options

Instance Method Summary collapse

Methods inherited from AgentMiddleware

#build_transaction_name, #initialize

Methods included from Instrumentation::MiddlewareTracing

#_nr_has_middleware_tracing, #build_transaction_options, #call, #capture_http_response_code, #capture_response_content_type, #events, #merge_first_middleware_options, #note_transaction_started, #sinatra_static?

Constructor Details

This class inherits a constructor from TingYun::AgentMiddleware

Instance Method Details

#auto_instrument_source(response, js_to_inject) ⇒ Object



68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/ting_yun/middleware/browser_monitoring.rb', line 68

def auto_instrument_source(response, js_to_inject)
  source = gather_source(response)
  close_old_response(response)
  return nil unless source

  beginning_of_source = source[0..SCAN_LIMIT]
  insertion_index = find_tag_end(beginning_of_source)

  if insertion_index
    source = source[0...insertion_index] <<
        js_to_inject <<
        source[insertion_index..-1]
  else
    TingYun::Agent.logger.debug "Skipping RUM instrumentation. Could not properly determine location to inject script."
  end

  source
rescue => e
  TingYun::Agent.logger.debug "Skipping RUM instrumentation on exception.", e
  nil
end

#close_old_response(response) ⇒ Object



96
97
98
99
100
# File 'lib/ting_yun/middleware/browser_monitoring.rb', line 96

def close_old_response(response)
  if response.respond_to?(:close)
    response.close
  end
end

#find_tag_end(beginning_of_source) ⇒ Object



102
103
104
105
106
107
108
109
# File 'lib/ting_yun/middleware/browser_monitoring.rb', line 102

def find_tag_end(beginning_of_source)
  tag_end = beginning_of_source.index(TITLE_END) ||
      beginning_of_source.index(HEAD_END) ||
      beginning_of_source.index(TITLE_END_CAPITAL) ||
      beginning_of_source.index(HEAD_END_CAPITAL)

  beginning_of_source.index(GT, tag_end) + 1 if tag_end
end

#gather_source(response) ⇒ Object



90
91
92
93
94
# File 'lib/ting_yun/middleware/browser_monitoring.rb', line 90

def gather_source(response)
  source = nil
  response.each {|fragment| source ? (source << fragment.to_s) : (source = fragment.to_s)}
  source
end

#is_ajax?(env) ⇒ Boolean

Returns:

  • (Boolean)


60
61
62
# File 'lib/ting_yun/middleware/browser_monitoring.rb', line 60

def is_ajax?(env)
  env["HTTP_X_REQUESTED_WITH"].nil?
end

#is_attachment?(headers) ⇒ Boolean

Returns:

  • (Boolean)


64
65
66
# File 'lib/ting_yun/middleware/browser_monitoring.rb', line 64

def is_attachment?(headers)
  headers[CONTENT_DISPOSITION] && headers[CONTENT_DISPOSITION].include?(ATTACHMENT)
end

#is_html?(headers) ⇒ Boolean

Returns:

  • (Boolean)


56
57
58
# File 'lib/ting_yun/middleware/browser_monitoring.rb', line 56

def is_html?(headers)
  headers[CONTENT_TYPE] && headers[CONTENT_TYPE].include?(TEXT_HTML)
end

#should_instrument?(env, status, headers) ⇒ Boolean

Returns:

  • (Boolean)


48
49
50
51
52
53
54
# File 'lib/ting_yun/middleware/browser_monitoring.rb', line 48

def should_instrument?(env, status, headers)
  status == 200 &&
      is_ajax?(env) &&
      !env[ALREADY_INSTRUMENTED_KEY] &&
      is_html?(headers) &&
      !is_attachment?(headers)
end

#traced_call(env) ⇒ Object



26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/ting_yun/middleware/browser_monitoring.rb', line 26

def traced_call(env)
  result = @app.call(env)   # [status, headers, response]

  js_to_inject = TingYun::Instrumentation::Support::JavascriptInstrument.browser_timing_header

  if (js_to_inject != '') && should_instrument?(env, result[0], result[1])
    response_string = auto_instrument_source(result[2], 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