httplog 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +27 -6
- data/lib/httplog/http_log.rb +107 -54
- data/lib/httplog/version.rb +2 -1
- metadata +1 -1
data/README.rdoc
CHANGED
@@ -4,6 +4,18 @@ Log outgoing HTTP requests made from your application.
|
|
4
4
|
See the {blog post}[http://trusche.github.com/blog/2011/09/29/logging-outgoing-http-requests/]
|
5
5
|
for more details.
|
6
6
|
|
7
|
+
So far this gem works with the following HTTP libraries:
|
8
|
+
|
9
|
+
* Net::HTTP
|
10
|
+
* OpenURI
|
11
|
+
* HTTPClient
|
12
|
+
|
13
|
+
In theory, it should also work with any library built on top of these. But since
|
14
|
+
the difference between theory and practice is bigger in practice than in theory, YMMV.
|
15
|
+
|
16
|
+
*PLEASE NOTE* that this is very much a development tool; it is *not recommended* to
|
17
|
+
use this in a production environment.
|
18
|
+
|
7
19
|
=== Installation
|
8
20
|
|
9
21
|
gem install httplog
|
@@ -27,7 +39,7 @@ You can override the following default options:
|
|
27
39
|
HttpLog.options[:log_status] = true
|
28
40
|
HttpLog.options[:log_response] = true
|
29
41
|
HttpLog.options[:log_benchmark] = true
|
30
|
-
HttpLog.options[:compact_log] = false
|
42
|
+
HttpLog.options[:compact_log] = false # setting this to true will make all "log_*" options redundant
|
31
43
|
|
32
44
|
So if you want to use this in a Rails app:
|
33
45
|
|
@@ -55,8 +67,8 @@ With the default configuration, the log output might look like this:
|
|
55
67
|
|
56
68
|
=== Known Issues
|
57
69
|
|
58
|
-
When using open-uri, the reading of the HTTP response body is deferred,
|
59
|
-
so it is not available for logging. This will be noted in the logging statement:
|
70
|
+
* When using open-uri, the reading of the HTTP response body is deferred,
|
71
|
+
so it is not available for logging. This will be noted in the logging statement:
|
60
72
|
|
61
73
|
D, [2012-11-21T15:09:03.547005 #6857] DEBUG -- : [httplog] Connecting: localhost
|
62
74
|
D, [2012-11-21T15:09:03.547938 #6857] DEBUG -- : [httplog] Sending: GET http://localhost:9292/index.html
|
@@ -64,11 +76,12 @@ so it is not available for logging. This will be noted in the logging statement:
|
|
64
76
|
D, [2012-11-21T15:09:03.548662 #6857] DEBUG -- : [httplog] Benchmark: 0.000617 seconds
|
65
77
|
D, [2012-11-21T15:09:03.548695 #6857] DEBUG -- : [httplog] Response: (not available yet)
|
66
78
|
|
79
|
+
* When using HTTPClient, the +:log_connect+ option has no effect.
|
67
80
|
|
68
81
|
=== Running the specs
|
69
82
|
|
70
|
-
Make sure you have the necessary dependencies installed by running
|
71
|
-
Then
|
83
|
+
Make sure you have the necessary dependencies installed by running +bundle install+.
|
84
|
+
Then simply run +bundle exec rspec spec+.
|
72
85
|
This will launch a simple rack server on port 9292 and run all tests locally against that server.
|
73
86
|
|
74
87
|
=== Contributing
|
@@ -77,4 +90,12 @@ If you have any issues with httplog,
|
|
77
90
|
or feature requests,
|
78
91
|
please {add an issue}[https://github.com/trusche/httplog/issues] on GitHub
|
79
92
|
or fork the project and send a pull request.
|
80
|
-
Please include passing specs with all pull requests.
|
93
|
+
Please include passing specs with all pull requests.
|
94
|
+
|
95
|
+
=== Contributors
|
96
|
+
|
97
|
+
Thanks to these fine folks for contributing pull requests:
|
98
|
+
|
99
|
+
* {Eric Cohen}[https://github.com/eirc]
|
100
|
+
* {Nikos Dimitrakopoulos}[https://github.com/nikosd]
|
101
|
+
* {Marcos Hack}[https://github.com/marcoshack]
|
data/lib/httplog/http_log.rb
CHANGED
@@ -1,80 +1,109 @@
|
|
1
1
|
require "net/http"
|
2
2
|
require "logger"
|
3
|
+
require "benchmark"
|
3
4
|
|
4
5
|
module HttpLog
|
6
|
+
DEFAULT_LOGGER = Logger.new($stdout)
|
7
|
+
DEFAULT_OPTIONS = {
|
8
|
+
:logger => DEFAULT_LOGGER,
|
9
|
+
:severity => Logger::Severity::DEBUG,
|
10
|
+
:log_connect => true,
|
11
|
+
:log_request => true,
|
12
|
+
:log_headers => false,
|
13
|
+
:log_data => true,
|
14
|
+
:log_status => true,
|
15
|
+
:log_response => true,
|
16
|
+
:log_benchmark => true,
|
17
|
+
:compact_log => false
|
18
|
+
}
|
19
|
+
|
20
|
+
class << self
|
21
|
+
def options
|
22
|
+
@@options ||= DEFAULT_OPTIONS.clone
|
23
|
+
end
|
5
24
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
:severity => Logger::Severity::DEBUG,
|
10
|
-
:log_connect => true,
|
11
|
-
:log_request => true,
|
12
|
-
:log_headers => false,
|
13
|
-
:log_data => true,
|
14
|
-
:log_status => true,
|
15
|
-
:log_response => true,
|
16
|
-
:log_benchmark => true,
|
17
|
-
:log_compact => false
|
18
|
-
}
|
19
|
-
end
|
25
|
+
def reset_options!
|
26
|
+
@@options = DEFAULT_OPTIONS.clone
|
27
|
+
end
|
20
28
|
|
21
|
-
|
22
|
-
|
23
|
-
|
29
|
+
def log(msg)
|
30
|
+
@@options[:logger].add(@@options[:severity]) { "[httplog] #{msg}" }
|
31
|
+
end
|
32
|
+
|
33
|
+
def log_request(method, uri)
|
34
|
+
return if options[:compact_log] || !options[:log_request]
|
35
|
+
log("Sending: #{method.to_s.upcase} #{uri}")
|
36
|
+
end
|
37
|
+
|
38
|
+
def log_headers(headers = {})
|
39
|
+
return if options[:compact_log] || !options[:log_headers]
|
40
|
+
headers.each do |key,value|
|
41
|
+
log("Header: #{key}: #{value}")
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def log_status(status)
|
46
|
+
return if options[:compact_log] || !options[:log_status]
|
47
|
+
log("Status: #{status}")
|
48
|
+
end
|
49
|
+
|
50
|
+
def log_benchmark(seconds)
|
51
|
+
return if options[:compact_log] || !options[:log_benchmark]
|
52
|
+
log("Benchmark: #{seconds} seconds")
|
53
|
+
end
|
54
|
+
|
55
|
+
def log_body(body)
|
56
|
+
return if options[:compact_log] || !options[:log_response]
|
57
|
+
if body.is_a?(Net::ReadAdapter)
|
58
|
+
# open-uri wraps the response in a Net::ReadAdapter that defers reading
|
59
|
+
# the content, so the reponse body is not available here.
|
60
|
+
log("Response: (not available yet)")
|
61
|
+
else
|
62
|
+
log("Response:\n#{body.to_s}")
|
63
|
+
end
|
64
|
+
end
|
24
65
|
|
66
|
+
def log_data(data)
|
67
|
+
return if options[:compact_log] || !options[:log_data]
|
68
|
+
log("Data: #{data}")
|
69
|
+
end
|
70
|
+
|
71
|
+
def log_compact(method, uri, status, seconds)
|
72
|
+
return unless options[:compact_log]
|
73
|
+
log("#{method} #{uri} completed with status code #{status} in #{seconds} seconds")
|
74
|
+
end
|
75
|
+
end
|
25
76
|
end
|
26
77
|
|
27
78
|
module Net
|
28
|
-
|
29
79
|
class HTTP
|
30
80
|
alias_method(:orig_request, :request) unless method_defined?(:orig_request)
|
31
81
|
alias_method(:orig_connect, :connect) unless method_defined?(:orig_connect)
|
32
82
|
|
33
83
|
def request(req, body = nil, &block)
|
34
84
|
|
85
|
+
url = "http://#{@address}:#{@port}#{req.path}"
|
86
|
+
|
35
87
|
if started? && !HttpLog.options[:compact_log]
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
if
|
41
|
-
req.each_header do |key,value|
|
42
|
-
HttpLog::log("Header: #{key}: #{value}")
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
if req.method == "POST" && HttpLog.options[:log_data]
|
47
|
-
# A bit convoluted becase post_form uses form_data= to assign the data, so
|
48
|
-
# in that case req.body will be empty.
|
49
|
-
data = req.body.nil? || req.body.size == 0 ? body : req.body
|
50
|
-
HttpLog::log("Data: #{data}")
|
51
|
-
end
|
88
|
+
HttpLog.log_request(req.method, url)
|
89
|
+
HttpLog.log_headers(req.each_header.collect)
|
90
|
+
# A bit convoluted becase post_form uses form_data= to assign the data, so
|
91
|
+
# in that case req.body will be empty.
|
92
|
+
HttpLog::log_data(req.body.nil? || req.body.size == 0 ? body : req.body) if req.method == 'POST'
|
52
93
|
end
|
53
94
|
|
54
|
-
|
55
|
-
|
56
|
-
|
95
|
+
bm = Benchmark.realtime do
|
96
|
+
@response = orig_request(req, body, &block)
|
97
|
+
end
|
57
98
|
|
58
99
|
if started?
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
HttpLog::log("Benchmark: #{benchmark} seconds") if HttpLog.options[:log_benchmark]
|
64
|
-
|
65
|
-
if HttpLog.options[:log_response]
|
66
|
-
if response.body.is_a?(Net::ReadAdapter)
|
67
|
-
# open-uri wraps the response in a Net::ReadAdapter that defers reading
|
68
|
-
# the contrent, so the reponse body is not available here.
|
69
|
-
HttpLog::log("Response: (not available yet)")
|
70
|
-
else
|
71
|
-
HttpLog::log("Response:\n#{response.body.to_s}")
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
100
|
+
HttpLog.log_compact(req.method, url, @response.code, bm)
|
101
|
+
HttpLog.log_status(@response.code)
|
102
|
+
HttpLog.log_benchmark(bm)
|
103
|
+
HttpLog.log_body(@response.body)
|
75
104
|
end
|
76
105
|
|
77
|
-
response
|
106
|
+
@response
|
78
107
|
end
|
79
108
|
|
80
109
|
def connect
|
@@ -86,3 +115,27 @@ module Net
|
|
86
115
|
end
|
87
116
|
|
88
117
|
end
|
118
|
+
|
119
|
+
if defined?(::HTTPClient)
|
120
|
+
class HTTPClient
|
121
|
+
private
|
122
|
+
alias_method :orig_do_request, :do_request
|
123
|
+
|
124
|
+
def do_request(method, uri, query, body, header, &block)
|
125
|
+
HttpLog.log_request(method, uri)
|
126
|
+
HttpLog.log_headers(header)
|
127
|
+
HttpLog.log_data(body) if method == :post
|
128
|
+
|
129
|
+
bm = Benchmark.realtime do
|
130
|
+
@response = orig_do_request(method, uri, query, body, header, &block)
|
131
|
+
end
|
132
|
+
|
133
|
+
HttpLog.log_compact(method, uri, @response.status, bm)
|
134
|
+
HttpLog.log_status(@response.status)
|
135
|
+
HttpLog.log_benchmark(bm)
|
136
|
+
HttpLog.log_body(@response.body)
|
137
|
+
|
138
|
+
@response
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
data/lib/httplog/version.rb
CHANGED