opbeat 2.0.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (130) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +4 -3
  3. data/.travis.yml +19 -28
  4. data/.yardopts +3 -0
  5. data/Gemfile +4 -2
  6. data/HISTORY.md +3 -0
  7. data/LICENSE +7 -196
  8. data/README.md +96 -177
  9. data/Rakefile +19 -13
  10. data/gemfiles/Gemfile.base +28 -0
  11. data/gemfiles/Gemfile.rails-3.2.x +3 -0
  12. data/gemfiles/Gemfile.rails-4.0.x +3 -0
  13. data/gemfiles/Gemfile.rails-4.1.x +3 -0
  14. data/gemfiles/Gemfile.rails-4.2.x +3 -0
  15. data/lib/opbeat.rb +113 -93
  16. data/lib/opbeat/capistrano.rb +3 -4
  17. data/lib/opbeat/client.rb +243 -82
  18. data/lib/opbeat/configuration.rb +51 -64
  19. data/lib/opbeat/data_builders.rb +16 -0
  20. data/lib/opbeat/data_builders/error.rb +27 -0
  21. data/lib/opbeat/data_builders/transactions.rb +85 -0
  22. data/lib/opbeat/error.rb +1 -2
  23. data/lib/opbeat/error_message.rb +71 -0
  24. data/lib/opbeat/error_message/exception.rb +12 -0
  25. data/lib/opbeat/error_message/http.rb +62 -0
  26. data/lib/opbeat/error_message/stacktrace.rb +75 -0
  27. data/lib/opbeat/error_message/user.rb +23 -0
  28. data/lib/opbeat/filter.rb +53 -43
  29. data/lib/opbeat/http_client.rb +141 -0
  30. data/lib/opbeat/injections.rb +83 -0
  31. data/lib/opbeat/injections/json.rb +19 -0
  32. data/lib/opbeat/injections/net_http.rb +43 -0
  33. data/lib/opbeat/injections/redis.rb +23 -0
  34. data/lib/opbeat/injections/sequel.rb +32 -0
  35. data/lib/opbeat/injections/sinatra.rb +56 -0
  36. data/lib/opbeat/{capistrano → integration}/capistrano2.rb +6 -6
  37. data/lib/opbeat/{capistrano → integration}/capistrano3.rb +3 -3
  38. data/lib/opbeat/{integrations → integration}/delayed_job.rb +6 -11
  39. data/lib/opbeat/integration/rails/inject_exceptions_catcher.rb +23 -0
  40. data/lib/opbeat/integration/railtie.rb +53 -0
  41. data/lib/opbeat/integration/resque.rb +16 -0
  42. data/lib/opbeat/integration/sidekiq.rb +38 -0
  43. data/lib/opbeat/line_cache.rb +21 -0
  44. data/lib/opbeat/logging.rb +37 -0
  45. data/lib/opbeat/middleware.rb +59 -0
  46. data/lib/opbeat/normalizers.rb +65 -0
  47. data/lib/opbeat/normalizers/action_controller.rb +21 -0
  48. data/lib/opbeat/normalizers/action_view.rb +71 -0
  49. data/lib/opbeat/normalizers/active_record.rb +41 -0
  50. data/lib/opbeat/sql_summarizer.rb +27 -0
  51. data/lib/opbeat/subscriber.rb +80 -0
  52. data/lib/opbeat/tasks.rb +20 -18
  53. data/lib/opbeat/trace.rb +47 -0
  54. data/lib/opbeat/trace_helpers.rb +29 -0
  55. data/lib/opbeat/transaction.rb +99 -0
  56. data/lib/opbeat/util.rb +26 -0
  57. data/lib/opbeat/util/constantize.rb +54 -0
  58. data/lib/opbeat/util/inspector.rb +75 -0
  59. data/lib/opbeat/version.rb +1 -1
  60. data/lib/opbeat/worker.rb +55 -0
  61. data/opbeat.gemspec +6 -14
  62. data/spec/opbeat/client_spec.rb +216 -29
  63. data/spec/opbeat/configuration_spec.rb +34 -38
  64. data/spec/opbeat/data_builders/error_spec.rb +43 -0
  65. data/spec/opbeat/data_builders/transactions_spec.rb +51 -0
  66. data/spec/opbeat/error_message/exception_spec.rb +22 -0
  67. data/spec/opbeat/error_message/http_spec.rb +65 -0
  68. data/spec/opbeat/error_message/stacktrace_spec.rb +56 -0
  69. data/spec/opbeat/error_message/user_spec.rb +28 -0
  70. data/spec/opbeat/error_message_spec.rb +78 -0
  71. data/spec/opbeat/filter_spec.rb +21 -99
  72. data/spec/opbeat/http_client_spec.rb +64 -0
  73. data/spec/opbeat/injections/net_http_spec.rb +37 -0
  74. data/spec/opbeat/injections/sequel_spec.rb +33 -0
  75. data/spec/opbeat/injections/sinatra_spec.rb +13 -0
  76. data/spec/opbeat/injections_spec.rb +49 -0
  77. data/spec/opbeat/integration/delayed_job_spec.rb +35 -0
  78. data/spec/opbeat/integration/json_spec.rb +41 -0
  79. data/spec/opbeat/integration/rails_spec.rb +88 -0
  80. data/spec/opbeat/integration/redis_spec.rb +20 -0
  81. data/spec/opbeat/integration/resque_spec.rb +42 -0
  82. data/spec/opbeat/integration/sidekiq_spec.rb +40 -0
  83. data/spec/opbeat/integration/sinatra_spec.rb +66 -0
  84. data/spec/opbeat/line_cache_spec.rb +38 -0
  85. data/spec/opbeat/logging_spec.rb +47 -0
  86. data/spec/opbeat/middleware_spec.rb +32 -0
  87. data/spec/opbeat/normalizers/action_controller_spec.rb +32 -0
  88. data/spec/opbeat/normalizers/action_view_spec.rb +77 -0
  89. data/spec/opbeat/normalizers/active_record_spec.rb +70 -0
  90. data/spec/opbeat/normalizers_spec.rb +16 -0
  91. data/spec/opbeat/sql_summarizer_spec.rb +6 -0
  92. data/spec/opbeat/subscriber_spec.rb +83 -0
  93. data/spec/opbeat/trace_spec.rb +43 -0
  94. data/spec/opbeat/transaction_spec.rb +98 -0
  95. data/spec/opbeat/util/inspector_spec.rb +40 -0
  96. data/spec/opbeat/util_spec.rb +20 -0
  97. data/spec/opbeat/worker_spec.rb +54 -0
  98. data/spec/opbeat_spec.rb +49 -0
  99. data/spec/spec_helper.rb +79 -6
  100. metadata +89 -149
  101. data/Makefile +0 -3
  102. data/gemfiles/rails30.gemfile +0 -9
  103. data/gemfiles/rails31.gemfile +0 -9
  104. data/gemfiles/rails32.gemfile +0 -9
  105. data/gemfiles/rails40.gemfile +0 -9
  106. data/gemfiles/rails41.gemfile +0 -9
  107. data/gemfiles/rails42.gemfile +0 -9
  108. data/gemfiles/ruby192_rails31.gemfile +0 -10
  109. data/gemfiles/ruby192_rails32.gemfile +0 -10
  110. data/gemfiles/sidekiq31.gemfile +0 -11
  111. data/lib/opbeat/better_attr_accessor.rb +0 -44
  112. data/lib/opbeat/event.rb +0 -223
  113. data/lib/opbeat/integrations/resque.rb +0 -22
  114. data/lib/opbeat/integrations/sidekiq.rb +0 -32
  115. data/lib/opbeat/interfaces.rb +0 -35
  116. data/lib/opbeat/interfaces/exception.rb +0 -16
  117. data/lib/opbeat/interfaces/http.rb +0 -57
  118. data/lib/opbeat/interfaces/message.rb +0 -19
  119. data/lib/opbeat/interfaces/stack_trace.rb +0 -50
  120. data/lib/opbeat/linecache.rb +0 -25
  121. data/lib/opbeat/logger.rb +0 -21
  122. data/lib/opbeat/rack.rb +0 -46
  123. data/lib/opbeat/rails/middleware/debug_exceptions_catcher.rb +0 -22
  124. data/lib/opbeat/railtie.rb +0 -26
  125. data/spec/opbeat/better_attr_accessor_spec.rb +0 -99
  126. data/spec/opbeat/event_spec.rb +0 -138
  127. data/spec/opbeat/integrations/delayed_job_spec.rb +0 -38
  128. data/spec/opbeat/logger_spec.rb +0 -55
  129. data/spec/opbeat/opbeat_spec.rb +0 -64
  130. data/spec/opbeat/rack_spec.rb +0 -117
