class RequestRecorder::Middleware
Constants
- AUTH
- MARKER
- MAX_STEPS
- SEPARATOR
Public Class Methods
new(app, options={})
click to toggle source
# File lib/request_recorder/middleware.rb, line 19 def initialize(app, options={}) @app = app @store = options.fetch(:store) @auth = options[AUTH] @headers = options[:headers] || {} end
Public Instance Methods
call(env)
click to toggle source
# File lib/request_recorder/middleware.rb, line 26 def call(env) # keep this part as fast as possible, since 99.99999% of requests will not need it return @app.call(env) unless "#{env["PATH_INFO"]}-#{env["QUERY_STRING"]}-#{env["HTTP_COOKIE"]}" =~ /#{MARKER}[\/=]/ if env["PATH_INFO"].to_s.starts_with?("/#{MARKER}/") key = env["PATH_INFO"].split("/")[2] render_frontend(env, key) else result = nil log = capture_logging do begin result = @app.call(env) rescue Exception => e result = e end end steps_left, id = read_state_from_env(env) return [500, {}, ["#{MARKER} exceeded maximum value #{MAX_STEPS}"]] if steps_left > MAX_STEPS id = persist_log(id, log) if result.is_a?(Exception) raise result # Do not mess up the apps normal exception behavior else path = [env["PATH_INFO"], env["QUERY_STRING"]].compact.join("?") if @auth && auth = @auth.call(env) extra_headers = chrome_logger_headers(log, path) unless auth.is_a?(Array) end response_with_data_in_cookie(result, steps_left, id, extra_headers) end end end
Private Instance Methods
capture_logging(&block)
click to toggle source
# File lib/request_recorder/middleware.rb, line 162 def capture_logging(&block) logger = ActiveRecord::Base.logger recorder = StringIO.new debug_level(logger){ logger.subscribe(Logger.new(recorder), &block) } recorder.string end
chrome_logger_headers(log, path)
click to toggle source
# File lib/request_recorder/middleware.rb, line 104 def chrome_logger_headers(log, path) log = log.split("\n").map { |line| remove_console_colors(line) } log = reduce_header_size(log) if @headers # fake chrome-logger format rows = [] rows << [["Rails log #{path}"],"xxx.rb:1","group"] rows.concat log.map{|line| [line.split(" "), "xxx.rb:1", ""] } rows << [[], "xxx.rb:1", "groupEnd"] data = { 'version' => "0.1.1", 'columns' => [ 'log' , 'backtrace' , 'type' ], 'rows' => rows } # encode data = MultiJson.dump(data) data = data.encode("UTF-8") if defined?(Encoding) data = Base64.encode64(data).gsub("\n", "") {"X-ChromeLogger-Data" => data} end
debug_level(logger) { || ... }
click to toggle source
# File lib/request_recorder/middleware.rb, line 169 def debug_level(logger) old, logger.level = logger.level, Logger::DEBUG yield ensure logger.level = old end
persist_log(id, log)
click to toggle source
# File lib/request_recorder/middleware.rb, line 81 def persist_log(id, log) @store.write(id, log) end
read_state_from_env(env)
click to toggle source
# File lib/request_recorder/middleware.rb, line 85 def read_state_from_env(env) request = Rack::Request.new(env) value = request.cookies[MARKER] || env["QUERY_STRING"][/#{MARKER}=([^&]+)/, 1] steps, id = value.split(SEPARATOR) [steps.to_i, id] end
reduce_header_size(array)
click to toggle source
# File lib/request_recorder/middleware.rb, line 129 def reduce_header_size(array) size = @headers.fetch(:max) return array if array.sum(&:size) <= size size -= 20 # make room for removed message removed_count = 0 removed_match = [] unimportant = (@headers[:remove] || []).dup while array.sum(&:size) > size if remove = unimportant.shift removed_match << remove array.reject! { |line| line =~ remove } else removed_count += 1 array.shift end end # tell user what was removed message = [] message << "all #{removed_match.map(&:inspect).join(", ")}" if removed_match.any? message << "#{removed_count} lines" if removed_count > 0 array << "Removed: #{message.join(", ")}" array end
remove_console_colors(string)
click to toggle source
# File lib/request_recorder/middleware.rb, line 158 def remove_console_colors(string) string.gsub(/\e\[[\d;]+m/, "") end
render_frontend(env, key)
click to toggle source
# File lib/request_recorder/middleware.rb, line 61 def render_frontend(env, key) if @auth if response = @auth.call(env) if response.is_a?(Array) response else if log = @store.read(key) [200, {}, [Frontend.render(log)]] else [404, {}, ["Did not find a log for key #{key}"]] end end else [401, {}, ["Unauthorized"]] end else [500, {}, ["you need to provide #{AUTH.inspect} option"]] end end