class Eye::Checker include Eye::Dsl::Validation autoload :Memory, 'eye/checker/memory' autoload :Cpu, 'eye/checker/cpu' autoload :Http, 'eye/checker/http' autoload :FileCTime, 'eye/checker/file_ctime' autoload :FileSize, 'eye/checker/file_size' autoload :FileTouched, 'eye/checker/file_touched' autoload :Socket, 'eye/checker/socket' autoload :SslSocket, 'eye/checker/ssl_socket' autoload :Nop, 'eye/checker/nop' autoload :Runtime, 'eye/checker/runtime' autoload :Cputime, 'eye/checker/cputime' autoload :ChildrenCount, 'eye/checker/children_count' autoload :ChildrenMemory, 'eye/checker/children_memory' TYPES = { memory: 'Memory', cpu: 'Cpu', http: 'Http', ctime: 'FileCTime', fsize: 'FileSize', file_touched: 'FileTouched', socket: 'Socket', nop: 'Nop', runtime: 'Runtime', cputime: 'Cputime', children_count: 'ChildrenCount', children_memory: 'ChildrenMemory', ssl_socket: 'SslSocket' } attr_accessor :value, :values, :options, :pid, :type, :check_count, :process param :every, [Integer, Float], false, 5 param :times, [Integer, Array], nil, 1 param :fires, [Symbol, Array, Proc], nil, nil, [:stop, :restart, :unmonitor, :start, :delete, :nothing, :notify] param :initial_grace, [Integer, Float] param :skip_initial_fails, [TrueClass, FalseClass] def self.name_and_class(type) type = type.to_sym return { name: type, type: type } if TYPES[type] if type =~ %r[\A(.*?)_?[0-9]+\z] ctype = Regexp.last_match(1).to_sym return { name: type, type: ctype } if TYPES[ctype] end end def self.get_class(type) klass = eval("Eye::Checker::#{TYPES[type]}") rescue nil raise "Unknown checker #{type}" unless klass if deps = klass.requires Array(deps).each { |d| require d } end klass end def self.create(pid, options = {}, process = nil) get_class(options[:type]).new(pid, options, process) rescue Object => ex log_ex(ex) nil end def self.validate!(options) get_class(options[:type]).validate(options) end def initialize(pid, options = {}, process = nil) @process = process @pid = pid @options = options.dup @type = options[:type] @full_name = @process.full_name if @process @initialized_at = Time.now debug { "create checker, with #{options}" } @value = nil @values = Eye::Utils::Tail.new(max_tries) @check_count = 0 end def inspect "<#{self.class} @process='#{@full_name}' @options=#{@options} @pid=#{@pid}>" end def logger_tag @logger_tag ||= @process ? @process.logger.prefix : nil end def logger_sub_tag @logger_sub_tag ||= "check:#{check_name}" end def last_human_values h_values = @values.map do |v| sign = v[:good] ? '' : '*' sign + human_value(v[:value]).to_s end '[' + h_values * ', ' + ']' end def check if initial_grace if Time.now - @initialized_at < initial_grace debug { 'skipped initial grace' } return true else @options[:initial_grace] = nil end end @value = get_value_safe @good_value = good?(value) @values << { value: @value, good: @good_value } result = true @check_count += 1 if @values.size == max_tries bad_count = @values.count { |v| !v[:good] } result = false if bad_count >= min_tries end if skip_initial_fails if @good_value @options[:skip_initial_fails] = nil else result = true end end info { "#{last_human_values} => #{result ? 'OK' : 'Fail'}" } result rescue Object => ex raise(ex) if ex.class == Celluloid::TaskTerminated log_ex(ex) end def get_value_safe get_value end def get_value raise NotImplementedError end def human_value(value) value.to_s end # true if check ok # false if check bad def good?(value) value end def check_name @check_name ||= @type.to_s end def max_tries @max_tries ||= if times if times.is_a?(Array) times[-1].to_i else times.to_i end else 1 end end def min_tries @min_tries ||= if times if times.is_a?(Array) times[0].to_i else max_tries end else max_tries end end def previous_value @values[-1][:value] if @values.present? end def run_in_process_context(p) process.instance_exec(&p) if process.alive? end def fire actions = fires ? Array(fires) : [:restart] process.notify :warn, "Bounded #{check_name}: #{last_human_values} send to #{actions}" actions.each do |action| reason = "bounded #{check_name}" if action.is_a?(Proc) process.schedule command: :instance_exec, reason: reason, block: action else process.schedule command: action, reason: reason end end end def defer(&block) Celluloid::Future.new(&block).value end class Defer < Eye::Checker def get_value_safe Celluloid::Future.new { get_value }.value end end def self.register(base) name = base.to_s.gsub('Eye::Checker::', '') type = name.underscore.to_sym Eye::Checker::TYPES[type] = name Eye::Checker.const_set(name, base) end def self.requires; end class CustomCell < Eye::Checker def self.inherited(base) super register(base) end end class Custom < Defer def self.inherited(base) super register(base) end end class CustomDefer < Defer def self.inherited(base) super register(base) end end class Measure < Eye::Checker param :below, [Integer, Float] param :above, [Integer, Float] def good?(value) return false if below && (value > below) return false if above && (value < above) true end def measure_str if below && above ">#{human_value(above)}<#{human_value(below)}" elsif below "<#{human_value(below)}" elsif above ">#{human_value(above)}" else '-' end end end end