rails_api_logger 0.9.0 → 0.10.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +6 -0
  3. data/.semaphore/semaphore.yml +132 -2
  4. data/.standard.yml +2 -0
  5. data/Appraisals +31 -0
  6. data/CHANGELOG.md +62 -3
  7. data/README.md +90 -53
  8. data/Rakefile +7 -2
  9. data/app/middlewares/rails_api_logger/middleware.rb +80 -0
  10. data/app/models/rails_api_logger/inbound_request_log.rb +5 -0
  11. data/app/models/rails_api_logger/loggable.rb +27 -0
  12. data/app/models/rails_api_logger/logger.rb +22 -0
  13. data/app/models/rails_api_logger/outbound_request_log.rb +5 -0
  14. data/app/models/rails_api_logger/request_log.rb +85 -0
  15. data/bin/rails +16 -0
  16. data/gemfiles/rails_6.1.gemfile +10 -0
  17. data/gemfiles/rails_6.1.gemfile.lock +240 -0
  18. data/gemfiles/rails_7.0.gemfile +10 -0
  19. data/gemfiles/rails_7.0.gemfile.lock +239 -0
  20. data/gemfiles/rails_7.1.gemfile +9 -0
  21. data/gemfiles/rails_7.1.gemfile.lock +272 -0
  22. data/gemfiles/rails_7.2.gemfile +9 -0
  23. data/gemfiles/rails_7.2.gemfile.lock +272 -0
  24. data/gemfiles/rails_8.0.gemfile +9 -0
  25. data/lib/generators/rails_api_logger/install_generator.rb +1 -1
  26. data/lib/rails_api_logger/engine.rb +25 -0
  27. data/lib/rails_api_logger/version.rb +5 -0
  28. data/lib/rails_api_logger.rb +9 -27
  29. data/rails_api_logger.gemspec +10 -6
  30. metadata +58 -29
  31. data/CODE_OF_CONDUCT.md +0 -74
  32. data/lib/rails_api_logger/inbound_request_log.rb +0 -2
  33. data/lib/rails_api_logger/inbound_requests_logger_middleware.rb +0 -71
  34. data/lib/rails_api_logger/outbound_request_log.rb +0 -2
  35. data/lib/rails_api_logger/request_log.rb +0 -76
  36. /data/{lib/rails_api_logger → app/controllers}/inbound_requests_logger.rb +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4f1fb1fae010aa6cf4bdda941fd6ad2ce3ac5583faa690d23680194d2f3eb991
4
- data.tar.gz: 0dd85cd48972098af775d7a5feec4d17b8aaaa51641e9a4405244d8ccdd12644
3
+ metadata.gz: 4959094cde8a80515bac5d15b4bd4f475e7c723b94803a2ecdc00de59aba1995
4
+ data.tar.gz: 283881fcb6952b5ffab21a816558f8c6ef9c705252d25f183a261e614317db3b
5
5
  SHA512:
6
- metadata.gz: 4d310658b9bbae0d5406a8df1d7a85e11d161db8405ad970cb422fbd9ad38d2a95c251a8ba3f6acbe30089290b61d94ed8c8cd1b0e601fbbea6f8d2a111b7ae0
7
- data.tar.gz: c3c29eaf30dbbb4548b3527ac7220193135a4ca0eb69b5f17b12e0ef88516b8aa91c1b8367dacd4a4ee5988c4722ba493c5e98a28ba7b5c61645db971c36bcdb
6
+ metadata.gz: 1d727ed904aff3668f16d2bdfdedee5576b30c53819911303f7df0723608344fbe9c30eb7e313cde7c2ca6d33e832c9ac4073baa065331f2cb9e11276975e3f8
7
+ data.tar.gz: e76f28822d9646a1973f4fb75612c52a0ed0d18d089ae27e9eccc5ac5f1313ee58e5236564b086246a17d6f158d4616a29ebc1419e25484a7a6cb73021529629
data/.gitignore CHANGED
@@ -6,6 +6,12 @@
6
6
  /pkg/
