Merge pull request #16 from mmclead/one_line_log_option

adding an option to format log message to be one line
This commit is contained in:
Ryan Buckley 2019-03-07 15:15:21 -08:00 committed by GitHub
commit 3e64a80ab7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 81 additions and 30 deletions

5
.byebug_history Normal file
View File

@ -0,0 +1,5 @@
exit
c
log_statements.compact.delete_if(&:empty?).each(&:strip!)
log_statements.compact.delete_if(&:empty?).each(&:lstrip)
log_statements.compact.delete_if(&:empty?).each(&:strip)

View File

@ -55,6 +55,7 @@ Completed 422 in 6.29ms
The middleware logger can be customized with the following options: The middleware logger can be customized with the following options:
* The `:logger` option can be any object that responds to `.info(String)` * The `:logger` option can be any object that responds to `.info(String)`
* The `:condensed` option configures the log output to be on one line instead of multiple. It accepts `true` or `false`. The default configuration is `false`
* The `:filter` option can be any object that responds to `.filter(Hash)` and returns a hash. * The `:filter` option can be any object that responds to `.filter(Hash)` and returns a hash.
* The `:headers` option can be either `:all` or array of strings. * The `:headers` option can be either `:all` or array of strings.
+ If `:all`, all request headers will be output. + If `:all`, all request headers will be output.
@ -65,6 +66,7 @@ For example:
```ruby ```ruby
insert_after Grape::Middleware::Formatter, Grape::Middleware::Logger, { insert_after Grape::Middleware::Formatter, Grape::Middleware::Logger, {
logger: Logger.new(STDERR), logger: Logger.new(STDERR),
condensed: true,
filter: Class.new { def filter(opts) opts.reject { |k, _| k.to_s == 'password' } end }.new, filter: Class.new { def filter(opts) opts.reject { |k, _| k.to_s == 'password' } end }.new,
headers: %w(version cache-control) headers: %w(version cache-control)
} }

View File

