babysitter 0.0.1 → 0.0.2
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/README.md +5 -29
- data/babysitter.gemspec +2 -4
- data/lib/babysitter.rb +5 -11
- data/lib/babysitter/configuration.rb +1 -10
- data/lib/babysitter/null_logger.rb +1 -1
- data/lib/babysitter/{monitor.rb → progress.rb} +13 -13
- data/lib/babysitter/{counter.rb → progress_counter.rb} +13 -16
- data/lib/babysitter/version.rb +1 -1
- data/spec/lib/babysitter/configuration_spec.rb +1 -30
- data/spec/lib/babysitter/{monitor_spec.rb → progress_spec.rb} +10 -118
- data/spec/spec_helper.rb +1 -1
- metadata +6 -50
- data/lib/babysitter/exception_notifiers.rb +0 -1
- data/lib/babysitter/exception_notifiers/simple_notification_service.rb +0 -42
- data/lib/babysitter/logger_with_stats.rb +0 -43
- data/lib/babysitter/tracker.rb +0 -52
- data/spec/lib/babysitter/exception_notifiers/simple_notification_service_spec.rb +0 -105
- data/spec/lib/babysitter/tracker_spec.rb +0 -81
- data/spec/lib/babysitter_spec.rb +0 -50
data/README.md
CHANGED
@@ -7,7 +7,7 @@ When provided with a Logger it will output statistics of progress to the logs.
|
|
7
7
|
|
8
8
|
Add this line to your application's Gemfile:
|
9
9
|
|
10
|
-
gem "babysitter"
|
10
|
+
gem "babysitter", git: 'git@github.com:lonelyplanet/babysitter.git'
|
11
11
|
|
12
12
|
And then execute:
|
13
13
|
|
@@ -28,32 +28,13 @@ Or install it yourself as:
|
|
28
28
|
|
29
29
|
The default logger does nothing, override if you want to see log output.
|
30
30
|
|
31
|
-
### Amazon Simple Notification Service Integration
|
32
|
-
|
33
|
-
Babysitter can also make use of Amazons Simple Notification Service to provide notifications of when exceptions occur.
|
34
|
-
|
35
|
-
Babysitter.configure do |c|
|
36
|
-
c.enable_simple_notification_service(
|
37
|
-
topic_arn: "my-topic-arn",
|
38
|
-
credentials: -> {
|
39
|
-
access_key_id: "YOUR_ACCESS_KEY_ID",
|
40
|
-
secret_address_key: "YOUR_SECRET_ADDRESS_KEY",
|
41
|
-
}
|
42
|
-
)
|
43
|
-
end
|
44
|
-
|
45
|
-
The credentials should be supplied as a Proc so that they are only requested when they are required. This supports the use of temporary
|
46
|
-
credentials with IAM and prevents any credentials fetched at configuration time having expired at some point later when an error occurs.
|
47
|
-
|
48
31
|
### Monitoring
|
49
32
|
|
50
33
|
monitor = Babysitter.monitor("statsd.bucket.name")
|
51
|
-
monitor.start("Workername: description") do |
|
34
|
+
monitor.start("Workername: description") do |progress|
|
52
35
|
things_to_do.each do |work|
|
53
36
|
do_some work
|
54
|
-
|
55
|
-
tracker.warn(:suspicions,'Something supicious happenedd') if something_bad?
|
56
|
-
tracker.inc("Workername: {{count}} tasks completed", 1, counting: :things_to_do) # report progress here
|
37
|
+
progress.inc("Workername: {{count}} tasks completed", 1, counting: :work_things) # report progress here
|
57
38
|
end
|
58
39
|
end
|
59
40
|
|
@@ -61,15 +42,10 @@ credentials with IAM and prevents any credentials fetched at configuration time
|
|
61
42
|
This will send statistics to StatsD in the supplied bucket name and will generate logs like this:
|
62
43
|
|
63
44
|
|
64
|
-
INFO -- : Start:
|
45
|
+
INFO -- : Start: update.combinations Matcher generating possible combinations
|
65
46
|
INFO -- : Done: 100 combinations generated
|
66
47
|
INFO -- : Rate: 20746.88796680498 combinations per second
|
67
|
-
INFO -- : End:
|
68
|
-
|
69
|
-
Logging statistics will incremented for bucket names
|
70
|
-
|
71
|
-
statsd.bucket.name.badness.errors
|
72
|
-
statsd.bucket.name.suspicions.warnings
|
48
|
+
INFO -- : End: update.combinations
|
73
49
|
|
74
50
|
|
75
51
|
Any exceptions that occur will be logged nicely. Exceptions will abort the process.
|
data/babysitter.gemspec
CHANGED
@@ -6,8 +6,8 @@ require 'babysitter/version'
|
|
6
6
|
Gem::Specification.new do |gem|
|
7
7
|
gem.name = "babysitter"
|
8
8
|
gem.version = Babysitter::VERSION
|
9
|
-
gem.authors = ["Nicolas Overloop", "Paul Grayson", "Andy Roberts"
|
10
|
-
gem.email = ["noverloop@gmail.com", "paul.grayson@lonelyplanet.com", "coder@onesandthrees.com"
|
9
|
+
gem.authors = ["Nicolas Overloop", "Paul Grayson", "Andy Roberts"]
|
10
|
+
gem.email = ["noverloop@gmail.com", "paul.grayson@lonelyplanet.com", "coder@onesandthrees.com"]
|
11
11
|
gem.description = %q{Babysits long-running processes and reports progress or failures}
|
12
12
|
gem.summary = %q{Babysits long-running processes and reports progress or failures}
|
13
13
|
gem.homepage = ""
|
@@ -17,8 +17,6 @@ Gem::Specification.new do |gem|
|
|
17
17
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
18
|
gem.require_paths = ["lib"]
|
19
19
|
gem.add_dependency 'fozzie'
|
20
|
-
gem.add_dependency 'timecop'
|
21
|
-
gem.add_dependency 'aws-sdk'
|
22
20
|
|
23
21
|
gem.add_development_dependency 'awesome_print'
|
24
22
|
gem.add_development_dependency 'rspec'
|
data/lib/babysitter.rb
CHANGED
@@ -4,17 +4,14 @@ require_relative "babysitter/version"
|
|
4
4
|
require_relative "babysitter/null_logger"
|
5
5
|
require_relative "babysitter/configuration"
|
6
6
|
require_relative "babysitter/logging"
|
7
|
-
require_relative "babysitter/
|
8
|
-
require_relative "babysitter/
|
9
|
-
|
10
|
-
require_relative "babysitter/counter"
|
11
|
-
require_relative "babysitter/exception_notifiers"
|
12
|
-
require 'fozzie'
|
7
|
+
require_relative "babysitter/progress_counter"
|
8
|
+
require_relative "babysitter/progress"
|
9
|
+
|
13
10
|
|
14
11
|
module Babysitter
|
15
12
|
|
16
13
|
def self.monitor(*args)
|
17
|
-
|
14
|
+
Progress.new(*args)
|
18
15
|
end
|
19
16
|
|
20
17
|
def self.configuration
|
@@ -29,8 +26,5 @@ module Babysitter
|
|
29
26
|
configuration.logger
|
30
27
|
end
|
31
28
|
|
32
|
-
def self.exception_notifiers
|
33
|
-
configuration.exception_notifiers
|
34
|
-
end
|
35
29
|
|
36
|
-
end
|
30
|
+
end
|
@@ -3,20 +3,11 @@ module Babysitter
|
|
3
3
|
class Configuration
|
4
4
|
|
5
5
|
attr_writer :logger
|
6
|
-
attr_reader :exception_notifiers
|
7
|
-
|
8
|
-
def initialize
|
9
|
-
@exception_notifiers = []
|
10
|
-
end
|
11
6
|
|
12
7
|
def logger
|
13
8
|
@logger ||= NullLogger.new
|
14
9
|
end
|
15
10
|
|
16
|
-
def enable_simple_notification_service(opts = {})
|
17
|
-
@exception_notifiers << ExceptionNotifiers::SimpleNotificationService.new(opts)
|
18
|
-
end
|
19
|
-
|
20
11
|
end
|
21
12
|
|
22
|
-
end
|
13
|
+
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
module Babysitter
|
2
|
-
class
|
2
|
+
class Progress
|
3
3
|
include Logging
|
4
4
|
|
5
5
|
attr_accessor :stat_name
|
@@ -11,21 +11,21 @@ module Babysitter
|
|
11
11
|
def start(msg=nil, log_every=100, &blk)
|
12
12
|
raise ArgumentError, "Stats bucket name must not be blank" if stat_name.nil? or stat_name.empty?
|
13
13
|
log_msg = format_log_message(msg)
|
14
|
-
|
14
|
+
counter = ProgressCounter.new(log_every, stat_name)
|
15
15
|
logger.info "Start: #{log_msg}"
|
16
16
|
|
17
17
|
begin
|
18
18
|
result = Stats.time_to_do stat_name+[:overall] do
|
19
|
-
blk.call(
|
19
|
+
blk.call(counter)
|
20
20
|
end
|
21
21
|
rescue Exception => e
|
22
|
-
|
23
|
-
log_exception_details(log_msg, e)
|
22
|
+
counter.final_report rescue nil
|
23
|
+
log_exception_details( log_msg, e )
|
24
24
|
raise
|
25
25
|
end
|
26
26
|
|
27
|
-
|
28
|
-
|
27
|
+
Stats.gauge stat_name+[counter.counting, :total], counter.count
|
28
|
+
counter.final_report
|
29
29
|
logger.info "End: #{log_msg}"
|
30
30
|
result
|
31
31
|
end
|
@@ -45,13 +45,13 @@ module Babysitter
|
|
45
45
|
stat_name.is_a?(Array) ? stat_name : stat_name.split('.') unless stat_name.nil? or stat_name.empty?
|
46
46
|
end
|
47
47
|
|
48
|
-
def log_exception_details(msg, exception)
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
Babysitter.exception_notifiers.each { |notifier| notifier.notify(exception.class.name, lines.join("\n")) }
|
48
|
+
def log_exception_details( msg, exception )
|
49
|
+
logger.error "Aborting: #{msg} due to exception #{exception.class}: #{exception}"
|
50
|
+
if exception.backtrace
|
51
|
+
exception.backtrace.each { |line| Babysitter.logger.error " #{line}" }
|
52
|
+
end
|
54
53
|
end
|
55
54
|
end
|
56
55
|
|
56
|
+
|
57
57
|
end
|
@@ -1,17 +1,16 @@
|
|
1
1
|
module Babysitter
|
2
|
-
class
|
2
|
+
class ProgressCounter
|
3
3
|
include Logging
|
4
4
|
|
5
|
-
attr_reader :count, :
|
5
|
+
attr_reader :count, :counting, :template, :logged_count, :stat_name
|
6
|
+
attr_accessor :log_every
|
6
7
|
|
7
|
-
|
8
|
-
|
9
|
-
def initialize(log_every, opts)
|
8
|
+
def initialize(log_every, stat_name=nil)
|
10
9
|
@count = 0
|
11
10
|
@logged_count = 0
|
11
|
+
@stat_name = stat_name
|
12
|
+
@counting = :iterations
|
12
13
|
@log_every = log_every
|
13
|
-
@stat_name = opts.delete(:stat_name)
|
14
|
-
@counting = opts.delete(:counting)
|
15
14
|
@timer_start = Time.now
|
16
15
|
end
|
17
16
|
|
@@ -24,12 +23,14 @@ module Babysitter
|
|
24
23
|
log_counter_messsage if log_this_time
|
25
24
|
end
|
26
25
|
|
27
|
-
def
|
28
|
-
count
|
26
|
+
def final_report
|
27
|
+
log_counter_messsage if !(template.nil? or template.empty?) && count != logged_count
|
29
28
|
end
|
30
29
|
|
31
|
-
|
32
|
-
|
30
|
+
private
|
31
|
+
|
32
|
+
def block_number(count)
|
33
|
+
count / @log_every
|
33
34
|
end
|
34
35
|
|
35
36
|
def log_counter_messsage
|
@@ -52,9 +53,5 @@ module Babysitter
|
|
52
53
|
Stats.count stat_name+[counting, :progress], progress unless stat_name.nil?
|
53
54
|
end
|
54
55
|
|
55
|
-
def send_total_stats
|
56
|
-
Stats.gauge stat_name+[counting, :total], count
|
57
|
-
end
|
58
|
-
|
59
56
|
end
|
60
|
-
end
|
57
|
+
end
|
data/lib/babysitter/version.rb
CHANGED
@@ -11,36 +11,7 @@ module Babysitter
|
|
11
11
|
subject.logger.should === null_logger
|
12
12
|
end
|
13
13
|
|
14
|
-
it 'has no exception notifiers' do
|
15
|
-
subject.exception_notifiers.should be_empty
|
16
|
-
end
|
17
|
-
|
18
|
-
end
|
19
|
-
|
20
|
-
describe 'enabling Amazon simple notification service integration' do
|
21
|
-
let (:sns_exception_notifier) { double }
|
22
|
-
let (:valid_params) { {
|
23
|
-
arbritary_key: "some-value",
|
24
|
-
topic_arn: "my-topic-arn"
|
25
|
-
} }
|
26
|
-
|
27
|
-
before :each do
|
28
|
-
Babysitter::ExceptionNotifiers::SimpleNotificationService.stub(:new).and_return(sns_exception_notifier)
|
29
|
-
end
|
30
|
-
|
31
|
-
it 'adds an exception notifier' do
|
32
|
-
subject.enable_simple_notification_service(valid_params)
|
33
|
-
|
34
|
-
subject.exception_notifiers.should_not be_empty
|
35
|
-
subject.exception_notifiers.first.should eql(sns_exception_notifier)
|
36
|
-
end
|
37
|
-
|
38
|
-
it 'configures the exception notifier' do
|
39
|
-
ExceptionNotifiers::SimpleNotificationService.should_receive(:new).with(hash_including(valid_params))
|
40
|
-
|
41
|
-
subject.enable_simple_notification_service(valid_params)
|
42
|
-
end
|
43
14
|
end
|
44
15
|
|
45
16
|
end
|
46
|
-
end
|
17
|
+
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
module Babysitter
|
4
|
-
describe
|
4
|
+
describe Progress do
|
5
5
|
before(:each) do
|
6
6
|
Stats.stub!(:count).with(anything, anything)
|
7
7
|
Stats.stub!(:gauge).with(anything, anything)
|
@@ -9,7 +9,7 @@ module Babysitter
|
|
9
9
|
|
10
10
|
context 'when initialized with a dot separated bucket name' do
|
11
11
|
|
12
|
-
subject{
|
12
|
+
subject{ Progress.new(bucket_name) }
|
13
13
|
let(:bucket_name) { 'my.splendid.bucket.name' }
|
14
14
|
let(:start_block) { Proc.new{ block_result } }
|
15
15
|
let(:block_result) { double('block result').as_null_object }
|
@@ -17,7 +17,7 @@ module Babysitter
|
|
17
17
|
|
18
18
|
describe '#completed' do
|
19
19
|
it 'logs a done message' do
|
20
|
-
|
20
|
+
Progress.any_instance.stub(:logger).and_return(logger)
|
21
21
|
logger.should_receive(:info).with("Done: the completed thing")
|
22
22
|
subject.completed('the completed thing')
|
23
23
|
end
|
@@ -36,26 +36,26 @@ module Babysitter
|
|
36
36
|
end
|
37
37
|
|
38
38
|
it 'calls logger.info with start message' do
|
39
|
-
|
39
|
+
Progress.any_instance.stub(:logger).and_return(logger)
|
40
40
|
logger.should_receive(:info).with("Start: #{bucket_name}")
|
41
41
|
subject.start(&start_block)
|
42
42
|
end
|
43
43
|
|
44
44
|
it 'calls logger.info with end message' do
|
45
|
-
|
45
|
+
Progress.any_instance.stub(:logger).and_return(logger)
|
46
46
|
logger.should_receive(:info).with("End: #{bucket_name}")
|
47
47
|
subject.start(&start_block)
|
48
48
|
end
|
49
49
|
|
50
50
|
context 'when the start method is given a message' do
|
51
51
|
it 'calls logger.info with start message' do
|
52
|
-
|
52
|
+
Progress.any_instance.stub(:logger).and_return(logger)
|
53
53
|
logger.should_receive(:info).with("Start: #{bucket_name} special message")
|
54
54
|
subject.start('special message', &start_block)
|
55
55
|
end
|
56
56
|
|
57
57
|
it 'calls logger.info with end message' do
|
58
|
-
|
58
|
+
Progress.any_instance.stub(:logger).and_return(logger)
|
59
59
|
logger.should_receive(:info).with("End: #{bucket_name} special message")
|
60
60
|
subject.start('special message', &start_block)
|
61
61
|
end
|
@@ -75,7 +75,7 @@ module Babysitter
|
|
75
75
|
end
|
76
76
|
|
77
77
|
it 'calls logger.info with each done message once' do
|
78
|
-
|
78
|
+
ProgressCounter.any_instance.stub(:logger).and_return(logger)
|
79
79
|
[5,10].each { |inc| logger.should_receive(:info).with( "Done: incrementing by #{inc} things").once }
|
80
80
|
subject.start('short message', 5, &start_block_two_increments)
|
81
81
|
end
|
@@ -95,7 +95,7 @@ module Babysitter
|
|
95
95
|
end
|
96
96
|
|
97
97
|
it 'calls logger.info with each done message once' do
|
98
|
-
|
98
|
+
ProgressCounter.any_instance.stub(:logger).and_return(logger)
|
99
99
|
[5,7].each { |inc| logger.should_receive(:info).with( "Done: incrementing by #{inc} things").once }
|
100
100
|
subject.start('short message', 5, &start_block_seven_increments)
|
101
101
|
end
|
@@ -109,120 +109,12 @@ module Babysitter
|
|
109
109
|
end
|
110
110
|
|
111
111
|
it 'calls logger.info with increments 18,27,36,45,54,63' do
|
112
|
-
|
112
|
+
ProgressCounter.any_instance.stub(:logger).and_return(logger)
|
113
113
|
[18,27,36,45,54,63].each { |inc| logger.should_receive(:info).with( "Done: incrementing by #{inc} things").once }
|
114
114
|
subject.start('short message', 10, &start_block_three_increments)
|
115
115
|
end
|
116
116
|
end # context 'when logging every 10th call, and the block increments the counter 7 times, each with a count of 9, and identifies counted objects' do
|
117
117
|
|
118
|
-
context "when the block logs a warning" do
|
119
|
-
let(:start_block_with_warning) do
|
120
|
-
Proc.new do |monitor|
|
121
|
-
monitor.warn(:my_warning_bucket, 'my warning message')
|
122
|
-
end
|
123
|
-
end
|
124
|
-
before(:each) do
|
125
|
-
Babysitter.stub(:logger).and_return(logger)
|
126
|
-
logger.stub!(:warn)
|
127
|
-
Stats.stub!(:increment)
|
128
|
-
end
|
129
|
-
|
130
|
-
it 'calls logger.info with the warning message' do
|
131
|
-
logger.should_receive(:warn).with( "my warning message")
|
132
|
-
subject.start(&start_block_with_warning)
|
133
|
-
end
|
134
|
-
|
135
|
-
it 'calls Stats.count with warning bucket name' do
|
136
|
-
expected_bucket_name = bucket_name.split('.') + [:my_warning_bucket, :warnings]
|
137
|
-
Stats.should_receive(:increment).with(expected_bucket_name)
|
138
|
-
subject.start(&start_block_with_warning)
|
139
|
-
end
|
140
|
-
end
|
141
|
-
|
142
|
-
context "when the block logs an error" do
|
143
|
-
let(:start_block_with_error) do
|
144
|
-
Proc.new do |monitor|
|
145
|
-
monitor.error(:my_error_bucket, 'my error message')
|
146
|
-
end
|
147
|
-
end
|
148
|
-
before(:each) do
|
149
|
-
Babysitter.stub(:logger).and_return(logger)
|
150
|
-
logger.stub!(:error)
|
151
|
-
Stats.stub!(:increment)
|
152
|
-
end
|
153
|
-
|
154
|
-
it 'calls logger.error with the error message' do
|
155
|
-
logger.should_receive(:error).with( "my error message")
|
156
|
-
subject.start(&start_block_with_error)
|
157
|
-
end
|
158
|
-
|
159
|
-
it 'calls Stats.count with error bucket name' do
|
160
|
-
expected_bucket_name = bucket_name.split('.') + [:my_error_bucket, :errors]
|
161
|
-
Stats.should_receive(:increment).with(expected_bucket_name)
|
162
|
-
subject.start(&start_block_with_error)
|
163
|
-
end
|
164
|
-
end
|
165
|
-
|
166
|
-
context 'when the block raises an error' do
|
167
|
-
let(:error) { RuntimeError.new(error_message) }
|
168
|
-
let(:backtrace) { 3.times.map { |i| "Line #{i}"} }
|
169
|
-
let(:error_message) { 'A big fat error' }
|
170
|
-
let(:expected_message) { "Aborting: #{bucket_name} due to exception RuntimeError: #{error_message}" }
|
171
|
-
let(:start_block_with_error) { Proc.new { raise error } }
|
172
|
-
before(:each) do
|
173
|
-
error.stub(:backtrace).and_return(backtrace)
|
174
|
-
Babysitter.stub(:logger).and_return(logger)
|
175
|
-
Babysitter.stub(:exception_notifiers).and_return(2.times.map { double notify: nil })
|
176
|
-
logger.stub!(:error)
|
177
|
-
Stats.stub!(:increment)
|
178
|
-
end
|
179
|
-
|
180
|
-
it 'calls logger.error with the exeption details' do
|
181
|
-
logger.should_receive(:error).with(expected_message)
|
182
|
-
backtrace.each do |line|
|
183
|
-
logger.should_receive(:error).with(/\w*#{line}/)
|
184
|
-
end
|
185
|
-
|
186
|
-
begin
|
187
|
-
subject.start(&start_block_with_error)
|
188
|
-
rescue
|
189
|
-
end
|
190
|
-
end
|
191
|
-
|
192
|
-
it 'calls each exception notifier with the exception details' do
|
193
|
-
message = [expected_message].concat(backtrace).join("\n")
|
194
|
-
|
195
|
-
Babysitter.exception_notifiers.each do |exception_notifier|
|
196
|
-
exception_notifier.should_receive(:notify).with('RuntimeError', message)
|
197
|
-
end
|
198
|
-
|
199
|
-
begin
|
200
|
-
subject.start(&start_block_with_error)
|
201
|
-
rescue
|
202
|
-
end
|
203
|
-
end
|
204
|
-
end
|
205
|
-
|
206
|
-
context 'when the block increments 2 times at intervals of 2 seconds' do
|
207
|
-
let(:start_block_for_timing) do
|
208
|
-
Proc.new do |counter|
|
209
|
-
2.times do
|
210
|
-
Timecop.travel(Time.now+2) # move on 2 seconds
|
211
|
-
counter.inc('doing increment',1)
|
212
|
-
end
|
213
|
-
end
|
214
|
-
end
|
215
|
-
before(:each) { Timecop.travel(Time.now) }
|
216
|
-
after(:each) { Timecop.return }
|
217
|
-
|
218
|
-
it 'calculates a rate close to 0.5 per second' do
|
219
|
-
Counter.any_instance.should_receive(:send_rate_stats) do |rate|
|
220
|
-
rate.should be_within(0.01).of(0.5)
|
221
|
-
end
|
222
|
-
subject.start(&start_block_for_timing)
|
223
|
-
end
|
224
|
-
end
|
225
|
-
|
226
118
|
end
|
227
119
|
|
228
120
|
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,18 +1,17 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: babysitter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Nicolas Overloop
|
9
9
|
- Paul Grayson
|
10
10
|
- Andy Roberts
|
11
|
-
- Mike Wagg
|
12
11
|
autorequire:
|
13
12
|
bindir: bin
|
14
13
|
cert_chain: []
|
15
|
-
date: 2013-
|
14
|
+
date: 2013-01-15 00:00:00.000000000 Z
|
16
15
|
dependencies:
|
17
16
|
- !ruby/object:Gem::Dependency
|
18
17
|
name: fozzie
|
@@ -30,38 +29,6 @@ dependencies:
|
|
30
29
|
- - ! '>='
|
31
30
|
- !ruby/object:Gem::Version
|
32
31
|
version: '0'
|
33
|
-
- !ruby/object:Gem::Dependency
|
34
|
-
name: timecop
|
35
|
-
requirement: !ruby/object:Gem::Requirement
|
36
|
-
none: false
|
37
|
-
requirements:
|
38
|
-
- - ! '>='
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: '0'
|
41
|
-
type: :runtime
|
42
|
-
prerelease: false
|
43
|
-
version_requirements: !ruby/object:Gem::Requirement
|
44
|
-
none: false
|
45
|
-
requirements:
|
46
|
-
- - ! '>='
|
47
|
-
- !ruby/object:Gem::Version
|
48
|
-
version: '0'
|
49
|
-
- !ruby/object:Gem::Dependency
|
50
|
-
name: aws-sdk
|
51
|
-
requirement: !ruby/object:Gem::Requirement
|
52
|
-
none: false
|
53
|
-
requirements:
|
54
|
-
- - ! '>='
|
55
|
-
- !ruby/object:Gem::Version
|
56
|
-
version: '0'
|
57
|
-
type: :runtime
|
58
|
-
prerelease: false
|
59
|
-
version_requirements: !ruby/object:Gem::Requirement
|
60
|
-
none: false
|
61
|
-
requirements:
|
62
|
-
- - ! '>='
|
63
|
-
- !ruby/object:Gem::Version
|
64
|
-
version: '0'
|
65
32
|
- !ruby/object:Gem::Dependency
|
66
33
|
name: awesome_print
|
67
34
|
requirement: !ruby/object:Gem::Requirement
|
@@ -99,7 +66,6 @@ email:
|
|
99
66
|
- noverloop@gmail.com
|
100
67
|
- paul.grayson@lonelyplanet.com
|
101
68
|
- coder@onesandthrees.com
|
102
|
-
- michael@guerillatactics.co.uk
|
103
69
|
executables: []
|
104
70
|
extensions: []
|
105
71
|
extra_rdoc_files: []
|
@@ -112,20 +78,13 @@ files:
|
|
112
78
|
- babysitter.gemspec
|
113
79
|
- lib/babysitter.rb
|
114
80
|
- lib/babysitter/configuration.rb
|
115
|
-
- lib/babysitter/counter.rb
|
116
|
-
- lib/babysitter/exception_notifiers.rb
|
117
|
-
- lib/babysitter/exception_notifiers/simple_notification_service.rb
|
118
|
-
- lib/babysitter/logger_with_stats.rb
|
119
81
|
- lib/babysitter/logging.rb
|
120
|
-
- lib/babysitter/monitor.rb
|
121
82
|
- lib/babysitter/null_logger.rb
|
122
|
-
- lib/babysitter/
|
83
|
+
- lib/babysitter/progress.rb
|
84
|
+
- lib/babysitter/progress_counter.rb
|
123
85
|
- lib/babysitter/version.rb
|
124
86
|
- spec/lib/babysitter/configuration_spec.rb
|
125
|
-
- spec/lib/babysitter/
|
126
|
-
- spec/lib/babysitter/monitor_spec.rb
|
127
|
-
- spec/lib/babysitter/tracker_spec.rb
|
128
|
-
- spec/lib/babysitter_spec.rb
|
87
|
+
- spec/lib/babysitter/progress_spec.rb
|
129
88
|
- spec/spec_helper.rb
|
130
89
|
homepage: ''
|
131
90
|
licenses: []
|
@@ -153,8 +112,5 @@ specification_version: 3
|
|
153
112
|
summary: Babysits long-running processes and reports progress or failures
|
154
113
|
test_files:
|
155
114
|
- spec/lib/babysitter/configuration_spec.rb
|
156
|
-
- spec/lib/babysitter/
|
157
|
-
- spec/lib/babysitter/monitor_spec.rb
|
158
|
-
- spec/lib/babysitter/tracker_spec.rb
|
159
|
-
- spec/lib/babysitter_spec.rb
|
115
|
+
- spec/lib/babysitter/progress_spec.rb
|
160
116
|
- spec/spec_helper.rb
|
@@ -1 +0,0 @@
|
|
1
|
-
require_relative "exception_notifiers/simple_notification_service"
|
@@ -1,42 +0,0 @@
|
|
1
|
-
require 'aws-sdk'
|
2
|
-
|
3
|
-
module Babysitter
|
4
|
-
module ExceptionNotifiers
|
5
|
-
class SimpleNotificationService
|
6
|
-
def initialize(opts = {})
|
7
|
-
@topic_arn = opts.delete(:topic_arn)
|
8
|
-
@get_credentials = opts.delete(:credentials)
|
9
|
-
raise ArgumentError, "topic_arn is required." if @topic_arn.nil?
|
10
|
-
raise ArgumentError, "credentials is required and must be a Proc." if @get_credentials.nil? || !@get_credentials.is_a?(Proc)
|
11
|
-
|
12
|
-
validate_topic
|
13
|
-
end
|
14
|
-
|
15
|
-
def notify(subject, msg)
|
16
|
-
topic.publish(msg, subject: sanitise_subject(subject))
|
17
|
-
end
|
18
|
-
|
19
|
-
private
|
20
|
-
|
21
|
-
def sns
|
22
|
-
AWS::SNS.new(@get_credentials.call)
|
23
|
-
end
|
24
|
-
|
25
|
-
def topic
|
26
|
-
sns.topics[@topic_arn]
|
27
|
-
end
|
28
|
-
|
29
|
-
def sanitise_subject(subject)
|
30
|
-
sanitised = /\s*([^\x00-\x1F]*)/.match(subject)[1]
|
31
|
-
|
32
|
-
return "(no subject)" if sanitised.empty?
|
33
|
-
return sanitised[0..96] + "..." if sanitised.size > 100
|
34
|
-
sanitised
|
35
|
-
end
|
36
|
-
|
37
|
-
def validate_topic
|
38
|
-
topic.display_name
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
@@ -1,43 +0,0 @@
|
|
1
|
-
module Babysitter
|
2
|
-
class LoggerWithStats
|
3
|
-
include Logging
|
4
|
-
|
5
|
-
attr_accessor :stat_name_prefix
|
6
|
-
|
7
|
-
STATS_SUFFIX_BY_METHOD = { warn: :warnings, error: :errors, fatal: :fatals }
|
8
|
-
|
9
|
-
def initialize(stat_name_prefix)
|
10
|
-
@stat_name_prefix = stat_name_prefix
|
11
|
-
end
|
12
|
-
|
13
|
-
def method_missing(meth, *opts)
|
14
|
-
unless %w{ info debug error fatal}.include?(meth.to_s)
|
15
|
-
super
|
16
|
-
return
|
17
|
-
end
|
18
|
-
stats_suffix_from_method(meth).tap{ |suffix| increment(suffix) if suffix }
|
19
|
-
logger.send(meth, *opts)
|
20
|
-
end
|
21
|
-
|
22
|
-
def warn(*opts)
|
23
|
-
increment(stats_suffix_from_method(:warn))
|
24
|
-
logger.warn(*opts)
|
25
|
-
end
|
26
|
-
|
27
|
-
private
|
28
|
-
|
29
|
-
def stats_suffix_from_method(meth)
|
30
|
-
STATS_SUFFIX_BY_METHOD[meth]
|
31
|
-
end
|
32
|
-
|
33
|
-
def increment(stat_name_suffix)
|
34
|
-
Stats.increment full_stat_name(stat_name_suffix)
|
35
|
-
end
|
36
|
-
|
37
|
-
def full_stat_name(stat_name_suffix)
|
38
|
-
stat_name_prefix + [stat_name_suffix]
|
39
|
-
end
|
40
|
-
|
41
|
-
end
|
42
|
-
|
43
|
-
end
|
data/lib/babysitter/tracker.rb
DELETED
@@ -1,52 +0,0 @@
|
|
1
|
-
module Babysitter
|
2
|
-
class Tracker
|
3
|
-
include Logging
|
4
|
-
|
5
|
-
attr_reader :counting, :stat_name, :counter
|
6
|
-
attr_accessor :log_every
|
7
|
-
|
8
|
-
def initialize(log_every, stat_name=nil)
|
9
|
-
@stat_name = stat_name
|
10
|
-
@counting = :iterations
|
11
|
-
@log_every = log_every
|
12
|
-
@counter = Counter.new(log_every, stat_name: stat_name, counting: counting)
|
13
|
-
end
|
14
|
-
|
15
|
-
def inc(*args)
|
16
|
-
counter.inc(*args)
|
17
|
-
end
|
18
|
-
|
19
|
-
def count
|
20
|
-
counter.count
|
21
|
-
end
|
22
|
-
|
23
|
-
def final_report
|
24
|
-
counter.log_counter_messsage if counter.final_report?
|
25
|
-
end
|
26
|
-
|
27
|
-
def warn(topic_name, message)
|
28
|
-
logger_with_stats_for(topic_name).warn(message)
|
29
|
-
end
|
30
|
-
|
31
|
-
def error(topic_name, message)
|
32
|
-
logger_with_stats_for(topic_name).error(message)
|
33
|
-
end
|
34
|
-
|
35
|
-
def send_total_stats
|
36
|
-
counter.send_total_stats
|
37
|
-
end
|
38
|
-
|
39
|
-
def logger_with_stats_for(topic_name)
|
40
|
-
@loggers ||= {}
|
41
|
-
@loggers[topic_name] ||= LoggerWithStats.new(stats_prefix_for_topic(topic_name))
|
42
|
-
end
|
43
|
-
|
44
|
-
private
|
45
|
-
|
46
|
-
def stats_prefix_for_topic(topic_name)
|
47
|
-
stat_name+[topic_name]
|
48
|
-
end
|
49
|
-
|
50
|
-
end
|
51
|
-
|
52
|
-
end
|
@@ -1,105 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
module Babysitter
|
4
|
-
module ExceptionNotifiers
|
5
|
-
describe SimpleNotificationService do
|
6
|
-
subject { SimpleNotificationService.new(valid_opts) }
|
7
|
-
let(:get_credentials_lambda) { -> {
|
8
|
-
{
|
9
|
-
access_key_id: 'an-access-key-id',
|
10
|
-
secret_access_key: 'a-secret-address-key',
|
11
|
-
}
|
12
|
-
} }
|
13
|
-
let(:valid_opts) { {
|
14
|
-
credentials: get_credentials_lambda,
|
15
|
-
topic_arn: 'my-topic-arn'
|
16
|
-
} }
|
17
|
-
let(:sns) { double :sns, topics: { 'my-topic-arn' => topic } }
|
18
|
-
let(:topic) { double :topic, publish: nil, display_name: "A topic" }
|
19
|
-
before :each do
|
20
|
-
AWS::SNS.stub(:new).and_return(sns)
|
21
|
-
end
|
22
|
-
|
23
|
-
it 'requires a topic_arn' do
|
24
|
-
valid_opts.delete :topic_arn
|
25
|
-
-> { subject }.should raise_error(ArgumentError, /topic_arn/)
|
26
|
-
end
|
27
|
-
|
28
|
-
it "requires credentials" do
|
29
|
-
valid_opts.delete :credentials
|
30
|
-
-> { subject }.should raise_error(ArgumentError, /credentials/)
|
31
|
-
end
|
32
|
-
|
33
|
-
it 'requires a block to retrieve AWS credentials' do
|
34
|
-
valid_opts[:credentials] = {}
|
35
|
-
-> { subject }.should raise_error(ArgumentError, /credentials/)
|
36
|
-
end
|
37
|
-
|
38
|
-
it 'uses the options passed to configure the credentials for sns' do
|
39
|
-
AWS::SNS.should_receive(:new).with(get_credentials_lambda.call)
|
40
|
-
subject
|
41
|
-
end
|
42
|
-
|
43
|
-
it 'validates the topic by checking it has a display name' do
|
44
|
-
topic.should_receive(:display_name)
|
45
|
-
subject
|
46
|
-
end
|
47
|
-
|
48
|
-
describe '.notify' do
|
49
|
-
let(:message) { "the message" }
|
50
|
-
let(:notification_subject) { "the subject" }
|
51
|
-
|
52
|
-
it 'again uses the options passed to configure the credentials for sns' do
|
53
|
-
AWS::SNS.should_receive(:new).with(get_credentials_lambda.call).twice
|
54
|
-
subject.notify(notification_subject, message)
|
55
|
-
end
|
56
|
-
|
57
|
-
it 'publishes to the topic specified' do
|
58
|
-
topic.should_receive(:publish)
|
59
|
-
|
60
|
-
subject.notify(notification_subject, message)
|
61
|
-
end
|
62
|
-
|
63
|
-
it 'publishes the message' do
|
64
|
-
topic.should_receive(:publish).with(message, hash_including(subject: notification_subject))
|
65
|
-
|
66
|
-
subject.notify(notification_subject, message)
|
67
|
-
end
|
68
|
-
|
69
|
-
it "shortens the subject to 100 characters if necessary" do
|
70
|
-
shortened_subject = 97.times.map { "x" }.join + "..."
|
71
|
-
original_subject = 101.times.map { "x" }.join
|
72
|
-
|
73
|
-
topic.should_receive(:publish).with(message, hash_including(subject: shortened_subject))
|
74
|
-
|
75
|
-
subject.notify(original_subject, message)
|
76
|
-
end
|
77
|
-
|
78
|
-
it "strips control characters" do
|
79
|
-
expected_subject = "this is the subject"
|
80
|
-
original_subject = "#{expected_subject}"
|
81
|
-
32.times.each { |code| original_subject << code.chr }
|
82
|
-
|
83
|
-
topic.should_receive(:publish).with(message, hash_including(subject: expected_subject))
|
84
|
-
|
85
|
-
subject.notify(original_subject, message)
|
86
|
-
end
|
87
|
-
|
88
|
-
it "strips leading whitespace" do
|
89
|
-
expected_subject = "this is the subject"
|
90
|
-
original_subject = " #{expected_subject}"
|
91
|
-
|
92
|
-
topic.should_receive(:publish).with(message, hash_including(subject: expected_subject))
|
93
|
-
|
94
|
-
subject.notify(original_subject, message)
|
95
|
-
end
|
96
|
-
|
97
|
-
it "handles empty subject" do
|
98
|
-
topic.should_receive(:publish).with(message, hash_including(subject: "(no subject)"))
|
99
|
-
|
100
|
-
subject.notify("", message)
|
101
|
-
end
|
102
|
-
end
|
103
|
-
end
|
104
|
-
end
|
105
|
-
end
|
@@ -1,81 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
module Babysitter
|
4
|
-
describe Tracker do
|
5
|
-
|
6
|
-
subject{ Tracker.new(log_interval, stat_bucket_prefix) }
|
7
|
-
let(:log_interval) { 5 }
|
8
|
-
let(:stat_bucket_prefix) { [:my, :stat, :bucket, :prefix] }
|
9
|
-
let(:logger) { double('boring old vanilla babysitter logger') }
|
10
|
-
before(:each) do
|
11
|
-
Babysitter.stub(:logger).and_return(logger)
|
12
|
-
end
|
13
|
-
|
14
|
-
describe '#logger_with_stats_for' do
|
15
|
-
|
16
|
-
it 'returns the same logger when passed the same symbol twice' do
|
17
|
-
l1 = subject.logger_with_stats_for(:lodgings)
|
18
|
-
l2 = subject.logger_with_stats_for(:lodgings)
|
19
|
-
l1.should be_equal(l2)
|
20
|
-
end
|
21
|
-
|
22
|
-
it 'returns different loggers when passed different symbols' do
|
23
|
-
l = subject.logger_with_stats_for(:lodgings)
|
24
|
-
p = subject.logger_with_stats_for(:places)
|
25
|
-
l.should_not be_equal(p)
|
26
|
-
end
|
27
|
-
|
28
|
-
end # describe '#logger_with_stats' do
|
29
|
-
|
30
|
-
describe 'logger returned by logger_with_stats_for(some_topic)' do
|
31
|
-
subject{ Tracker.new(log_interval, stat_bucket_prefix).logger_with_stats_for(some_topic) }
|
32
|
-
let(:some_topic) { :some_topic }
|
33
|
-
let(:text_of_the_message) {'the message we want in the logs'}
|
34
|
-
|
35
|
-
{ warn: :warnings, error: :errors, fatal: :fatals }.each do |message_type, stats_bucket_suffix|
|
36
|
-
describe "##{message_type}" do
|
37
|
-
before(:each) do
|
38
|
-
logger.stub(message_type)
|
39
|
-
Stats.stub!(:increment)
|
40
|
-
end
|
41
|
-
|
42
|
-
it 'logs the message' do
|
43
|
-
logger.should_receive(message_type).with(text_of_the_message)
|
44
|
-
subject.send(message_type, text_of_the_message)
|
45
|
-
end
|
46
|
-
|
47
|
-
it 'sends the stats' do
|
48
|
-
expected_stats_bucket = stat_bucket_prefix + [some_topic, stats_bucket_suffix]
|
49
|
-
Stats.should_receive(:increment).with(expected_stats_bucket)
|
50
|
-
subject.send(message_type, text_of_the_message)
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
end
|
55
|
-
|
56
|
-
[:info, :debug].each do |message_type|
|
57
|
-
describe "##{message_type}" do
|
58
|
-
before(:each) do
|
59
|
-
logger.stub(message_type)
|
60
|
-
Stats.stub!(:increment)
|
61
|
-
end
|
62
|
-
|
63
|
-
it 'logs the message' do
|
64
|
-
logger.should_receive(message_type).with(text_of_the_message)
|
65
|
-
subject.send(message_type, text_of_the_message)
|
66
|
-
end
|
67
|
-
|
68
|
-
it 'sends no stats' do
|
69
|
-
Stats.should_not_receive(:increment)
|
70
|
-
subject.send(message_type, text_of_the_message)
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
end
|
75
|
-
|
76
|
-
end # describe 'logger returned by logger_with_stats_for(:something)' do
|
77
|
-
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
|
data/spec/lib/babysitter_spec.rb
DELETED
@@ -1,50 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe Babysitter do
|
4
|
-
|
5
|
-
describe '.monitor' do
|
6
|
-
it 'returns an instance of Monitor' do
|
7
|
-
Babysitter.monitor.should be_an_instance_of(Babysitter::Monitor)
|
8
|
-
end
|
9
|
-
end
|
10
|
-
|
11
|
-
describe '.configuration' do
|
12
|
-
it 'returns an instance of Configuration' do
|
13
|
-
Babysitter.configuration.should be_an_instance_of(Babysitter::Configuration)
|
14
|
-
end
|
15
|
-
|
16
|
-
it 'returns the same instance every time' do
|
17
|
-
c = Babysitter.configuration
|
18
|
-
Babysitter.configuration.should eql(c)
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
describe '.configure' do
|
23
|
-
describe 'object yielded to block' do
|
24
|
-
it 'is the unique configuration object' do
|
25
|
-
Babysitter.configure do |c|
|
26
|
-
c.should eql(Babysitter.configuration)
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
describe '.logger' do
|
33
|
-
let(:configured_logger) { double('configured logger').as_null_object }
|
34
|
-
|
35
|
-
it 'returns the logger from the configuration' do
|
36
|
-
Babysitter::Configuration.any_instance.stub(:logger).and_return(configured_logger)
|
37
|
-
Babysitter.logger.should eql(configured_logger)
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
describe '.exception_notifiers' do
|
42
|
-
let(:exception_notifiers) { double('notifiers').as_null_object}
|
43
|
-
|
44
|
-
it 'returns the notifiers from the configuration' do
|
45
|
-
Babysitter::Configuration.any_instance.stub(:exception_notifiers).and_return(exception_notifiers)
|
46
|
-
Babysitter.exception_notifiers.should eql(exception_notifiers)
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
end
|