class ScoutApm::Instant::DevTraceResponseManipulator

Attributes

env[R]
rack_body[R]
rack_headers[R]
rack_response[R]
rack_status[R]

Public Class Methods

new(env, rack_response) click to toggle source
# File lib/scout_apm/instant/middleware.rb, line 71
def initialize(env, rack_response)
  @env = env
  @rack_response = rack_response

  @rack_status = rack_response[0]
  @rack_headers = rack_response[1]
  @rack_body = rack_response[2]
end

Public Instance Methods

adjust_ajax_header() click to toggle source
# File lib/scout_apm/instant/middleware.rb, line 133
def adjust_ajax_header
  rack_headers['X-scoutapminstant'] = payload
end
adjust_html_response() click to toggle source
# File lib/scout_apm/instant/middleware.rb, line 137
def adjust_html_response
  case true
  when older_rails_response? then adjust_older_rails_response
  when newer_rails_response? then adjust_newer_rails_response
  when rack_proxy_response? then  adjust_rack_proxy_response
  else
    # No action taken, we only adjust if we know exactly what we have.
  end
end
adjust_newer_rails_response() click to toggle source

Preserve the ActionDispatch::Response object we're working with

# File lib/scout_apm/instant/middleware.rb, line 169
def adjust_newer_rails_response
  logger.debug("DevTrace: in middleware, dev_trace is active, and response has a (newer) body. This appears to be an HTML page and an ActionDispatch::Response. Path=#{path}; ContentType=#{content_type}")
  @rack_body = [ html_manipulator.res ]
end
adjust_older_rails_response() click to toggle source
# File lib/scout_apm/instant/middleware.rb, line 163
def adjust_older_rails_response
  logger.debug("DevTrace: in middleware, dev_trace is active, and response has a (older) body. This appears to be an HTML page and an ActionDispatch::Response. Path=#{path}; ContentType=#{content_type}")
  rack_body.body = [ html_manipulator.res ]
end
adjust_rack_proxy_response() click to toggle source
# File lib/scout_apm/instant/middleware.rb, line 174
def adjust_rack_proxy_response
  logger.debug("DevTrace: in middleware, dev_trace is active, and response has a body. This appears to be an HTML page and an Rack::BodyProxy. Path=#{path}; ContentType=#{content_type}")
  @rack_body = [ html_manipulator.res ]
  @rack_headers.delete("Content-Length")
