bloopletech-webstats 0.1.0 → 0.2.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.
data/Rakefile CHANGED
@@ -8,10 +8,10 @@ begin
8
8
 
9
9
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
10
10
  s.authors = ["Brenton Fletcher"]
11
- s.date = %q{2009-04-22}
11
+ s.date = Date.today.strftime("%Y-%m-%d")
12
12
  s.description = s.summary = %q{Display server CPU/Memory/Disk Usage on a web page, suitable for remote performance monitoring.}
13
13
  s.email = %q{i@bloople.net}
14
- s.files = Dir['**/*']
14
+ s.files = Dir['**/*'].reject { |fn| fn =~ /(\.o|\.so|Makefile)$/ }
15
15
  s.executables = ['webstats']
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: 1
3
+ :minor: 2
4
4
  :patch: 0
@@ -1,5 +1,7 @@
1
1
  class DataProviders::CpuInfo
2
- def initialize
2
+ def initialize(settings)
3
+ @settings = self.class.default_settings.merge(settings)
4
+
3
5
  @readings = []
4
6
  @mutex = Mutex.new
5
7
 
@@ -31,7 +33,7 @@ class DataProviders::CpuInfo
31
33
  last_idle = idle
32
34
  last_iowait = iowait
33
35
  last_time = time
34
- sleep(2.5)
36
+ sleep(@settings[:update_rate])
35
37
  end
36
38
  end
37
39
  end
@@ -41,8 +43,8 @@ class DataProviders::CpuInfo
41
43
  @mutex.synchronize do
42
44
  unless @readings.empty?
43
45
  out[:usage] = @readings.first
44
- out[:status] = 'warning' unless @readings.detect { |r| out[:usage] < 95 }
45
- out[:status] = 'danger' unless @readings.detect { |r| out[:usage] < 99.5 }
46
+ out[:status] = 'warning' unless @readings.detect { |r| out[:usage] < @settings[:usage_warning_level] }
47
+ out[:status] = 'danger' unless @readings.detect { |r| out[:usage] < @settings[:usage_danger_level] }
46
48
  end
47
49
  end
48
50
  out[:loadavg_1], out[:loadavg_5], out[:loadavg_15] = IO.readlines("/proc/loadavg").first.split(' ', 4).map { |v| v.to_f }
@@ -58,12 +60,12 @@ sc.innerHTML = "<div class='major_figure'><span class='title'>Usage</span><span
58
60
  } })
59
61
  end
60
62
 
61
- def information
62
- { :name => "CPU Info", :in_sentence => 'CPU load', :importance => importance }
63
+ def self.default_settings
64
+ { :update_rate => 2.5, :usage_warning_level => 95, :usage_danger_level => 99.5 }
63
65
  end
64
66
 
65
- def importance
66
- 100
67
+ def information
68
+ { :name => "CPU Info", :in_sentence => 'CPU load', :importance => 100 }
67
69
  end
68
70
 
69
71
  def kill
@@ -1,5 +1,7 @@
1
1
  class DataProviders::DiskActivity
2
- def initialize
2
+ def initialize(settings)
3
+ @settings = self.class.default_settings.merge(settings)
4
+
3
5
  @reads_sec = 0
4
6
  @writes_sec = 0
5
7
 
@@ -20,7 +22,7 @@ class DataProviders::DiskActivity
20
22
  last_reads = reads
21
23
  last_writes = writes
22
24
  last_time = time
23
- sleep(2.5)
25
+ sleep(@settings[:update_rate])
24
26
  end
25
27
  end
26
28
  end
@@ -30,18 +32,18 @@ class DataProviders::DiskActivity
30
32
  end
31
33
 
32
34
  def renderer
