Я разработал rails_request_stats, чтобы решить свою личную проблему, оптимизируя определенные конечные точки API Rails на работе. Это дало мне быстрый способ быстро получить необходимую информацию в журналах разработки.

Чтобы лучше всего описать, что и как можно использовать rails_request_stats, следующее взято непосредственно из README rails_request_stats:

Во время разработки вы когда-нибудь:

  • Интересно, сколько запросов SQL произошло во время запроса?
  • Вас интересовало среднее время просмотра и время работы базы данных для запроса?
  • Хотите отчет, содержащий общую статистику всех уникальных запросов?
  • Нужен лучший способ итеративной оптимизации запросов?

RailsRequestStats предоставляет простое встраиваемое решение для предоставления дополнительной статистики по запросам. Новая информация представлена ​​в ваших журналах разработки, предоставляя вам необходимую информацию для итеративной оптимизации запросов, отмечая незначительные изменения в количестве запросов и среднем времени выполнения.

Как это работает

RailsRequestStats::NotificationSubscribers при необходимости подпишется на sql.active_record, start_processing.action_controller и process_action.action_controller ActionSupport::Notifications.

  • Событие sql.active_record позволяет нам подсчитывать каждый SQL-запрос, проходящий через ActiveRecord, который мы подсчитываем внутренне.
  • Событие cache_read.active_support позволяет нам подсчитывать каждое чтение и попадание в кеш Rails.
  • Событие cache_fetch_hit.active_support позволяет нам подсчитывать обращения к кешу Rails при использовании fetch.
  • Событие start_processing.action_controller позволяет нам очистить внутренние счетчики, а также выполнить GC.start и зафиксировать количество объектов, находящихся в ObjectSpace.
  • Событие process_action.action_controller предоставляет нам информацию о времени выполнения вместе с идентифицирующими деталями действия контроллера, мы даже определяем количество сгенерированных объектов с момента начала обработки действия. На этом этапе мы можем синтезировать информацию о запросе и информацию о времени выполнения и хранить их внутри в текущей коллекции RailsRequestStats::RequestStats объектов.

Примечание: сбор данных отслеживается и сохраняется в переменных экземпляра уровня класса. Таким образом, это не потокобезопасно, так как не используются механизмы параллелизма (т. е. мьютекс). Для серверов приложений без потоков и разветвлений это должно быть хорошо.

Установка

Добавьте эту строку в Gemfile вашего приложения:

gem 'rails_request_stats', group: :development

Пример выходных данных

В консоли ./log/development.log вы должны увидеть следующий оператор, появляющийся в конце обработки запроса:

[RailsRequestStats] (AVG view_runtime: 163.655ms | AVG db_runtime: 15.465ms | AVG generated_object_count: 14523 | query_count: 9 | cached_query_count: 0 | cache_read_count: 3 | cache_hit_count: 3)

Наконец, когда вы выходите из сервера приложения, вы должны увидеть сводный отчет обо всех захваченных данных:

[RailsRequestStats] INDEX:html "/users" (AVG view_runtime: 128.492ms | AVG db_runtime: 9.186ms | AVG generated_object_count: 25529 | MIN query_count: 8 | MAX query_count: 9) from 4 requests
[RailsRequestStats] SHOW:html "/users/2" (AVG view_runtime: 13.0429ms | AVG db_runtime: 1.69033ms | AVG generated_object_count: 14523 | MIN query_count: 2 | MAX query_count: 2) from 3 requests
[RailsRequestStats] SHOW:html "/users/2?test=1&blah=2" (AVG view_runtime: 17.8252ms | AVG db_runtime: 1.621ms | AVG generated_object_count: 18511 | MIN query_count: 2 | MAX query_count: 2) from 1 requests

Настройка выходов

Статистика памяти

Установив следующую переменную класса в инициализаторе (./config/initializers/rails_request_stats.rb):

RailsRequestStats::Report.print_memory_stats = true

Вы можете увидеть созданные объекты в ObjectSpace для отдельных запросов:

[RailsRequestStats] (AVG view_runtime: 93.7252ms | AVG db_runtime: 8.66075ms | AVG generated_object_count: 125282 | query_count: 8 | cached_query_count: 0 | cache_read_count: 3 | cache_hit_count: 3 | generated_objects: {:total_generated_objects=>111878, :object=>921, :class=>35, :module=>0, :float=>0, :string=>49501, :regexp=>1556, :array=>17855, :hash=>2087, :struct=>103, :bignum=>0, :file=>0, :data=>37682, :match=>373, :complex=>0, :node=>1688, :iclass=>0})

Переопределить отчеты

Вы можете вручную переопределить вывод, заплатив обезьяну в инициализаторе (./config/initializers/rails_request_stats.rb):

module RailsRequestStats
  class Report
    # Called after every request
    def report_text
      # Access to @request_stats (instance of RequestStats)
    end
    # Called after the application server is closed (via #at_exit_handler)
    def exit_report_text
      # Access to @request_stats (instance of RequestStats)
    end
  end
  class NotificationSubscribers
    # Called when the application server is closed
    def self.at_exit_handler
      # Access to @requests (hash of { <paths> => RequestStats })
    end
  end
end

Размышления

Я лично использую RailsRequestStats на работе с момента его создания. Прием был довольно хорошим, он не оказал большого влияния и просто предоставляет дополнительную информацию разработчикам во время повседневной работы. Отмечу, что rack-mini-profiler — отличный драгоценный камень, который как бы вдохновил меня на создание этого драгоценного камня. Я не мог найти хороший способ получить нужную информацию при работе с конечными точками API, с помощью RailsRequestStats я смог просто вывести ее в журналы.

В будущем я мог бы добавить больше подробных отчетов памяти. Я также должен убедиться, что он продолжает работать в Rails 5, где веб-сервером по умолчанию является Puma, который является параллельным веб-сервером. Это может создать проблему, поскольку информация хранится в переменной экземпляра класса и изменяется на различных этапах жизненного цикла запросов.

Первоначально опубликовано на kevinjalbert.com.