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
@@ -0,0 +1,23 @@
1
+ module Opbeat
2
+ class ErrorMessage
3
+ class User < Struct.new(:is_authenticated, :id, :username, :email)
4
+ CONTROLLER_KEY = 'action_controller.instance'.freeze
5
+
6
+ def self.from_rack_env config, env
7
+ controller = env[CONTROLLER_KEY]
8
+ method = config.current_user_method.to_sym
9
+
10
+ return unless controller && controller.respond_to?(method)
11
+
12
+ user = controller.send method
13
+
14
+ new(
15
+ true,
16
+ user.respond_to?(:id) ? user.id : nil,
17
+ user.respond_to?(:username) ? user.username : nil,
18
+ user.respond_to?(:email) ? user.email : nil
19
+ )
20
+ end
21
+ end
22
+ end
23
+ end
data/lib/opbeat/filter.rb CHANGED
@@ -1,63 +1,73 @@
1
1
  module Opbeat
2
+ # @api private
2
3
  class Filter
3
- MASK = '[FILTERED]'
4
- DEFAULT_FILTER = [/(authorization|password|passwd|secret)/i]
5
4
 
6
- def initialize(filters=nil)
7
- if defined?(::Rails)
8
- rails_filters = ::Rails.application.config.filter_parameters
9
- rails_filters = nil if rails_filters.count == 0
10
- end
11
- @filters = filters || rails_filters || DEFAULT_FILTER
5
+ MASK = '[FILTERED]'.freeze
6
+
7
+ def initialize config
8
+ @config = config
9
+ @params = rails_filters || config.filter_parameters
12
10
  end
13
11
 
14
- def apply(value, key=nil, &block)
15
- if value.is_a?(Hash)
16
- value.each.inject({}) do |memo, (k, v)|
17
- memo[k] = apply(v, k, &block)
18
- memo
19
- end
20
- elsif value.is_a?(Array)
21
- value.map do |value|
22
- apply(value, key, &block)
23
- end
24
- else
25
- block.call(key, value)
12
+ attr_reader :config
13
+
14
+ def apply data, opts = {}
15
+ case data
16
+ when String
17
+ apply_to_string data, opts = {}
18
+ when Hash
19
+ apply_to_hash data
26
20
  end
27
21
  end
28
22
 
29
- def sanitize(key, value)
30
- if !value.is_a?(String) || value.empty?
31
- value
32
- elsif @filters.any? { |filter| filter.is_a?(Regexp) ? filter.match(key) : filter.to_s == key.to_s }
33
- MASK
34
- else
35
- value
36
- end
23
+ def apply_to_string str, opts = {}
24
+ sep = opts[:separator] || '&'.freeze
25
+ kv_sep = opts[:kv_separator] || '='.freeze
26
+
27
+ str.split(sep).map do |kv|
28
+ key, value = kv.split(kv_sep)
29
+ [key, kv_sep, sanitize(key, value)].join
30
+ end.join(sep)
37
31
  end
38
32
 
39
- def process_event_hash(data)
40
- return data unless data.has_key? 'http'
41
- if data['http'].has_key? 'data'
42
- data['http']['data'] = process_hash(data['http']['data'])
33
+ def apply_to_hash hsh
34
+ hsh.inject({}) do |filtered, kv|
35
+ key, value = kv
36
+ filtered[key] = sanitize(key, value)
37
+ filtered
43
38
  end
44
- if data['http'].has_key? 'query_string'
45
- data['http']['query_string'] = process_string(data['http']['query_string'], '&')
46
- end
47
- if data['http'].has_key? 'cookies'
48
- data['http']['cookies'] = process_string(data['http']['cookies'], ';')
39
+ end
40
+
41
+ def sanitize key, value
42
+ return value unless value.is_a?(String)
43
+
44
+ if should_filter?(key)
45
+ return MASK
49
46
  end
50
- data
47
+
48
+ value
51
49
  end
52
50
 
53
- def process_hash(data)
54
- apply(data) do |key, value|
55
- sanitize(key, value)
51
+ private
52
+
53
+ def should_filter? key
54
+ @params.any? do |param|
55
+ case param
56
+ when String
57
+ key.to_s == param.to_s
58
+ when Regexp
59
+ param.match(key)
60
+ end
56
61
  end
57
62
  end
58
63
 
