aboutsummaryrefslogtreecommitdiffstats
path: root/activesupport/lib/active_support/notifications
diff options
context:
space:
mode:
Diffstat (limited to 'activesupport/lib/active_support/notifications')
-rw-r--r--activesupport/lib/active_support/notifications/fanout.rb101
-rw-r--r--activesupport/lib/active_support/notifications/instrumenter.rb47
2 files changed, 148 insertions, 0 deletions
diff --git a/activesupport/lib/active_support/notifications/fanout.rb b/activesupport/lib/active_support/notifications/fanout.rb
new file mode 100644
index 0000000000..bb07e4765c
--- /dev/null
+++ b/activesupport/lib/active_support/notifications/fanout.rb
@@ -0,0 +1,101 @@
+require 'thread'
+
+module ActiveSupport
+ module Notifications
+ # This is a default queue implementation that ships with Notifications. It
+ # consumes events in a thread and publish them to all registered subscribers.
+ #
+ class Fanout
+ def initialize(sync = false)
+ @subscriber_klass = sync ? Subscriber : AsyncSubscriber
+ @subscribers = []
+ end
+
+ def bind(pattern)
+ Binding.new(self, pattern)
+ end
+
+ def subscribe(pattern = nil, &block)
+ @subscribers << @subscriber_klass.new(pattern, &block)
+ end
+
+ def publish(*args)
+ @subscribers.each { |s| s.publish(*args) }
+ end
+
+ def wait
+ sleep(0.05) until @subscribers.all?(&:drained?)
+ end
+
+ # Used for internal implementation only.
+ class Binding #:nodoc:
+ def initialize(queue, pattern)
+ @queue = queue
+ @pattern =
+ case pattern
+ when Regexp, NilClass
+ pattern
+ else
+ /^#{Regexp.escape(pattern.to_s)}/
+ end
+ end
+
+ def subscribe(&block)
+ @queue.subscribe(@pattern, &block)
+ end
+ end
+
+ class Subscriber #:nodoc:
+ def initialize(pattern, &block)
+ @pattern = pattern
+ @block = block
+ end
+
+ def publish(*args)
+ push(*args) if matches?(args.first)
+ end
+
+ def drained?
+ true
+ end
+
+ private
+ def matches?(name)
+ !@pattern || @pattern =~ name.to_s
+ end
+
+ def push(*args)
+ @block.call(*args)
+ end
+ end
+
+ # Used for internal implementation only.
+ class AsyncSubscriber < Subscriber #:nodoc:
+ def initialize(pattern, &block)
+ super
+ @events = Queue.new
+ start_consumer
+ end
+
+ def drained?
+ @events.empty?
+ end
+
+ private
+ def start_consumer
+ Thread.new { consume }
+ end
+
+ def consume
+ while args = @events.shift
+ @block.call(*args)
+ end
+ end
+
+ def push(*args)
+ @events << args
+ end
+ end
+ end
+ end
+end
diff --git a/activesupport/lib/active_support/notifications/instrumenter.rb b/activesupport/lib/active_support/notifications/instrumenter.rb
new file mode 100644
index 0000000000..fb95422af2
--- /dev/null
+++ b/activesupport/lib/active_support/notifications/instrumenter.rb
@@ -0,0 +1,47 @@
+require 'active_support/secure_random'
+require 'active_support/core_ext/module/delegation'
+
+module ActiveSupport
+ module Notifications
+ class Instrumenter
+ def initialize(notifier)
+ @id = unique_id
+ @notifier = notifier
+ end
+
+ def instrument(name, payload={})
+ time = Time.now
+ result = yield if block_given?
+ ensure
+ @notifier.publish(name, time, Time.now, result, @id, payload)
+ end
+
+ private
+ def unique_id
+ SecureRandom.hex(10)
+ end
+ end
+
+ class Event
+ attr_reader :name, :time, :end, :transaction_id, :result, :payload
+
+ def initialize(name, start, ending, result, transaction_id, payload)
+ @name = name
+ @payload = payload.dup
+ @time = start
+ @transaction_id = transaction_id
+ @end = ending
+ @result = result
+ end
+
+ def duration
+ @duration ||= 1000.0 * (@end - @time)
+ end
+
+ def parent_of?(event)
+ start = (self.time - event.time) * 1000
+ start <= 0 && (start + duration >= event.duration)
+ end
+ end
+ end
+end