Class: ScoutApm::Instant::DevTraceResponseManipulator

Inherits:
Object
  • Object
show all
Defined in:
lib/scout_apm/instant/middleware.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(env, rack_response) ⇒ DevTraceResponseManipulator

Returns a new instance of DevTraceResponseManipulator.



69
70
71
72
73
74
75
76
# File 'lib/scout_apm/instant/middleware.rb', line 69

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

Instance Attribute Details

#envObject (readonly)

Returns the value of attribute env.



67
68
69
# File 'lib/scout_apm/instant/middleware.rb', line 67

def env
  @env
end

#rack_bodyObject (readonly)

Returns the value of attribute rack_body.



66
67
68
# File 'lib/scout_apm/instant/middleware.rb', line 66

def rack_body
  @rack_body
end

#rack_headersObject (readonly)

Returns the value of attribute rack_headers.



66
67
68
# File 'lib/scout_apm/instant/middleware.rb', line 66

def rack_headers
  @rack_headers
end

#rack_responseObject (readonly)

Returns the value of attribute rack_response.



65
66
67
# File 'lib/scout_apm/instant/middleware.rb', line 65

def rack_response
  @rack_response
end

#rack_statusObject (readonly)

Returns the value of attribute rack_status.



66
67
68
# File 'lib/scout_apm/instant/middleware.rb', line 66

def rack_status
  @rack_status
end

Instance Method Details

#adjust_ajax_headerObject



131
132
133
# File 'lib/scout_apm/instant/middleware.rb', line 131

def adjust_ajax_header
  rack_headers['X-scoutapminstant'] = payload
end

#adjust_html_responseObject



135
136
137
138
139
140
141
142
143
# File 'lib/scout_apm/instant/middleware.rb', line 135

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_responseObject

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



167
168
169
170
# File 'lib/scout_apm/instant/middleware.rb', line 167

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_responseObject



161
162
163
164
# File 'lib/scout_apm/instant/middleware.rb', line 161

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_responseObject



172
173
174
175
176
# File 'lib/scout_apm/instant/middleware.rb', line 172

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?Boolean

Returns:

  • (Boolean)


195
196
197
# File 'lib/scout_apm/instant/middleware.rb', line 195

def ajax_request?
  env['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest' || content_type.include?("application/json")
end

#apm_hostObject



223
224
225
# File 'lib/scout_apm/instant/middleware.rb', line 223

def apm_host
  ScoutApm::Agent.instance.context.config.value("direct_host")
end

#callObject



78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/scout_apm/instant/middleware.rb', line 78

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_typeObject



207
208
209
# File 'lib/scout_apm/instant/middleware.rb', line 207

def content_type
  rack_headers['Content-Type']
end

#dev_trace_disabled?Boolean

Returns:

  • (Boolean)


119
120
121
# File 'lib/scout_apm/instant/middleware.rb', line 119

def dev_trace_disabled?
  ! ScoutApm::Agent.instance.context.config.value('dev_trace')
end

#development_asset?Boolean

Returns:

  • (Boolean)


199
200
201
# File 'lib/scout_apm/instant/middleware.rb', line 199

def development_asset?
  !rack_body.respond_to?(:body)
end

#html_manipulatorObject



178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
# File 'lib/scout_apm/instant/middleware.rb', line 178

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

#loggerObject

APM Helpers & Shorthands #



215
216
217
# File 'lib/scout_apm/instant/middleware.rb', line 215

def logger
  ScoutApm::Agent.instance.context.logger
end

#newer_rails_response?Boolean

Returns:

  • (Boolean)


151
152
153
154
155
# File 'lib/scout_apm/instant/middleware.rb', line 151

def newer_rails_response?
  if defined?(ActionDispatch::Response::RackBody)
    return true if rack_body.is_a?(ActionDispatch::Response::RackBody)
  end
end

#older_rails_response?Boolean

Returns:

  • (Boolean)


145
146
147
148
149
# File 'lib/scout_apm/instant/middleware.rb', line 145

def older_rails_response?
  if defined?(ActionDispatch::Response)
    return true if rack_body.is_a?(ActionDispatch::Response)
  end
end

#pathObject



203
204
205
# File 'lib/scout_apm/instant/middleware.rb', line 203

def path
  env['PATH_INFO']
end

#payloadObject



236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
# File 'lib/scout_apm/instant/middleware.rb', line 236

def payload
  @payload ||=
    begin
       = {
        :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 => )
      ScoutApm::Serializers::PayloadSerializerToJson.jsonify_hash(hash)
    end
end

#preconditions_met?Boolean

Precondition checking #

Returns:

  • (Boolean)


95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/scout_apm/instant/middleware.rb', line 95

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?Boolean

Returns:

  • (Boolean)


157
158
159
# File 'lib/scout_apm/instant/middleware.rb', line 157

def rack_proxy_response?
  rack_body.is_a?(::Rack::BodyProxy)
end

#rebuild_rack_responseObject

Response Injection #



127
128
129
# File 'lib/scout_apm/instant/middleware.rb', line 127

def rebuild_rack_response
  [rack_status, rack_headers, rack_body]
end

#traceObject



227
228
229
230
231
232
233
234
# File 'lib/scout_apm/instant/middleware.rb', line 227

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_requestObject



219
220
221
# File 'lib/scout_apm/instant/middleware.rb', line 219

def tracked_request
  @tracked_request ||= ScoutApm::RequestManager.lookup
end