data/Rakefile CHANGED
@@ -1,19 +1,25 @@
1
- require 'rake'
2
1
  require 'rubygems/package_task'
3
-
4
2
  gemspec = Gem::Specification.load(Dir['*.gemspec'].first)
5
-
6
3
  Gem::PackageTask.new(gemspec).define
7
4
 
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
5
+ require 'rspec/core/rake_task'
6
+ RSpec::Core::RakeTask.new(:spec) do |spec|
7
+ spec.pattern = 'spec/**/*_spec.rb'
17
8
  end
18
-
19
9
  task :default => :spec
10
+
11
+ require 'yard'
12
+ YARD::Rake::YardocTask.new
13
+
14
+ task :mem_profile do
15
+ require 'memory_profiler'
16
+ $:.unshift Dir.pwd + '/lib'
17
+
18
+ filename = "profile-#{Time.now.to_i}.txt"
19
+
20
+ MemoryProfiler.report(allow_files: /opbeat/i) do
21
+ require 'opbeat'
22
+ end.pretty_print(to_file: filename)
23
+
24
+ filename
25
+ end
@@ -0,0 +1,28 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec path: File.expand_path('../..', __FILE__)
4
+
5
+ gem 'rake'
6
+ gem 'rspec'
7
+ gem 'timecop'
8
+ gem 'webmock'
9
+ gem 'rack-test'
10
+
11
+ gem 'yard'
12
+ gem 'simplecov'
13
+
14
+ # external libs
15
+
16
+ gem 'sinatra'
17
+ gem 'redis'
18
+ gem 'fakeredis'
19
+ gem 'sqlite3'
20
+ gem 'sequel'
21
+
22
+ gem 'delayed_job', require: false
23
+ gem 'resque', require: false
24
+
25
+ unless RUBY_VERSION.to_i <= 1
26
+ gem 'sidekiq', require: false
27
+ end
28
+
@@ -0,0 +1,3 @@
1
+ eval_gemfile File.expand_path('../Gemfile.base', __FILE__)
2
+
3
+ gem 'rails', '~> 3.2.0'
@@ -0,0 +1,3 @@
1
+ eval_gemfile File.expand_path('../Gemfile.base', __FILE__)
2
+
3
+ gem 'rails', '~> 4.0.0'
@@ -0,0 +1,3 @@
1
+ eval_gemfile File.expand_path('../Gemfile.base', __FILE__)
2
+
3
+ gem 'rails', '~> 4.1.0'
@@ -0,0 +1,3 @@
1
+ eval_gemfile File.expand_path('../Gemfile.base', __FILE__)
2
+
3
+ gem 'rails', '~> 4.2.0'
data/lib/opbeat.rb CHANGED
@@ -1,121 +1,141 @@
1
1
  require 'opbeat/version'
