rails_api_logger 0.6.3 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a81b65d48bd11340944d7d656808134a8f6ba51d6a8b9a16997e22d68cea2781
4
- data.tar.gz: b4fe1ffa4236a408177b46346e0491f425e085d22c7e89791ebeb7068ea04f7f
3
+ metadata.gz: b99069d517d5cd82e5e073121ebb94306335c052644115cc56765a0a3c166447
4
+ data.tar.gz: bfac0a6e391ff5c1e29e481e313c47419a6b6f4835871e9f12624638b38d497e
5
5
  SHA512:
6
- metadata.gz: cb5a895c1c041f879a7602cfc03d1282146dbd140a2fc6bb48a14c34d682f3e1b99ea887a1aabc75dc764a889e02a9bb6c27d770ae5e048987f114fe9deafe2d
7
- data.tar.gz: cb631dc32ff4f8595d7953440f27e1600d35f2bc84bff7f5bf3c58171fc6aff2831a1a30cad39ceba14dcd4da40a070d89b459a714ba39955e7c11af07eb31cf
6
+ metadata.gz: 3b97315feb5e4792465a85819c829d33733a854fe77a9af907935058eb468c44829f524a29fb6c969cef6efe342903bcd6384989720bd54180f274fbde2bb0c7
7
+ data.tar.gz: 7139532155a71e2f9d068438164425acebb9f5bb6825f6fbdac0a5f0d0b0f58204dd824b7dcfd474776133b2c4e616a6b7834bd40a94f6500d845c885d45f326
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 2.7.2
1
+ 3.2.1
@@ -21,6 +21,7 @@ blocks:
21
21
  - cache restore
22
22
  - bundle config set path 'vendor/bundle'
23
23
  - bundle install -j 4
24
+ - sem-service start postgres 14 --username=semaphore
24
25
  - cache store
25
26
  jobs:
26
27
  - name: tests
data/CHANGELOG.md CHANGED
@@ -1,3 +1,8 @@
1
+ # 0.7.0
2
+ * Fix an issue in the middleware where the request body was not read correctly if there were encoding issues.
3
+ * Improved documentation about outboud request logging.
4
+ * Add option skip_body_regexp to skip logging the body of requests matching a regexp.
5
+
1
6
  # 0.6.3
2
7
  * Fix the CHANGELOG path in gemspec.
3
8
 
data/README.md CHANGED
@@ -71,34 +71,67 @@ log.save!
71
71
  You can also use the provided logger class to do that in a simpler and safer manner:
72
72
 
73
73
  ```ruby
74
- uri = URI('http://example.com/some_path?query=string')
75
- http = Net::HTTP.new(uri.host, uri.port)
76
- request = Net::HTTP::Get.new(uri)
77
- response = RailsApiLogger.new.call(uri, request) { http.start { |http| http.request(request) } }
74
+ uri = URI('https://example.com/some_path')
75
+ request = Net::HTTP::Post.new(uri)
76
+ request.body = { answer: 42 }.to_json
77
+ request.content_type = 'application/json'
78
+
79
+ response = RailsApiLogger.new.call(nil, request) do
80
+ Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(request) }
81
+ end
78
82
  ```
79
83
 
80
84
  This will guarantee that the log is always persisted, even in case of errors.
81
85
 
