ActiveSupport::Notifications
is a notification system built into the rails. You can subscribe to certain notifications in Rails and call your code when they are sent. This is somewhat similar to ActiveSupport::Callbacks
, but they work throughout the project and no events need to be announced in advance.'render'
notification: ActiveSupport::Notifications.subscribe("render") do |*args| # render end
subscribe
method. The subscribe
method returns a link to the subscriber, it may be required to unsubscribe from the notification.ActiveSupport::Notifications.publish
method: ActiveSupport::Notifications.publish('render', 'arg1', 'arg2')
'render'
, 'arg1'
and 'arg2'
will be transferred to the subscribe
block.ActiveSupport::Notifications
there is a very useful method instrument
. It accepts a block and after which it sends a notification with time stamps of the beginning and end of block execution, as well as a hash with additional data. Rails uses this mechanism, and indirectly, both the subscribe
and publish
methods to create detailed logs in the development environment (production uses the same “gauges”, but not everything is written to the log) def do_something # end
ActiveSupport::Notifications.instrument
def do_something ActiveSupport::Notifications.instrument('benchmark.do_something', desc: 'Some description') do # end end
config/initializers/benchmarking.rb
subscribe to all notifications with the string 'benchmark.'
. logger = Logger.new(File.join(Rails.root, 'log', "benchmarks.log")) ActiveSupport::Notifications.subscribe(%r/benchmark\.*/) do |name, start, ending, transaction_id, payload| method = name.split(?.).last duration = (1000.0 * (ending - start)) message = if payload[:exception].present? payload[:exception].join(' ') else payload[:desc].to_s end logger.info("Benchmark:%s: %.0fms - %s" % method, duration, message) end
name, start, ending, transaction_id, payload
.name
- the name of the captured notificationstart
- start
time of the blockending
- block end timetransaction_id
- unique id, usually unique within one threadpayload
- additional data transferred to the “meter”payload[:exception]
will be written with the name of the exception and an error message. This also needs to be taken into account.ActiveSupport::Notifications
in logging Rails can be found in more detail in the modules ActiveSupport::LogSubscriber
, ActiveRecord::LogSubscriber
, ActionController::LogSubscriber
and ActionMailer::LogSubscriber
. callback = lambda do|*args| # # "event.name" end ActiveSupport::Notifications.subscribed(callback, "event.name") do # end
unsubscribe
method and pass it a link to the subscriber. ActiveSupport::Notifications.unsubscribe(subscriber)
ActiveSupport::Notifications::Fanout
, it will also be interesting to look at the ActiveSupport::Notifications::Instrumenter
class, which is responsible for measuring the block execution time in the instrument
methodActiveSupport::Notifications
: class Module def benchmark_it *names options, names = benchmark_options_and_names(*names) names.each do |name| target, punctuation = name.to_s.sub(/([?!=])$/, ''), $1 define_method "#{target}_with_benchmark#{punctuation}" do |*args| ActiveSupport::Notifications.instrument("benchmark.#{self.name}.#{name}", options) do send("#{target}_without_benchmark#{punctuation}", *args) end end alias_method_chain name, :benchmark end end protected def benchmark_options_and_names *args options = args.last.is_a?(Hash) ? args.pop : {} [{desc: ''}.merge(options), args] end end ActiveSupport::Notifications.subscribe(%r/benchmark\.*/) do |name, start, ending, transaction_id, payload| _, classname, method = name.split(?.) duration = (1000.0 * (ending - start)) message = if payload[:exception].present? payload[:exception].join(' ') else payload[:desc].to_s end Rails.logger.info("Benchmark: %s.%s: %.0fms - %s" % classname, method, duration, message) end
class MyClass def my_method # - end # benchmark_it :my_method, desc: ' ' end
MyORM
, and hang the logging task on the MyORM::LogSubscriber
class inherited from ActiveSupport::LogSubscriber
. The message code responsible for displaying the logs is not spread over the application, but in one place. Well, of course, you need to place the sensors throughout the library. By the way, these same sensors can be used for anything else besides logging.Source: https://habr.com/ru/post/160701/
All Articles