httplog 1.1.1 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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 log_enabled && started?
26
- HttpLog.log_compact(req.method, url, @response.code, bm)
27
- HttpLog.log_status(@response.code)
28
- HttpLog.log_benchmark(bm)
29
- HttpLog.log_headers(@response.each_header.collect)
30
- HttpLog.log_body(@response.body, @response['Content-Encoding'], @response['Content-Type'])
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 log_enabled
21
- headers = @response.headers
22
- HttpLog.log_compact(action_name, url, @response.status, bm)
23
- HttpLog.log_status(@response.status)
24
- HttpLog.log_benchmark(bm)
25
- HttpLog.log_headers(headers)
26
- HttpLog.log_body(@response.body, headers['Content-Encoding'], headers['Content-Type'])
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
@@ -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
- url.to_s.match(config.url_whitelist_pattern)
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 if config.compact_log || !config.log_request
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 if config.compact_log || !config.log_headers
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 if config.compact_log || !config.log_status
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 if config.compact_log || !config.log_benchmark
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 if config.compact_log || !config.log_response
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
- log('Response: (not showing binary data)')
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
- log('Response: (not available yet)')
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
- data = utf_encoded(body.to_s, content_type)
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 if config.compact_log || !config.log_data
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/ && content_type != 'application/octet-stream'
215
+ content_type =~ /^application/ && !['application/octet-stream', 'application/pdf'].include?(content_type)
152
216
  end
153
217
 
154
218
  def log_data_lines(data)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module HttpLog
4
- VERSION = '1.1.1'.freeze
4
+ VERSION = '1.2.0'.freeze
5
5
  end
@@ -3,12 +3,55 @@
3
3
  require 'spec_helper'
4
4
 
5
5
  describe HttpLog do
6
- let(:host) { 'localhost' }
7
- let(:port) { 9292 }
8
- let(:path) { '/index.html' }
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) { 'foo=bar&bar=foo' }
11
- let(:params) { { 'foo' => 'bar:form-data', 'bar' => 'foo' } }
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
- connection_test_method = adapter_class.is_libcurl? ? :to_not : :to
74
+ describe 'GET requests' do
75
+ let!(:res) { adapter.send_get_request }
31
76
 
32
- if adapter_class.method_defined? :send_get_request
33
- it 'should log GET requests' do
34
- res = adapter.send_get_request
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
- expect(log).send(connection_test_method, include(HttpLog::LOG_PREFIX + "Connecting: #{host}:#{port}"))
83
+ it { is_expected.to_not include('Header:') }
84
+ it { is_expected.to_not include("\e[0") }
37
85
 
38
- expect(log).to include(HttpLog::LOG_PREFIX + "Sending: GET http://#{host}:#{port}#{path}")
39
- expect(log).to include(HttpLog::LOG_PREFIX + 'Data:')
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
- it 'decompresses gzipped response body' do
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
- adapter.send_head_request
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
- it 'works' do
72
- adapter.send_get_request
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
- let(:path) { '/test.bin' }
82
- let(:data) { nil }
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
- it "doesn't log response" do
85
- adapter.send_get_request
86
- expect(log).to include(HttpLog::LOG_PREFIX + 'Response: (not showing binary data)')
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
- if adapter_class.method_defined? :send_post_request
92
- it 'logs POST requests' do
93
- res = adapter.send_post_request
128
+ describe 'POST requests' do
129
+ if adapter_class.method_defined? :send_post_request
130
+ let!(:res) { adapter.send_post_request }
94
131
 
95
- expect(log).send(connection_test_method, include(HttpLog::LOG_PREFIX + "Connecting: #{host}:#{port}"))
132
+ unless adapter_class.is_libcurl?
133
+ it { is_expected.to include("Connecting: #{host}:#{port}") }
134
+ end
96
135
 
