updawg 0.3.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
@@ -0,0 +1,25 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
22
+ Gemfile.lock
23
+
24
+ ## rubymine
25
+ .idea
@@ -0,0 +1,8 @@
1
+ 0.3.3:
2
+ * ADD text reporting
3
+ * ADD check timeouts
4
+ * ADD std deviation checks
5
+ 0.3.2:
6
+ * FIX handling of StandardError in checks
7
+ 0.3.1:
8
+ * ADD nagios reporting
data/Gemfile ADDED
@@ -0,0 +1,13 @@
1
+ source :rubygems
2
+
3
+ gem "builder", '~>2.1'
4
+ gem 'aggregate'
5
+
6
+ group :development do
7
+ gem "rspec", "~> 1.3"
8
+ gem "hpricot", '~>0.8'
9
+ end
10
+
11
+ group :test do
12
+ gem 'SystemTimer', :platforms => :ruby
13
+ end
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Viximo, Inc.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,99 @@
1
+ = updawg
2
+
3
+ A simple way to see what's up, dawg (and what's not.)
4
+
5
+ Define a list of checks and create a controller that renders the results. Ping the controller with
6
+ the monitoring tool of your choice, perhaps setting a 503 response when one of the checks fails.
7
+
8
+ Some options can be set:
9
+ UpDawg.configure do |config|
10
+ config.success_text = 'OK'
11
+ end
12
+
13
+ Checks are defined with a simple DSL:
14
+
15
+ monitor = Updawg.monitor do
16
+ check 'MySQL' do
17
+ # Will raise an exception if the connection is unavailable resulting in a failed check
18
+ ActiveRecord::Base.connection.execute('SELECT 1 FROM DUAL')
19
+ end
20
+
21
+ check 'Some threshold' do
22
+ result ||= error('Threshold exceeded y') if threshold_value > y
23
+ result ||= warning('Threshold exceeded x') if threshold_value > x
24
+ result
25
+ end
26
+
27
+ # alternatively, use warning!/error!/pass! to break out of the check immediately
28
+ check 'Something else' do
29
+ warning!('Something kind of bad happened') if threshold_value > x
30
+ error! ('Something really bad happened') if threshold_value > y
31
+
32
+ pass!('Everything is just fine')
33
+ end
34
+
35
+ # Specify a threshold check for captured purchases in the last 24 hours
36
+ threshold 'Captured Purchases (24 hours)', :warn_under => 10, :error_under => 1 do
37
+ Purchase.captured_at_after(24.hours.ago)
38
+ end
39
+
40
+ # Specify a "sweet spot threshold check" to alert when a business metric goes out of normal range
41
+ threshold 'Transactions (24 hours)',
42
+ :warn_under => 100, :error_under => 1,
43
+ :warn_over => 1000, :error_over => 2000 do
44
+ Transaction.created_at_after(24.hours.ago)
45
+ end
46
+
47
+ # Specify a std deviation check to alert when a value is outside of 2 std deviations from the mean
48
+ # The block should yield an array of values, the last of which is considered the sample.
49
+ # Supports options:
50
+ # scale_factor - multiple of the std_dev to user (default: 2)
51
+ # smoothing - whether to ignore outliers in the dataset (default: true)
52
+ deviation 'Hourly Transaction Value' do
53
+ Transaction.group('hour').order('hour ASC') # will report if the last hour of data is our of range
54
+ end
55
+ end
56
+
57
+ results = monitor.perform
58
+
59
+ results.success? => true if no checks error
60
+ results.warning? => true if no checks error and at least one has a warning
61
+ results.error? => true if at least one check fails
62
+
63
+ Render with:
64
+ results.to_html
65
+
66
+ <table class="updawg monitor">
67
+ <tr class="check error">
68
+ <td class="name">MySQL</td>
69
+ <td class="message">ActiveRecord::StatementInvalid: Mysql::Error: You have an error in your SQL syntax</td>
70
+ <td class="status">ERROR</td>
71
+ </tr>
72
+ <tr class="check pass">
73
+ <td class="name">Some threshold</td>
74
+ <td class="message"></td>
75
+ <td class="status">PASS</td>
76
+ </tr>
77
+ </table>
78
+
79
+ === Nagios Integration
80
+ Use Updawg to easily script nagios checks in Ruby. Create your checks as usual and call #nagios_report!
81
+ on the result. Updawg will print a status line formatted for nagios and exit with an appropriate status code.
82
+
83
+ == Note on Patches/Pull Requests
84
+
85
+ * Fork the project.
86
+ * Create a feature branch and make your feature addition or bug fix there.
87
+ * Add tests for it. This is important so I don't break it in a
88
+ future version unintentionally.
89
+ * Commit, do not mess with rakefile, version, or history.
90
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
91
+ * Send me a pull request.
92
+
93
+ == Authors
94
+
95
+ Matt Griffin http://github.com/betamatt (matt@griffinonline.org)
96
+
97
+ == Copyright
98
+
99
+ Copyright (c) 2010 Viximo, Inc. See LICENSE for details.
@@ -0,0 +1,45 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'bundler'
4
+
5
+ begin
6
+ require 'jeweler'
7
+ Jeweler::Tasks.new do |gem|
8
+ gem.name = "updawg"
9
+ gem.summary = %Q{A simple way to see what's up, dawg (and what's not.)}
10
+ gem.email = "matt@griffinonline.org"
11
+ gem.homepage = "http://github.com/Viximo/updawg"
12
+ gem.authors = ["Matt Griffin"]
13
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
14
+
15
+ gem.files = `git ls-files`.split("\n")
16
+ gem.test_files = `git ls-files -- spec/*`.split("\n")
17
+ end
18
+ Jeweler::GemcutterTasks.new
19
+ rescue LoadError
20
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
21
+ end
22
+
23
+ require 'spec/rake/spectask'
24
+ Spec::Rake::SpecTask.new(:spec) do |spec|
25
+ spec.libs << 'lib' << 'spec'
26
+ spec.spec_files = FileList['spec/**/*_spec.rb']
27
+ end
28
+
29
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
30
+ spec.libs << 'lib' << 'spec'
31
+ spec.pattern = 'spec/**/*_spec.rb'
32
+ spec.rcov = true
33
+ end
34
+
35
+ task :default => :spec
36
+
37
+ require 'rake/rdoctask'
38
+ Rake::RDocTask.new do |rdoc|
39
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
40
+
41
+ rdoc.rdoc_dir = 'rdoc'
42
+ rdoc.title = "updawg #{version}"
43
+ rdoc.rdoc_files.include('README*')
44
+ rdoc.rdoc_files.include('lib/**/*.rb')
45
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.3.5
@@ -0,0 +1,36 @@
1
+ require 'updawg/monitor'
2
+ require 'updawg/checks'
3
+ require 'updawg/results'
4
+
5
+ module Updawg
6
+
7
+ class Configuration
8
+ attr_accessor :error_text, :warning_text, :success_text, :timeout
9
+
10
+ def initialize
11
+ self.error_text = 'ERROR'
12
+ self.warning_text = 'WARNING'
13
+ self.success_text = 'PASS'
14
+ self.timeout = 30
15
+ end
16
+ end
17
+
18
+ class << self
19
+ attr_accessor :configuration
20
+ end
21
+ self.configuration = Configuration.new
22
+
23
+ class << self
24
+ def configure
25
+ self.configuration ||= Configuration.new
26
+ yield(configuration) if block_given?
27
+ configuration
28
+ end
29
+
30
+ def monitor(&block)
31
+ monitor = Updawg::Monitor.new
32
+ monitor.instance_eval(&block)
33
+ monitor
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,3 @@
1
+ require 'updawg/checks/check'
2
+ require 'updawg/checks/deviation_check'
3
+ require 'updawg/checks/threshold_check'
@@ -0,0 +1,67 @@
1
+ module Updawg
2
+ begin
3
+ require 'system_timer'
4
+ Timeout = SystemTimer
5
+ rescue LoadError
6
+ module Timeout
7
+ class << self
8
+ public :timeout
9
+ end
10
+ end
11
+ end
12
+
13
+ class Check
14
+ attr_reader :name
15
+
16
+ def initialize(name, options = {}, &block)
17
+ @name = name
18
+ @block = block
19
+ @timeout = options.delete(:timeout) || Updawg.configuration.timeout
20
+ end
21
+
22
+ def perform
23
+ result = execute_check
24
+ return result if result.kind_of?(Result)
25
+ pass
26
+ rescue RuntimeError => e
27
+ error e.message
28
+ end
29
+
30
+ def pass(message = nil)
31
+ Updawg::Result.new(name, :pass, message)
32
+ end
33
+
34
+ def warning(message = nil)
35
+ Updawg::Result.new(name, :warning, message)
36
+ end
37
+
38
+ def error(message = nil)
39
+ Updawg::Result.new(name, :error, message)
40
+ end
41
+
42
+ def pass!(message = nil)
43
+ raise ResultException.new(Updawg::Result.new(name, :pass, message))
44
+ end
45
+
46
+ def warning!(message = nil)
47
+ raise ResultException.new(Updawg::Result.new(name, :warning, message))
48
+ end
49
+
50
+ def error!(message = nil)
51
+ raise ResultException.new(Updawg::Result.new(name, :error, message))
52
+ end
53
+
54
+
55
+ private
56
+
57
+ def execute_check
58
+ begin
59
+ Updawg::Timeout.timeout(@timeout) { instance_eval(&@block) } if @block
60
+ rescue ::Timeout::Error
61
+ raise 'timeout'
62
+ rescue ResultException => exception
63
+ exception.result
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,64 @@
1
+ require 'aggregate'
2
+
3
+ module Updawg
4
+ class DeviationCheck < Check
5
+ def initialize(name, options = {}, &block)
6
+ super(name, options, &block)
7
+
8
+ unknown_keys = options.keys - [:deviates_high, :deviates_low, :scale_factor, :smoothing].flatten
9
+ raise(ArgumentError, "Unknown key(s): #{unknown_keys.join(", ")}") unless unknown_keys.empty?
10
+
11
+ @options = options
12
+ @scale_factor = options.fetch(:scale_factor, 2)
13
+ @smoothing = options.fetch(:smoothing, true)
14
+ end
15
+
16
+ def scale_factor
17
+ @scale_factor
18
+ end
19
+
20
+ def smoothing
21
+ @smoothing
22
+ end
23
+
24
+ private
25
+
26
+ def execute_check
27
+ values = @block.call
28
+ sample = values.pop
29
+
30
+ return pass('Not enough data.') if sample.nil? || values.size < 2
31
+
32
+ aggregate = Aggregate.new
33
+ values.each {|v| aggregate << v}
34
+ std_dev = aggregate.std_dev
35
+
36
+ mean = aggregate.mean
37
+ min = mean - std_dev
38
+ max = mean + std_dev
39
+
40
+ if smoothing
41
+ without_outliers = values.select{|v| v >= min && v <= max}
42
+ smoothed_aggregate = Aggregate.new
43
+ without_outliers.each {|v| smoothed_aggregate << v}
44
+ mean = smoothed_aggregate.mean
45
+ std_dev = smoothed_aggregate.std_dev
46
+ end
47
+
48
+ min = mean - scale_factor * std_dev
49
+ max = mean + scale_factor * std_dev
50
+
51
+ return low(sample, min) if sample < min
52
+ return high(sample, max) if sample > max
53
+ return pass("#{sample} within expected range (#{min..max})")
54
+ end
55
+
56
+ def low(value, min)
57
+ send(@options.fetch(:deviates_low, :error), "#{value} is below expected minimum (#{min})")
58
+ end
59
+
60
+ def high(value, max)
61
+ send(@options.fetch(:deviates_high, :error), "#{value} is above expected maximum (#{max})")
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,36 @@
1
+ module Updawg
2
+ class ThresholdCheck < Check
3
+ def initialize(name, options = {}, &block)
4
+ super(name, options, &block)
5
+
6
+ unknown_keys = options.keys - [:error_under, :error_over, :warning_under, :warning_over].flatten
7
+ raise(ArgumentError, "Unknown key(s): #{unknown_keys.join(", ")}") unless unknown_keys.empty?
8
+
9
+ @options = options
10
+ end
11
+
12
+ private
13
+
14
+ def execute_check
15
+ value = @block.call
16
+
17
+ return under(:error, value, @options[:error_under]) if @options[:error_under] &&
18
+ value < @options[:error_under]
19
+ return over(:error, value, @options[:error_over]) if @options[:error_over] &&
20
+ value > @options[:error_over]
21
+ return under(:warning, value, @options[:warning_under]) if @options[:warning_under] &&
22
+ value < @options[:warning_under]
23
+ return over(:warning, value, @options[:warning_over]) if @options[:warning_over] &&
24
+ value > @options[:warning_over]
25
+ pass("#{value} OK")
26
+ end
27
+
28
+ def under(type, value, limit)
29
+ send(type, "Threshold lower bound exceeded: Got #{value}, expected > #{limit}")
30
+ end
31
+
32
+ def over(type, value, limit)
33
+ send(type, "Threshold upper bound exceeded: Got #{value}, expected < #{limit}")
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,33 @@
1
+ module Updawg
2
+ class Monitor
3
+ attr_accessor :checks
4
+
5
+ def initialize
6
+ self.checks = []
7
+ end
8
+
9
+ def check(name, options = {}, &block)
10
+ check = Updawg::Check.new(name, options, &block).tap do |check|
11
+ self.checks << check
12
+ end
13
+ end
14
+
15
+ def threshold(name, options = {}, &block)
16
+ check = Updawg::ThresholdCheck.new(name, options, &block).tap do |check|
17
+ self.checks << check
18
+ end
19
+ end
20
+
21
+ def deviation(name, options = {}, &block)
22
+ Updawg::DeviationCheck.new(name, options, &block).tap do |check|
23
+ self.checks << check
24
+ end
25
+ end
26
+
27
+ def perform
28
+ results = ResultSet.new
29
+ checks.each{|c| results << c.perform}
30
+ results
31
+ end
32
+ end
33
+ end