failbot 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.
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+ require 'failbot'
3
+ Failbot::Server.new(port=ARGV.shift, tmp=ARGV.shift).exec!
4
+
5
+ # vim:ft=ruby
@@ -0,0 +1,237 @@
1
+ require 'yaml'
2
+ require 'digest/md5'
3
+ require 'logger'
4
+ require 'socket'
5
+ require "uri"
6
+
7
+ require 'failbot/version'
8
+ require "failbot/compat"
9
+
10
+ # Failbot asynchronously takes exceptions and reports them to the
11
+ # exception logger du jour. Keeps the main app from failing or lagging if
12
+ # the exception logger service is down or slow.
13
+ module Failbot
14
+ # The failbot relay server.
15
+ autoload :Server, 'failbot/server'
16
+
17
+ # Interface for posting exception data to haystack.
18
+ autoload :Haystack, 'failbot/haystack'
19
+
20
+ autoload :BERTBackend, 'failbot/bert_backend'
21
+ autoload :FileBackend, 'failbot/file_backend'
22
+ autoload :HerokuBackend, 'failbot/heroku_backend'
23
+ autoload :HTTPBackend, 'failbot/http_backend'
24
+ autoload :MemoryBackend, 'failbot/memory_backend'
25
+
26
+ # Public: Setup the backend for reporting exceptions.
27
+ def setup(settings={}, default_context={})
28
+ deprecated_settings = %w[
29
+ backend host port haystack workers service_logs
30
+ raise_errors
31
+ ]
32
+
33
+ if settings.empty? ||
34
+ settings.keys.any? { |key| deprecated_settings.include?(key) }
35
+ warn "%s Deprecated Failbot.setup usage. See %s for details." % [
36
+ caller[0], "https://github.com/github/failbot"
37
+ ]
38
+ return setup_deprecated(settings)
39
+ end
40
+
41
+ if default_context.respond_to?(:to_hash) && !default_context.to_hash.empty?
42
+ context[0] = default_context.to_hash
43
+ end
44
+
45
+ self.backend =
46
+ case (name = settings["FAILBOT_BACKEND"])
47
+ when "bert"
48
+ Failbot::BERTBackend.new(URI(settings["FAILBOT_BACKEND_BERT_URL"]))
49
+ when "memory"
50
+ Failbot::MemoryBackend.new
51
+ when "file"
52
+ Failbot::FileBackend.new(settings["FAILBOT_BACKEND_FILE_PATH"])
53
+ when "http"
54
+ Failbot::HTTPBackend.new(URI(settings["FAILBOT_HAYSTACK_URL"]))
55
+ else
56
+ raise ArgumentError, "Unknown backend: #{name.inspect}"
57
+ end
58
+
59
+ @raise_errors = !settings["FAILBOT_RAISE"].to_s.empty?
60
+ end
61
+
62
+ # Bring in deprecated methods
63
+ extend Failbot::Compat
64
+
65
+ # Stack of context information to include in the next failbot report. These
66
+ # hashes are condensed down into one and included in the next report. Don't
67
+ # mess with this structure directly - use the #push and #pop methods.
68
+ def context
69
+ @context ||= [{'server' => hostname}]
70
+ end
71
+
72
+ # Add info to be sent in the next failbot report, should one occur.
73
+ #
74
+ # info - Hash of name => value pairs to include in the exception report.
75
+ # block - When given, the info is removed from the current context after the
76
+ # block is executed.
77
+ #
78
+ # Returns the value returned by the block when given; otherwise, returns nil.
79
+ def push(info={})
80
+ context.push(info)
81
+ yield if block_given?
82
+ ensure
83
+ pop if block_given?
84
+ end
85
+
86
+ # Remove the last info hash from the context stack.
87
+ def pop
88
+ context.pop if context.size > 1
89
+ end
90
+
91
+ # Reset the context stack to a pristine state.
92
+ def reset!
93
+ @context = [context[0]]
94
+ end
95
+
96
+ # Public: Sends an exception to the exception tracking service along
97
+ # with a hash of custom attributes to be included with the report. When the
98
+ # raise_errors option is set, this method raises the exception instead of
99
+ # reporting to the exception tracking service.
100
+ #
101
+ # e - The Exception object. Must respond to #message and #backtrace.
102
+ # other - Hash of additional attributes to include with the report.
103
+ #
104
+ # Examples
105
+ #
106
+ # begin
107
+ # my_code
108
+ # rescue => e
109
+ # Failbot.report(e, :user => current_user)
110
+ # end
111
+ #
112
+ # Returns nothing.
113
+ def report(e, other = {})
114
+ if @raise_errors
115
+ squash_context(exception_info(e), other) # surface problems squashing
116
+ raise e
117
+ else
118
+ report!(e, other)
119
+ end
120
+ end
121
+
122
+ def report!(e, other = {})
123
+ data = squash_context(exception_info(e), other)
124
+ backend.report(data)
125
+ rescue Object => i
126
+ # don't fail for any reason
127
+ logger.debug "FAILBOT: #{data.inspect}" rescue nil
128
+ logger.debug e.message rescue nil
129
+ logger.debug e.backtrace.join("\n") rescue nil
130
+ logger.debug i.message rescue nil
131
+ logger.debug i.backtrace.join("\n") rescue nil
132
+ end
133
+
134
+ # Public: exceptions that were reported. Only available when using the
135
+ # memory and file backends.
136
+ #
137
+ # Returns an Array of exceptions data Hash.
138
+ def reports
139
+ backend.reports
140
+ end
141
+
142
+ # Combines all context hashes into a single hash converting non-standard
143
+ # data types in values to strings, then combines the result with a custom
144
+ # info hash provided in the other argument.
145
+ #
146
+ # other - Optional array of hashes to also squash in on top of the context
147
+ # stack hashes.
148
+ #
149
+ # Returns a Hash with all keys and values.
150
+ def squash_context(*other)
151
+ merged = {}
152
+ (context + other).each do |hash|
153
+ hash.each do |key, value|
154
+ value = (value.call rescue nil) if value.kind_of?(Proc)
155
+ merged[key.to_s] =
156
+ case value
157
+ when String, Numeric, true, false
158
+ value.to_s
159
+ else
160
+ value.inspect
161
+ end
162
+ end
163
+ end
164
+ merged
165
+ end
166
+
167
+ # Extract exception info into a simple Hash.
168
+ #
169
+ # e - The exception object to turn into a Hash.
170
+ #
171
+ # Returns a Hash including a 'class', 'message', 'backtrace', and 'rollup'
172
+ # keys. The rollup value is a MD5 hash of the exception class, file, and line
173
+ # number and is used to group exceptions.
174
+ def exception_info(e)
175
+ backtrace = Array(e.backtrace)[0, 500]
176
+
177
+ res = {
178
+ 'class' => e.class.to_s,
179
+ 'message' => e.message,
180
+ 'backtrace' => backtrace.join("\n"),
181
+ 'ruby' => RUBY_DESCRIPTION,
182
+ 'rollup' => Digest::MD5.hexdigest("#{e.class}#{backtrace[0]}")
183
+ }
184
+
185
+ if original = (e.respond_to?(:original_exception) && e.original_exception)
186
+ remote_backtrace = []
187
+ remote_backtrace << original.message
188
+ if original.backtrace
189
+ remote_backtrace.concat(Array(original.backtrace)[0,500])
190
+ end
191
+ res['remote_backtrace'] = remote_backtrace.join("\n")
192
+ end
193
+
194
+ res
195
+ end
196
+
197
+ # Installs an at_exit hook to report exceptions that raise all the way out of
198
+ # the stack and halt the interpreter. This is useful for catching boot time
199
+ # errors as well and even signal kills.
200
+ #
201
+ # To use, call this method very early during the program's boot to cover as
202
+ # much code as possible:
203
+ #
204
+ # require 'failbot'
205
+ # Failbot.install_unhandled_exception_hook!
206
+ #
207
+ # Returns true when the hook was installed, nil when the hook had previously
208
+ # been installed by another component.
209
+ def install_unhandled_exception_hook!
210
+ # only install the hook once, even when called from multiple locations
211
+ return if @unhandled_exception_hook_installed
212
+
213
+ # the $! is set when the interpreter is exiting due to an exception
214
+ at_exit do
215
+ boom = $!
216
+ if boom && !@raise_errors && !boom.is_a?(SystemExit)
217
+ report(boom, 'argv' => ([$0]+ARGV).join(" "), 'halting' => true)
218
+ end
219
+ end
220
+
221
+ @unhandled_exception_hook_installed = true
222
+ end
223
+
224
+ def logger
225
+ @logger ||= Logger.new($stderr)
226
+ end
227
+
228
+ def logger=(logger)
229
+ @logger = logger
230
+ end
231
+
232
+ def hostname
233
+ @hostname ||= Socket.gethostname
234
+ end
235
+
236
+ extend self
237
+ end
@@ -0,0 +1,17 @@
1
+ require 'bertrpc'
2
+
3
+ module Failbot
4
+ class BERTBackend
5
+ def initialize(url)
6
+ @service = BERTRPC::Service.new(url.host, url.port)
7
+ end
8
+
9
+ def report(data)
10
+ @service.cast.failbot.report(data)
11
+ end
12
+
13
+ def reports
14
+ raise NotImplementedError
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,157 @@
1
+ module Failbot
2
+ module Compat
3
+ # DEPRECATED Reset the backend and optionally override the environment
4
+ # configuration.
5
+ #
6
+ # config - The optional configuration Hash.
7
+ #
8
+ # Returns nothing.
9
+ def setup_deprecated(_config={})
10
+ config.merge!(_config)
11
+ @backend = nil
12
+ @raise_errors = nil
13
+ end
14
+
15
+ # The current "environment". This dictates which section will be read
16
+ # from the failbot.yml config file.
17
+ def environment
18
+ warn "#{caller[0]} Failbot.environment is deprecated and will be " \
19
+ "removed in subsequent releases."
20
+ @environment ||= ENV['FAILBOT_ENV'] || ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'development'
21
+ end
22
+
23
+ # Hash of configuration data from lib/failbot/failbot.yml.
24
+ def config
25
+ warn "#{caller[0]} Failbot.config is deprecated and will be " \
26
+ "removed in subsequent releases."
27
+ @config ||= YAML.load_file(config_file)[environment]
28
+ end
29
+
30
+ # Location of failbot.yml config file.
31
+ def config_file
32
+ warn "#{caller[0]} Failbot.config_file is deprecated and will be " \
33
+ "removed in subsequent releases."
34
+ File.expand_path('../../failbot/failbot.yml', __FILE__)
35
+ end
36
+
37
+ # The name of the backend that should be used to post exceptions to the
38
+ # exceptions-collection service. The fellowing backends are available:
39
+ #
40
+ # memory - Dummy backend that simply save exceptions in memory. Typically
41
+ # used in testing environments.
42
+ #
43
+ # bert - The BERT-RPC backend relays exceptions using the bert library.
44
+ # See <https://github.com/mojombo/bert> for details.
45
+ #
46
+ # heroku - In-process posting for outside of vpn apps
47
+ #
48
+ # file - Append JSON-encoded exceptions to a file.
49
+ #
50
+ # Returns the String backend name. See also `Failbot.backend`.
51
+ def backend_name
52
+ warn "#{caller[0]} Failbot.backend_name is deprecated and will be " \
53
+ "removed in subsequent releases."
54
+ config['backend']
55
+ end
56
+
57
+ # Determines whether exceptions are raised instead of being reported to
58
+ # the exception tracking service. This is typically enabled in development
59
+ # and test environments. When set true, no exception information is reported
60
+ # and the exception is raised instead. When false (default in production
61
+ # environments), the exception is reported to the exception tracking service
62
+ # but not raised.
63
+ def raise_errors?
64
+ warn "#{caller[0]} Failbot.raise_errors? is deprecated and will be " \
65
+ "removed in subsequent releases."
66
+
67
+ if @raise_errors.nil?
68
+ config['raise_errors']
69
+ else
70
+ @raise_errors
71
+ end
72
+ end
73
+
74
+ def raise_errors=(v)
75
+ warn "#{caller[0]} Failbot.raise_errors= is deprecated and will be " \
76
+ "removed in subsequent releases."
77
+ @raise_errors = v
78
+ end
79
+
80
+ # Load and initialize the exception reporting backend as specified by
81
+ # the 'backend' configuration option.
82
+ #
83
+ # Raises ArgumentError for invalid backends.
84
+ def backend
85
+ @backend ||= backend!
86
+ end
87
+ attr_writer :backend
88
+
89
+ def backend!
90
+ warn "#{caller[0]} Failbot.backend! is deprecated and will be " \
91
+ "removed in subsequent releases."
92
+ case backend_name
93
+ when 'bert'
94
+ url = URI("bertrpc://#{config["host"]}:#{config["port"]}")
95
+ Failbot::BERTBackend.new(url)
96
+ when 'memory'
97
+ Failbot::MemoryBackend.new
98
+ when 'file'
99
+ Failbot::FileBackend.new(config['file_path'])
100
+ when 'heroku', 'http'
101
+ if backend_name == "heroku"
102
+ warn "The Failbot \"heroku\" backend is deprecated. Use \"http\" " \
103
+ "instead. #{caller[1]}"
104
+ end
105
+ Failbot::HerokuBackend.new(config['haystack'])
106
+ else
107
+ raise ArgumentError, "Unknown backend: #{backend_name.inspect}"
108
+ end
109
+ end
110
+
111
+ # The URL where exceptions should be posted. Each exception is converted into
112
+ # JSON and posted to this URL.
113
+ def haystack
114
+ warn "#{caller[0]} Failbot.haystack is deprecated and will be " \
115
+ "removed in subsequent releases."
116
+ config['haystack']
117
+ end
118
+
119
+ # Send the exception data to the relay service using
120
+ # a non-waiting cast call.
121
+ #
122
+ # data - Hash of string key => string value pairs.
123
+ #
124
+ # Returns nothing.
125
+ def cast(data)
126
+ warn "#{caller[0]} Failbot.cast is deprecated and will be " \
127
+ "removed in subsequent releases."
128
+ backend.report(data)
129
+ end
130
+
131
+ def service
132
+ warn "#{caller[0]} Failbot.service is deprecated and will be " \
133
+ "removed in subsequent releases."
134
+ @service ||= BERTRPC::Service.new(config['host'], config['port'])
135
+ end
136
+
137
+ alias svc service
138
+
139
+ def fail
140
+ warn "#{caller[0]} Failbot.fail is deprecated and will be " \
141
+ "removed in subsequent releases."
142
+ raise "failure failure!"
143
+ end
144
+
145
+ def default_options
146
+ warn "#{caller[0]} Failbot.default_options is deprecated and will be " \
147
+ "removed in subsequent releases."
148
+ context[0]
149
+ end
150
+
151
+ def default_options=(hash)
152
+ warn "#{caller[0]} Failbot.default_options= is deprecated. Please use " \
153
+ "Failbot.setup(ENV, context={}) instead."
154
+ context[0] = hash
155
+ end
156
+ end
157
+ end
@@ -0,0 +1,16 @@
1
+ # This file exists so that the unhandled exception hook may easily be injected
2
+ # into programs that don't register it themselves. To use, set RUBYOPT or pass
3
+ # an -r argument to ruby:
4
+ #
5
+ # With RUBYOPT:
6
+ #
7
+ # RUBYOPT=rfailbot/exit_hook some-program.rb
8
+ #
9
+ # With ruby -r:
10
+ #
11
+ # ruby -rfailbot/exit_hook some-program.rb
12
+ #
13
+ # Note: it may be necessary to also require rubygems w/ RUBYOPT or -r before
14
+ # requiring failbot/exit_hook.
15
+ require 'failbot'
16
+ Failbot.install_unhandled_exception_hook!
@@ -0,0 +1,82 @@
1
+ development:
2
+ backend: bert
3
+ host: localhost
4
+ port: 6666
5
+ haystack: http://localhost:9393/async
6
+ workers: 1
7
+ service_log: /tmp/failbot.log
8
+ raise_errors: true
9
+
10
+ test:
11
+ backend: memory
12
+ host: localhost
13
+ port: 6666
14
+ haystack: http://localhost:9393/async
15
+ workers: 0
16
+ raise_errors: true
17
+
18
+ production:
19
+ backend: bert
20
+ host: localhost
21
+ port: 6666
22
+ haystack: https://haystack.githubapp.com/_needles
23
+ workers: 2
24
+ service_log: /data/github/current/log/failbot.log
25
+ raise_errors: false
26
+
27
+ staging:
28
+ backend: bert
29
+ host: localhost
30
+ port: 6666
31
+ haystack: https://haystack.githubapp.com/_needles
32
+ workers: 1
33
+ service_log: /data/github/current/log/failbot.log
34
+ raise_errors: false
35
+
36
+ standalone:
37
+ backend: bert
38
+ host: localhost
39
+ port: 6666
40
+ haystack: https://haystack.githubapp.com/_needles
41
+ service_log: /tmp/failbot.log
42
+ workers: 1
43
+ raise_errors: false
44
+
45
+ standalone-staging:
46
+ backend: bert
47
+ host: localhost
48
+ port: 6666
49
+ haystack: https://haystack-staging.githubapp.com/async
50
+ service_log: /tmp/failbot.log
51
+ workers: 1
52
+ raise_errors: false
53
+
54
+ standalone-ng:
55
+ backend: bert
56
+ host: localhost
57
+ port: 6666
58
+ haystack: https://haystack-ng.githubapp.com/async
59
+ service_log: /tmp/failbot.log
60
+ workers: 1
61
+ raise_errors: false
62
+
63
+ haystack-ng:
64
+ backend: heroku
65
+ haystack: https://haystack-ng.githubapp.com/async
66
+ service_log: /tmp/failbot.log
67
+ workers: 1
68
+ raise_errors: false
69
+
70
+ heroku:
71
+ backend: heroku
72
+ haystack: https://hubble.githubapp.com/async
73
+ service_log: /tmp/failbot.log
74
+ workers: 1
75
+ raise_errors: false
76
+
77
+ heroku-staging:
78
+ backend: heroku
79
+ haystack: https://hubble-staging.githubapp.com/async
80
+ service_log: /tmp/failbot.log
81
+ workers: 1
82
+ raise_errors: false
@@ -0,0 +1,27 @@
1
+ require 'yajl'
2
+
3
+ module Failbot
4
+ class FileBackend
5
+ def initialize(path)
6
+ @path = path
7
+
8
+ if path.to_s.empty?
9
+ raise ArgumentError, "FAILBOT_BACKEND_FILE_PATH setting required."
10
+ end
11
+ end
12
+
13
+ def report(data)
14
+ File.open(@path, 'a') do |file|
15
+ file.puts(Yajl.dump(data))
16
+ end
17
+ end
18
+
19
+ def reports
20
+ reports = []
21
+ File.foreach(@path) do |line|
22
+ reports << Yajl.load(line)
23
+ end
24
+ reports
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env ruby
2
+ require 'time'
3
+ require 'failbot'
4
+
5
+ # Reopen stderr/stdout on the logfile if enabled.
6
+ if log_file = Failbot::config['service_log']
7
+ $stderr.reopen(log_file, 'ab')
8
+ $stdout.reopen($stderr)
9
+ [$stderr, $stdout].each { |fd| fd.sync = true }
10
+ end
11
+ warn "failbot handler #$$ starting in #{Failbot::environment} at #{Time.now.iso8601}"
12
+
13
+ require 'ernie'
14
+ require 'failbot/haystack'
15
+
16
+ # The Ernie handler implementation responsible for delivering exception reports
17
+ # to the exception backend. The report method is made available as a BERT-RPC
18
+ # service.
19
+ module Failbot::Handler
20
+ def report(data)
21
+ Failbot::Haystack.send_data(data)
22
+ true
23
+ end
24
+ end
25
+
26
+ Ernie.expose(:failbot, Failbot::Handler)
27
+
28
+ # vim:ft=ruby
@@ -0,0 +1,42 @@
1
+ require 'net/https'
2
+ require 'uri'
3
+ require 'yajl'
4
+
5
+ module Failbot
6
+ class Haystack
7
+ def initialize(url)
8
+ @url = url
9
+ end
10
+
11
+ def user
12
+ @url.user || "failbot"
13
+ end
14
+
15
+ def password
16
+ @url.password
17
+ end
18
+
19
+ def send_data(data)
20
+ # make a post
21
+ post = Net::HTTP::Post.new(@url.path)
22
+ post.set_form_data('json' => Yajl.dump(data))
23
+
24
+ if user && password
25
+ post.basic_auth(user, password)
26
+ end
27
+
28
+ # make request
29
+ req = Net::HTTP.new(@url.host, @url.port)
30
+
31
+ # use SSL if applicable
32
+ req.use_ssl = true if @url.scheme == "https"
33
+
34
+ # push it through
35
+ req.start { |http| http.request(post) }
36
+ end
37
+
38
+ def self.send_data(data)
39
+ new(Failbot.haystack).send_data(data)
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,15 @@
1
+ module Failbot
2
+ class HerokuBackend
3
+ def initialize(url)
4
+ @hubble = Failbot::Haystack.new(url)
5
+ end
6
+
7
+ def report(data)
8
+ @hubble.send_data(data)
9
+ end
10
+
11
+ def reports
12
+ []
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,19 @@
1
+ module Failbot
2
+ class HTTPBackend
3
+ def initialize(url)
4
+ if url.to_s.empty?
5
+ raise ArgumentError, "FAILBOT_HAYSTACK_URL setting required."
6
+ end
7
+
8
+ @haystack = Failbot::Haystack.new(url)
9
+ end
10
+
11
+ def report(data)
12
+ @haystack.send_data(data)
13
+ end
14
+
15
+ def reports
16
+ []
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,22 @@
1
+ module Failbot
2
+ class MemoryBackend
3
+ def initialize
4
+ @reports = []
5
+ @fail = false
6
+ end
7
+
8
+ attr_accessor :reports
9
+
10
+ def fail!
11
+ @fail = true
12
+ end
13
+
14
+ def report(data)
15
+ if @fail
16
+ fail
17
+ end
18
+
19
+ @reports << data
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,34 @@
1
+ module Failbot
2
+ # Rack middleware that rescues exceptions raised from the downstream app and
3
+ # reports to failbot. The exception is reraised after being sent to haystack
4
+ # so upstream middleware can still display an error page or whathaveyou.
5
+ class Rescuer
6
+ def initialize(app, other)
7
+ @app = app
8
+ @other = other
9
+ end
10
+
11
+ def call(env)
12
+ start = Time.now
13
+ @app.call(env)
14
+ rescue Object => boom
15
+ elapsed = Time.now - start
16
+ self.class.report(boom, env, @other.merge(:time => elapsed.to_s))
17
+ raise
18
+ end
19
+
20
+ def self.report(boom, env, other={})
21
+ request = Rack::Request.new(env)
22
+ Failbot.context.push(other.merge({
23
+ :method => request.request_method,
24
+ :user_agent => env['HTTP_USER_AGENT'],
25
+ :params => (request.params.inspect rescue nil),
26
+ :session => (request.session.inspect rescue nil),
27
+ :referrer => request.referrer,
28
+ :remote_ip => request.ip,
29
+ :url => request.url
30
+ }))
31
+ Failbot.report(boom)
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,28 @@
1
+ require 'resque'
2
+
3
+ module Resque
4
+ module Failure
5
+ class Failbot < Base
6
+ def self.url
7
+ if ENV['RAILS_ENV'] == 'staging'
8
+ "http://haystack.stg.github.com/types/exception"
9
+ else
10
+ "http://haystack.rs.github.com/types/exception"
11
+ end
12
+ end
13
+
14
+ def self.count
15
+ # Fake it for now.
16
+ Stat[:failed]
17
+ end
18
+
19
+ def save
20
+ ::Failbot.report(exception,
21
+ :worker => worker.to_s,
22
+ :queue => queue.to_s,
23
+ :job => payload['class'].to_s,
24
+ :args => payload['args'].inspect)
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,53 @@
1
+ require 'failbot'
2
+ require 'tmpdir'
3
+
4
+ module Failbot
5
+ # Interface for starting the exception relay server. This class constructs a
6
+ # temporary config file for ernie and starts the erlang server process.
7
+ class Server
8
+ def initialize(port=nil, tmpdir=nil)
9
+ @port = (port || 6666).to_i
10
+ @tmp = tmpdir || Dir.tmpdir
11
+ end
12
+
13
+ attr_reader :port
14
+ attr_reader :tmp
15
+
16
+ def workers
17
+ Failbot.config['workers'] || 1
18
+ end
19
+
20
+ def handler_file
21
+ File.expand_path('../handler.rb', __FILE__)
22
+ end
23
+
24
+ def pid_file
25
+ "#{@tmp}/failbot.#{port}.pid"
26
+ end
27
+
28
+ def configure
29
+ write_config
30
+ config_file
31
+ end
32
+
33
+ def config_file
34
+ "#{@tmp}/ernie.failbot.#{Failbot.environment}.cfg"
35
+ end
36
+
37
+ def write_config
38
+ File.open(config_file, 'wb') do |io|
39
+ io.write((<<-ERLANG).gsub(/^ {10}/, ''))
40
+ [{module, failbot},
41
+ {type, external},
42
+ {command, "ruby #{handler_file}"},
43
+ {count, #{workers}}].
44
+ ERLANG
45
+ end
46
+ end
47
+
48
+ def exec!
49
+ configure
50
+ exec "ernie", "-p", port.to_s, "-P", pid_file, "-d", "-c", config_file
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,3 @@
1
+ module Failbot
2
+ VERSION = "0.9.0"
3
+ end
metadata ADDED
@@ -0,0 +1,112 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: failbot
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.9.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - ! '@rtomayko'
9
+ - ! '@atmos'
10
+ - ! '@sr'
11
+ autorequire:
12
+ bindir: bin
13
+ cert_chain: []
14
+ date: 2013-03-14 00:00:00.000000000 Z
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: yajl-ruby
18
+ requirement: !ruby/object:Gem::Requirement
19
+ none: false
20
+ requirements:
21
+ - - ~>
22
+ - !ruby/object:Gem::Version
23
+ version: '1.1'
24
+ type: :runtime
25
+ prerelease: false
26
+ version_requirements: !ruby/object:Gem::Requirement
27
+ none: false
28
+ requirements:
29
+ - - ~>
30
+ - !ruby/object:Gem::Version
31
+ version: '1.1'
32
+ - !ruby/object:Gem::Dependency
33
+ name: bertrpc
34
+ requirement: !ruby/object:Gem::Requirement
35
+ none: false
36
+ requirements:
37
+ - - ~>
38
+ - !ruby/object:Gem::Version
39
+ version: 1.3.0
40
+ type: :runtime
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ none: false
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: 1.3.0
48
+ - !ruby/object:Gem::Dependency
49
+ name: rake
50
+ requirement: !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ~>
54
+ - !ruby/object:Gem::Version
55
+ version: 0.8.7
56
+ type: :development
57
+ prerelease: false
58
+ version_requirements: !ruby/object:Gem::Requirement
59
+ none: false
60
+ requirements:
61
+ - - ~>
62
+ - !ruby/object:Gem::Version
63
+ version: 0.8.7
64
+ description: ! '...'
65
+ email:
66
+ - github+failbot@lists.github.com
67
+ executables:
68
+ - failbot
69
+ extensions: []
70
+ extra_rdoc_files: []
71
+ files:
72
+ - lib/failbot.rb
73
+ - lib/failbot/bert_backend.rb
74
+ - lib/failbot/compat.rb
75
+ - lib/failbot/exit_hook.rb
76
+ - lib/failbot/failbot.yml
77
+ - lib/failbot/file_backend.rb
78
+ - lib/failbot/handler.rb
79
+ - lib/failbot/haystack.rb
80
+ - lib/failbot/heroku_backend.rb
81
+ - lib/failbot/http_backend.rb
82
+ - lib/failbot/memory_backend.rb
83
+ - lib/failbot/middleware.rb
84
+ - lib/failbot/resque_failure_backend.rb
85
+ - lib/failbot/server.rb
86
+ - lib/failbot/version.rb
87
+ - bin/failbot
88
+ homepage: http://github.com/github/failbot#readme
89
+ licenses: []
90
+ post_install_message:
91
+ rdoc_options: []
92
+ require_paths:
93
+ - lib
94
+ required_ruby_version: !ruby/object:Gem::Requirement
95
+ none: false
96
+ requirements:
97
+ - - ! '>='
98
+ - !ruby/object:Gem::Version
99
+ version: '0'
100
+ required_rubygems_version: !ruby/object:Gem::Requirement
101
+ none: false
102
+ requirements:
103
+ - - ! '>='
104
+ - !ruby/object:Gem::Version
105
+ version: 1.3.6
106
+ requirements: []
107
+ rubyforge_project:
108
+ rubygems_version: 1.8.23
109
+ signing_key:
110
+ specification_version: 3
111
+ summary: Deliver exceptions to Haystack
112
+ test_files: []