kuende-opbeat 2.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +5 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +36 -0
  5. data/Gemfile +2 -0
  6. data/LICENSE +205 -0
  7. data/Makefile +3 -0
  8. data/README.md +267 -0
  9. data/Rakefile +19 -0
  10. data/gemfiles/rails30.gemfile +9 -0
  11. data/gemfiles/rails31.gemfile +9 -0
  12. data/gemfiles/rails32.gemfile +9 -0
  13. data/gemfiles/rails40.gemfile +9 -0
  14. data/gemfiles/rails41.gemfile +9 -0
  15. data/gemfiles/rails42.gemfile +9 -0
  16. data/gemfiles/ruby192_rails31.gemfile +10 -0
  17. data/gemfiles/ruby192_rails32.gemfile +10 -0
  18. data/gemfiles/sidekiq31.gemfile +11 -0
  19. data/lib/opbeat.rb +132 -0
  20. data/lib/opbeat/better_attr_accessor.rb +44 -0
  21. data/lib/opbeat/capistrano.rb +9 -0
  22. data/lib/opbeat/capistrano/capistrano2.rb +47 -0
  23. data/lib/opbeat/capistrano/capistrano3.rb +26 -0
  24. data/lib/opbeat/client.rb +122 -0
  25. data/lib/opbeat/configuration.rb +90 -0
  26. data/lib/opbeat/error.rb +6 -0
  27. data/lib/opbeat/event.rb +223 -0
  28. data/lib/opbeat/filter.rb +63 -0
  29. data/lib/opbeat/integrations/delayed_job.rb +32 -0
  30. data/lib/opbeat/integrations/resque.rb +22 -0
  31. data/lib/opbeat/integrations/sidekiq.rb +32 -0
  32. data/lib/opbeat/interfaces.rb +35 -0
  33. data/lib/opbeat/interfaces/exception.rb +16 -0
  34. data/lib/opbeat/interfaces/http.rb +57 -0
  35. data/lib/opbeat/interfaces/message.rb +19 -0
  36. data/lib/opbeat/interfaces/stack_trace.rb +50 -0
  37. data/lib/opbeat/linecache.rb +25 -0
  38. data/lib/opbeat/logger.rb +21 -0
  39. data/lib/opbeat/rack.rb +44 -0
  40. data/lib/opbeat/rails/middleware/debug_exceptions_catcher.rb +22 -0
  41. data/lib/opbeat/railtie.rb +26 -0
  42. data/lib/opbeat/tasks.rb +24 -0
  43. data/lib/opbeat/version.rb +3 -0
  44. data/opbeat.gemspec +28 -0
  45. data/spec/opbeat/better_attr_accessor_spec.rb +99 -0
  46. data/spec/opbeat/client_spec.rb +35 -0
  47. data/spec/opbeat/configuration_spec.rb +50 -0
  48. data/spec/opbeat/event_spec.rb +138 -0
  49. data/spec/opbeat/filter_spec.rb +101 -0
  50. data/spec/opbeat/integrations/delayed_job_spec.rb +38 -0
  51. data/spec/opbeat/logger_spec.rb +55 -0
  52. data/spec/opbeat/opbeat_spec.rb +89 -0
  53. data/spec/opbeat/rack_spec.rb +116 -0
  54. data/spec/spec_helper.rb +22 -0
  55. metadata +218 -0