59
- def process_string(str, separator='&')
60
- str.split(separator).map { |s| s.split('=') }.map { |a| a[0]+'='+sanitize(a[0], a[1]) }.join(separator)
64
+ def rails_filters
65
+ if defined?(::Rails) && Rails.application
66
+ if filters = ::Rails.application.config.filter_parameters
67
+ filters.any? ? filters : nil
68
+ end
69
+ end
61
70
  end
71
+
62
72
  end
63
73
  end
@@ -0,0 +1,141 @@
1
+ require 'uri'
2
+ require 'net/http'
3
+ require 'json'
4
+
5
+ module Opbeat
6
+ # @api private
7
+ class HttpClient
8
+ include Logging
9
+
10
+ USER_AGENT = "opbeat-ruby/#{Opbeat::VERSION}".freeze
11
+
12
+ attr_reader :state
13
+ attr_reader :adapter
14
+
15
+ def initialize(config)
16
+ @config = config
17
+ @adapter = HTTPAdapter.new(config)
18
+ @state = ClientState.new config
19
+ end
20
+
21
+ attr_reader :config
22
+
23
+ def post(resource, body)
24
+ path = abs_path(resource)
25
+ debug "POST #{resource}"
26
+
27
+ unless state.should_try?
28
+ info "Temporarily skipping sending to Opbeat due to previous failure."
29
+ return
30
+ end
31
+
32
+ if body.is_a?(Hash) || body.is_a?(Array)
33
+ body = JSON.dump(body)
34
+ end
35
+
36
+ request = adapter.post path do |req|
37
+ req['Authorization'] = auth_header
38
+ req['Content-Type'] = 'application/json'.freeze
39
+ req['Content-Length'] = body.bytesize.to_s
40
+ req['User-Agent'] = USER_AGENT
41
+ req.body = body
42
+ end
43
+
44
+ begin
45
+ response = adapter.perform_request request
46
+ unless response.code.to_i.between?(200, 299)
47
+ raise Error.new("Error from Opbeat server (#{response.code}): #{response.body}")
48
+ end
49
+ rescue
50
+ debug { JSON.parse(body).inspect }
51
+ @state.fail!
52
+ raise
53
+ end
54
+
55
+ @state.success!
56
+
57
+ response
58
+ end
59
+
60
+ private
61
+
62
+ def auth_header
63
+ "Bearer #{@config.secret_token}"
64
+ end
65
+
66
+ def abs_path path
67
+ "/api/v1/organizations/#{@config.organization_id}" +
68
+ "/apps/#{@config.app_id}#{path}"
69
+ end
70
+
71
+ def encode(event)
72
+ event_hash = @filter.process_event_hash(event.to_hash)
73
+ event_hash.to_json
74
+ end
75
+
76
+ class HTTPAdapter
77
+ def initialize conf
78
+ @config = conf
79
+ end
80
+
81
+ def post path
82
+ req = Net::HTTP::Post.new path
83
+ yield req if block_given?
84
+ req
85
+ end
86
+
87
+ def perform_request req
88
+ http.start do |http|
89
+ http.request req
90
+ end
91
+ end
92
+
93
+ private
94
+
95
+ def http
96
+ return @http if @http
97
+
98
+ http = Net::HTTP.new server_uri.host, server_uri.port
99
+ http.use_ssl = @config.use_ssl
100
+ http.read_timeout = @config.timeout
101
+ http.open_timeout = @config.open_timeout
102
+
103
+ @http = http
104
+ end
105
+
106
+ def server_uri
107
+ @uri ||= URI(@config.server)
108
+ end
109
+ end
110
+
111
+ class ClientState
112
+ def initialize(config)
113
+ @config = config
114
+ @retry_number = 0
115
+ @last_check = Time.now.utc
116
+ end
117
+
118
+ def should_try?
119
+ return true if @status == :online
120
+
121
+ interval = ([@retry_number, 6].min() ** 2) * @config.backoff_multiplier
122
+ return true if Time.now.utc - @last_check > interval
123
+
124
+ false
125
+ end
126
+
127
+ def fail!
128
+ @status = :error
129
+ @retry_number += 1
130
+ @last_check = Time.now.utc
131
+ end
132
+
133
+ def success!
134
+ @status = :online
135
+ @retry_number = 0
136
+ @last_check = nil
137
+ end
138
+ end
139
+ end
140
+
141
+ end
@@ -0,0 +1,83 @@
1
+ require 'opbeat/util/constantize'
2
+
3
+ module Opbeat
4
+ # @api private
5
+ module Injections
6
+ class Registration
7
+ def initialize const_name, require_paths, injector
8
+ @const_name = const_name
9
+ @require_paths = Array(require_paths)
10
+ @injector = injector
11
+ end
12
+
13
+ attr_reader :const_name, :require_paths, :injector
14
+
15
+ def install
16
+ injector.install
17
+ end
18
+ end
19
+
20
+ def self.require_hooks
21
+ @require_hooks ||= {}
22
+ end
23
+
24
+ def self.installed
25
+ @installed ||= {}
26
+ end
27
+
28
+ def self.register(*args)
29
+ registration = Registration.new(*args)
30
+
31
+ if const_defined?(registration.const_name)
32
+ installed[registration.const_name] = registration
33
+ registration.install
34
+ else
35
+ register_require_hook registration
36
+ end
37
+ end
38
+
39
+ def self.register_require_hook registration
40
+ registration.require_paths.each do |p|
41
+ require_hooks[p] = registration
42
+ end
43
+ end
44
+
45
+ def self.hook_into name
46
+ return unless registration = lookup(name)
47
+
48
+ if const_defined?(registration.const_name)
49
+ installed[registration.const_name] = registration
50
+ registration.install
51
+
52
+ registration.require_paths.each do |p|
53
+ require_hooks.delete p
54
+ end
55
+ end
56
+ end
57
+
58
+ def self.lookup require_path
59
+ require_hooks[require_path]
60
+ end
61
+
62
+ def self.const_defined? const_name
63
+ const = Util.constantize(const_name) rescue nil
64
+ !!const
65
+ end
66
+ end
67
+ end
68
+
69
+ # @api private
70
+ module ::Kernel
71
+ alias require_without_op require
72
+
73
+ def require name
74
+ res = require_without_op name
75
+
76
+ begin
77
+ Opbeat::Injections.hook_into name
78
+ rescue Exception
79
+ end
80
+
81
+ res
82
+ end
83
+ end
@@ -0,0 +1,19 @@
1
+ module Opbeat
2
+ module Injections
3
+ module JSON
4
+ class Injector
5
+ def install
6
+ ::JSON.class_eval do
7
+ include TraceHelpers
8
+
9
+ trace_class_method :parse, 'JSON#parse', 'json.parse'
10
+ trace_class_method :parse!, 'JSON#parse!', 'json.parse'
11
+ trace_class_method :generate, 'JSON#generate', 'json.generate'
12
+ end
13
+ end
14
+ end
15
+ end
16
+
17
+ register 'JSON', 'json', JSON::Injector.new
18
+ end
19
+ end
@@ -0,0 +1,43 @@
1
+ module Opbeat
2
+ module Injections
3
+ module NetHTTP
4
+ class Injector
5
+ def install
6
+ Net::HTTP.class_eval do
7
+ alias request_without_opb request
8
+
9
+ def request req, body = nil, &block
10
+ unless Opbeat.started?
11
+ return request_without_opb req, body, &block
12
+ end
13
+
14
+ host, port = req['host'] && req['host'].split(':')
15
+ method = req.method
16
+ path = req.path
17
+ scheme = use_ssl? ? 'https' : 'http'
18
+
19
+ # inside a session
20
+ host ||= self.address
21
+ port ||= self.port
22
+
23
+ extra = {
24
+ scheme: scheme,
25
+ port: port,
26
+ path: path
27
+ }
28
+
29
+ signature = "#{method} #{host}".freeze
30
+ kind = "ext.net_http.#{method}".freeze
31
+
32
+ Opbeat.trace signature, kind, extra do
33
+ request_without_opb(req, body, &block)
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+
41
+ register 'Net::HTTP', 'net/http', NetHTTP::Injector.new
42
+ end
43
+ end
@@ -0,0 +1,23 @@
1
+ module Opbeat
2
+ module Injections
3
+ module Redis
4
+ class Injector
5
+ def install
6
+ ::Redis::Client.class_eval do
7
+ alias call_without_opbeat call
8
+
9
+ def call(command, &block)
10
+ signature = command[0]
11
+
12
+ Opbeat.trace signature.to_s, 'cache.redis'.freeze do
13
+ call_without_opbeat(command, &block)
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+
21
+ register 'Redis', 'redis', Redis::Injector.new
22
+ end
23
+ end