sonar_connector 0.8.5
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/LICENSE +20 -0
- data/README.rdoc +18 -0
- data/Rakefile +41 -0
- data/VERSION +1 -0
- data/bin/sonar-connector +69 -0
- data/config/config.example.json +82 -0
- data/lib/sonar_connector.rb +40 -0
- data/lib/sonar_connector/commands/command.rb +21 -0
- data/lib/sonar_connector/commands/commit_seppuku_command.rb +15 -0
- data/lib/sonar_connector/commands/increment_status_value_command.rb +14 -0
- data/lib/sonar_connector/commands/send_admin_email_command.rb +12 -0
- data/lib/sonar_connector/commands/update_disk_usage_command.rb +13 -0
- data/lib/sonar_connector/commands/update_status_command.rb +16 -0
- data/lib/sonar_connector/config.rb +166 -0
- data/lib/sonar_connector/connectors/base.rb +243 -0
- data/lib/sonar_connector/connectors/dummy_connector.rb +17 -0
- data/lib/sonar_connector/connectors/seppuku_connector.rb +26 -0
- data/lib/sonar_connector/consumer.rb +94 -0
- data/lib/sonar_connector/controller.rb +164 -0
- data/lib/sonar_connector/emailer.rb +16 -0
- data/lib/sonar_connector/rspec/spec_helper.rb +61 -0
- data/lib/sonar_connector/status.rb +43 -0
- data/lib/sonar_connector/utils.rb +39 -0
- data/script/console +10 -0
- data/spec/sonar_connector/commands/command_spec.rb +34 -0
- data/spec/sonar_connector/commands/commit_seppuku_command_spec.rb +25 -0
- data/spec/sonar_connector/commands/increment_status_value_command_spec.rb +25 -0
- data/spec/sonar_connector/commands/send_admin_email_command_spec.rb +14 -0
- data/spec/sonar_connector/commands/update_disk_usage_command_spec.rb +21 -0
- data/spec/sonar_connector/commands/update_status_command_spec.rb +24 -0
- data/spec/sonar_connector/config_spec.rb +93 -0
- data/spec/sonar_connector/connectors/base_spec.rb +207 -0
- data/spec/sonar_connector/connectors/dummy_connector_spec.rb +22 -0
- data/spec/sonar_connector/connectors/seppuku_connector_spec.rb +37 -0
- data/spec/sonar_connector/consumer_spec.rb +116 -0
- data/spec/sonar_connector/controller_spec.rb +46 -0
- data/spec/sonar_connector/emailer_spec.rb +36 -0
- data/spec/sonar_connector/status_spec.rb +78 -0
- data/spec/sonar_connector/utils_spec.rb +62 -0
- data/spec/spec.opts +2 -0
- data/spec/spec_helper.rb +6 -0
- metadata +235 -0
@@ -0,0 +1,243 @@
|
|
1
|
+
module Sonar
|
2
|
+
module Connector
|
3
|
+
class Base
|
4
|
+
|
5
|
+
# every connector has a unique name
|
6
|
+
attr_reader :name
|
7
|
+
|
8
|
+
# this connector
|
9
|
+
attr_reader :connector
|
10
|
+
|
11
|
+
# Connector-specific config hash
|
12
|
+
attr_reader :raw_config
|
13
|
+
|
14
|
+
# each connector instance has a working dir for its state and files
|
15
|
+
attr_reader :connector_dir
|
16
|
+
|
17
|
+
# logger instance
|
18
|
+
attr_reader :log
|
19
|
+
|
20
|
+
# state hash that is serialized and persisted to disk every cycle of the run loop
|
21
|
+
attr_reader :state
|
22
|
+
|
23
|
+
# repeat delay which is waited out on each cycle of the run loop
|
24
|
+
attr_reader :repeat_delay
|
25
|
+
|
26
|
+
# central command queue for sending messages back to the controller
|
27
|
+
attr_reader :queue
|
28
|
+
|
29
|
+
# run loop flag
|
30
|
+
attr_reader :run
|
31
|
+
|
32
|
+
# filestore for whole connector
|
33
|
+
attr_reader :connector_filestore
|
34
|
+
|
35
|
+
# filestore for an action run
|
36
|
+
attr_reader :filestore
|
37
|
+
|
38
|
+
# Array of associated connectors that provide source data via the file system
|
39
|
+
attr_reader :source_connectors
|
40
|
+
|
41
|
+
def initialize(connector_config, base_config)
|
42
|
+
@base_config = base_config
|
43
|
+
@raw_config = connector_config
|
44
|
+
|
45
|
+
@name = connector_config["name"]
|
46
|
+
@connector = self
|
47
|
+
|
48
|
+
# Create STDOUT logger and inherit the logger settings from the base controller config
|
49
|
+
@log_file = File.join base_config.log_dir, "connector_#{@name}.log"
|
50
|
+
@log = Sonar::Connector::Utils.stdout_logger base_config
|
51
|
+
|
52
|
+
# every connector instance must set the repeat delay
|
53
|
+
raise InvalidConfig.new("Connector '#{@name}': repeat_delay is missing or blank") if connector_config["repeat_delay"].blank?
|
54
|
+
@repeat_delay = connector_config["repeat_delay"].to_i
|
55
|
+
raise InvalidConfig.new("Connector '#{@name}': repeat_delay must be >= 1 second") if @repeat_delay < 1
|
56
|
+
|
57
|
+
@connector_dir = File.join(base_config.connectors_dir, @name)
|
58
|
+
FileUtils.mkdir_p(@connector_dir)
|
59
|
+
@state_file = File.join(@connector_dir, "state.yml")
|
60
|
+
|
61
|
+
# empty state hash which will get written to by parse, and then potentially over-written by load_state
|
62
|
+
@state = {}
|
63
|
+
|
64
|
+
@connector_filestore = Sonar::Connector::FileStore.new(@connector_dir,
|
65
|
+
"#{@name}_filestore",
|
66
|
+
[:working, :error, :complete, :actions],
|
67
|
+
:logger=>@log)
|
68
|
+
|
69
|
+
parse connector_config
|
70
|
+
load_state
|
71
|
+
|
72
|
+
@run = true
|
73
|
+
end
|
74
|
+
|
75
|
+
def prepare(queue)
|
76
|
+
@queue = queue
|
77
|
+
switch_to_log_file
|
78
|
+
|
79
|
+
cleanup_old_action_filestores # in case we were interrupted mid-action
|
80
|
+
cleanup # before we begin
|
81
|
+
end
|
82
|
+
|
83
|
+
# Logging defaults to use STDOUT. After initialization we need to switch the
|
84
|
+
# logger to use an output file.
|
85
|
+
def switch_to_log_file
|
86
|
+
@log = Sonar::Connector::Utils.disk_logger(log_file, base_config)
|
87
|
+
@connector_filestore.logger = @log if @connector_filestore
|
88
|
+
end
|
89
|
+
|
90
|
+
# Load the state hash from YAML file
|
91
|
+
def load_state
|
92
|
+
@state.merge! read_state
|
93
|
+
end
|
94
|
+
|
95
|
+
# Read state file
|
96
|
+
def read_state
|
97
|
+
s = {}
|
98
|
+
if File.exist?(state_file)
|
99
|
+
ds = YAML.load_file state_file
|
100
|
+
raise "State file did not contain a serialised hash." unless ds.is_a?(Hash)
|
101
|
+
s = ds # only return the parsed value if it is actually a hash
|
102
|
+
end
|
103
|
+
rescue Exception => e
|
104
|
+
log.error "Error loading #{state_file} so it was ignored. Original error: #{e.message}\n" + e.backtrace.join("\n")
|
105
|
+
ensure
|
106
|
+
return s
|
107
|
+
end
|
108
|
+
|
109
|
+
# Save the state hash to a YAML file
|
110
|
+
def save_state
|
111
|
+
make_dir
|
112
|
+
File.open(state_file, "w"){|f| f << state.to_yaml }
|
113
|
+
end
|
114
|
+
|
115
|
+
# Cleanup routine after connector shutdown
|
116
|
+
def cleanup
|
117
|
+
end
|
118
|
+
|
119
|
+
# the main run loop that every connector executes indefinitely
|
120
|
+
# until Thread.raise is called on this instance.
|
121
|
+
def start
|
122
|
+
begin
|
123
|
+
run_loop
|
124
|
+
|
125
|
+
@run = false
|
126
|
+
cleanup
|
127
|
+
true
|
128
|
+
rescue Exception=>e
|
129
|
+
log.error([e.class.to_s, e.message, *e.backtrace].join("\n"))
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def run_loop
|
134
|
+
while run
|
135
|
+
begin
|
136
|
+
run_once
|
137
|
+
sleep_for repeat_delay
|
138
|
+
rescue ThreadTerminator
|
139
|
+
break
|
140
|
+
rescue Exception => e
|
141
|
+
log.error ["Connector '#{name} raised an unhandled exception:",
|
142
|
+
e.class.to_s,
|
143
|
+
e.message,
|
144
|
+
*e.backtrace].join("\n")
|
145
|
+
log.info "Connector blew up with an exception - waiting 5 seconds before retrying."
|
146
|
+
queue << Sonar::Connector::UpdateStatusCommand.new(connector, 'last_action', Sonar::Connector::ACTION_FAILED)
|
147
|
+
sleep_for 5
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def run_once
|
153
|
+
log.info "beginning action"
|
154
|
+
|
155
|
+
with_action_filestore do
|
156
|
+
action
|
157
|
+
|
158
|
+
save_state
|
159
|
+
log.info "finished action,saved state"
|
160
|
+
log.info "working: #{filestore.count(:working)}, error: #{filestore.count(:error)}, complete: #{filestore.count(:complete)}"
|
161
|
+
|
162
|
+
queue << Sonar::Connector::UpdateStatusCommand.new(connector, 'last_action', Sonar::Connector::ACTION_OK)
|
163
|
+
queue << Sonar::Connector::UpdateDiskUsageCommand.new(connector)
|
164
|
+
queue << Sonar::Connector::UpdateStatusCommand.new(connector, 'working_count', filestore.count(:working))
|
165
|
+
queue << Sonar::Connector::UpdateStatusCommand.new(connector, 'error_count', filestore.count(:error))
|
166
|
+
queue << Sonar::Connector::UpdateStatusCommand.new(connector, 'complete_count', filestore.count(:complete))
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
# Connector subclasses can overload the parse method.
|
171
|
+
def parse(config)
|
172
|
+
log.warn "Method #parse called on connector base class. Connector #{name} should define #parse method."
|
173
|
+
end
|
174
|
+
|
175
|
+
def to_s
|
176
|
+
"#{self.class} '#{name}'"
|
177
|
+
end
|
178
|
+
alias :inspect :to_s
|
179
|
+
|
180
|
+
private
|
181
|
+
|
182
|
+
attr_reader :state_file, :base_config, :log_file
|
183
|
+
attr_writer :source_connectors
|
184
|
+
|
185
|
+
def sleep_for(seconds=0)
|
186
|
+
sleep seconds
|
187
|
+
end
|
188
|
+
|
189
|
+
def make_dir
|
190
|
+
FileUtils.mkdir_p(@connector_dir) unless File.directory?(@connector_dir)
|
191
|
+
end
|
192
|
+
|
193
|
+
def with_action_filestore
|
194
|
+
fs = create_action_filestore
|
195
|
+
begin
|
196
|
+
initialize_action_filestore(fs)
|
197
|
+
@filestore = fs
|
198
|
+
yield
|
199
|
+
ensure
|
200
|
+
@filestore = nil
|
201
|
+
finalize_action_filestore(fs)
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
def create_action_filestore
|
206
|
+
now = Time.new
|
207
|
+
fs_name = now.strftime("action_%Y%m%d_%H%M%S_") + UUIDTools::UUID.timestamp_create.to_s.gsub('-','_')
|
208
|
+
action_fs_root = connector_filestore.area_path(:actions)
|
209
|
+
fs=FileStore.new(action_fs_root, fs_name, [:working, :error, :complete], :logger=>@log)
|
210
|
+
log.info("action_filestore path: #{fs.filestore_path}")
|
211
|
+
fs
|
212
|
+
end
|
213
|
+
|
214
|
+
def initialize_action_filestore(fs)
|
215
|
+
# grab any unfinished work for this action
|
216
|
+
connector_filestore.flip(:working, fs, :working)
|
217
|
+
fs.scrub!(:working)
|
218
|
+
end
|
219
|
+
|
220
|
+
def finalize_action_filestore(fs)
|
221
|
+
[:complete, :error].each do |area|
|
222
|
+
fs.scrub!(area)
|
223
|
+
fs.flip(area, connector_filestore, area, false) # flip with uuid folders
|
224
|
+
end
|
225
|
+
fs.scrub!(:working)
|
226
|
+
fs.flip(:working, connector_filestore, :working) # flip without folders
|
227
|
+
fs.destroy!
|
228
|
+
end
|
229
|
+
|
230
|
+
def cleanup_old_action_filestores
|
231
|
+
actionfs_root = connector_filestore.area_path(:actions)
|
232
|
+
|
233
|
+
Dir.foreach(actionfs_root) do |fs_name|
|
234
|
+
fs_path = File.join(actionfs_root, fs_name)
|
235
|
+
if File.directory?(fs_path) && FileStore.valid_filestore_name?(fs_name)
|
236
|
+
fs = FileStore.new(actionfs_root, fs_name, [:working, :error, :complete], :logger=>@log)
|
237
|
+
finalize_action_filestore(fs)
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Sonar
|
2
|
+
module Connector
|
3
|
+
|
4
|
+
##
|
5
|
+
# Dummy connector type. Does nothing except report back to the controller.
|
6
|
+
class DummyConnector < Sonar::Connector::Base
|
7
|
+
|
8
|
+
def parse(config)
|
9
|
+
end
|
10
|
+
|
11
|
+
def action
|
12
|
+
log.debug "#{name} mumbled incoherently to itself."
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Sonar
|
2
|
+
module Connector
|
3
|
+
|
4
|
+
# Suicide connector.Submits a command to cause the death of the entire connector framework.
|
5
|
+
# The connector will then get restarted by god or by the Windows service wrapper.
|
6
|
+
class SeppukuConnector < Sonar::Connector::Base
|
7
|
+
|
8
|
+
attr_accessor :run_count
|
9
|
+
|
10
|
+
def parse(config)
|
11
|
+
@run_count = 0
|
12
|
+
end
|
13
|
+
|
14
|
+
def action
|
15
|
+
|
16
|
+
if @run_count > 0
|
17
|
+
log.info "切腹! #{name} committing honourable suicide and terminating the connector service."
|
18
|
+
queue << Sonar::Connector::CommitSeppukuCommand.new
|
19
|
+
end
|
20
|
+
|
21
|
+
@run_count += 1
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
module Sonar
|
2
|
+
module Connector
|
3
|
+
|
4
|
+
# Command execution context, whereby commands can be injected with consumer context
|
5
|
+
# variables for the logger and status objects.
|
6
|
+
class ExecutionContext
|
7
|
+
attr_reader :log
|
8
|
+
attr_reader :status
|
9
|
+
attr_reader :controller
|
10
|
+
|
11
|
+
def initialize(params)
|
12
|
+
@log = params[:log]
|
13
|
+
@status = params[:status]
|
14
|
+
|
15
|
+
# pass the controller all the way from the consumer initialisation into the command execution context,
|
16
|
+
# so that we can call privileged methods such as shutdown.
|
17
|
+
@controller = params[:controller]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
##
|
23
|
+
# Listens to thread message queue and processes messages.
|
24
|
+
class Consumer
|
25
|
+
|
26
|
+
attr_reader :base_config
|
27
|
+
attr_reader :queue
|
28
|
+
attr_reader :controller
|
29
|
+
attr_reader :status
|
30
|
+
attr_reader :log
|
31
|
+
attr_reader :run
|
32
|
+
|
33
|
+
def initialize(controller, base_config)
|
34
|
+
@controller = controller
|
35
|
+
@base_config = base_config
|
36
|
+
|
37
|
+
# Consumer holds the status object because changes
|
38
|
+
# to status should be centrally moderated.
|
39
|
+
@status = Sonar::Connector::Status.new(@base_config)
|
40
|
+
|
41
|
+
# Creat logger and inherit the logger settings from the base controller config
|
42
|
+
@log_file = File.join(base_config.log_dir, "consumer.log")
|
43
|
+
@log = Sonar::Connector::Utils.stdout_logger base_config
|
44
|
+
|
45
|
+
@run = true
|
46
|
+
end
|
47
|
+
|
48
|
+
# It's kinda evil to be passing in the controller here. The better option is to
|
49
|
+
# refactor the consumer to be part of the controller.
|
50
|
+
def prepare(queue)
|
51
|
+
@queue = queue
|
52
|
+
switch_to_log_file
|
53
|
+
end
|
54
|
+
|
55
|
+
def switch_to_log_file
|
56
|
+
FileUtils.mkdir_p(base_config.log_dir) unless File.directory?(base_config.log_dir)
|
57
|
+
@log = Sonar::Connector::Utils.disk_logger(@log_file, base_config)
|
58
|
+
end
|
59
|
+
|
60
|
+
def cleanup
|
61
|
+
log.info "Shut down consumer"
|
62
|
+
log.close
|
63
|
+
end
|
64
|
+
|
65
|
+
##
|
66
|
+
# Main loop to watch the command queue and process commands.
|
67
|
+
def watch
|
68
|
+
while run
|
69
|
+
begin
|
70
|
+
run_once
|
71
|
+
rescue ThreadTerminator
|
72
|
+
break
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
@run = false
|
77
|
+
cleanup
|
78
|
+
true
|
79
|
+
end
|
80
|
+
|
81
|
+
def run_once
|
82
|
+
begin
|
83
|
+
command = queue.pop
|
84
|
+
command.execute ExecutionContext.new(:log=>log, :status=>status, :controller=>controller)
|
85
|
+
rescue ThreadTerminator => e
|
86
|
+
raise
|
87
|
+
rescue Exception => e
|
88
|
+
log.error ["Command #{command.class} raised an unhandled exception: ",
|
89
|
+
e.class.to_s, e.message, *e.backtrace].join('\n')
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,164 @@
|
|
1
|
+
module Sonar
|
2
|
+
module Connector
|
3
|
+
|
4
|
+
class ThreadTerminator < Exception; end
|
5
|
+
|
6
|
+
class Controller
|
7
|
+
|
8
|
+
DEFAULT_CONFIG_FILENAME = File.join("config", "config.json")
|
9
|
+
|
10
|
+
##
|
11
|
+
# single command queue for threads to communicate with the controller
|
12
|
+
attr_reader :queue
|
13
|
+
|
14
|
+
##
|
15
|
+
# array of instantiated connector instances
|
16
|
+
attr_reader :connectors
|
17
|
+
|
18
|
+
##
|
19
|
+
# instance of Sonar::Connector::Consumer
|
20
|
+
attr_reader :consumer
|
21
|
+
|
22
|
+
##
|
23
|
+
# instance of Sonar::Connector::Config
|
24
|
+
attr_reader :config
|
25
|
+
|
26
|
+
##
|
27
|
+
# instance of Sonar::Connector::Status
|
28
|
+
attr_reader :status
|
29
|
+
|
30
|
+
##
|
31
|
+
# controller logger
|
32
|
+
attr_reader :log
|
33
|
+
|
34
|
+
##
|
35
|
+
# array of threads
|
36
|
+
attr_reader :threads
|
37
|
+
|
38
|
+
##
|
39
|
+
# Parse the config file and create instances of each connector,
|
40
|
+
# parsing their config in turn.
|
41
|
+
def initialize(config_filename = DEFAULT_CONFIG_FILENAME)
|
42
|
+
@config = Sonar::Connector::Config.load config_filename
|
43
|
+
@log = Sonar::Connector::Utils.stdout_logger @config
|
44
|
+
|
45
|
+
@connectors = @config.connectors
|
46
|
+
@consumer = Sonar::Connector::Consumer.new(self, @config)
|
47
|
+
|
48
|
+
@threads = []
|
49
|
+
|
50
|
+
@queue = Queue.new
|
51
|
+
|
52
|
+
create_startup_dirs_and_files
|
53
|
+
rescue Sonar::Connector::InvalidConfig => e
|
54
|
+
$stderr << ([e.class.to_s, e.message, *e.backtrace].join("\n")) << "\n"
|
55
|
+
raise RuntimeError, "Invalid configuration in #{config_filename}: \n #{e.message}"
|
56
|
+
end
|
57
|
+
|
58
|
+
def switch_to_log_file
|
59
|
+
@log = Sonar::Connector::Utils.disk_logger(config.controller_log_file, config)
|
60
|
+
end
|
61
|
+
|
62
|
+
def start
|
63
|
+
prepare_connector
|
64
|
+
start_threads
|
65
|
+
|
66
|
+
# let the controlling thread go into an endless sleep
|
67
|
+
puts "Ctrl-C to stop."
|
68
|
+
|
69
|
+
# Standardize shutdown via ctrl-c and SIGTERM (from god)
|
70
|
+
trap "SIGINT", shutdown_lambda
|
71
|
+
trap "SIGTERM", shutdown_lambda
|
72
|
+
|
73
|
+
endless_sleep
|
74
|
+
end
|
75
|
+
|
76
|
+
# prepare the connector, start an IRB console, but don't start any threads
|
77
|
+
def start_console
|
78
|
+
prepare_connector
|
79
|
+
# make the Controller globally visible
|
80
|
+
Connector.const_set("CONTROLLER", self)
|
81
|
+
|
82
|
+
require 'irb'
|
83
|
+
IRB.start
|
84
|
+
end
|
85
|
+
|
86
|
+
def prepare_connector
|
87
|
+
switch_to_log_file
|
88
|
+
log_startup_params
|
89
|
+
|
90
|
+
connectors.each do |connector|
|
91
|
+
log.info "preparing connector '#{connector.name}'"
|
92
|
+
connector.prepare(queue)
|
93
|
+
end
|
94
|
+
|
95
|
+
log.info "preparing message queue consumer"
|
96
|
+
consumer.prepare(queue)
|
97
|
+
end
|
98
|
+
|
99
|
+
##
|
100
|
+
# Main framework loop. Fire up one thread per connector,
|
101
|
+
# plus the message queue consumer. Then wait for quit signal.
|
102
|
+
def start_threads
|
103
|
+
# fire up the connector threads
|
104
|
+
connectors.each do |connector|
|
105
|
+
log.info "starting connector '#{connector.name}'"
|
106
|
+
threads << Thread.new { connector.start }
|
107
|
+
end
|
108
|
+
|
109
|
+
log.info "starting the message queue consumer"
|
110
|
+
threads << Thread.new{ consumer.watch }
|
111
|
+
end
|
112
|
+
|
113
|
+
|
114
|
+
def shutdown_lambda
|
115
|
+
lambda do
|
116
|
+
puts "\nGiving threads 10 seconds to shut down..."
|
117
|
+
threads.each{|t| t.raise(ThreadTerminator)}
|
118
|
+
begin
|
119
|
+
Timeout::timeout(10) {
|
120
|
+
threads.map(&:join)
|
121
|
+
}
|
122
|
+
rescue Timeout::Error
|
123
|
+
puts "...couldn't stop all threads cleanly."
|
124
|
+
log.info "Could not cleanly terminate all threads."
|
125
|
+
log.close
|
126
|
+
exit(1)
|
127
|
+
rescue ThreadTerminator
|
128
|
+
# ignore it, since it's come from one of the recently-nuked threads.
|
129
|
+
rescue Exception => e
|
130
|
+
log.debug ["Caught unhandled exception: ",
|
131
|
+
e.class.to_s,
|
132
|
+
e.message,
|
133
|
+
*e.backtrace].join("\n")
|
134
|
+
end
|
135
|
+
|
136
|
+
puts "...exited cleanly."
|
137
|
+
log.info "Terminated all threads cleanly."
|
138
|
+
log.close
|
139
|
+
exit(0)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
private
|
144
|
+
|
145
|
+
def log_startup_params
|
146
|
+
log.info "Startup: base directory is #{config.base_dir}"
|
147
|
+
log.info "Startup: logging directory is #{config.log_dir}"
|
148
|
+
log.info "Startup: log level is " + config.send(:raw_config)['log_level']
|
149
|
+
log.info "Startup: controller logging to #{config.controller_log_file}"
|
150
|
+
end
|
151
|
+
|
152
|
+
def create_startup_dirs_and_files
|
153
|
+
FileUtils.mkdir_p(config.base_dir) unless File.directory?(config.base_dir)
|
154
|
+
FileUtils.mkdir_p(config.log_dir) unless File.directory?(config.log_dir)
|
155
|
+
FileUtils.touch config.controller_log_file
|
156
|
+
end
|
157
|
+
|
158
|
+
def endless_sleep
|
159
|
+
sleep
|
160
|
+
end
|
161
|
+
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|