Permalink
Browse files

Accumulator and AccumulationsConsumer make it easy for processes to t…

…rack single events over time.
  • Loading branch information...
1 parent ae614ba commit 7acb021d2497de85698dc4c2a47b62e079d75b3f Dhruv Bansal committed Sep 10, 2010
View
@@ -3,11 +3,12 @@
module Graphiterb
autoload :Monitors, 'graphiterb/monitors'
autoload :Sender, 'graphiterb/sender'
+ autoload :Accumulator, 'graphiterb/accumulator'
end
Settings.use :define, :config_file
-Settings.define :log, :default => STDOUT, :description => "Log output for Graphiterb"
+Settings.define :log, :description => "Log output for Graphiterb"
Settings.define :carbon_server, :default => 'localhost', :description => "Host address for carbon database server", :required => true
Settings.define :carbon_port, :default => '2003', :description => "Port for carbon database server", :required => true
Settings.define :update_delay, :default => 30, :description => "How long to wait between updates. Must be faster than the value in the graphite/conf/storage-schemas", :required => true, :type => Integer
@@ -0,0 +1,81 @@
+module Graphiterb
+
+ # An accumulator that uses a Redis database as a fast store.
+ #
+ # a = Accumulator.new
+ # a.increment('my_value')
+ #
+ # It's assumed that the Redis database is local and on the default
+ # port, but pass in :host or :port (or any other options Redis.new
+ # understands) to change this.
+ #
+ # By default incrementing 'my_value' which actually increment a
+ # counter stored at the key
+ # 'graphiterb_accumulator:my_value:HOSTNAME'.
+ #
+ # See Graphiterb::Monitors::AccumulationsConsumer for the periodic
+ # monitor that will consume the accumulated counts.
+ class Accumulator
+
+ # The Redis database.
+ attr_accessor :redis
+
+ # The Redis namespace used for the accumulators.
+ attr_accessor :accumulators
+
+ # The top-level scope under which to accumulate.
+ attr_accessor :main_scope
+
+ # Provides methods for finding out about the node this code is
+ # running on.
+ include Graphiterb::Utils::SystemInfo
+
+ # Initialize a new Accumulator.
+ #
+ # Takes the same options as Redis.new.
+ #
+ # @param [String] main_scope
+ # @param [Hash] options
+ def initialize main_scope, options={}
+ require 'redis'
+ require 'redis-namespace'
+ @main_scope = main_scope
+ @redis = Redis.new(options)
+ @accumulators = Redis::Namespace.new('graphiterb_accumulators', :redis => redis)
+ end
+
+ # Increment the Graphite target +args+ by the given +amount+.
+ #
+ # The target will be automatically scoped, see Accumulator#scope.
+ #
+ # @param [Integer] amount
+ # @param [Array<String>, String] args
+ def increment_by amount, *args
+ accumulators.incrby(scope(*args), amount)
+ end
+
+ # Increment the Graphite target +args+.
+ #
+ # @param [Array<String>, String] args
+ def increment *args
+ accumulators.incr(scope(*args))
+ end
+
+ # Return the scoped accumulator name.
+ #
+ # This will be a valid string target that can be passed directly
+ # to Graphite.
+ #
+ # a = Accumulator.new('scrapers')
+ # a.scope('foo.bar', 'baz')
+ # #=> 'scrapers.foo.bar.baz.ip-120.112.4.383'
+ #
+ # @param [Array<String>, String] args
+ # @return [String]
+ def scope *args
+ [main_scope, args, hostname].flatten.compact.map(&:to_s).join('.')
+ end
+
+ end
+end
+
@@ -4,6 +4,7 @@ module Monitors
autoload :DiskSpace, 'graphiterb/monitors/disk_space'
autoload :System, 'graphiterb/monitors/system'
autoload :DirectoryTree, 'graphiterb/monitors/directory_tree'
+ autoload :AccumulationsConsumer, 'graphiterb/monitors/accumulations_consumer'
# Accepts a lightweight call every iteration.
#
@@ -0,0 +1,39 @@
+module Graphiterb
+ module Monitors
+
+ # A monitor which consumes counts accumulated in a Redis store by
+ # Graphiterb::Accumulator.
+ class AccumulationsConsumer < Graphiterb::Monitors::PeriodicMonitor
+
+ # The Redis database.
+ attr_accessor :redis
+
+ # The Redis namespace used for the accumulators.
+ attr_accessor :accumulators
+
+ # Instantiate a new AccumulationsConsumer.
+ #
+ # Options are passed to Redis.new as well as
+ # Graphiterb::Monitors::PeriodicMonitor.
+ def initialize options={}
+ require 'redis'
+ require 'redis-namespace'
+ @redis = Redis.new(options)
+ @accumulators = Redis::Namespace.new('graphiterb_accumulators', :redis => @redis)
+ super('fake_scope', options)
+ end
+
+ # Uses Redis' +getset+ call to retrieve total accumulated counts
+ # from Redis and reset them to 0 atomically.
+ def get_metrics metrics, since
+ accumulators.keys.each do |target|
+ current_count = accumulators.getset(target, 0) rescue 0.0
+ rate = current_count.to_f / since.to_f rescue 0.0
+ metrics << [target, rate] # no need to scope as targets are pre-scoped
+ end
+ end
+
+ end
+ end
+end
+

0 comments on commit 7acb021

Please sign in to comment.