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 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, open a terminal on your computer, then run (the first line is only needed the first time you run the notifier):
41
- ~$ git clone git://github.com/bloopletech/webstats.git
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 http://<server's hostname>:9970/
45
+ ~/webstats/clients/growl_notifier$ ruby growl_notifier.rb
44
46
 
45
- Where server's hostname is the host name of the server you want to monitor (e.g. bloople.net). The Growl notifier will then run in the background.
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{Display server CPU/Memory/Disk Usage on a web page, suitable for remote performance monitoring.}
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
@@ -1,4 +1,4 @@
1
1
  ---
2
2
  :major: 0
3
- :minor: 3
4
- :patch: 0
3
+ :minor: 5
4
+ :patch: 1
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
- exit if fork
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
- url = ARGV[0]
15
+ require File.dirname(__FILE__) + '/../common'
16
+ load_settings('growl_notifier', { 'urls' => [{ 'url' => '', 'password' => nil }] })
10
17
 
11
- g = GrowlNotifier.new("Webstats for #{url}",['Webstats Notification'], nil, OSX::NSWorkspace.sharedWorkspace().iconForFileType_('unknown'))
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
- meta_info = JSON.parse(Net::HTTP.get(URI.join(url, "information")))
15
- last_warnings_text = last_danger_text = nil
16
- last_time = 0
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
- data = JSON.parse(Net::HTTP.get(URI.join(url, "update")))
34
+ urls.each do |url|
35
+ data = make_request(URI.join(url[:url], "update"), url[:password])
20
36
 
21
- bad = data.sort { |a, b| b[1]['importance'].to_f <=> a[1]['importance'].to_f }.select { |(k, v)| !v['status'].nil? && v['status'] != '' }
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
- has_warnings = bad.detect { |(k, v)| v['status'] == 'warning' }
24
- has_dangers = bad.detect { |(k, v)| v['status'] == 'danger' }
39
+ has_warnings = bad.detect { |(k, v)| v['status'] == 'warning' }
40
+ has_dangers = bad.detect { |(k, v)| v['status'] == 'danger' }
25
41
 
26
- title = []
27
- title << "Danger" if has_dangers
28
- title << "Warnings" if has_warnings
29
- title = title.join(" & ") + " for host #{URI.parse(url).host}"
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
- warnings_text = has_warnings ? "Warnings for #{bad.select { |(k, v)| v['status'] == 'warning' }.map { |(k, v)| meta_info[k]['in_sentence'] }.join(", ")}." : nil
32
- danger_text = has_dangers ? "Dangerous situation for #{bad.select { |(k, v)| v['status'] == 'danger' }.map { |(k, v)| meta_info[k]['in_sentence'] }.join(", ")}." : nil
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
- if last_warnings_text != warnings_text or danger_text != last_danger_text or (Time.now - last_time) > 60
35
- last_warnings_text = warnings_text
36
- last_danger_text = danger_text
37
- last_time = Time.now
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
- unless bad.empty?
40
- g.notify "Webstats Notification", title, [danger_text, warnings_text].compact.join(" "), nil, nil, true, (has_dangers ? 2 : 1)
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
- out = self.dup
67
- out.each_pair { |k, v| out[k] = v.formatted }
68
- out
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
- DataProviders::DATA_SOURCES_CLASSES.each_pair do |k, v|
108
- settings[k] = v.default_settings
109
- end
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") { |user, pass| user == 'webstats' and pass == ARGV[0] } unless ARGV.empty?
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
- fix_leaves_hash(out)
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.3.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{Display server CPU/Memory/Disk Usage on a web page, suitable for remote performance monitoring.}
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{Display server CPU/Memory/Disk Usage on a web page, suitable for remote performance monitoring.}
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.3.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: Display server CPU/Memory/Disk Usage on a web page, suitable for remote performance monitoring.
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: Display server CPU/Memory/Disk Usage on a web page, suitable for remote performance monitoring.
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