Compare commits

..

4 Commits

6 changed files with 89 additions and 5 deletions

View File

@ -42,12 +42,21 @@ end
### Include and exclude headers/bodies ### Include and exclude headers/bodies
By default, the `logger` middleware logs only headers for security reasons, however, you can configure it By default, the `logger` middleware logs only headers for security reasons, however, you can configure it
to log bodies as well, or disable headers logging if you need to. To do so, simply provide a configuration hash to log bodies and errors as well, or disable headers logging if you need to.
when you add the middleware to the stack: To do so, simply provide a configuration hash when you add the middleware to the stack:
```ruby ```ruby
conn = Faraday.new(url: 'http://httpbingo.org') do |faraday| conn = Faraday.new(url: 'http://httpbingo.org') do |faraday|
faraday.response :logger, nil, { headers: true, bodies: true } faraday.response :logger, nil, { headers: true, bodies: true, errors: true }
end
```
You can also configure the `logger` middleware with a little more complex settings
like "do not log the request bodies, but log the response bodies".
```ruby
conn = Faraday.new(url: 'http://httpbingo.org') do |faraday|
faraday.response :logger, nil, { bodies: { request: false, response: true } }
end end
``` ```
@ -84,9 +93,10 @@ end
### Customize the formatter ### Customize the formatter
You can also provide a custom formatter to control how requests and responses are logged. You can also provide a custom formatter to control how requests, responses and errors are logged.
Any custom formatter MUST implement the `request` and `response` method, with one argument which Any custom formatter MUST implement the `request` and `response` method, with one argument which
will be passed being the Faraday environment. will be passed being the Faraday environment.
Any custom formatter CAN implement the `error` method, with one argument which will be passed being the Faraday error.
If you make your formatter inheriting from `Faraday::Logging::Formatter`, If you make your formatter inheriting from `Faraday::Logging::Formatter`,
then the methods `debug`, `info`, `warn`, `error` and `fatal` are automatically delegated to the logger. then the methods `debug`, `info`, `warn`, `error` and `fatal` are automatically delegated to the logger.
@ -101,6 +111,11 @@ class MyFormatter < Faraday::Logging::Formatter
# Build a custom message using `env` # Build a custom message using `env`
info('Response') { 'Response Received' } info('Response') { 'Response Received' }
end end
def error(error)
# Build a custom message using `error`
info('Error') { 'Error Raised' }
end
end end
conn = Faraday.new(url: 'http://httpbingo.org/api_key=s3cr3t') do |faraday| conn = Faraday.new(url: 'http://httpbingo.org/api_key=s3cr3t') do |faraday|

View File

@ -6,7 +6,7 @@ module Faraday
class Formatter class Formatter
extend Forwardable extend Forwardable
DEFAULT_OPTIONS = { headers: true, bodies: false, DEFAULT_OPTIONS = { headers: true, bodies: false, errors: false,
log_level: :info }.freeze log_level: :info }.freeze
def initialize(logger:, options:) def initialize(logger:, options:)
@ -35,6 +35,18 @@ module Faraday
log_body('response', env[:body]) if env[:body] && log_body?(:response) log_body('response', env[:body]) if env[:body] && log_body?(:response)
end end
def error(error)
return unless log_errors?
error_log = proc { error.full_message }
public_send(log_level, 'error', &error_log)
log_headers('error', error.response_headers) if error.respond_to?(:response_headers) && log_headers?(:error)
return unless error.respond_to?(:response_body) && error.response_body && log_body?(:error)
log_body('error', error.response_body)
end
def filter(filter_word, filter_replacement) def filter(filter_word, filter_replacement)
@filter.push([filter_word, filter_replacement]) @filter.push([filter_word, filter_replacement])
end end
@ -75,6 +87,10 @@ module Faraday
end end
end end
def log_errors?
@options[:errors]
end
def apply_filters(output) def apply_filters(output)
@filter.each do |pattern, replacement| @filter.each do |pattern, replacement|
output = output.to_s.gsub(pattern, replacement) output = output.to_s.gsub(pattern, replacement)

View File

@ -17,6 +17,9 @@ module Faraday
app.call(env).on_complete do |environment| app.call(env).on_complete do |environment|
on_complete(environment) if respond_to?(:on_complete) on_complete(environment) if respond_to?(:on_complete)
end end
rescue StandardError => e
on_error(e) if respond_to?(:on_error)
raise
end end
def close def close

View File

@ -26,6 +26,10 @@ module Faraday
def on_complete(env) def on_complete(env)
@formatter.response(env) @formatter.response(env)
end end
def on_error(error)
@formatter.error(error) if @formatter.respond_to?(:error)
end
end end
end end
end end

View File

@ -33,6 +33,24 @@ RSpec.describe Faraday::Middleware do
end end
end end
describe '#on_error' do
subject do
Class.new(described_class) do
def on_error(error)
# do nothing
end
end.new(app)
end
it 'is called by #call' do
expect(app).to receive(:call).and_raise(Faraday::ConnectionFailed)
is_expected.to receive(:call).and_call_original
is_expected.to receive(:on_error)
expect { subject.call(double) }.to raise_error(Faraday::ConnectionFailed)
end
end
describe '#close' do describe '#close' do
context "with app that doesn't support \#close" do context "with app that doesn't support \#close" do
it 'should issue warning' do it 'should issue warning' do

View File

@ -64,6 +64,15 @@ RSpec.describe Faraday::Response::Logger do
expect(formatter).to receive(:response).with(an_instance_of(Faraday::Env)) expect(formatter).to receive(:response).with(an_instance_of(Faraday::Env))
conn.get '/hello' conn.get '/hello'
end end
context 'when no route' do
it 'delegates logging to the formatter' do
expect(formatter).to receive(:request).with(an_instance_of(Faraday::Env))
expect(formatter).to receive(:error).with(an_instance_of(Faraday::Adapter::Test::Stubs::NotFound))
expect { conn.get '/noroute' }.to raise_error(Faraday::Adapter::Test::Stubs::NotFound)
end
end
end end
context 'with custom formatter' do context 'with custom formatter' do
@ -94,6 +103,16 @@ RSpec.describe Faraday::Response::Logger do
expect(string_io.string).to match('GET http:/hello') expect(string_io.string).to match('GET http:/hello')
end end
it 'logs status' do
conn.get '/hello', nil, accept: 'text/html'
expect(string_io.string).to match('Status 200')
end
it 'does not log error message by default' do
expect { conn.get '/noroute' }.to raise_error(Faraday::Adapter::Test::Stubs::NotFound)
expect(string_io.string).not_to match(%(no stubbed request for get http:/noroute))
end
it 'logs request headers by default' do it 'logs request headers by default' do
conn.get '/hello', nil, accept: 'text/html' conn.get '/hello', nil, accept: 'text/html'
expect(string_io.string).to match(%(Accept: "text/html)) expect(string_io.string).to match(%(Accept: "text/html))
@ -188,6 +207,15 @@ RSpec.describe Faraday::Response::Logger do
end end
end end
context 'when logging errors' do
let(:logger_options) { { errors: true } }
it 'logs error message' do
expect { conn.get '/noroute' }.to raise_error(Faraday::Adapter::Test::Stubs::NotFound)
expect(string_io.string).to match(%(no stubbed request for get http:/noroute))
end
end
context 'when using log_level' do context 'when using log_level' do
let(:logger_options) { { bodies: true, log_level: :debug } } let(:logger_options) { { bodies: true, log_level: :debug } }