97
- expect(log).to include(HttpLog::LOG_PREFIX + "Sending: POST http://#{host}:#{port}#{path}")
98
- expect(log).to include(HttpLog::LOG_PREFIX + 'Data: foo=bar&bar=foo')
99
- expect(log).to_not include(HttpLog::LOG_PREFIX + 'Header:')
100
- expect(log).to include(HttpLog::LOG_PREFIX + 'Status: 200')
101
- expect(log).to include(HttpLog::LOG_PREFIX + 'Benchmark: ')
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
- expect(res).to be_a adapter.response if adapter.respond_to? :response
105
- end
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
- context 'with non-UTF request data' do
108
- let(:data) { "a UTF-8 striñg with an 8BIT-ASCII character: \xC3" }
109
- it 'does not raise and error' do
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
- context 'with URI encoded non-UTF data' do
116
- let(:data) { 'a UTF-8 striñg with a URI encoded 8BIT-ASCII character: %c3' }
117
- it 'does not raise and error' do
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
- it 'should not log anything unless enabled is set' do
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
- it 'should log at other levels' do
134
- HttpLog.configure { |c| c.severity = Logger::Severity::INFO }
135
- adapter.send_get_request
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
- it 'should log headers if enabled' do
140
- HttpLog.configure { |c| c.log_headers = true }
141
- adapter.send_get_request
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
- it 'should not log headers if disabled' do
149
- HttpLog.configure { |c| c.log_headers = false }
150
- adapter.send_get_request
151
- expect(log).to_not include(HttpLog::LOG_PREFIX + 'Header:')
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
- it 'should log the request if url does not match blacklist pattern' do
155
- HttpLog.configure { |c| c.url_blacklist_pattern = /example.com/ }
156
- adapter.send_get_request
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
- it 'should log the request if url matches whitelist pattern and not the blacklist pattern' do
161
- HttpLog.configure { |c| c.url_blacklist_pattern = /example.com/ }
162
- HttpLog.configure { |c| c.url_whitelist_pattern = /#{host}:#{port}/ }
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
- it 'should not log the request if url matches blacklist pattern' do
168
- HttpLog.configure { |c| c.url_blacklist_pattern = /#{host}:#{port}/ }
169
- adapter.send_get_request
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
- it 'should not log the request if url matches blacklist pattern and the whitelist pattern' do
180
- HttpLog.configure { |c| c.url_blacklist_pattern = /#{host}:#{port}/ }
181
- HttpLog.configure { |c| c.url_whitelist_pattern = /#{host}:#{port}/ }
182
- adapter.send_get_request
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
- it 'should not log the request if disabled' do
187
- HttpLog.configure { |c| c.log_request = false }
188
- adapter.send_get_request
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
- it 'should not log the connection if disabled' do
193
- HttpLog.configure { |c| c.log_connect = false }
194
- adapter.send_get_request
195
- expect(log).to_not include(HttpLog::LOG_PREFIX + "Connecting: #{host}:#{port}")
196
- end
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
- it 'should not log data if disabled' do
199
- HttpLog.configure { |c| c.log_data = false }
200
- adapter.send_get_request
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
- it 'should colorized output with single color' do
205
- HttpLog.configure { |c| c.color = :red }
206
- adapter.send_get_request
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
- it 'should colorized output with color hash' do
211
- HttpLog.configure { |c| c.color = { color: :black, background: :yellow } }
212
- adapter.send_get_request
213
- expect(log).to include("\e[30m\e[43m")
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
- it 'should log with custom string prefix' do
217
- HttpLog.configure { |c| c.prefix = '[my logger]' }
218
- adapter.send_get_request
219
- expect(log).to include('[my logger]')
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
- it 'should log with custom lambda prefix' do
224
- HttpLog.configure { |c| c.prefix = -> { '[custom prefix]' } }
225
- adapter.send_get_request
226
- expect(log).to include('[custom prefix]')
227
- expect(log).to_not include(HttpLog::LOG_PREFIX)
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
- it 'should not log data if disabled' do
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
- it 'should not log the benchmark if disabled' do
263
- HttpLog.configure { |c| c.log_benchmark = false }
264
- adapter.send_post_request
265
- expect(log).to_not include(HttpLog::LOG_PREFIX + 'Benchmark:')
266
- end
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
- it 'should not log data if disabled' do
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
- it 'should not log the benchmark if disabled' do
285
- HttpLog.configure { |c| c.log_benchmark = false }
286
- adapter.send_post_form_request
287
- expect(log).to_not include(HttpLog::LOG_PREFIX + 'Benchmark:')
288
- end
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
- it 'should not log data if disabled' do
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
- it 'should not log the response if disabled' do
304
- HttpLog.configure { |c| c.log_response = false }
305
- adapter.send_multipart_post_request
306
- expect(log).to_not include(HttpLog::LOG_PREFIX + 'Reponse:')
307
- end
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 compact config' do
319
- before(:each) { HttpLog.configure { |c| c.compact_log = true } }
282
+ context 'with JSON config' do
283
+ let(:json_log) { true }
320
284
 
321
- it 'should log a single line with status and benchmark' do
322
- adapter.send_get_request
323
- expect(log).to match(%r{\[httplog\] GET http://#{host}:#{port}#{path}(\?.*)? completed with status code \d{3} in (\d|\.)+})
324
- expect(log).to_not include(HttpLog::LOG_PREFIX + "Connecting: #{host}:#{port}")
325
- expect(log).to_not include(HttpLog::LOG_PREFIX + 'Response:')
326
- expect(log).to_not include(HttpLog::LOG_PREFIX + 'Data:')
327
- expect(log).to_not include(HttpLog::LOG_PREFIX + 'Benchmark: ')
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