procmon 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/README ADDED
@@ -0,0 +1,44 @@
1
+ Example of how to use Procmon
2
+
3
+ require 'procmon'
4
+
5
+
6
+ notifier = Proc.new do
7
+ puts "I NEED TO SEND OUT AN EMAIL"
8
+ end
9
+
10
+ notifier2 = Proc.new do
11
+ puts "I NEED TO SEND OUT AN SMS"
12
+ end
13
+
14
+ # action be be a Proc object. You can define whatever you want to do
15
+ Procmon.process("Mail") do |process|
16
+ process.checks :mem_usage, :above => 100.megabytes, :actions => [notifier, notifier2]
17
+ end
18
+
19
+ # You can specify a code block as well
20
+ Procmon.process("Mail") do |process|
21
+ process.pid = 29124
22
+ process.checks :mem_usage, :above => 100.megabytes do
23
+ puts "I'm in your base"
24
+ end
25
+ end
26
+
27
+ # Using built-in email notifier
28
+ NOTIFICATION_TARGET='ddao@example.com'
29
+ Procmon.process("Mail") do |process|
30
+ process.checks :mem_usage, :above => 100.megabytes, :actions => [Procmon::Notifiers::Email]
31
+ end
32
+
33
+ # Would be nice if we can do this as well
34
+ # Procmon.process("Mail") do |process|
35
+ # process.checks :mem_usage, :above => 100.megabytes do |action|
36
+ # action.perform(notifier)
37
+ # action.perform(Procmon::Notifiers::Email, 'someone@example.com')
38
+ # end
39
+ # process.checks :mem_usage, :above => 100.megabytes, :actions => [notifier, notifier2]
40
+ # process.checks :mem_usage, :above => 100.megabytes, :actions => [Procmon::Notifiers::Email]
41
+ #end
42
+
43
+
44
+
data/bin/placeholder ADDED
File without changes
Binary file
@@ -0,0 +1,41 @@
1
+ module Procmon
2
+ class ProcessFactory
3
+ attr_reader :attributes, :name, :checks
4
+ def initialize(name, attributes, process_block)
5
+ @checks = {}
6
+ @name = name
7
+ @attributes = attributes
8
+ if process_block.arity == 0
9
+ instance_eval &process_block
10
+ else
11
+ instance_exec(self, &process_block)
12
+ end
13
+ end
14
+
15
+ def method_missing(name, *args)
16
+ if args.size == 1 && name.to_s =~ /^(.*)=$/
17
+ @attributes[$1.to_sym] = args.first
18
+ elsif args.size == 1
19
+ @attributes[name.to_sym] = args.first
20
+ elsif args.size == 0 && name.to_s =~ /^(.*)!$/
21
+ @attributes[$1.to_sym] = true
22
+ elsif args.empty? && @attributes.key?(name.to_sym)
23
+ @attributes[name.to_sym]
24
+ else
25
+ super
26
+ end
27
+ end
28
+
29
+ def checks(name, options = {}, &block)
30
+ if block
31
+ options[:actions] ||= []
32
+ options[:actions] << block
33
+ end
34
+ @checks[name] = options
35
+ end
36
+
37
+ def create_process
38
+ Process.new(@name, @checks, @attributes)
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,7 @@
1
+ module Procmon
2
+ def self.process(proc_name, options = {}, &block)
3
+ proc_fact = ProcessFactory.new(proc_name, options, block)
4
+ process = proc_fact.create_process
5
+ process.run_checks
6
+ end
7
+ end
@@ -0,0 +1,16 @@
1
+ module Procmon
2
+ module Notifiers
3
+ class Email < Notifier
4
+ def self.notify(notification, target=nil)
5
+ if target.nil? && !defined?(NOTIFICATION_TARGET)
6
+ warn "Don't know where to send the notification"
7
+ elsif target.nil?
8
+ target = NOTIFICATION_TARGET
9
+ end
10
+
11
+ puts "Need to email #{target} the following"
12
+ puts notification
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,9 @@
1
+ module Procmon
2
+ module Notifiers
3
+ class Notifier
4
+ def self.notify
5
+ raise "Implement in subclass!"
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,5 @@
1
+ require 'procmon/notifiers/notifier'
2
+
3
+ Dir["#{File.dirname(__FILE__)}/notifiers/*.rb"].each do |file|
4
+ require file
5
+ end
@@ -0,0 +1,81 @@
1
+ module Procmon
2
+ class Process
3
+ CONFIGURABLE_ATTRIBUTES = [
4
+ :pid_file,
5
+ :pid_command,
6
+ :pid
7
+ ]
8
+ attr_accessor *CONFIGURABLE_ATTRIBUTES
9
+ attr_accessor :name, :checks
10
+
11
+ def initialize(name, checks, options={})
12
+ @name = name
13
+ @checks = []
14
+
15
+ CONFIGURABLE_ATTRIBUTES.each do |attribute_name|
16
+ self.send("#{attribute_name}=", options[attribute_name]) if options.has_key?(attribute_name)
17
+ end
18
+
19
+ if @pid_command.nil? or @pid_command.empty?
20
+ @pid_command = "pidof"
21
+ end
22
+
23
+ if @pid.nil?
24
+ @pid = fetch_pid
25
+ end
26
+
27
+ checks.each do |name, opts|
28
+ self.add_check(name, opts)
29
+ end
30
+ end
31
+
32
+ def run_checks
33
+ @checks.each do |check|
34
+ value = check.run(pid)
35
+ condition_met = check.check(value)
36
+ perform_actions(check) if condition_met
37
+ end
38
+ end
39
+
40
+ def add_check(name, options)
41
+ self.checks << ProcessConditions[name].new(options)
42
+ end
43
+
44
+ def perform_actions(check)
45
+ return if check.actions.nil? or check.actions.empty?
46
+
47
+ check.actions.each do |action|
48
+ if action.kind_of?(Proc)
49
+ action.call(self, check)
50
+ elsif action.kind_of?(Class) && action.respond_to?('notify')
51
+ notification = ProcessNotification.new(self, check)
52
+ action.notify(notification)
53
+ else
54
+ warn "Don't know what to do for #{action}."
55
+ end
56
+ end
57
+ end
58
+
59
+ private
60
+ def fetch_pid
61
+ pid_from_command || pid_from_file
62
+ end
63
+
64
+ def pid_from_file
65
+ if pid_file
66
+ if File.exists?(pid_file)
67
+ str = File.read(pid_file)
68
+ str.to_i if str.size > 0
69
+ else
70
+ logger.warning("pid_file #{pid_file} does not exist or cannot be read")
71
+ nil
72
+ end
73
+ end
74
+ end
75
+
76
+ def pid_from_command
77
+ pid = %x{#{pid_command} #{@name}}.strip
78
+ (pid =~ /\A\d+\z/) ? pid.to_i : nil
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,55 @@
1
+ module Procmon
2
+ module ProcessConditions
3
+ class MemUsage < ProcessCondition
4
+ MB = 1024 ** 2
5
+ FORMAT_STR = "%d%s"
6
+ MB_LABEL = "MB"
7
+ KB_LABEL = "KB"
8
+
9
+ attr_reader :actions
10
+
11
+ def initialize(options = {})
12
+ @options = options
13
+ @actions = options[:actions]
14
+ @below = options[:below]
15
+ @above = options[:above]
16
+ if @below && @above && @below < @above
17
+ raise "Invalid range for mem check condition"
18
+ elsif @below.nil? && @above.nil?
19
+ raise "Invalid range for mem check condition"
20
+ end
21
+ end
22
+
23
+ def run(pid)
24
+ # rss is on the 5th col
25
+ System.memory_usage(pid).to_f
26
+ end
27
+
28
+ def check(value)
29
+ if @below && @above
30
+ value.kilobytes < @below && value.kilobytes > @above
31
+ elsif @below
32
+ value.kilobytes < @below
33
+ elsif @above
34
+ value.kilobytes > @above
35
+ end
36
+ end
37
+
38
+ def format_value(value)
39
+ if value.kilobytes >= MB
40
+ FORMAT_STR % [(value / 1024).round, MB_LABEL]
41
+ else
42
+ FORMAT_STR % [value, KB_LABEL]
43
+ end
44
+ end
45
+
46
+ def description
47
+ ret = "Memory usage is "
48
+ conditions = {}
49
+ conditions[:below] = @options[:below] if @options[:below]
50
+ conditions[:above] = @options[:above] if @options[:above]
51
+ ret += conditions.inspect
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,25 @@
1
+ module Procmon
2
+ module ProcessConditions
3
+ class ProcessCondition
4
+ def initialize(options = {})
5
+ @options = options
6
+ end
7
+
8
+ def run(pid)
9
+ raise "Implement in subclass!"
10
+ end
11
+
12
+ def check(value)
13
+ raise "Implement in subclass!"
14
+ end
15
+
16
+ def format_value(value)
17
+ value
18
+ end
19
+
20
+ def description
21
+ raise "Implement in subclass!"
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,12 @@
1
+ module Procmon
2
+ module ProcessConditions
3
+ def self.[](name)
4
+ const_get(name.to_s.camelcase)
5
+ end
6
+ end
7
+ end
8
+
9
+ require "procmon/process_conditions/process_condition"
10
+ Dir["#{File.dirname(__FILE__)}/process_conditions/*.rb"].each do |pc|
11
+ require pc
12
+ end
@@ -0,0 +1,14 @@
1
+ module Procmon
2
+ class ProcessNotification
3
+ def initialize(process, check)
4
+ hostname = `hostname`.strip
5
+ @msg = ""
6
+ @msg += "Hostname: #{hostname}" + "\n"
7
+ @msg += "Process: #{process.name}" + "\n"
8
+ @msg += "Description: #{check.description}"
9
+ end
10
+ def to_s
11
+ @msg
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,38 @@
1
+ module Procmon
2
+ # Copied from Bluepill code
3
+ module System
4
+ extend self
5
+
6
+ # The position of each field in ps output
7
+ IDX_MAP = {
8
+ :pid => 0,
9
+ :ppid => 1,
10
+ :pcpu => 2,
11
+ :rss => 3
12
+ }
13
+
14
+ def store
15
+ @store ||= Hash.new
16
+ end
17
+
18
+ def memory_usage(pid)
19
+ ps_axu[pid] && ps_axu[pid][IDX_MAP[:rss]].to_f
20
+ end
21
+
22
+ def ps_axu
23
+ # TODO: need a mutex here
24
+ store[:ps_axu] ||= begin
25
+ # BSD style ps invocation
26
+ lines = `ps axo pid,ppid,pcpu,rss`.split("\n")
27
+
28
+ lines.inject(Hash.new) do |mem, line|
29
+ chunks = line.split(/\s+/)
30
+ chunks.delete_if {|c| c.strip.empty? }
31
+ pid = chunks[IDX_MAP[:pid]].strip.to_i
32
+ mem[pid] = chunks
33
+ mem
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,3 @@
1
+ module Procmon
2
+ VERSION = "0.0.1".freeze
3
+ end
data/lib/procmon.rb ADDED
@@ -0,0 +1,13 @@
1
+ require 'rubygems'
2
+ require 'active_support/inflector'
3
+ require 'active_support/core_ext/hash'
4
+ require 'active_support/core_ext/numeric'
5
+ require 'active_support/duration'
6
+
7
+ require 'procmon/system'
8
+ require 'procmon/process'
9
+ require 'procmon/dsl'
10
+ require 'procmon/dsl/process_factory'
11
+ require 'procmon/process_conditions'
12
+ require 'procmon/process_notification'
13
+ require 'procmon/notifiers'
data/lib/test.rb ADDED
@@ -0,0 +1,36 @@
1
+ require 'procmon'
2
+
3
+ NOTIFICATION_TARGET='ddao@example.com'
4
+
5
+ notifier = Proc.new do
6
+ puts "I NEED TO SEND OUT AN EMAIL"
7
+ end
8
+
9
+ notifier2 = Proc.new do
10
+ puts "I NEED TO SEND OUT AN EMAIL 2"
11
+ end
12
+
13
+ Procmon.process("Mail") do |process|
14
+ process.pid = 29124
15
+ process.checks :mem_usage, :above => 100.megabytes do
16
+ puts "I'm in your base"
17
+ end
18
+ end
19
+
20
+ Procmon.process("Mail") do |process|
21
+ process.checks :mem_usage, :above => 100.megabytes, :actions => [notifier, notifier2]
22
+ end
23
+
24
+ Procmon.process("Mail") do |process|
25
+ process.checks :mem_usage, :above => 100.megabytes, :actions => [Procmon::Notifiers::Email]
26
+ end
27
+
28
+ # Would be nice if we can do this
29
+ # Procmon.process("Mail") do |process|
30
+ # process.checks :mem_usage, :above => 100.megabytes do |action|
31
+ # action.perform(notifier)
32
+ # action.perform(Procmon::Notifiers::Email, 'someone@eharmony.com')
33
+ # end
34
+ # process.checks :mem_usage, :above => 100.megabytes, :actions => [notifier, notifier2]
35
+ # process.checks :mem_usage, :above => 100.megabytes, :actions => [Procmon::Notifiers::Email]
36
+ #end
data/procmon.gemspec ADDED
@@ -0,0 +1,24 @@
1
+ $LOAD_PATH.push File.expand_path("../lib", __FILE__)
2
+
3
+ require "procmon/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "procmon"
7
+ s.version = Procmon::VERSION.dup
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Darren Dao"]
10
+ s.email = ["darrendao@gmail.com"]
11
+ s.homepage = "http://github.com/darrendao/procmon"
12
+ s.summary = %q{A process monitor written in Ruby. Concepts and design are based on Bluepill and God, but with emphasis on simplicity and extensibility.}
13
+ s.description = %q{Procmon allows you to check on processes and then perform arbitary actions based on the result of the checks. For example, you can send notification when mem usage is too high, or you can restart a process if it's dead, etc. It is not meant to run as a daemon. It is designed to be invoked manually or via cron.}
14
+
15
+ s.add_dependency 'activesupport', '>= 3.0.0'
16
+ s.add_dependency 'i18n', '>= 0.5.0'
17
+
18
+ s.files = `git ls-files`.split("\n")
19
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
20
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
21
+ s.require_paths = ["lib"]
22
+ s.extra_rdoc_files = ["README"]
23
+ end
24
+
metadata ADDED
@@ -0,0 +1,92 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: procmon
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Darren Dao
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2011-11-15 00:00:00 -08:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: activesupport
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 3.0.0
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: i18n
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 0.5.0
34
+ version:
35
+ description: Procmon allows you to check on processes and then perform arbitary actions based on the result of the checks. For example, you can send notification when mem usage is too high, or you can restart a process if it's dead, etc. It is not meant to run as a daemon. It is designed to be invoked manually or via cron.
36
+ email:
37
+ - darrendao@gmail.com
38
+ executables:
39
+ - placeholder
40
+ extensions: []
41
+
42
+ extra_rdoc_files:
43
+ - README
44
+ files:
45
+ - README
46
+ - bin/placeholder
47
+ - lib/procmon.rb
48
+ - lib/procmon/.dsl.rb.swp
49
+ - lib/procmon/dsl.rb
50
+ - lib/procmon/dsl/process_factory.rb
51
+ - lib/procmon/notifiers.rb
52
+ - lib/procmon/notifiers/email.rb
53
+ - lib/procmon/notifiers/notifier.rb
54
+ - lib/procmon/process.rb
55
+ - lib/procmon/process_conditions.rb
56
+ - lib/procmon/process_conditions/mem_usage.rb
57
+ - lib/procmon/process_conditions/process_condition.rb
58
+ - lib/procmon/process_notification.rb
59
+ - lib/procmon/system.rb
60
+ - lib/procmon/version.rb
61
+ - lib/test.rb
62
+ - procmon.gemspec
63
+ has_rdoc: true
64
+ homepage: http://github.com/darrendao/procmon
65
+ licenses: []
66
+
67
+ post_install_message:
68
+ rdoc_options: []
69
+
70
+ require_paths:
71
+ - lib
72
+ required_ruby_version: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: "0"
77
+ version:
78
+ required_rubygems_version: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: "0"
83
+ version:
84
+ requirements: []
85
+
86
+ rubyforge_project:
87
+ rubygems_version: 1.3.5
88
+ signing_key:
89
+ specification_version: 3
90
+ summary: A process monitor written in Ruby. Concepts and design are based on Bluepill and God, but with emphasis on simplicity and extensibility.
91
+ test_files: []
92
+