process_monitoring 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,47 @@
1
+ # Author: Kolman Vornovitsky (kolmanv@gmail.com)
2
+ # This files gives example of how to use process monitoring module.
3
+
4
+ require 'process_monitoring'
5
+
6
+ # What are we monitoring?
7
+ # 1) Logs
8
+ require 'log'
9
+
10
+ # 2) Parameters
11
+ require 'params'
12
+
13
+ Params.init ARGV
14
+ Params['log_write_to_console'] = true
15
+
16
+ Log.init
17
+
18
+ # 3) Custom hash of variables
19
+ variables = ThreadSafeHash::ThreadSafeHash.new
20
+ variables.set('some_variable', 'this is an example :)')
21
+
22
+ # If you want this example to send emails, set Params['enable_monitoring_emails']=true
23
+ # Before you do that, check the emails parameters at 'monitoring.rb'
24
+
25
+ # Lets initialize the monitoring task.
26
+ # The command below generates new thread which can be retrieved by 'mon.thread'.
27
+ # The command below starts checking periodically for errors in logs and/or send status update emails
28
+ # to Params['monitoring_email']
29
+ mon = Monitoring::Monitoring.new(variables)
30
+
31
+ # Lets initialize the monitoring variables HTTP interface (look at localhost:5555).
32
+ # The command below generates new thread which can be retrieved by 'mon_http.thread'.
33
+ mon_http = MonitoringInfo::MonitoringInfo.new(variables)
34
+
35
+ # Lets connect the monitoring to the log.
36
+ Log.add_consumer(mon)
37
+
38
+ # Let there is a running process
39
+ number = 1
40
+ loop do
41
+ number += 1
42
+ variables.set('number', number)
43
+ Log.info "number: #{number}"
44
+ remote_mon_info = MonitoringInfo::MonitoringInfo.get_remote_monitoring_info('localhost', 5555)
45
+ Log.info "Remote monitoring info (localhost:5555): #{remote_mon_info}" if remote_mon_info
46
+ sleep(3)
47
+ end
@@ -0,0 +1,6 @@
1
+ require 'process_monitoring/monitoring'
2
+ require 'process_monitoring/monitoring_info'
3
+ require 'process_monitoring/thread_safe_hash'
4
+
5
+ module ProcessMonitoring
6
+ end
@@ -0,0 +1,85 @@
1
+ require 'thread'
2
+
3
+ require 'log'
4
+ require 'params'
5
+
6
+ require 'process_monitoring/send_email'
7
+
8
+ # This module purpose it to alert the user on different behaviour such as no space left
9
+ # or other error cases. The module will send email to the user to notify him on those cases.
10
+ module Monitoring
11
+ Params.float('monitoring_sleep_time_in_seconds', 10,
12
+ 'Represents the monitoring sleeping time in seconds to check data')
13
+ Params.float('send_email_duration_4_monitoring_state', 60*60, # Once per hour.
14
+ 'Represents the duration in seconds to send email monitoring state')
15
+
16
+ # Username and Password for gmail account to send monitoting emails.
17
+ Params.string('gmail_username', nil, 'Backup server gmail username.')
18
+ Params.string('gmail_password', nil, 'Backup server gmail password.')
19
+
20
+ class Monitoring
21
+ attr_reader :thread
22
+ def initialize(process_variables)
23
+ @thread = 'dummy'
24
+ end
25
+ end
26
+ =begin
27
+ class Monitoring < Log::Consumer
28
+ attr_reader :thread
29
+ def initialize(process_variables)
30
+ super(false)
31
+ @passed_time_dur = 0
32
+ @process_variables = process_variables
33
+ @thread = Thread.new do
34
+ loop do
35
+ Log.debug3 'Sleeping in process monitoring.'
36
+ sleep(Params['monitoring_sleep_time_in_seconds'])
37
+ Log.debug3 'Awake in process monitoring.'
38
+ @passed_time_dur += Params['monitoring_sleep_time_in_seconds']
39
+ Log.debug3 'handle_logs in process monitoring.'
40
+ handle_logs()
41
+ Log.debug3 'handle_monitoring_state in process monitoring.'
42
+ handle_monitoring_state()
43
+ @process_variables.inc('monitoring_loop_count')
44
+ end
45
+ end
46
+ # For debug purposes.
47
+ @thread.abort_on_exception = true
48
+ end
49
+
50
+ private
51
+ # To keep thread safe state all methods should be private and executed only from @thread
52
+ def send_email(subject, body)
53
+ SendEmail.send_email({
54
+ :to => Params['gmail_username'],
55
+ :from => Params['gmail_username'],
56
+ :password => Params['gmail_password'],
57
+ :body => body,
58
+ :subject => subject,
59
+ })
60
+ end
61
+
62
+ # Override logs consumer handler.
63
+ def handle_logs
64
+ while log_msg = @consumer_queue.pop(non_block=true) do
65
+ send_log_email(log_msg) if Log.is_error(log_msg)
66
+ end rescue ThreadError # Rescue when queue is empty.
67
+ end
68
+
69
+ def handle_monitoring_state
70
+ if @passed_time_dur > Params['send_email_duration_4_monitoring_state']
71
+ send_email('BBFS Current Monitoring State notification', get_monitoring_state_body())
72
+ @passed_time_dur = 0
73
+ end
74
+ end
75
+
76
+ def get_monitoring_state_body
77
+ # TODO(slava): Implement the following function.
78
+ # MonitoringInfo::MonitoringInfo.get_html(@process_variables)
79
+ "server_name: #{@process_variables.get('server_name')}\n" +
80
+ "num_files_received: #{@process_variables.get('num_files_received')}\n" +
81
+ "monitoring_loop_count: #{@process_variables.get('monitoring_loop_count')}"
82
+ end
83
+ end
84
+ =end
85
+ end
@@ -0,0 +1,79 @@
1
+ require 'eventmachine'
2
+ require 'json'
3
+ require 'net/http'
4
+ require 'thin'
5
+ require 'sinatra'
6
+
7
+ require 'params'
8
+
9
+ # Set up event machine to exit on ctrl+c.
10
+ EventMachine.schedule do
11
+ trap("INT") do
12
+ puts "Caught SIGINT"
13
+ EventMachine.exit # this is useless
14
+ # exit # this stops the EventMachine
15
+ end
16
+ end
17
+
18
+ # This module export process info to http port, that way the user may access with the
19
+ # browser to the process to see what is happening inside, what is it's state and parameters.
20
+ module MonitoringInfo
21
+ Params.integer('process_monitoring_web_port', 5555,
22
+ 'The port from which monitoring data will be served as http.')
23
+
24
+ class MonitoringInfo
25
+ attr_reader :thread
26
+
27
+ def initialize(process_variables)
28
+ @process_variables = process_variables
29
+ @web_interface = Sinatra.new { get('/') { MonitoringInfo.get_json(process_variables.clone) } }
30
+ @web_interface.set(:port, Params['process_monitoring_web_port'])
31
+ @thread = Thread.new do
32
+ @web_interface.run!
33
+ end
34
+ end
35
+
36
+ def self.get_json(hash)
37
+ return '' if !hash.is_a?(Hash)
38
+
39
+ entries = []
40
+ hash.each do |key, value|
41
+ if value.is_a? String
42
+ entries << "\"#{key}\": \"#{value}\""
43
+ else
44
+ entries << "\"#{key}\": #{value}"
45
+ end
46
+ end
47
+
48
+ return '{' + entries.join(',') + '}'
49
+ end
50
+
51
+ def self.get_html (hash, opts = {})
52
+ return if !hash.is_a?(Hash)
53
+
54
+ indent_level = opts.fetch(:indent_level) { 0 }
55
+
56
+ out = " " * indent_level + "<ul>\n"
57
+
58
+ hash.each do |key, value|
59
+ out += " " * (indent_level + 2) + "<li><strong>#{key}:</strong>"
60
+
61
+ if value.is_a?(Hash)
62
+ out += "\n" + get_html(value, :indent_level => indent_level + 2) + " " * (indent_level + 2) + "</li>\n"
63
+ else
64
+ out += " <span>#{value}</span></li>\n"
65
+ end
66
+ end
67
+
68
+ out += " " * indent_level + "</ul>\n"
69
+ end
70
+
71
+ def self.get_remote_monitoring_info(host, port)
72
+ begin
73
+ JSON.parse(Net::HTTP.get(URI("http://#{host}:#{port}/")))
74
+ rescue Errno::ECONNREFUSED => e
75
+ ''
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,40 @@
1
+ require 'net/smtp'
2
+
3
+ require 'log'
4
+ require 'params'
5
+
6
+ module SendEmail
7
+ Params.boolean('enable_monitoring_emails', false, 'Whether to send emails for process monitoring events.')
8
+
9
+ #class SendEmail
10
+ def SendEmail.send_email(opts={})
11
+ opts[:to] ||= '' # Required.
12
+ opts[:from] ||= '' # Required.
13
+ opts[:from_alias] ||= 'BBFS Monitoring'
14
+ opts[:subject] ||= 'BBFS Notification subject'
15
+ opts[:body] ||= 'BBFS Body'
16
+ opts[:password] ||= '' # Required.
17
+
18
+ msg = "From: #{opts[:from_alias]} <#{opts[:from]}>\n" \
19
+ "To: <#{opts[:to]}>\n" \
20
+ "Subject: #{opts[:subject]}\n" \
21
+ "#{opts[:body]}"
22
+
23
+ email_log_message = "Send actual email: #{Params['enable_monitoring_emails']}.\n" \
24
+ "to: #{opts[:to]}\n" \
25
+ "msg:#{msg}"
26
+ if Params['enable_monitoring_emails']
27
+ Log.debug1(email_log_message)
28
+ smtp = Net::SMTP.new('smtp.gmail.com', 587)
29
+ smtp.enable_starttls
30
+ smtp.start('bbfs.com', opts[:from], opts[:password], :login) do
31
+ smtp.send_message(msg, opts[:from], opts[:to])
32
+ end
33
+ else
34
+ Log.info(email_log_message)
35
+ end
36
+ end
37
+ #end
38
+ end
39
+
40
+
@@ -0,0 +1,39 @@
1
+ # Simple thread safe hash.
2
+ module ThreadSafeHash
3
+ class ThreadSafeHash
4
+ def initialize()
5
+ @hash_data = Hash.new()
6
+ @mutex = Mutex.new
7
+ end
8
+
9
+ def inc(key)
10
+ @mutex.synchronize do
11
+ value = @hash_data[key]
12
+ if value.nil?
13
+ @hash_data[key] = 1
14
+ else
15
+ @hash_data[key] = value + 1
16
+ end
17
+ end
18
+ end
19
+
20
+ def get(key)
21
+ @mutex.synchronize do
22
+ @hash_data[key]
23
+ end
24
+ end
25
+
26
+ def set(key, value)
27
+ @mutex.synchronize do
28
+ @hash_data[key] = value
29
+ end
30
+ end
31
+
32
+ def clone
33
+ @mutex.synchronize do
34
+ @hash_data.clone
35
+ end
36
+ end
37
+ end
38
+ end
39
+
@@ -0,0 +1,3 @@
1
+ module ProcessMonitoring
2
+ VERSION = "1.0.0"
3
+ end
metadata ADDED
@@ -0,0 +1,148 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: process_monitoring
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Gena Petelko, Kolman Vornovitsky
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-05-01 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: eventmachine
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: json
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: log
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: params
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: sinatra
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :runtime
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: thin
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :runtime
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ description: Holds variable set which is regularly updated and export it to http protocol
111
+ email: bbfsdev@gmail.com
112
+ executables: []
113
+ extensions: []
114
+ extra_rdoc_files: []
115
+ files:
116
+ - lib/process_monitoring.rb
117
+ - lib/process_monitoring/thread_safe_hash.rb
118
+ - lib/process_monitoring/monitoring_info.rb
119
+ - lib/process_monitoring/send_email.rb
120
+ - lib/process_monitoring/monitoring.rb
121
+ - lib/process_monitoring/version.rb
122
+ - examples/process_monitoring.rb
123
+ homepage: http://github.com/bbfsdev/bbfs
124
+ licenses: []
125
+ post_install_message:
126
+ rdoc_options: []
127
+ require_paths:
128
+ - lib
129
+ required_ruby_version: !ruby/object:Gem::Requirement
130
+ none: false
131
+ requirements:
132
+ - - ! '>='
133
+ - !ruby/object:Gem::Version
134
+ version: '0'
135
+ required_rubygems_version: !ruby/object:Gem::Requirement
136
+ none: false
137
+ requirements:
138
+ - - ! '>='
139
+ - !ruby/object:Gem::Version
140
+ version: '0'
141
+ requirements: []
142
+ rubyforge_project:
143
+ rubygems_version: 1.8.24
144
+ signing_key:
145
+ specification_version: 3
146
+ summary: Library to add monitoring for processes.
147
+ test_files:
148
+ - examples/process_monitoring.rb