opbeat 2.0.0 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +4 -3
- data/.travis.yml +19 -28
- data/.yardopts +3 -0
- data/Gemfile +4 -2
- data/HISTORY.md +3 -0
- data/LICENSE +7 -196
- data/README.md +96 -177
- data/Rakefile +19 -13
- data/gemfiles/Gemfile.base +28 -0
- data/gemfiles/Gemfile.rails-3.2.x +3 -0
- data/gemfiles/Gemfile.rails-4.0.x +3 -0
- data/gemfiles/Gemfile.rails-4.1.x +3 -0
- data/gemfiles/Gemfile.rails-4.2.x +3 -0
- data/lib/opbeat.rb +113 -93
- data/lib/opbeat/capistrano.rb +3 -4
- data/lib/opbeat/client.rb +243 -82
- data/lib/opbeat/configuration.rb +51 -64
- data/lib/opbeat/data_builders.rb +16 -0
- data/lib/opbeat/data_builders/error.rb +27 -0
- data/lib/opbeat/data_builders/transactions.rb +85 -0
- data/lib/opbeat/error.rb +1 -2
- data/lib/opbeat/error_message.rb +71 -0
- data/lib/opbeat/error_message/exception.rb +12 -0
- data/lib/opbeat/error_message/http.rb +62 -0
- data/lib/opbeat/error_message/stacktrace.rb +75 -0
- data/lib/opbeat/error_message/user.rb +23 -0
- data/lib/opbeat/filter.rb +53 -43
- data/lib/opbeat/http_client.rb +141 -0
- data/lib/opbeat/injections.rb +83 -0
- data/lib/opbeat/injections/json.rb +19 -0
- data/lib/opbeat/injections/net_http.rb +43 -0
- data/lib/opbeat/injections/redis.rb +23 -0
- data/lib/opbeat/injections/sequel.rb +32 -0
- data/lib/opbeat/injections/sinatra.rb +56 -0
- data/lib/opbeat/{capistrano → integration}/capistrano2.rb +6 -6
- data/lib/opbeat/{capistrano → integration}/capistrano3.rb +3 -3
- data/lib/opbeat/{integrations → integration}/delayed_job.rb +6 -11
- data/lib/opbeat/integration/rails/inject_exceptions_catcher.rb +23 -0
- data/lib/opbeat/integration/railtie.rb +53 -0
- data/lib/opbeat/integration/resque.rb +16 -0
- data/lib/opbeat/integration/sidekiq.rb +38 -0
- data/lib/opbeat/line_cache.rb +21 -0
- data/lib/opbeat/logging.rb +37 -0
- data/lib/opbeat/middleware.rb +59 -0
- data/lib/opbeat/normalizers.rb +65 -0
- data/lib/opbeat/normalizers/action_controller.rb +21 -0
- data/lib/opbeat/normalizers/action_view.rb +71 -0
- data/lib/opbeat/normalizers/active_record.rb +41 -0
- data/lib/opbeat/sql_summarizer.rb +27 -0
- data/lib/opbeat/subscriber.rb +80 -0
- data/lib/opbeat/tasks.rb +20 -18
- data/lib/opbeat/trace.rb +47 -0
- data/lib/opbeat/trace_helpers.rb +29 -0
- data/lib/opbeat/transaction.rb +99 -0
- data/lib/opbeat/util.rb +26 -0
- data/lib/opbeat/util/constantize.rb +54 -0
- data/lib/opbeat/util/inspector.rb +75 -0
- data/lib/opbeat/version.rb +1 -1
- data/lib/opbeat/worker.rb +55 -0
- data/opbeat.gemspec +6 -14
- data/spec/opbeat/client_spec.rb +216 -29
- data/spec/opbeat/configuration_spec.rb +34 -38
- data/spec/opbeat/data_builders/error_spec.rb +43 -0
- data/spec/opbeat/data_builders/transactions_spec.rb +51 -0
- data/spec/opbeat/error_message/exception_spec.rb +22 -0
- data/spec/opbeat/error_message/http_spec.rb +65 -0
- data/spec/opbeat/error_message/stacktrace_spec.rb +56 -0
- data/spec/opbeat/error_message/user_spec.rb +28 -0
- data/spec/opbeat/error_message_spec.rb +78 -0
- data/spec/opbeat/filter_spec.rb +21 -99
- data/spec/opbeat/http_client_spec.rb +64 -0
- data/spec/opbeat/injections/net_http_spec.rb +37 -0
- data/spec/opbeat/injections/sequel_spec.rb +33 -0
- data/spec/opbeat/injections/sinatra_spec.rb +13 -0
- data/spec/opbeat/injections_spec.rb +49 -0
- data/spec/opbeat/integration/delayed_job_spec.rb +35 -0
- data/spec/opbeat/integration/json_spec.rb +41 -0
- data/spec/opbeat/integration/rails_spec.rb +88 -0
- data/spec/opbeat/integration/redis_spec.rb +20 -0
- data/spec/opbeat/integration/resque_spec.rb +42 -0
- data/spec/opbeat/integration/sidekiq_spec.rb +40 -0
- data/spec/opbeat/integration/sinatra_spec.rb +66 -0
- data/spec/opbeat/line_cache_spec.rb +38 -0
- data/spec/opbeat/logging_spec.rb +47 -0
- data/spec/opbeat/middleware_spec.rb +32 -0
- data/spec/opbeat/normalizers/action_controller_spec.rb +32 -0
- data/spec/opbeat/normalizers/action_view_spec.rb +77 -0
- data/spec/opbeat/normalizers/active_record_spec.rb +70 -0
- data/spec/opbeat/normalizers_spec.rb +16 -0
- data/spec/opbeat/sql_summarizer_spec.rb +6 -0
- data/spec/opbeat/subscriber_spec.rb +83 -0
- data/spec/opbeat/trace_spec.rb +43 -0
- data/spec/opbeat/transaction_spec.rb +98 -0
- data/spec/opbeat/util/inspector_spec.rb +40 -0
- data/spec/opbeat/util_spec.rb +20 -0
- data/spec/opbeat/worker_spec.rb +54 -0
- data/spec/opbeat_spec.rb +49 -0
- data/spec/spec_helper.rb +79 -6
- metadata +89 -149
- data/Makefile +0 -3
- data/gemfiles/rails30.gemfile +0 -9
- data/gemfiles/rails31.gemfile +0 -9
- data/gemfiles/rails32.gemfile +0 -9
- data/gemfiles/rails40.gemfile +0 -9
- data/gemfiles/rails41.gemfile +0 -9
- data/gemfiles/rails42.gemfile +0 -9
- data/gemfiles/ruby192_rails31.gemfile +0 -10
- data/gemfiles/ruby192_rails32.gemfile +0 -10
- data/gemfiles/sidekiq31.gemfile +0 -11
- data/lib/opbeat/better_attr_accessor.rb +0 -44
- data/lib/opbeat/event.rb +0 -223
- data/lib/opbeat/integrations/resque.rb +0 -22
- data/lib/opbeat/integrations/sidekiq.rb +0 -32
- data/lib/opbeat/interfaces.rb +0 -35
- data/lib/opbeat/interfaces/exception.rb +0 -16
- data/lib/opbeat/interfaces/http.rb +0 -57
- data/lib/opbeat/interfaces/message.rb +0 -19
- data/lib/opbeat/interfaces/stack_trace.rb +0 -50
- data/lib/opbeat/linecache.rb +0 -25
- data/lib/opbeat/logger.rb +0 -21
- data/lib/opbeat/rack.rb +0 -46
- data/lib/opbeat/rails/middleware/debug_exceptions_catcher.rb +0 -22
- data/lib/opbeat/railtie.rb +0 -26
- data/spec/opbeat/better_attr_accessor_spec.rb +0 -99
- data/spec/opbeat/event_spec.rb +0 -138
- data/spec/opbeat/integrations/delayed_job_spec.rb +0 -38
- data/spec/opbeat/logger_spec.rb +0 -55
- data/spec/opbeat/opbeat_spec.rb +0 -64
- 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
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
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
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
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
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
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
|
40
|
-
|
41
|
-
|
42
|
-
|
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
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
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
|
-
|
47
|
+
|
48
|
+
value
|
51
49
|
end
|
52
50
|
|
53
|
-
|
54
|
-
|
55
|
-
|
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
|
60
|
-
|
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
|