2
2
  require 'opbeat/configuration'
3
- require 'opbeat/logger'
3
+
4
+ require 'opbeat/logging'
4
5
  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'
6
+ require 'opbeat/error'
7
+ require 'opbeat/trace_helpers'
8
+
9
+ require 'opbeat/middleware'
11
10
 
12
- require 'opbeat/integrations/delayed_job'
13
- require 'opbeat/integrations/sidekiq'
11
+ require 'opbeat/integration/railtie' if defined?(Rails)
14
12
 
15
- require 'opbeat/railtie' if defined?(::Rails::Railtie)
13
+ require 'opbeat/injections'
14
+ require 'opbeat/injections/net_http'
15
+ require 'opbeat/injections/redis'
16
+ require 'opbeat/injections/sinatra'
17
+ require 'opbeat/injections/sequel'
16
18
 
19
+ require 'opbeat/integration/delayed_job'
20
+ require 'opbeat/integration/sidekiq'
21
+ require 'opbeat/integration/resque'
17
22
 
18
23
  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
24
+ # Start the Opbeat client
25
+ #
26
+ # @param conf [Configuration] An Configuration object
27
+ def self.start! conf
28
+ Client.start! conf
29
+ end
23
30
 
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
31
+ # Stop the Opbeat client
32
+ def self.stop!
33
+ Client.stop!
34
+ end
27
35
 
