diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..3dbc0a6 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,30 @@ +name: CI +on: [push, pull_request] + +jobs: + rubocop: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: ruby/setup-ruby@v1 + with: + ruby-version: 3.4.4 + - name: Install dependencies + run: bundle install --jobs 4 --retry 3 + - name: Run rubocop + run: bundle exec rake rubocop + + specs: + runs-on: ubuntu-latest + strategy: + matrix: + ruby-version: [3.4.4, 3.3.8, 3.2.8, 3.1.7] + steps: + - uses: actions/checkout@v4 + - uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby-version }} + - name: Install dependencies + run: bundle install --jobs 4 --retry 3 + - name: Run specs + run: bundle exec rake spec diff --git a/.gitignore b/.gitignore index fe95d4d..0950536 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,5 @@ /.bundle/ /.yardoc -/Gemfile.lock /_yardoc/ /coverage/ /doc/ diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 0000000..c73d60d --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,16 @@ +inherit_from: .rubocop_todo.yml + +AllCops: + Exclude: + - grape_logging.gemspec + NewCops: enable + TargetRubyVersion: 3.1 + +Layout/LineLength: + Max: 100 + +Style/Documentation: + Enabled: false + +Style/FrozenStringLiteralComment: + Enabled: false diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml new file mode 100644 index 0000000..485fd77 --- /dev/null +++ b/.rubocop_todo.yml @@ -0,0 +1,82 @@ +# This configuration was generated by +# `rubocop --auto-gen-config` +# on 2025-06-03 20:45:19 UTC using RuboCop version 1.75.8. +# The point is for the user to remove these configuration records +# one by one as the offenses are removed from the code base. +# Note that changes in the inspected code, or installation of new +# versions of RuboCop, may require this file to be generated again. + +# Offense count: 2 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: Max, AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, AllowedPatterns, SplitStrings. +# URISchemes: http, https +Layout/LineLength: + Exclude: + - 'spec/lib/grape_logging/loggers/request_headers_spec.rb' + +# Offense count: 1 +# Configuration parameters: IgnoreLiteralBranches, IgnoreConstantBranches, IgnoreDuplicateElseBranch. +Lint/DuplicateBranch: + Exclude: + - 'lib/grape_logging/util/parameter_filter.rb' + +# Offense count: 1 +# Configuration parameters: AllowedParentClasses. +Lint/MissingSuper: + Exclude: + - 'lib/grape_logging/loggers/filter_parameters.rb' + +# Offense count: 3 +# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes. +Metrics/AbcSize: + Max: 37 + +# Offense count: 8 +# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns. +# AllowedMethods: refine +Metrics/BlockLength: + Max: 88 + +# Offense count: 1 +# Configuration parameters: CountComments, CountAsOne. +Metrics/ClassLength: + Max: 104 + +# Offense count: 3 +# Configuration parameters: AllowedMethods, AllowedPatterns. +Metrics/CyclomaticComplexity: + Max: 18 + +# Offense count: 6 +# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns. +Metrics/MethodLength: + Max: 22 + +# Offense count: 2 +# Configuration parameters: AllowedMethods, AllowedPatterns. +Metrics/PerceivedComplexity: + Max: 19 + +# Offense count: 2 +# Configuration parameters: EnforcedStyle, CheckMethodNames, CheckSymbols, AllowedIdentifiers, AllowedPatterns. +# SupportedStyles: snake_case, normalcase, non_integer +# AllowedIdentifiers: TLS1_1, TLS1_2, capture3, iso8601, rfc1123_date, rfc822, rfc2822, rfc3339, x86_64 +Naming/VariableNumber: + Exclude: + - 'spec/lib/grape_logging/formatters/rails_spec.rb' + +# Offense count: 10 +Style/OpenStructUse: + Exclude: + - 'spec/lib/grape_logging/loggers/client_env_spec.rb' + - 'spec/lib/grape_logging/loggers/filter_parameters_spec.rb' + - 'spec/lib/grape_logging/loggers/request_headers_spec.rb' + - 'spec/lib/grape_logging/loggers/response_spec.rb' + +# Offense count: 2 +# This cop supports unsafe autocorrection (--autocorrect-all). +# Configuration parameters: Mode. +Style/StringConcatenation: + Exclude: + - 'lib/grape_logging/formatters/json.rb' + - 'lib/grape_logging/formatters/logstash.rb' diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index b620a5f..0000000 --- a/.travis.yml +++ /dev/null @@ -1,6 +0,0 @@ -language: ruby -rvm: - - 2.3.1 -cache: bundler -script: - - bundle exec rspec \ No newline at end of file diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..e02f425 --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,130 @@ +PATH + remote: . + specs: + grape_logging (2.1.0) + grape + rack + +GEM + remote: https://rubygems.org/ + specs: + activesupport (8.0.2) + base64 + benchmark (>= 0.3) + bigdecimal + concurrent-ruby (~> 1.0, >= 1.3.1) + connection_pool (>= 2.2.5) + drb + i18n (>= 1.6, < 2) + logger (>= 1.4.2) + minitest (>= 5.1) + securerandom (>= 0.3) + tzinfo (~> 2.0, >= 2.0.5) + uri (>= 0.13.1) + ast (2.4.3) + base64 (0.3.0) + benchmark (0.4.1) + bigdecimal (3.2.1) + concurrent-ruby (1.3.5) + connection_pool (2.5.3) + diff-lcs (1.6.2) + drb (2.2.3) + dry-core (1.1.0) + concurrent-ruby (~> 1.0) + logger + zeitwerk (~> 2.6) + dry-inflector (1.2.0) + dry-logic (1.6.0) + bigdecimal + concurrent-ruby (~> 1.0) + dry-core (~> 1.1) + zeitwerk (~> 2.6) + dry-types (1.8.2) + bigdecimal (~> 3.0) + concurrent-ruby (~> 1.0) + dry-core (~> 1.0) + dry-inflector (~> 1.0) + dry-logic (~> 1.4) + zeitwerk (~> 2.6) + grape (2.3.0) + activesupport (>= 6) + dry-types (>= 1.1) + mustermann-grape (~> 1.1.0) + rack (>= 2) + zeitwerk + i18n (1.14.7) + concurrent-ruby (~> 1.0) + json (2.12.2) + language_server-protocol (3.17.0.5) + lint_roller (1.1.0) + logger (1.7.0) + minitest (5.25.5) + mustermann (3.0.3) + ruby2_keywords (~> 0.0.1) + mustermann-grape (1.1.0) + mustermann (>= 1.0.0) + parallel (1.27.0) + parser (3.3.8.0) + ast (~> 2.4.1) + racc + prism (1.4.0) + racc (1.8.1) + rack (3.1.15) + rainbow (3.1.1) + rake (13.3.0) + regexp_parser (2.10.0) + rspec (3.13.1) + rspec-core (~> 3.13.0) + rspec-expectations (~> 3.13.0) + rspec-mocks (~> 3.13.0) + rspec-core (3.13.4) + rspec-support (~> 3.13.0) + rspec-expectations (3.13.5) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-mocks (3.13.5) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-support (3.13.4) + rubocop (1.75.8) + json (~> 2.3) + language_server-protocol (~> 3.17.0.2) + lint_roller (~> 1.1.0) + parallel (~> 1.10) + parser (>= 3.3.0.2) + rainbow (>= 2.2.2, < 4.0) + regexp_parser (>= 2.9.3, < 3.0) + rubocop-ast (>= 1.44.0, < 2.0) + ruby-progressbar (~> 1.7) + unicode-display_width (>= 2.4.0, < 4.0) + rubocop-ast (1.45.0) + parser (>= 3.3.7.2) + prism (~> 1.4) + rubocop-rspec (3.6.0) + lint_roller (~> 1.1) + rubocop (~> 1.72, >= 1.72.1) + ruby-progressbar (1.13.0) + ruby2_keywords (0.0.5) + securerandom (0.4.1) + tzinfo (2.0.6) + concurrent-ruby (~> 1.0) + unicode-display_width (3.1.4) + unicode-emoji (~> 4.0, >= 4.0.4) + unicode-emoji (4.0.4) + uri (1.0.3) + zeitwerk (2.7.3) + +PLATFORMS + arm64-darwin-23 + ruby + +DEPENDENCIES + bundler (~> 2.5) + grape_logging! + rake (~> 13.2) + rspec (~> 3.13) + rubocop (~> 1.75) + rubocop-rspec (~> 3.6) + +BUNDLED WITH + 2.6.9 diff --git a/Rakefile b/Rakefile index 33eb07c..3f9046d 100644 --- a/Rakefile +++ b/Rakefile @@ -1,2 +1,13 @@ require 'bundler/gem_tasks' +require 'rspec/core' +require 'rspec/core/rake_task' +RSpec::Core::RakeTask.new(:spec) do |spec| + spec.rspec_opts = ['-fd'] + spec.pattern = FileList['spec/**/*_spec.rb'] +end + +require 'rubocop/rake_task' +RuboCop::RakeTask.new(:rubocop) + +task default: %i[rubocop spec] diff --git a/grape_logging.gemspec b/grape_logging.gemspec index b99365c..ea3ec72 100644 --- a/grape_logging.gemspec +++ b/grape_logging.gemspec @@ -1,3 +1,5 @@ + + lib = File.expand_path('lib', __dir__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require 'grape_logging/version' @@ -26,4 +28,7 @@ Gem::Specification.new do |spec| spec.add_development_dependency 'bundler', '~> 2.5' spec.add_development_dependency 'rake', '~> 13.2' spec.add_development_dependency 'rspec', '~> 3.13' + spec.add_development_dependency 'rubocop', '~> 1.75' + spec.add_development_dependency 'rubocop-rspec', '~> 3.6' + spec.metadata['rubygems_mfa_required'] = 'true' end diff --git a/lib/grape_logging/formatters/default.rb b/lib/grape_logging/formatters/default.rb index 54c1344..4f7734e 100644 --- a/lib/grape_logging/formatters/default.rb +++ b/lib/grape_logging/formatters/default.rb @@ -6,18 +6,24 @@ def call(severity, datetime, _, data) end def format(data) - if data.is_a?(String) + case data + when String data - elsif data.is_a?(Exception) + when Exception format_exception(data) - elsif data.is_a?(Hash) - "#{data.delete(:status)} -- #{format_hash(data.delete(:time))} -- #{data.delete(:method)} #{data.delete(:path)} #{format_hash(data)}" + when Hash + [ + data.delete(:status), + format_hash(data.delete(:time)), + "#{data.delete(:method)} #{data.delete(:path)} #{format_hash(data)}" + ].join(' -- ') else data.inspect end end private + def format_hash(hash) hash.keys.sort.map { |key| "#{key}=#{hash[key]}" }.join(' ') end diff --git a/lib/grape_logging/formatters/lograge.rb b/lib/grape_logging/formatters/lograge.rb index 7473314..582192f 100644 --- a/lib/grape_logging/formatters/lograge.rb +++ b/lib/grape_logging/formatters/lograge.rb @@ -10,7 +10,7 @@ def call(severity, datetime, _, data) view: time[:view], datetime: datetime.iso8601 }.merge(data) - ::Lograge.formatter.call(attributes) + "\n" + "#{::Lograge.formatter.call(attributes)}\n" end end end diff --git a/lib/grape_logging/formatters/logstash.rb b/lib/grape_logging/formatters/logstash.rb index 51192a2..5d3c36f 100644 --- a/lib/grape_logging/formatters/logstash.rb +++ b/lib/grape_logging/formatters/logstash.rb @@ -3,20 +3,21 @@ module Formatters class Logstash def call(severity, datetime, _, data) { - :'@timestamp' => datetime.iso8601, - :'@version' => '1', - :severity => severity + '@timestamp': datetime.iso8601, + '@version': '1', + severity: severity }.merge!(format(data)).to_json + "\n" end private def format(data) - if data.is_a?(Hash) + case data + when Hash data - elsif data.is_a?(String) + when String { message: data } - elsif data.is_a?(Exception) + when Exception format_exception(data) else { message: data.inspect } diff --git a/lib/grape_logging/formatters/rails.rb b/lib/grape_logging/formatters/rails.rb index 1451862..443ff66 100644 --- a/lib/grape_logging/formatters/rails.rb +++ b/lib/grape_logging/formatters/rails.rb @@ -3,13 +3,13 @@ module GrapeLogging module Formatters class Rails - def call(severity, datetime, _, data) - if data.is_a?(String) + case data + when String "#{severity[0..0]} [#{datetime}] #{severity} -- : #{data}\n" - elsif data.is_a?(Exception) + when Exception "#{severity[0..0]} [#{datetime}] #{severity} -- : #{format_exception(data)}\n" - elsif data.is_a?(Hash) + when Hash format_hash(data) else "#{data.inspect}\n" @@ -24,7 +24,7 @@ def format_exception(exception) [ "#{exception.message} (#{exception.class})", backtrace_array.join("\n") - ].reject{|line| line == ""}.join("\n") + ].reject { |line| line == '' }.join("\n") end def format_hash(hash) @@ -32,28 +32,28 @@ def format_hash(hash) # Completed 200 OK in 958ms (Views: 951.1ms | ActiveRecord: 3.8ms) # See: actionpack/lib/action_controller/log_subscriber.rb - message = "" + message = '' additions = [] status = hash.delete(:status) params = hash.delete(:params) - total_time = hash[:time] && hash[:time][:total] && hash[:time][:total].round(2) - view_time = hash[:time] && hash[:time][:view] && hash[:time][:view].round(2) - db_time = hash[:time] && hash[:time][:db] && hash[:time][:db].round(2) + total_time = hash[:time] && hash[:time][:total]&.round(2) + view_time = hash[:time] && hash[:time][:view]&.round(2) + db_time = hash[:time] && hash[:time][:db]&.round(2) additions << "Views: #{view_time}ms" if view_time additions << "DB: #{db_time}ms" if db_time message << " Parameters: #{params.inspect}\n" if params - message << "Completed #{status} #{::Rack::Utils::HTTP_STATUS_CODES[status]} in #{total_time}ms" - message << " (#{additions.join(" | ".freeze)})" if additions.size > 0 + code = ::Rack::Utils::HTTP_STATUS_CODES[status] + message << "Completed #{status} #{code} in #{total_time}ms" + message << " (#{additions.join(' | ')})" if additions.size.positive? message << "\n" message << "\n" if defined?(::Rails.env) && ::Rails.env.development? message end - end end end diff --git a/lib/grape_logging/loggers/base.rb b/lib/grape_logging/loggers/base.rb index 75b14dc..5197e9f 100644 --- a/lib/grape_logging/loggers/base.rb +++ b/lib/grape_logging/loggers/base.rb @@ -1,7 +1,7 @@ module GrapeLogging module Loggers class Base - def parameters(request, _status, _response_body) + def parameters(_request, _status, _response_body) {} end end diff --git a/lib/grape_logging/loggers/client_env.rb b/lib/grape_logging/loggers/client_env.rb index d3790d2..bf6fb25 100644 --- a/lib/grape_logging/loggers/client_env.rb +++ b/lib/grape_logging/loggers/client_env.rb @@ -3,8 +3,8 @@ module Loggers class ClientEnv < GrapeLogging::Loggers::Base def parameters(request, _status, _response_body) { - ip: request.env["HTTP_X_FORWARDED_FOR"] || request.env["REMOTE_ADDR"], - ua: request.env["HTTP_USER_AGENT"], + ip: request.env['HTTP_X_FORWARDED_FOR'] || request.env['REMOTE_ADDR'], + ua: request.env['HTTP_USER_AGENT'] } end end diff --git a/lib/grape_logging/loggers/filter_parameters.rb b/lib/grape_logging/loggers/filter_parameters.rb index 061f74b..8094f1e 100644 --- a/lib/grape_logging/loggers/filter_parameters.rb +++ b/lib/grape_logging/loggers/filter_parameters.rb @@ -3,8 +3,11 @@ module Loggers class FilterParameters < GrapeLogging::Loggers::Base AD_PARAMS = 'action_dispatch.request.parameters'.freeze - def initialize(filter_parameters = nil, replacement = nil, exceptions = %w(controller action format)) - @filter_parameters = filter_parameters || (defined?(::Rails.application) ? ::Rails.application.config.filter_parameters : []) + def initialize(filter_parameters = nil, replacement = nil, + exceptions = %w[controller action format]) + @filter_parameters = filter_parameters || ( + defined?(::Rails.application) ? ::Rails.application.config.filter_parameters : [] + ) @replacement = replacement || '[FILTERED]' @exceptions = exceptions end @@ -29,7 +32,7 @@ def safe_parameters(request) end def clean_parameters(parameters) - parameter_filter.filter(parameters).reject{ |key, _value| @exceptions.include?(key) } + parameter_filter.filter(parameters).except(*@exceptions) end end end diff --git a/lib/grape_logging/loggers/request_headers.rb b/lib/grape_logging/loggers/request_headers.rb index 0cd6ca5..35ba742 100644 --- a/lib/grape_logging/loggers/request_headers.rb +++ b/lib/grape_logging/loggers/request_headers.rb @@ -1,7 +1,6 @@ module GrapeLogging module Loggers class RequestHeaders < GrapeLogging::Loggers::Base - HTTP_PREFIX = 'HTTP_'.freeze def parameters(request, _status, _response_body) @@ -10,13 +9,12 @@ def parameters(request, _status, _response_body) request.env.each_pair do |k, v| next unless k.to_s.start_with? HTTP_PREFIX - k = k[5..-1].split('_').each(&:capitalize!).join('-') + k = k[5..].split('_').each(&:capitalize!).join('-') headers[k] = v end { headers: headers } end - end end end diff --git a/lib/grape_logging/loggers/response.rb b/lib/grape_logging/loggers/response.rb index 258faf5..0f3ab15 100644 --- a/lib/grape_logging/loggers/response.rb +++ b/lib/grape_logging/loggers/response.rb @@ -15,12 +15,11 @@ def parameters(_, status, response_body) # For example, if you POST on a PUT endpoint, response.body is equal to """". # It's strange but it's the Grape behavior... def parsed_response_body(response) - if response.respond_to?(:body) # Rack responses begin response.body.map { |body| JSON.parse(body.to_s) } - rescue + rescue StandardError response.body end else diff --git a/lib/grape_logging/middleware/request_logger.rb b/lib/grape_logging/middleware/request_logger.rb index fb1425b..afbd809 100644 --- a/lib/grape_logging/middleware/request_logger.rb +++ b/lib/grape_logging/middleware/request_logger.rb @@ -3,11 +3,12 @@ module GrapeLogging module Middleware class RequestLogger < Grape::Middleware::Base - - ActiveSupport::Notifications.subscribe('sql.active_record') do |*args| - event = ActiveSupport::Notifications::Event.new(*args) - GrapeLogging::Timings.append_db_runtime(event) - end if defined?(ActiveRecord) + if defined?(ActiveRecord) + ActiveSupport::Notifications.subscribe('sql.active_record') do |*args| + event = ActiveSupport::Notifications::Event.new(*args) + GrapeLogging::Timings.append_db_runtime(event) + end + end # Persist response status & response (body) # to use int in parameters @@ -18,10 +19,11 @@ def initialize(app, options = {}) @included_loggers = @options[:include] || [] @reporter = if options[:instrumentation_key] - Reporters::ActiveSupportReporter.new(@options[:instrumentation_key]) - else - Reporters::LoggerReporter.new(@options[:logger], @options[:formatter], @options[:log_level]) - end + Reporters::ActiveSupportReporter.new(@options[:instrumentation_key]) + else + Reporters::LoggerReporter.new(@options[:logger], @options[:formatter], + @options[:log_level]) + end end def before @@ -58,7 +60,7 @@ def call!(env) error = catch(:error) do begin @app_response = @app.call(@env) - rescue => e + rescue StandardError => e # Log as 500 + message after(e.respond_to?(:status) ? e.status : 500, e.message) @@ -138,7 +140,8 @@ def stop_time def collect_parameters parameters.tap do |params| @included_loggers.each do |logger| - params.merge! logger.parameters(request, response_status, response_body) do |_, oldval, newval| + params.merge! logger.parameters(request, response_status, + response_body) do |_, oldval, newval| oldval.respond_to?(:merge) ? oldval.merge(newval) : newval end end diff --git a/lib/grape_logging/multi_io.rb b/lib/grape_logging/multi_io.rb index 5c70c48..2ace6c5 100644 --- a/lib/grape_logging/multi_io.rb +++ b/lib/grape_logging/multi_io.rb @@ -5,11 +5,11 @@ def initialize(*targets) end def write(*args) - @targets.each {|t| t.write(*args)} + @targets.each { |t| t.write(*args) } end def close @targets.each(&:close) end end -end \ No newline at end of file +end diff --git a/lib/grape_logging/reporters/active_support_reporter.rb b/lib/grape_logging/reporters/active_support_reporter.rb index 392f9ae..e48ce3e 100644 --- a/lib/grape_logging/reporters/active_support_reporter.rb +++ b/lib/grape_logging/reporters/active_support_reporter.rb @@ -8,4 +8,4 @@ def perform(params) ActiveSupport::Notifications.instrument @instrumentation_key, params end end -end \ No newline at end of file +end diff --git a/lib/grape_logging/reporters/logger_reporter.rb b/lib/grape_logging/reporters/logger_reporter.rb index 9af7636..d92fc69 100644 --- a/lib/grape_logging/reporters/logger_reporter.rb +++ b/lib/grape_logging/reporters/logger_reporter.rb @@ -1,11 +1,11 @@ module Reporters class LoggerReporter def initialize(logger, formatter, log_level) - @logger = logger || Logger.new(STDOUT) + @logger = logger || Logger.new($stdout) @log_level = log_level || :info - if @logger.respond_to?(:formatter=) - @logger.formatter = formatter || @logger.formatter || GrapeLogging::Formatters::Default.new - end + return unless @logger.respond_to?(:formatter=) + + @logger.formatter = formatter || @logger.formatter || GrapeLogging::Formatters::Default.new end def perform(params) diff --git a/lib/grape_logging/timings.rb b/lib/grape_logging/timings.rb index 9c1e417..dde7cb2 100644 --- a/lib/grape_logging/timings.rb +++ b/lib/grape_logging/timings.rb @@ -1,6 +1,6 @@ module GrapeLogging module Timings - extend self + module_function def db_runtime=(value) Thread.current[:grape_db_runtime] = value @@ -18,4 +18,4 @@ def append_db_runtime(event) self.db_runtime += event.duration end end -end \ No newline at end of file +end diff --git a/lib/grape_logging/util/parameter_filter.rb b/lib/grape_logging/util/parameter_filter.rb index 1b04fb9..c5e6d0d 100644 --- a/lib/grape_logging/util/parameter_filter.rb +++ b/lib/grape_logging/util/parameter_filter.rb @@ -1,4 +1,4 @@ -if defined?(::Rails.application) +if defined?(Rails.application) if Gem::Version.new(Rails.version) < Gem::Version.new('6.0.0') class ParameterFilter < ActionDispatch::Http::ParameterFilter def initialize(_replacement, filter_parameters) @@ -6,7 +6,7 @@ def initialize(_replacement, filter_parameters) end end else - require "active_support/parameter_filter" + require 'active_support/parameter_filter' class ParameterFilter < ActiveSupport::ParameterFilter def initialize(_replacement, filter_parameters) @@ -37,9 +37,11 @@ def compiled_filter class CompiledFilter # :nodoc: def self.compile(replacement, filters) - return lambda { |params| params.dup } if filters.empty? + return lamda(&:dup) if filters.empty? - strings, regexps, blocks = [], [], [] + strings = [] + regexps = [] + blocks = [] filters.each do |item| case item @@ -52,11 +54,11 @@ def self.compile(replacement, filters) end end - deep_regexps, regexps = regexps.partition { |r| r.to_s.include?("\\.".freeze) } - deep_strings, strings = strings.partition { |s| s.include?("\\.".freeze) } + deep_regexps, regexps = regexps.partition { |r| r.to_s.include?('\\.') } + deep_strings, strings = strings.partition { |s| s.include?('\\.') } - regexps << Regexp.new(strings.join('|'.freeze), true) unless strings.empty? - deep_regexps << Regexp.new(deep_strings.join('|'.freeze), true) unless deep_strings.empty? + regexps << Regexp.new(strings.join('|'), true) unless strings.empty? + deep_regexps << Regexp.new(deep_strings.join('|'), true) unless deep_strings.empty? new replacement, regexps, deep_regexps, blocks end @@ -67,7 +69,7 @@ def initialize(replacement, regexps, deep_regexps, blocks) @replacement = replacement @regexps = regexps @deep_regexps = deep_regexps.any? ? deep_regexps : nil - @blocks = blocks + @blocks = blocks end def call(original_params, parents = []) @@ -77,7 +79,9 @@ def call(original_params, parents = []) parents.push(key) if deep_regexps if regexps.any? { |r| key =~ r } value = @replacement - elsif deep_regexps && (joined = parents.join('.')) && deep_regexps.any? { |r| joined =~ r } + elsif deep_regexps && (joined = parents.join('.')) && deep_regexps.any? do |r| + joined =~ r + end value = @replacement elsif value.is_a?(Hash) value = call(value, parents) diff --git a/lib/grape_logging/version.rb b/lib/grape_logging/version.rb index 33fa879..b6b302b 100644 --- a/lib/grape_logging/version.rb +++ b/lib/grape_logging/version.rb @@ -1,3 +1,3 @@ module GrapeLogging - VERSION = '2.1.0' + VERSION = '2.1.0'.freeze end diff --git a/spec/lib/grape_logging/formatters/rails_spec.rb b/spec/lib/grape_logging/formatters/rails_spec.rb index 1914813..0d4f91e 100644 --- a/spec/lib/grape_logging/formatters/rails_spec.rb +++ b/spec/lib/grape_logging/formatters/rails_spec.rb @@ -2,11 +2,11 @@ describe GrapeLogging::Formatters::Rails do let(:formatter) { described_class.new } - let(:severity) { "INFO" } + let(:severity) { 'INFO' } let(:datetime) { Time.new('2018', '03', '02', '10', '35', '04', '+13:00') } let(:exception_data) { ArgumentError.new('Message') } - let(:hash_data) { + let(:hash_data) do { status: 200, time: { @@ -14,11 +14,11 @@ db: 40.63, view: 231.76999999999998 }, - method: "GET", - path: "/api/endpoint", - host: "localhost" + method: 'GET', + path: '/api/endpoint', + host: 'localhost' } - } + end describe '#call' do context 'string data' do @@ -36,7 +36,7 @@ message = formatter.call(severity, datetime, nil, exception_data) lines = message.split("\n") - expect(lines[0]).to eq "I [2018-03-02 10:35:04 +1300] INFO -- : Message (ArgumentError)" + expect(lines[0]).to eq 'I [2018-03-02 10:35:04 +1300] INFO -- : Message (ArgumentError)' expect(lines[1]).to include '.rb' expect(lines.size).to be > 1 end @@ -52,9 +52,9 @@ it 'includes params if included (from GrapeLogging::Loggers::FilterParameters)' do hash_data.merge!( params: { - "some_param" => { - value_1: "123", - value_2: "456" + 'some_param' => { + value_1: '123', + value_2: '456' } } ) @@ -62,12 +62,18 @@ message = formatter.call(severity, datetime, nil, hash_data) lines = message.split("\n") - expect(lines.first).to eq ' Parameters: {"some_param" => {value_1: "123", value_2: "456"}}' - expect(lines.last).to eq "Completed 200 OK in 272.4ms (Views: 231.77ms | DB: 40.63ms)" + expect(lines.first).to eq( + if RUBY_VERSION.start_with?('3.4') + ' Parameters: {"some_param" => {value_1: "123", value_2: "456"}}' + else + ' Parameters: {"some_param"=>{:value_1=>"123", :value_2=>"456"}}' + end + ) + expect(lines.last).to eq 'Completed 200 OK in 272.4ms (Views: 231.77ms | DB: 40.63ms)' end end - context "unhandled data" do + context 'unhandled data' do it 'returns the #inspect string representation' do message = formatter.call(severity, datetime, nil, [1, 2, 3]) @@ -75,5 +81,4 @@ end end end - end diff --git a/spec/lib/grape_logging/loggers/client_env_spec.rb b/spec/lib/grape_logging/loggers/client_env_spec.rb index 1424d59..ff29859 100644 --- a/spec/lib/grape_logging/loggers/client_env_spec.rb +++ b/spec/lib/grape_logging/loggers/client_env_spec.rb @@ -4,14 +4,14 @@ describe GrapeLogging::Loggers::ClientEnv do let(:ip) { '10.0.0.1' } let(:user_agent) { 'user agent' } - let(:forwarded_for) { "forwarded for" } - let(:remote_addr) { "remote address" } + let(:forwarded_for) { 'forwarded for' } + let(:remote_addr) { 'remote address' } context 'forwarded for' do let(:mock_request) do OpenStruct.new(env: { - "HTTP_X_FORWARDED_FOR" => forwarded_for - }) + 'HTTP_X_FORWARDED_FOR' => forwarded_for + }) end it 'sets the ip key' do @@ -27,8 +27,8 @@ context 'remote address' do let(:mock_request) do OpenStruct.new(env: { - "REMOTE_ADDR" => remote_addr - }) + 'REMOTE_ADDR' => remote_addr + }) end it 'sets the ip key' do @@ -39,8 +39,8 @@ context 'user agent' do let(:mock_request) do OpenStruct.new(env: { - "HTTP_USER_AGENT" => user_agent - }) + 'HTTP_USER_AGENT' => user_agent + }) end it 'sets the ua key' do diff --git a/spec/lib/grape_logging/loggers/filter_parameters_spec.rb b/spec/lib/grape_logging/loggers/filter_parameters_spec.rb index 1f298f0..924ed0a 100644 --- a/spec/lib/grape_logging/loggers/filter_parameters_spec.rb +++ b/spec/lib/grape_logging/loggers/filter_parameters_spec.rb @@ -1,26 +1,27 @@ require 'spec_helper' require 'ostruct' - describe GrapeLogging::Loggers::FilterParameters do let(:filtered_parameters) { %w[one four] } let(:mock_request) do - OpenStruct.new(params: { - this_one: 'this one', - that_one: 'one', - two: 'two', - three: 'three', - four: 'four' - }) + OpenStruct.new( + params: { + this_one: 'this one', + that_one: 'one', + two: 'two', + three: 'three', + four: 'four' + } + ) end let(:mock_request_with_deep_nesting) do - deep_clone = lambda { Marshal.load Marshal.dump mock_request.params } + deep_clone = -> { Marshal.load Marshal.dump mock_request.params } OpenStruct.new( params: deep_clone.call.merge( five: deep_clone.call.merge( - deep_clone.call.merge({six: {seven: 'seven', eight: 'eight', one: 'another one'}}) + deep_clone.call.merge({ six: { seven: 'seven', eight: 'eight', one: 'another one' } }) ) ) ) @@ -34,35 +35,39 @@ shared_examples 'filtering' do it 'filters out sensitive parameters' do - expect(subject.parameters(mock_request, 200, nil)).to eq(params: { - this_one: subject.instance_variable_get('@replacement'), - that_one: subject.instance_variable_get('@replacement'), - two: 'two', - three: 'three', - four: subject.instance_variable_get('@replacement'), - }) + expect(subject.parameters(mock_request, 200, nil)).to eq( + params: { + this_one: subject.instance_variable_get('@replacement'), + that_one: subject.instance_variable_get('@replacement'), + two: 'two', + three: 'three', + four: subject.instance_variable_get('@replacement') + } + ) end it 'deeply filters out sensitive parameters' do - expect(subject.parameters(mock_request_with_deep_nesting, 200, nil)).to eq(params: { - this_one: subject.instance_variable_get('@replacement'), - that_one: subject.instance_variable_get('@replacement'), - two: 'two', - three: 'three', - four: subject.instance_variable_get('@replacement'), - five: { + expect(subject.parameters(mock_request_with_deep_nesting, 200, nil)).to eq( + params: { this_one: subject.instance_variable_get('@replacement'), that_one: subject.instance_variable_get('@replacement'), two: 'two', three: 'three', four: subject.instance_variable_get('@replacement'), - six: { - seven: 'seven', - eight: 'eight', - one: subject.instance_variable_get('@replacement'), - }, - }, - }) + five: { + this_one: subject.instance_variable_get('@replacement'), + that_one: subject.instance_variable_get('@replacement'), + two: 'two', + three: 'three', + four: subject.instance_variable_get('@replacement'), + six: { + seven: 'seven', + eight: 'eight', + one: subject.instance_variable_get('@replacement') + } + } + } + ) end end diff --git a/spec/lib/grape_logging/loggers/request_headers_spec.rb b/spec/lib/grape_logging/loggers/request_headers_spec.rb index c5bc511..91c3301 100644 --- a/spec/lib/grape_logging/loggers/request_headers_spec.rb +++ b/spec/lib/grape_logging/loggers/request_headers_spec.rb @@ -3,38 +3,40 @@ describe GrapeLogging::Loggers::RequestHeaders do let(:mock_request) do - OpenStruct.new(env: {HTTP_REFERER: 'http://example.com', HTTP_ACCEPT: 'text/plain'}) + OpenStruct.new(env: { HTTP_REFERER: 'http://example.com', HTTP_ACCEPT: 'text/plain' }) end let(:mock_request_with_unhandle_headers) do OpenStruct.new(env: { - HTTP_REFERER: 'http://example.com', - "PATH_INFO"=>"/api/v1/users" - }) + HTTP_REFERER: 'http://example.com', + 'PATH_INFO' => '/api/v1/users' + }) end let(:mock_request_with_long_headers) do OpenStruct.new(env: { - HTTP_REFERER: 'http://example.com', - HTTP_USER_AGENT: "Mozilla/5.0" - }) + HTTP_REFERER: 'http://example.com', + HTTP_USER_AGENT: 'Mozilla/5.0' + }) end it 'strips HTTP_ from the parameter' do expect(subject.parameters(mock_request, 200, nil)).to eq({ - headers: {'Referer' => 'http://example.com', 'Accept' => 'text/plain'} - }) + headers: { 'Referer' => 'http://example.com', 'Accept' => 'text/plain' } + }) end it 'only handle things which start with HTTP_' do expect(subject.parameters(mock_request_with_unhandle_headers, 200, nil)).to eq({ - headers: {'Referer' => 'http://example.com' } - }) + headers: { 'Referer' => 'http://example.com' } + }) end it 'substitutes _ with -' do expect(subject.parameters(mock_request_with_long_headers, 200, nil)).to eq({ - headers: {'Referer' => 'http://example.com', 'User-Agent' => 'Mozilla/5.0' } - }) + headers: { + 'Referer' => 'http://example.com', 'User-Agent' => 'Mozilla/5.0' + } + }) end end diff --git a/spec/lib/grape_logging/loggers/response_spec.rb b/spec/lib/grape_logging/loggers/response_spec.rb index 187d82b..5a768f5 100644 --- a/spec/lib/grape_logging/loggers/response_spec.rb +++ b/spec/lib/grape_logging/loggers/response_spec.rb @@ -4,27 +4,27 @@ describe GrapeLogging::Loggers::Response do context 'with a parseable JSON body' do let(:response) do - OpenStruct.new(body: [{"one": "two", "three": {"four": 5}}]) + OpenStruct.new(body: [{ one: 'two', three: { four: 5 } }]) end it 'returns an array of parsed JSON objects' do expect(subject.parameters(nil, 200, response)).to eq({ - status: 200, - response: [response.body.first], - }) + status: 200, + response: [response.body.first] + }) end end context 'with a body that is not parseable JSON' do let(:response) do - OpenStruct.new(body: "this is a body") + OpenStruct.new(body: 'this is a body') end it 'just returns the body' do expect(subject.parameters(nil, 200, response)).to eq({ - status: 200, - response: response.body, - }) + status: 200, + response: response.body + }) end end end diff --git a/spec/lib/grape_logging/middleware/request_logger_spec.rb b/spec/lib/grape_logging/middleware/request_logger_spec.rb index fc38107..5b02e09 100644 --- a/spec/lib/grape_logging/middleware/request_logger_spec.rb +++ b/spec/lib/grape_logging/middleware/request_logger_spec.rb @@ -3,10 +3,10 @@ describe GrapeLogging::Middleware::RequestLogger do let(:subject) { request.send(request_method, path) } - let(:app) { proc{ [status, {} , ['response body']] } } + let(:app) { proc { [status, {}, ['response body']] } } let(:stack) { described_class.new app, options } let(:request) { Rack::MockRequest.new(stack) } - let(:options) { {include: [], logger: logger} } + let(:options) { { include: [], logger: logger } } let(:logger) { double('logger') } let(:path) { '/' } let(:request_method) { 'get' } @@ -49,7 +49,7 @@ end context 'with a nil response' do - let(:app) { proc{ [500, {} , nil] } } + let(:app) { proc { [500, {}, nil] } } it 'should log "fail" instead of a status' do expect(Rack::MockResponse).to receive(:new) { nil } expect(logger).to receive('info') do |arguments| @@ -64,7 +64,7 @@ options[:include] << GrapeLogging::Loggers::RequestHeaders.new options[:include] << GrapeLogging::Loggers::ClientEnv.new options[:include] << GrapeLogging::Loggers::Response.new - options[:include] << GrapeLogging::Loggers::FilterParameters.new(["replace_me"]) + options[:include] << GrapeLogging::Loggers::FilterParameters.new(['replace_me']) end %w[get put post delete options head patch].each do |the_method| @@ -84,9 +84,9 @@ it 'should filter parameters in the log' do expect(logger).to receive('info') do |arguments| expect(arguments[:params]).to eq( - "replace_me" => '[FILTERED]', - "replace_me_too" => '[FILTERED]', - "cant_touch_this" => 'should see' + 'replace_me' => '[FILTERED]', + 'replace_me_too' => '[FILTERED]', + 'cant_touch_this' => 'should see' ) end parameters = { diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 59d065a..a68250e 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,4 +1,4 @@ -$:.unshift '.' +$LOAD_PATH.unshift '.' require 'lib/grape_logging' @@ -33,57 +33,55 @@ # triggering implicit auto-inclusion in groups with matching metadata. config.shared_context_metadata_behavior = :apply_to_host_groups -# The settings below are suggested to provide a good initial experience -# with RSpec, but feel free to customize to your heart's content. -=begin - # This allows you to limit a spec run to individual examples or groups - # you care about by tagging them with `:focus` metadata. When nothing - # is tagged with `:focus`, all examples get run. RSpec also provides - # aliases for `it`, `describe`, and `context` that include `:focus` - # metadata: `fit`, `fdescribe` and `fcontext`, respectively. - config.filter_run_when_matching :focus - - # Allows RSpec to persist some state between runs in order to support - # the `--only-failures` and `--next-failure` CLI options. We recommend - # you configure your source control system to ignore this file. - config.example_status_persistence_file_path = "spec/examples.txt" - - # Limits the available syntax to the non-monkey patched syntax that is - # recommended. For more details, see: - # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/ - # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/ - # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode - config.disable_monkey_patching! - - # This setting enables warnings. It's recommended, but in some cases may - # be too noisy due to issues in dependencies. - config.warnings = true - - # Many RSpec users commonly either run the entire suite or an individual - # file, and it's useful to allow more verbose output when running an - # individual spec file. - if config.files_to_run.one? - # Use the documentation formatter for detailed output, - # unless a formatter has already been configured - # (e.g. via a command-line flag). - config.default_formatter = 'doc' - end - - # Print the 10 slowest examples and example groups at the - # end of the spec run, to help surface which specs are running - # particularly slow. - config.profile_examples = 10 - - # Run specs in random order to surface order dependencies. If you find an - # order dependency and want to debug it, you can fix the order by providing - # the seed, which is printed after each run. - # --seed 1234 - config.order = :random - - # Seed global randomization in this process using the `--seed` CLI option. - # Setting this allows you to use `--seed` to deterministically reproduce - # test failures related to randomization by passing the same `--seed` value - # as the one that triggered the failure. - Kernel.srand config.seed -=end + # The settings below are suggested to provide a good initial experience + # with RSpec, but feel free to customize to your heart's content. + # # This allows you to limit a spec run to individual examples or groups + # # you care about by tagging them with `:focus` metadata. When nothing + # # is tagged with `:focus`, all examples get run. RSpec also provides + # # aliases for `it`, `describe`, and `context` that include `:focus` + # # metadata: `fit`, `fdescribe` and `fcontext`, respectively. + # config.filter_run_when_matching :focus + # + # # Allows RSpec to persist some state between runs in order to support + # # the `--only-failures` and `--next-failure` CLI options. We recommend + # # you configure your source control system to ignore this file. + # config.example_status_persistence_file_path = "spec/examples.txt" + # + # # Limits the available syntax to the non-monkey patched syntax that is + # # recommended. For more details, see: + # # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/ + # # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/ + # # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode + # config.disable_monkey_patching! + # + # # This setting enables warnings. It's recommended, but in some cases may + # # be too noisy due to issues in dependencies. + # config.warnings = true + # + # # Many RSpec users commonly either run the entire suite or an individual + # # file, and it's useful to allow more verbose output when running an + # # individual spec file. + # if config.files_to_run.one? + # # Use the documentation formatter for detailed output, + # # unless a formatter has already been configured + # # (e.g. via a command-line flag). + # config.default_formatter = 'doc' + # end + # + # # Print the 10 slowest examples and example groups at the + # # end of the spec run, to help surface which specs are running + # # particularly slow. + # config.profile_examples = 10 + # + # # Run specs in random order to surface order dependencies. If you find an + # # order dependency and want to debug it, you can fix the order by providing + # # the seed, which is printed after each run. + # # --seed 1234 + # config.order = :random + # + # # Seed global randomization in this process using the `--seed` CLI option. + # # Setting this allows you to use `--seed` to deterministically reproduce + # # test failures related to randomization by passing the same `--seed` value + # # as the one that triggered the failure. + # Kernel.srand config.seed end