@@ -0,0 +1,19 @@
1
+ require 'rake'
2
+ require 'rubygems/package_task'
3
+
4
+ gemspec = Gem::Specification.load(Dir['*.gemspec'].first)
5
+
6
+ Gem::PackageTask.new(gemspec).define
7
+
8
+ begin
9
+ require 'rspec/core/rake_task'
10
+ RSpec::Core::RakeTask.new(:spec) do |spec|
11
+ spec.pattern = 'spec/**/*_spec.rb'
12
+ end
13
+ rescue LoadError
14
+ task :spec do
15
+ abort "Rspec is not available. bundle install to run unit tests"
16
+ end
17
+ end
18
+
19
+ task :default => :spec
@@ -0,0 +1,9 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem "rails", "~> 3.0.0"
4
+ gem "rake"
5
+ gem "rspec"
6
+
7
+ gem "opbeat", :path => "../"
8
+
9
+ gemspec :path => "../"
@@ -0,0 +1,9 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem "rails", "~> 3.1.0"
4
+ gem "rake"
5
+ gem "rspec"
6
+
7
+ gem "opbeat", :path => "../"
8
+
9
+ gemspec :path => "../"
@@ -0,0 +1,9 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem "rails", "~> 3.2.0"
4
+ gem "rake"
5
+ gem "rspec"
6
+
7
+ gem "opbeat", :path => "../"
8
+
9
+ gemspec :path => "../"
@@ -0,0 +1,9 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem "rails", "~> 4.0.0"
4
+ gem "rake"
5
+ gem "rspec"
6
+
7
+ gem "opbeat", :path => "../"
8
+
9
+ gemspec :path => "../"
@@ -0,0 +1,9 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem "rails", "~> 4.1.0"
4
+ gem "rake"
5
+ gem "rspec"
6
+
7
+ gem "opbeat", :path => "../"
8
+
9
+ gemspec :path => "../"
@@ -0,0 +1,9 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem "rails", "~> 4.2.0"
4
+ gem "rake"
5
+ gem "rspec"
6
+
7
+ gem "opbeat", :path => "../"
8
+
9
+ gemspec :path => "../"
@@ -0,0 +1,10 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem "i18n", "~> 0.6.11" # pin to <0.7 to support ruby 1.9.2
4
+ gem "rails", "~> 3.1.0"
5
+ gem "rake"
6
+ gem "rspec"
7
+
8
+ gem "opbeat", :path => "../"
9
+
10
+ gemspec :path => "../"
@@ -0,0 +1,10 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem "i18n", "~> 0.6.11" # pin to <0.7 to support ruby 1.9.2
4
+ gem "rails", "~> 3.2.0"
5
+ gem "rake"
6
+ gem "rspec"
7
+
8
+ gem "opbeat", :path => "../"
9
+
10
+ gemspec :path => "../"
@@ -0,0 +1,11 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem "rails", "~> 3.2.6"
4
+ gem "rake"
5
+ gem "rspec"
6
+ gem "simplecov"
7
+ gem "delayed_job"
8
+ gem "sidekiq", "~> 3.1.0"
9
+ gem "opbeat", :path => "../"
10
+
11
+ gemspec :path => "../"
@@ -0,0 +1,132 @@
1
+ require 'opbeat/version'
2
+ require 'opbeat/configuration'
3
+ require 'opbeat/logger'
4
+ require 'opbeat/client'
5
+ require 'opbeat/event'
6
+ require 'opbeat/rack'
7
+ require 'opbeat/interfaces/message'
8
+ require 'opbeat/interfaces/exception'
9
+ require 'opbeat/interfaces/stack_trace'
10
+ require 'opbeat/interfaces/http'
11
+
12
+ require 'opbeat/integrations/delayed_job'
13
+ require 'opbeat/integrations/sidekiq'
14
+
15
+ require 'opbeat/railtie' if defined?(::Rails::Railtie)
16
+
17
+
18
+ module Opbeat
19
+ class << self
20
+ # The client object is responsible for delivering formatted data to the Opbeat server.
21
+ # Must respond to #send_event. See Opbeat::Client.
22
+ attr_accessor :client
23
+
24
+ # A Opbeat configuration object. Must act like a hash and return sensible
25
+ # values for all Opbeat configuration options. See Opbeat::Configuration.
26
+ attr_writer :configuration
27
+
28
+ def logger
29
+ @logger ||= Logger.new
30
+ end
31
+
32
+ # Tell the log that the client is good to go
33
+ def report_ready
34
+ self.logger.info "Opbeat #{VERSION} ready to catch errors"
35
+ end
36
+
37
+ # The configuration object.
38
+ # @see Opbeat.configure
39
+ def configuration
40
+ @configuration ||= Configuration.new
41
+ end
42
+
43
+ # Call this method to modify defaults in your initializers.
44
+ #
45
+ # @example
46
+ # Opbeat.configure do |config|
47
+ # config.server = 'http://...'
48
+ # end
49
+ def configure(silent = false)
50
+ yield(configuration)
51
+ self.client = Client.new(configuration)
52
+ report_ready unless silent
53
+ self.client
54
+ end
55
+
56
+ # Send an event to the configured Opbeat server
57
+ #
58
+ # @example
59
+ # evt = Opbeat::Event.new(:message => "An error")
60
+ # Opbeat.send(evt)
61
+ def send(evt)
62
+ @client.send_event(evt) if @client
63
+ end
64
+
65
+ # Capture and process any exceptions from the given block, or globally if
66
+ # no block is given
67
+ #
68
+ # @example
69
+ # Opbeat.capture do
70
+ # MyApp.run
71
+ # end
72
+ def capture(&block)
73
+ if block
74
+ begin
75
+ block.call
76
+ rescue Error => e
77
+ raise # Don't capture Opbeat errors
78
+ rescue Exception => e
79
+ self.captureException(e)
80
+ raise
81
+ end
82
+ else
83
+ # Install at_exit hook
84
+ at_exit do
85
+ if $!
86
+ logger.debug "Caught a post-mortem exception: #{$!.inspect}"
87
+ self.capture_exception($!)
88
+ end
89
+ end
90
+ end
91
+ end
92
+
93
+ def capture_exception(exception, options={})
94
+ exception.set_backtrace caller unless exception.backtrace
95
+ if (evt = Event.from_exception(exception, options))
96
+ if self.configuration.async?
97
+ self.configuration.async.call(evt)
98
+ else
99
+ send(evt)
100
+ end
101
+ end
102
+ end
103
+
104
+ def capture_rack_exception(exception, env, options={})
105
+ exception.set_backtrace caller unless exception.backtrace
106
+ if (evt = Event.from_rack_exception(exception, env, options))
107
+ if self.configuration.async?
108
+ self.configuration.async.call(evt)
109
+ else
110
+ send(evt)
111
+ end
112
+ end
113
+ end
114
+
115
+ def capture_message(message, options={})
116
+ if (evt = Event.from_message(message, caller, options))
117
+ if self.configuration.async?
118
+ self.configuration.async.call(evt)
119
+ else
120
+ send(evt)
121
+ end
122
+ end
123
+ end
124
+
125
+ def set_context(options={})
126
+ Event.set_context(options)
127
+ end
128
+
129
+ alias :captureException :capture_exception
130
+ alias :captureMessage :capture_message
131
+ end
132
+ end
@@ -0,0 +1,44 @@
1
+ require 'set'
2
+
3
+ module Opbeat
4
+ module BetterAttrAccessor
5
+
6
+ def attributes
7
+ Hash[
8
+ self.class.attributes.map do |attr|
9
+ [attr, send(attr)]
10
+ end
11
+ ]
12
+ end
13
+
14
+ def self.included(base)
15
+ base.extend ClassMethods
16
+ end
17
+
18
+ module ClassMethods
19
+ def attributes
20
+ @attributes ||= Set.new
21
+
22
+ if superclass.include? BetterAttrAccessor
23
+ @attributes + superclass.attributes
24
+ else
25
+ @attributes
26
+ end
27
+ end
28
+
29
+ def attr_accessor(attr, options = {})
30
+ @attributes ||= Set.new
31
+ @attributes << attr.to_s
32
+
33
+ define_method attr do
34
+ if instance_variable_defined? "@#{attr}"
35
+ instance_variable_get "@#{attr}"
36
+ elsif options.key? :default
37
+ instance_variable_set "@#{attr}", options[:default].dup
38
+ end
39
+ end
40
+ attr_writer attr
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,9 @@
1
+ require 'capistrano'
2
+ require 'capistrano/version'
3
+
4
+ is_cap3 = Capistrano.constants.include? :VERSION
5
+ if is_cap3
6
+ load File.expand_path('../capistrano/capistrano3.rb', __FILE__)
7
+ else
8
+ require_relative 'capistrano/capistrano2'
9
+ end
@@ -0,0 +1,47 @@
1
+ require 'capistrano'
2
+
3
+ module Opbeat
4
+ module Capistrano
5
+ def self.load_into(configuration)
6
+
7
+ configuration.load do
8
+ after "deploy", "opbeat:notify"
9
+ after "deploy:migrations", "opbeat:notify"
10
+ after "deploy:cold", "opbeat:notify"
11
+ namespace :opbeat do
12
+ desc "Notifies Opbeat of new deployments"
13
+ task :notify, :except => { :no_release => true } do
14
+
15
+ scm = fetch(:scm)
16
+ if scm.to_s != "git"
17
+ puts "Skipping Opbeat deployment notification because scm is not git."
18
+ next
19
+ end
20
+
21
+ branches = capture("cd #{current_release}; /usr/bin/env git branch --contains #{current_revision}").split
22
+ if branches.length == 1
23
+ branch = branch[0].sub("* ")
24
+ else
25
+ branch = nil
26
+ end
27
+
28
+ notify_command = "cd #{current_release}; REV=#{current_revision} "
29
+ notify_command << "BRANCH=#{branch} " if branch
30
+
31
+ rails_env = fetch(:rails_env, "production")
32
+ notify_command << "RAILS_ENV=#{rails_env} "
33
+
34
+ executable = fetch(:rake, 'bundle exec rake ')
35
+ notify_command << "#{executable} opbeat:deployment"
36
+ capture notify_command, :once => true
37
+
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+
45
+ if Capistrano::Configuration.instance
46
+ Opbeat::Capistrano.load_into(Capistrano::Configuration.instance)
47
+ end
@@ -0,0 +1,26 @@
1
+ namespace :opbeat do
2
+ desc "Notifies Opbeat of new deployments"
3
+ task :notify do
4
+ on roles(:app) do
5
+
6
+ scm = fetch(:scm)
7
+ if scm.to_s != "git"
8
+ info "Skipping Opbeat deployment because scm is not git."
9
+ next
10
+ end
11
+
12
+ rev = fetch(:current_revision)
13
+ branch = fetch(:branch, 'master')
14
+
15
+ within release_path do
16
+ with rails_env: fetch(:rails_env), rev: rev, branch: branch do
17
+ capture :rake, 'opbeat:deployment'
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+
24
+ namespace :deploy do
25
+ after :publishing, "opbeat:notify"
26
+ end
@@ -0,0 +1,122 @@
1
+ require 'openssl'
2
+ require 'uri'
3
+ require 'multi_json'
4
+ require 'faraday'
5
+
6
+ require 'opbeat/version'
7
+ require 'opbeat/error'
8
+ require 'opbeat/filter'
9
+
10
+ module Opbeat
11
+
12
+ class ClientState
13
+ def initialize(configuration)
14
+ @configuration = configuration
15
+ @retry_number = 0
16
+ @last_check = Time.now
17
+ end
18
+
19
+ def should_try?
20
+ return true if @status == :online
21
+
22
+ interval = ([@retry_number, 6].min() ** 2) * @configuration[:backoff_multiplier]
23
+ return true if Time.now - @last_check > interval
24
+
25
+ false
26
+ end
27
+
28
+ def set_fail
29
+ @status = :error
30
+ @retry_number += 1
31
+ @last_check = Time.now
32
+ end
33
+
34
+ def set_success
35
+ @status = :online
36
+ @retry_number = 0
37
+ @last_check = nil
38
+ end
39
+ end
40
+
41
+ class Client
42
+
43
+ USER_AGENT = "opbeat-ruby/#{Opbeat::VERSION}"
44
+
45
+ attr_accessor :configuration
46
+ attr_accessor :state
47
+
48
+ def initialize(conf)
49
+ raise Error.new('No server specified') unless conf.server
50
+ raise Error.new('No secret token specified') unless conf.secret_token
51
+ raise Error.new('No organization ID specified') unless conf.organization_id
52
+ raise Error.new('No app ID specified') unless conf.app_id
53
+
54
+ @configuration = conf
55
+ @state = ClientState.new conf
56
+ @filter = Filter.new conf.filter_parameters
57
+ @base_url = "#{conf.server}/api/v1/organizations/#{conf.organization_id}/apps/#{conf.app_id}"
58
+ @auth_header = 'Bearer ' + conf.secret_token
59
+ end
60
+
61
+ def conn
62
+ @conn ||= Faraday.new(@base_url) do |faraday|
63
+ Opbeat.logger.debug "Initializing connection to #{self.configuration.server}"
64
+ faraday.adapter Faraday.default_adapter
65
+ faraday.ssl[:verify] = self.configuration.ssl_verification
66
+ faraday.options[:timeout] = self.configuration.timeout if self.configuration.timeout
67
+ faraday.options[:open_timeout] = self.configuration.open_timeout if self.configuration.open_timeout
68
+ end
69
+ end
70
+
71
+ def encode(event)
72
+ event_hash = event.to_hash
73
+ event_hash = @filter.process_event_hash(event_hash)
74
+ return MultiJson.encode(event_hash)
75
+ end
76
+
77
+ def send(url_postfix, message)
78
+ begin
79
+ response = self.conn.post @base_url + url_postfix do |req|
80
+ req.body = self.encode(message)
81
+ req.headers['Authorization'] = @auth_header
82
+ req.headers['Content-Type'] = 'application/json'
83
+ req.headers['Content-Length'] = req.body.bytesize.to_s
84
+ req.headers['User-Agent'] = USER_AGENT
85
+ end
86
+ unless response.status.between?(200, 299)
87
+ raise Error.new("Error from Opbeat server (#{response.status}): #{response.body}")
88
+ end
89
+ rescue
90
+ @state.set_fail
91
+ raise
92
+ end
93
+
94
+ @state.set_success
95
+ response
96
+ end
97
+
98
+ def send_event(event)
99
+ return unless configuration.send_in_current_environment?
100
+ unless state.should_try?
101
+ Opbeat.logger.info "Temporarily skipping sending to Opbeat due to previous failure."
102
+ return
103
+ end
104
+
105
+ # Set the organization ID correctly
106
+ event.organization = self.configuration.organization_id
107
+ event.app = self.configuration.app_id
108
+ Opbeat.logger.debug "Sending event to Opbeat"
109
+ response = send("/errors/", event)
110
+ if response.status.between?(200, 299)
111
+ Opbeat.logger.info "Event logged successfully at " + response.headers["location"].to_s
112
+ end
113
+ response
114
+ end
115
+
116
+ def send_release(release)
117
+ Opbeat.logger.debug "Sending release to Opbeat"
118
+ send("/releases/", release)
119
+ end
120
+ end
121
+
122
+ end