7
7
  /spec/reports/
8
8
  /tmp/
9
+ /spec/dummy/log
10
+ /spec/dummy/tmp
11
+ /spec/dummy/db/*.sqlite3*
12
+ /spec/dummy/db/schema.rb
13
+ /spec/dummy/db/api_logger_schema.rb
14
+ .env
9
15
 
10
16
  # rspec failure tracking
11
17
  .rspec_status
@@ -21,12 +21,142 @@ 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
25
  - cache store
26
+ env_vars:
27
+ - name: BUNDLE_GEMFILE
28
+ value: /home/semaphore/rails_api_logger/gemfiles/rails_8.0.gemfile
26
29
  jobs:
27
- - name: tests
30
+ - name: linter
28
31
  commands:
29
32
  - bundle exec standardrb
33
+ - name: tests postgres separate db and separate target
34
+ env_vars:
35
+ - name: SAME_DB
36
+ value: "false"
37
+ - name: SAME_TARGET
38
+ value: "false"
39
+ - name: TARGET_DB
40
+ value: postgres
41
+ - name: RAILS_ENV
42
+ value: test
43
+ commands:
44
+ - sem-service start postgres 14
45
+ - bin/rails db:create db:migrate
46
+ - bundle exec rspec
47
+ - name: tests postgres separate db and same target
48
+ env_vars:
49
+ - name: SAME_DB
50
+ value: "false"
51
+ - name: SAME_TARGET
52
+ value: "true"
53
+ - name: TARGET_DB
54
+ value: postgres
55
+ - name: RAILS_ENV
56
+ value: test
57
+ commands:
58
+ - sem-service start postgres 14
59
+ - bin/rails db:create db:migrate
60
+ - bundle exec rspec
61
+ - name: tests postgres same db and separate target
62
+ env_vars:
63
+ - name: SAME_DB
64
+ value: "true"
65
+ - name: TARGET_DB
66
+ value: postgres
67
+ - name: RAILS_ENV
68
+ value: test
69
+ commands:
70
+ - sem-service start postgres 14
71
+ - bin/rails db:create db:migrate
72
+ - bundle exec rspec
73
+ - name: tests postgres separate db and separate target | rails 6.1
74
+ env_vars:
75
+ - name: SAME_DB
76
+ value: "false"
77
+ - name: SAME_TARGET
78
+ value: "false"
79
+ - name: TARGET_DB
80
+ value: postgres
81
+ - name: RAILS_ENV
82
+ value: test
83
+ - name: BUNDLE_GEMFILE
84
+ value: /home/semaphore/rails_api_logger/gemfiles/rails_6.1.gemfile
85
+ commands:
86
+ - sem-service start postgres 14
87
+ - bin/rails db:create db:migrate
88
+ - bundle exec rspec
89
+ - name: tests postgres separate db and separate target | rails 7.0
90
+ env_vars:
91
+ - name: SAME_DB
92
+ value: "false"
93
+ - name: SAME_TARGET
94
+ value: "false"
95
+ - name: TARGET_DB
96
+ value: postgres
97
+ - name: RAILS_ENV
98
+ value: test
99
+ - name: BUNDLE_GEMFILE
100
+ value: /home/semaphore/rails_api_logger/gemfiles/rails_7.0.gemfile
101
+ commands:
102
+ - sem-service start postgres 14
103
+ - bin/rails db:create db:migrate
104
+ - bundle exec rspec
105
+ - name: tests postgres separate db and separate target | rails 7.1
106
+ env_vars:
107
+ - name: SAME_DB
108
+ value: "false"
109
+ - name: SAME_TARGET
110
+ value: "false"
111
+ - name: TARGET_DB
112
+ value: postgres
113
+ - name: RAILS_ENV
114
+ value: test
115
+ - name: BUNDLE_GEMFILE
116
+ value: /home/semaphore/rails_api_logger/gemfiles/rails_7.1.gemfile
117
+ commands:
118
+ - sem-service start postgres 14
119
+ - bin/rails db:create db:migrate
120
+ - bundle exec rspec
121
+ - name: tests postgres separate db and separate target | rails 7.2
122
+ env_vars:
123
+ - name: SAME_DB
124
+ value: "false"
125
+ - name: SAME_TARGET
126
+ value: "false"
127
+ - name: TARGET_DB
128
+ value: postgres
129
+ - name: RAILS_ENV
130
+ value: test
131
+ - name: BUNDLE_GEMFILE
132
+ value: /home/semaphore/rails_api_logger/gemfiles/rails_7.2.gemfile
133
+ commands:
134
+ - sem-service start postgres 14
135
+ - bin/rails db:create db:migrate
136
+ - bundle exec rspec
137
+ - name: tests sqlite separate db and separate target
138
+ env_vars:
139
+ - name: SAME_DB
140
+ value: "false"
141
+ - name: SAME_TARGET
142
+ value: "false"
143
+ - name: TARGET_DB
144
+ value: sqlite
145
+ - name: RAILS_ENV
146
+ value: test
147
+ commands:
148
+ - bin/rails db:create db:migrate
149
+ - bundle exec rspec
150
+ - name: tests sqlite same db
151
+ env_vars:
152
+ - name: SAME_DB
153
+ value: "true"
154
+ - name: TARGET_DB
155
+ value: sqlite
156
+ - name: RAILS_ENV
157
+ value: test
158
+ commands:
159
+ - bin/rails db:create db:migrate
30
160
  - bundle exec rspec
31
161
  promotions:
32
162
  - name: main
data/.standard.yml ADDED
@@ -0,0 +1,2 @@
1
+ ignore:
2
+ - 'gemfiles/**/*'
data/Appraisals ADDED
@@ -0,0 +1,31 @@
1
+ appraise "rails-6.1" do
2
+ gem "concurrent-ruby", "1.3.4"
3
+ gem "rails", "~> 6.1.0"
4
+ gem "rspec-rails", "~> 6.1.0"
5
+ gem "sqlite3", "~> 1.7.3"
6
+ end
7
+
8
+ appraise "rails-7.0" do
9
+ gem "concurrent-ruby", "1.3.4"
10
+ gem "rails", "~> 7.0.0"
11
+ gem "rspec-rails", "~> 7.1.0"
12
+ gem "sqlite3", "~> 1.7.3"
13
+ end
14
+
15
+ appraise "rails-7.1" do
16
+ gem "rails", "~> 7.1.0"
17
+ gem "rspec-rails", "~> 7.1.0"
18
+ gem "sqlite3", "~> 1.7.3"
19
+ end
20
+
21
+ appraise "rails-7.2" do
22
+ gem "rails", "~> 7.2.0"
23
+ gem "rspec-rails", "~> 7.1.0"
24
+ gem "sqlite3", "~> 1.7.3"
25
+ end
26
+
27
+ appraise "rails-8.0" do
28
+ gem "rails", "~> 8.0.0"
29
+ gem "rspec-rails", "~> 7.1.0"
30
+ gem "sqlite3", "~> 2.1.0"
31
+ end
data/CHANGELOG.md CHANGED
@@ -1,41 +1,98 @@
1
+ # 0.10.1
2
+
3
+ * Fix a bug introduced in 0.8.1 where we fixed Rails 7.1 warnings. This broke the serialization in Rails < 7.1.
4
+ We now configure the serializer differently if you are running Rails 7.0 or lower.
5
+ If you used the gem version 0.8.1 or above in combination with Rails 7.0 or lower this caused the serialization of
6
+ the request and response body into YML format instead of JSON. This is now fixed and tests run on different rails
7
+ versions as well.
8
+
9
+ # 0.10.0
10
+
11
+ **BREAKING CHANGES**
12
+
13
+ This version contains many breaking changes. Consider this when upgrading:
14
+
15
+ * Replace calls to `RailsApiLogger.new` with `RailsApiLogger::Logger.new`. More in general the logger has been renamed.
16
+ * `InboundRequestLog` has been renamed to `RailsApiLogger::InboundRequestLog`. Table name did not change.
17
+ * `OutboundRequestLog` has been renamed to `RailsApiLogger::OutboundRequestLog`. Table name did not change.
18
+ * If you had `has_many :inbound_request_logs` or `has_many :outbound_request_logs` defined, this will break. There's
19
+ now [three methods](app/models/rails_api_logger/loggable.rb) you can use on your model.
20
+ * `InboundRequestsLoggerMiddleware` has been renamed to `RailsApiLogger::Middleware`
21
+
22
+ > Do the changes above and then continue with the following steps if you want to connect rails_api_logger to a different
23
+ > database:
24
+
25
+ * Specify a database called `api_logger`. [Check here](spec/dummy/config/database.yml) for an example.
26
+ * Check that everything still works (also in production!) with the new configuration.
27
+ * Add the migrations in the right folder to create again the necessary tables. Run the migrations that will generate a
28
+ new schema file.
29
+ * Release and do the same in production. Adapt your build/release steps if you need.
30
+
31
+ * Add the following line into `production.rb`:
32
+ `config.rails_api_logger.connects_to = { database: { writing: :api_logger } }` if you want to point to a new database.
33
+
34
+ > If you are not on SQLite you can point also `api_logger` database to the current database you have, so you benefit
35
+ > from isolated transactions but don't need to create a new database or migrate data.
36
+
37
+ ### List of changes in this version:
38
+
39
+ * Namespace correctly. Renamed all classes
40
+ * Added tests with a dummy app
41
+ * Use a separate database connection configuration to isolate transactions
42
+ * I acknowledge that there might be issues on mysql. I don't use it so I won't fix them, but PR are welcome.
43
+ * Added `host_regexp` option to the middleware.
44
+
1
45
  # 0.9.0
