Skip to content

Commit

Permalink
[CIOPS-1085] Silence log requests (#4)
Browse files Browse the repository at this point in the history
### Description

This pull request introduces the silencing of the Rack and Rails logger
for the endpoint. By default we will not log any of the requests to not
spam the output and log files with repeated simple checks. There is an
option to enable to when desired.

### Changes

* Silence logging by default
* Allow it to be enabled

### Ticket

[CIOPS-1085](https://customink.atlassian.net/browse/CIOPS-1085)
  • Loading branch information
Arne De Herdt authored Sep 16, 2022
2 parents 9dbf155 + 1c08eeb commit 8d80d2b
Show file tree
Hide file tree
Showing 9 changed files with 141 additions and 2 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Is It Ready? CHANGELOG

## 0.0.5
* Silence Rails and the Rack middleware by default
* Add an option to allow logging of requests

## 0.0.4
* Add support for HTTP Authorization Bearer tokens

Expand Down
6 changes: 6 additions & 0 deletions lib/is_it_ready.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# frozen_string_literal: true

require 'is_it_ready/log_silencer'
require "is_it_ready/engine"

# The namespace holding everything related to our Gem.
Expand All @@ -19,4 +20,9 @@ module IsItReady
# When enabled however, the request will need to provide the required token as a Bearer value
# in the AUTHORIZATION header of the request.
mattr_accessor :bearer_token

# Silences the logging of the request against the endpoint. Defaults to true.
# When disabled, the entire request will appear in the Rails logs.
mattr_accessor :silence_logs
@@silence_logs = true
end
6 changes: 6 additions & 0 deletions lib/is_it_ready/engine.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,11 @@ class Engine < ::Rails::Engine
mount ::IsItReady::Engine => ::IsItReady.endpoint
end
end

# If the user has enabled the silencing of the loggers, we will mount the middleware
# to do so, otherwise skip the process entirely.
initializer 'is_it_ready.add_middleware' do |app|
app.middleware.insert_before(::Rails::Rack::Logger, ::IsItReady::LogSilencer, silenced: ::IsItReady.endpoint)
end
end
end
41 changes: 41 additions & 0 deletions lib/is_it_ready/log_silencer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# frozen_string_literal: true

module IsItReady
# This class is a Rack::Middleware implementation that will support us in silencing the
# logging of calls for incoming requests against the endpoint. Otherwise the app would be
# writing all requests in the Rails logs, causing an overload of information to be reported
# that's simply not relevant. The usage of this Middleware can be controlled through the Engine's
# configuration on whether to silence logging or not.
class LogSilencer
# Creates a new instance of the Middleware and initializes it using the Rack standard approach
# for setting up the required values in a Rack::Middleware.
def initialize(app, opts = {})
@silenced = opts.delete(:silenced)
@app = app
end

# Executes the Middleware.
# If the environment contains the special X-SILENCE-LOGGER header to globally silence the request,
# or the path matches the provided silence configuration, the middleware will silence Rails for the
# request, otherwise pass the request along.
def call(env)
if ::IsItReady.silence_logs && silence_path?(env['PATH_INFO'])
::Rails.logger.silence do
@app.call(env)
end
else
@app.call(env)
end
end

private

# Returns true when the given path needs to be silenced.
# This uses a manual Regex check, since the .match? method might not exist depending on the Ruby
# version that's being used for this gem. So we perform a manual match and return true if there's
# a 0 response.
def silence_path?(path)
(path =~ /#{@silenced}/).present?
end
end
end
2 changes: 1 addition & 1 deletion lib/is_it_ready/version.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# frozen_string_literal: true

module IsItReady
VERSION = '0.0.4'
VERSION = '0.0.5'
end
10 changes: 9 additions & 1 deletion test/integration/custom_navigation_test.rb
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
require 'test_helper'

module IsItReady
# This test verifies whether the Engine respects the dynamic configuration of the endpoint,
# allowing it to be dynamically loaded when Rails loads the configuration, ensuring that we can
# mount the engine using a custom path when required.
class CustomNavigationTest < ActionDispatch::IntegrationTest
include Engine.routes.url_helpers

setup do
::IsItReady.endpoint = '/something_else'
Rails.application.reload_routes!
::Rails.application.reload_routes!
end

teardown do
::IsItReady.endpoint = ::IsItReady::DEFAULT_PATH
::Rails.application.reload_routes!
end

test('it returns the correct response status on the root') do
Expand Down
31 changes: 31 additions & 0 deletions test/integration/logging_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# frozen_string_literal: true

require 'test_helper'

module IsItReady
# This class tests whether the ::Rails.logger is properly enabled during the execution of the Rails.engine.
# By default silencing of the incoming requests on the health check is disabled to avoid the Rails logs
# from being spammed with the repeated health checks. With this test, we check whether the Engine
# respects the configuration at runtime.
class SilentLoggingTest < ::ActionDispatch::IntegrationTest
include Engine.routes.url_helpers
include ::LoggerIntrospection

setup do
::IsItReady.endpoint = ::IsItReady::DEFAULT_PATH
::IsItReady.silence_logs = false
end

teardown do
::IsItReady.silence_logs = true
end

test('it writes the request to the standard Rails logger') do
with_logger_introspection do |logger_output|
get root_url

assert_match(/Started GET "#{::IsItReady.endpoint}\/" for 127.0.0.1 at */, logger_output.string)
end
end
end
end
21 changes: 21 additions & 0 deletions test/integration/silent_logging_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# frozen_string_literal: true

require 'test_helper'

module IsItReady
# This class tests whether the ::Rails.logger is properly silenced during the execution of the Rails.engine.
# By default silencing of the incoming requests on the health check is disabled to avoid the Rails logs
# from being spammed with the repeated health checks.
class SilentLoggingTest < ::ActionDispatch::IntegrationTest
include Engine.routes.url_helpers
include ::LoggerIntrospection

test('it does not write anything to the standard Rails logger') do
with_logger_introspection do |logger_output|
get root_url

refute_match(/Started GET "#{::IsItReady.endpoint}" for 127.0.0.1 at */, logger_output.string)
end
end
end
end
22 changes: 22 additions & 0 deletions test/support/logger_introspection.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# frozen_string_literal: true

# This module contains methods that can be used in tests to perform introspection on the Rails logger.
# This allows us to wrap some support functionality inside our tests and check whether certain behavior
# is implemented correctly without overloading the test itself.
module LoggerIntrospection
# Performs introspection on the ::Rails.logger with the given block inside the test.
# The method will duplicate the original logger, and replace the logger with a simple StringIO object.
# All log entries are then made available in the object, and after the test the logger is restored
# to the original functionality to not affect other tests.
def with_logger_introspection(&block)
original_logger = ::Rails.logger.dup
@logger_output = ::StringIO.new

begin
::Rails.logger = ::ActiveSupport::Logger.new(@logger_output)
block.call(@logger_output)
ensure
::Rails.logger = original_logger
end
end
end

0 comments on commit 8d80d2b

Please sign in to comment.