28
- def logger
29
- @logger ||= Logger.new
30
- end
36
+ def self.started?
37
+ !!Client.inst
38
+ end
31
39
 
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"
40
+ # Start a new transaction or return the currently running
41
+ #
42
+ # @param endpoint [String] A description of the transaction, eg `ExamplesController#index`
43
+ # @param kind [String] The kind of the transaction, eg `app.request.get` or `db.mysql2.query`
44
+ # @param result [Object] Result of the transaction, eq `200` for a HTTP server
45
+ # @yield [Transaction] Optional block encapsulating transaction
46
+ # @return [Transaction] Unless block given
47
+ def self.transaction endpoint, kind = nil, result = nil, &block
48
+ unless client
49
+ return yield if block_given?
50
+ return nil
35
51
  end
36
52
 
37
- # The configuration object.
38
- # @see Opbeat.configure
39
- def configuration
40
- @configuration ||= Configuration.new
41
- end
53
+ client.transaction endpoint, kind, result, &block
54
+ end
42
55
 
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
56
+ # Starts a new trace under the current Transaction
57
+ #
58
+ # @param signature [String] A description of the trace, eq `SELECT FROM "users"`
59
+ # @param kind [String] The kind of trace, eq `db.mysql2.query`
60
+ # @param extra [Hash] Extra information about the trace
61
+ # @yield [Trace] Optional block encapsulating trace
62
+ # @return [Trace] Unless block given
63
+ def self.trace signature, kind = nil, extra = nil, &block
64
+ unless client
65
+ return yield if block_given?
66
+ return nil
54
67
  end
55
68
 
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
69
+ client.trace signature, kind, extra, &block
70
+ end
71
+
72
+ def self.flush_transactions
73
+ unless client
74
+ return yield if block_given?
75
+ return nil
63
76
  end
64
77
 
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
78
+ client.flush_transactions
79
+ end
80
+
81
+ # Send an exception to Opbeat
82
+ #
83
+ # @param exception [Exception]
84
+ # @param opts [Hash]
85
+ # @option opts [Hash] :rack_env A rack env object
86
+ # @return [Net::HTTPResponse]
87
+ def self.report exception, opts = {}
88
+ unless client
89
+ return yield if block_given?
90
+ return nil
91
91
  end
92
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
93
+ client.report exception, opts
94
+ end
95
+
96
+ # Send an exception to Opbeat
97
+ #
98
+ # @param message [String]
99
+ # @param opts [Hash]
100
+ # @return [Net::HTTPResponse]
101
+ def self.report_message message, opts = {}
102
+ unless client
103
+ return yield if block_given?
104
+ return nil
102
105
  end
103
106
 
104
- def capture_message(message, options={})
105
- if (evt = Event.from_message(message, caller, options))
106
- if self.configuration.async?
107
- self.configuration.async.call(evt)
108
- else
109
- send(evt)
110
- end
111
- end
107
+ client.report_message message, opts
108
+ end
109
+
110
+ # Captures any exceptions raised inside the block
111
+ #
112
+ def self.capture &block
113
+ unless client
114
+ return yield if block_given?
115
+ return nil
112
116
  end
113
117
 
114
- def set_context(options={})
115
- Event.set_context(options)
118
+ client.capture(&block)
119
+ end
120
+
121
+ # Notify Opbeat of a release
122
+ #
123
+ # @param rel [Hash]
124
+ # @option rel [String] :rev Revision
125
+ # @option rel [String] :branch
126
+ # @return [Net::HTTPResponse]
127
+ def self.release rel, opts = {}
128
+ unless client
129
+ return yield if block_given?
130
+ return nil
116
131
  end
117
132
 
118
- alias :captureException :capture_exception
119
- alias :captureMessage :capture_message
133
+ client.release rel, opts
134
+ end
135
+
136
+ private
137
+
138
+ def self.client
139
+ Client.inst
120
140
  end
121
141
  end
@@ -1,9 +1,8 @@
1
1
  require 'capistrano'
2
2
  require 'capistrano/version'
3
3
 
4
- is_cap3 = Capistrano.constants.include? :VERSION
5
- if is_cap3
6
- load File.expand_path('../capistrano/capistrano3.rb', __FILE__)
4
+ if Capistrano::VERSION.to_i <= 2
5
+ require 'opbeat/integration/capistrano2'
7
6
  else
8
- require_relative 'capistrano/capistrano2'
7
+ require 'opbeat/integration/capistrano3'
9
8
  end
