httplog 1.1.1 → 1.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.
- checksums.yaml +4 -4
- data/.travis.yml +3 -3
- data/CHANGELOG.md +9 -0
- data/Gemfile.lock +39 -35
- data/Guardfile +1 -1
- data/README.md +18 -2
- data/lib/httplog/adapters/ethon.rb +13 -12
- data/lib/httplog/adapters/excon.rb +27 -37
- data/lib/httplog/adapters/http.rb +18 -19
- data/lib/httplog/adapters/httpclient.rb +14 -15
- data/lib/httplog/adapters/net_http.rb +13 -16
- data/lib/httplog/adapters/patron.rb +13 -15
- data/lib/httplog/configuration.rb +3 -1
- data/lib/httplog/http_log.rb +85 -21
- data/lib/httplog/version.rb +1 -1
- data/spec/lib/http_log_spec.rb +190 -211
- data/spec/support/shared_examples.rb +63 -0
- data/spec/support/test.pdf +198 -0
- data/spec/support/test_server.rb +1 -0
- metadata +4 -2
@@ -8,26 +8,23 @@ module Net
|
|
8
8
|
def request(req, body = nil, &block)
|
9
9
|
url = "http://#{@address}:#{@port}#{req.path}"
|
10
10
|
|
11
|
-
log_enabled = HttpLog.url_approved?(url)
|
12
|
-
|
13
|
-
if log_enabled && started?
|
14
|
-
HttpLog.log_request(req.method, url)
|
15
|
-
HttpLog.log_headers(req.each_header.collect)
|
16
|
-
# A bit convoluted becase post_form uses form_data= to assign the data, so
|
17
|
-
# in that case req.body will be empty.
|
18
|
-
HttpLog.log_data(req.body.nil? || req.body.empty? ? body : req.body) # if req.method == 'POST'
|
19
|
-
end
|
20
|
-
|
21
11
|
bm = Benchmark.realtime do
|
22
12
|
@response = orig_request(req, body, &block)
|
23
13
|
end
|
24
14
|
|
25
|
-
if
|
26
|
-
HttpLog.
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
15
|
+
if HttpLog.url_approved?(url) && started?
|
16
|
+
HttpLog.call(
|
17
|
+
method: req.method,
|
18
|
+
url: url,
|
19
|
+
request_body: req.body.nil? || req.body.empty? ? body : req.body,
|
20
|
+
request_headers: req.each_header.collect,
|
21
|
+
response_code: @response.code,
|
22
|
+
response_body: @response.body,
|
23
|
+
response_headers: @response.each_header.collect,
|
24
|
+
benchmark: bm,
|
25
|
+
encoding: @response['Content-Encoding'],
|
26
|
+
content_type: @response['Content-Type']
|
27
|
+
)
|
31
28
|
end
|
32
29
|
|
33
30
|
@response
|
@@ -5,25 +5,23 @@ if defined?(Patron)
|
|
5
5
|
class Session
|
6
6
|
alias orig_request request
|
7
7
|
def request(action_name, url, headers, options = {})
|
8
|
-
log_enabled = HttpLog.url_approved?(url)
|
9
|
-
|
10
|
-
if log_enabled
|
11
|
-
HttpLog.log_request(action_name, url)
|
12
|
-
HttpLog.log_headers(headers)
|
13
|
-
HttpLog.log_data(options[:data]) # if action_name == :post
|
14
|
-
end
|
15
|
-
|
16
8
|
bm = Benchmark.realtime do
|
17
9
|
@response = orig_request(action_name, url, headers, options)
|
18
10
|
end
|
19
11
|
|
20
|
-
if
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
12
|
+
if HttpLog.url_approved?(url)
|
13
|
+
HttpLog.call(
|
14
|
+
method: action_name,
|
15
|
+
url: url,
|
16
|
+
request_body: options[:data],
|
17
|
+
request_headers: headers,
|
18
|
+
response_code: @response.status,
|
19
|
+
response_body: @response.body,
|
20
|
+
response_headers: @response.headers,
|
21
|
+
benchmark: bm,
|
22
|
+
encoding: @response.headers['Content-Encoding'],
|
23
|
+
content_type: @response.headers['Content-Type']
|
24
|
+
)
|
27
25
|
end
|
28
26
|
|
29
27
|
@response
|
@@ -4,6 +4,7 @@ module HttpLog
|
|
4
4
|
class Configuration
|
5
5
|
attr_accessor :enabled,
|
6
6
|
:compact_log,
|
7
|
+
:json_log,
|
7
8
|
:logger,
|
8
9
|
:severity,
|
9
10
|
:prefix,
|
@@ -24,6 +25,7 @@ module HttpLog
|
|
24
25
|
def initialize
|
25
26
|
@enabled = true
|
26
27
|
@compact_log = false
|
28
|
+
@json_log = false
|
27
29
|
@logger = Logger.new($stdout)
|
28
30
|
@severity = Logger::Severity::DEBUG
|
29
31
|
@prefix = LOG_PREFIX
|
@@ -34,7 +36,7 @@ module HttpLog
|
|
34
36
|
@log_status = true
|
35
37
|
@log_response = true
|
36
38
|
@log_benchmark = true
|
37
|
-
@url_whitelist_pattern =
|
39
|
+
@url_whitelist_pattern = nil
|
38
40
|
@url_blacklist_pattern = nil
|
39
41
|
@color = false
|
40
42
|
@prefix_data_lines = false
|
data/lib/httplog/http_log.rb
CHANGED
@@ -8,6 +8,7 @@ require 'rack'
|
|
8
8
|
|
9
9
|
module HttpLog
|
10
10
|
LOG_PREFIX = '[httplog] '.freeze
|
11
|
+
class BodyParsingError < StandardError; end
|
11
12
|
|
12
13
|
class << self
|
13
14
|
attr_writer :configuration
|
@@ -26,57 +27,91 @@ module HttpLog
|
|
26
27
|
yield(configuration)
|
27
28
|
end
|
28
29
|
|
30
|
+
def call(options = {})
|
31
|
+
if config.json_log
|
32
|
+
log_json(options)
|
33
|
+
elsif config.compact_log
|
34
|
+
log_compact(options[:method], options[:url], options[:response_code], options[:benchmark])
|
35
|
+
else
|
36
|
+
HttpLog.log_request(options[:method], options[:url])
|
37
|
+
HttpLog.log_headers(options[:request_headers])
|
38
|
+
HttpLog.log_data(options[:request_body])
|
39
|
+
HttpLog.log_status(options[:response_code])
|
40
|
+
HttpLog.log_benchmark(options[:benchmark])
|
41
|
+
HttpLog.log_headers(options[:response_headers])
|
42
|
+
HttpLog.log_body(options[:response_body], options[:encoding], options[:content_type])
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
29
46
|
def url_approved?(url)
|
30
47
|
return false if config.url_blacklist_pattern && url.to_s.match(config.url_blacklist_pattern)
|
31
|
-
|
48
|
+
|
49
|
+
!config.url_whitelist_pattern || url.to_s.match(config.url_whitelist_pattern)
|
32
50
|
end
|
33
51
|
|
34
52
|
def log(msg)
|
35
53
|
return unless config.enabled
|
54
|
+
|
36
55
|
config.logger.log(config.severity, colorize(prefix + msg))
|
37
56
|
end
|
38
57
|
|
39
58
|
def log_connection(host, port = nil)
|
40
|
-
return if config.compact_log || !config.log_connect
|
59
|
+
return if config.json_log || config.compact_log || !config.log_connect
|
60
|
+
|
41
61
|
log("Connecting: #{[host, port].compact.join(':')}")
|
42
62
|
end
|
43
63
|
|
44
64
|
def log_request(method, uri)
|
45
|
-
return
|
65
|
+
return unless config.log_request
|
66
|
+
|
46
67
|
log("Sending: #{method.to_s.upcase} #{uri}")
|
47
68
|
end
|
48
69
|
|
49
70
|
def log_headers(headers = {})
|
50
|
-
return
|
71
|
+
return unless config.log_headers
|
72
|
+
|
51
73
|
headers.each do |key, value|
|
52
74
|
log("Header: #{key}: #{value}")
|
53
75
|
end
|
54
76
|
end
|
55
77
|
|
56
78
|
def log_status(status)
|
57
|
-
return
|
79
|
+
return unless config.log_status
|
80
|
+
|
58
81
|
status = Rack::Utils.status_code(status) unless status == /\d{3}/
|
59
82
|
log("Status: #{status}")
|
60
83
|
end
|
61
84
|
|
62
85
|
def log_benchmark(seconds)
|
63
|
-
return
|
86
|
+
return unless config.log_benchmark
|
87
|
+
|
64
88
|
log("Benchmark: #{seconds.to_f.round(6)} seconds")
|
65
89
|
end
|
66
90
|
|
67
91
|
def log_body(body, encoding = nil, content_type = nil)
|
68
|
-
return
|
92
|
+
return unless config.log_response
|
93
|
+
|
94
|
+
data = parse_body(body, encoding, content_type)
|
69
95
|
|
96
|
+
if config.prefix_response_lines
|
97
|
+
log('Response:')
|
98
|
+
log_data_lines(data)
|
99
|
+
else
|
100
|
+
log("Response:\n#{data}")
|
101
|
+
end
|
102
|
+
rescue BodyParsingError => e
|
103
|
+
log("Response: #{e.message}")
|
104
|
+
end
|
105
|
+
|
106
|
+
def parse_body(body, encoding, content_type)
|
70
107
|
unless text_based?(content_type)
|
71
|
-
|
72
|
-
return
|
108
|
+
raise BodyParsingError, "(not showing binary data)"
|
73
109
|
end
|
74
110
|
|
75
111
|
if body.is_a?(Net::ReadAdapter)
|
76
112
|
# open-uri wraps the response in a Net::ReadAdapter that defers reading
|
77
113
|
# the content, so the reponse body is not available here.
|
78
|
-
|
79
|
-
return
|
114
|
+
raise BodyParsingError, '(not available yet)'
|
80
115
|
end
|
81
116
|
|
82
117
|
if encoding =~ /gzip/ && body && !body.empty?
|
@@ -89,18 +124,11 @@ module HttpLog
|
|
89
124
|
end
|
90
125
|
end
|
91
126
|
|
92
|
-
|
93
|
-
|
94
|
-
if config.prefix_response_lines
|
95
|
-
log('Response:')
|
96
|
-
log_data_lines(data)
|
97
|
-
else
|
98
|
-
log("Response:\n#{data}")
|
99
|
-
end
|
127
|
+
utf_encoded(body.to_s, content_type)
|
100
128
|
end
|
101
129
|
|
102
130
|
def log_data(data)
|
103
|
-
return
|
131
|
+
return unless config.log_data
|
104
132
|
data = utf_encoded(data.to_s.dup)
|
105
133
|
|
106
134
|
if config.prefix_data_lines
|
@@ -117,6 +145,42 @@ module HttpLog
|
|
117
145
|
log("#{method.to_s.upcase} #{uri} completed with status code #{status} in #{seconds} seconds")
|
118
146
|
end
|
119
147
|
|
148
|
+
def log_json(data = {})
|
149
|
+
return unless config.json_log
|
150
|
+
|
151
|
+
data[:response_code] = transform_response_code(data[:response_code]) if data[:response_code].is_a?(Symbol)
|
152
|
+
|
153
|
+
parsed_body = begin
|
154
|
+
parse_body(data[:response_body], data[:encoding], data[:content_type])
|
155
|
+
rescue BodyParsingError => e
|
156
|
+
e.message
|
157
|
+
end
|
158
|
+
|
159
|
+
if config.compact_log
|
160
|
+
log({
|
161
|
+
method: data[:method].to_s.upcase,
|
162
|
+
url: data[:url],
|
163
|
+
response_code: data[:response_code].to_i,
|
164
|
+
benchmark: data[:benchmark]
|
165
|
+
}.to_json)
|
166
|
+
else
|
167
|
+
log({
|
168
|
+
method: data[:method].to_s.upcase,
|
169
|
+
url: data[:url],
|
170
|
+
request_body: data[:request_body],
|
171
|
+
request_headers: data[:request_headers].to_h,
|
172
|
+
response_code: data[:response_code].to_i,
|
173
|
+
response_body: parsed_body,
|
174
|
+
response_headers: data[:response_headers].to_h,
|
175
|
+
benchmark: data[:benchmark]
|
176
|
+
}.to_json)
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
def transform_response_code(response_code_name)
|
181
|
+
Rack::Utils::HTTP_STATUS_CODES.detect{ |k, v| v.to_s.downcase == response_code_name.to_s.downcase }.first
|
182
|
+
end
|
183
|
+
|
120
184
|
def colorize(msg)
|
121
185
|
return msg unless config.color
|
122
186
|
if config.color.is_a?(Hash)
|
@@ -148,7 +212,7 @@ module HttpLog
|
|
148
212
|
# it will allow application/json and the like without having to resort to more
|
149
213
|
# heavy-handed checks.
|
150
214
|
content_type =~ /^text/ ||
|
151
|
-
content_type =~ /^application/ &&
|
215
|
+
content_type =~ /^application/ && !['application/octet-stream', 'application/pdf'].include?(content_type)
|
152
216
|
end
|
153
217
|
|
154
218
|
def log_data_lines(data)
|
data/lib/httplog/version.rb
CHANGED
data/spec/lib/http_log_spec.rb
CHANGED
@@ -3,12 +3,55 @@
|
|
3
3
|
require 'spec_helper'
|
4
4
|
|
5
5
|
describe HttpLog do
|
6
|
-
|
7
|
-
|
8
|
-
let(:
|
6
|
+
subject { log } # see spec_helper
|
7
|
+
|
8
|
+
let(:host) { 'localhost' }
|
9
|
+
let(:port) { 9292 }
|
10
|
+
let(:path) { '/index.html' }
|
9
11
|
let(:headers) { { 'accept' => '*/*', 'foo' => 'bar' } }
|
10
|
-
let(:data)
|
11
|
-
let(:params)
|
12
|
+
let(:data) { 'foo=bar&bar=foo' }
|
13
|
+
let(:params) { { 'foo' => 'bar:form-data', 'bar' => 'foo' } }
|
14
|
+
let(:html) { File.read('./spec/support/index.html') }
|
15
|
+
let(:json) { JSON.parse(log.match(/\[httplog\]\s(.*)/).captures.first) }
|
16
|
+
|
17
|
+
# Configuration
|
18
|
+
let(:enabled) { HttpLog.configuration.enabled }
|
19
|
+
let(:severity) { HttpLog.configuration.severity }
|
20
|
+
let(:log_headers) { HttpLog.configuration.log_headers }
|
21
|
+
let(:log_request) { HttpLog.configuration.log_request }
|
22
|
+
let(:log_response) { HttpLog.configuration.log_response }
|
23
|
+
let(:log_data) { HttpLog.configuration.log_data }
|
24
|
+
let(:log_connect) { HttpLog.configuration.log_connect }
|
25
|
+
let(:log_benchmark) { HttpLog.configuration.log_benchmark }
|
26
|
+
let(:color) { HttpLog.configuration.color }
|
27
|
+
let(:prefix) { HttpLog.configuration.prefix }
|
28
|
+
let(:prefix_response_lines) { HttpLog.configuration.prefix_response_lines }
|
29
|
+
let(:prefix_line_numbers) { HttpLog.configuration.prefix_line_numbers }
|
30
|
+
let(:json_log) { HttpLog.configuration.json_log }
|
31
|
+
let(:compact_log) { HttpLog.configuration.compact_log }
|
32
|
+
let(:url_blacklist_pattern) { HttpLog.configuration.url_blacklist_pattern }
|
33
|
+
let(:url_whitelist_pattern) { HttpLog.configuration.url_whitelist_pattern }
|
34
|
+
|
35
|
+
def configure
|
36
|
+
HttpLog.configure do |c|
|
37
|
+
c.enabled = enabled
|
38
|
+
c.severity = severity
|
39
|
+
c.log_headers = log_headers
|
40
|
+
c.log_request = log_request
|
41
|
+
c.log_response = log_response
|
42
|
+
c.log_data = log_data
|
43
|
+
c.log_connect = log_connect
|
44
|
+
c.log_benchmark = log_benchmark
|
45
|
+
c.color = color
|
46
|
+
c.prefix = prefix
|
47
|
+
c.prefix_response_lines = prefix_response_lines
|
48
|
+
c.prefix_line_numbers = prefix_line_numbers
|
49
|
+
c.json_log = json_log
|
50
|
+
c.compact_log = compact_log
|
51
|
+
c.url_blacklist_pattern = url_blacklist_pattern
|
52
|
+
c.url_whitelist_pattern = url_whitelist_pattern
|
53
|
+
end
|
54
|
+
end
|
12
55
|
|
13
56
|
ADAPTERS = [
|
14
57
|
NetHTTPAdapter,
|
@@ -25,41 +68,36 @@ describe HttpLog do
|
|
25
68
|
ADAPTERS.each do |adapter_class|
|
26
69
|
context adapter_class, adapter: adapter_class.to_s do
|
27
70
|
let(:adapter) { adapter_class.new(host: host, port: port, path: path, headers: headers, data: data, params: params) }
|
71
|
+
before { configure }
|
28
72
|
|
29
73
|
context 'with default configuration' do
|
30
|
-
|
74
|
+
describe 'GET requests' do
|
75
|
+
let!(:res) { adapter.send_get_request }
|
31
76
|
|
32
|
-
|
33
|
-
|
34
|
-
|
77
|
+
it_behaves_like 'logs request', 'GET'
|
78
|
+
it_behaves_like 'logs data'
|
79
|
+
it_behaves_like 'logs expected response'
|
80
|
+
it_behaves_like 'logs status', 200
|
81
|
+
it_behaves_like 'logs benchmark'
|
35
82
|
|
36
|
-
|
83
|
+
it { is_expected.to_not include('Header:') }
|
84
|
+
it { is_expected.to_not include("\e[0") }
|
37
85
|
|
38
|
-
|
39
|
-
|
40
|
-
expect(log).to_not include(HttpLog::LOG_PREFIX + 'Header:')
|
41
|
-
expect(log).to include(HttpLog::LOG_PREFIX + 'Status: 200')
|
42
|
-
expect(log).to include(HttpLog::LOG_PREFIX + 'Benchmark: ')
|
43
|
-
expect(log).to include(HttpLog::LOG_PREFIX + "Response:#{adapter.expected_response_body}")
|
44
|
-
|
45
|
-
expect(log).to_not include("\e[0")
|
46
|
-
|
47
|
-
expect(res).to be_a adapter.response if adapter.respond_to? :response
|
86
|
+
unless adapter_class.is_libcurl?
|
87
|
+
it { is_expected.to include("Connecting: #{host}:#{port}") }
|
48
88
|
end
|
49
89
|
|
90
|
+
it { expect(res).to be_a adapter.response if adapter.respond_to? :response }
|
91
|
+
|
50
92
|
context 'with gzip encoding' do
|
51
93
|
let(:path) { '/index.html.gz' }
|
52
94
|
let(:data) { nil }
|
53
95
|
|
54
|
-
|
55
|
-
adapter.send_get_request
|
56
|
-
expect(log).to include(HttpLog::LOG_PREFIX + "Response:#{adapter.expected_response_body}")
|
57
|
-
end
|
96
|
+
it_behaves_like 'logs expected response'
|
58
97
|
|
59
98
|
if adapter_class.method_defined? :send_head_request
|
60
99
|
it "doesn't try to decompress body for HEAD requests" do
|
61
|
-
|
62
|
-
expect(log).to include(HttpLog::LOG_PREFIX + 'Response:')
|
100
|
+
expect(log).to include('Response:')
|
63
101
|
end
|
64
102
|
end
|
65
103
|
end
|
@@ -68,55 +106,51 @@ describe HttpLog do
|
|
68
106
|
let(:path) { '/utf8.html' }
|
69
107
|
let(:data) { nil }
|
70
108
|
|
71
|
-
|
72
|
-
|
73
|
-
expect(log).to include(HttpLog::LOG_PREFIX + "Response:#{adapter.expected_response_body}")
|
74
|
-
if adapter.logs_data?
|
75
|
-
expect(log).to include(' <title>Блог Яндекса</title>')
|
76
|
-
end
|
77
|
-
end
|
109
|
+
it_behaves_like 'logs expected response'
|
110
|
+
it { is_expected.to include(' <title>Блог Яндекса</title>') if adapter.logs_data? }
|
78
111
|
end
|
79
112
|
|
80
113
|
context 'with binary response body' do
|
81
|
-
|
82
|
-
|
114
|
+
%w[/test.bin /test.pdf].each do |response_file_name|
|
115
|
+
let(:path) { response_file_name }
|
116
|
+
let(:data) { nil }
|
117
|
+
|
118
|
+
it { is_expected.to include('Response: (not showing binary data)') }
|
83
119
|
|
84
|
-
|
85
|
-
|
86
|
-
|
120
|
+
context 'and JSON logging' do
|
121
|
+
let(:json_log) { true }
|
122
|
+
it { expect(json['response_body']).to eq '(not showing binary data)' }
|
123
|
+
end
|
87
124
|
end
|
88
125
|
end
|
89
126
|
end
|
90
127
|
|
91
|
-
|
92
|
-
|
93
|
-
res
|
128
|
+
describe 'POST requests' do
|
129
|
+
if adapter_class.method_defined? :send_post_request
|
130
|
+
let!(:res) { adapter.send_post_request }
|
94
131
|
|
95
|
-
|
132
|
+
unless adapter_class.is_libcurl?
|
133
|
+
it { is_expected.to include("Connecting: #{host}:#{port}") }
|
134
|
+
end
|
96
135
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
expect(log).to include(HttpLog::LOG_PREFIX + "Response:#{adapter.expected_response_body}")
|
136
|
+
it_behaves_like 'logs request', 'POST'
|
137
|
+
it_behaves_like 'logs expected response'
|
138
|
+
it_behaves_like 'logs data', 'foo=bar&bar=foo'
|
139
|
+
it_behaves_like 'logs status', 200
|
140
|
+
it_behaves_like 'logs benchmark'
|
103
141
|
|
104
|
-
|
105
|
-
|
142
|
+
it { is_expected.to_not include('Header:') }
|
143
|
+
|
144
|
+
it { expect(res).to be_a adapter.response if adapter.respond_to? :response }
|
106
145
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
expect { adapter.send_post_request }.to_not raise_error
|
111
|
-
expect(log).to include(HttpLog::LOG_PREFIX + 'Response:')
|
146
|
+
context 'with non-UTF request data' do
|
147
|
+
let(:data) { "a UTF-8 striñg with an 8BIT-ASCII character: \xC3" }
|
148
|
+
it_behaves_like 'logs expected response' # == doesn't throw exception
|
112
149
|
end
|
113
|
-
end
|
114
150
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
expect { adapter.send_post_request }.to_not raise_error
|
119
|
-
expect(log).to include(HttpLog::LOG_PREFIX + 'Response:')
|
151
|
+
context 'with URI encoded non-UTF data' do
|
152
|
+
let(:data) { 'a UTF-8 striñg with a URI encoded 8BIT-ASCII character: %c3' }
|
153
|
+
it_behaves_like 'logs expected response' # == doesn't throw exception
|
120
154
|
end
|
121
155
|
end
|
122
156
|
end
|
@@ -124,168 +158,108 @@ describe HttpLog do
|
|
124
158
|
|
125
159
|
context 'with custom configuration' do
|
126
160
|
context 'GET requests' do
|
127
|
-
|
128
|
-
HttpLog.configure { |c| c.enabled = false }
|
129
|
-
adapter.send_get_request
|
130
|
-
expect(log).to eq('')
|
131
|
-
end
|
161
|
+
before { adapter.send_get_request }
|
132
162
|
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
expect(log).to include('INFO')
|
163
|
+
context 'when disabled' do
|
164
|
+
let(:enabled) { false }
|
165
|
+
it_behaves_like 'logs nothing'
|
137
166
|
end
|
138
167
|
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
# request header
|
143
|
-
expect(log.downcase).to include(HttpLog::LOG_PREFIX + 'Header: accept: */*'.downcase)
|
144
|
-
# response header
|
145
|
-
expect(log.downcase).to include(HttpLog::LOG_PREFIX + 'Header: server: thin'.downcase)
|
168
|
+
context 'with different log level' do
|
169
|
+
let(:severity) { Logger::Severity::INFO }
|
170
|
+
it { is_expected.to include('INFO') }
|
146
171
|
end
|
147
172
|
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
173
|
+
context 'with headers logging' do
|
174
|
+
let(:log_headers) { true }
|
175
|
+
it { is_expected.to match(%r{Header: accept: */*}i) } # request
|
176
|
+
it { is_expected.to match(/Header: Server: thin/i) } # response
|
152
177
|
end
|
153
178
|
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
expect(log).to include(HttpLog::LOG_PREFIX + 'Sending: GET')
|
179
|
+
context 'with blacklist hit' do
|
180
|
+
let(:url_blacklist_pattern) { /#{host}:#{port}/ }
|
181
|
+
it_behaves_like 'logs nothing'
|
158
182
|
end
|
159
183
|
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
adapter.send_get_request
|
164
|
-
expect(log).to include(HttpLog::LOG_PREFIX + 'Sending: GET')
|
184
|
+
context 'with blacklist miss' do
|
185
|
+
let(:url_blacklist_pattern) { /example.com/ }
|
186
|
+
it_behaves_like 'logs request', 'GET'
|
165
187
|
end
|
166
188
|
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
expect(log).to_not include(HttpLog::LOG_PREFIX + 'Sending: GET')
|
171
|
-
end
|
172
|
-
|
173
|
-
it 'should not log the request if url does not match whitelist pattern' do
|
174
|
-
HttpLog.configure { |c| c.url_whitelist_pattern = /example.com/ }
|
175
|
-
adapter.send_get_request
|
176
|
-
expect(log).to_not include(HttpLog::LOG_PREFIX + 'Sending: GET')
|
177
|
-
end
|
189
|
+
context 'with whitelist hit' do
|
190
|
+
let(:url_whitelist_pattern) { /#{host}:#{port}/ }
|
191
|
+
it_behaves_like 'logs request', 'GET'
|
178
192
|
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
expect(log).to_not include(HttpLog::LOG_PREFIX + 'Sending: GET')
|
193
|
+
context 'and blacklist hit' do
|
194
|
+
let(:url_blacklist_pattern) { /#{host}:#{port}/ }
|
195
|
+
it_behaves_like 'logs nothing'
|
196
|
+
end
|
184
197
|
end
|
185
198
|
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
expect(log).to_not include(HttpLog::LOG_PREFIX + 'Sending: GET')
|
199
|
+
context 'with whitelist miss' do
|
200
|
+
let(:url_whitelist_pattern) { /example.com/ }
|
201
|
+
it_behaves_like 'logs nothing'
|
190
202
|
end
|
191
203
|
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
204
|
+
it_behaves_like 'with request logging disabled'
|
205
|
+
it_behaves_like 'with connection logging disabled'
|
206
|
+
it_behaves_like 'data logging disabled'
|
207
|
+
it_behaves_like 'response logging disabled'
|
208
|
+
it_behaves_like 'benchmark logging disabled'
|
197
209
|
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
expect(log).to_not include(HttpLog::LOG_PREFIX + 'Data:')
|
210
|
+
context 'with single color' do
|
211
|
+
let(:color) { :red }
|
212
|
+
it { is_expected.to include("\e[31m") }
|
202
213
|
end
|
203
214
|
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
expect(log).to include("\e[31m")
|
215
|
+
context 'with color hash' do
|
216
|
+
let(:color) { { color: :black, background: :yellow } }
|
217
|
+
it { is_expected.to include("\e[30m\e[43m") }
|
208
218
|
end
|
209
219
|
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
220
|
+
context 'with custom prefix' do
|
221
|
+
let(:prefix) { '[my logger]' }
|
222
|
+
it { is_expected.to include('[my logger]') }
|
223
|
+
it { is_expected.to_not include(HttpLog::LOG_PREFIX) }
|
214
224
|
end
|
215
225
|
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
expect(log).to_not include(HttpLog::LOG_PREFIX)
|
226
|
+
context 'with custom lambda prefix' do
|
227
|
+
let(:prefix) { -> { '[custom prefix]' } }
|
228
|
+
it { is_expected.to include('[custom prefix]') }
|
229
|
+
it { is_expected.to_not include(HttpLog::LOG_PREFIX) }
|
221
230
|
end
|
222
231
|
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
232
|
+
context 'with compact config' do
|
233
|
+
let(:compact_log) { true }
|
234
|
+
it { is_expected.to match(%r{\[httplog\] GET http://#{host}:#{port}#{path}(\?.*)? completed with status code \d{3} in (\d|\.)+}) }
|
235
|
+
it { is_expected.to_not include("Connecting: #{host}:#{port}") }
|
236
|
+
it { is_expected.to_not include('Response:') }
|
237
|
+
it { is_expected.to_not include('Data:') }
|
238
|
+
it { is_expected.to_not include('Benchmark: ') }
|
228
239
|
end
|
229
240
|
end
|
230
241
|
|
231
242
|
context 'POST requests' do
|
232
243
|
if adapter_class.method_defined? :send_post_request
|
233
|
-
|
234
|
-
HttpLog.configure { |c| c.log_data = false }
|
235
|
-
adapter.send_post_request
|
236
|
-
expect(log).to_not include(HttpLog::LOG_PREFIX + 'Data:')
|
237
|
-
end
|
238
|
-
|
239
|
-
it 'should not log the response if disabled' do
|
240
|
-
HttpLog.configure { |c| c.log_response = false }
|
241
|
-
adapter.send_post_request
|
242
|
-
expect(log).to_not include(HttpLog::LOG_PREFIX + 'Reponse:')
|
243
|
-
end
|
244
|
-
|
245
|
-
it 'should prefix all response lines' do
|
246
|
-
HttpLog.configure { |c| c.prefix_response_lines = true }
|
247
|
-
|
248
|
-
adapter.send_post_request
|
249
|
-
expect(log).to include(HttpLog::LOG_PREFIX + 'Response:')
|
250
|
-
expect(log).to include(HttpLog::LOG_PREFIX + '<html>')
|
251
|
-
end
|
252
|
-
|
253
|
-
it 'should prefix all response lines with line numbers' do
|
254
|
-
HttpLog.configure { |c| c.prefix_response_lines = true }
|
255
|
-
HttpLog.configure { |c| c.prefix_line_numbers = true }
|
256
|
-
|
257
|
-
adapter.send_post_request
|
258
|
-
expect(log).to include(HttpLog::LOG_PREFIX + 'Response:')
|
259
|
-
expect(log).to include(HttpLog::LOG_PREFIX + '1: <html>')
|
260
|
-
end
|
244
|
+
before { adapter.send_post_request }
|
261
245
|
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
246
|
+
it_behaves_like 'data logging disabled'
|
247
|
+
it_behaves_like 'response logging disabled'
|
248
|
+
it_behaves_like 'benchmark logging disabled'
|
249
|
+
it_behaves_like 'with prefix response lines'
|
250
|
+
it_behaves_like 'with line numbers'
|
267
251
|
end
|
268
252
|
end
|
269
253
|
|
270
254
|
context 'POST form data requests' do
|
271
255
|
if adapter_class.method_defined? :send_post_form_request
|
272
|
-
|
273
|
-
HttpLog.configure { |c| c.log_data = false }
|
274
|
-
adapter.send_post_form_request
|
275
|
-
expect(log).to_not include(HttpLog::LOG_PREFIX + 'Data:')
|
276
|
-
end
|
277
|
-
|
278
|
-
it 'should not log the response if disabled' do
|
279
|
-
HttpLog.configure { |c| c.log_response = false }
|
280
|
-
adapter.send_post_form_request
|
281
|
-
expect(log).to_not include(HttpLog::LOG_PREFIX + 'Reponse:')
|
282
|
-
end
|
256
|
+
before { adapter.send_post_form_request }
|
283
257
|
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
258
|
+
it_behaves_like 'data logging disabled'
|
259
|
+
it_behaves_like 'response logging disabled'
|
260
|
+
it_behaves_like 'benchmark logging disabled'
|
261
|
+
it_behaves_like 'with prefix response lines'
|
262
|
+
it_behaves_like 'with line numbers'
|
289
263
|
end
|
290
264
|
end
|
291
265
|
|
@@ -294,37 +268,42 @@ describe HttpLog do
|
|
294
268
|
let(:params) { { 'foo' => 'bar', 'file' => upload } }
|
295
269
|
|
296
270
|
if adapter_class.method_defined? :send_multipart_post_request
|
297
|
-
|
298
|
-
HttpLog.configure { |c| c.log_data = false }
|
299
|
-
adapter.send_multipart_post_request
|
300
|
-
expect(log).to_not include(HttpLog::LOG_PREFIX + 'Data:')
|
301
|
-
end
|
271
|
+
before { adapter.send_multipart_post_request }
|
302
272
|
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
it 'should not log the benchmark if disabled' do
|
310
|
-
HttpLog.configure { |c| c.log_benchmark = false }
|
311
|
-
adapter.send_multipart_post_request
|
312
|
-
expect(log).to_not include(HttpLog::LOG_PREFIX + 'Benchmark:')
|
313
|
-
end
|
273
|
+
it_behaves_like 'data logging disabled'
|
274
|
+
it_behaves_like 'response logging disabled'
|
275
|
+
it_behaves_like 'benchmark logging disabled'
|
276
|
+
it_behaves_like 'with prefix response lines'
|
277
|
+
it_behaves_like 'with line numbers'
|
314
278
|
end
|
315
279
|
end
|
316
280
|
end
|
317
281
|
|
318
|
-
context 'with
|
319
|
-
|
282
|
+
context 'with JSON config' do
|
283
|
+
let(:json_log) { true }
|
320
284
|
|
321
|
-
|
322
|
-
adapter.
|
323
|
-
|
324
|
-
expect(
|
325
|
-
expect(
|
326
|
-
expect(
|
327
|
-
expect(
|
285
|
+
if adapter_class.method_defined? :send_post_request
|
286
|
+
before { adapter.send_post_request }
|
287
|
+
|
288
|
+
it { expect(json['method']).to eq('POST') }
|
289
|
+
it { expect(json['request_body']).to eq(data) }
|
290
|
+
it { expect(json['request_headers']).to be_a(Hash) }
|
291
|
+
it { expect(json['response_headers']).to be_a(Hash) }
|
292
|
+
it { expect(json['response_code']).to eq(200) }
|
293
|
+
it { expect(json['response_body']).to eq(html) }
|
294
|
+
it { expect(json['benchmark']).to be_a(Numeric) }
|
295
|
+
|
296
|
+
context 'and compact config' do
|
297
|
+
let(:compact_log) { true }
|
298
|
+
|
299
|
+
it { expect(json['method']).to eq('POST') }
|
300
|
+
it { expect(json['request_body']).to be_nil }
|
301
|
+
it { expect(json['request_headers']).to be_nil }
|
302
|
+
it { expect(json['response_headers']).to be_nil }
|
303
|
+
it { expect(json['response_code']).to eq(200) }
|
304
|
+
it { expect(json['response_body']).to be_nil }
|
305
|
+
it { expect(json['benchmark']).to be_a(Numeric) }
|
306
|
+
end
|
328
307
|
end
|
329
308
|
end
|
330
309
|
end
|