2
- * Add option skip_request_body to skip the request body. Use this option when you don't want to persist the request body. `[Skipped]` will be persisted instead.
46
+
47
+ * Add option skip_request_body to skip the request body. Use this option when you don't want to persist the request
48
+ body. `[Skipped]` will be persisted instead.
3
49
  * Add option skip_request_body_regexp to skip logging the body of requests matching a regexp.
4
50
  * Renamed the option skip_body into skip_response_body. This is a breaking change!
5
51
  * Renamed the option skip_body_regexp into skip_response_body_regexp. This is a breaking change!
6
52
 
7
53
  # 0.8.1
54
+
8
55
  * Fix Rails 7.1 warnings.
9
56
 
10
57
  # 0.8.0
11
- * Add option skip_body to skip the body for request responses. Use this option when you don't want to persist the response body. `[Skipped]` will be persisted instead. This is not a breaking change.
58
+
59
+ * Add option skip_body to skip the body for request responses. Use this option when you don't want to persist the
60
+ response body. `[Skipped]` will be persisted instead. This is not a breaking change.
12
61
 
13
62
  # 0.7.0
63
+
14
64
  * Fix an issue in the middleware where the request body was not read correctly if there were encoding issues.
15
65
  * Improved documentation about outboud request logging.
16
66
  * Add option skip_body_regexp to skip logging the body of requests matching a regexp.