@ -7,7 +7,7 @@ class Grape::Middleware::Logger < Grape::Middleware::Globals
attr_reader :logger attr_reader :logger
class << self class << self
attr_accessor :logger, :filter, :headers attr_accessor :logger, :filter, :headers, :condensed
def default_logger def default_logger
default = Logger.new(STDOUT) default = Logger.new(STDOUT)
@ -20,6 +20,7 @@ class Grape::Middleware::Logger < Grape::Middleware::Globals
super super
@options[:filter] ||= self.class.filter @options[:filter] ||= self.class.filter
@options[:headers] ||= self.class.headers @options[:headers] ||= self.class.headers
@options[:condensed] ||= false
@logger = options[:logger] || self.class.logger || self.class.default_logger @logger = options[:logger] || self.class.logger || self.class.default_logger
end end
@ -27,15 +28,19 @@ class Grape::Middleware::Logger < Grape::Middleware::Globals
start_time start_time
# sets env['grape.*'] # sets env['grape.*']
super super
logger.info ''
logger.info %Q(Started %s "%s" at %s) % [ log_statements = [
'',
%Q(Started %s "%s" at %s) % [
env[Grape::Env::GRAPE_REQUEST].request_method, env[Grape::Env::GRAPE_REQUEST].request_method,
env[Grape::Env::GRAPE_REQUEST].path, env[Grape::Env::GRAPE_REQUEST].path,
start_time.to_s start_time.to_s
] ],
logger.info %Q(Processing by #{processed_by}) %Q(Processing by #{processed_by}),
logger.info %Q( Parameters: #{parameters}) %Q( Parameters: #{parameters})]
logger.info %Q( Headers: #{headers}) if @options[:headers]
log_statements.append(%Q( Headers: #{headers})) if @options[:headers]
log_info(log_statements)
end end
# @note Error and exception handling are required for the +after+ hooks # @note Error and exception handling are required for the +after+ hooks
@ -64,8 +69,12 @@ class Grape::Middleware::Logger < Grape::Middleware::Globals
end end
def after(status) def after(status)
logger.info "Completed #{status} in #{((Time.now - start_time) * 1000).round(2)}ms" log_info(
logger.info '' [
"Completed #{status} in #{((Time.now - start_time) * 1000).round(2)}ms",
''
]
)
end end
# #
@ -120,6 +129,14 @@ class Grape::Middleware::Logger < Grape::Middleware::Globals
result.concat endpoint.options[:path].map { |path| path.to_s.sub(BACKSLASH, '') } result.concat endpoint.options[:path].map { |path| path.to_s.sub(BACKSLASH, '') }
endpoint.options[:for].to_s << result.join(BACKSLASH) endpoint.options[:for].to_s << result.join(BACKSLASH)
end end
def log_info(log_statements=[])
if @options[:condensed]
logger.info log_statements.compact.delete_if(&:empty?).each(&:strip!).join(" - ")
else
log_statements.each { |log_statement| logger.info log_statement }
end
end
end end
require_relative 'logger/railtie' if defined?(Rails) require_relative 'logger/railtie' if defined?(Rails)

View File

@ -11,7 +11,10 @@ describe Grape::Middleware::Logger, type: :integration do
let(:grape_endpoint) { build(:grape_endpoint) } let(:grape_endpoint) { build(:grape_endpoint) }
let(:env) { build(:expected_env, grape_endpoint: grape_endpoint) } let(:env) { build(:expected_env, grape_endpoint: grape_endpoint) }
it 'logs all parts of the request' do context 'when the option[:condensed] is false' do
let(:options) { { filter: build(:param_filter), logger: Logger.new(Tempfile.new('logger')), condensed: false } }
it 'logs all parts of the request on multiple lines' do
expect(subject.logger).to receive(:info).with '' expect(subject.logger).to receive(:info).with ''
expect(subject.logger).to receive(:info).with %Q(Started POST "/api/1.0/users" at #{subject.start_time}) expect(subject.logger).to receive(:info).with %Q(Started POST "/api/1.0/users" at #{subject.start_time})
expect(subject.logger).to receive(:info).with %Q(Processing by TestAPI/users) expect(subject.logger).to receive(:info).with %Q(Processing by TestAPI/users)
@ -20,6 +23,17 @@ describe Grape::Middleware::Logger, type: :integration do
expect(subject.logger).to receive(:info).with '' expect(subject.logger).to receive(:info).with ''
subject.call!(env) subject.call!(env)
end end
end
context 'when the options[:condensed is true' do
let(:options) { { filter: build(:param_filter), logger: Logger.new(Tempfile.new('logger')), condensed: true } }
it 'logs all parts of the request on one line' do
expect(subject.logger).to receive(:info).with %Q(Started POST "/api/1.0/users" at #{subject.start_time} - Processing by TestAPI/users - Parameters: {"id"=>"101001", "secret"=>"[FILTERED]", "customer"=>[], "name"=>"foo", "password"=>"[FILTERED]"})
expect(subject.logger).to receive(:info).with /Completed 200 in \d+.\d+ms/
subject.call!(env)
end
end
context 'when an exception occurs' do context 'when an exception occurs' do
it 'logs all parts of the request including the error class' do it 'logs all parts of the request including the error class' do

View File

@ -42,7 +42,10 @@ describe Grape::Middleware::Logger, type: :rails_integration do
end end
end end
it 'logs all parts of the request' do context 'when the option[:condensed] is false' do
let(:options) { { condensed: false } }
it 'logs all parts of the request on multiple lines' do
expect(subject.logger).to receive(:info).with '' expect(subject.logger).to receive(:info).with ''
expect(subject.logger).to receive(:info).with %Q(Started POST "/api/1.0/users" at #{subject.start_time}) expect(subject.logger).to receive(:info).with %Q(Started POST "/api/1.0/users" at #{subject.start_time})
expect(subject.logger).to receive(:info).with %Q(Processing by TestAPI/users) expect(subject.logger).to receive(:info).with %Q(Processing by TestAPI/users)
@ -51,6 +54,16 @@ describe Grape::Middleware::Logger, type: :rails_integration do
expect(subject.logger).to receive(:info).with '' expect(subject.logger).to receive(:info).with ''
subject.call!(env) subject.call!(env)
end end
end
context 'when the option[:condensed] is true' do
let(:options) { { condensed: true } }
it 'logs all parts of the request on one line' do
expect(subject.logger).to receive(:info).with %Q(Started POST "/api/1.0/users" at #{subject.start_time} - Processing by TestAPI/users - Parameters: {"id"=>"101001", "secret"=>"key", "customer"=>[], "name"=>"foo", "password"=>"[FILTERED]"})
expect(subject.logger).to receive(:info).with /Completed 200 in \d+.\d+ms/
subject.call!(env)
end
end
describe 'the "processing by" section' do describe 'the "processing by" section' do
before { subject.call!(env) } before { subject.call!(env) }