kuende-opbeat 2.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +5 -0
- data/.rspec +2 -0
- data/.travis.yml +36 -0
- data/Gemfile +2 -0
- data/LICENSE +205 -0
- data/Makefile +3 -0
- data/README.md +267 -0
- data/Rakefile +19 -0
- data/gemfiles/rails30.gemfile +9 -0
- data/gemfiles/rails31.gemfile +9 -0
- data/gemfiles/rails32.gemfile +9 -0
- data/gemfiles/rails40.gemfile +9 -0
- data/gemfiles/rails41.gemfile +9 -0
- data/gemfiles/rails42.gemfile +9 -0
- data/gemfiles/ruby192_rails31.gemfile +10 -0
- data/gemfiles/ruby192_rails32.gemfile +10 -0
- data/gemfiles/sidekiq31.gemfile +11 -0
- data/lib/opbeat.rb +132 -0
- data/lib/opbeat/better_attr_accessor.rb +44 -0
- data/lib/opbeat/capistrano.rb +9 -0
- data/lib/opbeat/capistrano/capistrano2.rb +47 -0
- data/lib/opbeat/capistrano/capistrano3.rb +26 -0
- data/lib/opbeat/client.rb +122 -0
- data/lib/opbeat/configuration.rb +90 -0
- data/lib/opbeat/error.rb +6 -0
- data/lib/opbeat/event.rb +223 -0
- data/lib/opbeat/filter.rb +63 -0
- data/lib/opbeat/integrations/delayed_job.rb +32 -0
- data/lib/opbeat/integrations/resque.rb +22 -0
- data/lib/opbeat/integrations/sidekiq.rb +32 -0
- data/lib/opbeat/interfaces.rb +35 -0
- data/lib/opbeat/interfaces/exception.rb +16 -0
- data/lib/opbeat/interfaces/http.rb +57 -0
- data/lib/opbeat/interfaces/message.rb +19 -0
- data/lib/opbeat/interfaces/stack_trace.rb +50 -0
- data/lib/opbeat/linecache.rb +25 -0
- data/lib/opbeat/logger.rb +21 -0
- data/lib/opbeat/rack.rb +44 -0
- data/lib/opbeat/rails/middleware/debug_exceptions_catcher.rb +22 -0
- data/lib/opbeat/railtie.rb +26 -0
- data/lib/opbeat/tasks.rb +24 -0
- data/lib/opbeat/version.rb +3 -0
- data/opbeat.gemspec +28 -0
- data/spec/opbeat/better_attr_accessor_spec.rb +99 -0
- data/spec/opbeat/client_spec.rb +35 -0
- data/spec/opbeat/configuration_spec.rb +50 -0
- data/spec/opbeat/event_spec.rb +138 -0
- data/spec/opbeat/filter_spec.rb +101 -0
- data/spec/opbeat/integrations/delayed_job_spec.rb +38 -0
- data/spec/opbeat/logger_spec.rb +55 -0
- data/spec/opbeat/opbeat_spec.rb +89 -0
- data/spec/opbeat/rack_spec.rb +116 -0
- data/spec/spec_helper.rb +22 -0
- metadata +218 -0
data/Rakefile
ADDED
@@ -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
|
data/lib/opbeat.rb
ADDED
@@ -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,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
|