failbot 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []