require 'rubygems' require "eventmachine" require 'em-hiredis' require 'redis' require "active_support/core_ext" require 'yajl' require 'sinatra/base' require 'haml' require 'thin' module FnordMetric @@namespaces = {} def self.namespace(key=nil, &block) @@namespaces[key] = block end def self.server_configuration=(configuration) @@server_configuration = configuration end def self.default_options(opts) opts[:redis_url] ||= "redis://localhost:6379" opts[:redis_prefix] ||= "fnordmetric" opts[:inbound_stream] ||= ["0.0.0.0", "1337"] opts[:web_interface] ||= ["0.0.0.0", "4242"] opts[:start_worker] ||= true opts[:print_stats] ||= 3 # events that aren't processed after 2 min get dropped opts[:event_queue_ttl] ||= 120 # event data is kept for one month opts[:event_data_ttl] ||= 3600*24*30 # session data is kept for one month opts[:session_data_ttl] ||= 3600*24*30 opts end def self.start_em(opts) EM.run do trap("TERM", &method(:shutdown)) trap("INT", &method(:shutdown)) opts = default_options(opts) if opts[:start_worker] worker = Worker.new(@@namespaces.clone, opts) worker.ready! end if opts[:inbound_stream] begin inbound_stream = InboundStream.start(opts) log "listening on tcp##{opts[:inbound_stream].join(":")}" rescue log "cant start FnordMetric::InboundStream. port in use?" end end if opts[:web_interface] begin app = FnordMetric::App.new(@@namespaces.clone, opts) Thin::Server.start(*opts[:web_interface], app) log "listening on http##{opts[:web_interface].join(":")}" rescue Exception => e log "cant start FnordMetric::App. port in use?" end end if opts[:print_stats] redis = connect_redis(opts[:redis_url]) EM::PeriodicTimer.new(opts[:print_stats]) do print_stats(opts, redis) end end end end def self.log(msg) puts "[#{Time.now.strftime("%y-%m-%d %H:%M:%S")}] #{msg}" end def self.error!(msg) raise msg if ENV['FNORDMETRIC_ENV'] == 'test' puts(msg); exit! end def self.run opts = (defined?(@@server_configuration) && @@server_configuration) || {} start_em(opts) rescue Exception => e log "!!! eventmachine died, restarting... #{e.message}" sleep(1); run(opts) end def self.shutdown log "shutting down, byebye" EM.stop end def self.connect_redis(redis_url) EM::Hiredis.connect(redis_url) end def self.print_stats(opts, redis) # FIXME: refactor this mess keys = [:events_received, :events_processed] redis.llen("#{opts[:redis_prefix]}-queue") do |queue_length| redis.hmget("#{opts[:redis_prefix]}-stats", *keys) do |data| data_human = keys.size.times.map{|n|"#{keys[n]}: #{data[n]}"} log "#{data_human.join(", ")}, queue_length: #{queue_length}" end end end def self.standalone require "fnordmetric/logger" require "fnordmetric/standalone" end end require "fnordmetric/inbound_stream" require "fnordmetric/worker" require "fnordmetric/widget" require "fnordmetric/timeline_widget" require "fnordmetric/numbers_widget" require "fnordmetric/bars_widget" require "fnordmetric/toplist_widget" require "fnordmetric/pie_widget" require "fnordmetric/namespace" require "fnordmetric/gauge_modifiers" require "fnordmetric/gauge_calculations" require "fnordmetric/context" require "fnordmetric/gauge" require "fnordmetric/session" require "fnordmetric/app" require "fnordmetric/dashboard" require "fnordmetric/event" #require "fnordmetric/metric_api" #require "fnordmetric/cache" #require "fnordmetric/report" #require "fnordmetric/metric" #require "fnordmetric/average_metric" #require "fnordmetric/count_metric" #require "fnordmetric/combine_metric" #require "fnordmetric/sum_metric" #require "fnordmetric/widget" # #require "fnordmetric/funnel_widget"