yeller_ruby 0.0.1

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 69148c3deef072819ef088784bbab18ba78499ff
4
+ data.tar.gz: dcb6d17d8c297cc968ea8915f562200498af1003
5
+ SHA512:
6
+ metadata.gz: 1a5e13ea29a5fb9b3385f50201fe2776d8ca105b5f75ce4b0a589e176a24e339c21caa0015330bf7215e1a1ef9f4618a5ee39749efc3308cc5efd45b1aa1cd90
7
+ data.tar.gz: a63d64093d5e56edc5b773829e1024edc835fac1341bd213991c78b393323e9f65e8bb92e08cad52198b548b6b9d907f4c2cb7acd2bb1ecdd6462e69ef9bd63e
@@ -0,0 +1,43 @@
1
+ module Yeller
2
+ class Client
3
+ def initialize(servers, token, startup_params, error_handler)
4
+ @servers = servers
5
+ @last_server = rand(servers.size)
6
+ @startup_params = startup_params
7
+ @token = token
8
+ @error_handler = error_handler
9
+ end
10
+
11
+ def report(exception, options={})
12
+ hash = ExceptionFormatter.format(exception, options)
13
+ serialized = JSON.dump(@startup_params.merge(hash))
14
+ report_with_roundtrip(serialized, 0)
15
+ end
16
+
17
+ def report_with_roundtrip(serialized, error_count)
18
+ next_server.client.post("/#{@token}", serialized, {"Content-Type" => "application/json"})
19
+ rescue StandardError => e
20
+ if error_count <= (@servers.size * 2)
21
+ report_with_roundtrip(serialized, error_count + 1)
22
+ else
23
+ @error_handler.handle(e)
24
+ end
25
+ end
26
+
27
+ def record_deploy(revision, user, environment)
28
+ post = Net::HTTP::Post.new("/#{@token}/deploys")
29
+ post.set_form_data('revision' => revision,
30
+ 'user' => user,
31
+ 'environment' => environment)
32
+ next_server.client.request(post)
33
+ end
34
+
35
+ private
36
+
37
+ def next_server
38
+ index = @last_server
39
+ @last_server = (index + 1) % @servers.size
40
+ @servers[index]
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,65 @@
1
+ require_relative 'server'
2
+
3
+ module Yeller
4
+ class Configuration
5
+ DEFAULT_SERVERS = [
6
+ Yeller::SecureServer.new("collector1.yellerapp.com", 443),
7
+ Yeller::SecureServer.new("collector2.yellerapp.com", 443),
8
+ Yeller::SecureServer.new("collector3.yellerapp.com", 443),
9
+ Yeller::SecureServer.new("collector4.yellerapp.com", 443),
10
+ Yeller::SecureServer.new("collector5.yellerapp.com", 443),
11
+ ]
12
+
13
+ def initialize
14
+ @servers = DEFAULT_SERVERS
15
+ @startup_params = {}
16
+ @error_handler = Yeller::LogErrorHandler.new
17
+ end
18
+
19
+ def remove_default_servers
20
+ @servers = []
21
+ self
22
+ end
23
+
24
+ def add_server(host, port=443)
25
+ @servers << Yeller::SecureServer.new(host, port)
26
+ self
27
+ end
28
+
29
+ def add_insecure_server(host, port=80)
30
+ @servers << Yeller::Server.new(host, port)
31
+ end
32
+
33
+ def environment=(new_environment)
34
+ @startup_params[:"application-environment"] = new_environment
35
+ end
36
+
37
+ def host=(new_host)
38
+ @startup_params[:host] = new_host
39
+ end
40
+
41
+ def token=(token)
42
+ @token = token
43
+ end
44
+
45
+ def token
46
+ @token
47
+ end
48
+
49
+ def servers
50
+ @servers
51
+ end
52
+
53
+ def startup_params
54
+ @startup_params
55
+ end
56
+
57
+ def error_handler
58
+ @error_handler
59
+ end
60
+
61
+ def error_handler=(new_error_handler)
62
+ @error_handler = new_error_handler
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,44 @@
1
+ module Yeller
2
+ class ExceptionFormatter
3
+ BACKTRACE_FORMAT = %r{^((?:[a-zA-Z]:)?[^:]+):(\d+)(?::in `([^']+)')?$}.freeze
4
+
5
+ def self.format(exception, options={})
6
+ new(exception, options).to_hash
7
+ end
8
+
9
+ attr_reader :type, :options
10
+
11
+ def initialize(exception, options)
12
+ @type = exception.class.name
13
+ @message = exception.message
14
+ @backtrace = exception.backtrace
15
+ @options = options
16
+ end
17
+
18
+ def message
19
+ # If a message is not given, rubby will set message to the class name
20
+ @message == type ? nil : @message
21
+ end
22
+
23
+ def formatted_backtrace
24
+ return [] unless @backtrace
25
+
26
+ @backtrace.map do |line|
27
+ _, file, number, method = line.match(BACKTRACE_FORMAT).to_a
28
+ [file, number, method]
29
+ end
30
+ end
31
+
32
+ def to_hash
33
+ result = {
34
+ message: message,
35
+ stacktrace: formatted_backtrace,
36
+ type: type,
37
+ :"custom-data" => options.fetch(:custom_data, {})
38
+ }
39
+ result[:url] = options[:url] if options.key?(:url)
40
+ result[:location] = options[:location] if options.key?(:location)
41
+ result
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,12 @@
1
+ require 'logger'
2
+ module Yeller
3
+ class LogErrorHandler
4
+ def initialize(logger=Logger.new(STDERR))
5
+ @logger = logger
6
+ end
7
+
8
+ def handle(e)
9
+ @logger.warn(e)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,40 @@
1
+ require 'rack/request'
2
+ require_relative '../yeller'
3
+
4
+ module Yeller
5
+ class Rack
6
+ def self.configure(&block)
7
+ @client = Yeller.client(&block)
8
+ end
9
+
10
+ def self.report(exception, options={})
11
+ @client.report(exception, options)
12
+ end
13
+
14
+ def initialize(app)
15
+ @app = app
16
+ end
17
+
18
+ def call(env)
19
+ begin
20
+ @app.call(env)
21
+ rescue Exception => exception
22
+ Yeller::Rack.rescue_rack_exception(exception, env)
23
+ raise exception
24
+ end
25
+ end
26
+
27
+ def self.rescue_rack_exception(exception, env)
28
+ ::Rails.logger.info("sending exception to yeller")
29
+ request = ::Rack::Request.new(env)
30
+ Yeller::Rack.report(
31
+ exception,
32
+ :url => request.url,
33
+ :custom_data => {
34
+ :params => request.params,
35
+ :session => env.fetch('rack.session', {}),
36
+ })
37
+ ::Rails.logger.info("sent exception to yeller")
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,49 @@
1
+ require 'rails'
2
+ require 'yeller'
3
+ require 'yeller/rack'
4
+
5
+ module Yeller
6
+ class Rails
7
+ def self.configure(&block)
8
+ Yeller::Rack.configure do |config|
9
+ config.error_handler = Yeller::LogErrorHandler.new(::Rails.logger)
10
+ block.call(config)
11
+ end
12
+ end
13
+
14
+ module ActionControllerCatchingHooks
15
+ def self.included(base)
16
+ base.send(:alias_method, :render_exception_without_yeller, :render_exception)
17
+ base.send(:alias_method, :render_exception, :render_exception_with_yeller)
18
+ end
19
+
20
+ protected
21
+ def render_exception_with_yeller(env, exception)
22
+ ::Rails.logger.info("yeller: caught exception")
23
+ Yeller::Rack.rescue_rack_exception(exception, env)
24
+ render_exception_without_yeller(env, exception)
25
+ rescue Exception => e
26
+ ::Rails.logger.info("exception whilst handling exception: #{e} #{e.class} #{e.backtrace.join("\n")}")
27
+ end
28
+ end
29
+
30
+ class Railtie < ::Rails::Railtie
31
+ initializer "yeller.use_rack_middleware" do |app|
32
+ app.config.middleware.insert 0, "Yeller::Rack"
33
+ end
34
+
35
+ initializer "yeller.action_controller" do
36
+ ActiveSupport.on_load :action_controller do
37
+ end
38
+ end
39
+
40
+ config.after_initialize do
41
+ if defined?(::ActionDispatch::DebugExceptions)
42
+ ::ActionDispatch::DebugExceptions.send(:include, Yeller::Rails::ActionControllerCatchingHooks)
43
+ elsif defined(::ActionDispatch::ShowExceptions)
44
+ ::ActionDispatch::ShowExceptions.send(:include, Yeller::Rails::ActionControllerCatchingHooks)
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,22 @@
1
+ require 'openssl'
2
+ module Yeller
3
+ Server = Struct.new(:host, :port) do
4
+ def client
5
+ @client ||= Net::HTTP.new(host, port)
6
+ end
7
+ end
8
+
9
+ SecureServer = Struct.new(:host, :port) do
10
+ def client
11
+ @client ||= setup_client
12
+ end
13
+
14
+ def setup_client
15
+ http = Net::HTTP.new(host, port)
16
+ http.use_ssl = true
17
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
18
+ http.ciphers = "DEFAULT:!aNULL:!eNULL:!LOW:!EXPORT:!SSLv2"
19
+ http
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,23 @@
1
+ require_relative 'version'
2
+
3
+ module Yeller
4
+ class StartupParams
5
+ PRODUCTION = 'production'.freeze
6
+ VERSION = "yeller_rubby: #{Yeller::VERSION}"
7
+
8
+ def self.defaults(options={})
9
+ {
10
+ :host => Socket.gethostname,
11
+ :"application-environment" => application_environment(options),
12
+ :"client-version" => VERSION,
13
+ }
14
+ end
15
+
16
+ def self.application_environment(options)
17
+ options[:"application-environment"] ||
18
+ ENV['RAILS_ENV'] ||
19
+ ENV['RACK_ENV'] ||
20
+ PRODUCTION
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,3 @@
1
+ module Yeller
2
+ VERSION = "0.0.1"
3
+ end
data/lib/yeller.rb ADDED
@@ -0,0 +1,27 @@
1
+ require 'net/http'
2
+ require 'yajl/json_gem'
3
+
4
+ require_relative 'yeller/client'
5
+ require_relative 'yeller/configuration'
6
+ require_relative 'yeller/exception_formatter'
7
+ require_relative 'yeller/server'
8
+ require_relative 'yeller/version'
9
+ require_relative 'yeller/startup_params'
10
+ require_relative 'yeller/log_error_handler'
11
+
12
+ module Yeller
13
+ def self.client(&block)
14
+ config = Yeller::Configuration.new
15
+ block.call(config)
16
+ build_client(config)
17
+ end
18
+
19
+ def self.build_client(config)
20
+ Yeller::Client.new(
21
+ config.servers,
22
+ config.token,
23
+ Yeller::StartupParams.defaults(config.startup_params),
24
+ config.error_handler
25
+ )
26
+ end
27
+ end
metadata ADDED
@@ -0,0 +1,96 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: yeller_ruby
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Tom Crayford
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-05-04 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.5'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.5'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: yajl-ruby
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '='
46
+ - !ruby/object:Gem::Version
47
+ version: 1.2.0
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '='
53
+ - !ruby/object:Gem::Version
54
+ version: 1.2.0
55
+ description: A Ruby/Rack/Rails client for yellerapp.com
56
+ email:
57
+ - tcrayford@googlemail.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - lib/yeller.rb
63
+ - lib/yeller/client.rb
64
+ - lib/yeller/configuration.rb
65
+ - lib/yeller/exception_formatter.rb
66
+ - lib/yeller/log_error_handler.rb
67
+ - lib/yeller/rack.rb
68
+ - lib/yeller/rails.rb
69
+ - lib/yeller/server.rb
70
+ - lib/yeller/startup_params.rb
71
+ - lib/yeller/version.rb
72
+ homepage: https://github.com/tcrayford/yeller_rubby
73
+ licenses:
74
+ - MIT
75
+ metadata: {}
76
+ post_install_message:
77
+ rdoc_options: []
78
+ require_paths:
79
+ - lib
80
+ required_ruby_version: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ version: '0'
85
+ required_rubygems_version: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ requirements: []
91
+ rubyforge_project:
92
+ rubygems_version: 2.2.0
93
+ signing_key:
94
+ specification_version: 4
95
+ summary: A Ruby/Rack/Rails client for yellerapp.com
96
+ test_files: []