17
67
 
18
68
  # 0.6.3
69
+
19
70
  * Fix the CHANGELOG path in gemspec.
20
71
 
21
72
  # 0.6.2
73
+
22
74
  * Fixes Zeitwerk warning.
23
75
 
24
76
  # 0.6.1
77
+
25
78
  * Fixes the loading of concern into controllers.
26
79
 
27
80
  # 0.6.0
81
+
28
82
  * Fixes an important concurrency issue by removing instance variables in the rack middleware.
29
83
 
30
84
  # 0.5.0
85
+
31
86
  * Started using Zeitwerk.
32
87
  * Removed RailsAdmin specific code.
33
88
  * Improved RailsApiLogger class.
34
89
 
35
90
  # 0.4.1
91
+
36
92
  * Fixed the `.failed` scope.
37
93
 
38
94
  # 0.4.0
95
+
39
96
  * Added `started_at`, `ended_at` and `duration` methods.
40
97
 
41
98
  Migrate your tables with:
@@ -47,12 +104,14 @@ add_column :outbound_request_logs, :started_at, :timestamp
47
104
  add_column :outbound_request_logs, :ended_at, :timestamp
48
105
  ```
49
106
 
50
-
51
107
  # 0.3.0
108
+
52
109
  * Added `formatted_request_body` and `formatted_response_body` methods.
53
110
 
54
111
  # 0.2.0
112
+
55
113
  * Switch to a middleware solution.
56
114
 
57
115
  # 0.1.0
116
+
58
117
  * Initial release.
data/README.md CHANGED
@@ -1,23 +1,23 @@
1
1
  # Rails API Logger
2
2
 
3
- The simplest way to log API requests of your Rails application in your database.
3
+ The simplest way to log API requests in your database.
4
4
 
5
5
  The Rails API logger gem introduces a set of tools to log and debug API requests.
6
+
6
7
  It works on two sides:
7
8
 
8
9
  * **Inbound requests**: API exposed by your application
9
- * **Outbound requests**: API invoked by your application
10
+ * **Outbound requests**: API invoked by your application
10
11
 
11
- This gem has been extracted from various Renuo projects, where we implemented this
12
- technique multiple times successfully.
12
+ This gem has been extracted from various [Renuo](https://www.renuo.ch) projects.
13
13
 
14
14
  This gem creates two database tables to log the following information:
15
15
 
16
16
  * **path** the path/url invoked
17
17
  * **method** the method used to invoke the API (get, post, put, etc...)
18
18
  * **request_body** what was included in the request body
19
- * **response_body** what was included in the response body
20
- * **response_code** the HTTP response code of the request
19
+ * **response_body** what was included in the response body
20
+ * **response_code** the HTTP response code of the request
21
21
  * **started_at** when the request started
22
22
  * **ended_at** when the request finished
23
23
 
@@ -33,14 +33,34 @@ And then execute:
33
33
 
34
34
  ```bash
