A Rails middleware to change log level at runtime


12 Jun 2019 - Development, Ruby on Rails

Mattia Roccoberton

4 mins read
A Rails middleware to change the log level at runtime

Have you ever found the Rails logs too verbose in development?

When working on a Rails application, you don’t always need that amount of detail in your logs. From time to time, a less verbose log is more than enough, especially after a long day coding. To change the log level by editing the settings is easy but you will also need to restart the server. And in some situations, it would be helpful to change the verbosity from request to request.

For this reason, in this article, we will see how to create a small middleware to change the log level at runtime.

A Rack middleware is a component that can filter/process/change either a request or a response by inserting some code in the request/response cycle. For more information on this topic, the Rails guide is a nice starting point: Action Dispatcher Middleware Stack.

In a basic Rails middleware there are 2 methods: initialize that is used to keep a reference to the current application and call which is invoked when a request arrives. The latter receives the context of the request that can be parsed easily using the Rack::Request class. At the end of our method we need to invoke the call method of the Rails app to allow the chain to go on or we can return a Rack response composed by status code, headers, and content.

To change the log level we can intercept a specific route (for example /dev) and serve a minimal interface from which we can check if a logging parameter is sent and change the log level as requested.

Here you find a small example:

  class RuntimeConfTool
    LOGGER_SEVERITY = %w[debug info warn error fatal unknown].freeze

    def initialize(app)
      @app = app

    def call(env)
      req = Rack::Request.new(env)
      return @app.call(env) unless req.path == '/dev'

      if LOGGER_SEVERITY.include?((req.params['log'] || '').downcase)
        level = "Logger::#{req.params['log'].upcase}"
        Rails.logger.level = level.constantize
        @message = "Logger level set to: #{level}"

      [200, { 'Content-Type' => 'text/html' }, [
          <h3>Runtime Rails Tools</h3>
            Set log level to: <%= RuntimeConfTool::LOGGER_SEVERITY.map{ |level| '<a class="button" href="?log=' + level + '">' + level + '</a>' }.join(' ') %>
            <br/><em><%= @message %></em>

To enable our middleware we need to change config/environments/development.rb by adding this line: config.middleware.use RuntimeConfTool. And using rake middleware from the console it’s possible to see our new component in the list of the current active middlewares.

From this point, we could do a lot of things like changing other settings (controlling the cache, enabling/disabling errors catching, etc.), here you can find a small project with this purpose in mind: Rails Runtime Config.

Rails Runtime Config Screenshot