86
+ ### Database Transactions Caveats
87
+
88
+ If you log your outbound requests inside of parent app transactions, your logs will not be persisted if
89
+ the transaction is rolled-back. You can circumvent that by opening another database connection
90
+ to the same (or another database if you're into that stuff) when logging.
91
+
92
+ ```
93
+ # config/initializers/outbound_request_log_patch.rb
94
+
95
+ module OutboundRequestLogTransactionPatch
96
+ extend ActiveSupport::Concern
97
+
98
+ included do
99
+ connects_to database: { writing: :primary, reading: :primary }
100
+ end
101
+ end
102
+
103
+ OutboundRequestLog.include(OutboundRequestLogTransactionPatch)
104
+ ```
105
+
106
+ another way to do the same is to start a new thread to log the request.
107
+ See the [linked test for an example](https://github.com/renuo/rails_api_logger/blob/main/spec/outbound_request_log_spec.rb:15)
108
+
82
109
  ## Log Inbound Requests
83
110
 
84
111
  If you are exposing some API you might be interested in logging the requests you receive.
85
112
  You can do so by adding this middleware in `config/application.rb`
86
113
 
87
114
  ```ruby
88
- config.middleware.insert_before Rails::Rack::Logger, InboundRequestLoggerMiddleware
115
+ config.middleware.insert_before Rails::Rack::Logger, InboundRequestsLoggerMiddleware
89
116
  ```
90
117
 
91
118
  this will by default only log requests that have an impact in your system (POST, PUT, and PATCH calls).
92
119
  If you want to log all requests (also GET ones) use
93
120
 
94
121
  ```ruby
95
- config.middleware.insert_before Rails::Rack::Logger, InboundRequestLoggerMiddleware, only_state_change: false
122
+ config.middleware.insert_before Rails::Rack::Logger, InboundRequestsLoggerMiddleware, only_state_change: false
96
123
  ```
97
124
 
98
125
  If you want to log only requests on a certain path, you can pass a regular expression:
99
126
 
100
127
  ```ruby
101
- config.middleware.insert_before Rails::Rack::Logger, InboundRequestLoggerMiddleware, path_regexp: /api/
128
+ config.middleware.insert_before Rails::Rack::Logger, InboundRequestsLoggerMiddleware, path_regexp: /api/
129
+ ```
130
+
131
+ If you want to skip logging the body of certain requests, you can pass a regular expression:
132
+
133
+ ```ruby
134
+ config.middleware.insert_before Rails::Rack::Logger, InboundRequestsLoggerMiddleware, skip_body_regexp: /api/letters/
102
135
  ```
103
136
 
104
137
 
@@ -106,7 +139,6 @@ In the implementation of your API, you can call any time `attach_inbound_request
106
139
  to attach an already persisted model to the log record.
107
140
 
108
141
 
109
-
110
142
  For example:
111
143
  ```ruby
112
144
 
@@ -170,6 +202,7 @@ This configuration will give you some nice views, and searches to work with the
170
202
  end
171
203
  ```
172
204
 
205
+
173
206
  ## Development
174
207
 
175
208
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -4,6 +4,7 @@ module InboundRequestsLogger
4
4
  private
5
5
 
6
6
  def attach_inbound_request_loggable(loggable)
7
+ return unless request.env["INBOUND_REQUEST_LOG"].present?
7
8
  request.env["INBOUND_REQUEST_LOG"].update(loggable: loggable) if loggable&.persisted?
8
9
  end
9
10
  end
@@ -1,10 +1,11 @@
1
1
  class InboundRequestsLoggerMiddleware
2
- attr_accessor :only_state_change, :path_regexp
2
+ attr_accessor :only_state_change, :path_regexp, :skip_body_regexp
3
3
 
4
- def initialize(app, only_state_change: true, path_regexp: /.*/)
4
+ def initialize(app, only_state_change: true, path_regexp: /.*/, skip_body_regexp: nil)
5
5
  @app = app
6
6
  self.only_state_change = only_state_change
7
7
  self.path_regexp = path_regexp
8
+ self.skip_body_regexp = skip_body_regexp
8
9
  end
9
10
 
10
11
  def call(env)
@@ -16,15 +17,24 @@ class InboundRequestsLoggerMiddleware
16
17
  end
17
18
  status, headers, body = @app.call(env)
18
19
  if logging
19
- env["INBOUND_REQUEST_LOG"].update_columns(response_body: parsed_body(body),
20
- response_code: status,
21
- ended_at: Time.current)
20
+ updates = {response_code: status, ended_at: Time.current}
21
+ updates[:response_body] = parsed_body(body) if log_response_body?(env)
22
+ # this usually works. let's be optimistic.
23
+ begin
24
+ env["INBOUND_REQUEST_LOG"].update_columns(updates)
25
+ rescue JSON::GeneratorError => _e # this can be raised by activerecord if the string is not UTF-8.
26
+ env["INBOUND_REQUEST_LOG"].update_columns(updates.except(:response_body))
27
+ end
22
28
  end
23
29
  [status, headers, body]
24
30
  end
25
31
 
26
32
  private
27
33
 
34
+ def log_response_body?(env)
35
+ skip_body_regexp.nil? || env["PATH_INFO"] !~ skip_body_regexp
36
+ end
37
+
28
38
  def log?(env, request)
29
39
  env["PATH_INFO"] =~ path_regexp && (!only_state_change || request_with_state_change?(request))
30
40
  end
@@ -39,7 +49,7 @@ class InboundRequestsLoggerMiddleware
39
49
  else
40
50
  body
41
51
  end
42
- rescue JSON::ParserError
52
+ rescue JSON::ParserError, ArgumentError
43
53
  body
44
54
  end
45
55
 
@@ -6,14 +6,14 @@ class RequestLog < ActiveRecord::Base
6
6
 
7
7
  belongs_to :loggable, optional: true, polymorphic: true
8
8
 
9
- scope :failed, -> { where.not(response_code: 200..299) }
9
+ scope :failed, -> { where(response_code: 400..599).or(where.not(ended_at: nil).where(response_code: nil)) }
10
10
 
11
11
  validates :method, presence: true
12
12
  validates :path, presence: true
13
13
 
14
14
  def self.from_request(request, loggable: nil)
15
15
  request_body = (request.body.respond_to?(:read) ? request.body.read : request.body)
16
- body = request_body ? request_body.dup.force_encoding("UTF-8") : nil
16
+ body = request_body&.dup&.force_encoding("UTF-8")
17
17
  begin
18
18
  body = JSON.parse(body) if body.present?
19
19
  rescue JSON::ParserError
@@ -24,13 +24,14 @@ class RequestLog < ActiveRecord::Base
24
24
 
25
25
  def from_response(response)
26
26
  self.response_code = response.code
27
- body = response.body ? response.body.dup.force_encoding("UTF-8") : nil
27
+ body = response.body&.dup&.force_encoding("UTF-8")
28
28
  begin
29
29
  body = JSON.parse(body) if body.present?
30
30
  rescue JSON::ParserError
31
31
  body
32
32
  end
33
33
  self.response_body = body
34
+ self
34
35
  end
35
36
 
36
37
  def formatted_request_body
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |spec|
2
2
  spec.name = "rails_api_logger"
3
- spec.version = "0.6.3"
3
+ spec.version = "0.7.0"
4
4
  spec.authors = ["Alessandro Rodi"]
5
5
  spec.email = ["alessandro.rodi@renuo.ch"]
6
6
 
@@ -30,7 +30,8 @@ Gem::Specification.new do |spec|
30
30
  spec.add_dependency "zeitwerk", ">= 2.0.0"
31
31
 
32
32
  spec.add_development_dependency "sqlite3", "~> 1.4.0"
33
- spec.add_development_dependency "standard", "~> 0.13.0"
33
+ spec.add_development_dependency "pg", "~> 1.5.4"
34
+ spec.add_development_dependency "standard", "~> 1.31"
34
35
  spec.add_development_dependency "rake", "~> 12.0"
35
36
  spec.add_development_dependency "rspec", "~> 3.0"
36
37
  spec.add_development_dependency "rack"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails_api_logger
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.3
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alessandro Rodi
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-07-12 00:00:00.000000000 Z
11
+ date: 2023-12-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: railties
@@ -94,20 +94,34 @@ dependencies:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
96
  version: 1.4.0
97
+ - !ruby/object:Gem::Dependency
98
+ name: pg
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: 1.5.4
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: 1.5.4
97
111
  - !ruby/object:Gem::Dependency
98
112
  name: standard
99
113
  requirement: !ruby/object:Gem::Requirement
100
114
  requirements:
101
115
  - - "~>"
102
116
  - !ruby/object:Gem::Version
103
- version: 0.13.0
117
+ version: '1.31'
104
118
  type: :development
105
119
  prerelease: false
106
120
  version_requirements: !ruby/object:Gem::Requirement
107
121
  requirements:
108
122
  - - "~>"
109
123
  - !ruby/object:Gem::Version
110
- version: 0.13.0
124
+ version: '1.31'
111
125
  - !ruby/object:Gem::Dependency
112
126
  name: rake
113
127
  requirement: !ruby/object:Gem::Requirement
@@ -186,7 +200,7 @@ metadata:
186
200
  homepage_uri: https://github.com/renuo/rails_api_logger
187
201
  source_code_uri: https://github.com/renuo/rails_api_logger
188
202
  changelog_uri: https://github.com/renuo/rails_api_logger/blob/main/CHANGELOG.md
189
- post_install_message:
203
+ post_install_message:
190
204
  rdoc_options: []
191
205
  require_paths:
192
206
  - lib
@@ -201,8 +215,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
201
215
  - !ruby/object:Gem::Version
202
216
  version: '0'
203
217
  requirements: []
204
- rubygems_version: 3.1.4
205
- signing_key:
218
+ rubygems_version: 3.4.21
219
+ signing_key:
206
220
  specification_version: 4
207
221
  summary: "Log API requests like a king \U0001F451"
208
222
  test_files: []