end
ajax_request?() click to toggle source
# File lib/scout_apm/instant/middleware.rb, line 197
def ajax_request?
  env['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest' || content_type.include?("application/json")
end
apm_host() click to toggle source
# File lib/scout_apm/instant/middleware.rb, line 225
def apm_host
  ScoutApm::Agent.instance.context.config.value("direct_host")
end
call() click to toggle source
# File lib/scout_apm/instant/middleware.rb, line 80
def call
  return rack_response unless preconditions_met?

  if ajax_request?
    ScoutApm::Agent.instance.context.logger.debug("DevTrace: in middleware, dev_trace is active, and response has a body. This is either AJAX or JSON. Path=#{path}; ContentType=#{content_type}")
    adjust_ajax_header
  else
    adjust_html_response
  end

  rebuild_rack_response
end
content_type() click to toggle source
# File lib/scout_apm/instant/middleware.rb, line 209
def content_type
  rack_headers['Content-Type']
end
dev_trace_disabled?() click to toggle source
# File lib/scout_apm/instant/middleware.rb, line 121
def dev_trace_disabled?
  ! ScoutApm::Agent.instance.context.config.value('dev_trace')
end
development_asset?() click to toggle source
# File lib/scout_apm/instant/middleware.rb, line 201
def development_asset?
  !rack_body.respond_to?(:body)
end
html_manipulator() click to toggle source
# File lib/scout_apm/instant/middleware.rb, line 180
def html_manipulator
  @html_manipulator ||=
    begin
      page = ScoutApm::Instant::Page.new(rack_body.body)

      # This monkey-patches XMLHttpRequest. It could possibly be part of the main scout_instant.js too. Putting it here so it runs as soon as possible.
      page.add_to_head(ScoutApm::Instant::Util.read_asset("xmlhttp_instrumentation.html"))

      # Add a link to CSS, then JS
      page.add_to_head("<link href='#{apm_host}/instant/scout_instant.css?cachebust=#{Time.now.to_i}' media='all' rel='stylesheet' />")
      page.add_to_body("<script src='#{apm_host}/instant/scout_instant.js?cachebust=#{Time.now.to_i}'></script>")
      page.add_to_body("<script>var scoutInstantPageTrace=#{payload};window.scoutInstant=window.scoutInstant('#{apm_host}', scoutInstantPageTrace)</script>")

      page
    end
end
logger() click to toggle source

APM Helpers & Shorthands #

# File lib/scout_apm/instant/middleware.rb, line 217
def logger
  ScoutApm::Agent.instance.context.logger
end
newer_rails_response?() click to toggle source
# File lib/scout_apm/instant/middleware.rb, line 153
def newer_rails_response?
  if defined?(ActionDispatch::Response::RackBody)
    return true if rack_body.is_a?(ActionDispatch::Response::RackBody)
  end
end
older_rails_response?() click to toggle source
# File lib/scout_apm/instant/middleware.rb, line 147
def older_rails_response?
  if defined?(ActionDispatch::Response)
    return true if rack_body.is_a?(ActionDispatch::Response)
  end
end
path() click to toggle source
# File lib/scout_apm/instant/middleware.rb, line 205
def path
  env['PATH_INFO']
end
payload() click to toggle source
# File lib/scout_apm/instant/middleware.rb, line 238
def payload
  @payload ||=
    begin
      metadata = {
        :app_root      => ScoutApm::Agent.instance.context.environment.root.to_s,
        :unique_id     => env['action_dispatch.request_id'], # note, this is a different unique_id than what "normal" payloads use
        :agent_version => ScoutApm::VERSION,
        :platform      => "ruby",
      }

      hash = ScoutApm::Serializers::PayloadSerializerToJson.
        rearrange_slow_transaction(trace).
        merge!(:metadata => metadata)
      ScoutApm::Serializers::PayloadSerializerToJson.jsonify_hash(hash)
    end
end
preconditions_met?() click to toggle source

Precondition checking #

# File lib/scout_apm/instant/middleware.rb, line 97
def preconditions_met?
  if dev_trace_disabled?
    # The line below is very noise as it is called on every request.
    # logger.debug("DevTrace: isn't activated via config. Try: SCOUT_DEV_TRACE=true rails server")
    return false
  end

  # Don't attempt to instrument assets.
  # Don't log this case, since it would be very noisy
  logger.debug("DevTrace: dev asset ignored") and return false if development_asset?

  # If we didn't have a tracked_request object, or we explicitly ignored
  # this request, don't do any work.
  logger.debug("DevTrace: no tracked request") and return false if tracked_request.nil? || tracked_request.ignoring_request?

  # If we didn't get a trace, we can't show a trace...
  if trace.nil?
    logger.debug("DevTrace: in middleware, dev_trace is active, and response has a body, but no trace was found. Path=#{path}; ContentType=#{content_type}")
    return false
  end

  true
end
rack_proxy_response?() click to toggle source
# File lib/scout_apm/instant/middleware.rb, line 159
def rack_proxy_response?
  rack_body.is_a?(::Rack::BodyProxy)
end
rebuild_rack_response() click to toggle source

Response Injection #

# File lib/scout_apm/instant/middleware.rb, line 129
def rebuild_rack_response
  [rack_status, rack_headers, rack_body]
end
trace() click to toggle source
# File lib/scout_apm/instant/middleware.rb, line 229
def trace
  @trace ||=
    begin
      layer_finder = LayerConverters::FindLayerByType.new(tracked_request)
      converter = LayerConverters::SlowRequestConverter.new(ScoutApm::Agent.instance.context, tracked_request, layer_finder, ScoutApm::FakeStore.new)
      converter.call
    end
end
tracked_request() click to toggle source
# File lib/scout_apm/instant/middleware.rb, line 221
def tracked_request
  @tracked_request ||= ScoutApm::RequestManager.lookup
end