strobemonkey-god 0.7.13
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/History.txt +289 -0
- data/Manifest.txt +114 -0
- data/README.txt +59 -0
- data/Rakefile +35 -0
- data/bin/god +128 -0
- data/examples/events.god +84 -0
- data/examples/gravatar.god +54 -0
- data/examples/single.god +66 -0
- data/ext/god/extconf.rb +55 -0
- data/ext/god/kqueue_handler.c +123 -0
- data/ext/god/netlink_handler.c +167 -0
- data/init/god +42 -0
- data/lib/god/behavior.rb +52 -0
- data/lib/god/behaviors/clean_pid_file.rb +21 -0
- data/lib/god/behaviors/clean_unix_socket.rb +21 -0
- data/lib/god/behaviors/notify_when_flapping.rb +51 -0
- data/lib/god/cli/command.rb +229 -0
- data/lib/god/cli/run.rb +176 -0
- data/lib/god/cli/version.rb +23 -0
- data/lib/god/condition.rb +96 -0
- data/lib/god/conditions/always.rb +23 -0
- data/lib/god/conditions/complex.rb +86 -0
- data/lib/god/conditions/cpu_usage.rb +80 -0
- data/lib/god/conditions/degrading_lambda.rb +52 -0
- data/lib/god/conditions/disk_usage.rb +27 -0
- data/lib/god/conditions/file_mtime.rb +28 -0
- data/lib/god/conditions/flapping.rb +128 -0
- data/lib/god/conditions/http_response_code.rb +168 -0
- data/lib/god/conditions/lambda.rb +25 -0
- data/lib/god/conditions/memory_usage.rb +82 -0
- data/lib/god/conditions/process_exits.rb +72 -0
- data/lib/god/conditions/process_running.rb +74 -0
- data/lib/god/conditions/tries.rb +44 -0
- data/lib/god/configurable.rb +57 -0
- data/lib/god/contact.rb +106 -0
- data/lib/god/contacts/campfire.rb +82 -0
- data/lib/god/contacts/email.rb +95 -0
- data/lib/god/contacts/jabber.rb +65 -0
- data/lib/god/contacts/twitter.rb +39 -0
- data/lib/god/contacts/webhook.rb +47 -0
- data/lib/god/dependency_graph.rb +41 -0
- data/lib/god/diagnostics.rb +37 -0
- data/lib/god/driver.rb +206 -0
- data/lib/god/errors.rb +24 -0
- data/lib/god/event_handler.rb +111 -0
- data/lib/god/event_handlers/dummy_handler.rb +13 -0
- data/lib/god/event_handlers/kqueue_handler.rb +17 -0
- data/lib/god/event_handlers/netlink_handler.rb +13 -0
- data/lib/god/logger.rb +120 -0
- data/lib/god/metric.rb +59 -0
- data/lib/god/process.rb +341 -0
- data/lib/god/registry.rb +32 -0
- data/lib/god/simple_logger.rb +53 -0
- data/lib/god/socket.rb +96 -0
- data/lib/god/sugar.rb +47 -0
- data/lib/god/system/portable_poller.rb +42 -0
- data/lib/god/system/process.rb +42 -0
- data/lib/god/system/slash_proc_poller.rb +92 -0
- data/lib/god/task.rb +491 -0
- data/lib/god/timeline.rb +25 -0
- data/lib/god/trigger.rb +43 -0
- data/lib/god/watch.rb +183 -0
- data/lib/god.rb +667 -0
- data/test/configs/child_events/child_events.god +44 -0
- data/test/configs/child_events/simple_server.rb +3 -0
- data/test/configs/child_polls/child_polls.god +37 -0
- data/test/configs/child_polls/simple_server.rb +12 -0
- data/test/configs/complex/complex.god +59 -0
- data/test/configs/complex/simple_server.rb +3 -0
- data/test/configs/contact/contact.god +84 -0
- data/test/configs/contact/simple_server.rb +3 -0
- data/test/configs/daemon_events/daemon_events.god +37 -0
- data/test/configs/daemon_events/simple_server.rb +8 -0
- data/test/configs/daemon_events/simple_server_stop.rb +11 -0
- data/test/configs/daemon_polls/daemon_polls.god +17 -0
- data/test/configs/daemon_polls/simple_server.rb +6 -0
- data/test/configs/degrading_lambda/degrading_lambda.god +31 -0
- data/test/configs/degrading_lambda/tcp_server.rb +15 -0
- data/test/configs/matias/matias.god +50 -0
- data/test/configs/real.rb +59 -0
- data/test/configs/running_load/running_load.god +16 -0
- data/test/configs/stress/simple_server.rb +3 -0
- data/test/configs/stress/stress.god +15 -0
- data/test/configs/task/logs/.placeholder +0 -0
- data/test/configs/task/task.god +26 -0
- data/test/configs/test.rb +61 -0
- data/test/helper.rb +151 -0
- data/test/suite.rb +6 -0
- data/test/test_behavior.rb +21 -0
- data/test/test_campfire.rb +41 -0
- data/test/test_condition.rb +50 -0
- data/test/test_conditions_disk_usage.rb +56 -0
- data/test/test_conditions_http_response_code.rb +109 -0
- data/test/test_conditions_process_running.rb +44 -0
- data/test/test_conditions_tries.rb +67 -0
- data/test/test_contact.rb +109 -0
- data/test/test_dependency_graph.rb +62 -0
- data/test/test_driver.rb +11 -0
- data/test/test_email.rb +45 -0
- data/test/test_event_handler.rb +80 -0
- data/test/test_god.rb +598 -0
- data/test/test_handlers_kqueue_handler.rb +16 -0
- data/test/test_logger.rb +63 -0
- data/test/test_metric.rb +72 -0
- data/test/test_process.rb +246 -0
- data/test/test_registry.rb +15 -0
- data/test/test_socket.rb +42 -0
- data/test/test_sugar.rb +42 -0
- data/test/test_system_portable_poller.rb +17 -0
- data/test/test_system_process.rb +30 -0
- data/test/test_task.rb +262 -0
- data/test/test_timeline.rb +37 -0
- data/test/test_trigger.rb +59 -0
- data/test/test_watch.rb +279 -0
- metadata +193 -0
data/lib/god/driver.rb
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
module God
|
|
2
|
+
class TimedEvent
|
|
3
|
+
include Comparable
|
|
4
|
+
|
|
5
|
+
attr_accessor :at
|
|
6
|
+
|
|
7
|
+
# Instantiate a new TimedEvent that will be triggered after the specified delay
|
|
8
|
+
# +delay+ is the number of seconds from now at which to trigger
|
|
9
|
+
#
|
|
10
|
+
# Returns TimedEvent
|
|
11
|
+
def initialize(delay = 0)
|
|
12
|
+
self.at = Time.now + delay
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def due?
|
|
16
|
+
Time.now >= self.at
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def <=>(other)
|
|
20
|
+
self.at <=> other.at
|
|
21
|
+
end
|
|
22
|
+
end # DriverEvent
|
|
23
|
+
|
|
24
|
+
class DriverEvent < TimedEvent
|
|
25
|
+
attr_accessor :condition, :task
|
|
26
|
+
|
|
27
|
+
def initialize(delay, task, condition)
|
|
28
|
+
super delay
|
|
29
|
+
self.task = task
|
|
30
|
+
self.condition = condition
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def handle_event
|
|
34
|
+
@task.handle_poll(@condition)
|
|
35
|
+
end
|
|
36
|
+
end # DriverEvent
|
|
37
|
+
|
|
38
|
+
class DriverOperation < TimedEvent
|
|
39
|
+
attr_accessor :task, :name, :args
|
|
40
|
+
|
|
41
|
+
def initialize(task, name, args)
|
|
42
|
+
super(0)
|
|
43
|
+
self.task = task
|
|
44
|
+
self.name = name
|
|
45
|
+
self.args = args
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Handle the next queued operation that was issued asynchronously
|
|
49
|
+
#
|
|
50
|
+
# Returns nothing
|
|
51
|
+
def handle_event
|
|
52
|
+
@task.send(@name, *@args)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
class DriverEventQueue
|
|
57
|
+
def initialize
|
|
58
|
+
@shutdown = false
|
|
59
|
+
@waiting = []
|
|
60
|
+
@events = []
|
|
61
|
+
@waiting.taint
|
|
62
|
+
@events.taint
|
|
63
|
+
self.taint
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
#
|
|
67
|
+
# Wake any sleeping threads after setting the sentinel
|
|
68
|
+
#
|
|
69
|
+
def shutdown
|
|
70
|
+
@shutdown = true
|
|
71
|
+
begin
|
|
72
|
+
Thread.critical = true
|
|
73
|
+
@waiting.each do |t|
|
|
74
|
+
t.run
|
|
75
|
+
end
|
|
76
|
+
ensure
|
|
77
|
+
Thread.critical = false
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
#
|
|
82
|
+
# Sleep until the queue has something due
|
|
83
|
+
#
|
|
84
|
+
def pop
|
|
85
|
+
begin
|
|
86
|
+
while (Thread.critical = true; @events.empty? or !@events.first.due?)
|
|
87
|
+
@waiting.push Thread.current
|
|
88
|
+
if @events.empty?
|
|
89
|
+
raise ThreadError, "queue empty" if @shutdown
|
|
90
|
+
Thread.stop
|
|
91
|
+
else
|
|
92
|
+
Thread.critical = false
|
|
93
|
+
sleep @events.first.at - Time.now
|
|
94
|
+
Thread.critical = true
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
@events.shift
|
|
98
|
+
ensure
|
|
99
|
+
Thread.critical = false
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
alias shift pop
|
|
104
|
+
alias deq pop
|
|
105
|
+
|
|
106
|
+
#
|
|
107
|
+
# Add an event to the queue, wake any waiters if what we added needs to
|
|
108
|
+
# happen sooner than the next pending event
|
|
109
|
+
#
|
|
110
|
+
def push(event)
|
|
111
|
+
Thread.critical = true
|
|
112
|
+
@events << event
|
|
113
|
+
@events.sort!
|
|
114
|
+
begin
|
|
115
|
+
t = @waiting.shift if @events.first == event
|
|
116
|
+
t.wakeup if t
|
|
117
|
+
rescue ThreadError
|
|
118
|
+
retry
|
|
119
|
+
ensure
|
|
120
|
+
Thread.critical = false
|
|
121
|
+
end
|
|
122
|
+
begin
|
|
123
|
+
t.run if t
|
|
124
|
+
rescue ThreadError
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
alias << push
|
|
129
|
+
alias enq push
|
|
130
|
+
|
|
131
|
+
def empty?
|
|
132
|
+
@que.empty?
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def clear
|
|
136
|
+
@events.clear
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def length
|
|
140
|
+
@events.length
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
alias size length
|
|
144
|
+
|
|
145
|
+
def num_waiting
|
|
146
|
+
@waiting.size
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
class Driver
|
|
152
|
+
attr_reader :thread
|
|
153
|
+
|
|
154
|
+
# Instantiate a new Driver and start the scheduler loop to handle events
|
|
155
|
+
# +task+ is the Task this Driver belongs to
|
|
156
|
+
#
|
|
157
|
+
# Returns Driver
|
|
158
|
+
def initialize(task)
|
|
159
|
+
@task = task
|
|
160
|
+
@events = God::DriverEventQueue.new
|
|
161
|
+
|
|
162
|
+
@thread = Thread.new do
|
|
163
|
+
loop do
|
|
164
|
+
begin
|
|
165
|
+
@events.pop.handle_event
|
|
166
|
+
rescue ThreadError => e
|
|
167
|
+
# queue is empty
|
|
168
|
+
break
|
|
169
|
+
rescue Exception => e
|
|
170
|
+
message = format("Unhandled exception in driver loop - (%s): %s\n%s",
|
|
171
|
+
e.class, e.message, e.backtrace.join("\n"))
|
|
172
|
+
applog(nil, :fatal, message)
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# Clear all events for this Driver
|
|
179
|
+
#
|
|
180
|
+
# Returns nothing
|
|
181
|
+
def clear_events
|
|
182
|
+
@events.clear
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# Queue an asynchronous message
|
|
186
|
+
# +name+ is the Symbol name of the operation
|
|
187
|
+
# +args+ is an optional Array of arguments
|
|
188
|
+
#
|
|
189
|
+
# Returns nothing
|
|
190
|
+
def message(name, args = [])
|
|
191
|
+
@events.push(DriverOperation.new(@task, name, args))
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
# Create and schedule a new DriverEvent
|
|
195
|
+
# +condition+ is the Condition
|
|
196
|
+
# +delay+ is the number of seconds to delay (default: interval defined in condition)
|
|
197
|
+
#
|
|
198
|
+
# Returns nothing
|
|
199
|
+
def schedule(condition, delay = condition.interval)
|
|
200
|
+
applog(nil, :debug, "driver schedule #{condition} in #{delay} seconds")
|
|
201
|
+
|
|
202
|
+
@events.push(DriverEvent.new(delay, @task, condition))
|
|
203
|
+
end
|
|
204
|
+
end # Driver
|
|
205
|
+
|
|
206
|
+
end # God
|
data/lib/god/errors.rb
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module God
|
|
2
|
+
|
|
3
|
+
class AbstractMethodNotOverriddenError < StandardError
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
class NoSuchWatchError < StandardError
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
class NoSuchConditionError < StandardError
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
class NoSuchBehaviorError < StandardError
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
class NoSuchContactError < StandardError
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
class InvalidCommandError < StandardError
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
class EventRegistrationFailedError < StandardError
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
end
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
module God
|
|
2
|
+
class EventHandler
|
|
3
|
+
@@actions = {}
|
|
4
|
+
@@handler = nil
|
|
5
|
+
@@loaded = false
|
|
6
|
+
|
|
7
|
+
def self.loaded?
|
|
8
|
+
@@loaded
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def self.event_system
|
|
12
|
+
@@handler::EVENT_SYSTEM
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def self.load
|
|
16
|
+
begin
|
|
17
|
+
case RUBY_PLATFORM
|
|
18
|
+
when /darwin/i, /bsd/i
|
|
19
|
+
require 'god/event_handlers/kqueue_handler'
|
|
20
|
+
@@handler = KQueueHandler
|
|
21
|
+
when /linux/i
|
|
22
|
+
require 'god/event_handlers/netlink_handler'
|
|
23
|
+
@@handler = NetlinkHandler
|
|
24
|
+
else
|
|
25
|
+
raise NotImplementedError, "Platform not supported for EventHandler"
|
|
26
|
+
end
|
|
27
|
+
@@loaded = true
|
|
28
|
+
rescue Exception
|
|
29
|
+
require 'god/event_handlers/dummy_handler'
|
|
30
|
+
@@handler = DummyHandler
|
|
31
|
+
@@loaded = false
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def self.register(pid, event, &block)
|
|
36
|
+
@@actions[pid] ||= {}
|
|
37
|
+
@@actions[pid][event] = block
|
|
38
|
+
@@handler.register_process(pid, @@actions[pid].keys)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def self.deregister(pid, event=nil)
|
|
42
|
+
if watching_pid? pid
|
|
43
|
+
running = ::Process.kill(0, pid.to_i) rescue false
|
|
44
|
+
if event.nil?
|
|
45
|
+
@@actions.delete(pid)
|
|
46
|
+
@@handler.register_process(pid, []) if running
|
|
47
|
+
else
|
|
48
|
+
@@actions[pid].delete(event)
|
|
49
|
+
@@handler.register_process(pid, @@actions[pid].keys) if running
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def self.call(pid, event, extra_data = {})
|
|
55
|
+
@@actions[pid][event].call(extra_data) if watching_pid?(pid) && @@actions[pid][event]
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def self.watching_pid?(pid)
|
|
59
|
+
@@actions[pid]
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def self.start
|
|
63
|
+
Thread.new do
|
|
64
|
+
loop do
|
|
65
|
+
begin
|
|
66
|
+
@@handler.handle_events
|
|
67
|
+
rescue Exception => e
|
|
68
|
+
message = format("Unhandled exception (%s): %s\n%s",
|
|
69
|
+
e.class, e.message, e.backtrace.join("\n"))
|
|
70
|
+
applog(nil, :fatal, message)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# do a real test to make sure events are working properly
|
|
76
|
+
@@loaded = self.operational?
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def self.operational?
|
|
80
|
+
com = [false]
|
|
81
|
+
|
|
82
|
+
Thread.new do
|
|
83
|
+
begin
|
|
84
|
+
event_system = God::EventHandler.event_system
|
|
85
|
+
|
|
86
|
+
pid = fork do
|
|
87
|
+
loop { sleep(1) }
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
self.register(pid, :proc_exit) do
|
|
91
|
+
com[0] = true
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
::Process.kill('KILL', pid)
|
|
95
|
+
|
|
96
|
+
sleep(0.1)
|
|
97
|
+
|
|
98
|
+
self.deregister(pid, :proc_exit) rescue nil
|
|
99
|
+
rescue => e
|
|
100
|
+
puts e.message
|
|
101
|
+
puts e.backtrace.join("\n")
|
|
102
|
+
end
|
|
103
|
+
end.join
|
|
104
|
+
|
|
105
|
+
sleep(0.1)
|
|
106
|
+
|
|
107
|
+
com.first
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
end
|
|
111
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
require 'kqueue_handler_ext'
|
|
2
|
+
|
|
3
|
+
module God
|
|
4
|
+
class KQueueHandler
|
|
5
|
+
EVENT_SYSTEM = "kqueue"
|
|
6
|
+
|
|
7
|
+
def self.register_process(pid, events)
|
|
8
|
+
monitor_process(pid, events_mask(events))
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def self.events_mask(events)
|
|
12
|
+
events.inject(0) do |mask, event|
|
|
13
|
+
mask |= event_mask(event)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
require 'netlink_handler_ext'
|
|
2
|
+
|
|
3
|
+
module God
|
|
4
|
+
class NetlinkHandler
|
|
5
|
+
EVENT_SYSTEM = "netlink"
|
|
6
|
+
|
|
7
|
+
def self.register_process(pid, events)
|
|
8
|
+
# netlink doesn't need to do this
|
|
9
|
+
# it just reads from the eventhandler actions to see if the pid
|
|
10
|
+
# matches the list we're looking for -- Kev
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
data/lib/god/logger.rb
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
module God
|
|
2
|
+
|
|
3
|
+
class Logger < SimpleLogger
|
|
4
|
+
SYSLOG_EQUIVALENTS = {:fatal => :crit,
|
|
5
|
+
:error => :err,
|
|
6
|
+
:warn => :debug,
|
|
7
|
+
:info => :debug,
|
|
8
|
+
:debug => :debug}
|
|
9
|
+
|
|
10
|
+
attr_accessor :logs
|
|
11
|
+
|
|
12
|
+
class << self
|
|
13
|
+
attr_accessor :syslog
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
self.syslog ||= true
|
|
17
|
+
|
|
18
|
+
# Instantiate a new Logger object
|
|
19
|
+
def initialize
|
|
20
|
+
super($stdout)
|
|
21
|
+
self.logs = {}
|
|
22
|
+
@mutex = Mutex.new
|
|
23
|
+
@capture = nil
|
|
24
|
+
@templogio = StringIO.new
|
|
25
|
+
@templog = SimpleLogger.new(@templogio)
|
|
26
|
+
@templog.level = Logger::INFO
|
|
27
|
+
load_syslog
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# If Logger.syslog is true then attempt to load the syslog bindings. If syslog
|
|
31
|
+
# cannot be loaded, then set Logger.syslog to false and continue.
|
|
32
|
+
#
|
|
33
|
+
# Returns nothing
|
|
34
|
+
def load_syslog
|
|
35
|
+
return unless Logger.syslog
|
|
36
|
+
|
|
37
|
+
begin
|
|
38
|
+
require 'syslog'
|
|
39
|
+
|
|
40
|
+
# Ensure that Syslog is open
|
|
41
|
+
begin
|
|
42
|
+
Syslog.open('god')
|
|
43
|
+
rescue RuntimeError
|
|
44
|
+
Syslog.reopen('god')
|
|
45
|
+
end
|
|
46
|
+
rescue Exception
|
|
47
|
+
Logger.syslog = false
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Log a message
|
|
52
|
+
# +watch+ is the String name of the Watch (may be nil if not Watch is applicable)
|
|
53
|
+
# +level+ is the log level [:debug|:info|:warn|:error|:fatal]
|
|
54
|
+
# +text+ is the String message
|
|
55
|
+
#
|
|
56
|
+
# Returns nothing
|
|
57
|
+
def log(watch, level, text)
|
|
58
|
+
# initialize watch log if necessary
|
|
59
|
+
self.logs[watch.name] ||= Timeline.new(God::LOG_BUFFER_SIZE_DEFAULT) if watch
|
|
60
|
+
|
|
61
|
+
# push onto capture and timeline for the given watch
|
|
62
|
+
@templogio.truncate(0)
|
|
63
|
+
@templogio.rewind
|
|
64
|
+
@templog.send(level, text % [])
|
|
65
|
+
@mutex.synchronize do
|
|
66
|
+
@capture.puts(@templogio.string.dup) if @capture
|
|
67
|
+
self.logs[watch.name] << [Time.now, @templogio.string.dup] if watch
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# send to regular logger
|
|
71
|
+
self.send(level, text % [])
|
|
72
|
+
|
|
73
|
+
# send to syslog
|
|
74
|
+
Syslog.send(SYSLOG_EQUIVALENTS[level], text) if Logger.syslog
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Get all log output for a given Watch since a certain Time.
|
|
78
|
+
# +watch_name+ is the String name of the Watch
|
|
79
|
+
# +since+ is the Time since which to fetch log lines
|
|
80
|
+
#
|
|
81
|
+
# Returns String
|
|
82
|
+
def watch_log_since(watch_name, since)
|
|
83
|
+
# initialize watch log if necessary
|
|
84
|
+
self.logs[watch_name] ||= Timeline.new(God::LOG_BUFFER_SIZE_DEFAULT)
|
|
85
|
+
|
|
86
|
+
# get and join lines since given time
|
|
87
|
+
@mutex.synchronize do
|
|
88
|
+
self.logs[watch_name].select do |x|
|
|
89
|
+
x.first > since
|
|
90
|
+
end.map do |x|
|
|
91
|
+
x[1]
|
|
92
|
+
end.join
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# private
|
|
97
|
+
|
|
98
|
+
# Enable capturing of log
|
|
99
|
+
#
|
|
100
|
+
# Returns nothing
|
|
101
|
+
def start_capture
|
|
102
|
+
@mutex.synchronize do
|
|
103
|
+
@capture = StringIO.new
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# Disable capturing of log and return what was captured since
|
|
108
|
+
# capturing was enabled with Logger#start_capture
|
|
109
|
+
#
|
|
110
|
+
# Returns String
|
|
111
|
+
def finish_capture
|
|
112
|
+
@mutex.synchronize do
|
|
113
|
+
cap = @capture.string
|
|
114
|
+
@capture = nil
|
|
115
|
+
cap
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
end
|
data/lib/god/metric.rb
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
module God
|
|
2
|
+
|
|
3
|
+
class Metric
|
|
4
|
+
attr_accessor :watch, :destination, :conditions
|
|
5
|
+
|
|
6
|
+
def initialize(watch, destination = nil)
|
|
7
|
+
self.watch = watch
|
|
8
|
+
self.destination = destination
|
|
9
|
+
self.conditions = []
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# Instantiate a Condition of type +kind+ and pass it into the optional
|
|
13
|
+
# block. Attributes of the condition must be set in the config file
|
|
14
|
+
def condition(kind)
|
|
15
|
+
# create the condition
|
|
16
|
+
begin
|
|
17
|
+
c = Condition.generate(kind, self.watch)
|
|
18
|
+
rescue NoSuchConditionError => e
|
|
19
|
+
abort e.message
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# send to block so config can set attributes
|
|
23
|
+
yield(c) if block_given?
|
|
24
|
+
|
|
25
|
+
# call prepare on the condition
|
|
26
|
+
c.prepare
|
|
27
|
+
|
|
28
|
+
# test generic and specific validity
|
|
29
|
+
unless Condition.valid?(c) && c.valid?
|
|
30
|
+
abort "Exiting on invalid condition"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# inherit interval from watch if no poll condition specific interval was set
|
|
34
|
+
if c.kind_of?(PollCondition) && !c.interval
|
|
35
|
+
if self.watch.interval
|
|
36
|
+
c.interval = self.watch.interval
|
|
37
|
+
else
|
|
38
|
+
abort "No interval set for Condition '#{c.class.name}' in Watch '#{self.watch.name}', and no default Watch interval from which to inherit"
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# remember
|
|
43
|
+
self.conditions << c
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def enable
|
|
47
|
+
self.conditions.each do |c|
|
|
48
|
+
self.watch.attach(c)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def disable
|
|
53
|
+
self.conditions.each do |c|
|
|
54
|
+
self.watch.detach(c)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
end
|