data/lib/opbeat/client.rb CHANGED
@@ -1,122 +1,283 @@
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'
1
+ require 'thread'
2
+ require 'opbeat/subscriber'
3
+ require 'opbeat/http_client'
4
+ require 'opbeat/worker'
5
+ require 'opbeat/transaction'
6
+ require 'opbeat/trace'
7
+ require 'opbeat/error_message'
8
+ require 'opbeat/data_builders'
9
9
 
10
10
  module Opbeat
11
+ # @api private
12
+ class Client
13
+ include Logging
14
+
15
+ KEY = :__opbeat_transaction_key
16
+ LOCK = Mutex.new
11
17
 
12
- class ClientState
13
- def initialize(configuration)
14
- @configuration = configuration
15
- @retry_number = 0
16
- @last_check = Time.now
18
+ class TransactionInfo
19
+ def current
20
+ Thread.current[KEY]
21
+ end
22
+ def current= transaction
23
+ Thread.current[KEY] = transaction
24
+ end
17
25
  end
18
26
 
19
- def should_try?
20
- return true if @status == :online
27
+ # life cycle
21
28
 
22
- interval = ([@retry_number, 6].min() ** 2) * @configuration[:backoff_multiplier]
23
- return true if Time.now - @last_check > interval
29
+ def self.inst
30
+ @instance
31
+ end
24
32
 
25
- false
33
+ def self.start! config = nil
34
+ return @instance if @instance
35
+
36
+ LOCK.synchronize do
37
+ return @instance if @instance
38
+ @instance = new(config).start!
39
+ end
26
40
  end
27
41
 
28
- def set_fail
29
- @status = :error
30
- @retry_number += 1
31
- @last_check = Time.now
42
+ def self.stop!
43
+ LOCK.synchronize do
44
+ return unless @instance
45
+
46
+ @instance.stop!
47
+ @instance = nil
48
+ end
32
49
  end
33
50
 
34
- def set_success
35
- @status = :online
36
- @retry_number = 0
37
- @last_check = nil
51
+ def initialize config
52
+ @config = config
53
+
54
+ @http_client = HttpClient.new config
55
+ @queue = Queue.new
56
+
57
+ @data_builders = Struct.new(:transactions, :error_message).new(
58
+ DataBuilders::Transactions.new(config),
59
+ DataBuilders::Error.new(config)
60
+ )
61
+
62
+ unless config.disable_performance
63
+ @transaction_info = TransactionInfo.new
64
+ @subscriber = Subscriber.new config, self
65
+ end
66
+
67
+ @pending_transactions = []
68
+ @last_sent_transactions = Time.now.utc
38
69
  end
39
- end
40
70
 
41
- class Client
71
+ attr_reader :config, :queue, :pending_transactions
42
72
 
43
- USER_AGENT = "opbeat-ruby/#{Opbeat::VERSION}"
73
+ def start!
74
+ info "Starting client"
44
75
 
45
- attr_accessor :configuration
46
- attr_accessor :state
76
+ @subscriber.register! if @subscriber
47
77
 
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
78
+ self
79
+ end
53
80
 
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
81
+ def stop!
82
+ flush_transactions
83
+ kill_worker
84
+ unregister! if @subscriber
59
85
  end
60
86
 
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
87
+ at_exit do
88
+ stop!
89
+ end
90
+
91
+ # metrics
92
+
93
+ def current_transaction
94
+ @transaction_info.current
69
95
  end
70
96
 
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)
97
+ def current_transaction= transaction
98
+ @transaction_info.current = transaction
75
99
  end
76
100
 
77
- def send(url_postfix, message)
101
+ def transaction endpoint, kind = nil, result = nil, &block
102
+ if config.disable_performance
103
+ return yield if block_given?
104
+ return nil
105
+ end
106
+
107
+ if transaction = current_transaction
108
+ yield transaction if block_given?
109
+ return transaction
110
+ end
111
+
112
+ transaction = Transaction.new self, endpoint, kind, result
113
+
114
+ self.current_transaction = transaction
115
+ return transaction unless block_given?
116
+
78
117
  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
118
+ yield transaction
119
+
120
+ ensure
121
+ self.current_transaction = nil
122
+ transaction.done
123
+ end
124
+
125
+ transaction
126
+ end
127
+
128
+ def trace *args, &block
129
+ if config.disable_performance
130
+ return yield if block_given?
131
+ return nil
132
+ end
133
+
134
+ unless transaction = current_transaction
135
+ return yield if block_given?
136
+ return
137
+ end
138
+
139
+ transaction.trace(*args, &block)
140
+ end
141
+
142
+ def submit_transaction transaction
143
+ ensure_worker_running
144
+
145
+ if config.debug_traces
146
+ unless transaction.endpoint == 'Rack'
147
+ debug { Util::Inspector.new.transaction transaction, include_parents: true }
85
148
  end
