grape_logging 1.8.4 → 3.0.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.
data/RELEASING.md ADDED
@@ -0,0 +1,113 @@
1
+ # Releasing grape_logging
2
+
3
+ There're no particular rules about when to release grape_logging. Release bug fixes frequently, features not so frequently and breaking API changes rarely.
4
+
5
+ ### Pre-flight Checks
6
+
7
+ Run tests, check that all tests succeed locally.
8
+
9
+ ```
10
+ bundle install
11
+ rake
12
+ ```
13
+
14
+ Check that the last build succeeded in [GitHub Actions](https://github.com/aserafin/grape_logging/actions) for all supported platforms.
15
+
16
+ ### Update Changelog
17
+
18
+ Change "Unreleased" in [CHANGELOG.md](https://github.com/aserafin/grape_logging/blob/master/CHANGELOG.md) to the new version and date:
19
+
20
+ ```
21
+ ## [1.8.5] - 2024-06-28
22
+
23
+ ### Changed
24
+ - Description of changes
25
+
26
+ ### Fixed
27
+ - Description of fixes
28
+
29
+ ### Added
30
+ - Description of additions
31
+
32
+ [1.8.5]: https://github.com/aserafin/grape_logging/compare/v1.8.4...v1.8.5
33
+ ```
34
+
35
+ Remove the line with "Your contribution here.", since there will be no more contributions to this release.
36
+
37
+ Only include the sections (Changed, Fixed, Added, etc.) that have actual changes.
38
+
39
+ Commit your changes.
40
+
41
+ ```shell
42
+ git add CHANGELOG.md lib/grape_logging/version.rb
43
+ git commit -m "Preparing for release, 1.8.5."
44
+ git push
45
+ ```
46
+
47
+ ### Release on RubyGems and GitHub
48
+
49
+ #### Option 1: Automated (Recommended)
50
+
51
+ Use the combined task that releases the gem and creates a GitHub release:
52
+
53
+ ```shell
54
+ rake github_release
55
+ ```
56
+
57
+ This will:
58
+ 1. Build and push the gem to RubyGems
59
+ 2. Create and push the git tag
60
+ 3. Create a GitHub release with auto-generated changelog
61
+
62
+ #### Option 2: Manual
63
+
64
+ First, release the gem:
65
+
66
+ ```shell
67
+ rake release
68
+ ```
69
+
70
+ Output will look something like:
71
+ ```
72
+ grape_logging 1.8.5 built to pkg/grape_logging-1.8.5.gem.
73
+ Tagged v1.8.5.
74
+ Pushed git commits and tags.
75
+ Pushed grape_logging 1.8.5 to rubygems.org.
76
+ ```
77
+
78
+ Then create the GitHub release on the web or using `gh`:
79
+
80
+ ```
81
+ gh release create v1.8.5 --generate-notes --verify-tag
82
+ ```
83
+
84
+ This uses GitHub's automatic changelog generation feature to create release notes from merged pull requests and commits since the last release.
85
+
86
+ ### Prepare for the Next Version
87
+
88
+ Modify `lib/grape_logging/version.rb`, increment the version number (eg. change `1.8.5` to `1.8.6`).
89
+
90
+ ```ruby
91
+ module GrapeLogging
92
+ VERSION = '1.8.6'.freeze
93
+ end
94
+ ```
95
+
96
+ Add the next release to [CHANGELOG.md](https://github.com/aserafin/grape_logging/blob/master/CHANGELOG.md).
97
+
98
+ ```
99
+ ## [1.8.6] - Unreleased
100
+
101
+ ### Changed or Fixed or Added
102
+ - Your contribution here.
103
+
104
+ [1.8.6]: https://github.com/aserafin/grape_logging/compare/v1.8.5...master
105
+ ```
106
+
107
+ Commit your changes.
108
+
109
+ ```
110
+ git add CHANGELOG.md lib/grape_logging/version.rb
111
+ git commit -m "Bump version to 1.8.6."
112
+ git push
113
+ ```
data/Rakefile CHANGED
@@ -1,2 +1,43 @@
1
1
  require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+ require 'rubocop/rake_task'
2
4
 
5
+ RSpec::Core::RakeTask.new(:spec) do |spec|
6
+ spec.rspec_opts = ['-fd -c']
7
+ spec.pattern = FileList['spec/**/*_spec.rb']
8
+ end
9
+
10
+ RuboCop::RakeTask.new(:rubocop) do |t|
11
+ t.patterns = ['lib/**/*.rb', 'spec/**/*.rb', 'Rakefile', 'Gemfile', 'grape_logging.gemspec']
12
+ end
13
+
14
+ task default: %i[spec rubocop]
15
+
16
+ desc 'Release gem and create GitHub release'
17
+ task github_release: :release do
18
+ require 'grape_logging/version'
19
+
20
+ version = "v#{GrapeLogging::VERSION}"
21
+
22
+ # Check if gh CLI is available
23
+ unless system('which gh > /dev/null 2>&1')
24
+ puts "\n⚠️ GitHub CLI (gh) not found"
25
+ puts 'To create a GitHub release with auto-generated changelog, install gh:'
26
+ puts ' brew install gh # macOS with Homebrew'
27
+ puts ' # or visit: https://github.com/cli/cli#installation'
28
+ puts "\nYou can manually create the release with:"
29
+ puts " gh release create v#{GrapeLogging::VERSION} --generate-notes"
30
+ next
31
+ end
32
+
33
+ # Create GitHub release
34
+ puts "\nCreating GitHub release #{version}..."
35
+
36
+ if system('gh', 'release', 'create', version, '--generate-notes', '--verify-tag')
37
+ puts "✅ GitHub release #{version} created successfully"
38
+ else
39
+ puts '❌ Failed to create GitHub release'
40
+ puts 'You can manually create it with:'
41
+ puts " gh release create '#{version}' --generate-notes --verify-tag"
42
+ end
43
+ end
data/bin/console CHANGED
File without changes
@@ -1,29 +1,32 @@
1
- # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
1
+ lib = File.expand_path('lib', __dir__)
3
2
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
3
  require 'grape_logging/version'
5
4
 
6
5
  Gem::Specification.new do |spec|
7
6
  spec.name = 'grape_logging'
8
7
  spec.version = GrapeLogging::VERSION
9
- spec.authors = ['aserafin']
10
- spec.email = ['adrian@softmad.pl']
8
+ spec.authors = ['aserafin', 'Sami Samhuri']
9
+ spec.email = ['adrian@softmad.pl', 'sami@samhuri.net']
11
10
 
12
- spec.summary = %q{Out of the box request logging for Grape!}
13
- spec.description = %q{This gem provides simple request logging for Grape with just few lines of code you have to put in your project! In return you will get response codes, paths, parameters and more!}
11
+ spec.summary = 'Out of the box request logging for Grape!'
12
+ spec.description = 'This gem provides simple request logging for Grape with just few lines ' \
13
+ 'of code you have to put in your project! In return you will get response ' \
14
+ 'codes, paths, parameters and more!'
14
15
  spec.homepage = 'http://github.com/aserafin/grape_logging'
15
16
  spec.license = 'MIT'
16
17
 
17
18
  spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
- spec.bindir = "exe"
19
+ spec.bindir = 'exe'
19
20
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
21
  spec.require_paths = ['lib']
21
22
 
22
- spec.add_dependency 'grape'
23
+ spec.add_dependency 'grape', '>= 2.4.0'
23
24
  spec.add_dependency 'rack'
24
25
 
25
- spec.add_development_dependency 'bundler', '~> 1.8'
26
- spec.add_development_dependency 'rake', '~> 10.0'
26
+ spec.add_development_dependency 'rake', '~> 13.3'
27
27
  spec.add_development_dependency 'rspec', '~> 3.5'
28
- spec.add_development_dependency 'pry-byebug', '~> 3.4.2'
28
+
29
+ # This is pinned to an exact version otherwise we can't know which rules
30
+ # are in play at any given time in different environments.
31
+ spec.add_development_dependency 'rubocop', '1.77.0'
29
32
  end
@@ -18,6 +18,7 @@ module GrapeLogging
18
18
  end
19
19
 
20
20
  private
21
+
21
22
  def format_hash(hash)
22
23
  hash.keys.sort.map { |key| "#{key}=#{hash[key]}" }.join(' ')
23
24
  end
@@ -3,9 +3,9 @@ module GrapeLogging
3
3
  class Logstash
4
4
  def call(severity, datetime, _, data)
5
5
  {
6
- :'@timestamp' => datetime.iso8601,
7
- :'@version' => '1',
8
- :severity => severity
6
+ '@timestamp': datetime.iso8601,
7
+ '@version': '1',
8
+ severity: severity
9
9
  }.merge!(format(data)).to_json + "\n"
10
10
  end
11
11
 
@@ -3,7 +3,6 @@ require 'rack/utils'
3
3
  module GrapeLogging
4
4
  module Formatters
5
5
  class Rails
6
-
7
6
  def call(severity, datetime, _, data)
8
7
  if data.is_a?(String)
9
8
  "#{severity[0..0]} [#{datetime}] #{severity} -- : #{data}\n"
@@ -24,7 +23,7 @@ module GrapeLogging
24
23
  [
25
24
  "#{exception.message} (#{exception.class})",
26
25
  backtrace_array.join("\n")
27
- ].reject{|line| line == ""}.join("\n")
26
+ ].reject { |line| line == '' }.join("\n")
28
27
  end
29
28
 
30
29
  def format_hash(hash)
@@ -32,7 +31,7 @@ module GrapeLogging
32
31
  # Completed 200 OK in 958ms (Views: 951.1ms | ActiveRecord: 3.8ms)
33
32
  # See: actionpack/lib/action_controller/log_subscriber.rb
34
33
 
35
- message = ""
34
+ message = ''
36
35
  additions = []
37
36
  status = hash.delete(:status)
38
37
  params = hash.delete(:params)
@@ -47,13 +46,12 @@ module GrapeLogging
47
46
  message << " Parameters: #{params.inspect}\n" if params
48
47
 
49
48
  message << "Completed #{status} #{::Rack::Utils::HTTP_STATUS_CODES[status]} in #{total_time}ms"
50
- message << " (#{additions.join(" | ".freeze)})" if additions.size > 0
49
+ message << " (#{additions.join(' | '.freeze)})" unless additions.empty?
51
50
  message << "\n"
52
51
  message << "\n" if defined?(::Rails.env) && ::Rails.env.development?
53
52
 
54
53
  message
55
54
  end
56
-
57
55
  end
58
56
  end
59
57
  end
@@ -1,7 +1,7 @@
1
1
  module GrapeLogging
2
2
  module Loggers
3
3
  class Base
4
- def parameters(request, response)
4
+ def parameters(_request, _response)
5
5
  {}
6
6
  end
7
7
  end
@@ -2,7 +2,7 @@ module GrapeLogging
2
2
  module Loggers
3
3
  class ClientEnv < GrapeLogging::Loggers::Base
4
4
  def parameters(request, _)
5
- { ip: request.env["HTTP_X_FORWARDED_FOR"] || request.env["REMOTE_ADDR"], ua: request.env["HTTP_USER_AGENT"] }
5
+ { ip: request.env['HTTP_X_FORWARDED_FOR'] || request.env['REMOTE_ADDR'], ua: request.env['HTTP_USER_AGENT'] }
6
6
  end
7
7
  end
8
8
  end
@@ -3,7 +3,7 @@ module GrapeLogging
3
3
  class FilterParameters < GrapeLogging::Loggers::Base
4
4
  AD_PARAMS = 'action_dispatch.request.parameters'.freeze
5
5
 
6
- def initialize(filter_parameters = nil, replacement = nil, exceptions = %w(controller action format))
6
+ def initialize(filter_parameters = nil, replacement = nil, exceptions = %w[controller action format])
7
7
  @filter_parameters = filter_parameters || (defined?(::Rails.application) ? ::Rails.application.config.filter_parameters : [])
8
8
  @replacement = replacement || '[FILTERED]'
9
9
  @exceptions = exceptions
@@ -29,7 +29,25 @@ module GrapeLogging
29
29
  end
30
30
 
31
31
  def clean_parameters(parameters)
32
- parameter_filter.filter(parameters).reject{ |key, _value| @exceptions.include?(key) }
32
+ original_encoding_map = build_encoding_map(parameters)
33
+ params = transform_key_encoding(parameters, Hash.new { |h, _| [Encoding::ASCII_8BIT, h] })
34
+ cleaned_params = parameter_filter.filter(params).reject { |key, _value| @exceptions.include?(key) }
35
+ transform_key_encoding(cleaned_params, original_encoding_map)
36
+ end
37
+
38
+ def build_encoding_map(parameters)
39
+ parameters.each_with_object({}) do |(k, v), h|
40
+ key_str = k.to_s
41
+ h[key_str.dup.force_encoding(Encoding::ASCII_8BIT)] = [key_str.encoding, v.is_a?(Hash) ? build_encoding_map(v) : nil]
42
+ end
43
+ end
44
+
45
+ def transform_key_encoding(parameters, encoding_map)
46
+ parameters.each_with_object({}) do |(k, v), h|
47
+ key_str = k.to_s
48
+ encoding, children_encoding_map = encoding_map[key_str]
49
+ h[key_str.dup.force_encoding(encoding)] = v.is_a?(Hash) ? transform_key_encoding(v, children_encoding_map) : v
50
+ end
33
51
  end
34
52
  end
35
53
  end
@@ -1,7 +1,6 @@
1
1
  module GrapeLogging
2
2
  module Loggers
3
3
  class RequestHeaders < GrapeLogging::Loggers::Base
4
-
5
4
  HTTP_PREFIX = 'HTTP_'.freeze
6
5
 
7
6
  def parameters(request, _)
@@ -16,7 +15,6 @@ module GrapeLogging
16
15
 
17
16
  { headers: headers }
18
17
  end
19
-
20
18
  end
21
19
  end
22
20
  end
@@ -9,14 +9,13 @@ module GrapeLogging
9
9
 
10
10
  # In some cases, response.body is not parseable by JSON.
11
11
  # For example, if you POST on a PUT endpoint, response.body is egal to """".
12
- # It's strange but it's the Grape behavior...
12
+ # It's strange, but it's the Grape behavior...
13
13
  def serialized_response_body(response)
14
-
15
14
  if response.respond_to?(:body)
16
15
  # Rack responses
17
16
  begin
18
- response.body.map{ |body| JSON.parse(body.to_s) }
19
- rescue # No reason to have "=> e" here when we don't use it..
17
+ response.body.map { |body| JSON.parse(body.to_s) }
18
+ rescue StandardError # No reason to have "=> e" here when we don't use it..
20
19
  response.body
21
20
  end
22
21
  else
@@ -1,27 +1,29 @@
1
- require 'grape/middleware/base'
1
+ require 'grape'
2
2
 
3
3
  module GrapeLogging
4
4
  module Middleware
5
5
  class RequestLogger < Grape::Middleware::Base
6
-
7
- ActiveSupport::Notifications.subscribe('sql.active_record') do |*args|
8
- event = ActiveSupport::Notifications::Event.new(*args)
9
- GrapeLogging::Timings.append_db_runtime(event)
10
- end if defined?(ActiveRecord)
6
+ if defined?(ActiveRecord)
7
+ ActiveSupport::Notifications.subscribe('sql.active_record') do |*args|
8
+ event = ActiveSupport::Notifications::Event.new(*args)
9
+ GrapeLogging::Timings.append_db_runtime(event)
10
+ end
11
+ end
11
12
 
12
13
  # Persist response status & response (body)
13
14
  # to use int in parameters
14
15
  attr_accessor :response_status, :response_body
15
16
 
16
- def initialize(app, options = {})
17
+ def initialize(app, **options)
17
18
  super
18
19
 
19
20
  @included_loggers = @options[:include] || []
20
- @reporter = if options[:instrumentation_key]
21
- Reporters::ActiveSupportReporter.new(@options[:instrumentation_key])
22
- else
23
- Reporters::LoggerReporter.new(@options[:logger], @options[:formatter], @options[:log_level])
24
- end
21
+ @reporter =
22
+ if options[:instrumentation_key]
23
+ Reporters::ActiveSupportReporter.new(@options[:instrumentation_key])
24
+ else
25
+ Reporters::LoggerReporter.new(@options[:logger], @options[:formatter], @options[:log_level])
26
+ end
25
27
  end
26
28
 
27
29
  def before
@@ -37,7 +39,7 @@ module GrapeLogging
37
39
  @response_status = status
38
40
  @response_body = response
39
41
 
40
- # Perform repotters
42
+ # Perform reporters
41
43
  @reporter.perform(collect_parameters)
42
44
 
43
45
  # Invoke loggers
@@ -56,20 +58,18 @@ module GrapeLogging
56
58
 
57
59
  # Catch error
58
60
  error = catch(:error) do
59
- begin
60
- @app_response = @app.call(@env)
61
- rescue => e
62
- # Log as 500 + message
63
- after(e.respond_to?(:status) ? e.status : 500, e.message)
64
-
65
- # Re-raise exception
66
- raise e
67
- end
61
+ @app_response = @app.call(@env)
68
62
  nil
63
+ rescue StandardError => e
64
+ # Log as 500 + message
65
+ after(e.respond_to?(:status) ? e.status : 500, e.message)
66
+
67
+ # Re-raise exception
68
+ raise e
69
69
  end
70
70
 
71
71
  # Get status & response from app_response
72
- # when no error occures.
72
+ # when no error occurs.
73
73
  if error
74
74
  # Call with error & response
75
75
  after(error[:status], error[:message])
@@ -100,7 +100,8 @@ module GrapeLogging
100
100
  method: request.request_method,
101
101
  path: request.path,
102
102
  params: request.params,
103
- host: request.host
103
+ host: request.host,
104
+ request_id: env['action_dispatch.request_id']
104
105
  }
105
106
  end
106
107
 
@@ -115,7 +116,7 @@ module GrapeLogging
115
116
  end
116
117
 
117
118
  def view_runtime
118
- total_runtime - db_runtime
119
+ (total_runtime - db_runtime).round(2)
119
120
  end
120
121
 
121
122
  def db_runtime
@@ -5,11 +5,11 @@ module GrapeLogging
5
5
  end
6
6
 
7
7
  def write(*args)
8
- @targets.each {|t| t.write(*args)}
8
+ @targets.each { |t| t.write(*args) }
9
9
  end
10
10
 
11
11
  def close
12
12
  @targets.each(&:close)
13
13
  end
14
14
  end
15
- end
15
+ end
@@ -8,4 +8,4 @@ module Reporters
8
8
  ActiveSupport::Notifications.instrument @instrumentation_key, params
9
9
  end
10
10
  end
11
- end
11
+ end
@@ -1,11 +1,9 @@
1
1
  module Reporters
2
2
  class LoggerReporter
3
3
  def initialize(logger, formatter, log_level)
4
- @logger = logger || Logger.new(STDOUT)
4
+ @logger = logger.clone || Logger.new(STDOUT)
5
5
  @log_level = log_level || :info
6
- if @logger.respond_to?(:formatter=)
7
- @logger.formatter = formatter || @logger.formatter || GrapeLogging::Formatters::Default.new
8
- end
6
+ @logger.formatter = formatter || @logger.formatter || GrapeLogging::Formatters::Default.new if @logger.respond_to?(:formatter=)
9
7
  end
10
8
 
11
9
  def perform(params)
@@ -1,21 +1,19 @@
1
1
  module GrapeLogging
2
2
  module Timings
3
- extend self
4
-
5
- def db_runtime=(value)
3
+ def self.db_runtime=(value)
6
4
  Thread.current[:grape_db_runtime] = value
7
5
  end
8
6
 
9
- def db_runtime
7
+ def self.db_runtime
10
8
  Thread.current[:grape_db_runtime] ||= 0
11
9
  end
12
10
 
13
- def reset_db_runtime
11
+ def self.reset_db_runtime
14
12
  self.db_runtime = 0
15
13
  end
16
14
 
17
- def append_db_runtime(event)
15
+ def self.append_db_runtime(event)
18
16
  self.db_runtime += event.duration
19
17
  end
20
18
  end
21
- end
19
+ end
@@ -1,4 +1,4 @@
1
- if defined?(::Rails.application)
1
+ if defined?(Rails.application)
2
2
  if Gem::Version.new(Rails.version) < Gem::Version.new('6.0.0')
3
3
  class ParameterFilter < ActionDispatch::Http::ParameterFilter
4
4
  def initialize(_replacement, filter_parameters)
@@ -6,7 +6,7 @@ if defined?(::Rails.application)
6
6
  end
7
7
  end
8
8
  else
9
- require "active_support/parameter_filter"
9
+ require 'active_support/parameter_filter'
10
10
 
11
11
  class ParameterFilter < ActiveSupport::ParameterFilter
12
12
  def initialize(_replacement, filter_parameters)
@@ -37,9 +37,11 @@ else
37
37
 
38
38
  class CompiledFilter # :nodoc:
39
39
  def self.compile(replacement, filters)
40
- return lambda { |params| params.dup } if filters.empty?
40
+ return ->(params) { params.dup } if filters.empty?
41
41
 
42
- strings, regexps, blocks = [], [], []
42
+ strings = []
43
+ regexps = []
44
+ blocks = []
43
45
 
44
46
  filters.each do |item|
45
47
  case item
@@ -52,8 +54,8 @@ else
52
54
  end
53
55
  end
54
56
 
55
- deep_regexps, regexps = regexps.partition { |r| r.to_s.include?("\\.".freeze) }
56
- deep_strings, strings = strings.partition { |s| s.include?("\\.".freeze) }
57
+ deep_regexps, regexps = regexps.partition { |r| r.to_s.include?('\\.'.freeze) }
58
+ deep_strings, strings = strings.partition { |s| s.include?('\\.'.freeze) }
57
59
 
58
60
  regexps << Regexp.new(strings.join('|'.freeze), true) unless strings.empty?
59
61
  deep_regexps << Regexp.new(deep_strings.join('|'.freeze), true) unless deep_strings.empty?
@@ -67,7 +69,7 @@ else
67
69
  @replacement = replacement
68
70
  @regexps = regexps
69
71
  @deep_regexps = deep_regexps.any? ? deep_regexps : nil
70
- @blocks = blocks
72
+ @blocks = blocks
71
73
  end
72
74
 
73
75
  def call(original_params, parents = [])
@@ -1,3 +1,3 @@
1
1
  module GrapeLogging
2
- VERSION = '1.8.4'
2
+ VERSION = '3.0.0'
3
3
  end