bloopletech-webstats 0.3.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README +8 -6
- data/Rakefile +2 -2
- data/VERSION.yml +2 -2
- data/clients/common.rb +26 -0
- data/clients/growl_notifier/growl_notifier.rb +40 -23
- data/server/webstats.rb +44 -50
- data/webstats.gemspec +4 -3
- metadata +4 -3
data/README
CHANGED
@@ -37,12 +37,16 @@ If all you want to do is be able to view the performance statistics for your ser
|
|
37
37
|
|
38
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.
|
39
39
|
|
40
|
-
To run the Growl notifier,
|
41
|
-
~$
|
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
|
42
|
+
|
43
|
+
Otherwise, if you installed webstats manually, run:
|
42
44
|
~$ cd webstats/clients/growl_notifier
|
43
|
-
~/webstats/clients/growl_notifier$ ruby growl_notifier.rb
|
45
|
+
~/webstats/clients/growl_notifier$ ruby growl_notifier.rb
|
44
46
|
|
45
|
-
|
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/).
|
48
|
+
|
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.
|
46
50
|
|
47
51
|
Installation
|
48
52
|
------------
|
@@ -55,8 +59,6 @@ To run the server application, run:
|
|
55
59
|
|
56
60
|
The server application will start in the background.
|
57
61
|
|
58
|
-
Coming soon: growl notifier client via rubygems.
|
59
|
-
|
60
62
|
If you don't want to use rubygems, then follow the below instructions:
|
61
63
|
To install the server app, ssh into the computer you want to monitor. then run:
|
62
64
|
~$ git clone git://github.com/bloopletech/webstats.git
|
data/Rakefile
CHANGED
@@ -9,9 +9,9 @@ begin
|
|
9
9
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
10
10
|
s.authors = ["Brenton Fletcher"]
|
11
11
|
s.date = Date.today.strftime("%Y-%m-%d")
|
12
|
-
s.description = s.summary = %q{
|
12
|
+
s.description = s.summary = %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.}
|
13
13
|
s.email = %q{i@bloople.net}
|
14
|
-
s.files = Dir['**/*'].reject { |fn| fn =~ /(\.o|\.so|Makefile|\.gem)$/ }
|
14
|
+
s.files = Dir['**/*'].reject { |fn| fn =~ /(\.o|\.so|\.bundle|Makefile|\.gem)$/ }
|
15
15
|
s.executables = ['webstats', 'webstats_growl_notifier']
|
16
16
|
s.extensions = ["server/data_providers/extconf.rb"]
|
17
17
|
s.has_rdoc = false
|
data/VERSION.yml
CHANGED
data/clients/common.rb
ADDED
@@ -0,0 +1,26 @@
|
|
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(defaults_key, defaults)
|
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))
|
15
|
+
else
|
16
|
+
$settings[defaults_key] = defaults
|
17
|
+
|
18
|
+
File.open(config_file_path, "w") do |f|
|
19
|
+
YAML.dump($settings, f)
|
20
|
+
end
|
21
|
+
|
22
|
+
puts "Please edit ~/.webstats_clients and add some URLs to monitor"
|
23
|
+
exit
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
@@ -1,43 +1,60 @@
|
|
1
|
-
|
1
|
+
if $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
|
2
8
|
|
3
9
|
require 'rubygems'
|
4
10
|
require 'json'
|
5
11
|
require 'net/http'
|
6
12
|
require 'uri'
|
7
|
-
require 'Growl'
|
13
|
+
require File.dirname(__FILE__) + '/Growl.rb'
|
8
14
|
|
9
|
-
|
15
|
+
require File.dirname(__FILE__) + '/../common'
|
16
|
+
load_settings('growl_notifier', { 'urls' => [{ 'url' => '', 'password' => nil }] })
|
10
17
|
|
11
|
-
|
18
|
+
def make_request(url, password)
|
19
|
+
req = Net::HTTP::Get.new(url.request_uri)
|
20
|
+
req.basic_auth 'webstats', password unless password.nil?
|
21
|
+
JSON.parse(Net::HTTP.new(url.host, url.port).start { |http| http.request(req).body })
|
22
|
+
end
|
23
|
+
|
24
|
+
urls = $settings[:growl_notifier][:urls]
|
25
|
+
|
26
|
+
g = GrowlNotifier.new("Webstats", ['Webstats Notification'], nil, OSX::NSWorkspace.sharedWorkspace().iconForFileType_('unknown'))
|
12
27
|
g.register
|
13
28
|
|
14
|
-
|
15
|
-
last_warnings_text
|
16
|
-
|
29
|
+
urls.each do |url|
|
30
|
+
url.merge!({ :meta_info => make_request(URI.join(url[:url], "information"), url[:password]), :last_warnings_text => nil, :last_danger_text => nil, :last_time => 0 })
|
31
|
+
end
|
17
32
|
|
18
33
|
while(true)
|
19
|
-
|
34
|
+
urls.each do |url|
|
35
|
+
data = make_request(URI.join(url[:url], "update"), url[:password])
|
20
36
|
|
21
|
-
|
37
|
+
bad = data.sort { |a, b| b[1]['importance'].to_f <=> a[1]['importance'].to_f }.select { |(k, v)| !v['status'].nil? && v['status'] != '' }
|
22
38
|
|
23
|
-
|
24
|
-
|
39
|
+
has_warnings = bad.detect { |(k, v)| v['status'] == 'warning' }
|
40
|
+
has_dangers = bad.detect { |(k, v)| v['status'] == 'danger' }
|
25
41
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
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}"
|
30
46
|
|
31
|
-
|
32
|
-
|
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
|
33
49
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
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
|
38
54
|
|
39
|
-
|
40
|
-
|
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
|
41
58
|
end
|
42
59
|
end
|
43
60
|
|
data/server/webstats.rb
CHANGED
@@ -17,21 +17,15 @@ Thread.new do
|
|
17
17
|
end
|
18
18
|
|
19
19
|
class NilClass
|
20
|
-
def to_json
|
21
|
-
"null"
|
22
|
-
end
|
20
|
+
def to_json; "null"; end
|
23
21
|
end
|
24
22
|
|
25
23
|
class TrueClass
|
26
|
-
def to_json
|
27
|
-
"true"
|
28
|
-
end
|
24
|
+
def to_json; "true"; end
|
29
25
|
end
|
30
26
|
|
31
27
|
class FalseClass
|
32
|
-
def to_json
|
33
|
-
"false"
|
34
|
-
end
|
28
|
+
def to_json; "false"; end
|
35
29
|
end
|
36
30
|
|
37
31
|
class String
|
@@ -56,17 +50,46 @@ class Numeric
|
|
56
50
|
end
|
57
51
|
|
58
52
|
class Array
|
53
|
+
def formatted!
|
54
|
+
each_with_index do |v, i|
|
55
|
+
if v.is_a? Numeric
|
56
|
+
self[i] = v.formatted
|
57
|
+
elsif v.is_a? Hash or v.is_a? Array
|
58
|
+
self[i] = self[i].dup.formatted!
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def stringify_keys!
|
64
|
+
each_with_index { |v, i| self[i] = self[i].dup.stringify_keys! if v.is_a? Hash }
|
65
|
+
end
|
66
|
+
|
59
67
|
def to_json
|
60
68
|
"[#{map { |e| e.to_json }.join(',')}]"
|
61
69
|
end
|
62
70
|
end
|
63
71
|
|
64
72
|
class Hash
|
65
|
-
def formatted
|
66
|
-
|
67
|
-
|
68
|
-
|
73
|
+
def formatted!
|
74
|
+
each_pair do |k, v|
|
75
|
+
if v.is_a? Numeric
|
76
|
+
self[k] = v.formatted
|
77
|
+
elsif v.is_a? Hash or v.is_a? Array
|
78
|
+
self[k] = self[k].dup.formatted!
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def stringify_keys!
|
84
|
+
keys.each { |key| self[key.to_s] = delete(key) }
|
85
|
+
each_pair { |k, v| self[k] = self[k].dup.stringify_keys! if v.is_a? Hash }
|
69
86
|
end
|
87
|
+
|
88
|
+
alias_method :undecorated_get, :[]
|
89
|
+
def [](key)
|
90
|
+
undecorated_get(key) or undecorated_get(key.is_a?(String) ? key.to_sym : key.to_s)
|
91
|
+
end
|
92
|
+
|
70
93
|
def to_json
|
71
94
|
arr = []
|
72
95
|
each_pair { |k, v| arr << "#{k.to_json}:#{v.to_json}" }
|
@@ -99,25 +122,21 @@ DataProviders.preload
|
|
99
122
|
|
100
123
|
WEBSTATS_PATH = File.expand_path("~/.webstats")
|
101
124
|
|
102
|
-
settings = {}
|
125
|
+
$settings = {}
|
103
126
|
|
104
127
|
if File.exists?(WEBSTATS_PATH)
|
105
|
-
settings = YAML.load(IO.read(WEBSTATS_PATH))
|
128
|
+
$settings = YAML.load(IO.read(WEBSTATS_PATH))
|
106
129
|
else
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
File.open(WEBSTATS_PATH, "w") do |f|
|
112
|
-
YAML.dump(settings, f)
|
113
|
-
end
|
130
|
+
$settings['webstats'] = { 'password' => nil }
|
131
|
+
DataProviders::DATA_SOURCES_CLASSES.each_pair { |k, v| $settings[k.to_s] = v.default_settings.stringify_keys! }
|
132
|
+
File.open(WEBSTATS_PATH, "w") { |f| YAML.dump($settings, f) }
|
114
133
|
end
|
115
134
|
|
116
|
-
DataProviders.setup(settings)
|
135
|
+
DataProviders.setup($settings)
|
117
136
|
|
118
137
|
class Webstats < WEBrick::HTTPServlet::AbstractServlet
|
119
138
|
def do_GET(req, res)
|
120
|
-
WEBrick::HTTPAuth.basic_auth(req, res, "Webstats") { |
|
139
|
+
WEBrick::HTTPAuth.basic_auth(req, res, "Webstats") { |u, p| u == 'webstats' and p == $settings[:webstats][:password] } unless $settings[:webstats][:password].nil?
|
121
140
|
|
122
141
|
body = ""
|
123
142
|
if req.path_info == '/'
|
@@ -201,7 +220,7 @@ EOF
|
|
201
220
|
out[k] = v.get.dup
|
202
221
|
end
|
203
222
|
|
204
|
-
|
223
|
+
out.formatted!
|
205
224
|
|
206
225
|
body << out.to_json
|
207
226
|
elsif req.path_info == '/information'
|
@@ -213,31 +232,6 @@ EOF
|
|
213
232
|
res.body = body
|
214
233
|
res['Content-Type'] = "text/html"
|
215
234
|
end
|
216
|
-
|
217
|
-
private
|
218
|
-
def fix_leaves_array(array)
|
219
|
-
array.each_with_index do |v, i|
|
220
|
-
if v.is_a? Numeric
|
221
|
-
array[i] = v.formatted
|
222
|
-
elsif v.is_a? Hash
|
223
|
-
array[i] = fix_leaves_hash(array[i].dup)
|
224
|
-
elsif v.is_a? Array
|
225
|
-
array[i] = fix_leaves_array(array[i].dup)
|
226
|
-
end
|
227
|
-
end
|
228
|
-
end
|
229
|
-
|
230
|
-
def fix_leaves_hash(hash)
|
231
|
-
hash.each_pair do |k, v|
|
232
|
-
if v.is_a? Numeric
|
233
|
-
hash[k] = v.formatted
|
234
|
-
elsif v.is_a? Hash
|
235
|
-
hash[k] = fix_leaves_hash(hash[k].dup)
|
236
|
-
elsif v.is_a? Array
|
237
|
-
hash[k] = fix_leaves_array(hash[k].dup)
|
238
|
-
end
|
239
|
-
end
|
240
|
-
end
|
241
235
|
end
|
242
236
|
|
243
237
|
s = WEBrick::HTTPServer.new(:Port => 9970)
|
data/webstats.gemspec
CHANGED
@@ -2,12 +2,12 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = %q{webstats}
|
5
|
-
s.version = "0.
|
5
|
+
s.version = "0.5.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
9
|
s.date = %q{2009-04-27}
|
10
|
-
s.description = %q{
|
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"]
|
@@ -22,6 +22,7 @@ Gem::Specification.new do |s|
|
|
22
22
|
"VERSION.yml",
|
23
23
|
"bin/webstats",
|
24
24
|
"bin/webstats_growl_notifier",
|
25
|
+
"clients/common.rb",
|
25
26
|
"clients/email_notifier/email_notifier.rb",
|
26
27
|
"clients/growl_notifier/Growl.rb",
|
27
28
|
"clients/growl_notifier/growl_notifier.rb",
|
@@ -39,7 +40,7 @@ Gem::Specification.new do |s|
|
|
39
40
|
s.rdoc_options = ["--charset=UTF-8"]
|
40
41
|
s.require_paths = [""]
|
41
42
|
s.rubygems_version = %q{1.3.1}
|
42
|
-
s.summary = %q{
|
43
|
+
s.summary = %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.}
|
43
44
|
|
44
45
|
if s.respond_to? :specification_version then
|
45
46
|
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
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.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brenton Fletcher
|
@@ -13,7 +13,7 @@ date: 2009-04-27 00:00:00 -07:00
|
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
16
|
-
description:
|
16
|
+
description: 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.
|
17
17
|
email: i@bloople.net
|
18
18
|
executables:
|
19
19
|
- webstats
|
@@ -30,6 +30,7 @@ files:
|
|
30
30
|
- VERSION.yml
|
31
31
|
- bin/webstats
|
32
32
|
- bin/webstats_growl_notifier
|
33
|
+
- clients/common.rb
|
33
34
|
- clients/email_notifier/email_notifier.rb
|
34
35
|
- clients/growl_notifier/Growl.rb
|
35
36
|
- clients/growl_notifier/growl_notifier.rb
|
@@ -67,6 +68,6 @@ rubyforge_project:
|
|
67
68
|
rubygems_version: 1.2.0
|
68
69
|
signing_key:
|
69
70
|
specification_version: 2
|
70
|
-
summary:
|
71
|
+
summary: 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.
|
71
72
|
test_files: []
|
72
73
|
|