35
35
  bundle install
36
- spring stop # if it's running. otherwise it does not see the new generator
37
- bundle exec rails generate rails_api_logger:install
38
- bundle exec rails db:migrate
36
+ bin/rails g rails_api_logger:install
37
+ bin/rails db:migrate
39
38
  ```
40
39
 
41
40
  This will generate two tables `inbound_request_logs` and `outbound_request_logs`.
42
41
  These tables will contain the logs.
43
42
 
43
+ ## Ensure logging of data
44
+
45
+ RailsApiLogger can use a separate database, to ensure that the logs are written in the database even if a
46
+ surrounding database transaction is rolled back.
47
+
48
+ Make sure to add the following in your `config/environments/production.rb`:
49
+
50
+ ```ruby
51
+ config.rails_api_logger.connects_to = { database: { writing: :api_logger } }
52
+ ```
53
+
54
+ and [configure a new database](spec/dummy/config/database.yml) accordingly.
55
+
56
+ > ⚠️ If you skip this step, rails_api_logger will use your primary database but a rollback will also rollback the
57
+ > writing of logs
58
+ > If you are not on SQLite you can point also `api_logger` to the same database! By doing so you can use a single
59
+ > database but still guarantee the writing of logs in an isolated transaction:
60
+ > ```
61
+ > config.rails_api_logger.connects_to = { database: { writing: :primary } }
62
+ > ```
63
+
44
64
  ## Log Outbound Requests
45
65
 
46
66
  Given an outbound request in the following format:
@@ -59,7 +79,7 @@ uri = URI('http://example.com/some_path?query=string')
59
79
  http = Net::HTTP.start(uri.host, uri.port)
60
80
  request = Net::HTTP::Get.new(uri)
61
81
 
62
- log = OutboundRequestLog.from_request(request)
82
+ log = RailsApiLogger::OutboundRequestLog.from_request(request)
63
83
 
64
84
  response = http.request(request)
65
85
 
@@ -76,7 +96,7 @@ request = Net::HTTP::Post.new(uri)
76
96
  request.body = { answer: 42 }.to_json
77
97
  request.content_type = 'application/json'
78
98
 
79
- response = RailsApiLogger.new.call(nil, request) do
99
+ response = RailsApiLogger::Logger.new.call(nil, request) do
80
100
  Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(request) }
81
101
  end
82
102
  ```
