# encoding: utf-8
require 'rack'
require 'rack/request'
require 'rack/response'
require 'rack/file'
require 'one_apm/support/collection_helper'
require 'one_apm/rack/middleware_base'
require 'one_apm/rack/middleware_wrapper'
require 'one_apm/transaction/transaction_sample'
require 'one_apm/transaction/transaction_analysis'
require 'one_apm/rack/developer_mode/helper'
require 'one_apm/collector/support/shell_poller'
module OneApm
module Rack
class DeveloperMode < MiddlewareBase
OA_VIEW_PATH = File.expand_path('../developer_mode/views/', __FILE__)
include OneApm::DeveloperModeHelper
def traced_call(env)
@req = ::Rack::Request.new(env)
@start_memory_used = current_memory_used
return dup._call(env) if /^\/oneapm/ =~ @req.path_info
set_profiled
status, headers, body = @app.call(env)
if status == 200 && headers['Content-Type'] =~ /text\/html/
result = inject_profiler(env, status, headers, body)
return result if result
end
return [status, headers, body]
end
def self.profiled?
@profiled
end
def self.profiled=(profiled)
@profiled = profiled
end
protected
def _call(env)
OneApm::Manager.ignore_transaction
@rendered = false
case @req.path_info
when /assets/
::Rack::File.new(OA_VIEW_PATH).call(env)
when /index/
index
when /threads/
threads
when /reset/
reset
when /show_sample_summary/
show_sample_data
when /show_sample_detail/
show_sample_data
when /show_sample_sql/
show_sample_data
when /show_sample_profile/
show_sample_profile
when /explain_sql/
explain_sql
when /^\/oneapm\/?$/
index
else
@app.call(env)
end
end
private
def set_profiled
DeveloperMode.profiled = params['p'] == 'on'
end
def request_path
@request_path ||= begin
path = @req.path.gsub('/', '-')
path.slice!(0)
path
end
end
def inject_profiler(env, status, headers, body)
# mini profiler is meddling with stuff, we can not cache cause we will get incorrect data
# Rack::ETag has already inserted some nonesense in the chain
source = gather_source(body)
close_old_response(body)
return nil unless source
response_string = get_and_inject_profile(source)
return nil unless response_string
response = ::Rack::Response.new(response_string, status, headers)
response.finish
end
def get_and_inject_profile source
inject_page!(source, stylesheets, :postion => :head)
inject_page!(source, javascripts(source))
inject_page!(source, slide_templates)
source
end
def javascripts body
script = ""
script_files = ["javascript/jquery.min.js", "javascript/layer.js", "javascript/functions.js" ]
script_files.shift if body =~ /jquery(.min)?/ rescue false
script_files.each do |sf|
script << "\r\n"
end
script
end
def stylesheets
stylesheets = []
stylesheets << ""
stylesheets << ""
stylesheets.join("\r\n")
end
def slide_templates
template = read_script_file "#{OA_VIEW_PATH}/oneapm/slide.tmpl"
samples = ""
duration = current_duration
memory_used = current_memory_used - @start_memory_used
samples = "#{(duration * 1000).round(2)} ms"
template.gsub(/\{samples\}/, samples)
end
def get_transaction_samples
return get_samples if last_sample_id <= 0
index = get_samples.find_index{|sample|sample.sample_id == last_sample_id}
index.nil? ? get_samples : get_samples[0...index]
end
def current_sample_guid
current_transaction = OneApm::TransactionState.tl_get.current_transaction
current_transaction.guid
end
def last_sample_id
Thread.current[:last_sample_id].to_i
end
def last_sample_id= sample_id
Thread.current[:last_sample_id] = sample_id.to_i
end
def inject_page! body, script, options = {:postion => :body}
inject!(body, script, options)
end
def read_script_file file_path
IO.read(::File.expand_path(file_path, ::File.dirname(__FILE__)))
end
def inject!(fragment, script, options = {})
if options[:postion] == :head && fragment.match(/<\/head>/i)
regex = /<\/head>/i
close_tag = ''
elsif options[:postion] == :body && fragment.match(/<\/body>/i)
regex = /<\/body>/i
close_tag = '