opbeat 2.0.0 → 3.0.0

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.
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