Skip to content

Commit 0bcf1f2

Browse files
committed
Add more options to skip request body
1 parent d171251 commit 0bcf1f2

9 files changed

+124
-47
lines changed

CHANGELOG.md

+6
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
# 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.
3+
* Add option skip_request_body_regexp to skip logging the body of requests matching a regexp.
4+
* Renamed the option skip_body into skip_response_body. This is a breaking change!
5+
* Renamed the option skip_body_regexp into skip_response_body_regexp. This is a breaking change!
6+
17
# 0.8.1
28
* Fix Rails 7.1 warnings.
39

README.md

+4-2
Original file line numberDiff line numberDiff line change
@@ -128,10 +128,12 @@ If you want to log only requests on a certain path, you can pass a regular expre
128128
config.middleware.insert_before Rails::Rack::Logger, InboundRequestsLoggerMiddleware, path_regexp: /api/
129129
```
130130

131-
If you want to skip logging the body of certain requests, you can pass a regular expression:
131+
If you want to skip logging the response or request body of certain requests, you can pass a regular expression:
132132

133133
```ruby
134-
config.middleware.insert_before Rails::Rack::Logger, InboundRequestsLoggerMiddleware, skip_body_regexp: /api/letters/
134+
config.middleware.insert_before Rails::Rack::Logger, InboundRequestsLoggerMiddleware,
135+
skip_request_body_regexp: /api/books/,
136+
skip_response_body_regexp: /api/letters/
135137
```
136138

137139

lib/rails_api_logger.rb

+5-4
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,16 @@
1010
class RailsApiLogger
1111
class Error < StandardError; end
1212

13-
def initialize(loggable = nil, skip_body: false)
13+
def initialize(loggable = nil, skip_request_body: false, skip_response_body: false)
1414
@loggable = loggable
15-
@skip_body = skip_body
15+
@skip_request_body = skip_request_body
16+
@skip_response_body = skip_response_body
1617
end
1718

1819
def call(url, request)
19-
log = OutboundRequestLog.from_request(request, loggable: @loggable)
20+
log = OutboundRequestLog.from_request(request, loggable: @loggable, skip_request_body: @skip_request_body)
2021
yield.tap do |response|
21-
log.from_response(response, skip_body: @skip_body)
22+
log.from_response(response, skip_response_body: @skip_response_body)
2223
end
2324
rescue => e
2425
log.response_body = {error: e.message}

lib/rails_api_logger/inbound_requests_logger_middleware.rb

+19-7
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,32 @@
11
class InboundRequestsLoggerMiddleware
2-
attr_accessor :only_state_change, :path_regexp, :skip_body_regexp
2+
attr_accessor :only_state_change, :path_regexp, :skip_request_body_regexp, :skip_response_body_regexp
33

4-
def initialize(app, only_state_change: true, path_regexp: /.*/, skip_body_regexp: nil)
4+
def initialize(app, only_state_change: true,
5+
path_regexp: /.*/,
6+
skip_request_body_regexp: nil,
7+
skip_response_body_regexp: nil)
58
@app = app
69
self.only_state_change = only_state_change
710
self.path_regexp = path_regexp
8-
self.skip_body_regexp = skip_body_regexp
11+
self.skip_request_body_regexp = skip_request_body_regexp
12+
self.skip_response_body_regexp = skip_response_body_regexp
913
end
1014

1115
def call(env)
1216
request = ActionDispatch::Request.new(env)
1317
logging = log?(env, request)
1418
if logging
15-
env["INBOUND_REQUEST_LOG"] = InboundRequestLog.from_request(request)
19+
env["INBOUND_REQUEST_LOG"] = InboundRequestLog.from_request(request, skip_request_body: skip_request_body?(env))
1620
request.body.rewind
1721
end
1822
status, headers, body = @app.call(env)
1923
if logging
2024
updates = {response_code: status, ended_at: Time.current}
21-
updates[:response_body] = parsed_body(body) if log_response_body?(env)
25+
updates[:response_body] = if skip_response_body?(env)
26+
"[Skipped]"
27+
else
28+
parsed_body(body)
29+
end
2230
# this usually works. let's be optimistic.
2331
begin
2432
env["INBOUND_REQUEST_LOG"].update_columns(updates)
@@ -31,8 +39,12 @@ def call(env)
3139

3240
private
3341

34-
def log_response_body?(env)
35-
skip_body_regexp.nil? || env["PATH_INFO"] !~ skip_body_regexp
42+
def skip_request_body?(env)
43+
skip_request_body_regexp && env["PATH_INFO"] =~ skip_request_body_regexp
44+
end
45+
46+
def skip_response_body?(env)
47+
skip_response_body_regexp && env["PATH_INFO"] =~ skip_response_body_regexp
3648
end
3749

3850
def log?(env, request)

lib/rails_api_logger/request_log.rb

+16-10
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,24 @@ class RequestLog < ActiveRecord::Base
1111
validates :method, presence: true
1212
validates :path, presence: true
1313

14-
def self.from_request(request, loggable: nil)
15-
request_body = (request.body.respond_to?(:read) ? request.body.read : request.body)
16-
body = request_body&.dup&.force_encoding("UTF-8")
17-
begin
18-
body = JSON.parse(body) if body.present?
19-
rescue JSON::ParserError
20-
body
14+
def self.from_request(request, loggable: nil, skip_request_body: false)
15+
if skip_request_body
16+
body = "[Skipped]"
17+
else
18+
request_body = (request.body.respond_to?(:read) ? request.body.read : request.body)
19+
body = request_body&.dup&.force_encoding("UTF-8")
20+
begin
21+
body = JSON.parse(body) if body.present?
22+
rescue JSON::ParserError
23+
body
24+
end
2125
end
2226
create(path: request.path, request_body: body, method: request.method, started_at: Time.current, loggable: loggable)
2327
end
2428

25-
def from_response(response, skip_body: false)
29+
def from_response(response, skip_response_body: false)
2630
self.response_code = response.code
27-
self.response_body = skip_body ? "[Skipped]" : manipulate_body(response.body)
31+
self.response_body = skip_response_body ? "[Skipped]" : manipulate_body(response.body)
2832
self
2933
end
3034

@@ -37,7 +41,9 @@ def formatted_response_body
3741
end
3842

3943
def formatted_body(body)
40-
if body.is_a?(Hash)
44+
if body.is_a?(String) && body.blank?
45+
""
46+
elsif body.is_a?(Hash)
4147
JSON.pretty_generate(body)
4248
else
4349
xml = Nokogiri::XML(body)

rails_api_logger.gemspec

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Gem::Specification.new do |spec|
22
spec.name = "rails_api_logger"
3-
spec.version = "0.8.2"
3+
spec.version = "0.9.0"
44
spec.authors = ["Alessandro Rodi"]
55
spec.email = ["[email protected]"]
66

spec/inbound_requests_logger_middleware_spec.rb

+26-6
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,13 @@ def request
2323
end
2424

2525
RSpec.describe InboundRequestsLoggerMiddleware do
26-
let(:skip_body_regexp) { nil }
26+
let(:skip_request_body_regexp) { nil }
27+
let(:skip_response_body_regexp) { nil }
2728
let(:app) do
28-
InboundRequestsLoggerMiddleware.new(MyApp.new, path_regexp: path_regexp, skip_body_regexp: skip_body_regexp)
29+
InboundRequestsLoggerMiddleware.new(MyApp.new,
30+
path_regexp: path_regexp,
31+
skip_request_body_regexp: skip_request_body_regexp,
32+
skip_response_body_regexp: skip_response_body_regexp)
2933
end
3034
let(:request) { Rack::MockRequest.new(app) }
3135
let(:response) { request.post("/api/v1/books") }
@@ -54,10 +58,26 @@ def request
5458
expect(inbound_request_log.loggable_id).to be_present
5559
end
5660

57-
context "when the PATH_INFO matches the skip_body_regexp" do
58-
let(:skip_body_regexp) { /books/ }
61+
context "when the PATH_INFO matches the skip_request_body_regexp" do
62+
let(:skip_request_body_regexp) { /books/ }
5963

60-
it "logs a request in the database but without a body" do
64+
it "logs a request in the database but without a request body" do
65+
expect(response.status).to eq(200)
66+
expect(response.body).to eq("Hello World")
67+
expect(InboundRequestLog.count).to eq(1)
68+
inbound_request_log = InboundRequestLog.first
69+
expect(inbound_request_log.method).to eq("POST")
70+
expect(inbound_request_log.path).to eq("/api/v1/books")
71+
expect(inbound_request_log.request_body).to eq("[Skipped]")
72+
expect(inbound_request_log.response_code).to eq(200)
73+
expect(inbound_request_log.response_body).to eq("Hello World")
74+
end
75+
end
76+
77+
context "when the PATH_INFO matches the skip_response_body_regexp" do
78+
let(:skip_response_body_regexp) { /books/ }
79+
80+
it "logs a request in the database but without a response body" do
6181
expect(response.status).to eq(200)
6282
expect(response.body).to eq("Hello World")
6383
expect(InboundRequestLog.count).to eq(1)
@@ -66,7 +86,7 @@ def request
6686
expect(inbound_request_log.path).to eq("/api/v1/books")
6787
expect(inbound_request_log.request_body).to eq("")
6888
expect(inbound_request_log.response_code).to eq(200)
69-
expect(inbound_request_log.response_body).to be_nil
89+
expect(inbound_request_log.response_body).to eq("[Skipped]")
7090
end
7191
end
7292

spec/outbound_request_log_spec.rb

+41-12
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,45 @@
55
OutboundRequestLog.delete_all
66
end
77

8-
it "logs a request in the database" do
9-
uri = URI("http://example.com/some_path?query=string")
10-
http = Net::HTTP.new(uri.host, uri.port)
11-
request = Net::HTTP::Get.new(uri)
12-
RailsApiLogger.new.call(uri, request) { http.start { |http| http.request(request) } }
13-
expect(OutboundRequestLog.count).to eq(1)
14-
log = OutboundRequestLog.last
15-
expect(log.started_at).to be_present
16-
expect(log.ended_at).to be_present
8+
describe "logging of a request" do
9+
let(:skip_request_body) { false }
10+
let(:skip_response_body) { false }
11+
12+
before do
13+
uri = URI("https://httpbin.org/anything?query=string")
14+
http = Net::HTTP.new(uri.host, uri.port)
15+
request = Net::HTTP::Post.new(uri)
16+
request.body = {"my" => {"request" => "body"}}.to_json
17+
RailsApiLogger.new(skip_request_body: skip_request_body, skip_response_body: skip_response_body)
18+
.call(uri, request) { http.start { |http| http.request(request) } }
19+
end
20+
21+
it "logs all information" do
22+
expect(OutboundRequestLog.count).to eq(1)
23+
log = OutboundRequestLog.last
24+
expect(log.started_at).to be_present
25+
expect(log.ended_at).to be_present
26+
expect(log.request_body).to be_present
27+
expect(log.response_body).to be_present
28+
end
29+
30+
describe "if the skip_request_body option is set" do
31+
let(:skip_request_body) { true }
32+
33+
it "does not log the request body" do
34+
log = OutboundRequestLog.last
35+
expect(log.request_body).to eq("[Skipped]")
36+
end
37+
end
38+
39+
describe "if the skip_response_body option is set" do
40+
let(:skip_response_body) { true }
41+
42+
it "does not log the response body" do
43+
log = OutboundRequestLog.last
44+
expect(log.response_body).to eq("[Skipped]")
45+
end
46+
end
1747
end
1848

1949
describe "if the request fails" do
@@ -42,14 +72,13 @@
4272
describe "#formatted_request_body" do
4373
it "renders the request body in a nice format" do
4474
outbound_request_log = OutboundRequestLog.new(request_body: {"my" => {"request" => "body"}})
45-
puts outbound_request_log.formatted_request_body
4675
expect { outbound_request_log.formatted_request_body }.not_to raise_error
4776
outbound_request_log.request_body = "simple text"
48-
puts outbound_request_log.formatted_request_body
4977
expect { outbound_request_log.formatted_request_body }.not_to raise_error
5078
outbound_request_log.request_body = "<i><a>Hello</a><b>From</b><c>XML</c></i>"
51-
puts outbound_request_log.formatted_request_body
5279
expect { outbound_request_log.formatted_request_body }.not_to raise_error
80+
outbound_request_log.request_body = ""
81+
expect(outbound_request_log.formatted_request_body).to eq("")
5382
end
5483
end
5584

spec/request_log_spec.rb

+6-5
Original file line numberDiff line numberDiff line change
@@ -26,19 +26,20 @@
2626
let(:request) { Net::HTTP::Get.new(uri) }
2727
let(:response) { http.start { |http| http.request(request) } }
2828

29-
before { RailsApiLogger.new(skip_body: skip_body).call(uri, request) { response } }
29+
before { RailsApiLogger.new(skip_response_body: skip_response_body).call(uri, request) { response } }
3030

31-
context "when skip_body is set to false" do
32-
let(:skip_body) { false }
31+
context "when skip_response_body is set to false" do
32+
let(:skip_response_body) { false }
3333

3434
it "sets the response_body to the original request's response body" do
3535
log = OutboundRequestLog.last
3636
expect(log.response_body).to eq(response.body)
37+
expect(log.response_body).to be_present
3738
end
3839
end
3940

40-
context "when skip_body is set to true" do
41-
let(:skip_body) { true }
41+
context "when skip_response_body is set to true" do
42+
let(:skip_response_body) { true }
4243

4344
it "sets the response_body to [Skipped]" do
4445
log = OutboundRequestLog.last

0 commit comments

Comments
 (0)