simple_monitor 0.0.1
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/.gitignore +5 -0
- data/Gemfile +4 -0
- data/README.md +74 -0
- data/Rakefile +4 -0
- data/lib/simple_monitor/version.rb +3 -0
- data/lib/simple_monitor.rb +92 -0
- data/license.txt +23 -0
- data/simple_monitor.gemspec +24 -0
- data/spec/simple_monitor_spec.rb +117 -0
- data/spec/spec_helper.rb +6 -0
- metadata +82 -0
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
SimpleMonitor
|
2
|
+
=============
|
3
|
+
|
4
|
+
Send alerts based on simple monitored conditions in your app.
|
5
|
+
|
6
|
+
It provides a basic skeleton for writing unique but consistent
|
7
|
+
monitoring checks for your application. Examples of such checks
|
8
|
+
are a Delayed::Job queue that is too full, too many failed logins
|
9
|
+
in the last 5 minutes, or a remote service being unreachable.
|
10
|
+
|
11
|
+
Installation
|
12
|
+
------------
|
13
|
+
|
14
|
+
install it via rubygems:
|
15
|
+
|
16
|
+
```
|
17
|
+
gem install simple_monitor
|
18
|
+
```
|
19
|
+
|
20
|
+
or put it in your Gemfile:
|
21
|
+
|
22
|
+
```ruby
|
23
|
+
# Gemfile
|
24
|
+
gem 'simple_monitor'
|
25
|
+
```
|
26
|
+
|
27
|
+
Usage
|
28
|
+
-----
|
29
|
+
|
30
|
+
SimpleMonitor should be mixed in to a SomeConditionMonitor class
|
31
|
+
|
32
|
+
```ruby
|
33
|
+
require "simple_monitor"
|
34
|
+
|
35
|
+
class HighJobMonitor
|
36
|
+
include SimpleMonitor
|
37
|
+
|
38
|
+
def needs_alert?
|
39
|
+
Queue.jobs.count > options[:job_count_threshold]
|
40
|
+
end
|
41
|
+
|
42
|
+
def send_alert
|
43
|
+
# Alert sending method of your choice. SimpleMonitor
|
44
|
+
# leaves this up to you
|
45
|
+
Mailer.deliver_high_job_alert(Queue.jobs.count)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
monitor = HighJobMonitor.new(:job_count_threshold => 99)
|
50
|
+
monitor.check
|
51
|
+
```
|
52
|
+
|
53
|
+
For a typical application, it could be desireable to define an
|
54
|
+
AppMonitor class with a default send_alert method, and have your
|
55
|
+
individual monitor classes inherit from that.
|
56
|
+
|
57
|
+
A monitor class can take options on initialization; this is recommended
|
58
|
+
for passing in thresholds, email addresses, or other dependencies.
|
59
|
+
|
60
|
+
Logging
|
61
|
+
-------
|
62
|
+
|
63
|
+
SimpleMonitor defaults its logger to a new Logger instance, or
|
64
|
+
Rails.logger if that is defined. If you want to override this,
|
65
|
+
do so in your class or via the `logger=` instance method.
|
66
|
+
|
67
|
+
When running a `check`, the logger will be warned or provided
|
68
|
+
with info whether an alert was needed. Note this is in addition
|
69
|
+
to sending out an alert.
|
70
|
+
|
71
|
+
#### Copyright
|
72
|
+
|
73
|
+
Copyright (c) (2012) Brendon Murphy. See license.txt for details.
|
74
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
require "simple_monitor/version"
|
2
|
+
|
3
|
+
module SimpleMonitor
|
4
|
+
attr_reader :options
|
5
|
+
attr_accessor :logger
|
6
|
+
|
7
|
+
LOG_METHODS = %w[info warn error debug].freeze
|
8
|
+
|
9
|
+
def initialize(options = {})
|
10
|
+
@options = options
|
11
|
+
end
|
12
|
+
|
13
|
+
# Runs the check and sends an alert if needed
|
14
|
+
#
|
15
|
+
# Returns false if the check failed, true if passed
|
16
|
+
def check
|
17
|
+
if needs_alert?
|
18
|
+
warn_alert
|
19
|
+
send_alert
|
20
|
+
false
|
21
|
+
else
|
22
|
+
info_passed
|
23
|
+
true
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def warn_alert
|
28
|
+
warn(alert_log_message)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Public: Message to log in case of an alert
|
32
|
+
#
|
33
|
+
# Override this in your monitors to inject data
|
34
|
+
#
|
35
|
+
# Returns: String
|
36
|
+
def alert_log_message
|
37
|
+
"check generated an alert"
|
38
|
+
end
|
39
|
+
|
40
|
+
def info_passed
|
41
|
+
info(passed_log_message)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Public: Message to log in case of a check passing
|
45
|
+
#
|
46
|
+
# Override this in your monitors to inject data
|
47
|
+
#
|
48
|
+
# Returns: String
|
49
|
+
def passed_log_message
|
50
|
+
"check passed"
|
51
|
+
end
|
52
|
+
|
53
|
+
# Public: Conditional method to check if the alert should be sent
|
54
|
+
#
|
55
|
+
# This should be overridden in your individual monitor classes
|
56
|
+
#
|
57
|
+
# Returns a boolean
|
58
|
+
def needs_alert?
|
59
|
+
false
|
60
|
+
end
|
61
|
+
|
62
|
+
# Public: Send out an alert
|
63
|
+
#
|
64
|
+
# This should be overridden in your individual monitor classes,
|
65
|
+
# or base monitor class. This might be to send an SMS, email
|
66
|
+
# or IRC message
|
67
|
+
def send_alert
|
68
|
+
#no-op
|
69
|
+
end
|
70
|
+
|
71
|
+
# A memoized logger.
|
72
|
+
#
|
73
|
+
# Returns: a logger that responds to warn, info, debug, and error
|
74
|
+
def logger
|
75
|
+
@logger ||=
|
76
|
+
if defined?(Rails)
|
77
|
+
Rails.logger
|
78
|
+
else
|
79
|
+
require "logger"
|
80
|
+
Logger.new(STDOUT)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# Generated methods delegated to the logger. This is for convenience
|
85
|
+
# as well as for prefixing the monitor class name into the message
|
86
|
+
LOG_METHODS.each do |method|
|
87
|
+
define_method method do |message|
|
88
|
+
message = [self.class.name, message.to_s].join(" -- ")
|
89
|
+
logger.send(method, message)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
data/license.txt
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
licensed under MIT License:
|
2
|
+
|
3
|
+
Copyright (c) 2012 Brendon Murphy
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
23
|
+
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "simple_monitor/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "simple_monitor"
|
7
|
+
s.version = SimpleMonitor::VERSION
|
8
|
+
s.authors = ["Brendon Murphy"]
|
9
|
+
s.email = ["xternal1+github@gmail.com"]
|
10
|
+
s.licenses = ["MIT"]
|
11
|
+
s.homepage = ""
|
12
|
+
s.summary = %q{Send alerts based on simple monitored conditions in your app}
|
13
|
+
s.description = s.description
|
14
|
+
|
15
|
+
s.rubyforge_project = "simple_monitor"
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
|
22
|
+
s.add_development_dependency "rake"
|
23
|
+
s.add_development_dependency "rspec"
|
24
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class TestMonitor
|
4
|
+
include SimpleMonitor
|
5
|
+
|
6
|
+
attr_reader :last_alert
|
7
|
+
|
8
|
+
def needs_alert?
|
9
|
+
options[:force_alert]
|
10
|
+
end
|
11
|
+
|
12
|
+
def send_alert
|
13
|
+
@last_alert = "Test Monitor Alert"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe SimpleMonitor do
|
18
|
+
it "is initialized with optional options" do
|
19
|
+
monitor = TestMonitor.new
|
20
|
+
monitor = TestMonitor.new(:foo => :bar)
|
21
|
+
monitor.options[:foo].should == :bar
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe SimpleMonitor, "logger" do
|
26
|
+
subject { TestMonitor.new }
|
27
|
+
|
28
|
+
it "is defaulted to Logger.new(STDOUT)" do
|
29
|
+
subject.logger.should be_kind_of(Logger)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Problem, what if its vendored and rails and run in a spec run
|
33
|
+
# that boots rails?
|
34
|
+
it "is defaulted to the Rails.logger if present" do
|
35
|
+
module Rails
|
36
|
+
def self.logger
|
37
|
+
:rails_logger
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
subject.logger.should == :rails_logger
|
42
|
+
|
43
|
+
Object.send(:remove_const, :Rails)
|
44
|
+
end
|
45
|
+
|
46
|
+
it "sends info, warn, error, and debug methods to it prefixed by the class name" do
|
47
|
+
stub_logger = stub('logger')
|
48
|
+
subject.logger = stub_logger
|
49
|
+
%w[info warn error debug].each do |log_method|
|
50
|
+
stub_logger.should_receive(log_method).with("TestMonitor -- #{log_method}")
|
51
|
+
subject.send(log_method, log_method)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
describe SimpleMonitor, "running the check" do
|
57
|
+
subject { TestMonitor.new }
|
58
|
+
let(:logger) { stub('logger', :warn => nil, :info => nil) }
|
59
|
+
before { subject.logger = logger }
|
60
|
+
|
61
|
+
context "when an alert needs sending" do
|
62
|
+
before { subject.options[:force_alert] = true }
|
63
|
+
|
64
|
+
it "logs a warning that the check is in alert" do
|
65
|
+
logger.should_receive(:warn).with(/TestMonitor --.*alert/)
|
66
|
+
subject.check
|
67
|
+
end
|
68
|
+
|
69
|
+
it "sends an alert" do
|
70
|
+
subject.check
|
71
|
+
subject.last_alert.should == "Test Monitor Alert"
|
72
|
+
end
|
73
|
+
|
74
|
+
it "returns false" do
|
75
|
+
subject.check.should == false
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
context "when no alert needs sending" do
|
80
|
+
before { subject.options[:force_alert] = false }
|
81
|
+
|
82
|
+
it "logs info that the check passed" do
|
83
|
+
logger.should_receive(:info).with(/TestMonitor --.*passed/)
|
84
|
+
subject.check
|
85
|
+
end
|
86
|
+
|
87
|
+
it "doesn't send an alert" do
|
88
|
+
subject.check
|
89
|
+
subject.last_alert.should be_nil
|
90
|
+
end
|
91
|
+
|
92
|
+
it "returns true" do
|
93
|
+
subject.check.should == true
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
describe SimpleMonitor, "supports a class including SimpleMonitor" do
|
99
|
+
class ChildTestMonitor < TestMonitor
|
100
|
+
def needs_alert?
|
101
|
+
true
|
102
|
+
end
|
103
|
+
|
104
|
+
def send_alert
|
105
|
+
@last_alert = "CHILD HERE"
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
subject { ChildTestMonitor.new }
|
110
|
+
let(:logger) { stub('logger', :warn => nil, :info => nil) }
|
111
|
+
before { subject.logger = logger }
|
112
|
+
|
113
|
+
it "to be inherited" do
|
114
|
+
subject.check
|
115
|
+
subject.last_alert.should == "CHILD HERE"
|
116
|
+
end
|
117
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: simple_monitor
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Brendon Murphy
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-07-21 00:00:00.000000000 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: rake
|
17
|
+
requirement: &2160677420 !ruby/object:Gem::Requirement
|
18
|
+
none: false
|
19
|
+
requirements:
|
20
|
+
- - ! '>='
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '0'
|
23
|
+
type: :development
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: *2160677420
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: rspec
|
28
|
+
requirement: &2160677000 !ruby/object:Gem::Requirement
|
29
|
+
none: false
|
30
|
+
requirements:
|
31
|
+
- - ! '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: *2160677000
|
37
|
+
description: ''
|
38
|
+
email:
|
39
|
+
- xternal1+github@gmail.com
|
40
|
+
executables: []
|
41
|
+
extensions: []
|
42
|
+
extra_rdoc_files: []
|
43
|
+
files:
|
44
|
+
- .gitignore
|
45
|
+
- Gemfile
|
46
|
+
- README.md
|
47
|
+
- Rakefile
|
48
|
+
- lib/simple_monitor.rb
|
49
|
+
- lib/simple_monitor/version.rb
|
50
|
+
- license.txt
|
51
|
+
- simple_monitor.gemspec
|
52
|
+
- spec/simple_monitor_spec.rb
|
53
|
+
- spec/spec_helper.rb
|
54
|
+
has_rdoc: true
|
55
|
+
homepage: ''
|
56
|
+
licenses:
|
57
|
+
- MIT
|
58
|
+
post_install_message:
|
59
|
+
rdoc_options: []
|
60
|
+
require_paths:
|
61
|
+
- lib
|
62
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
63
|
+
none: false
|
64
|
+
requirements:
|
65
|
+
- - ! '>='
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '0'
|
68
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
69
|
+
none: false
|
70
|
+
requirements:
|
71
|
+
- - ! '>='
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: '0'
|
74
|
+
requirements: []
|
75
|
+
rubyforge_project: simple_monitor
|
76
|
+
rubygems_version: 1.6.2
|
77
|
+
signing_key:
|
78
|
+
specification_version: 3
|
79
|
+
summary: Send alerts based on simple monitored conditions in your app
|
80
|
+
test_files:
|
81
|
+
- spec/simple_monitor_spec.rb
|
82
|
+
- spec/spec_helper.rb
|