request_recorder 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Gemfile +2 -0
- data/Gemfile.lock +6 -2
- data/Readme.md +12 -5
- data/lib/request_recorder/middleware.rb +19 -15
- data/lib/request_recorder/redis_logger.rb +16 -0
- data/lib/request_recorder/version.rb +1 -1
- data/lib/request_recorder.rb +1 -2
- data/request_recorder.gemspec +0 -1
- data/spec/request_recorder_spec.rb +67 -21
- data/spec/spec_helper.rb +3 -3
- metadata +4 -22
- data/MIGRATION +0 -12
- data/lib/request_recorder/log.rb +0 -7
- data/lib/request_recorder/recorder.rb +0 -13
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
request_recorder (0.0.
|
5
|
-
activerecord (= 2.3.14)
|
4
|
+
request_recorder (0.0.2)
|
6
5
|
rack
|
7
6
|
|
8
7
|
GEM
|
@@ -12,8 +11,11 @@ GEM
|
|
12
11
|
activesupport (= 2.3.14)
|
13
12
|
activesupport (2.3.14)
|
14
13
|
diff-lcs (1.1.3)
|
14
|
+
fakeredis (0.4.0)
|
15
|
+
redis (~> 3.0.0)
|
15
16
|
rack (1.4.1)
|
16
17
|
rake (0.9.2)
|
18
|
+
redis (3.0.1)
|
17
19
|
rspec (2.6.0)
|
18
20
|
rspec-core (~> 2.6.0)
|
19
21
|
rspec-expectations (~> 2.6.0)
|
@@ -28,6 +30,8 @@ PLATFORMS
|
|
28
30
|
ruby
|
29
31
|
|
30
32
|
DEPENDENCIES
|
33
|
+
activerecord (= 2.3.14)
|
34
|
+
fakeredis
|
31
35
|
rake
|
32
36
|
request_recorder!
|
33
37
|
rspec (~> 2)
|
data/Readme.md
CHANGED
@@ -4,15 +4,22 @@ Install
|
|
4
4
|
=======
|
5
5
|
|
6
6
|
gem install request_recorder
|
7
|
-
|
7
|
+
|
8
|
+
Add to your middleware stack:
|
9
|
+
|
10
|
+
require "request_recorder"
|
11
|
+
use RequestRecorder::Middleware, :store => RequestRecorder::RedisLogger.new(Redis.new)
|
12
|
+
|
13
|
+
### No Redis, No problem
|
14
|
+
If you do not have redis, you can write your own logger, you only need a .write method,
|
15
|
+
see [RedisLogger](https://github.com/grosser/request_recorder/blob/master/lib/request_recorder/redis_logger.rb)
|
8
16
|
|
9
17
|
Usage
|
10
18
|
=====
|
11
19
|
|
12
|
-
-
|
13
|
-
-
|
14
|
-
-
|
15
|
-
- go to the database or build a nice frontend for RequestRecorded::Requests
|
20
|
+
- request a page with `/something?__request_recording=10` -> record next 10 requests from my browser
|
21
|
+
- redis 'request_recorder' gets a new entry with all the logging info from rails + activerecord
|
22
|
+
- go to redis or build a nice frontend
|
16
23
|
|
17
24
|
Author
|
18
25
|
======
|
@@ -1,3 +1,4 @@
|
|
1
|
+
require "stringio"
|
1
2
|
require "rack/request"
|
2
3
|
require "rack/response"
|
3
4
|
|
@@ -8,6 +9,7 @@ module RequestRecorder
|
|
8
9
|
|
9
10
|
def initialize(app, options={})
|
10
11
|
@app = app
|
12
|
+
@store = options.fetch(:store)
|
11
13
|
end
|
12
14
|
|
13
15
|
def call(env)
|
@@ -17,29 +19,28 @@ module RequestRecorder
|
|
17
19
|
(env["HTTP_COOKIE"] && env["HTTP_COOKIE"].include?(MARKER))
|
18
20
|
)
|
19
21
|
|
20
|
-
result =
|
21
|
-
|
22
|
+
result = nil
|
23
|
+
log = capture_logging do
|
24
|
+
result = @app.call(env)
|
22
25
|
end
|
23
26
|
|
24
27
|
steps_left, id = read_state_from_env(env)
|
25
28
|
return [500, {}, "__request_recording exceeded maximum value #{MAX_STEPS}"] if steps_left > MAX_STEPS
|
26
|
-
id = persist_log(id,
|
29
|
+
id = persist_log(id, log)
|
27
30
|
response_with_data_in_cookie(result, steps_left, id)
|
28
31
|
end
|
29
32
|
|
30
33
|
private
|
31
34
|
|
32
35
|
def persist_log(id, log)
|
33
|
-
|
34
|
-
record.log += log.join("\n") + "\n\n"
|
35
|
-
record.save!
|
36
|
-
record.id
|
36
|
+
@store.write(id, log)
|
37
37
|
end
|
38
38
|
|
39
39
|
def read_state_from_env(env)
|
40
40
|
request = Rack::Request.new(env)
|
41
41
|
if request.cookies[MARKER]
|
42
|
-
request.cookies[MARKER].split(":")
|
42
|
+
steps, id = request.cookies[MARKER].split(":")
|
43
|
+
[steps.to_i, id]
|
43
44
|
else
|
44
45
|
[env["QUERY_STRING"][/#{MARKER}=(\d+)/, 1].to_i, nil]
|
45
46
|
end
|
@@ -51,27 +52,30 @@ module RequestRecorder
|
|
51
52
|
if to_go <= 1
|
52
53
|
response.delete_cookie(MARKER)
|
53
54
|
else
|
54
|
-
response.set_cookie(MARKER, {:value => "#{to_go.to_i - 1}:#{id}", :
|
55
|
+
response.set_cookie(MARKER, {:value => "#{to_go.to_i - 1}:#{id}", :expires => Time.now+24*60*60, :httponly => true})
|
55
56
|
end
|
56
57
|
|
57
58
|
response.finish # finish writes out the response in the expected format.
|
58
59
|
end
|
59
60
|
|
60
|
-
def
|
61
|
-
|
61
|
+
def capture_logging
|
62
|
+
recorder = StringIO.new
|
62
63
|
old = [
|
63
64
|
ActiveRecord::Base.logger.instance_variable_get("@log"),
|
64
65
|
ActiveRecord::Base.logger.auto_flushing,
|
65
66
|
ActiveRecord::Base.logger.level
|
66
67
|
]
|
67
|
-
ActiveRecord::Base.logger.instance_variable_set("@log",
|
68
|
+
ActiveRecord::Base.logger.instance_variable_set("@log", recorder)
|
68
69
|
ActiveRecord::Base.logger.auto_flushing = true
|
69
70
|
ActiveRecord::Base.logger.level = Logger::DEBUG
|
70
71
|
yield
|
72
|
+
recorder.string
|
71
73
|
ensure
|
72
|
-
|
73
|
-
|
74
|
-
|
74
|
+
if old
|
75
|
+
ActiveRecord::Base.logger.instance_variable_set("@log", old[0])
|
76
|
+
ActiveRecord::Base.logger.auto_flushing = old[1]
|
77
|
+
ActiveRecord::Base.logger.level = old[2]
|
78
|
+
end
|
75
79
|
end
|
76
80
|
end
|
77
81
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module RequestRecorder
|
2
|
+
class RedisLogger
|
3
|
+
KEY = "request_recorder"
|
4
|
+
|
5
|
+
def initialize(redis)
|
6
|
+
@redis = redis
|
7
|
+
end
|
8
|
+
|
9
|
+
def write(id, text)
|
10
|
+
old = (id ? @redis.hget(KEY, id) : "")
|
11
|
+
id = "#{Time.now.to_i}_#{Process.pid}" unless id
|
12
|
+
@redis.hset(KEY, id, old.to_s + text)
|
13
|
+
id
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/request_recorder.rb
CHANGED
data/request_recorder.gemspec
CHANGED
@@ -7,7 +7,10 @@ describe RequestRecorder do
|
|
7
7
|
Car.first
|
8
8
|
[200, {}, "assadasd"]
|
9
9
|
} }
|
10
|
-
let(:
|
10
|
+
let(:middleware){ RequestRecorder::Middleware.new(inner_app, :store => RequestRecorder::RedisLogger.new(redis)) }
|
11
|
+
let(:redis){ FakeRedis::Redis.new }
|
12
|
+
let(:redis_key){ RequestRecorder::RedisLogger::KEY }
|
13
|
+
let(:existing_request_id){ redis.hset(redis_key, "123_456", "BEFORE") ; "123_456"}
|
11
14
|
|
12
15
|
before do
|
13
16
|
ActiveRecord::Base.logger.instance_variable_set("@log", original_logger)
|
@@ -16,7 +19,7 @@ describe RequestRecorder do
|
|
16
19
|
end
|
17
20
|
|
18
21
|
after do
|
19
|
-
|
22
|
+
redis.flushall
|
20
23
|
end
|
21
24
|
|
22
25
|
it "has a VERSION" do
|
@@ -24,13 +27,11 @@ describe RequestRecorder do
|
|
24
27
|
end
|
25
28
|
|
26
29
|
it "records activerecord queries" do
|
27
|
-
middleware = RequestRecorder::Middleware.new(inner_app)
|
28
30
|
middleware.call(activate_logger)
|
29
|
-
|
31
|
+
stored.values.last.should include "SELECT"
|
30
32
|
end
|
31
33
|
|
32
34
|
it "blows up if you go over the maximum" do
|
33
|
-
middleware = RequestRecorder::Middleware.new(inner_app)
|
34
35
|
status, headers, body = middleware.call("QUERY_STRING" => "__request_recording=99999")
|
35
36
|
status.should == 500
|
36
37
|
body.should include "maximum"
|
@@ -38,47 +39,92 @@ describe RequestRecorder do
|
|
38
39
|
|
39
40
|
context "subsequent requests" do
|
40
41
|
it "sets cookie in first step" do
|
41
|
-
middleware = RequestRecorder::Middleware.new(inner_app)
|
42
42
|
status, headers, body = middleware.call(activate_logger)
|
43
|
-
|
44
|
-
headers["Set-Cookie"].should include "__request_recording=9%3A#{
|
43
|
+
generated_id = stored.keys.last
|
44
|
+
headers["Set-Cookie"].should include "__request_recording=9%3A#{generated_id}; expires="
|
45
|
+
headers["Set-Cookie"].should include "; HttpOnly"
|
45
46
|
end
|
46
47
|
|
47
48
|
it "appends to existing log" do
|
48
|
-
middleware =
|
49
|
-
|
50
|
-
existing_request.
|
51
|
-
existing_request.
|
52
|
-
|
49
|
+
middleware.call("HTTP_COOKIE" => "__request_recording=8:#{existing_request_id}")
|
50
|
+
existing_request = redis.hget(redis_key, existing_request_id)
|
51
|
+
existing_request.should include "SELECT"
|
52
|
+
existing_request.should include "BEFORE"
|
53
|
+
end
|
54
|
+
|
55
|
+
it "creates a new log if redis dies" do
|
56
|
+
existing_request_id # store key
|
57
|
+
redis.flushall
|
58
|
+
middleware.call("HTTP_COOKIE" => "__request_recording=8:#{existing_request_id}")
|
59
|
+
existing_request = redis.hget(redis_key, existing_request_id)
|
60
|
+
existing_request.should include "SELECT"
|
61
|
+
existing_request.should_not include "BEFORE"
|
53
62
|
end
|
54
63
|
|
55
64
|
it "decrements cookie on each step" do
|
56
|
-
|
57
|
-
|
58
|
-
headers["Set-Cookie"].should include "__request_recording=1%3A#{existing_request.id}; path=/; expires="
|
65
|
+
status, headers, body = middleware.call("HTTP_COOKIE" => "__request_recording=2:#{existing_request_id};foo=bar")
|
66
|
+
headers["Set-Cookie"].should include "__request_recording=1%3A#{existing_request_id}; expires="
|
59
67
|
end
|
60
68
|
|
61
69
|
it "removes cookie if final step is reached" do
|
62
|
-
|
63
|
-
status, headers, body = middleware.call("HTTP_COOKIE" => "__request_recording=1:#{existing_request.id};foo=bar")
|
70
|
+
status, headers, body = middleware.call("HTTP_COOKIE" => "__request_recording=1:#{existing_request_id};foo=bar")
|
64
71
|
headers["Set-Cookie"].should include "__request_recording=; expires="
|
65
72
|
end
|
66
73
|
end
|
67
74
|
|
68
75
|
it "should not record if __request_recording is not given" do
|
69
|
-
middleware = RequestRecorder::Middleware.new(inner_app)
|
70
76
|
middleware.call(
|
71
77
|
"QUERY_STRING" => "stuff=hello", "HTTP_COOKIE" => "bar=foo"
|
72
78
|
)
|
73
|
-
|
79
|
+
stored.count.should == 0
|
74
80
|
end
|
75
81
|
|
76
82
|
it "restores the AR logger after executing" do
|
77
|
-
middleware = RequestRecorder::Middleware.new(inner_app)
|
78
83
|
middleware.call(activate_logger)
|
79
84
|
|
80
85
|
ActiveRecord::Base.logger.instance_variable_get("@log").object_id.should == original_logger.object_id
|
81
86
|
ActiveRecord::Base.logger.auto_flushing.should == 1000
|
82
87
|
ActiveRecord::Base.logger.level.should == Logger::ERROR
|
83
88
|
end
|
89
|
+
|
90
|
+
it "fails with a nice message if logging_to_recorded blows up" do
|
91
|
+
StringIO.should_receive(:new).and_raise("Oooops")
|
92
|
+
expect{
|
93
|
+
middleware.call(activate_logger)
|
94
|
+
}.to raise_error "Oooops"
|
95
|
+
end
|
96
|
+
|
97
|
+
it "integrates" do
|
98
|
+
stored.size.should == 0
|
99
|
+
|
100
|
+
# request 1 - start + decrement + log
|
101
|
+
status, headers, body = middleware.call({"QUERY_STRING" => "__request_recording=3"})
|
102
|
+
stored.size.should == 1
|
103
|
+
stored.values.last.scan("SELECT").size.should == 1
|
104
|
+
cookie = headers["Set-Cookie"].split(";").first
|
105
|
+
|
106
|
+
# request 2 - decrement + log
|
107
|
+
status, headers, body = middleware.call({"HTTP_COOKIE" => cookie})
|
108
|
+
stored.size.should == 1
|
109
|
+
stored.values.last.scan("SELECT").size.should == 2
|
110
|
+
cookie = headers["Set-Cookie"].split(";").first
|
111
|
+
|
112
|
+
# request 3 - remove cookie + log
|
113
|
+
status, headers, body = middleware.call({"HTTP_COOKIE" => cookie})
|
114
|
+
stored.size.should == 1
|
115
|
+
stored.values.last.scan("SELECT").size.should == 3
|
116
|
+
cookie = headers["Set-Cookie"].split(";").first
|
117
|
+
cookie.should == "__request_recording="
|
118
|
+
|
119
|
+
# request 4 - no more logging
|
120
|
+
status, headers, body = middleware.call({})
|
121
|
+
stored.size.should == 1
|
122
|
+
stored.values.last.scan("SELECT").size.should == 3
|
123
|
+
end
|
124
|
+
|
125
|
+
private
|
126
|
+
|
127
|
+
def stored
|
128
|
+
redis.hgetall(redis_key)
|
129
|
+
end
|
84
130
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
require "request_recorder"
|
2
2
|
|
3
|
+
require "active_record"
|
4
|
+
require "fakeredis"
|
5
|
+
|
3
6
|
ActiveRecord::Base.logger = ActiveSupport::BufferedLogger.new("/dev/null")
|
4
7
|
|
5
8
|
# connect
|
@@ -11,9 +14,6 @@ ActiveRecord::Base.establish_connection(
|
|
11
14
|
# create tables
|
12
15
|
ActiveRecord::Schema.verbose = false
|
13
16
|
ActiveRecord::Schema.define(:version => 1) do
|
14
|
-
eval(File.read(File.expand_path("../../MIGRATION", __FILE__)))
|
15
|
-
AddRecordedRequests.up
|
16
|
-
|
17
17
|
create_table :cars do |t|
|
18
18
|
t.timestamps
|
19
19
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: request_recorder
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -11,22 +11,6 @@ bindir: bin
|
|
11
11
|
cert_chain: []
|
12
12
|
date: 2012-10-19 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
|
-
- !ruby/object:Gem::Dependency
|
15
|
-
name: activerecord
|
16
|
-
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
|
-
requirements:
|
19
|
-
- - '='
|
20
|
-
- !ruby/object:Gem::Version
|
21
|
-
version: 2.3.14
|
22
|
-
type: :runtime
|
23
|
-
prerelease: false
|
24
|
-
version_requirements: !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
|
-
requirements:
|
27
|
-
- - '='
|
28
|
-
- !ruby/object:Gem::Version
|
29
|
-
version: 2.3.14
|
30
14
|
- !ruby/object:Gem::Dependency
|
31
15
|
name: rack
|
32
16
|
requirement: !ruby/object:Gem::Requirement
|
@@ -52,13 +36,11 @@ files:
|
|
52
36
|
- .travis.yml
|
53
37
|
- Gemfile
|
54
38
|
- Gemfile.lock
|
55
|
-
- MIGRATION
|
56
39
|
- Rakefile
|
57
40
|
- Readme.md
|
58
41
|
- lib/request_recorder.rb
|
59
|
-
- lib/request_recorder/log.rb
|
60
42
|
- lib/request_recorder/middleware.rb
|
61
|
-
- lib/request_recorder/
|
43
|
+
- lib/request_recorder/redis_logger.rb
|
62
44
|
- lib/request_recorder/version.rb
|
63
45
|
- request_recorder.gemspec
|
64
46
|
- spec/request_recorder_spec.rb
|
@@ -78,7 +60,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
78
60
|
version: '0'
|
79
61
|
segments:
|
80
62
|
- 0
|
81
|
-
hash: -
|
63
|
+
hash: -3099537396771962597
|
82
64
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
83
65
|
none: false
|
84
66
|
requirements:
|
@@ -87,7 +69,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
87
69
|
version: '0'
|
88
70
|
segments:
|
89
71
|
- 0
|
90
|
-
hash: -
|
72
|
+
hash: -3099537396771962597
|
91
73
|
requirements: []
|
92
74
|
rubyforge_project:
|
93
75
|
rubygems_version: 1.8.24
|
data/MIGRATION
DELETED
data/lib/request_recorder/log.rb
DELETED