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
@@ -0,0 +1,65 @@
1
+ module Opbeat
2
+ # @api private
3
+ module Normalizers
4
+
5
+ class Normalizer
6
+ def self.register name
7
+ Normalizers.register name, self
8
+ end
9
+
10
+ def initialize config
11
+ @config = config
12
+ end
13
+
14
+ attr_reader :config
15
+ end
16
+
17
+ class Default < Normalizer
18
+ def normalize transaction, name, payload
19
+ :skip
20
+ end
21
+ end
22
+
23
+ DEFAULT = Default.new nil
24
+
25
+ def self.register name, cls
26
+ (@registered ||= {})[name] = cls
27
+ end
28
+
29
+ def self.build config
30
+ normalizers = @registered.reduce({}) do |coll, kv|
31
+ name, cls = kv
32
+ coll[name] = cls.new config
33
+ coll
34
+ end
35
+
36
+ Container.new(normalizers)
37
+ end
38
+
39
+ class Container
40
+ def initialize normalizers
41
+ @normalizers = normalizers
42
+ end
43
+
44
+ def keys
45
+ @normalizers.keys
46
+ end
47
+
48
+ def normalizer_for name
49
+ @normalizers[name] || DEFAULT
50
+ end
51
+
52
+ def normalize transaction, name, payload
53
+ normalizer_for(name).normalize transaction, name, payload
54
+ end
55
+ end
56
+
57
+ %w{
58
+ action_controller
59
+ active_record
60
+ action_view
61
+ }.each do |f|
62
+ require "opbeat/normalizers/#{f}"
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,21 @@
1
+ module Opbeat
2
+ module Normalizers
3
+ module ActionController
4
+ class ProcessAction < Normalizer
5
+ register 'process_action.action_controller'
6
+ KIND = 'app.controller.action'.freeze
7
+
8
+ def normalize transaction, name, payload
9
+ transaction.endpoint = endpoint(payload)
10
+ [transaction.endpoint, KIND, nil]
11
+ end
12
+
13
+ private
14
+
15
+ def endpoint payload
16
+ "#{payload[:controller]}##{payload[:action]}"
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,71 @@
1
+ module Opbeat
2
+ module Normalizers
3
+ module ActionView
4
+
5
+ class RenderNormalizer < Normalizer
6
+ def normalize_render payload, kind
7
+ signature = path_for(payload[:identifier])
8
+
9
+ [signature, kind, nil]
10
+ end
11
+
12
+ private
13
+
14
+ def path_for identifier
15
+ return "Unknown template".freeze unless path = identifier
16
+ return path unless path.start_with?("/")
17
+
18
+ path && relative_path(path)
19
+ end
20
+
21
+ def relative_path path
22
+ root = config.view_paths.find { |p| path.start_with? p }
23
+ type = :app
24
+
25
+ unless root
26
+ root = Gem.path.find { |p| path.start_with? p }
27
+ type = :gem
28
+ end
29
+
30
+ return "Absolute path".freeze unless root
31
+
32
+ start = root.length
33
+ start += 1 if path[root.length] == "/".freeze
34
+
35
+ if type == :gem
36
+ "$GEM_PATH/#{path[start, path.length]}"
37
+ else
38
+ path[start, path.length]
39
+ end
40
+ end
41
+ end
42
+
43
+ class RenderTemplate < RenderNormalizer
44
+ register 'render_template.action_view'
45
+ KIND = 'template.view'.freeze
46
+
47
+ def normalize transaction, name, payload
48
+ normalize_render(payload, KIND)
49
+ end
50
+ end
51
+
52
+ class RenderPartial < RenderNormalizer
53
+ register 'render_partial.action_view'
54
+ KIND = 'template.view.partial'.freeze
55
+
56
+ def normalize transaction, name, payload
57
+ normalize_render(payload, KIND)
58
+ end
59
+ end
60
+
61
+ class RenderCollection < RenderNormalizer
62
+ register 'render_collection.action_view'
63
+ KIND = 'template.view.collection'.freeze
64
+
65
+ def normalize transaction, name, payload
66
+ normalize_render(payload, KIND)
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,41 @@
1
+ require 'opbeat/sql_summarizer'
2
+
3
+ module Opbeat
4
+ module Normalizers
5
+ module ActiveRecord
6
+ class SQL < Normalizer
7
+ register 'sql.active_record'
8
+
9
+ def initialize *args
10
+ super(*args)
11
+ adapter = ::ActiveRecord::Base.connection.adapter_name.downcase rescue nil
12
+ @kind = "db.#{adapter || 'unknown'}.sql".freeze
13
+ @sql_parser = SqlSummarizer.new config
14
+ end
15
+
16
+ def normalize transaction, name, payload
17
+ if %w{SCHEMA CACHE}.include? payload[:name]
18
+ return :skip
19
+ end
20
+
21
+ signature =
22
+ signature_for(payload[:sql]) || # SELECT FROM "users"
23
+ payload[:name] || # Users load
24
+ "SQL".freeze
25
+
26
+ if signature == 'SELECT FROM "schema_migrations"'
27
+ return :skip
28
+ end
29
+
30
+ [signature, @kind, { sql: payload[:sql] }]
31
+ end
32
+
33
+ private
34
+
35
+ def signature_for sql
36
+ @sql_parser.signature_for(sql)
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,27 @@
1
+ module Opbeat
2
+ # @api private
3
+ class SqlSummarizer
4
+ CACHE = {}
5
+ TBL = "[^ ]+".freeze
6
+ REGEXES = {
7
+ /^SELECT .* FROM (#{TBL})/i => "SELECT FROM ".freeze,
8
+ /^INSERT INTO (#{TBL})/i => "INSERT INTO ".freeze,
9
+ /^UPDATE (#{TBL})/i => "UPDATE ".freeze,
10
+ /^DELETE FROM (#{TBL})/i => "DELETE FROM ".freeze
11
+ }.freeze
12
+
13
+ def initialize config
14
+ @config = config
15
+ end
16
+
17
+ def signature_for sql
18
+ return CACHE[sql] if CACHE[sql]
19
+
20
+ REGEXES.find do |regex, sig|
21
+ if match = sql.match(regex)
22
+ break sig + match[1]
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,80 @@
1
+ require 'active_support/notifications'
2
+ require 'opbeat/normalizers'
3
+
4
+ module Opbeat
5
+ # @api private
6
+ class Subscriber
7
+ include Logging
8
+
9
+ def initialize config, client
10
+ @config = config
11
+ @client = client
12
+ @normalizers = Normalizers.build config
13
+ end
14
+
15
+ attr_reader :config
16
+
17
+ def register!
18
+ unregister! if @subscription
19
+ @subscription = ActiveSupport::Notifications.subscribe actions_regex, self
20
+ end
21
+
22
+ def unregister!
23
+ ActiveSupport::Notifications.unsubscribe @subscription
24
+ @subscription = nil
25
+ end
26
+
27
+ # AS::Notifications API
28
+
29
+ class Notification
30
+ def initialize id, trace
31
+ @id = id
32
+ @trace = trace
33
+ end
34
+ attr_reader :id, :trace
35
+ end
36
+
37
+ def start name, id, payload
38
+ return unless transaction = @client.current_transaction
39
+
40
+ normalized = @normalizers.normalize(transaction, name, payload)
41
+
42
+ trace = nil
43
+
44
+ unless normalized == :skip
45
+ sig, kind, extra = normalized
46
+
47
+ trace = Trace.new(transaction, sig, kind, transaction.running_traces, extra)
48
+ offset = transaction.current_offset
49
+
50
+ transaction.traces << trace
51
+
52
+ trace.start offset
53
+ end
54
+
55
+ transaction.notifications << Notification.new(id, trace)
56
+ end
57
+
58
+ def finish name, id, payload
59
+ return unless transaction = @client.current_transaction
60
+
61
+ while notification = transaction.notifications.pop
62
+ if notification.id == id
63
+ if trace = notification.trace
64
+ trace.done
65
+ end
66
+ return
67
+ end
68
+ end
69
+ end
70
+
71
+ private
72
+
73
+ def actions_regex
74
+ @actions_regex ||= Regexp.new(
75
+ "(".freeze + @normalizers.keys.join("|".freeze) + ")".freeze
76
+ )
77
+ end
78
+
79
+ end
80
+ end
data/lib/opbeat/tasks.rb CHANGED
@@ -1,24 +1,26 @@
1
- # Capistrano tasks for notifying Opbeat of deploys
2
1
  namespace :opbeat do
3
- desc "Notify Opbeat of a new deploy."
4
- task :deployment do
5
- if defined?(::Rails.root)
6
- initializer_file = ::Rails.root.join('config', 'initializers','opbeat.rb')
7
-
8
- if initializer_file.exist?
9
- load initializer_file
10
- else
11
- Rake::Task[:environment].invoke
12
- end
2
+ desc "Notify Opbeat of a release"
3
+ task :release => :environment do
4
+ unless rev = ENV["REV"]
5
+ puts "Please specify a revision in an env variable\n" +
6
+ "eg. REV=abc123 rake opbeat:release"
7
+ exit 1
13
8
  end
14
- rev = ENV['REV']
15
9
 
16
- unless rev
17
- puts "No revision given. Set environment variable REV."
18
- else
19
- data = {'rev' => ENV['REV'], 'branch' => ENV['BRANCH'], 'status' => 'completed'}
20
- Opbeat::client.send_release(data)
10
+ # empty env means dev
11
+ ENV["RAILS_ENV"] ||= 'development'
12
+
13
+ # log to STDOUT
14
+ Opbeat::Client.inst.config.logger = Logger.new STDOUT
15
+
16
+ unless Opbeat.release({
17
+ rev: rev,
18
+ branch: ENV['BRANCH'],
19
+ status: 'completed'
20
+ }, inline: true)
21
+ exit 1 # release returned nil
21
22
  end
22
23
  end
23
- end
24
24
 
25
+ task :deployment => :release
26
+ end
@@ -0,0 +1,47 @@
1
+ require 'opbeat/util'
2
+
3
+ module Opbeat
4
+ class Trace
5
+
6
+ DEFAULT_KIND = 'code.custom'.freeze
7
+
8
+ def initialize transaction, signature, kind = nil, parents = [], extra = nil
9
+ @transaction = transaction
10
+ @signature = signature
11
+ @kind = kind || DEFAULT_KIND
12
+ @parents = parents || []
13
+ @extra = extra
14
+
15
+ @timestamp = Util.nearest_minute.to_i
16
+ end
17
+
18
+ attr_accessor :signature, :kind, :parents, :extra
19
+ attr_reader :transaction, :timestamp, :duration, :relative_start, :start_time
20
+
21
+ def start relative_to
22
+ @start_time = Util.nanos
23
+ @relative_start = start_time - relative_to
24
+
25
+ self
26
+ end
27
+
28
+ def done ms = Util.nanos
29
+ @duration = ms - start_time
30
+
31
+ self
32
+ end
33
+
34
+ def done?
35
+ !!duration
36
+ end
37
+
38
+ def running?
39
+ !done?
40
+ end
41
+
42
+ def inspect
43
+ info = %w{signature kind parents extra timestamp duration relative_start}
44
+ "<Trace #{info.map { |m| "#{m}:#{send(m).inspect}" }.join(' ')}>"
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,29 @@
1
+ module Opbeat
2
+ module TraceHelpers
3
+ module ClassMethods
4
+ def trace_class_method method, signature, kind
5
+ __trace_method_on(singleton_class, method, signature, kind)
6
+ end
7
+
8
+ private
9
+
10
+ def __trace_method_on(klass, method, signature, kind)
11
+ klass.class_eval <<-RUBY, __FILE__, __LINE__ + 1
12
+ alias :"__without_opb_#{method}" :"#{method}"
13
+
14
+ def #{method}(*args, &block)
15
+ Opbeat.trace "#{signature}", "#{kind}" do
16
+ __without_opb_#{method}(*args, &block)
17
+ end
18
+ end
19
+ RUBY
20
+ end
21
+ end
22
+
23
+ def self.included(kls)
24
+ kls.class_eval do
25
+ extend ClassMethods
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,99 @@
1
+ require 'opbeat/util'
2
+
3
+ module Opbeat
4
+ class Transaction
5
+
6
+ ROOT_TRACE_NAME = 'transaction'.freeze
7
+
8
+ def initialize client, endpoint, kind = 'code.custom', result = nil
9
+ @client = client
10
+ @endpoint = endpoint
11
+ @kind = kind
12
+ @result = result
13
+
14
+ @timestamp = Util.nearest_minute.to_i
15
+
16
+ @root_trace = Trace.new(self, ROOT_TRACE_NAME, ROOT_TRACE_NAME)
17
+ @traces = [@root_trace]
18
+ @notifications = []
19
+
20
+ @start_time = Util.nanos
21
+ @root_trace.start @start_time
22
+ end
23
+
24
+ attr_accessor :endpoint, :kind, :result, :duration
25
+ attr_reader :timestamp, :start_time, :traces, :notifications, :root_trace
26
+
27
+ def release
28
+ @client.current_transaction = nil
29
+ end
30
+
31
+ def done result = nil
32
+ @result = result
33
+
34
+ @root_trace.done Util.nanos
35
+ @duration = @root_trace.duration
36
+
37
+ self
38
+ end
39
+
40
+ def done?
41
+ @root_trace.done?
42
+ end
43
+
44
+ def submit result = nil
45
+ done result
46
+
47
+ release
48
+
49
+ @client.submit_transaction self
50
+
51
+ self
52
+ end
53
+
54
+ def trace signature, kind = nil, extra = nil, &block
55
+ trace = Trace.new(self, signature, kind, running_traces, extra)
56
+
57
+ rel_time = current_offset
58
+
59
+ traces << trace
60
+
61
+ trace.start rel_time
62
+
63
+ return trace unless block_given?
64
+
65
+ begin
66
+ result = yield trace
67
+ ensure
68
+ trace.done
69
+ end
70
+
71
+ result
72
+ end
73
+
74
+ def running_traces
75
+ traces.select(&:running?)
76
+ end
77
+
78
+ def current_trace
79
+ traces.reverse.find(&:running?)
80
+ end
81
+
82
+ def current_offset
83
+ if curr = current_trace
84
+ return curr.start_time
85
+ end
86
+
87
+ start_time
88
+ end
89
+
90
+ def inspect
91
+ info = %w{endpoint kind result duration timestamp start_time}
92
+ <<-TEXT
93
+ <Transaction #{info.map { |m| "#{m}:#{send(m).inspect}" }.join(' ')}>
94
+ #{traces.map(&:inspect).join("\n ")}"
95
+ TEXT
96
+ end
97
+
98
+ end
99
+ end