class Rack::Rreplay
Public Class Methods
Middleware(directory:, format: :msgpack, logger: nil)
click to toggle source
sample¶ ↑
use Rack::Rreplay
.Middleware(directory: './tmp', format: :json),
sample: 5, extra_header_keys: %w[ACCESS_TOKEN], debug: true
@param directory [String] rreplay dump file directory, and if nil, use logger as debug @param logger [IO] if directory is nil, logger can be given
# File lib/rack/rreplay.rb, line 21 def Middleware(directory:, format: :msgpack, logger: nil) if directory.nil? && logger.nil? raise "Invalid arguments. directory: or logger: must be given", ArgumentError end format = ::Rreplay::Format.of(format) if directory ::FileUtils.mkdir_p(directory) logger = ::Logger::LogDevice.new( ::File.join(directory, ::Rreplay::LOG_FILE_NAME_PREFIX + format.file_suffix), shift_age: 10, shift_size: 1048576, binmode: format.is_binary? ) end class_definition(logger, format) end
call(env)
click to toggle source
# File lib/rack/rreplay.rb, line 55 def call(env) start_time = Time.now @app.call(env).tap do |res| write(start_time, env, res) end end
extra_header(env, key)
click to toggle source
# File lib/rack/rreplay.rb, line 128 def extra_header(env, key) { key => env[key] || env["HTTP_#{key}"] || env[key.upcase] || env[key.upcase.gsub('-', '_')] || env["HTTP_#{key.upcase}"] || env["HTTP_#{key.upcase.gsub('-', '_')}"] } end
initialize(app, **kwargs)
click to toggle source
@params kwargs [Integer] output sample (if 10, output a log once every 10 requests) @params kwargs [Array] more header keys @params kwargs :msgpack | :json @params kwargs if true, output debugging logs to stderr
# File lib/rack/rreplay.rb, line 48 def initialize(app, **kwargs) @app = app @debugger = ::Rreplay::Debugger.new($stderr, kwargs[:debug] || false) @sample = kwargs[:sample] || 10 @extra_header_keys = kwargs[:extra_header_keys] || [] end
request_hash(env)
click to toggle source
# File lib/rack/rreplay.rb, line 109 def request_hash(env) headers = { 'content-type' => env['CONTENT_TYPE'], 'cookie' => env['HTTP_COOKIE'], 'user-agent' => env['HTTP_USER_AGENT'], } @extra_header_keys.each do |key| headers.merge!(extra_header(env, key)) end { 'method' => env['REQUEST_METHOD'], 'path' => env['PATH_INFO'], 'body' => env['rack.input'].gets, 'query_strings' => env['QUERY_STRING'].empty? ? '' : '?' + env['QUERY_STRING'], 'headers' => headers } end
response_body(body)
click to toggle source
# File lib/rack/rreplay.rb, line 101 def response_body(body) return body unless body.respond_to?(:each) [].tap do |b| body.each { |content| b << content } end.join('') end
response_hash(res)
click to toggle source
# File lib/rack/rreplay.rb, line 92 def response_hash(res) status, headers, body = res { 'status' => status, 'headers' => headers, 'body' => response_body(body), } end
serialize(start_time, env, res)
click to toggle source
# File lib/rack/rreplay.rb, line 78 def serialize(start_time, env, res) uuid = SecureRandom.uuid end_time = Time.now hash = { 'uuid' => uuid, 'time' => end_time.iso8601, 'response_time' => (end_time - start_time).to_s, 'request' => request_hash(env), 'response' => response_hash(res) } @@format.serializer.call(hash) end
write(start_time, env, res)
click to toggle source
# File lib/rack/rreplay.rb, line 64 def write(start_time, env, res) payload = nil if (@@counter % @sample).zero? payload = serialize(start_time, env, res) @@logger.write(payload + "\n") end @@counter += 1 @debugger.out do payload ||= serialize(start_time, env, res) "[Rreplay DEBUG]#{Time.now}: counter: #{@@counter}, sample: #{@sample}, payload: #{payload}" end end
Private Class Methods
class_definition(logger, format)
click to toggle source
# File lib/rack/rreplay.rb, line 38 def class_definition(logger, format) Class.new do @@counter = 0 @@logger = logger @@format = format # @params kwargs[:sample] [Integer] output sample (if 10, output a log once every 10 requests) # @params kwargs[:extra_header_keys] [Array[String]] more header keys # @params kwargs[:format] :msgpack | :json # @params kwargs[:debug] if true, output debugging logs to stderr def initialize(app, **kwargs) @app = app @debugger = ::Rreplay::Debugger.new($stderr, kwargs[:debug] || false) @sample = kwargs[:sample] || 10 @extra_header_keys = kwargs[:extra_header_keys] || [] end def call(env) start_time = Time.now @app.call(env).tap do |res| write(start_time, env, res) end end private def write(start_time, env, res) payload = nil if (@@counter % @sample).zero? payload = serialize(start_time, env, res) @@logger.write(payload + "\n") end @@counter += 1 @debugger.out do payload ||= serialize(start_time, env, res) "[Rreplay DEBUG]#{Time.now}: counter: #{@@counter}, sample: #{@sample}, payload: #{payload}" end end def serialize(start_time, env, res) uuid = SecureRandom.uuid end_time = Time.now hash = { 'uuid' => uuid, 'time' => end_time.iso8601, 'response_time' => (end_time - start_time).to_s, 'request' => request_hash(env), 'response' => response_hash(res) } @@format.serializer.call(hash) end def response_hash(res) status, headers, body = res { 'status' => status, 'headers' => headers, 'body' => response_body(body), } end def response_body(body) return body unless body.respond_to?(:each) [].tap do |b| body.each { |content| b << content } end.join('') end def request_hash(env) headers = { 'content-type' => env['CONTENT_TYPE'], 'cookie' => env['HTTP_COOKIE'], 'user-agent' => env['HTTP_USER_AGENT'], } @extra_header_keys.each do |key| headers.merge!(extra_header(env, key)) end { 'method' => env['REQUEST_METHOD'], 'path' => env['PATH_INFO'], 'body' => env['rack.input'].gets, 'query_strings' => env['QUERY_STRING'].empty? ? '' : '?' + env['QUERY_STRING'], 'headers' => headers } end def extra_header(env, key) { key => env[key] || env["HTTP_#{key}"] || env[key.upcase] || env[key.upcase.gsub('-', '_')] || env["HTTP_#{key.upcase}"] || env["HTTP_#{key.upcase.gsub('-', '_')}"] } end end end