33
- information.merge({ :name => "Disk Activity", :in_sentence => "Disk Activity", :importance => importance, :contents => %{
35
+ information.merge({ :contents => %{
34
36
  sc.innerHTML = "<div class='major_figure'><span class='title'>Reads</span><span class='figure'>" + data_source['reads'] + "</span><span class='unit'>mb/s</span></div>" +
35
37
  "<div class='major_figure'><span class='title'>Writes</span><span class='figure'>" + data_source['writes'] + "</span><span class='unit'>mb/s</span></div>";
36
38
  } })
37
39
  end
38
40
 
39
- def information
40
- { :name => "Disk Activity", :in_sentence => "Disk Activity", :importance => importance }
41
+ def self.default_settings
42
+ { :update_rate => 2.5 }
41
43
  end
42
44
 
43
- def importance
44
- 70
45
+ def information
46
+ { :name => "Disk Activity", :in_sentence => "Disk Activity", :importance => 70 }
45
47
  end
46
48
 
47
49
  def kill
@@ -1,5 +1,6 @@
1
1
  class DataProviders::DiskUsage
2
- def initialize
2
+ def initialize(settings)
3
+ @settings = self.class.default_settings.merge(settings)
3
4
  end
4
5
 
5
6
  def get
@@ -16,15 +17,15 @@ class DataProviders::DiskUsage
16
17
  out[:mounts].map do |mp|
17
18
  mp[1]['free'] /= (1024.0 * 1024)
18
19
  mp[1]['total'] /= (1024.0 * 1024)
19
- out[:status] = "warning" if mp[1]['free'] < 50 and mp[1]['total'] > 100 and out[:status] != 'danger'
20
- out[:status] = "danger" if mp[1]['free'] < 10 and mp[1]['total'] > 20
20
+ out[:status] = "warning" if mp[1]['free'] < @settings[:warning_threshold] and mp[1]['total'] > @settings[:warning_minimum_mount_point_size] and out[:status] != 'danger'
21
+ out[:status] = "danger" if mp[1]['free'] < @settings[:danger_threshold] and mp[1]['total'] > @settings[:danger_minimum_mount_point_size]
21
22
  end
22
23
 
23
24
  out
24
25
  end
25
26
 
26
27
  def renderer
27
- information.merge({ :name => "Disk Usage by Mount Point", :in_sentence => "Disk Usage", :importance => importance, :contents => %{
28
+ information.merge({ :contents => %{
28
29
  var temp = "";
29
30
  for(var i = 0; i < data_source['mounts'].length; i++)
30
31
  {
@@ -38,12 +39,12 @@ sc.innerHTML = temp;
38
39
  } })
39
40
  end
40
41
 
41
- def information
42
- { :name => "Disk Usage by Mount Point", :in_sentence => "Disk Usage", :importance => importance }
42
+ def self.default_settings
43
+ { :warning_threshold => 50, :danger_threshold => 10, :warning_minimum_mount_point_size => 100, :danger_minimum_mount_point_size => 20 }
43
44
  end
44
45
 
45
- def importance
46
- 80
46
+ def information
47
+ { :name => "Disk Usage by Mount Point", :in_sentence => "Disk Usage", :importance => 80 }
47
48
  end
48
49
 
49
50
  def kill
@@ -1,5 +1,7 @@
1
1
  class DataProviders::MemInfo
2
- def initialize
2
+ def initialize(settings)
3
+ @settings = self.class.default_settings.merge(settings)
4
+
3
5
  @readings = []
4
6
  @mutex = Mutex.new
5
7
 
@@ -13,7 +15,7 @@ class DataProviders::MemInfo
13
15
  @readings.unshift(out)
14
16
  @readings.pop while @readings.length > 5
15
17
  end
16
- sleep(2.5)
18
+ sleep(@settings[:update_rate])
17
19
  end
18
20
  end
19
21
  end
@@ -38,12 +40,12 @@ sc.innerHTML = "<div class='major_figure'><span class='title'>Free</span><span c
38
40
  } })
39
41
  end
40
42
 
41
- def information
42
- { :name => "Memory Info", :in_sentence => 'Memory Usage', :importance => importance }
43
+ def self.default_settings
44
+ { :update_rate => 2.5 }
43
45
  end
44
46
 
45
- def importance
46
- 90
47
+ def information
48
+ { :name => "Memory Info", :in_sentence => 'Memory Usage', :importance => 90 }
47
49
  end
48
50
 
49
51
  def kill
@@ -0,0 +1,67 @@
1
+ require 'net/http'
2
+
3
+ class DataProviders::UrlMonitor
4
+ def initialize(settings)
5
+ @settings = self.class.default_settings.merge(settings)
6
+
7
+ @readings = []
8
+
9
+ @mutex = Mutex.new
10
+
11
+ @thread = Thread.new do
12
+ while(true)
13
+ @mutex.synchronize { @readings = [] }
14
+
15
+ @settings[:urls].sort.each do |url|
16
+ duration = -1
17
+ works = false
18
+ begin
19
+ start = Time.now
20
+ result = Net::HTTP.get(URI.parse(url))
21
+ duration = Time.now - start
22
+ works = true
23
+ rescue Exception => e
24
+ end
25
+ @mutex.synchronize { @readings << [url, { :response_time => duration * 1000, :works => works }] }
26
+ end
27
+ sleep(@settings[:update_rate])
28
+ end
29
+ end
30
+
31
+ end
32
+
33
+ def get
34
+ out = {}
35
+ @mutex.synchronize { out[:urls] = @readings }
36
+ out[:urls].each do |(url, info)|
37
+ out[:status] = 'warning' if !info[:works] or info[:response_time] > @settings[:warning_response_time_threshold] and !out[:status] == 'danger'
38
+ out[:status] = 'danger' if !info[:works] or info[:response_time] > @settings[:danger_response_time_threshold]
39
+ end
40
+ out
41
+ end
42
+
43
+ def renderer
44
+ information.merge({ :contents => %{
45
+ var temp = "";
46
+ for(var i = 0; i < data_source['urls'].length; i++)
47
+ {
48
+ var ud = data_source['urls'][i][1];
49
+ temp += "<div class='major_figure'><span class='title'>" + data_source['urls'][i][0] + "</span><span class='figure'>" +
50
+ (!ud['works'] ? 'Failed</span>' : ud['response_time'] + "</span><span class='unit'>ms</span>") + "</div>";
51
+ }
52
+
53
+ sc.innerHTML = temp;
54
+ } })
55
+ end
56
+
57
+ def self.default_settings
58
+ { :update_rate => 30, :warning_response_time_threshold => 5000, :danger_response_time_threshold => 30000, :urls => ['http://localhost/'] }
59
+ end
60
+
61
+ def information
62
+ { :name => "URL Monitor", :in_sentence => "URL Monitor", :importance => 60 }
63
+ end
64
+
65
+ def kill
66
+ end
67
+ end
data/server/webstats.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'webrick'
2
+ require 'yaml'
2
3
 
3
4
  if $DEBUG
4
5
  Thread.abort_on_exception
@@ -15,6 +16,24 @@ Thread.new do
15
16
  end
16
17
  end
17
18
 
19
+ class NilClass
20
+ def to_json
21
+ "null"
22
+ end
23
+ end
24
+
25
+ class TrueClass
26
+ def to_json
27
+ "true"
28
+ end
29
+ end
30
+
31
+ class FalseClass
32
+ def to_json
33
+ "false"
34
+ end
35
+ end
36
+
18
37
  class String
19
38
  def underscore
20
39
  self.gsub(/::/, '/').
@@ -62,17 +81,39 @@ class Symbol
62
81
  end
63
82
 
64
83
  module DataProviders
84
+ DATA_SOURCES_CLASSES = {}
65
85
  DATA_SOURCES = {}
66
- def self.setup
86
+ def self.preload
67
87
  Dir.glob("#{File.dirname(__FILE__)}/data_providers/*.rb").each { |file| load file }
68
88
  DataProviders.constants.each do |c|
69
89
  c = DataProviders.const_get(c)
70
- DATA_SOURCES[c.to_s.gsub(/^DataProviders::/, '').underscore] = c.new if c.is_a? Class
90
+ DATA_SOURCES_CLASSES[c.to_s.gsub(/^DataProviders::/, '').underscore] = c if c.is_a? Class
71
91
  end
72
92
  end
93
+ def self.setup(settings)
94
+ DATA_SOURCES_CLASSES.each_pair { |k, v| DATA_SOURCES[k] = v.new(settings[k]) }
95
+ end
96
+ end
97
+
98
+ DataProviders.preload
99
+
100
+ WEBSTATS_PATH = File.expand_path("~/.webstats")
101
+
102
+ settings = {}
103
+
104
+ if File.exists?(WEBSTATS_PATH)
105
+ settings = YAML.load(IO.read(WEBSTATS_PATH))
106
+ 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
73
114
  end
74
115
 
75
- DataProviders.setup
116
+ DataProviders.setup(settings)
76
117
 
77
118
  class Webstats < WEBrick::HTTPServlet::AbstractServlet
78
119
  def do_GET(req, res)
@@ -144,7 +185,7 @@ body << <<-EOF
144
185
  <div id="main">
145
186
  <h1><span>Stats for #{req.host}</span></h1>
146
187
  EOF
147
- DataProviders::DATA_SOURCES.sort { |a, b| b[1].importance <=> a[1].importance }.each do |(k, v)|
188
+ DataProviders::DATA_SOURCES.sort { |a, b| b[1].information[:importance] <=> a[1].information[:importance] }.each do |(k, v)|
148
189
  r = v.renderer
149
190
  body << %{<div class="source" id="source_#{k}"><h2><span>#{r[:name]}</span></h2><div class="source_contents" id="source_contents_#{k}">Loading...</div></div>}
150
191
  end
@@ -157,7 +198,7 @@ EOF
157
198
  elsif req.path_info == '/update'
158
199
  out = {}
159
200
  DataProviders::DATA_SOURCES.each_pair do |k, v|
160
- out[k] = v.get
201
+ out[k] = v.get.dup
161
202
  end
162
203
 
163
204
  fix_leaves_hash(out)
@@ -179,9 +220,9 @@ EOF
179
220
  if v.is_a? Numeric
180
221
  array[i] = v.formatted
181
222
  elsif v.is_a? Hash
182
- array[i] = fix_leaves_hash(array[i])
223
+ array[i] = fix_leaves_hash(array[i].dup)
183
224
  elsif v.is_a? Array
184
- array[i] = fix_leaves_array(array[i])
225
+ array[i] = fix_leaves_array(array[i].dup)
185
226
  end
186
227
  end
187
228
  end
@@ -191,9 +232,9 @@ EOF
191
232
  if v.is_a? Numeric
192
233
  hash[k] = v.formatted
193
234
  elsif v.is_a? Hash
194
- hash[k] = fix_leaves_hash(hash[k])
235
+ hash[k] = fix_leaves_hash(hash[k].dup)
195
236
  elsif v.is_a? Array
196
- hash[k] = fix_leaves_array(hash[k])
237
+ hash[k] = fix_leaves_array(hash[k].dup)
197
238
  end
198
239
  end
199
240
  end
data/webstats.gemspec CHANGED
@@ -2,11 +2,11 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{webstats}
5
- s.version = "0.1.0"
5
+ s.version = "0.2.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-04-22}
9
+ s.date = %q{2009-04-25}
10
10
  s.default_executable = %q{webstats}
11
11
  s.description = %q{Display server CPU/Memory/Disk Usage on a web page, suitable for remote performance monitoring.}
12
12
  s.email = %q{i@bloople.net}
@@ -31,6 +31,7 @@ Gem::Specification.new do |s|
31
31
  "server/data_providers/disk_usage.rb",
32
32
  "server/data_providers/extconf.rb",
33
33
  "server/data_providers/mem_info.rb",
34
+ "server/data_providers/url_monitor.rb",
34
35
  "server/webstats.rb",
35
36
  "webstats.gemspec"
36
37
  ]
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.1.0
4
+ version: 0.2.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-04-22 00:00:00 -07:00
12
+ date: 2009-04-25 00:00:00 -07:00
13
13
  default_executable: webstats
14
14
  dependencies: []
15
15
 
@@ -37,6 +37,7 @@ files:
37
37
  - server/data_providers/disk_usage.rb
38
38
  - server/data_providers/extconf.rb
39
39
  - server/data_providers/mem_info.rb
40
+ - server/data_providers/url_monitor.rb
40
41
  - server/webstats.rb
41
42
  - webstats.gemspec
42
43
  has_rdoc: false