bloopletech-webstats 0.9.0 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,17 +1,16 @@
1
- Webstats
2
- ========
1
+ h1. Webstats
3
2
 
4
3
  Webstats is a server and clients that monitors your servers performance (CPU usage, memory usage, disk usage, disk activity, url load time) and allows you to get notifications on your computer when the server is having problems, as well as showing (http://kimag.es/share/91090734.png) the current performance stats for the server on a web page.
5
4
 
6
5
  Webstats has 2 components: the _server_ and one or more _clients_.
7
6
 
8
- Server
9
- ------
7
+ h2. Server
8
+
10
9
  The server runs on the computer whose performance you want to monitor; the server application's job is to (1) monitor the computer's performance and (2) report these performance statistics through a webserver that the server application runs.
11
10
 
12
11
  See Installation below for install instructions.
13
12
 
14
- The server application executes in the background and starts a tiny webserver on port 9970. You can view the performance stats for your server by going to http://<server's hostname>:9970/, for example if my server was under the domain name bloople.net, I would go to http://bloople.net:9970/. The statistics on this page automatically update every 5 seconds. You may need to open port 9970 if you have a firewall on your server. Note that the various clients get their data via the webserver the server application runs, just like the built-in stats page.
13
+ The server application executes in the background and starts a tiny webserver on port 9970. You can view the performance stats for your server by going to http://&lt;server's hostname&gt;:9970/, for example if my server was under the domain name bloople.net, I would go to http://bloople.net:9970/. The statistics on this page automatically update every 5 seconds. You may need to open port 9970 if you have a firewall on your server. Note that the various clients get their data via the webserver the server application runs, just like the built-in stats page.
15
14
 
16
15
  The statistic provided by the server application are:
17
16
 
@@ -31,48 +30,49 @@ The server application requires Linux kernel 2.6+, will not work in *BSD or OS X
31
30
 
32
31
  All the client applications depend on the server application being run to work.
33
32
 
34
- Clients
35
- -------
33
+ h2. Clients
34
+
36
35
  If all you want to do is be able to view the performance statistics for your server in a web browser, then you don't need to use any of these clients; going to http://<server's hostname>:9970/ will do just fine. But if you want to have Growl or email notification when something goes wrong, without having to look at a web page all the time, then you'll want one of the client applications that come with Webstats.
37
36
 
38
- Right now there's only one client application available, which is a Growl notifier. The Growl notifier only works on OS X, with RubyCocoa installed. This application runs in the background and monitors the server; if the server goes under high CPU load for more than a few seconds, or if the server is nearly out of memory, you'll get a Growl notification saying what the problem is. This way, you can just set and forget, knowing that Webstats will report any problems.
37
+ There are currently two client applications available:
39
38
 
40
- To run the Growl notifier, first install Webstats on your local machine, using the installation instructions below (Skip the step that starts the server application). Then, if you installed webstats locally via rubygems, run:
41
- ~$ webstats_growl_notifier
39
+ * An email notifier; documentation is available in clients/email_notifier/README.textile
40
+ * A Growl notifier; documentation is available in clients/growl_notifier/README.textile
42
41
 
43
- Otherwise, if you installed webstats manually, run:
44
- ~$ cd webstats/clients/growl_notifier
45
- ~/webstats/clients/growl_notifier$ ruby growl_notifier.rb
42
+ The notifiers that ship with Webstats can be started in 2 ways; if you have installed webstats via rubygems, run:
43
+ <pre><code>~$ webstats_<notifier_name></code></pre>
46
44
 
47
- The first time you run the growl notifier, it will create a configuration file at ~/.webstats_clients; you must edit this file to set the URL's for the growl notifier to monitor. The URL's should the the hostnames of the servers you want to monitor, along with the correct port number (e.g. http://bloople.net:9970/).
45
+ For example, to run the email notifier, you can run webstats_email_notifier. If you installed webstats outside of rubygems, first change directory into the directory containing webstats, then run:
46
+ <pre><code>~/webstats$ ruby clients/<notifier_name>/<notifier_name>.rb</code></pre>
48
47
 
49
- Once you've edited the configuration file to add your URL's, run the notifier again; the Growl notifier will then run in the background and notify you od any warnings or danger situations on the server.
48
+ Many notifiers can also be run on the server, in the same ruby process as teh webstats server itself. To do this, change any settings as required by the documentation for the notifier, then add the notifier name (e.g. email_notifier) to the list of clients under webstats > clients in the ~/.webstats file
50
49
 
51
- Installation
52
- ------------
50
+ h2. Installation
53
51
 
54
52
  You can now install webstats using rubygems; this saves you a bit of hassle. To install webstats on the server via rubygems, ensure github gems are in your gems sources; then run:
55
- ~$ sudo gem install bloopletech-webstats
53
+ <pre><code>~$ sudo gem install bloopletech-webstats</code></pre>
56
54
 
57
55
  To run the server application, run:
58
- ~$ webstats
56
+ <pre><code>~$ webstats</code></pre>
59
57
 
60
58
  The server application will start in the background.
61
59
 
62
60
  If you don't want to use rubygems, then follow the below instructions:
63
61
  To install the server app, ssh into the computer you want to monitor. then run:
64
- ~$ git clone git://github.com/bloopletech/webstats.git
62
+ <pre><code>~$ git clone git://github.com/bloopletech/webstats.git
65
63
  ~$ cd webstats
66
64
  ~/webstats$ cd server/data_providers
67
65
  ~/webstats/server/data_providers$ ruby extconf.rb
68
66
  ~/webstats/server/data_providers$ make
67
+ </code></pre>
69
68
 
70
69
  To run the server app, ssh into the computer you want to monitor. then run:
71
- ~$ cd webstats
70
+ <pre><code>~$ cd webstats
72
71
  ~/webstats$ ruby server/webstats.rb
72
+ </pre></code>
73
+
74
+ h2. Todo
73
75
 
74
- Todo
75
- ----
76
76
  * Add email notifier to client applications.
77
77
  * More client applications.
78
78
  * Extend server to work on *BSD (including OS X).
data/VERSION.yml CHANGED
@@ -1,4 +1,4 @@
1
1
  ---
2
2
  :major: 0
3
- :minor: 9
4
- :patch: 0
3
+ :minor: 10
4
+ :patch: 1
@@ -0,0 +1,13 @@
1
+ h1. Email notifier for Webstats
2
+
3
+ The email notifier montiros one or more servers and emails you whenever the server is in danger or could be in danger soon - for example, running out of hard drive space.
4
+
5
+ h2. Usage
6
+
7
+ Run the notifier by running webstats_email_notifier or ruby <webstats_install_dir>/clients/email_notifier/email_notifier.rb on the terminal.
8
+
9
+ It'll create a template of the settings you'll need to change in ~/.webstats_client - you'll need to set the recipient address for the emails, you may need to change the mail server settings - you can supply these settings: address, port, domain, username, password, authentication (one of :plain, :login, or :cram_md5).
10
+
11
+ You'll also need to add some URL's for the email notifier to monitor - these are the URL's who statistics will be monitored. You will be notified when there is a warning or danger situation for a URL, as well as if one of the URL's can not be loaded. The URL's should the the hostnames of the servers you want to monitor, along with the correct port number (e.g. http://bloople.net:9970/).
12
+
13
+ Once that's done, run the notifier again and it will begin monitoring.
@@ -1,3 +1,163 @@
1
+ require File.dirname(__FILE__) + '/../simple_notifier'
2
+ daemonize!
3
+
4
+ class EmailNotifier < SimpleNotifier
5
+ def initialize(settings = {}, read_config = true)
6
+ @name = 'email_notifier'
7
+ @no_settings_message = "Please edit ~/.webstats_clients and add some URLs to monitor and an email address to notfiy on"
8
+
9
+ super({ 'recipient' => '', 'mail_server' => { 'address' => 'localhost', 'domain' => 'localhost', 'port' => 25 } }.merge(settings), read_config)
10
+ end
11
+
12
+ private
13
+ def send_mail(subject, message)
14
+ sm = @settings[:mail_server]
15
+
16
+ msg = <<END_OF_MESSAGE
17
+ From: Webstats Email Notifier <#{@settings[:recipient]}>
18
+ To: #{@settings[:recipient]} <#{@settings[:recipient]}>
19
+ Subject: #{subject}
20
+ Date: #{Time.now.rfc2822}
21
+ Message-Id: <#{Time.now.to_i}.#{rand(10000000)}@#{sm[:domain]}>
22
+
23
+ #{message}
24
+ END_OF_MESSAGE
25
+
26
+ Net::SMTP.start(sm[:address], sm[:port], sm[:domain], sm[:username], sm[:password], sm[:authentication]) { |smtp| smtp.send_message msg, @settings[:recipient], @settings[:recipient] }
27
+ end
28
+
29
+ def failed_url(url, password, exception)
30
+ send_mail("Webstats Notification - Cannot load Webstats data", "Could not load #{url}#{!password.nil? ? " with password #{password}" : ""}, error was #{exception.message}. Will try again in 60 seconds.")
31
+ sleep(60)
32
+ true
33
+ end
34
+
35
+ def notify
36
+ messages = []
37
+ has_warnings = has_dangers = false
38
+
39
+ @settings[:urls].each do |url|
40
+ if !url[:bad].empty? and (url[:changed] or url[:time_past])
41
+ has_warnings = !url[:warnings].empty?
42
+ has_dangers = !url[:dangers].empty?
43
+
44
+ title = []
45
+ title << "Danger" if has_dangers
46
+ title << "Warnings" if has_warnings
47
+ title = title.join(" & ")
48
+
49
+ warnings_text = !url[:warnings].empty? ? "Warnings for #{url[:bad].select { |(k, v)| v['status'] == 'warning' }.map { |(k, v)| url[:meta_info][k]['in_sentence'] }.join(", ")}." : nil
50
+ danger_text = !url[:dangers].empty? ? "Dangerous situation for #{url[:bad].select { |(k, v)| v['status'] == 'danger' }.map { |(k, v)| url[:meta_info][k]['in_sentence'] }.join(", ")}." : nil
51
+
52
+ host = URI.parse(url[:url]).host
53
+ messages << "#{title} for #{host}\n#{"-" * (title.length + host.length + 5)}\n\n#{[danger_text, warnings_text].compact.join("\n")}\n\nCheck statistics online at #{url[:url]}"
54
+ end
55
+ end
56
+
57
+ title = []
58
+ title << "Danger" if has_dangers
59
+ title << "Warnings" if has_warnings
60
+
61
+ send_mail("Webstats Notification - #{title.join(" & ")}", messages.join("\n\n\n")) unless messages.empty?
62
+ end
63
+ end
64
+
65
+ s = EmailNotifier.new()
66
+ s.start
67
+
68
+ =begin
69
+
70
+
71
+
72
+
73
+
74
+
75
+ require File.dirname(__FILE__) + '/../common'
76
+ prepare_client
77
+
78
+ load_settings('email_notifier', { 'email_addresses' => ['example@example.com'] }] }, "Please edit ~/.webstats_clients and add some email addresss to send to")
79
+
80
+ urls = $settings[:urls]
81
+ emails = $settings[:emails]
82
+
83
+ failed_url = lambda do |url, password, exception|
84
+ g.notify "Webstats Notification", "Cannot load Webstats data", "Could not load #{url}#{!password.nil? ? " with password #{password}" : ""}, error was #{exception.message}. Will try again in 60 seconds."
85
+ sleep(60)
86
+ true
87
+ end
88
+
89
+ urls.each do |url|
90
+ url.merge!({ :meta_info => make_request(URI.join(url[:url], "information"), url[:password], failed_url), :last_warnings_text => nil, :last_danger_text => nil, :last_time => 0 })
91
+ end
92
+
93
+ while(true)
94
+ urls.each do |url|
95
+ data = make_request(URI.join(url[:url], "update"), url[:password], failed_url)
96
+
97
+ bad = data.sort { |a, b| b[1]['importance'].to_f <=> a[1]['importance'].to_f }.select { |(k, v)| !v['status'].nil? && v['status'] != '' }
98
+
99
+ has_warnings = bad.detect { |(k, v)| v['status'] == 'warning' }
100
+ has_dangers = bad.detect { |(k, v)| v['status'] == 'danger' }
101
+
102
+ title = []
103
+ title << "Danger" if has_dangers
104
+ title << "Warnings" if has_warnings
105
+ title = title.join(" & ") + " for host #{URI.parse(url[:url]).host}"
106
+
107
+ warnings_text = has_warnings ? "Warnings for #{bad.select { |(k, v)| v['status'] == 'warning' }.map { |(k, v)| url[:meta_info][k]['in_sentence'] }.join(", ")}." : nil
108
+ danger_text = has_dangers ? "Dangerous situation for #{bad.select { |(k, v)| v['status'] == 'danger' }.map { |(k, v)| url[:meta_info][k]['in_sentence'] }.join(", ")}." : nil
109
+
110
+ if url[:last_warnings_text] != warnings_text or url[:last_danger_text] != danger_text or (url[:last_time] != 0 and (Time.now - url[:last_time]) > 60)
111
+ url[:last_warnings_text] = warnings_text
112
+ url[:last_danger_text] = danger_text
113
+ url[:last_time] = Time.now
114
+
115
+ unless bad.empty?
116
+ g.notify "Webstats Notification", title, [danger_text, warnings_text].compact.join(" "), nil, nil, true, (has_dangers ? 2 : 1)
117
+ end
118
+ end
119
+ end
120
+
121
+ sleep(10)
122
+ end
123
+
124
+
125
+
126
+
127
+
128
+
129
+
130
+
131
+
132
+
133
+
134
+
135
+
136
+
137
+
138
+
139
+
140
+
141
+
142
+
143
+
144
+
145
+
146
+
147
+
148
+
149
+
150
+
151
+
152
+
153
+
154
+
155
+
156
+
157
+
158
+
159
+
160
+
1
161
  require 'rubygems'
2
162
  require 'net/smtp'
3
163
  require 'net/dns/resolver'
@@ -26,4 +186,5 @@ END_OF_MESSAGE
26
186
  Net::SMTP.start(mxrs.first.exchange, 25, domain) { |smtp| smtp.send_message msg, recipient, recipient }
27
187
  end
28
188
 
29
- send_mail_hardcore(*ARGV)
189
+ send_mail_hardcore(*ARGV)
190
+ =end
@@ -0,0 +1,13 @@
1
+ h1. Growl notifer for Webstats
2
+
3
+ The Growl notifier runs in the background and monitors the server; if the server goes under high CPU load for more than a few seconds, or if the server is nearly out of memory, you'll get a Growl notification saying what the problem is. This way, you can just set and forget, knowing that Webstats will report any problems.
4
+
5
+ Thy works on OS X only, with RubyCocoa installed.
6
+
7
+ .h2 Usage
8
+
9
+ Run the notifier by running webstats_growl_notifier or ruby <webstats_install_dir>/clients/growl_notifier/growl_notifier.rb on the terminal.
10
+
11
+ The first time you run the growl notifier, it will create a template of the settings you need to edit at ~/.webstats_clients; you must edit this file to set the URL's for the growl notifier to monitor. The URL's should the the hostnames of the servers you want to monitor, along with the correct port number (e.g. http://bloople.net:9970/).
12
+
13
+ Once you've edited the configuration file to add your URL's, run the notifier again; the Growl notifier will then run in the background and notify you od any warnings or danger situations on the server.
@@ -1,62 +1,41 @@
1
- if true or $DEBUG
2
- Thread.abort_on_exception
3
- else
4
- exit if fork
5
- $stdout = File.new('/dev/null', 'w')
6
- $stderr = File.new('/dev/null', 'w')
7
- end
8
-
9
- require 'rubygems'
10
- require 'json'
11
- require 'net/http'
12
- require 'uri'
1
+ require File.dirname(__FILE__) + '/../simple_notifier'
2
+ daemonize!
13
3
  require File.dirname(__FILE__) + '/Growl.rb'
14
4
 
15
- require File.dirname(__FILE__) + '/../common'
16
- load_settings('growl_notifier', { 'urls' => [{ 'url' => '', 'password' => nil }] }, "Please edit ~/.webstats_clients and add some URLs to monitor")
17
-
18
- urls = $settings[:urls]
19
-
20
- g = GrowlNotifier.new("Webstats", ['Webstats Notification'], nil, OSX::NSWorkspace.sharedWorkspace().iconForFileType_('unknown'))
21
- g.register
22
-
23
- failed_url = lambda do |url, password, exception|
24
- g.notify "Webstats Notification", "Cannot load Webstats data", "Could not load #{url}#{!password.nil? ? " with password #{password}" : ""}, error was #{exception.message}. Will try again in 60 seconds."
25
- sleep(60)
26
- true
27
- end
28
-
29
- urls.each do |url|
30
- url.merge!({ :meta_info => make_request(URI.join(url[:url], "information"), url[:password], failed_url), :last_warnings_text => nil, :last_danger_text => nil, :last_time => 0 })
31
- end
5
+ class WGrowlNotifier < SimpleNotifier
6
+ def initialize(settings = {}, read_config = true)
7
+ @name = "growl_notifier"
8
+ @no_settings_message = "Please edit ~/.webstats_clients and add some URLs to monitor"
32
9
 
33
- while(true)
34
- urls.each do |url|
35
- data = make_request(URI.join(url[:url], "update"), url[:password], failed_url)
36
-
37
- bad = data.sort { |a, b| b[1]['importance'].to_f <=> a[1]['importance'].to_f }.select { |(k, v)| !v['status'].nil? && v['status'] != '' }
38
-
39
- has_warnings = bad.detect { |(k, v)| v['status'] == 'warning' }
40
- has_dangers = bad.detect { |(k, v)| v['status'] == 'danger' }
41
-
42
- title = []
43
- title << "Danger" if has_dangers
44
- title << "Warnings" if has_warnings
45
- title = title.join(" & ") + " for host #{URI.parse(url[:url]).host}"
10
+ @g = GrowlNotifier.new("Webstats", ['Webstats Notification'], nil, OSX::NSWorkspace.sharedWorkspace().iconForFileType_('unknown'))
11
+ @g.register
12
+
13
+ super(settings, read_config)
14
+ end
46
15
 
47
- warnings_text = has_warnings ? "Warnings for #{bad.select { |(k, v)| v['status'] == 'warning' }.map { |(k, v)| url[:meta_info][k]['in_sentence'] }.join(", ")}." : nil
48
- danger_text = has_dangers ? "Dangerous situation for #{bad.select { |(k, v)| v['status'] == 'danger' }.map { |(k, v)| url[:meta_info][k]['in_sentence'] }.join(", ")}." : nil
16
+ private
17
+ def failed_url(url, password, exception)
18
+ @g.notify "Webstats Notification", "Cannot load Webstats data", "Could not load #{url}#{!password.nil? ? " with password #{password}" : ""}, error was #{exception.message}. Will try again in 60 seconds."
19
+ sleep(60)
20
+ true
21
+ end
22
+
23
+ def notify
24
+ @settings[:urls].each do |url|
25
+ if !url[:bad].empty? and (url[:changed] or url[:time_past])
26
+ title = []
27
+ title << "Danger" unless url[:dangers].empty?
28
+ title << "Warnings" unless url[:warnings].empty?
29
+ title = title.join(" & ") + " for host #{URI.parse(url[:url]).host}"
49
30
 
50
- if url[:last_warnings_text] != warnings_text or url[:last_danger_text] != danger_text or (url[:last_time] != 0 and (Time.now - url[:last_time]) > 60)
51
- url[:last_warnings_text] = warnings_text
52
- url[:last_danger_text] = danger_text
53
- url[:last_time] = Time.now
31
+ warnings_text = !url[:warnings].empty? ? "Warnings for #{url[:bad].select { |(k, v)| v['status'] == 'warning' }.map { |(k, v)| url[:meta_info][k]['in_sentence'] }.join(", ")}." : nil
32
+ danger_text = !url[:dangers].empty? ? "Dangerous situation for #{url[:bad].select { |(k, v)| v['status'] == 'danger' }.map { |(k, v)| url[:meta_info][k]['in_sentence'] }.join(", ")}." : nil
54
33
 
55
- unless bad.empty?
56
- g.notify "Webstats Notification", title, [danger_text, warnings_text].compact.join(" "), nil, nil, true, (has_dangers ? 2 : 1)
57
- end
58
- end
34
+ @g.notify "Webstats Notification", title, [danger_text, warnings_text].compact.join(" "), nil, nil, true, (!url[:dangers].empty? ? 2 : 1)
35
+ end
36
+ end
59
37
  end
38
+ end
60
39
 
61
- sleep(10)
62
- end
40
+ s = WGrowlNotifier.new()
41
+ s.start
@@ -0,0 +1,106 @@
1
+ require 'yaml'
2
+ require 'net/http'
3
+ require 'uri'
4
+ require 'thread'
5
+ require 'time'
6
+ require 'net/smtp'
7
+
8
+ def daemonize!
9
+ return if $DAEMONIZE == false
10
+ if $DEBUG
11
+ Thread.abort_on_exception
12
+ else
13
+ if (pid = fork)
14
+ Signal.trap('HUP', 'IGNORE')
15
+ Process.detach(pid)
16
+ exit
17
+ end
18
+ end
19
+ end
20
+
21
+ class Hash
22
+ alias_method :undecorated_get, :[] unless method_defined?(:undecorated_get)
23
+ def [](key)
24
+ undecorated_get(key) or undecorated_get(key.is_a?(String) ? key.to_sym : key.to_s)
25
+ end
26
+ end
27
+
28
+ class SimpleNotifier
29
+ def initialize(settings = {}, read_config = true)
30
+ @name ||= "notifier"
31
+ @default_settings ||= {}
32
+
33
+ if read_config
34
+ config_file_path = File.expand_path("~/.webstats_clients")
35
+
36
+ @settings = {}
37
+
38
+ if File.exists?(config_file_path)
39
+ @settings = YAML.load(IO.read(config_file_path))
40
+ @settings ||= {}
41
+ end
42
+
43
+ unless @settings.key?(@name)
44
+ @settings[@name] = { 'urls' => [{ 'url' => 'http://localhost:9970/', 'password' => nil }] }.merge(@default_settings).merge(settings)
45
+ File.open(config_file_path, "w") { |f| YAML.dump(@settings, f) }
46
+
47
+ puts @no_settings_message
48
+ exit
49
+ end
50
+
51
+ @settings = @settings[@name]
52
+ else
53
+ @settings = settings
54
+ end
55
+ end
56
+
57
+ def start
58
+ @settings[:urls].each do |url|
59
+ url[:mutex] = Mutex.new
60
+ Thread.new do
61
+ url[:mutex].synchronize { url.merge!({ :meta_info => make_request(URI.join(url[:url], "information"), url[:password]), :last_time => 0 }) }
62
+ end
63
+ end
64
+
65
+ while(true)
66
+ threads = []
67
+ @settings[:urls].each do |url|
68
+ threads << Thread.new do
69
+ url[:mutex].synchronize do
70
+ url[:data] = make_request(URI.join(url[:url], "update"), url[:password])
71
+ url[:bad] = url[:data].sort { |a, b| b[1]['importance'].to_f <=> a[1]['importance'].to_f }.select { |(k, v)| !v['status'].nil? && v['status'] != '' }
72
+ url[:last_warnings] = url[:warnings] || []
73
+ url[:warnings] = url[:bad].select { |(k, v)| v['status'] == 'warning' }
74
+ url[:has_warnings]= !url[:warnings].empty?
75
+ url[:last_dangers] = url[:dangers] || []
76
+ url[:dangers] = url[:bad].select { |(k, v)| v['status'] == 'danger' }
77
+ url[:has_dangers] = !url[:dangers].empty?
78
+ url[:changed] = (!url[:warnings].empty? || !url[:dangers].empty?) && (!url.key?(:changed) or (url[:warnings].length > url[:last_warnings].length) or (url[:dangers].length > url[:last_dangers].length))
79
+ url[:time_past] = url[:last_time] != 0 && (Time.now - url[:last_time]) > 60
80
+ url[:last_time] = Time.now if url[:changed] || url[:last_time].nil? || url[:time_past]
81
+ end
82
+ end
83
+ end
84
+ threads.each { |t| t.join }
85
+ notify
86
+ sleep(10)
87
+ end
88
+ end
89
+
90
+ private
91
+ def make_request(url, password)
92
+ while(true)
93
+ begin
94
+ Net::HTTP.start(url.host, url.port) { |http|
95
+ http.read_timeout = http.open_timeout = 15
96
+ req = Net::HTTP::Get.new(url.request_uri)
97
+ req.basic_auth 'webstats', password unless password.nil?
98
+ return YAML.load(http.request(req).body)
99
+ }
100
+ rescue Exception => e
101
+ return nil unless failed_url(url, password, e)
102
+ end
103
+ end
104
+ end
105
+ end
106
+
data/server/webstats.rb CHANGED
@@ -1,3 +1,4 @@
1
+ #NO GEM DEPENDENCIES FTW
1
2
  require 'webrick'
2
3
  require 'yaml'
3
4
 
@@ -9,8 +10,6 @@ else
9
10
  Process.detach(pid)
10
11
  exit
11
12
  end
12
- $stdout = File.new('/dev/null', 'w')
13
- $stderr = File.new('/dev/null', 'w')
14
13
  end
15
14
 
16
15
  Thread.new do
@@ -73,7 +72,7 @@ class Array
73
72
  end
74
73
 
75
74
  def to_json
76
- "[#{map { |e| e.to_json }.join(',')}]"
75
+ "[#{map { |e| e.to_json }.join(', ')}]"
77
76
  end
78
77
  end
79
78
 
@@ -98,15 +97,15 @@ class Hash
98
97
  each_pair { |k, v| self[k] = self[k].dup.stringify_keys! if v.is_a? Hash }
99
98
  end
100
99
 
101
- alias_method :undecorated_get, :[]
100
+ alias_method :undecorated_get, :[] unless method_defined?(:undecorated_get)
102
101
  def [](key)
103
102
  undecorated_get(key) or undecorated_get(key.is_a?(String) ? key.to_sym : key.to_s)
104
103
  end
105
104
 
106
105
  def to_json
107
106
  arr = []
108
- each_pair { |k, v| arr << "#{k.to_json}:#{v.to_json}" }
109
- "{#{arr.join(',')}}"
107
+ each_pair { |k, v| arr << "#{k.to_json}: #{v.to_json}" }
108
+ "{#{arr.join(', ')}}"
110
109
  end
111
110
  end
112
111
 
@@ -140,7 +139,7 @@ $settings = {}
140
139
  if File.exists?(WEBSTATS_PATH)
141
140
  $settings = YAML.load(IO.read(WEBSTATS_PATH)).symbolize_keys!
142
141
  else
143
- $settings['webstats'] = { 'password' => nil }
142
+ $settings['webstats'] = { 'password' => nil, 'clients' => [] }
144
143
  DataProviders::DATA_SOURCES_CLASSES.each_pair { |k, v| $settings[k.to_s] = v.default_settings.stringify_keys! }
145
144
  File.open(WEBSTATS_PATH, "w") { |f| YAML.dump($settings, f) }
146
145
  end
@@ -198,7 +197,6 @@ class Webstats < WEBrick::HTTPServlet::AbstractServlet
198
197
  {
199
198
  var results = eval("(" + http.responseText + ")");
200
199
  if(!results) return;
201
- console.log('procing');
202
200
  EOF
203
201
 
204
202
  DataProviders::DATA_SOURCES.each_pair do |k, v|
@@ -248,14 +246,24 @@ EOF
248
246
  end
249
247
  end
250
248
 
251
- s = WEBrick::HTTPServer.new(:Port => 9970)
249
+ threads = []
250
+
251
+ s = WEBrick::HTTPServer.new(:Port => 9970, :Logger => WEBrick::Log.new(nil, 0), :AccessLog => WEBrick::Log.new(nil, 0))
252
252
 
253
253
  death = proc do
254
254
  s.shutdown
255
255
  DataProviders::DATA_SOURCES.each_pair { |k, v| v.kill }
256
+ threads.each { |t| t.kill }
256
257
  end
257
258
  trap("INT", death)
258
259
  trap("TERM", death)
259
260
 
260
261
  s.mount("/", Webstats)
261
- s.start
262
+ threads << Thread.new { s.start }
263
+
264
+ if $settings['webstats'].key?(:clients) and !$settings['webstats'][:clients].nil?
265
+ $DAEMONIZE = false
266
+ $settings['webstats'][:clients].each { |name| threads << Thread.new { require "#{File.dirname(__FILE__)}/../clients/#{name}/#{name}.rb" } }
267
+ end
268
+
269
+ threads.first.join
data/webstats.gemspec CHANGED
@@ -2,30 +2,32 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{webstats}
5
- s.version = "0.9.0"
5
+ s.version = "0.10.0"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["Brenton Fletcher"]
9
- s.date = %q{2009-05-06}
9
+ s.date = %q{2009-05-14}
10
10
  s.description = %q{Monitor server CPU/Memory/Disk Usage/URL Loading, so that you can view those statistics on a web page, as well as providing an interface to client prorams to read those statistics.}
11
11
  s.email = %q{i@bloople.net}
12
12
  s.executables = ["webstats", "webstats_growl_notifier"]
13
13
  s.extensions = ["server/data_providers/extconf.rb"]
14
14
  s.extra_rdoc_files = [
15
15
  "LICENSE",
16
- "README"
16
+ "README.textile"
17
17
  ]
18
18
  s.files = [
19
19
  "LICENSE",
20
- "README",
20
+ "README.textile",
21
21
  "Rakefile",
22
22
  "VERSION.yml",
23
23
  "bin/webstats",
24
24
  "bin/webstats_growl_notifier",
25
- "clients/common.rb",
25
+ "clients/email_notifier/README.textile",
26
26
  "clients/email_notifier/email_notifier.rb",
27
27
  "clients/growl_notifier/Growl.rb",
28
+ "clients/growl_notifier/README.textile",
28
29
  "clients/growl_notifier/growl_notifier.rb",
30
+ "clients/simple_notifier.rb",
29
31
  "server/data_providers/cpu_info.rb",
30
32
  "server/data_providers/disk_activity.rb",
31
33
  "server/data_providers/disk_usage.c",
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bloopletech-webstats
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.0
4
+ version: 0.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brenton Fletcher
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-05-06 00:00:00 -07:00
12
+ date: 2009-05-14 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -22,18 +22,20 @@ extensions:
22
22
  - server/data_providers/extconf.rb
23
23
  extra_rdoc_files:
24
24
  - LICENSE
25
- - README
25
+ - README.textile
26
26
  files:
27
27
  - LICENSE
28
- - README
28
+ - README.textile
29
29
  - Rakefile
30
30
  - VERSION.yml
31
31
  - bin/webstats
32
32
  - bin/webstats_growl_notifier
33
- - clients/common.rb
33
+ - clients/email_notifier/README.textile
34
34
  - clients/email_notifier/email_notifier.rb
35
35
  - clients/growl_notifier/Growl.rb
36
+ - clients/growl_notifier/README.textile
36
37
  - clients/growl_notifier/growl_notifier.rb
38
+ - clients/simple_notifier.rb
37
39
  - server/data_providers/cpu_info.rb
38
40
  - server/data_providers/disk_activity.rb
39
41
  - server/data_providers/disk_usage.c
data/clients/common.rb DELETED
@@ -1,42 +0,0 @@
1
- class Hash
2
- alias_method :undecorated_get, :[]
3
- def [](key)
4
- undecorated_get(key) or undecorated_get(key.is_a?(String) ? key.to_sym : key.to_s)
5
- end
6
- end
7
-
8
- def load_settings(client_key, defaults, message)
9
- config_file_path = File.expand_path("~/.webstats_clients")
10
-
11
- $settings = {}
12
-
13
- if File.exists?(config_file_path)
14
- $settings = YAML.load(IO.read(config_file_path))[client_key]
15
- else
16
- $settings[client_key] = defaults
17
-
18
- File.open(config_file_path, "w") do |f|
19
- YAML.dump($settings, f)
20
- end
21
-
22
- puts message
23
- exit
24
- end
25
- end
26
-
27
- def make_request(url, password, failed_proc)
28
- while(true)
29
- begin
30
- Net::HTTP.start(url.host, url.port) { |http|
31
- http.read_timeout = http.open_timeout = 15
32
- puts url.request_uri
33
- req = Net::HTTP::Get.new(url.request_uri)
34
- req.basic_auth 'webstats', password unless password.nil?
35
- return JSON.parse(http.request(req).body)
36
- }
37
- rescue Exception => e
38
- return nil unless failed_proc.call(url, password, e)
39
- end
40
- end
41
- end
42
-