notifications.rb 2.6 KB
Newer Older
1
require 'active_support/core_ext/module/delegation'
J
José Valim 已提交
2 3

module ActiveSupport
4 5
  # Notifications provides an instrumentation API for Ruby. To instrument an
  # action in Ruby you just need to do:
J
José Valim 已提交
6
  #
7
  #   ActiveSupport::Notifications.instrument(:render, :extra => :information) do
J
José Valim 已提交
8 9 10
  #     render :text => "Foo"
  #   end
  #
11
  # You can consume those events and the information they provide by registering
12
  # a log subscriber. For instance, let's store all instrumented events in an array:
J
José Valim 已提交
13
  #
14 15
  #   @events = []
  #
16 17
  #   ActiveSupport::Notifications.subscribe do |*args|
  #     @events << ActiveSupport::Notifications::Event.new(*args)
18
  #   end
J
José Valim 已提交
19
  #
20
  #   ActiveSupport::Notifications.instrument(:render, :extra => :information) do
J
José Valim 已提交
21 22 23
  #     render :text => "Foo"
  #   end
  #
24
  #   event = @events.first
J
José Valim 已提交
25
  #   event.name      #=> :render
26
  #   event.duration  #=> 10 (in milliseconds)
J
José Valim 已提交
27 28
  #   event.payload   #=> { :extra => :information }
  #
29
  # When subscribing to Notifications, you can pass a pattern, to only consume
30 31
  # events that match the pattern:
  #
32
  #   ActiveSupport::Notifications.subscribe(/render/) do |event|
33 34 35
  #     @render_events << event
  #   end
  #
36
  # Notifications ships with a queue implementation that consumes and publish events
37
  # to log subscribers in a thread. You can use any queue implementation you want.
J
José Valim 已提交
38
  #
39
  module Notifications
40 41 42
    autoload :Instrumenter, 'active_support/notifications/instrumenter'
    autoload :Event, 'active_support/notifications/instrumenter'
    autoload :Fanout, 'active_support/notifications/fanout'
43

44 45
    @instrumenters = Hash.new { |h,k| h[k] = notifier.listening?(k) }

46
    class << self
47
      attr_writer :notifier
48 49
      delegate :publish, :unsubscribe, :to => :notifier

50
      def instrument(name, payload = {}, info = nil)
51
        if @instrumenters[name]
52 53 54
          instrumenter.instrument(name, payload, info) {
            yield payload if block_given?
          }
55
        else
56 57 58 59 60 61 62 63 64
          value = nil
          if block_given?
            if info
              info[:elapsed] = Benchmark.ms { value = yield payload }
            else
              value = yield payload
            end
          end
          value
65 66 67 68 69 70 71 72 73 74 75 76 77
        end
      end

      def subscribe(*args, &block)
        notifier.subscribe(*args, &block).tap do
          @instrumenters.clear
        end
      end

      def unsubscribe(*args)
        notifier.unsubscribe(*args)
        @instrumenters.clear
      end
78

79
      def notifier
80
        @notifier ||= Fanout.new
81
      end
82 83 84 85

      def instrumenter
        Thread.current[:"instrumentation_#{notifier.object_id}"] ||= Instrumenter.new(notifier)
      end
86
    end
J
José Valim 已提交
87 88
  end
end