All Files (91.46% covered at 96.64 hits/line)
47 files in total.
1546 relevant lines.
1414 lines covered and
132 lines missed
-
1
module Insight
-
1
class << self
-
1
def enable
-
165
Thread.current["insight.enabled"] = true
-
end
-
-
1
def disable
-
Thread.current["insight.enabled"] = false
-
end
-
-
1
def enabled?
-
6
Thread.current["insight.enabled"] == true
-
end
-
end
-
end
-
-
1
require 'insight/app'
-
1
require "rack"
-
1
require "digest/sha1"
-
1
require "insight/filtered_backtrace"
-
1
require "insight/options"
-
1
require "insight/logger"
-
1
require "insight/panel"
-
1
require "insight/panel_app"
-
1
require "insight/params_signature"
-
1
require "insight/rack_static_bug_avoider"
-
1
require "insight/redirect_interceptor"
-
1
require "insight/render"
-
1
require "insight/toolbar"
-
1
require "insight/enable-button"
-
1
require "insight/path-filter"
-
1
require 'insight/logger'
-
1
require 'insight/request-recorder'
-
1
require 'insight/instrumentation/setup'
-
1
require 'insight/panels-content'
-
1
require 'insight/panels-header'
-
-
1
module Insight
-
1
class App
-
1
include Options
-
1
INSIGHT_ROOT = "/__insight__"
-
1
INSIGHT_REGEX = %r{^#{INSIGHT_ROOT}}
-
-
1
VERSION = "0.4.4"
-
-
1
class SecurityError < StandardError
-
end
-
-
1
def initialize(app, options = {}, &block)
-
1
initialize_options options
-
1
@base_app = app
-
1
@panels = []
-
1
instance_eval(&block) if block_given?
-
-
1
@logger = Logger.new(read_option(:log_level), read_option(:log_path))
-
1
Thread.current['insight.logger'] = @logger
-
1
build_normal_stack
-
1
build_debug_stack
-
1
if options[:on_initialize]
-
1
options[:on_initialize].call(self)
-
end
-
end
-
1
attr_reader :logger
-
1
attr_accessor :panels
-
-
1
def call(env)
-
86
@original_request = Rack::Request.new(env)
-
86
if insight_active?
-
80
@env = env
-
80
self.options = @default_options
-
-
80
env['insight.logger'] = @logger
-
80
Thread.current['insight.logger'] = @logger
-
-
80
Insight.enable
-
80
env["insight.panels"] = []
-
80
@debug_stack.call(env)
-
else
-
6
@normal_stack.call(env)
-
end
-
end
-
-
-
1
def reset(new_options=nil)
-
85
@env = nil
-
85
initialize_options(new_options)
-
-
85
Insight::Instrumentation::ClassProbe::all_probes.each do |probe|
-
144
probe.clear_collectors
-
end
-
85
Insight::Instrumentation::InstanceProbe::all_probes.each do |probe|
-
291
probe.clear_collectors
-
end
-
85
Insight::Instrumentation::PackageDefinition.clear_collectors
-
-
85
build_debug_stack
-
end
-
-
1
private
-
-
1
def insight_active?
-
86
return (toolbar_requested? && ip_authorized? && password_authorized?)
-
end
-
-
1
def build_normal_stack
-
1
builder = Rack::Builder.new
-
1
builder.use EnableButton, self
-
1
builder.run Rack::Cascade.new([ asset_mapped(Rack::Builder.new), @base_app ])
-
1
@normal_stack = builder.to_app
-
end
-
-
1
def build_debug_stack
-
86
@panels.clear
-
86
builder = Rack::Builder.new
-
86
builder.use Toolbar, self
-
86
builder.run Rack::Cascade.new([panel_mappings, shortcut_stack(@base_app), collection_stack(@base_app)])
-
-
86
@debug_stack = builder.to_app
-
end
-
-
1
def panel_mappings
-
86
classes = read_option(:panel_classes)
-
86
root = INSIGHT_ROOT
-
86
insight = self
-
86
builder = Rack::Builder.new do
-
86
classes.each do |panel_class|
-
590
panel_class.panel_mappings.each do |path, app|
-
76
map [root, path].join("/") do
-
76
run app
-
end
-
end
-
end
-
86
map root + "/panels_content" do
-
86
run PanelsContent.new(insight)
-
end
-
86
map root + "/panels_header" do
-
86
run PanelsHeader.new(insight)
-
end
-
end
-
86
return asset_mapped(builder)
-
end
-
-
1
def shortcut_stack(app)
-
86
Rack::Builder.app do
-
86
use PathFilter
-
86
run app
-
end
-
end
-
-
1
def collection_stack(app)
-
86
classes = read_option(:panel_classes)
-
86
insight_id = self.object_id
-
86
panels = self.panels
-
-
#Builder makes it impossible to access the panels
-
-
86
app = Instrumentation::Setup.new(app)
-
86
app = RedirectInterceptor.new(app)
-
#Reversed? Does it matter?
-
86
app = classes.inject(app) do |app, panel_class|
-
590
panel = panel_class.new(app)
-
590
panels << panel
-
590
panel
-
end
-
86
app = RequestRecorder.new(app)
-
86
return app
-
end
-
-
1
def asset_mapped(builder)
-
87
path = public_path
-
87
builder.map INSIGHT_ROOT do
-
87
run Rack::File.new(path)
-
end
-
87
builder.to_app
-
end
-
-
1
def public_path
-
87
::File.expand_path("../../insight/public/__insight__", __FILE__)
-
end
-
-
1
def toolbar_requested?
-
86
@original_request.cookies["insight_enabled"]
-
end
-
-
1
def ip_authorized?
-
84
return true unless options["insight.ip_masks"]
-
-
168
logger.debug{ "Checking #{@original_request.ip} against ip_masks" }
-
84
ip = IPAddr.new(@original_request.ip)
-
-
84
mask = options["insight.ip_masks"].find do |ip_mask|
-
84
ip_mask.include?(ip)
-
end
-
84
if mask
-
164
logger.debug{ "Matched #{mask}" }
-
82
return true
-
else
-
4
logger.debug{ "Matched no masks" }
-
2
return false
-
end
-
end
-
-
1
def password_authorized?
-
82
return true unless options["insight.password"]
-
-
6
logger.debug{"Checking password"}
-
-
3
expected_sha = Digest::SHA1.hexdigest ["insight", options["insight.password"]].join(":")
-
3
actual_sha = @original_request.cookies["insight_password"]
-
-
6
logger.debug{"Password result: #{actual_sha == expected_sha}"}
-
3
actual_sha == expected_sha
-
end
-
end
-
end
-
1
require 'insight'
-
1
require 'sqlite3'
-
1
require 'base64'
-
-
1
module Insight
-
1
class Database
-
1
module RequestDataClient
-
1
def key_sql_template(sql)
-
1
@key_sql_template = sql
-
end
-
-
1
def table_setup(name, *keys)
-
520
@table = DataTable.new(name, *keys)
-
520
if keys.empty?
-
518
@key_sql_template = ""
-
end
-
end
-
-
1
def store(env, *keys_and_value)
-
369
return if env.nil?
-
369
request_id = env["insight.request-id"]
-
369
return if request_id.nil?
-
-
369
value = keys_and_value[-1]
-
369
keys = keys_and_value[0...-1]
-
-
369
@table.store(request_id, value, @key_sql_template % keys)
-
end
-
-
1
def retrieve(request_id)
-
665
@table.for_request(request_id)
-
end
-
1
alias retreive retrieve #JDL cannot spell
-
-
1
def table_length
-
1
@table.length
-
end
-
end
-
-
1
class << self
-
1
include Logging
-
-
1
def database_path=(value)
-
86
@database_path = value || "insight.sqlite"
-
end
-
-
1
def database_path
-
85
@database_path
-
end
-
-
1
def db
-
2775
@db ||= open_database
-
end
-
-
1
def reset
-
85
@db = nil
-
end
-
-
1
def open_database
-
85
@db = SQLite3::Database.new(database_path)
-
85
@db.execute("pragma foreign_keys = on")
-
85
@db
-
rescue Object => ex
-
msg = "Issue while loading SQLite DB:" + [ex.class, ex.message, ex.backtrace[0..4]].inspect
-
logger.error{ msg }
-
-
return {}
-
end
-
-
1
if defined?(PhusionPassenger)
-
PhusionPassenger.on_event(:starting_worker_process) do |forked|
-
Insight::Database::open_database if forked
-
end
-
end
-
end
-
-
1
class Table
-
1
include Logging
-
-
1
def db
-
2775
Insight::Database.db
-
end
-
-
1
def create_keys_clause
-
340
"#{@keys.map{|key| "#{key} varchar"}.join(", ")}"
-
end
-
-
1
def create_sql
-
596
"create table #@table_name ( id integer primary key, #{create_keys_clause} )"
-
end
-
-
1
def execute(*args)
-
5408
logger.info{ ins_args = args.inspect; "(#{[ins_args.length,120].min}/#{ins_args.length})" + ins_args[0..120] }
-
2704
db.execute(*args)
-
end
-
-
1
def initialize(table_name, *keys)
-
864
@table_name = table_name
-
864
@keys = keys
-
864
if(execute("select * from sqlite_master where name = ?", table_name).empty?)
-
1192
logger.warn{ "Initializing a table called #{table_name}" }
-
596
execute(create_sql)
-
end
-
end
-
-
1
def select(which_sql, condition_sql)
-
665
execute("select #{which_sql} from #@table_name where #{condition_sql}")
-
end
-
-
1
def fields_sql
-
440
"#{@keys.join(",")}"
-
end
-
-
1
def insert(values_sql)
-
440
execute("insert into #@table_name(#{fields_sql}) values (#{values_sql})")
-
end
-
-
1
def keys(name)
-
execute("select #{name} from #@table_name").flatten
-
end
-
-
1
def length(where = "1 = 1")
-
1
execute("select count(1) from #@table_name where #{where}").first.first
-
end
-
-
1
def to_a
-
66
execute("select * from #@table_name")
-
end
-
end
-
-
1
class RequestTable < Table
-
1
def initialize()
-
344
super("requests", "method", "url", "date")
-
end
-
-
1
def store(method, url)
-
71
result = insert("'#{method}', '#{url}', #{Time.now.to_i}")
-
71
db.last_insert_row_id
-
end
-
-
1
def last_request_id
-
1
execute("select max(id) from #@table_name").first.first
-
end
-
-
1
def sweep
-
71
execute("delete from #@table_name where date < #{Time.now.to_i - (60 * 60 * 12)}")
-
end
-
end
-
-
1
require 'yaml'
-
1
class DataTable < Table
-
1
def initialize(name, *keys)
-
520
super(name, *(%w{request_id} + keys + %w{value}))
-
end
-
-
1
def create_keys_clause
-
511
non_request_keys = @keys - %w"request_id"
-
1023
sql = non_request_keys.map{|key| "#{key} varchar"}.join(", ")
-
511
sql += ", request_id references requests(id) on delete cascade"
-
511
sql
-
end
-
-
1
def store(request_id, value, keys_sql = "")
-
369
sql = "'#{encode_value(value)}'"
-
369
sql = keys_sql + ", " + sql unless keys_sql.empty?
-
369
sql = "#{request_id}, #{sql}"
-
369
insert(sql)
-
end
-
-
1
def encode_value(value)
-
369
Base64.encode64(YAML.dump(value))
-
end
-
-
1
def decode_value(value)
-
519
YAML.load(Base64.decode64(value))
-
end
-
-
1
def retrieve(key_sql)
-
1184
select("value", key_sql).map{|value| decode_value(value.first)}
-
end
-
-
1
def for_request(id)
-
665
retrieve("request_id = #{id}")
-
end
-
-
1
def to_a
-
super.map do |row|
-
row[-1] = decode_value(row[-1])
-
end
-
end
-
end
-
end
-
end
-
1
module Insight
-
1
class EnableButton
-
1
include Render
-
-
1
MIME_TYPES = ["text/html", "application/xhtml+xml"]
-
-
1
def initialize(app, insight)
-
1
@app = app
-
1
@insight = insight
-
end
-
-
1
def call(env)
-
6
@env = env
-
6
status, headers, body = @app.call(@env)
-
-
6
response = Rack::Response.new(body, status, headers)
-
-
6
inject_button(response) if okay_to_modify?(env, response)
-
-
6
return response.to_a
-
end
-
-
1
def okay_to_modify?(env, response)
-
6
req = Rack::Request.new(env)
-
6
content_type, charset = response.content_type.split(";")
-
-
6
response.ok? && MIME_TYPES.include?(content_type) && !req.xhr?
-
end
-
-
1
def inject_button(response)
-
4
full_body = response.body.join
-
4
full_body.sub! /<\/body>/, render + "</body>"
-
-
4
response["Content-Length"] = full_body.bytesize.to_s
-
-
4
response.body = [full_body]
-
end
-
-
1
def render
-
4
render_template("enable-button")
-
end
-
end
-
end
-
1
module Insight
-
1
module FilteredBacktrace
-
-
1
def backtrace
-
@backtrace
-
end
-
-
1
def has_backtrace?
-
16
filtered_backtrace.any?
-
end
-
-
1
def filtered_backtrace
-
25
@filtered_backtrace ||= @backtrace.grep(FilteredBacktrace.backtrace_regexp)
-
end
-
-
1
def self.backtrace_regexp
-
@backtrace_regexp ||=
-
begin
-
1
if true or (app_root = root_for_backtrace_filtering).nil?
-
1
/.*/
-
else
-
excludes = %w{vendor}
-
%r{^#{app_root}(?:#{::File::Separator}(?!#{excludes.join("|")}).+)$}
-
end
-
10
end
-
end
-
-
1
def self.root_for_backtrace_filtering(sub_path = nil)
-
if defined?(Rails) && Rails.respond_to?(:root)
-
sub_path ? Rails.root.join(sub_path) : Rails.root
-
else
-
root = if defined?(RAILS_ROOT)
-
RAILS_ROOT
-
elsif defined?(ROOT)
-
ROOT
-
elsif defined?(Sinatra::Application)
-
Sinatra::Application.root
-
else
-
nil
-
end
-
sub_path ? ::File.join(root, sub_path) : root
-
end
-
end
-
end
-
end
-
1
module Insight
-
1
module Instrumentation; end
-
end
-
1
require 'insight/instrumentation/instrument'
-
1
require 'insight/instrumentation/probe'
-
1
require 'insight/instrumentation/client'
-
1
require 'insight/instrumentation/setup'
-
1
require 'insight/instrumentation/package-definition'
-
1
require 'insight/instrumentation/probe-definition'
-
1
module Insight::Instrumentation
-
1
module Backstage
-
1
def backstage
-
409
Thread.current["instrumented_backstage"] = true
-
409
yield
-
ensure
-
409
Thread.current["instrumented_backstage"] = false
-
end
-
end
-
end
-
1
require 'insight/instrumentation/package-definition'
-
1
module Insight::Instrumentation
-
1
module Client
-
1
def probe(collector, &block)
-
341
::Insight::Instrumentation::PackageDefinition::probe(collector, &block)
-
end
-
-
1
def request_start(env, start)
-
end
-
-
1
def before_detect(method_call, arguments)
-
end
-
-
1
def after_detect(method_call, timing, arguments, result)
-
end
-
-
1
def request_finish(env, status, headers, body, timing)
-
end
-
end
-
end
-
1
require 'insight/instrumentation/backstage'
-
1
require 'insight/logger'
-
-
1
module Insight
-
1
module Instrumentation
-
1
class Instrument
-
1
MethodCall = Struct.new(:call_number, :backtrace, :file, :line, :object, :context, :kind, :method, :thread)
-
1
class Timing
-
1
def initialize(request_start, start, finish)
-
177
@request_start, @start, @finish = request_start, start, finish
-
end
-
-
1
attr_reader :request_start, :start, :finish
-
-
1
def duration
-
109
@duration ||= ((@finish - @start) * 1000).to_i
-
end
-
-
1
def delta_t
-
44
@delta_t ||= ((@start - @request_start) * 1000).to_i
-
end
-
end
-
-
1
@@call_seq = 0
-
-
1
def self.seq_number
-
101
Thread.exclusive do
-
101
return @@call_seq += 1
-
end
-
end
-
-
1
def initialize()
-
76
@start = Time.now
-
76
@collectors = nil
-
end
-
-
1
include Backstage
-
-
1
include Logging
-
-
1
def run(object, context="::", kind=:instance, called_at = caller[0], method = "<unknown>", args=[], &blk)
-
101
file, line, rest = called_at.split(':')
-
202
call_number = backstage{ self.class.seq_number }
-
202
method_call = backstage{ MethodCall.new(call_number, caller(1), file, line, object, context, kind, method, Thread::current) }
-
#$stderr.puts [method_call.context, method_call.method].inspect
-
101
start_time = Time.now
-
101
backstage do
-
101
start_event(method_call, args)
-
end
-
101
result = blk.call # execute the provided code block
-
101
backstage do
-
101
finish_event(method_call, args, start_time, result)
-
end
-
end
-
-
1
def collectors_for(method_call)
-
202
probe_chain = if method_call.kind == :instance
-
174
InstanceProbe.get_probe_chain(method_call.context)
-
else
-
28
ClassProbe.get_probe_chain(method_call.context)
-
end
-
202
collectors = probe_chain.inject([]) do |list, probe|
-
202
probe.collectors(method_call.method)
-
end
-
202
logger.debug do
-
552
"Probe chain for: #{method_call.context} #{method_call.kind} #{method_call.method}:\n #{collectors.map{|col| col.class.name}.join(", ")}"
-
end
-
202
collectors
-
end
-
-
1
def start_event(method_call, arguments)
-
202
logger.debug{ "Starting event: #{method_call.context} #{method_call.kind} #{method_call.method}" }
-
-
101
collectors_for(method_call).each do |collector|
-
175
collector.before_detect(method_call, arguments)
-
end
-
end
-
-
1
def finish_event(method_call, arguments, start_time, result)
-
101
timing = Timing.new(@start, start_time, Time.now)
-
202
logger.debug{ "Finishing event: #{method_call.context} #{method_call.kind} #{method_call.method}" }
-
101
collectors_for(method_call).each do |collector|
-
175
collector.after_detect(method_call, timing, arguments, result)
-
end
-
end
-
-
1
def all_collectors
-
152
PackageDefinition.all_collectors
-
end
-
-
1
def start(env)
-
76
all_collectors.each do |collector|
-
323
collector.request_start(env, @start)
-
end
-
end
-
-
1
def finish(env, status, headers, body)
-
76
@timing = Timing.new(@start, @start, Time.now)
-
76
all_collectors.each do |collector|
-
323
collector.request_finish(env, status, headers, body, @timing)
-
end
-
end
-
-
1
def duration
-
76
@timing.duration
-
end
-
end
-
end
-
end
-
1
module Insight::Instrumentation
-
1
class PackageDefinition
-
1
class << self
-
1
def start
-
76
@started = begin
-
76
probes.each do |probe|
-
372
probe.fulfill_probe_orders
-
end
-
76
true
-
end
-
end
-
-
1
def probes
-
76
InstanceProbe.all_probes + ClassProbe.all_probes
-
end
-
-
1
def clear_collectors
-
85
all_collectors.clear
-
end
-
-
1
def all_collectors
-
919
@all_collectors ||= []
-
end
-
-
1
def add_collector(collector)
-
341
unless all_collectors.include?(collector)
-
341
all_collectors << collector
-
end
-
end
-
-
1
def probe(collector, &block)
-
341
add_collector(collector)
-
341
definer = self.new(collector)
-
341
definer.instance_eval &block
-
end
-
end
-
-
1
def get_class_probe(name)
-
100
ClassProbe.probe_for(name)
-
end
-
-
1
def get_instance_probe(name)
-
770
InstanceProbe.probe_for(name)
-
end
-
-
1
def initialize(collector)
-
341
@collector = collector
-
end
-
-
1
attr_reader :collector
-
-
1
def instrument(name, &block)
-
836
definer = ProbeDefinition.new(self, name)
-
836
definer.instance_eval(&block) unless block.nil?
-
836
return definer
-
end
-
end
-
end
-
1
module Insight::Instrumentation
-
1
class ProbeDefinition
-
1
def initialize(package, target_name)
-
836
@package = package
-
836
@target_name = target_name
-
end
-
-
1
def instance_probe(*method_names)
-
770
if probes = @package.get_instance_probe(@target_name)
-
184
probes.probe(@package.collector, *method_names)
-
end
-
end
-
-
1
def class_probe(*method_names)
-
100
if probes = @package.get_class_probe(@target_name)
-
73
probes.probe(@package.collector, *method_names)
-
end
-
end
-
end
-
end
-
1
module Insight
-
1
module Instrumentation
-
1
class Probe
-
1
module Interpose
-
end
-
-
1
@@class_list = nil
-
-
1
module ProbeRunner
-
1
include Backstage
-
1
include Logging
-
-
1
def probe_run(object, context = "::", kind=:instance, args=[], called_at=caller[1], method_name = nil)
-
106
if Thread.current['instrumented_backstage']
-
#warn "probe_run while backstage: #{context}, #{kind},
-
##{method_name}" unless method_name.to_sym == :add
-
return yield
-
end
-
106
instrument = Thread.current['insight.instrument']
-
106
result = nil
-
106
if instrument.nil?
-
5
backstage do
-
5
result = yield
-
end
-
else
-
202
instrument.run(object, context, kind, called_at, method_name, args){ result = yield }
-
end
-
106
result
-
end
-
1
extend self
-
end
-
-
1
class << self
-
1
include Logging
-
-
1
def class_list
-
@@class_list ||= begin
-
1
classes = []
-
1
ObjectSpace.each_object(Class) do |klass|
-
958
classes << klass
-
end
-
1
classes
-
8
end
-
end
-
-
1
def get_probe_chain(name)
-
202
const = const_from_name(name)
-
202
chain = []
-
202
const.ancestors.each do |mod|
-
1742
if probes.has_key?(mod.name)
-
202
chain << probes[mod.name]
-
end
-
end
-
202
chain
-
end
-
-
1
def const_from_name(name)
-
823
parts = name.split("::")
-
823
const = parts.inject(Kernel) do |namespace, part|
-
968
namespace.const_get(part)
-
end
-
end
-
-
1
def probes
-
@probes ||= Hash.new do |h,k|
-
621
begin
-
621
h[k] = self.new(const_from_name(k))
-
rescue NameError
-
1226
logger.warn{ "Cannot find constant: #{k}" }
-
end
-
3136
end
-
end
-
-
1
def all_probes
-
322
probes.values
-
end
-
-
1
def probe_for(const)
-
870
probes[const]
-
end
-
end
-
-
1
def initialize(const)
-
8
@const = const
-
8
@probed = {}
-
499
@collectors = Hash.new{|h,k| h[k] = []}
-
8
@probe_orders = []
-
end
-
-
1
def collectors(key)
-
202
@collectors[key.to_sym]
-
end
-
-
1
def all_collectors
-
@collectors.values
-
end
-
-
1
def clear_collectors
-
435
@collectors.clear
-
end
-
-
1
def probe(collector, *methods)
-
257
methods.each do |name|
-
516
unless @collectors[name.to_sym].include?(collector)
-
516
@collectors[name.to_sym] << collector
-
end
-
516
@probe_orders << name
-
end
-
end
-
-
1
def descendants
-
@descendants ||= self.class.class_list.find_all do |klass|
-
7664
klass.ancestors.include?(@const)
-
1032
end
-
end
-
-
1
def local_method_defs(klass)
-
11
klass.instance_methods(false)
-
end
-
-
1
def descendants_that_define(method_name)
-
1032
log{{ :descendants => descendants }}
-
516
descendants.find_all do |klass|
-
151
(@const != klass and local_method_defs(klass).include?(method_name))
-
end
-
end
-
-
1
include Logging
-
1
def log &block
-
1460
logger.debug &block
-
end
-
-
1
def fulfill_probe_orders
-
744
log{{:probes_for => @const.name, :type => self.class}}
-
372
@probe_orders.each do |method_name|
-
1032
log{{ :method => method_name }}
-
516
build_tracing_wrappers(@const, method_name)
-
516
descendants_that_define(method_name).each do |klass|
-
44
log{{ :subclass => klass.name }}
-
22
build_tracing_wrappers(klass, method_name)
-
end
-
end
-
372
@probe_orders.clear
-
end
-
-
1
def get_method(klass, method_name)
-
22
klass.instance_method(method_name)
-
end
-
-
1
def define_trace_method(target, method)
-
3
target.class_exec() do
-
3
define_method(method.name) do |*args, &block|
-
69
ProbeRunner::probe_run(self, method.owner.name, :instance, args, caller(0)[0], method.name) do
-
69
method.bind(self).call(*args, &block)
-
end
-
end
-
end
-
end
-
-
1
def build_tracing_wrappers(target, method_name)
-
538
return if @probed.has_key?([target,method_name])
-
34
@probed[[target,method_name]] = true
-
-
34
meth = get_method(target, method_name)
-
-
16
log{ {:tracing => meth } }
-
8
define_trace_method(target, meth)
-
rescue NameError => ne
-
52
log{ {:not_tracing => NameError } }
-
end
-
end
-
-
1
class ClassProbe < Probe
-
1
def local_method_defs(klass)
-
33
klass.singleton_methods(false)
-
end
-
-
1
def get_method(klass, method_name)
-
24
(class << klass; self; end).instance_method(method_name)
-
end
-
-
1
def define_trace_method(target, method)
-
10
(class << target; self; end).class_exec() do
-
5
define_method(method.name) do |*args, &block|
-
9
ProbeRunner::probe_run(self, target.name, :class, args, caller(0)[0], method.name) do
-
9
method.bind(self).call(*args, &block)
-
end
-
end
-
end
-
end
-
end
-
-
1
class InstanceProbe < Probe
-
end
-
end
-
end
-
1
module Insight::Instrumentation
-
1
class Setup
-
1
def initialize(app)
-
97
@app = app
-
end
-
-
1
def setup(env)
-
76
instrument = Instrument.new
-
-
76
PackageDefinition.start
-
76
instrument.start(env)
-
-
76
env["insight.instrument"] = instrument
-
76
Thread::current["insight.instrument"] = instrument
-
end
-
-
1
def teardown(env, status, headers, body)
-
76
instrument, env["insight.instrument"] = env["insight.instrument"], nil
-
76
instrument.finish(env, status, headers, body)
-
76
Thread::current["insight.instrument"] = nil
-
-
76
env["insight.duration"] = instrument.duration
-
end
-
-
1
def call(env)
-
65
setup(env)
-
65
status, headers, body = @app.call(env)
-
65
teardown(env, status, headers, body)
-
65
return [status, headers, body]
-
end
-
end
-
end
-
1
module Insight
-
1
class Logger
-
1
def initialize(level, path)
-
85
@level = level
-
85
@log_path = path
-
85
@logfile = nil
-
end
-
-
1
attr_accessor :level
-
-
1
DEBUG = 0
-
1
INFO = 1
-
1
WARN = 2
-
1
ERROR = 3
-
1
FATAL = 4
-
1
UNKNOWN = 5
-
-
1
def log(severity, message)
-
7051
message = message.inspect unless String === message
-
7051
return unless severity >= @level
-
-
1582
if defined? Rails and Rails.respond_to?(:logger) and not Rails.logger.nil?
-
Rails.logger.add(severity, "[Insight]: " + message)
-
end
-
-
1582
logfile.puts(message)
-
end
-
-
1
def logfile
-
1582
@logfile ||= File::open(@log_path, "a+")
-
rescue
-
198
$stderr
-
end
-
-
3063
def debug; log(DEBUG, yield) end
-
2781
def info; log(INFO, yield) end
-
1210
def warn; log(WARN, yield) end
-
1
def error; log(ERROR, yield) end
-
1
def fatal; log(FATAL, yield) end
-
1
def unknown; log(UNKNOWN, yield) end
-
end
-
-
1
module Logging
-
1
def logger(env = nil)
-
6877
if env.nil?
-
6827
Thread.current['insight.logger'] ||= Logger.new(Logger::DEBUG, "")
-
else
-
50
env["insight.logger"]
-
end
-
end
-
1
module_function :logger
-
end
-
end
-
1
require 'ipaddr'
-
-
1
module Insight
-
1
module Options
-
1
class << self
-
1
private
-
1
def option_accessor(key)
-
6
define_method(key) { || read_option(key) }
-
32
define_method("#{key}=") { |value| write_option(key, value) }
-
6
define_method("#{key}?") { || !! read_option(key) }
-
end
-
end
-
-
1
option_accessor :secret_key
-
1
option_accessor :ip_masks
-
1
option_accessor :password
-
1
option_accessor :panel_classes
-
1
option_accessor :intercept_redirects
-
1
option_accessor :database_path
-
-
# The underlying options Hash. During initialization (or outside of a
-
# request), this is a default values Hash. During a request, this is the
-
# Rack environment Hash. The default values Hash is merged in underneath
-
# the Rack environment before each request is processed.
-
1
def options
-
253
@env || @default_options
-
end
-
-
# Set multiple options.
-
1
def options=(hash={})
-
1192
hash.each { |key,value| write_option(key, value) }
-
end
-
-
# Set an option. When +option+ is a Symbol, it is set in the Rack
-
# Environment as "rack-cache.option". When +option+ is a String, it
-
# exactly as specified. The +option+ argument may also be a Hash in
-
# which case each key/value pair is merged into the environment as if
-
# the #set method were called on each.
-
1
def set(option, value=self, &block)
-
if block_given?
-
write_option option, block
-
elsif value == self
-
self.options = option.to_hash
-
else
-
write_option option, value
-
end
-
end
-
-
1
private
-
-
1
def read_option(key)
-
432
key = option_name(key)
-
432
if @env and @env.has_key?(key)
-
@env[key]
-
else
-
432
@default_options[key]
-
end
-
end
-
-
1
def write_option(key, value)
-
1138
@default_options[option_name(key)] = value
-
1138
if @env.respond_to? :[]
-
962
@env[option_name(key)] = value
-
end
-
end
-
-
1
def option_name(key)
-
2532
case key
-
264
when Symbol ; "insight.#{key}"
-
2268
when String ; key
-
else raise ArgumentError
-
end
-
end
-
-
1
def process_options
-
86
if(file_list = read_option('insight.panel_files'))
-
86
class_list = read_option('insight.panel_classes') || []
-
86
file_list.each do |file|
-
590
class_list |= Insight::Panel.from_file(file)
-
end
-
86
write_option('insight.panel_classes', class_list)
-
end
-
-
86
Insight::Database.database_path = read_option('insight.database_path')
-
end
-
-
1
def initialize_options(options=nil)
-
86
@default_options = {
-
'insight.ip_masks' => [IPAddr.new("127.0.0.1")],
-
'insight.password' => nil,
-
'insight.verbose' => nil,
-
'insight.secret_key' => nil,
-
'insight.intercept_redirects' => false,
-
'insight.panels' => [],
-
'insight.path_filters' => %w{/assets/},
-
'insight.log_level' => Logger::INFO,
-
'insight.log_path' => "log/insight.log",
-
'insight.database_path' => "insight.sqlite",
-
'insight.panel_files' => %w{
-
rails_info_panel
-
timer_panel
-
request_variables_panel
-
sql_panel
-
active_record_panel
-
cache_panel
-
templates_panel
-
log_panel
-
memory_panel
-
}
-
}
-
86
self.options = options || {}
-
86
process_options
-
end
-
-
end
-
end
-
1
require "erb"
-
1
require 'insight/database'
-
1
require 'insight/instrumentation'
-
1
require 'insight/render'
-
-
1
module Insight
-
-
# Panels are also Rack middleware
-
1
class Panel
-
1
include Render
-
1
include ERB::Util
-
1
include Database::RequestDataClient
-
1
include Logging
-
1
include Instrumentation::Client
-
-
1
attr_reader :request
-
-
1
class << self
-
1
def file_index
-
return @file_index ||= Hash.new do |h,k|
-
11
h[k] = []
-
601
end
-
end
-
-
1
def panel_exclusion
-
590
return @panel_exclusion ||= []
-
end
-
-
1
def from_file(rel_path)
-
590
old_rel, Thread::current['panel_file'] = Thread::current['panel_file'], rel_path
-
590
require File::join('insight', 'panels', rel_path)
-
590
return (file_index[rel_path] - panel_exclusion)
-
ensure
-
590
Thread::current['panel_file'] = old_rel
-
end
-
-
1
def current_panel_file
-
return Thread::current['panel_file'] ||
-
begin
-
1
file_name = nil
-
1
caller.each do |line|
-
13
md = %r{^[^:]*insight/panels/([^:]*)\.rb:}.match line
-
13
unless md.nil?
-
file_name = md[1]
-
end
-
end
-
1
file_name
-
23
end
-
end
-
-
1
def inherited(sub)
-
12
if filename = current_panel_file
-
11
Panel::file_index[current_panel_file] << sub
-
else
-
1
warn "Insight::Panel inherited by #{sub.name} outside of an insight/panels/* file. Discarded"
-
end
-
end
-
-
1
def excluded(klass = nil)
-
Panel::panel_exclusion << klass || self
-
end
-
-
end
-
-
1
def initialize(app)
-
590
if panel_app
-
#XXX use mappings
-
63
@app = Rack::Cascade.new([panel_app, app])
-
else
-
527
@app = app
-
end
-
end
-
-
1
def call(env)
-
462
@env = env
-
924
logger.debug{ "Before call: #{self.name}" }
-
462
before(env)
-
462
status, headers, body = @app.call(env)
-
462
@request = Rack::Request.new(env)
-
924
logger.debug{ "After call: #{self.name}" }
-
462
after(env, status, headers, body)
-
462
env["insight.panels"] << self
-
462
return [status, headers, body]
-
end
-
-
1
def panel_app
-
nil
-
end
-
-
1
def self.panel_mappings
-
514
{}
-
end
-
-
1
def has_content?
-
832
true
-
end
-
-
1
def name
-
"Unnamed panel: #{self.class.name}" #for shame
-
end
-
-
1
def heading_for_request(number)
-
159
heading rescue "xxx" #XXX: no panel should need this
-
end
-
-
1
def content_for_request(number)
-
58
content rescue "" #XXX: no panel should need this
-
end
-
-
1
def before(env)
-
end
-
-
1
def after(env, status, headers, body)
-
end
-
-
1
def render(template)
-
end
-
-
end
-
-
end
-
1
module Insight
-
-
1
class PanelApp
-
1
include Insight::Render
-
-
1
attr_reader :request
-
-
1
def call(env)
-
64
@request = Rack::Request.new(env)
-
64
dispatch
-
end
-
-
1
def render_template(*args)
-
4
Rack::Response.new([super]).to_a
-
end
-
-
1
def params
-
12
@request.GET
-
end
-
-
1
def not_found(message="")
-
50
[404, {}, [message]]
-
end
-
-
1
def validate_params
-
14
ParamsSignature.new(request).validate!
-
end
-
-
end
-
-
end
-
1
module Insight
-
1
class PanelsContent < PanelApp
-
1
def initialize(insight_app)
-
86
@insight_app = insight_app
-
86
@request_table = Database::RequestTable.new
-
end
-
-
1
def dispatch
-
return not_found("not get") unless @request.get?
-
return not_found("id nil") if params['request_id'].nil?
-
request = @request_table.select("*", "id = #{params['request_id']}").first
-
return not_found("id not found") if request.nil?
-
requests = @request_table.to_a.map do |row|
-
{ :id => row[0], :method => row[1], :path => row[2] }
-
end
-
render_template("request_fragment",
-
:request_id => params['request_id'].to_i,
-
:requests => requests,
-
:panels => @insight_app.panels)
-
end
-
end
-
end
-
1
module Insight
-
1
class PanelsHeader < PanelApp
-
1
def initialize(insight_app)
-
86
@insight_app = insight_app
-
86
@request_table = Database::RequestTable.new
-
end
-
-
1
def dispatch
-
return not_found("not get") unless @request.get?
-
return not_found("id nil") if params['request_id'].nil?
-
request = @request_table.select("*", "id = #{params['request_id']}").first
-
return not_found("id not found") if request.nil?
-
render_template("headers_fragment",
-
:request_id => params['request_id'].to_i,
-
:panels => @insight_app.panels)
-
end
-
end
-
end
-
1
module Insight
-
1
class ActiveRecordPanel < Panel
-
1
def initialize(app)
-
65
super
-
-
65
table_setup("active_record")
-
-
65
probe(self) do
-
65
instrument "ActiveRecord::Base" do
-
65
class_probe :allocate
-
end
-
end
-
end
-
-
1
def request_start(env, start)
-
56
@records = Hash.new{ 0 }
-
end
-
-
1
def after_detect(method_call, timing, results, args)
-
5
@records[method_call.object.base_class.name] += 1
-
end
-
-
1
def request_finish(env, status, headers, body, timing)
-
52
store(env, @records)
-
end
-
-
1
def name
-
226
"active_record"
-
end
-
-
1
def heading_for_request(number)
-
52
record = retrieve(number).first
-
52
total = record.inject(0) do |memo, (key, value)|
-
4
memo + value
-
end
-
46
"#{total} AR Objects"
-
end
-
-
1
def content_for_request(number)
-
56
records = retreive(number).first.to_a.sort_by { |key, value| value }.reverse
-
52
render_template "panels/active_record", :records => records
-
end
-
-
end
-
-
end
-
-
1
module Insight
-
-
1
class CachePanel < Panel
-
1
require "insight/panels/cache_panel/panel_app"
-
1
require "insight/panels/cache_panel/stats"
-
-
1
def initialize(app)
-
63
super
-
-
63
probe(self) do
-
63
instrument("Memcached") do
-
63
instance_probe :decrement, :get, :increment, :set, :add,
-
:replace, :delete, :prepend, :append
-
end
-
-
63
instrument("MemCache") do
-
63
instance_probe :decr, :get, :get_multi, :incr, :set, :add, :delete
-
end
-
-
63
instrument("Dalli::Client") do
-
63
instance_probe :perform
-
end
-
end
-
-
63
table_setup("cache")
-
end
-
-
1
def request_start(env, start)
-
50
@stats = Stats.new
-
end
-
-
1
def request_finish(env, st, hd, bd, timing)
-
100
logger(env).debug{ "Stats: #@stats" }
-
50
store(env, @stats)
-
end
-
-
1
def after_detect(method_call, timing, args, result)
-
10
method, key = method_call.method, args.first
-
10
if(defined? Dalli and Dalli::Client === method_call.object)
-
method, key = args[0], args[1]
-
end
-
20
logger.info{ "Cache panel got #{method} #{key.inspect}" }
-
10
@stats.record_call(method, timing.duration, !result.nil?, key)
-
end
-
-
1
def panel_app
-
126
PanelApp.new
-
end
-
-
1
def name
-
218
"cache"
-
end
-
-
1
def heading_for_request(number)
-
50
stats = retreive(number).first
-
-
50
"Cache: %.2fms (#{stats.queries.size} calls)" % stats.time
-
end
-
-
1
def content_for_request(number)
-
100
logger.debug{{ :req_num => number }}
-
50
stats = retreive(number).first
-
50
render_template "panels/cache", :stats => stats
-
end
-
-
end
-
-
end
-
1
module Insight
-
1
class CachePanel
-
-
1
class PanelApp < ::Insight::PanelApp
-
-
1
def dispatch
-
56
case request.path_info
-
2
when "/__insight__/view_cache" then view_cache
-
2
when "/__insight__/delete_cache" then delete_cache
-
2
when "/__insight__/delete_cache_list" then delete_cache_list
-
50
else not_found
-
end
-
end
-
-
1
def ok
-
4
Rack::Response.new(["OK"]).to_a
-
end
-
-
1
def view_cache
-
2
validate_params
-
2
render_template "panels/view_cache", :key => params["key"], :value => Rails.cache.read(params["key"])
-
end
-
-
1
def delete_cache
-
2
validate_params
-
2
raise "Rails not found... can't delete key" unless defined?(Rails)
-
2
Rails.cache.delete(params["key"])
-
2
ok
-
end
-
-
1
def delete_cache_list
-
2
validate_params
-
2
raise "Rails not found... can't delete key" unless defined?(Rails)
-
-
2
params.each do |key, value|
-
10
next unless key =~ /^keys_/
-
8
Rails.cache.delete(value)
-
end
-
-
2
ok
-
end
-
-
end
-
-
end
-
end
-
1
module Insight
-
1
class CachePanel
-
-
1
class Stats
-
1
class Query
-
1
attr_reader :method, :time, :hit, :keys
-
-
1
def initialize(method, time, hit, keys)
-
10
@method = method
-
10
@time = time
-
10
@hit = hit
-
10
@keys = keys
-
end
-
-
1
def display_time
-
10
"%.2fms" % time
-
end
-
-
1
def display_keys
-
10
if keys.size == 1
-
10
keys.first
-
else
-
keys.join(", ")
-
end
-
end
-
end
-
-
1
attr_reader :calls
-
1
attr_reader :keys
-
1
attr_reader :queries
-
-
1
def initialize
-
50
@queries = []
-
50
@misses =
-
@calls = 0
-
50
@time = 0.0
-
50
@keys = []
-
end
-
-
1
def record_call(method, time, hit, key)
-
10
if Array === key
-
@queries << Query.new(:get_multi, time, hit, key)
-
else
-
10
@queries << Query.new(method.to_sym, time, hit, [key])
-
end
-
10
@calls += 1
-
10
@time += time
-
10
@keys += keys
-
end
-
-
1
def display_time
-
44
"%.2fms" % time
-
end
-
-
1
def time
-
88
@queries.inject(0) do |memo, query|
-
20
memo + query.time
-
end
-
end
-
-
1
def gets
-
44
count_queries(:get)
-
end
-
-
1
def sets
-
44
count_queries(:set)
-
end
-
-
1
def deletes
-
44
count_queries(:delete)
-
end
-
-
1
def get_multis
-
44
count_queries(:get_multi)
-
end
-
-
1
def hits
-
54
@queries.select { |q| [:get, :get_multi].include?(q.method) && q.hit }.size
-
end
-
-
1
def misses
-
54
@queries.select { |q| [:get, :get_multi].include?(q.method) && !q.hit }.size
-
end
-
-
1
def count_queries(method)
-
216
@queries.select { |q| q.method == method }.size
-
end
-
-
1
def queries_to_param
-
10
params = {}
-
10
@queries.each_with_index do |query, index|
-
10
params["keys_#{index}"] = query.keys.first
-
end
-
10
params
-
end
-
end
-
end
-
end
-
1
module Insight
-
1
class LogPanel < Panel
-
1
class LogEntry
-
1
attr_reader :level, :time, :message
-
1
LEVELS = ['DEBUG', 'INFO', 'WARN', 'ERROR', 'FATAL']
-
-
1
def initialize(level, time, message)
-
18
@level = LEVELS[level]
-
18
@time = time
-
18
@message = message
-
end
-
-
1
def cleaned_message
-
18
@message.to_s.gsub(/\e\[[;\d]+m/, "")
-
end
-
end
-
-
1
def after_detect(method_call, timing, args, message)
-
18
message = args[1] || args[2] unless message.is_a?(String)
-
18
log_level = args[0]
-
18
store(@env, LogEntry.new(log_level, timing.delta_t, message))
-
end
-
-
1
def initialize(app)
-
18
probe(self) do
-
18
instrument "ActiveSupport::BufferedLogger" do
-
18
instance_probe :add
-
end
-
-
18
instrument "Logger" do
-
18
instance_probe :add
-
end
-
end
-
-
18
table_setup("log_entries")
-
-
18
super
-
end
-
-
1
def name
-
96
"log"
-
end
-
-
1
def heading
-
24
"Log"
-
end
-
-
1
def content_for_request(number)
-
24
render_template "panels/log", :logs => retrieve(number)
-
end
-
end
-
-
end
-
1
module Insight
-
-
1
class MemoryPanel < Panel
-
1
def initialize(app)
-
63
super
-
63
table_setup("memory_records")
-
end
-
-
1
def before(env)
-
56
@original_memory = `ps -o rss= -p #{$$}`.to_i
-
end
-
-
1
def after(env, status, headers, body)
-
56
total_memory = `ps -o rss= -p #{$$}`.to_i
-
56
store(env, {:total_memory => total_memory,
-
:memory_increase => total_memory - @original_memory,
-
:original_memory => @original_memory})
-
end
-
-
1
def heading_for_request(number)
-
50
record = retrieve(number).first
-
-
50
"#{record[:memory_increase]} KB Δ, #{record[:total_memory]} KB total"
-
end
-
-
1
def has_content?
-
100
false
-
end
-
-
1
def name
-
112
"Memory"
-
end
-
-
end
-
-
end
-
1
module Insight
-
1
class RailsInfoPanel < Panel
-
-
1
def name
-
212
"rails_info"
-
end
-
-
1
def heading
-
50
return unless (defined?(Rails) && defined?(Rails::Info))
-
2
"Rails #{Rails.version}"
-
end
-
-
1
def content
-
50
return unless (defined?(Rails) && defined?(Rails::Info))
-
2
render_template "panels/rails_info"
-
end
-
-
end
-
end
-
1
module Insight
-
-
1
class RedisPanel < Panel
-
1
require "insight/panels/redis_panel/redis_extension"
-
-
1
require "insight/panels/redis_panel/stats"
-
-
1
def self.record(redis_command_args, backtrace, &block)
-
6
return block.call unless Insight.enabled?
-
-
6
start_time = Time.now
-
6
result = block.call
-
6
total_time = Time.now - start_time
-
6
stats.record_call(total_time * 1_000, redis_command_args, backtrace)
-
6
return result
-
end
-
-
1
def self.reset
-
8
Thread.current["insight.redis"] = Stats.new
-
end
-
-
1
def self.stats
-
30
Thread.current["insight.redis"] ||= Stats.new
-
end
-
-
1
def name
-
32
"redis"
-
end
-
-
1
def heading
-
8
"Redis: %.2fms (#{self.class.stats.queries.size} calls)" % self.class.stats.time
-
end
-
-
1
def content
-
8
result = render_template "panels/redis", :stats => self.class.stats
-
8
self.class.reset
-
8
return result
-
end
-
-
end
-
-
end
-
1
if defined?(Redis)
-
Redis.class_eval do
-
if Redis.methods.include?('call_command') # older versions of redis-rb
-
def call_command_with_insight(*argv)
-
Insight::RedisPanel.record(argv, Kernel.caller) do
-
call_command_without_insight(*argv)
-
end
-
end
-
-
alias_method_chain :call_command, :insight
-
-
elsif defined?(Redis::Client) # newer versions of redis-rb
-
-
Redis::Client.class_eval do
-
def call_with_insight(*argv)
-
end
-
end
-
-
alias_method_chain :call, :insight
-
-
end
-
end
-
end
-
1
module Insight
-
1
class RedisPanel
-
-
1
class Stats
-
1
class Query
-
1
include Insight::FilteredBacktrace
-
-
1
attr_reader :time
-
1
attr_reader :command
-
-
1
def initialize(time, command_args, backtrace)
-
6
@time = time
-
6
@command = command_args.inspect
-
6
@backtrace = backtrace
-
end
-
-
1
def display_time
-
6
"%.2fms" % time
-
end
-
end
-
-
1
attr_reader :calls
-
1
attr_reader :queries
-
-
1
def initialize
-
9
@queries = []
-
9
@calls = 0
-
9
@time = 0.0
-
end
-
-
1
def record_call(time, command_args, backtrace)
-
6
@queries << Query.new(time, command_args, backtrace)
-
6
@calls += 1
-
6
@time += time
-
end
-
-
1
def display_time
-
8
"%.2fms" % time
-
end
-
-
1
def time
-
16
@queries.inject(0) do |memo, query|
-
12
memo + query.time
-
end
-
end
-
-
end
-
-
end
-
end
-
1
module Insight
-
1
class RequestVariablesPanel < Panel
-
1
def initialize(app)
-
63
super
-
-
63
table_setup("request_variables")
-
end
-
-
1
def name
-
212
"request_variables"
-
end
-
-
1
def after(env,status,headers,body)
-
56
sections = {}
-
56
sections["GET"] = sort(@request.GET) if @request.GET.any?
-
56
sections["POST"] = sort(@request.POST) if @request.POST.any?
-
56
sections["Session"] = sort(@request.env["rack.session"]) if @request.env["rack.session"] && @request.env["rack.session"].any?
-
56
sections["Cookies"] = sort(@request.env["rack.request.cookie_hash"]) if @request.env["rack.request.cookie_hash"] && @request.env["rack.request.cookie_hash"].any?
-
56
server, rack = split_and_filter_env(@env)
-
56
sections["SERVER VARIABLES"] = sort(server)
-
56
sections["Rack ENV"] = sort(rack)
-
-
# require 'pp'
-
# ::File.open("sections.dump", "w") do |file|
-
# PP.pp(sections, file)
-
# end
-
56
store(env, sections)
-
end
-
-
1
def heading
-
50
"Rack Env"
-
end
-
-
1
def content_for_request(number)
-
50
sections = retrieve(number).first
-
-
50
render_template "panels/request_variables", :sections => sections
-
end
-
-
1
private
-
1
def sort(hash)
-
1601
scrub(hash.sort_by { |k, v| k.to_s })
-
end
-
-
1
def scrub(enum)
-
177
enum.map do |k,v|
-
1424
if Hash === v
-
112
[k, v.inspect]
-
else
-
1312
[k, v.to_s]
-
end
-
end
-
end
-
-
1
def split_and_filter_env(env)
-
56
server, rack = {}, {}
-
56
env.each do |k,v|
-
2232
if k.index("rack.") == 0
-
728
rack[k] = v
-
elsif k.index("insight") == 0 or k.index("insight") == 0
-
#don't output the insight variables - especially secret_key
-
else
-
618
server[k] = v
-
end
-
end
-
56
return server, rack
-
end
-
-
end
-
end
-
1
begin
-
1
require 'yajl'
-
rescue LoadError
-
#Means no Chrome Speedtracer...
-
end
-
1
require 'uuid'
-
1
require 'insight/panels/speedtracer_panel/trace-app'
-
1
require 'insight/panels/speedtracer_panel/tracer'
-
-
1
module Insight
-
1
module SpeedTracer
-
1
class Panel < ::Insight::Panel
-
-
1
def initialize(app)
-
1
@app = app
-
1
@uuid = UUID.new
-
1
table_setup("speedtracer", "uuid")
-
1
key_sql_template("'%s'")
-
-
1
@tracer = Tracer.new(@table)
-
1
probe(@tracer) do
-
1
instrument("ActiveSupport::Notifications") do
-
1
class_probe :instrument
-
end
-
-
1
instrument("ActionView::Rendering") do
-
1
instance_probe :render
-
end
-
-
1
instrument("ActionView::Helpers::RecordTagHelper") do
-
1
instance_probe :content_tag_for
-
end
-
-
1
instrument("ActionView::Partials::PartialRenderer") do
-
1
instance_probe :render, :find_template, :render_collection, :collection_with_template, :collection_without_template, :partial_path, :collection_paths
-
end
-
-
1
instrument("ActionView::Template") do
-
1
instance_probe :render, :compile
-
end
-
-
1
instrument("ActiveRecord::Base") do
-
1
class_probe :find, :find_by_sql, :all, :first, :last, :count, :delete_all
-
1
instance_probe :save, :save!, :destroy, :delete
-
end
-
-
1
instrument("ActionController::Base") do
-
1
instance_probe :process, :render
-
end
-
end
-
-
1
super
-
end
-
-
-
1
def call(env)
-
1
env['insight.speedtracer-id'] = @uuid.generate
-
-
1
status, headers, body = @app.call(env)
-
-
1
store(env, env['insight.speedtracer-id'], env['insight.speedtracer-record'])
-
1
headers['X-TraceUrl'] = '__insight__/speedtracer?id=' + env['insight.speedtracer-id']
-
1
return [status, headers, body]
-
end
-
-
1
def self.panel_mappings
-
1
{ "speedtracer" => TraceApp.new }
-
end
-
-
1
def name
-
2
"speedtracer"
-
end
-
-
1
def heading
-
1
"#{table_length} traces"
-
end
-
-
1
def content_for_request(request_id)
-
1
trace = retrieve(request_id).first
-
1
return "" if trace.nil?
-
1
advice = []
-
1
if not defined?(Yajl)
-
1
advice << "yajl-ruby not installed - Speedtracer server events won't be available"
-
end
-
1
render_template "panels/speedtracer/traces", :trace => trace, :advice => advice
-
end
-
end
-
end
-
end
-
1
module Insight
-
1
module SpeedTracer
-
1
class TraceApp
-
1
include Database::RequestDataClient
-
-
1
CONTENT_TYPE = 'application/json;charset=UTF-8'.freeze
-
-
1
FourOhFour = [404, {"Content-Type" => "text/html"}, "App tracker doesn't know that path or id"].freeze
-
-
1
def initialize
-
1
table_setup("speedtracer", "uuid")
-
1
key_sql_template = "'%s'"
-
end
-
-
1
def call(env)
-
resp = Rack::Response.new('', 200)
-
resp['Content-Type'] = CONTENT_TYPE
-
-
case env['REQUEST_METHOD']
-
when 'HEAD' then
-
# SpeedTracer dispatches HEAD requests to verify the
-
# tracer endpoint when it detects the X-TraceUrl
-
# header for the first time. After the initial load
-
# the verification is cached by the extension.
-
#
-
# By default, we'll return 200.
-
-
when 'GET' then
-
# GET requests for specific trace are generated by
-
# the extension when the user expands the network
-
# resource tab. Hence, server-side tracer data is
-
# request on-demand, and we need to store it for
-
# some time.
-
#
-
-
qs = Rack::Utils.parse_query(env['QUERY_STRING'])
-
if qs['id'] && (trace = @table.retrieve("uuid = '#{qs['id']}'"))
-
resp.write trace.to_json
-
else
-
# Invalid request or missing request trace id
-
return FourOhFour
-
end
-
else
-
# SpeedTracer should only issue GET & HEAD requests
-
resp.status = 400
-
end
-
-
return resp.finish
-
end
-
end
-
end
-
end
-
1
module Insight
-
1
module SpeedTracer
-
1
class Tracer
-
1
def initialize(table)
-
1
@pstack = []
-
1
@table = table
-
1
@event_id = 0
-
end
-
-
1
def request_start(env, start)
-
1
id, method, uri = env.values_at("insight.speedtracer-id", "REQUEST_METHOD", "PATH_INFO")
-
1
@pstack.push RequestRecord.new(id, method, uri)
-
end
-
-
1
def request_finish(env, status, headers, body, timing)
-
1
env["insight.speedtracer-record"] = @pstack.pop
-
end
-
-
1
def before_detect(method_call, arguments)
-
@event_id += 1
-
-
#arguments_string = make_string_of(arguments)
-
arguments_string = ""
-
#XXX ServerEvent use method call...
-
event = ServerEvent.new(method_call, arguments_string)
-
@pstack.push event
-
end
-
-
1
def after_detect(method_call, timing, arguments, result)
-
event = @pstack.pop
-
if event.nil?
-
else
-
event.finish
-
-
unless (parent = @pstack.last).nil?
-
parent.children.push event
-
else
-
@children.push event
-
end
-
end
-
end
-
-
1
def make_string_of(array)
-
array.map do |item|
-
short_string(item)
-
end.join(",")
-
end
-
-
1
def short_string(item, max_per_elem = 50)
-
begin
-
string = item.inspect
-
if string.length > max_per_elem
-
case item
-
when NilClass
-
"nil"
-
when Hash
-
"{ " + item.map do |key, value|
-
short_string(key, 15) + "=>" + short_string(value, 30)
-
end.join(", ") + " }"
-
when find_constant("ActionView::Base")
-
tmpl = item.template
-
if tmpl.nil?
-
item.path.inspect
-
else
-
[tmpl.base_path, tmpl.name].join("/")
-
end
-
when find_constant("ActiveRecord::Base")
-
string = "#{item.class.name}(#{item.id})"
-
else
-
string = item.class.name
-
end
-
else
-
string
-
end
-
rescue Exception => ex
-
"..."
-
end
-
end
-
-
end
-
-
1
class TraceRecord
-
1
include Render
-
1
def initialize
-
1
@start = Time.now
-
1
@children = []
-
end
-
-
1
attr_accessor :children
-
1
attr_reader :start
-
-
1
def finish
-
1
@finish ||= Time.now
-
end
-
-
1
def time_in_children
-
1
@children.inject(0) do |time, child|
-
time + child.duration
-
end
-
end
-
-
1
def duration
-
1
((@finish - @start) * 1000).to_i
-
end
-
-
1
def to_json
-
Yajl::Encoder.encode(hash_representation, :pretty => true, :indent => ' ')
-
end
-
-
1
private
-
# all timestamps in SpeedTracer are in milliseconds
-
1
def range(start, finish)
-
{
-
2
'duration' => ((finish - start) * 1000).to_i,
-
'start' => [start.to_i, start.usec/1000].join(''),
-
#'end' => [finish.to_i, finish.usec/1000].join('')
-
2
}
-
end
-
-
1
def symbolize_hash(hash)
-
1
symbolled_hash = {}
-
1
hash.each_key do |key|
-
3
if String === key
-
3
next if hash.has_key?(key.to_sym)
-
3
symbolled_hash[key.to_sym] = hash[key]
-
end
-
end
-
1
hash.merge!(symbolled_hash)
-
end
-
end
-
-
1
class ServerEvent < TraceRecord
-
1
attr_reader :name
-
-
1
def initialize(method_call, arguments)
-
super()
-
@arguments = arguments
-
@name = "#{method_call.context}#{method_call.kind == :instance ? "#" : "::"}#{method_call.method}(#{arguments})"
-
end
-
-
1
def hash_representation
-
{
-
'range' => range(@start, @finish),
-
'operation' => {
-
# 'sourceCodeLocation' => {
-
# 'className' => @file,
-
# 'methodName' => @method,
-
# 'lineNumber' => @line
-
# },
-
'type' => 'METHOD',
-
'label' => @name
-
},
-
'children' => @children
-
}
-
end
-
-
1
def to_html
-
render_template('panels/speedtracer/serverevent',
-
{:self_time => duration - time_in_children}.merge(symbolize_hash(hash_representation)))
-
end
-
end
-
-
-
1
class RequestRecord < TraceRecord
-
1
def initialize(id, method, uri)
-
1
super()
-
-
1
@id = id
-
1
@method = method
-
1
@uri = uri
-
1
@event_id = 0
-
end
-
-
1
def uuid
-
1
@id
-
end
-
-
1
def hash_representation
-
1
finish
-
{ 'trace' => {
-
-
'url' => "/__insight__/speedtracer?id=#@id",
-
-
'frameStack' => {
-
-
'range' => range(@start, @finish),
-
'operation' => {
-
'type' => 'HTTP',
-
'label' => [@method, @uri].join(' ')
-
},
-
'children' => @children
-
-
}, #end frameStack
-
-
'resources' => {
-
'Application' => '/', #Should get the Rails app name...
-
'Application.endpoint' => '/' #Should get the env path thing
-
}, #From what I can tell, Speed Tracer treats this whole hash as optional
-
-
'range' => range(@start, @finish)
-
}
-
1
}
-
end
-
-
1
def to_html
-
1
hash = hash_representation
-
1
extra = {:self_time => duration - time_in_children}
-
"<a href='#{hash['trace']['url']}'>Raw JSON</a>\n" +
-
1
render_template('panels/speedtracer/serverevent', extra.merge(symbolize_hash(hash['trace']['frameStack'])))
-
end
-
end
-
end
-
end
-
1
require "digest"
-
-
1
module Insight
-
-
1
class SQLPanel < Panel
-
-
1
require "insight/panels/sql_panel/panel_app"
-
1
require "insight/panels/sql_panel/query"
-
-
1
def initialize(app)
-
75
super
-
75
probe(self) do
-
75
%w{ PostgreSQLAdapter MysqlAdapter SQLiteAdapter
-
Mysql2Adapter OracleEnhancedAdapter }.each do |adapter|
-
375
instrument "ActiveRecord::ConnectionAdapters::#{adapter}" do
-
375
instance_probe :execute
-
end
-
end
-
end
-
75
table_setup("sql_queries")
-
end
-
-
1
def self.panel_mappings
-
75
{ "sql" => PanelApp.new }
-
end
-
-
1
def after_detect(method_call, timing, arguments, results)
-
4
store(@env, QueryResult.new(arguments.first, timing.duration, method_call.backtrace, results))
-
end
-
-
1
def total_time(queries)
-
55
(queries.inject(0) do |memo, query|
-
4
memo + query.time
-
end)
-
end
-
-
1
def name
-
230
"sql"
-
end
-
-
1
def heading_for_request(number)
-
55
queries = retrieve(number)
-
55
"#{queries.size} Queries (%.2fms)" % total_time(queries)
-
end
-
-
1
def content_for_request(number)
-
55
queries = retrieve(number)
-
55
render_template "panels/sql", :queries => queries
-
end
-
-
end
-
-
end
-
1
require 'insight/panels/sql_panel/query'
-
-
1
module Insight
-
1
class SQLPanel
-
-
1
class PanelApp < ::Insight::PanelApp
-
-
1
def dispatch
-
8
case request.path_info
-
4
when "/explain" then explain_sql
-
when "/profile" then profile_sql
-
4
when "/execute" then execute_sql
-
else not_found
-
end
-
end
-
-
1
def explain_sql
-
4
validate_params
-
1
query = ExplainResult.new(params["query"], params["time"].to_f)
-
1
render_template "panels/explain_sql", :query => query
-
end
-
-
1
def profile_sql
-
validate_params
-
query = ProfileResult.new(params["query"], params["time"].to_f)
-
render_template "panels/profile_sql", :query => query
-
end
-
-
1
def execute_sql
-
4
validate_params
-
1
query = QueryResult.new(params["query"], params["time"].to_f)
-
1
render_template "panels/execute_sql", :query => query
-
end
-
-
end
-
end
-
end
-
1
module Insight
-
1
class SQLPanel
-
-
1
class QueryResult
-
1
include Insight::FilteredBacktrace
-
-
1
attr_reader :sql
-
1
attr_reader :time
-
-
1
def initialize(sql, time, backtrace = [], result=nil)
-
6
@sql = sql
-
6
@time = time
-
6
@backtrace = backtrace
-
6
@result = result
-
6
@results = nil
-
end
-
-
1
def result
-
8
@results ||= execute
-
8
return @results
-
end
-
-
1
def column_names
-
2
if result.respond_to?(:fields)
-
return result.fields
-
else
-
4
return result.fetch_fields.map{|col| col.name}
-
end
-
end
-
-
1
def rows
-
2
if result.respond_to?(:values)
-
result.values
-
else
-
2
result.map do |row|
-
2
row
-
end
-
end
-
end
-
-
1
def human_time
-
4
"%.2fms" % (@time)
-
end
-
-
1
def inspectable?
-
4
sql.strip =~ /^SELECT /i
-
end
-
-
#Downside is: we re-execute the SQL...
-
1
def self.execute(sql)
-
2
ActiveRecord::Base.connection.execute(sql)
-
end
-
-
1
def execute
-
1
self.class.execute(@sql)
-
end
-
-
1
def valid_hash?(secret_key, possible_hash)
-
hash = Digest::SHA1.hexdigest [secret_key, @sql].join(":")
-
possible_hash == hash
-
end
-
end
-
-
1
class ExplainResult < QueryResult
-
1
def execute
-
1
self.class.execute "EXPLAIN #{@sql}"
-
end
-
end
-
-
1
class ProfileResult < QueryResult
-
1
def with_profiling
-
result = nil
-
begin
-
self.class.execute("SET PROFILING=1")
-
result = yield
-
ensure
-
self.class.execute("SET PROFILING=0")
-
end
-
return result
-
end
-
-
1
def execute
-
with_profiling do
-
super
-
self.class.execute <<-SQL
-
SELECT *
-
FROM information_schema.profiling
-
WHERE query_id = (SELECT query_id FROM information_schema.profiling ORDER BY query_id DESC LIMIT 1)
-
SQL
-
end
-
end
-
end
-
end
-
end
-
1
module Insight
-
-
1
class TemplatesPanel < Panel
-
1
require "insight/panels/templates_panel/rendering"
-
-
1
def initialize(app)
-
63
super
-
-
63
probe(self) do
-
63
instrument "ActionView::Template" do
-
63
instance_probe :render
-
end
-
end
-
-
63
table_setup("templates")
-
-
63
@current = nil
-
end
-
-
-
1
def request_start(env, start)
-
50
@current = Rendering.new("root")
-
end
-
-
1
def request_finish(env, status, headers, body, timing)
-
50
store(env, @current)
-
50
@current = nil
-
end
-
-
1
def before_detect(method_call, args)
-
8
template_name = method_call.object.virtual_path
-
-
8
rendering = Rendering.new(template_name)
-
8
@current.add(rendering)
-
8
@current = rendering
-
end
-
-
1
def after_detect(method_call, timing, args, result)
-
8
@current.timing = timing
-
8
@current = @current.parent
-
end
-
-
1
def name
-
212
"templates"
-
end
-
-
1
def heading_for_request(number)
-
94
"Templates: %.2fms" % (retrieve(number).inject(0.0){|memo, rendering| memo + rendering.duration})
-
end
-
-
1
def content_for_request(number)
-
50
result = render_template "panels/templates", :root_rendering => retrieve(number).first
-
44
return result
-
end
-
-
end
-
-
end
-
1
module Insight
-
1
class TemplatesPanel
-
-
1
class Rendering
-
1
attr_accessor :name
-
1
attr_accessor :parent
-
1
attr_accessor :timing
-
1
attr_reader :children
-
-
1
def initialize(name, timing = nil)
-
58
@name = name
-
58
@timing = timing
-
58
@children = []
-
end
-
-
1
def start_time
-
@timing.start
-
end
-
1
alias_method :time, :start_time
-
-
1
def end_time
-
@timing.end
-
end
-
-
1
def add(rendering)
-
8
@children << rendering
-
8
rendering.parent = self
-
end
-
-
1
def delete(rendering)
-
@children.delete(rendering)
-
end
-
-
1
def duration
-
63
if @timing
-
19
@timing.duration
-
else
-
44
child_duration
-
end
-
end
-
-
1
def exclusive_duration
-
3
duration - child_duration
-
end
-
-
1
def child_duration
-
55
children.inject(0.0) { |memo, c| memo + c.duration }
-
end
-
-
1
def duration_summary
-
8
if children.any?
-
3
"%.2fms, %.2f exclusive" % [duration, exclusive_duration]
-
else
-
5
"%.2fms" % (duration)
-
end
-
end
-
1
def html
-
<<-HTML
-
<li>
-
8
<p>#{name} (#{duration_summary})</p>
-
-
#{children_html}
-
</li>
-
HTML
-
end
-
-
1
def children_html
-
8
return "" unless children.any?
-
-
<<-HTML
-
3
<ul>#{joined_children_html}</ul>
-
HTML
-
end
-
-
1
def joined_children_html
-
6
children.map { |c| c.html }.join
-
end
-
end
-
-
end
-
end
-
1
require 'benchmark'
-
-
1
module Insight
-
1
class TimerPanel < Panel
-
1
def initialize(app)
-
63
super
-
63
table_setup("timer")
-
end
-
-
1
def name
-
100
"timer"
-
end
-
-
1
def call(env)
-
56
status, headers, body = nil
-
56
@times = Benchmark.measure do
-
56
status, headers, body = @app.call(env)
-
end
-
-
56
store(env, [
-
56
["User CPU time", "%.2fms" % (@times.utime * 1_000)],
-
56
["System CPU time", "%.2fms" % (@times.stime * 1_000)],
-
56
["Total CPU time", "%.2fms" % (@times.total * 1_000)],
-
56
["Elapsed time", "%.2fms" % (@times.real * 1_000)]
-
])
-
-
56
return [status, headers, body]
-
end
-
-
1
def heading_for_request(number)
-
50
measurements = retreive(number).first
-
-
50
measurements.last.last
-
end
-
-
1
def content_for_request(number)
-
50
render_template "panels/timer", :measurements => retrieve(number).first
-
end
-
end
-
end
-
1
require "digest"
-
-
1
module Insight
-
-
1
class ParamsSignature
-
1
extend ERB::Util
-
-
1
def self.sign(request, hash)
-
42
parts = []
-
-
42
hash.keys.sort.each do |key|
-
54
parts << "#{key}=#{u(hash[key])}"
-
end
-
-
42
signature = new(request).signature(hash)
-
42
parts << "hash=#{u(signature)}"
-
-
42
parts.join("&")
-
end
-
-
1
attr_reader :request
-
-
1
def initialize(request)
-
56
@request = request
-
end
-
-
1
def secret_key
-
78
@request.env['insight.secret_key']
-
end
-
-
1
def secret_key_blank?
-
14
secret_key.nil? || secret_key == ""
-
end
-
-
1
def validate!
-
14
if secret_key_blank?
-
4
raise SecurityError.new("Missing secret key")
-
10
elsif request.params["hash"] != signature(request.params)
-
2
raise SecurityError.new("Invalid query hash.")
-
end
-
end
-
-
1
def signature(params)
-
52
Digest::SHA1.hexdigest(signature_base(params))
-
end
-
-
1
def signature_base(params)
-
52
signature = []
-
52
signature << secret_key
-
-
52
params.keys.sort.each do |key|
-
80
next if key == "hash"
-
70
signature << params[key].to_s
-
end
-
-
52
signature.join(":")
-
end
-
-
end
-
-
end
-
1
require 'insight/logger'
-
-
1
module Insight
-
1
class PathFilter
-
1
include Logging
-
1
def initialize(app)
-
86
@app = app
-
end
-
-
1
def call(env)
-
71
filters = env['insight.path_filters'].map do |string|
-
71
%r{^#{string}}
-
end
-
-
142
unless filter = filters.find{|regex| regex =~ env['PATH_INFO']}
-
71
return [404, {}, []]
-
end
-
-
logger.debug{ "Shortcutting collection stack: #{filter} =~ #{env['PATH_INFO']}"}
-
return @app.call(env)
-
end
-
end
-
end
-
1
module Insight
-
1
class RackStaticBugAvoider
-
1
def initialize(app, static_app)
-
@app = app
-
@static_app = static_app
-
end
-
-
1
def call(env)
-
if env["PATH_INFO"]
-
@static_app.call(env)
-
else
-
@app.call(env)
-
end
-
end
-
end
-
end
-
1
module Insight
-
1
class RedirectInterceptor
-
1
include Render
-
-
1
def initialize(app)
-
86
@app = app
-
end
-
-
1
def call(env)
-
65
status, headers, body = @app.call(env)
-
65
@response = Rack::Response.new(body, status, headers)
-
65
if @response.redirect? && env["insight.intercept_redirects"]
-
3
intercept_redirect
-
end
-
65
@response.to_a
-
end
-
-
1
def intercept_redirect
-
3
new_body = render_template("redirect", :redirect_to => @response.location)
-
3
new_headers = { "Content-Type" => "text/html", "Content-Length" => new_body.size.to_s }
-
3
@response = Rack::Response.new(new_body, 200, new_headers)
-
end
-
-
end
-
end
-
1
require 'erb'
-
-
1
module Insight
-
1
module Render
-
1
include ERB::Util
-
1
include Logging
-
-
1
def signed_params(hash)
-
42
ParamsSignature.sign(request, hash)
-
end
-
-
1
module CompiledTemplates
-
end
-
1
include CompiledTemplates
-
-
1
def render_template(filename, local_assigns = {})
-
578
compile(filename, local_assigns)
-
578
render_symbol = method_name(filename, local_assigns)
-
578
send(render_symbol, local_assigns)
-
end
-
-
1
def compile(filename, local_assigns)
-
578
render_symbol = method_name(filename, local_assigns)
-
-
578
if !CompiledTemplates.instance_methods.include?(render_symbol.to_s)
-
578
compile!(filename, local_assigns)
-
end
-
end
-
-
1
def compile!(filename, local_assigns)
-
578
render_symbol = method_name(filename, local_assigns)
-
1489
locals_code = local_assigns.keys.map { |key| "#{key} = local_assigns[:#{key}];" }.join
-
-
578
source = <<-end_src
-
def #{render_symbol}(local_assigns)
-
#{locals_code}
-
#{compiled_source(filename)}
-
end
-
end_src
-
-
578
begin
-
578
CompiledTemplates.module_eval(source, filename, 0)
-
rescue Object => ex
-
logger.debug do
-
"#{ex.class.name}: #{ex.message} in\n" +
-
source +
-
ex.backtrace.join("\n")
-
end
-
end
-
end
-
-
1
def compiled_source(filename)
-
578
::ERB.new(::File.read(::File.dirname(__FILE__) + "/views/#{filename}.html.erb"), nil, "-").src
-
end
-
-
1
def method_name(filename, local_assigns)
-
1734
if local_assigns && local_assigns.any?
-
1716
method_name = method_name_without_locals(filename).dup
-
4449
method_name << "_locals_#{local_assigns.keys.map { |k| k.to_s }.sort.join('_')}"
-
else
-
18
method_name = method_name_without_locals(filename)
-
end
-
1734
method_name.to_sym
-
end
-
-
1
def method_name_without_locals(filename)
-
1734
filename.split("/").join("_").tr("-", "_")
-
end
-
-
end
-
-
end
-
1
require 'insight'
-
1
require 'insight/database'
-
-
1
module Insight
-
1
class RequestRecorder
-
1
def initialize(app)
-
86
@app = app
-
86
@request_table = Database::RequestTable.new()
-
end
-
-
1
def call(env)
-
71
env["insight.request-id"] =
-
@request_table.store(env["REQUEST_METHOD"],
-
env["PATH_INFO"])
-
-
71
results = @app.call(env)
-
-
71
@request_table.sweep
-
-
71
return results
-
end
-
end
-
end
-
1
module Insight
-
1
class Toolbar
-
1
include Render
-
1
include Logging
-
-
1
MIME_TYPES = ["text/html", "application/xhtml+xml"]
-
-
1
def initialize(app, insight)
-
86
@app = app
-
86
@insight = insight
-
86
@request_table = Database::RequestTable.new
-
end
-
-
1
def call(env)
-
80
@env = env
-
80
status, headers, body = @app.call(@env)
-
-
74
response = Rack::Response.new(body, status, headers)
-
-
74
if okay_to_modify?(env, response)
-
66
inject_toolbar(response)
-
end
-
-
74
return response.to_a
-
end
-
-
1
def okay_to_modify?(env, response)
-
74
req = Rack::Request.new(env)
-
74
content_type, charset = response.content_type.split(";")
-
-
74
response.ok? && MIME_TYPES.include?(content_type) && !req.xhr?
-
end
-
-
1
def inject_toolbar(response)
-
66
full_body = response.body.join
-
66
full_body.sub! /<\/body>/, render + "</body>"
-
-
66
response["Content-Length"] = full_body.size.to_s
-
-
# Ensure that browser doesn't cache
-
66
response["Etag"] = ""
-
66
response["Cache-Control"] = "no-cache"
-
-
66
response.body = [full_body]
-
end
-
-
1
def render
-
66
req_id = (@env['insight.request-id'] || @request_table.last_request_id).to_i
-
66
requests = @request_table.to_a.map do |row|
-
71
{ :id => row[0], :method => row[1], :path => row[2] }
-
end
-
-
598
logger.info{ "Injecting toolbar: active panels: #{@insight.panels.map{|pnl| pnl.class.name}.inspect}" }
-
-
66
headers_fragment = render_template("headers_fragment",
-
:panels => @insight.panels,
-
:request_id => req_id)
-
-
66
current_request_fragment = render_template("request_fragment",
-
:request_id => req_id,
-
:requests => requests,
-
:panels => @insight.panels)
-
66
render_template("toolbar",
-
:request_fragment => current_request_fragment,
-
:headers_fragment => headers_fragment,
-
:request_id => req_id)
-
end
-
end
-
end
-
1
require 'insight'