@@ -86,25 +106,7 @@ This will guarantee that the log is always persisted, even in case of errors.
86
106
  ### Database Transactions Caveats
87
107
 
88
108
  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
- You can also log the request in a separate thread to provoke the checkout of a separate database connection.
107
- Have a look at [this example here](https://github.com/renuo/rails_api_logger/blob/28d4ced88fea5a5f4fd72f5a1db42ad4734eb547/spec/outbound_request_log_spec.rb#L28-L30).
109
+ the transaction is rolled-back. Use a separate database to prevent this.
108
110
 
109
111
  ## Log Inbound Requests
110
112
 
@@ -112,36 +114,41 @@ If you are exposing some API you might be interested in logging the requests you
112
114
  You can do so by adding this middleware in `config/application.rb`
113
115
 
114
116
  ```ruby
115
- config.middleware.insert_before Rails::Rack::Logger, InboundRequestsLoggerMiddleware
117
+ config.middleware.insert_before Rails::Rack::Logger, RailsApiLogger::Middleware
116
118
  ```
117
119
 
118
120
  this will by default only log requests that have an impact in your system (POST, PUT, and PATCH calls).
119
121
  If you want to log all requests (also GET ones) use
120
122
 
121
123
  ```ruby
122
- config.middleware.insert_before Rails::Rack::Logger, InboundRequestsLoggerMiddleware, only_state_change: false
124
+ config.middleware.insert_before Rails::Rack::Logger, RailsApiLogger::Middleware, only_state_change: false
123
125
  ```
124
126
 
125
127
  If you want to log only requests on a certain path, you can pass a regular expression:
126
128
 
127
129
  ```ruby
128
- config.middleware.insert_before Rails::Rack::Logger, InboundRequestsLoggerMiddleware, path_regexp: /api/
130
+ config.middleware.insert_before Rails::Rack::Logger, RailsApiLogger::Middleware, path_regexp: /api/
129
131
  ```
130
132
 
131
- If you want to skip logging the response or request body of certain requests, you can pass a regular expression:
133
+ If you want to log only requests on a certain host, you can also use a regular expression:
132
134
 
133
135
  ```ruby
134
- config.middleware.insert_before Rails::Rack::Logger, InboundRequestsLoggerMiddleware,
135
- skip_request_body_regexp: /api/books/,
136
- skip_response_body_regexp: /api/letters/
136
+ config.middleware.insert_before Rails::Rack::Logger, RailsApiLogger::Middleware, host_regexp: /api.example.com/
137
137
  ```
138
138
 
139
+ If you want to skip logging the response or request body of certain requests, you can pass a regular expression:
140
+
141
+ ```ruby
142
+ config.middleware.insert_before Rails::Rack::Logger, RailsApiLogger::Middleware,
143
+ skip_request_body_regexp: /api\/books/,
144
+ skip_response_body_regexp: /api\/letters/
145
+ ```
139
146
 
140
147
  In the implementation of your API, you can call any time `attach_inbound_request_loggable(model)`
141
148
  to attach an already persisted model to the log record.
142
149
 
143
-
144
150
  For example:
151
+
145
152
  ```ruby
146
153
 
147
154
  def create
@@ -158,18 +165,21 @@ end
158
165
  in the User model you can define:
159
166
 
160
167
  ```ruby
161
- has_many :inbound_request_logs, inverse_of: :loggable, dependent: :destroy, as: :loggable
168
+ has_many_inbound_request_logs
162
169
  ```
163
170
 
164
- to be able to access the logs attached to the model.
171
+ to be able to access the inbound logs attached to the model.
172
+
173
+ You also have `has_many_outbound_request_logs` and `has_many_request_logs` that includes both.
165
174
 
166
175
  ## RailsAdmin integration
167
176
 
168
177
  We provide here some code samples to integrate the models in [RailsAdmin](https://github.com/sferik/rails_admin).
169
178
 
170
- This configuration will give you some nice views, and searches to work with the logs efficiently.
179
+ This configuration will give you some nice views, and searches to work with the logs efficiently.
180
+
171
181
  ```ruby
172
- %w[InboundRequestLog OutboundRequestLog].each do |logging_model|
182
+ %w[RailsApiLogger::InboundRequestLog RailsApiLogger::OutboundRequestLog].each do |logging_model|
173
183
  config.model logging_model do
174
184
  list do
175
185
  filters %i[method path response_code request_body response_body created_at]
@@ -204,24 +214,51 @@ This configuration will give you some nice views, and searches to work with the
204
214
  end
205
215
  ```
206
216
 
207
-
208
217
  ## Development
209
218
 
210
- 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.
219
+ We use Appraisals to un on different Rails versions. This is a run example:
211
220
 
212
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
221
+ ```bash
222
+ export SAME_TARGET=false
223
+ export SAME_DB=false
224
+ export TARGET_DB=postgres
225
+ export BUNDLE_GEMFILE=/Users/alessandrorodi/RenuoWorkspace/rails_api_logger/gemfiles/rails_6.1.gemfile
226
+ bundle exec rails db:create db:migrate
227
+ bundle exec rspec
228
+ ```
229
+
230
+ These are the possible ENV variables:
231
+
232
+ * `SAME_TARGET` if true, the api_logger database is the same as the primary one. It will still use two separate
233
+ connection pools, but they'll point to the same database. This cannot be set to true if TARGET_DB is sqlite because
234
+ sqlite does not support multiple connection pools to the same database.
235
+ * `SAME_DB` if true, the api_logger uses the primary database. In this case SAME_TARGET is ignored.
236
+ * `TARGET_DB` the database to use. Can be `postgres`, `mysql`, or `sqlite`.
237
+ * `BUNDLE_GEMFILE` the gemfile to use. This is used to run the tests on different Rails versions.
238
+
239
+ Possible combinations:
240
+
241
+ | SAME_DB | SAME_TARGET | TARGET_DB | Description |
242
+ |---------|-------------|-----------|----------------------------------------------------------------------|
243
+ | false | false | postgres | Separate database, separate connection pool. |
244
+ | false | true | postgres | Same database, separate connection pool. |
245
+ | true | - | postgres | Same connection pool so the separate target is ignored. |
246
+ | false | false | sqlite | Separate database, separate connection pool. |
247
+ | false | true | sqlite | Not allowed. sqlite cannot have two connection pools to the same db. |
248
+ | true | - | sqlite | Same connection pool so the separate target is ignored. |
249
+
250
+
251
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the
252
+ version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version,
253
+ push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
213
254
 
214
255
  ## Contributing
215
256
 
216
- Bug reports and pull requests are welcome on GitHub at https://github.com/renuo/rails_api_logger.
217
- This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to
218
- the [code of conduct](https://github.com/renuo/rails_api_logger/blob/main/CODE_OF_CONDUCT.md).
257
+ Bug reports and pull requests are welcome on GitHub at https://github.com/renuo/rails_api_logger.
258
+ This project is intended to be a safe, welcoming space for collaboration.
259
+
260
+ Try to be a decent human being while interacting with other people.
219
261
 
220
262
  ## License
221
263
 
222
264
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
223
-
224
- ## Code of Conduct
225
-
226
- Everyone interacting in the RailsApiLogger project's codebases, issue trackers, chat rooms and mailing lists is
227
- expected to follow the [code of conduct](https://github.com/renuo/rails_api_logger/blob/main/CODE_OF_CONDUCT.md).
data/Rakefile CHANGED
@@ -1,6 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/setup"
1
4
  require "bundler/gem_tasks"
2
- require "rspec/core/rake_task"
5
+ require "rake/testtask"
3
6
 
4
- RSpec::Core::RakeTask.new(:spec)
7
+ APP_RAKEFILE = File.expand_path("spec/dummy/Rakefile", __dir__)
8
+ load "rails/tasks/engine.rake"
9
+ load "rails/tasks/statistics.rake"
5
10
 
6
11
  task default: :spec
@@ -0,0 +1,80 @@
1
+ module RailsApiLogger
2
+ class Middleware
3
+ attr_accessor :only_state_change, :host_regexp, :path_regexp, :skip_request_body_regexp, :skip_response_body_regexp
4
+
5
+ def initialize(app, only_state_change: true,
6
+ host_regexp: /.*/,
7
+ path_regexp: /.*/,
8
+ skip_request_body_regexp: nil,
9
+ skip_response_body_regexp: nil)
10
+ @app = app
11
+ self.only_state_change = only_state_change
12
+ self.host_regexp = host_regexp
13
+ self.path_regexp = path_regexp
14
+ self.skip_request_body_regexp = skip_request_body_regexp
15
+ self.skip_response_body_regexp = skip_response_body_regexp
16
+ end
17
+
18
+ def call(env)
19
+ request = ActionDispatch::Request.new(env)
20
+ logging = log?(env, request)
21
+ if logging
22
+ env["INBOUND_REQUEST_LOG"] = InboundRequestLog.from_request(request, skip_request_body: skip_request_body?(env))
23
+ request.body.rewind if request.body.respond_to?(:read)
24
+ end
25
+ status, headers, body = @app.call(env)
26
+ if logging
27
+ updates = {response_code: status, ended_at: Time.current}
28
+ updates[:response_body] = if skip_response_body?(env)
29
+ "[Skipped]"
30
+ else
31
+ parsed_body(body)
32
+ end
33
+ # this usually works. let's be optimistic.
34
+ begin
35
+ env["INBOUND_REQUEST_LOG"].update_columns(updates)
36
+ rescue JSON::GeneratorError => _e # this can be raised by activerecord if the string is not UTF-8.
37
+ env["INBOUND_REQUEST_LOG"].update_columns(updates.except(:response_body))
38
+ end
39
+ end
40
+ [status, headers, body]
41
+ end
42
+
43
+ private
44
+
45
+ def skip_request_body?(env)
46
+ skip_request_body_regexp && env["PATH_INFO"] =~ skip_request_body_regexp
47
+ end
48
+
49
+ def skip_response_body?(env)
50
+ skip_response_body_regexp && env["PATH_INFO"] =~ skip_response_body_regexp
51
+ end
52
+
53
+ def log?(env, request)
54
+ # The HTTP_HOST header is preferred to the SERVER_NAME header per the Rack spec: https://github.com/rack/rack/blob/main/SPEC.rdoc#label-The+Environment
55
+ host = env["HTTP_HOST"] || env["SERVER_NAME"]
56
+ path = env["PATH_INFO"]
57
+ (host =~ host_regexp) &&
58
+ (path =~ path_regexp) &&
59
+ (!only_state_change || request_with_state_change?(request))
60
+ end
61
+
62
+ def parsed_body(body)
63
+ return unless body.present?
64
+
65
+ if body.respond_to?(:to_ary)
66
+ JSON.parse(body.to_ary[0])
67
+ elsif body.respond_to?(:body)
68
+ JSON.parse(body.body)
69
+ else
70
+ body
71
+ end
72
+ rescue JSON::ParserError, ArgumentError
73
+ body
74
+ end
75
+
76
+ def request_with_state_change?(request)
77
+ request.post? || request.put? || request.patch? || request.delete?
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,5 @@
1
+ module RailsApiLogger
2
+ class InboundRequestLog < RequestLog
3
+ self.table_name = "inbound_request_logs"
4
+ end
5
+ end