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 +2 -2
- data/VERSION.yml +1 -1
- data/server/data_providers/cpu_info.rb +10 -8
- data/server/data_providers/disk_activity.rb +9 -7
- data/server/data_providers/disk_usage.rb +9 -8
- data/server/data_providers/mem_info.rb +8 -6
- data/server/data_providers/url_monitor.rb +67 -0
- data/server/webstats.rb +50 -9
- data/webstats.gemspec +3 -2
- metadata +3 -2
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 = %
|
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,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(
|
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] <
|
45
|
-
out[:status] = 'danger' unless @readings.detect { |r| out[:usage] <
|
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
|
62
|
-
{ :
|
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
|
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(
|
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({ :
|
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
|
40
|
-
{ :
|
41
|
+
def self.default_settings
|
42
|
+
{ :update_rate => 2.5 }
|
41
43
|
end
|
42
44
|
|
43
|
-
def
|
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'] <
|
20
|
-
out[:status] = "danger" if mp[1]['free'] <
|
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({ :
|
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
|
42
|
-
{ :
|
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
|
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(
|
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
|
42
|
-
{ :
|
43
|
+
def self.default_settings
|
44
|
+
{ :update_rate => 2.5 }
|
43
45
|
end
|
44
46
|
|
45
|
-
def
|
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.
|
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
|
-
|
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.
|
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-
|
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.
|
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-
|
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
|