Commit eb3f08bfbe0f4e3c7b4ff9385cadd33d2d6054eb:
Merge branch 'master' of github.com:apache/whimsy
Branch: refs/heads/master
Author: Craig L Russell <[email protected]>
Committer: Craig L Russell <[email protected]>
Pusher: clr <[email protected]>
------------------------------------------------------------
lib/whimsy/asf/ldap.rb | + -
www/status/README.md | +++++++++ ------
www/status/errors.cgi | ++++++++++ --
www/status/js/status.js | +++++++++++
www/status/monitor.rb | ++++++ ----
www/status/monitors/board_minutes.rb | + -
www/status/monitors/public_json.rb | + -
www/status/monitors/secmail.rb | + -
------------------------------------------------------------
120 changes: 79 additions, 41 deletions.
------------------------------------------------------------
diff --git a/lib/whimsy/asf/ldap.rb b/lib/whimsy/asf/ldap.rb
index 33948c7..bd24245 100644
--- a/lib/whimsy/asf/ldap.rb
+++ b/lib/whimsy/asf/ldap.rb
@@ -76,7 +76,7 @@ def self.connect
def self.init_ldap
return @ldap if @ldap
@mtime = Time.now
- @ldap = ASF::LDAP.init
+ @ldap = ASF::LDAP.connect
end
# determine where ldap.conf resides
diff --git a/www/status/README.md b/www/status/README.md
index a24d6e7..ed4130e 100644
--- a/www/status/README.md
+++ b/www/status/README.md
@@ -69,6 +69,10 @@ Anchors and the top of each major branch emanating from the
root have an
*mtime* value which indicates when that data was last updated. This is
described below in the control flow section below.
+Leaf nodes can have a mtime value in place of data. Such values will be
+converted to local time and displayed as the last update value. Hovering over
+such items will show the GMT value of the time specified in ISO-8601 format.
+
Control Flow
============
@@ -87,12 +91,11 @@ no more often than once a minute, and are passed the
normalized results of the
previous call.
As monitors are called in response to a ping, they are expected to produce
-results in sub-second time in order to avoid the ping timing out. (Currently
-monitors are called in series, but in the future this may change to threads so
-that statuses may be collected in parallel.) Monitors that perform activities
-that take a substantial amount of time may elect to do so less frequently than
-once a minute, and can take advantage of the `mtime` values to determine when
-to do so.
+results in sub-second time in order to avoid the ping timing out. (Monitors
+are run in separate threads to minimize the total elapsed time). Monitors
+that perform activities that take a substantial amount of time may elect to do
+so less frequently than once a minute, and can take advantage of the `mtime`
+values to determine when to do so.
Results are collected into a hash, and that hash is then normalized.
Normalization resolves default values for items like levels and titles
diff --git a/www/status/errors.cgi b/www/status/errors.cgi
index f98b3f1..36374d2 100755
--- a/www/status/errors.cgi
+++ b/www/status/errors.cgi
@@ -30,8 +30,8 @@ _html do
_table.table do
_tr_ do
- _th 'Time'
_th 'Pinger'
+ _th 'Time'
_th 'Status'
_th 'Error'
end
@@ -39,11 +39,11 @@ _html do
exceptions.sort.reverse.each do |time, pinger, text|
color = (text.include?('HTTP/1.1 3') ? 'warning' : 'danger')
_tr_ class: color do
- _td time.iso8601
_td align: 'right' do
_a pinger, href:
"https://www.pingmybox.com/pings?location=470&pinger=#{pinger}"
end
+ _td.time time.iso8601
_td text[/^HTTP\/1.1 (\d+)/, 1]
_td text.sub(/^HTTP\/1.1 \d+/, '')
end
@@ -53,6 +53,14 @@ _html do
_p do
_a 'raw log', href: uri.to_s
end
+
+ _script %{
+ Array.from(document.querySelectorAll('.time')).forEach(function(time) {
+ var date = new Date(Date.parse(time.textContent));
+ time.setAttribute('title', time.textContent);
+ time.textContent = date.toLocaleString();
+ });
+ }
end
diff --git a/www/status/js/status.js b/www/status/js/status.js
index c0d5c77..69aa22e 100644
--- a/www/status/js/status.js
+++ b/www/status/js/status.js
@@ -25,6 +25,12 @@ $(function() {
var div = $('<div>').addClass('list-group').addClass('collapse').
attr('id', prefix + item);
+ // default data to mtime, if present
+ if (!value.data && value.mtime) {
+ value.data = "Last updated " +
+ new Date(Date.parse(value.mtime)).toLocaleString()
+ }
+
// build nested content (recursively if value.data is an object)
if (!value.data) {
div.append($('<a>').addClass('list-group-item').
@@ -46,6 +52,11 @@ $(function() {
div.children('a:not(.data-toggle)').attr('href', value.href);
}
+ // provide ISO-8601 formatted GMT time as a tooltip
+ if (value.mtime && !value.title) {
+ div.children('a:not(.data-toggle)').attr('title', value.mtime);
+ }
+
// append each to the container
container.append(anchor);
container.append(div);
diff --git a/www/status/monitor.rb b/www/status/monitor.rb
index f58fb21..6822875 100644
--- a/www/status/monitor.rb
+++ b/www/status/monitor.rb
@@ -6,6 +6,7 @@
require 'json'
require 'time'
+require 'thread'
class Monitor
# match http://getbootstrap.com/components/#alerts
@@ -30,42 +31,52 @@ def initialize(args = [])
return
end
- # invoke each monitor, collecting status from each
- newstatus = {}
+ # start each monitor in a separate thread
+ threads = []
self.class.singleton_methods.sort.each do |method|
next if args.length > 0 and not args.include? method.to_s
- # invoke method to determine current status
- begin
- previous = baseline[method] || {mtime: Time.at(0).gmtime.iso8601}
- status = Monitor.send(method, previous) || previous
-
- # convert non-hashes in proper statuses
- if not status.instance_of? Hash
- if status.instance_of? String or status.instance_of? Array
- status = {data: status}
- else
- status = {level: 'danger', data: status.inspect}
+ threads << Thread.new do
+ begin
+ # invoke method to determine current status
+ previous = baseline[method] || {mtime: Time.at(0)}
+ status = Monitor.send(method, previous) || previous
+
+ # convert non-hashes in proper statuses
+ if not status.instance_of? Hash
+ if status.instance_of? String or status.instance_of? Array
+ status = {data: status}
+ else
+ status = {level: 'danger', data: status.inspect}
+ end
end
- end
- rescue Exception => e
- status = {
- level: 'danger',
- data: {
- exception: {
- level: 'danger',
- text: e.inspect,
- data: e.backtrace
+ rescue Exception => e
+ status = {
+ level: 'danger',
+ data: {
+ exception: {
+ level: 'danger',
+ text: e.inspect,
+ data: e.backtrace
+ }
}
}
- }
- end
+ end
+
+ # default mtime to now
+ status['mtime'] ||= Time.now if status.instance_of? Hash
- # default mtime to now
- status['mtime'] ||= Time.now.gmtime.iso8601 if status.instance_of? Hash
+ # store status in thread local storage
+ Thread.current[:name] = method.to_s
+ Thread.current[:status] = status
+ end
+ end
- # update baseline
- newstatus[method] = status
+ # collect status from each monitor thread
+ newstatus = {}
+ threads.each do |thread|
+ thread.join
+ newstatus[thread[:name]] = thread[:status]
end
# normalize status
@@ -104,12 +115,17 @@ def normalize(status)
if status['data'].instance_of? Hash
# recursively normalize the data structure
status['data'].values.each {|value| normalize(value)}
- elsif not status['data']
+ elsif not status['data'] and not status['mtime']
# default data
status['data'] = 'missing'
status['level'] ||= 'danger'
end
+ # normalize time
+ if status['mtime'].instance_of? Time
+ status['mtime'] = status['mtime'].gmtime.iso8601
+ end
+
# normalize level (filling in title when this occurs)
if status['level']
if not LEVELS.include? status['level']
diff --git a/www/status/monitors/board_minutes.rb
b/www/status/monitors/board_minutes.rb
index a6781c9..9289ca4 100644
--- a/www/status/monitors/board_minutes.rb
+++ b/www/status/monitors/board_minutes.rb
@@ -19,6 +19,6 @@ def Monitor.board_minutes(previous_status)
href: '../logs/collate_minutes'
}
else
- "Last updated: #{File.mtime(index)}"
+ {mtime: File.mtime(index)}
end
end
diff --git a/www/status/monitors/public_json.rb
b/www/status/monitors/public_json.rb
index 90466fb..3e65185 100644
--- a/www/status/monitors/public_json.rb
+++ b/www/status/monitors/public_json.rb
@@ -11,7 +11,7 @@ def Monitor.public_json(previous_status)
name = File.basename(log).sub('public-', '')
if File.size(log) == 0
- status[name] = {data: "Last updated #{File.mtime(log)}"}
+ status[name] = {mtime: File.mtime(log)}
else
status[name] = {level: 'danger', data: File.readlines(log)}
end
diff --git a/www/status/monitors/secmail.rb b/www/status/monitors/secmail.rb
index 587c07c..e465e5e 100644
--- a/www/status/monitors/secmail.rb
+++ b/www/status/monitors/secmail.rb
@@ -5,5 +5,5 @@
def Monitor.secmail(previous_status)
log = '/srv/mail/procmail.log'
- "Last updated: #{File.mtime(log)}"
+ {mtime: File.mtime(log)}
end