86
- unless response.status.between?(200, 299)
87
- raise Error.new("Error from Opbeat server (#{response.status}): #{response.body}")
149
+ end
150
+
151
+ @pending_transactions << transaction
152
+
153
+ if should_send_transactions?
154
+ flush_transactions
155
+ end
156
+ end
157
+
158
+ def flush_transactions
159
+ return if @pending_transactions.empty?
160
+
161
+ data = @data_builders.transactions.build(@pending_transactions)
162
+ enqueue Worker::PostRequest.new('/transactions/', data)
163
+
164
+ @last_sent_transactions = Time.now.utc
165
+ @pending_transactions = []
166
+
167
+ true
168
+ end
169
+
170
+ # errors
171
+
172
+ def report exception, opts = {}
173
+ return if config.disable_errors
174
+ return unless exception
175
+
176
+ unless exception.backtrace
177
+ exception.set_backtrace caller
178
+ end
179
+
180
+ error_message = ErrorMessage.from_exception(config, exception, opts)
181
+ data = @data_builders.error_message.build error_message
182
+ enqueue Worker::PostRequest.new('/errors/', data)
183
+ end
184
+
185
+ def report_message message, opts = {}
186
+ return if config.disable_errors
187
+
188
+ error_message = ErrorMessage.new(config, message, opts)
189
+ data = @data_builders.error_message.build error_message
190
+ enqueue Worker::PostRequest.new('/errors/', data)
191
+ end
192
+
193
+ def capture &block
194
+ unless block_given?
195
+ return Kernel.at_exit do
196
+ if $!
197
+ debug $!.inspect
198
+ report $!
199
+ end
88
200
  end
89
- rescue
90
- @state.set_fail
201
+ end
202
+
203
+ begin
204
+ yield
205
+ rescue Error => e
206
+ raise # Don't capture Opbeat errors
207
+ rescue Exception => e
208
+ report e
91
209
  raise
92
210
  end
211
+ end
93
212
 
94
- @state.set_success
95
- response
213
+ # releases
214
+
215
+ def release rel, opts = {}
216
+ rev = rel[:rev]
217
+ if opts[:inline]
218
+ debug "Sending release #{rev}"
219
+ @http_client.post '/releases/', rel
220
+ else
221
+ debug "Enqueuing release #{rev}"
222
+ enqueue Worker::PostRequest.new('/releases/', rel)
223
+ end
224
+ end
225
+
226
+ private
227
+
228
+ def enqueue request
229
+ @queue << request
96
230
  end
97
231
 
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."
232
+ def start_worker
233
+ return if worker_running?
234
+
235
+ if config.disable_worker
102
236
  return
103
237
  end
104
238
 
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
239
+ info "Starting worker in thread"
240
+
241
+ @worker_thread = Thread.new do
242
+ begin
243
+ Worker.new(config, @queue, @http_client).run
244
+ rescue => e
245
+ fatal "Failed booting worker:\n#{e.inspect}"
246
+ debug e.backtrace.join("\n")
247
+ raise
248
+ end
112
249
  end
113
- response
114
250
  end
115
251
 
116
- def send_release(release)
117
- Opbeat.logger.debug "Sending release to Opbeat"
118
- send("/releases/", release)
252
+ def kill_worker
253
+ return unless worker_running?
254
+ @queue << Worker::StopMessage.new
255
+ @worker_thread.join 1
256
+ @worker_thread = nil
119
257
  end
120
- end
121
258
 
259
+ def ensure_worker_running
260
+ return if worker_running?
261
+
262
+ LOCK.synchronize do
263
+ return if worker_running?
264
+ start_worker
265
+ end
266
+ end
267
+
268
+ def worker_running?
269
+ @worker_thread && @worker_thread.alive?
270
+ end
271
+
272
+ def unregister!
273
+ @subscriber.unregister!
274
+ end
275
+
276
+ def should_send_transactions?
277
+ return true if config.transaction_post_interval.nil?
278
+
279
+ Time.now.utc - @last_sent_transactions > config.transaction_post_interval
280
+ end
281
+
282
+ end
122
283
  end