droid 0.9.0
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/bin/conf +31 -0
- data/lib/droid.rb +531 -0
- data/lib/droid/sync.rb +0 -0
- data/lib/heroku_droid.rb +86 -0
- data/lib/local_stats.rb +143 -0
- data/lib/memcache_cluster.rb +127 -0
- data/lib/stats.rb +30 -0
- data/lib/utilization.rb +90 -0
- data/speaker/droid_speaker.rb +79 -0
- data/speaker/speak +33 -0
- data/speaker/test.rb +58 -0
- data/test/base.rb +43 -0
- data/test/droid_test.rb +53 -0
- data/test/heroku_droid_test.rb +42 -0
- data/test/wait_for_port_test.rb +23 -0
- data/vendor/logger_client/Rakefile +53 -0
- data/vendor/logger_client/init.rb +1 -0
- data/vendor/logger_client/lib/logger_client.rb +210 -0
- data/vendor/logger_client/test.rb +18 -0
- metadata +115 -0
@@ -0,0 +1,79 @@
|
|
1
|
+
$LOAD_PATH.unshift File.dirname(__FILE__) + '/../vendor/json_pure-1.1.3/lib'
|
2
|
+
require 'json'
|
3
|
+
require 'drb'
|
4
|
+
require 'timeout'
|
5
|
+
|
6
|
+
class DroidSpeaker
|
7
|
+
|
8
|
+
def self.drb_connect(port)
|
9
|
+
DRb.start_service()
|
10
|
+
@drb = DRbObject.new(nil,"druby://localhost:#{port}")
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.drb_cast(topic, payload, &p)
|
14
|
+
@drb.publish(topic, payload, &p)
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.drb_call(topic, payload)
|
18
|
+
## this is the best stupid trick I could think of the force blocking
|
19
|
+
lock = Thread.new { sleep 9999 }
|
20
|
+
@r = nil
|
21
|
+
@drb.publish(topic, payload) do |result|
|
22
|
+
@r = result
|
23
|
+
lock.kill
|
24
|
+
end
|
25
|
+
lock.join
|
26
|
+
@r
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.send(topic, payload, &p)
|
30
|
+
i = new
|
31
|
+
i.send(topic, payload, &p)
|
32
|
+
i.flush
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.start
|
36
|
+
i = new
|
37
|
+
yield(i)
|
38
|
+
i.flush
|
39
|
+
end
|
40
|
+
|
41
|
+
def initialize
|
42
|
+
@message = nil
|
43
|
+
@callback = nil
|
44
|
+
end
|
45
|
+
|
46
|
+
def send(topic, payload, &blk)
|
47
|
+
if defined?(Log) and event_id = Log.default_options[:event_id]
|
48
|
+
payload.merge!(:event_hash => event_id)
|
49
|
+
end
|
50
|
+
|
51
|
+
attrs = { :topic => topic, :payload => payload }
|
52
|
+
if blk # this message should be replied
|
53
|
+
attrs.merge!(:has_reply => true)
|
54
|
+
@callback = blk
|
55
|
+
end
|
56
|
+
@message = attrs
|
57
|
+
end
|
58
|
+
|
59
|
+
class MessageError < RuntimeError; end
|
60
|
+
|
61
|
+
def flush
|
62
|
+
out = ''
|
63
|
+
IO.popen("#{File.dirname(__FILE__)}/../speaker/speak 2>/dev/null", 'w+') do |io|
|
64
|
+
io.puts @message.to_json
|
65
|
+
out = io.read
|
66
|
+
end
|
67
|
+
begin
|
68
|
+
response = JSON.parse(out)
|
69
|
+
response.delete('event_hash')
|
70
|
+
@callback.call(response) if @callback
|
71
|
+
rescue JSON::ParserError
|
72
|
+
if out.include?('Timeout::Error')
|
73
|
+
raise Timeout::Error, "Timed out while waiting for a response for #{@message[:topic]}"
|
74
|
+
else
|
75
|
+
raise MessageError, out
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
data/speaker/speak
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
begin
|
4
|
+
$:.unshift '/usr/local/droid/lib'
|
5
|
+
require 'heroku_droid'
|
6
|
+
|
7
|
+
msg = JSON.load(gets)
|
8
|
+
|
9
|
+
Timeout.timeout(10) do
|
10
|
+
HerokuDroid.new('Droid Speaker', :standard_topics => false) do |droid|
|
11
|
+
if msg['has_reply']
|
12
|
+
@event_hash = msg['payload']['event_hash'] ||= Droid::Basic.new(droid).event_hash
|
13
|
+
droid.listen4('event.error') do |d|
|
14
|
+
if d.headers[:event_hash] == @event_hash
|
15
|
+
puts "Message #{msg['topic']} failed: see event #{@event_hash}"
|
16
|
+
Droid.stop_safe
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
droid.publish(msg['topic'], msg['payload']) do |d|
|
21
|
+
puts d.params.to_json
|
22
|
+
Droid.stop_safe
|
23
|
+
end
|
24
|
+
else
|
25
|
+
droid.publish(msg['topic'], msg['payload'])
|
26
|
+
puts({}.to_json)
|
27
|
+
Droid.stop_safe
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
rescue Object => e
|
32
|
+
puts "#{e.class}: #{e.message}\n\t#{e.backtrace.join("\n\t")}"
|
33
|
+
end
|
data/speaker/test.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
Thread.new do
|
2
|
+
require File.dirname(__FILE__) + '/../lib/heroku_droid'
|
3
|
+
HerokuDroid.new('Droid speaker test') do |droid|
|
4
|
+
droid.listen4('speaker.test') do |msg|
|
5
|
+
if msg['marco'] == 1
|
6
|
+
puts "Got 'marco', returning 'polo'..."
|
7
|
+
msg.reply('polo' => 1)
|
8
|
+
else
|
9
|
+
puts "Bad payload"
|
10
|
+
msg.reply('error' => 1)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
droid.listen4('speaker.raise') do |msg|
|
14
|
+
raise "droid raised exception"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
sleep 2
|
20
|
+
|
21
|
+
require File.dirname(__FILE__) + '/droid_speaker'
|
22
|
+
|
23
|
+
puts "\nTest 1: Sending simple message"
|
24
|
+
DroidSpeaker.send('fire.and.forget', 'val' => 1)
|
25
|
+
puts "\tSuccess!"
|
26
|
+
|
27
|
+
puts "\nTest 2: Sending and receiving response"
|
28
|
+
DroidSpeaker.send('speaker.test', 'marco' => 1) do |r|
|
29
|
+
if r['polo'] == 1
|
30
|
+
puts "\tSuccess!"
|
31
|
+
else
|
32
|
+
puts "\tFailed, result: #{r.inspect}"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
puts "\nTest 3: Exceptions"
|
37
|
+
begin
|
38
|
+
DroidSpeaker.send('speaker.raise', {}) do |r|
|
39
|
+
puts "\tFailed, expected exception"
|
40
|
+
end
|
41
|
+
rescue DroidSpeaker::MessageError => e
|
42
|
+
if e.message =~ /event \w+/
|
43
|
+
puts "\tSuccess!"
|
44
|
+
else
|
45
|
+
puts "\tFailed, droid speaker raised #{e.message}"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
puts "\nTest 4: Timeout"
|
50
|
+
begin
|
51
|
+
DroidSpeaker.send('dont.reply', {}) do |r|
|
52
|
+
puts "\tFailed, didnt expect a response"
|
53
|
+
end
|
54
|
+
rescue Timeout::Error
|
55
|
+
puts "\tSuccess!"
|
56
|
+
rescue Exception => e
|
57
|
+
puts "\tFailed, expected Timeout::Error, got #{e.class.name}"
|
58
|
+
end
|
data/test/base.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'rush'
|
2
|
+
|
3
|
+
def publish(message, payload={})
|
4
|
+
Droid.new('Sender', RabbitmqSetup.credentials) do |d|
|
5
|
+
d.publish(message, payload)
|
6
|
+
Droid.stop_safe
|
7
|
+
end
|
8
|
+
sleep(0.1)
|
9
|
+
end
|
10
|
+
|
11
|
+
$exchange = nil
|
12
|
+
$mutex = Mutex.new
|
13
|
+
def exchange
|
14
|
+
$mutex.synchronize { $exchange }
|
15
|
+
end
|
16
|
+
def save_in_exchange(v)
|
17
|
+
$mutex.synchronize { $exchange = v }
|
18
|
+
end
|
19
|
+
|
20
|
+
module RabbitmqSetup
|
21
|
+
extend self
|
22
|
+
|
23
|
+
def credentials
|
24
|
+
{
|
25
|
+
:vhost => '/test',
|
26
|
+
:host => 'localhost',
|
27
|
+
:user => 'test',
|
28
|
+
:pass => 'test'
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
def run_rabbitmqctl(command)
|
33
|
+
Rush::Box.new.bash "rabbitmqctl #{command}"
|
34
|
+
rescue Rush::BashFailed => e
|
35
|
+
raise unless e.message.match(/already_exists/)
|
36
|
+
end
|
37
|
+
|
38
|
+
def run
|
39
|
+
run_rabbitmqctl("add_vhost #{credentials[:vhost]}")
|
40
|
+
run_rabbitmqctl("add_user #{credentials[:user]} #{credentials[:pass]}")
|
41
|
+
run_rabbitmqctl("map_user_vhost #{credentials[:user]} #{credentials[:vhost]}")
|
42
|
+
end
|
43
|
+
end
|
data/test/droid_test.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../lib/droid'
|
2
|
+
require File.dirname(__FILE__) + '/base'
|
3
|
+
|
4
|
+
require 'rubygems'
|
5
|
+
require 'thread'
|
6
|
+
require 'bacon'
|
7
|
+
|
8
|
+
RabbitmqSetup.run
|
9
|
+
|
10
|
+
Bacon.summary_on_exit
|
11
|
+
|
12
|
+
Thread.new do
|
13
|
+
Droid.new('Test Droid', RabbitmqSetup.credentials) do |d|
|
14
|
+
d.on_error do |msg, e|
|
15
|
+
save_in_exchange(e)
|
16
|
+
end
|
17
|
+
|
18
|
+
d.before_filter do |msg|
|
19
|
+
if to_save = msg[:save_in_before_filter]
|
20
|
+
save_in_exchange(to_save)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
d.listen4('save.msg') do |msg|
|
25
|
+
save_in_exchange(msg)
|
26
|
+
end
|
27
|
+
|
28
|
+
d.listen4('do.nothing') do |msg|
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
sleep(2) # wait until droid is up
|
34
|
+
describe Droid do
|
35
|
+
before do
|
36
|
+
save_in_exchange(nil)
|
37
|
+
end
|
38
|
+
|
39
|
+
it "sends and receives simple messages" do
|
40
|
+
publish('save.msg')
|
41
|
+
exchange.should.not == nil
|
42
|
+
end
|
43
|
+
|
44
|
+
it "sends messages with params" do
|
45
|
+
publish('save.msg', :test => 1)
|
46
|
+
exchange[:test].should == 1
|
47
|
+
end
|
48
|
+
|
49
|
+
it "accepts before filters" do
|
50
|
+
publish('do.nothing', :save_in_before_filter => 42)
|
51
|
+
exchange.should == 42
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../lib/heroku_droid'
|
2
|
+
require File.dirname(__FILE__) + '/base'
|
3
|
+
|
4
|
+
require 'rubygems'
|
5
|
+
require 'thread'
|
6
|
+
require 'bacon'
|
7
|
+
|
8
|
+
Bacon.summary_on_exit
|
9
|
+
|
10
|
+
Thread.new do
|
11
|
+
HerokuDroid.new('Test Heroku Droid') do |d|
|
12
|
+
d.stats do
|
13
|
+
"some stats"
|
14
|
+
end
|
15
|
+
|
16
|
+
d.listen4('pong') do |msg|
|
17
|
+
save_in_exchange('worked') if msg['notes'] == 'some stats'
|
18
|
+
end
|
19
|
+
|
20
|
+
d.listen4('save.msg') do |msg|
|
21
|
+
save_in_exchange(msg)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
sleep(2) # wait until droid is up
|
27
|
+
describe HerokuDroid do
|
28
|
+
before do
|
29
|
+
save_in_exchange(nil)
|
30
|
+
end
|
31
|
+
|
32
|
+
it "sends and receives simple messages" do
|
33
|
+
publish('save.msg')
|
34
|
+
exchange.should.not == nil
|
35
|
+
end
|
36
|
+
|
37
|
+
it "responds to ping with a pong message and instances/droid stats filled in" do
|
38
|
+
publish('ping')
|
39
|
+
sleep 3
|
40
|
+
exchange.should == 'worked'
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../lib/droid'
|
2
|
+
require File.dirname(__FILE__) + '/base'
|
3
|
+
|
4
|
+
require 'rubygems'
|
5
|
+
require 'thread'
|
6
|
+
require 'bacon'
|
7
|
+
|
8
|
+
Bacon.summary_on_exit
|
9
|
+
|
10
|
+
Thread.new do
|
11
|
+
sleep 2
|
12
|
+
TCPServer.new('localhost', 20_001).accept.close
|
13
|
+
end
|
14
|
+
|
15
|
+
describe Droid do
|
16
|
+
it "waits for the rabbitmq server to come up before opening the amqp connection" do
|
17
|
+
start = Time.now
|
18
|
+
Droid.wait_for_tcp_port('localhost', 20_001)
|
19
|
+
finish = Time.now
|
20
|
+
(finish - start).should > 1
|
21
|
+
(finish - start).should < 3
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'spec/rake/spectask'
|
3
|
+
|
4
|
+
desc "Run all specs"
|
5
|
+
Spec::Rake::SpecTask.new('spec') do |t|
|
6
|
+
t.spec_opts = ['--colour --format progress --loadby mtime --reverse']
|
7
|
+
t.spec_files = FileList['spec/*_spec.rb']
|
8
|
+
end
|
9
|
+
|
10
|
+
task :default => :spec
|
11
|
+
|
12
|
+
###
|
13
|
+
|
14
|
+
require 'rake'
|
15
|
+
require 'rake/testtask'
|
16
|
+
require 'rake/clean'
|
17
|
+
require 'rake/gempackagetask'
|
18
|
+
require 'rake/rdoctask'
|
19
|
+
require 'fileutils'
|
20
|
+
|
21
|
+
version = "0.1"
|
22
|
+
name = "logger_client"
|
23
|
+
|
24
|
+
spec = Gem::Specification.new do |s|
|
25
|
+
s.name = name
|
26
|
+
s.version = version
|
27
|
+
s.summary = "Client for Heroku logger"
|
28
|
+
s.author = "Pedro Belo"
|
29
|
+
s.email = "pedro@heroku.com"
|
30
|
+
|
31
|
+
s.platform = Gem::Platform::RUBY
|
32
|
+
s.has_rdoc = false
|
33
|
+
|
34
|
+
s.files = %w(Rakefile init.rb) + Dir.glob("{lib,spec}/**/*")
|
35
|
+
|
36
|
+
s.require_path = "lib"
|
37
|
+
|
38
|
+
s.add_dependency('rest-client', '>=0.6.2')
|
39
|
+
end
|
40
|
+
|
41
|
+
Rake::GemPackageTask.new(spec) do |p|
|
42
|
+
p.need_tar = true if RUBY_PLATFORM !~ /mswin/
|
43
|
+
end
|
44
|
+
|
45
|
+
task :install => [ :package ] do
|
46
|
+
sh %{sudo gem install pkg/#{name}-#{version}.gem}
|
47
|
+
end
|
48
|
+
|
49
|
+
task :uninstall => [ :clean ] do
|
50
|
+
sh %{sudo gem uninstall #{name}}
|
51
|
+
end
|
52
|
+
|
53
|
+
CLEAN.include [ 'pkg', '*.gem', '.config' ]
|
@@ -0,0 +1 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/lib/logger_client'
|
@@ -0,0 +1,210 @@
|
|
1
|
+
# Logger Client
|
2
|
+
# Use as a gem or Rails plugin. Example configuration:
|
3
|
+
#
|
4
|
+
# Log.configure do |config|
|
5
|
+
# config.component = 'core'
|
6
|
+
# config.instance = 'userapps.123'
|
7
|
+
# config.failsafe = :file
|
8
|
+
# end
|
9
|
+
|
10
|
+
require 'rubygems'
|
11
|
+
require 'time'
|
12
|
+
require 'syslog'
|
13
|
+
|
14
|
+
module Log; end
|
15
|
+
|
16
|
+
class Log::InvalidConfiguration < RuntimeError
|
17
|
+
def message
|
18
|
+
"Invalid component. Configure with Log.configure { |c| c.component = 'myComponent' }"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class Log::Config
|
23
|
+
def initialize
|
24
|
+
@contents = { :console_log => true }
|
25
|
+
end
|
26
|
+
|
27
|
+
def method_missing(method, value)
|
28
|
+
@contents[method.to_s.gsub(/=$/, '').to_sym] = value
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_hash
|
32
|
+
@contents
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
module Log
|
37
|
+
extend self
|
38
|
+
|
39
|
+
unless defined? SyslogConvertion
|
40
|
+
SyslogConvertion = {
|
41
|
+
'error' => 3,
|
42
|
+
'warning' => 4,
|
43
|
+
'notice' => 5,
|
44
|
+
'debug' => 7,
|
45
|
+
}
|
46
|
+
end
|
47
|
+
|
48
|
+
def debug(msg, options={})
|
49
|
+
log msg, options.merge(:level => 'debug')
|
50
|
+
end
|
51
|
+
|
52
|
+
def notice(msg, options={})
|
53
|
+
log msg, options.merge(:level => 'notice')
|
54
|
+
end
|
55
|
+
|
56
|
+
def warning(msg, options={})
|
57
|
+
log msg, options.merge(:level => 'warning')
|
58
|
+
end
|
59
|
+
|
60
|
+
def error(msg, options={})
|
61
|
+
# Fake an exception for error messages with no exception object
|
62
|
+
# so that we get a backtrace.
|
63
|
+
if options[:exception].nil?
|
64
|
+
begin
|
65
|
+
raise StandardError, msg
|
66
|
+
rescue => error
|
67
|
+
error.backtrace.shift
|
68
|
+
options[:exception] = error
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
log msg, options.merge(:level => 'error')
|
73
|
+
end
|
74
|
+
|
75
|
+
##########
|
76
|
+
|
77
|
+
def log(msg, options={})
|
78
|
+
console_log(msg)
|
79
|
+
syslog(msg, options)
|
80
|
+
rescue InvalidConfiguration => e
|
81
|
+
raise e
|
82
|
+
end
|
83
|
+
|
84
|
+
def default_error(e)
|
85
|
+
# avoid backtrace in /usr or vendor if possible
|
86
|
+
system, app = e.backtrace.partition { |b| b =~ /(^\/usr\/|vendor)/ }
|
87
|
+
reordered_backtrace = app + system
|
88
|
+
|
89
|
+
# avoid "/" as the method name (we want the controller action)
|
90
|
+
row = 0
|
91
|
+
row = 1 if reordered_backtrace[row].match(/in `\/'$/)
|
92
|
+
|
93
|
+
# get file and method name
|
94
|
+
begin
|
95
|
+
file, method = reordered_backtrace[row].match(/(.*):in `(.*)'$/)[1..2]
|
96
|
+
file.gsub!(/.*\//, '')
|
97
|
+
self.log "#{e.class} in #{file} #{method}: #{e.message}", :exception => e, :level => 'error'
|
98
|
+
rescue
|
99
|
+
self.log "#{e.class} in #{e.backtrace.first}: #{e.message}", :exception => e, :level => 'error'
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def web_error(args)
|
104
|
+
e = args[:exception]
|
105
|
+
summary = "#{e.class} processing #{args[:url]}"
|
106
|
+
|
107
|
+
body = []
|
108
|
+
body << "\tURL: #{args[:url]}"
|
109
|
+
body << "\tParams: #{args[:params].inspect}"
|
110
|
+
body << "\tUser: #{args[:user]}" if args[:user]
|
111
|
+
body << "\tBacktrace:"
|
112
|
+
body << "\t\t#{e.class} (#{e.message}):"
|
113
|
+
body += e.backtrace.map { |l| "\t\t#{l}" }
|
114
|
+
body = body.join("\n")
|
115
|
+
|
116
|
+
log "#{summary}\n#{body}", :level => 'error'
|
117
|
+
end
|
118
|
+
|
119
|
+
def exception(e)
|
120
|
+
msg = "Exception #{e.class} -> #{e.message}\n"
|
121
|
+
msg += filtered_backtrace(e.backtrace)
|
122
|
+
STDERR.puts msg
|
123
|
+
Log.error e.message, :exception => e
|
124
|
+
end
|
125
|
+
|
126
|
+
def filtered_backtrace(backtrace)
|
127
|
+
backtrace.select do |line|
|
128
|
+
!line.match(/^\/usr/)
|
129
|
+
end.map do |line|
|
130
|
+
" #{line}\n"
|
131
|
+
end.join
|
132
|
+
end
|
133
|
+
|
134
|
+
def event(name, options = {})
|
135
|
+
console_log("EVENT: #{name} begins")
|
136
|
+
result = yield
|
137
|
+
console_log("EVENT: #{name} complete")
|
138
|
+
result
|
139
|
+
end
|
140
|
+
|
141
|
+
def context(options)
|
142
|
+
prev_options = default_options.dup
|
143
|
+
default_options.merge!(options)
|
144
|
+
yield
|
145
|
+
@@default_options = prev_options
|
146
|
+
end
|
147
|
+
|
148
|
+
def configure
|
149
|
+
config = Config.new
|
150
|
+
yield(config)
|
151
|
+
set_default_options(config.to_hash)
|
152
|
+
end
|
153
|
+
|
154
|
+
def set_default_options(options)
|
155
|
+
default_options.merge!(options)
|
156
|
+
end
|
157
|
+
|
158
|
+
def default_options
|
159
|
+
@@default_options ||= {}
|
160
|
+
end
|
161
|
+
|
162
|
+
def failsafe(params)
|
163
|
+
case default_options[:failsafe]
|
164
|
+
when :file
|
165
|
+
dir = defined?(RAILS_ROOT) ? RAILS_ROOT : '.'
|
166
|
+
File.open("#{dir}/failsafe.log", "a") do |f|
|
167
|
+
f.puts "#{Time.now} #{params[:log][:level]} : #{params[:log][:message]}"
|
168
|
+
f.puts params[:log][:addendum] if params[:log][:addendum]
|
169
|
+
end
|
170
|
+
when :console, nil
|
171
|
+
console_log(params[:log][:message])
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def console_log(msg)
|
176
|
+
STDERR.puts "#{Time.now.iso8601} #{msg}" if default_options[:console_log]
|
177
|
+
end
|
178
|
+
|
179
|
+
def syslog(msg, opts = {})
|
180
|
+
@retried = false
|
181
|
+
begin
|
182
|
+
level = SyslogConvertion[opts[:level]]
|
183
|
+
if opts[:exception]
|
184
|
+
msg += "\n" + format_syslog_exception(opts[:exception])
|
185
|
+
end
|
186
|
+
syslog_resource.log(level, '%s', msg)
|
187
|
+
rescue Exception => e
|
188
|
+
failsafe(:log => { :level => 'error', :message => "could not log to syslog: #{e.class.name} #{e.message}"})
|
189
|
+
unless @retried
|
190
|
+
@retried = true
|
191
|
+
@@syslog.close rescue nil
|
192
|
+
@@syslog = Syslog.open(default_options[:component]) rescue nil
|
193
|
+
retry
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
def format_syslog_exception(e)
|
199
|
+
if e.respond_to?(:backtrace)
|
200
|
+
"\t#{e.class}: #{e.message}\n" + e.backtrace.map { |t| "\t#{t}" }.join("\n")
|
201
|
+
else
|
202
|
+
"\t#{e.class}: #{e.message}"
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
def syslog_resource
|
207
|
+
@@syslog ||= Syslog.open(default_options[:component].to_s, Syslog::LOG_PID | Syslog::LOG_CONS, default_options[:syslog_facility] || Syslog::LOG_USER)
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|