httplog 0.1.1 → 0.2.0
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/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