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.
@@ -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