New Discussion

Notifications

You’re not receiving notifications from this thread.

How can I squish SQL for log tidiness (automatically)?

3
Rails

Hello everyone,

I’m dealing with a challenge in my codebase where there are numerous multiline SQL heredocs being logged. While these may be helpful for local debugging, they become a hassle when grepping through logs in production. Searching for specific queries often requires running grep with the -C flag (or restarting grep multiple times with adjusted context), which is inefficient and frustrating for an everyday task like log analysis. For this reason, I want to prioritize tidy, single-line SQL logs that are easier to grep.

While I know that applying .squish to individual queries can help, it’s not developer-friendly and requires everyone to remember to do this consistently. Additionally, it doesn’t always work—for example, when the query begins with a single-line comment.

To address this, I’ve tried dynamically squishing the SQL using a custom LogSubscriber, as shown below. However, I’m concerned that modifying event.payload[:sql] could have unintended side effects when the event is processed elsewhere:

class SQLSquishLogSubscriber < ActiveRecord::LogSubscriber
  def sql(event)
    return super if Rails.env.local?

    sql = event.payload[:sql]
    return super if sql.blank?

    event.payload[:sql] = sql.squish
    super
  end
end

Does anyone have a better solution for ensuring clean, single-line SQL logs without risking side effects or placing extra burden on developers?

Thanks in advance!

Is that a better approach?

class SQLSquishLogSubscriber < ActiveRecord::LogSubscriber
  def sql(event)
    return super if Rails.env.local?

    original_sql = event.payload[:sql]
    return super if original_sql.blank?

    event.payload[:sql] = original_sql.squish
    super
  ensure
    event.payload[:sql] = original_sql
  end 
end

A couple things come to mind:

  1. Unless you have other things parsing the SQL, squishing it should be fine.
  2. You probably add to the payload instead of modifying it.
event.payload[:squished_sql] = event.payload[:sql].squish

Thanks, but my goal was to not copy the original code from the rails codebase and adjust the line to log the sql (e.g. I didn't want to do something like):

# original code derived from ActiveRecord::LogSubscriber
def sql(event)
  ...
  squished_sql = event.payload[:sql].squish
  debug "  #{name}  #{squished_sql}#{binds}"
end

I think this works out:

class SQLSquishLogSubscriber < ActiveRecord::LogSubscriber
  def sql(event)
    return super unless squish_sql_in_logs?

    event_dup = event.dup.tap do |e|
      e.payload = e.payload.dup
      e.payload[:sql] = e.payload[:sql].squish if e.payload[:sql]
    end

    super(event_dup)
  end

  private

  def squish_sql_in_logs?
    conf = Rails.application.config.x.logger.squish_sql
    conf.nil? ? true : conf
  end
end
Join the discussion
Create an account Log in

Want to stay up-to-date with Ruby on Rails?

Join 88,440+ developers who get early access to new tutorials, screencasts, articles, and more.

    We care about the protection of your data. Read our Privacy Policy.