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
data/lib/opbeat/configuration.rb
CHANGED
@@ -1,90 +1,77 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
1
3
|
module Opbeat
|
2
4
|
class Configuration
|
5
|
+
DEFAULTS = {
|
6
|
+
server: "https://intake.opbeat.com".freeze,
|
7
|
+
logger: Logger.new(nil),
|
8
|
+
context_lines: 3,
|
9
|
+
enabled_environments: %w{production},
|
10
|
+
excluded_exceptions: [],
|
11
|
+
filter_parameters: [/(authorization|password|passwd|secret)/i],
|
12
|
+
timeout: 100,
|
13
|
+
open_timeout: 100,
|
14
|
+
backoff_multiplier: 2,
|
15
|
+
use_ssl: true,
|
16
|
+
current_user_method: :current_user,
|
17
|
+
environment: ENV['RACK_ENV'] || ENV['RAILS_ENV'] || 'default',
|
18
|
+
transaction_post_interval: 60,
|
19
|
+
|
20
|
+
disable_performance: false,
|
21
|
+
disable_errors: false,
|
22
|
+
|
23
|
+
debug_traces: false,
|
24
|
+
|
25
|
+
# for tests
|
26
|
+
disable_worker: false
|
27
|
+
}.freeze
|
3
28
|
|
4
|
-
# Base URL of the Opbeat server
|
5
|
-
attr_accessor :server
|
6
|
-
|
7
|
-
# Secret access token for authentication with Opbeat
|
8
29
|
attr_accessor :secret_token
|
9
|
-
|
10
|
-
# Organization ID to use with Opbeat
|
11
30
|
attr_accessor :organization_id
|
12
|
-
|
13
|
-
# App ID to use with Opbeat
|
14
31
|
attr_accessor :app_id
|
15
32
|
|
16
|
-
|
33
|
+
attr_accessor :server
|
17
34
|
attr_accessor :logger
|
18
|
-
|
19
|
-
# Number of lines of code context to capture, or nil for none
|
20
35
|
attr_accessor :context_lines
|
21
|
-
|
22
|
-
# Whitelist of environments that will send notifications to Opbeat
|
23
|
-
attr_accessor :environments
|
24
|
-
|
25
|
-
# Which exceptions should never be sent
|
36
|
+
attr_accessor :enabled_environments
|
26
37
|
attr_accessor :excluded_exceptions
|
27
|
-
|
28
|
-
# An array of parameters whould should be filtered from the log
|
29
38
|
attr_accessor :filter_parameters
|
30
|
-
|
31
|
-
# Timeout when waiting for the server to return data in seconds
|
32
39
|
attr_accessor :timeout
|
33
|
-
|
34
|
-
# Timout when opening connection to the server
|
35
40
|
attr_accessor :open_timeout
|
36
|
-
|
37
|
-
# Backoff multipler
|
38
41
|
attr_accessor :backoff_multiplier
|
42
|
+
attr_accessor :use_ssl
|
43
|
+
attr_accessor :current_user_method
|
44
|
+
attr_accessor :environment
|
45
|
+
attr_accessor :transaction_post_interval
|
39
46
|
|
40
|
-
|
41
|
-
attr_accessor :
|
47
|
+
attr_accessor :disable_performance
|
48
|
+
attr_accessor :disable_errors
|
42
49
|
|
43
|
-
|
50
|
+
attr_accessor :debug_traces
|
44
51
|
|
45
|
-
attr_accessor :
|
52
|
+
attr_accessor :disable_worker
|
46
53
|
|
47
|
-
|
48
|
-
attr_reader :async
|
54
|
+
attr_accessor :view_paths
|
49
55
|
|
50
|
-
def initialize
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
self.app_id = ENV['OPBEAT_APP_ID'] if ENV['OPBEAT_APP_ID']
|
55
|
-
@context_lines = 3
|
56
|
-
self.environments = %w[ development production default ]
|
57
|
-
self.current_environment = (defined?(::Rails) && ::Rails.env) || ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'default'
|
58
|
-
self.excluded_exceptions = []
|
59
|
-
self.timeout = 1
|
60
|
-
self.open_timeout = 1
|
61
|
-
self.backoff_multiplier = 2
|
62
|
-
self.ssl_verification = true
|
63
|
-
self.user_controller_method = 'current_user'
|
64
|
-
self.async = false
|
65
|
-
end
|
56
|
+
def initialize opts = {}
|
57
|
+
DEFAULTS.merge(opts).each do |k, v|
|
58
|
+
self.send("#{k}=", v)
|
59
|
+
end
|
66
60
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
def [](option)
|
71
|
-
send(option)
|
61
|
+
if block_given?
|
62
|
+
yield self
|
63
|
+
end
|
72
64
|
end
|
73
65
|
|
74
|
-
def
|
75
|
-
|
76
|
-
|
66
|
+
def validate!
|
67
|
+
%w{app_id secret_token organization_id}.each do |key|
|
68
|
+
raise Error.new("Configuration missing `#{key}'") unless self.send(key)
|
69
|
+
end
|
77
70
|
|
78
|
-
|
79
|
-
|
71
|
+
true
|
72
|
+
rescue Error => e
|
73
|
+
logger.error e.message
|
74
|
+
false
|
80
75
|
end
|
81
|
-
|
82
|
-
def async=(value)
|
83
|
-
raise ArgumentError.new("async must be callable (or false to disable)") unless (value == false || value.respond_to?(:call))
|
84
|
-
@async = value
|
85
|
-
end
|
86
|
-
|
87
|
-
alias_method :async?, :async
|
88
|
-
|
89
76
|
end
|
90
77
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Opbeat
|
2
|
+
# @api private
|
3
|
+
module DataBuilders
|
4
|
+
class DataBuilder
|
5
|
+
def initialize config
|
6
|
+
@config = config
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_reader :config
|
10
|
+
end
|
11
|
+
|
12
|
+
%w{transactions error}.each do |f|
|
13
|
+
require "opbeat/data_builders/#{f}"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'opbeat/filter'
|
2
|
+
|
3
|
+
module Opbeat
|
4
|
+
module DataBuilders
|
5
|
+
class Error < DataBuilder
|
6
|
+
def build error_message
|
7
|
+
h = {
|
8
|
+
message: error_message.message,
|
9
|
+
timestamp: error_message.timestamp,
|
10
|
+
level: error_message.level,
|
11
|
+
logger: error_message.logger,
|
12
|
+
culprit: error_message.culprit,
|
13
|
+
machine: error_message.machine,
|
14
|
+
extra: error_message.extra,
|
15
|
+
param_message: error_message.param_message
|
16
|
+
}
|
17
|
+
|
18
|
+
h[:exception] = error_message.exception.to_h if error_message.exception
|
19
|
+
h[:stacktrace] = error_message.stacktrace.to_h if error_message.stacktrace
|
20
|
+
h[:http] = error_message.http.to_h if error_message.http
|
21
|
+
h[:user] = error_message.user.to_h if error_message.user
|
22
|
+
|
23
|
+
h
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
module Opbeat
|
2
|
+
module DataBuilders
|
3
|
+
class Transactions < DataBuilder
|
4
|
+
def build transactions
|
5
|
+
reduced = transactions.reduce({ transactions: {}, traces: {} }) do |data, transaction|
|
6
|
+
key = [transaction.endpoint, transaction.result, transaction.timestamp]
|
7
|
+
|
8
|
+
if data[:transactions][key].nil?
|
9
|
+
data[:transactions][key] = build_transaction(transaction)
|
10
|
+
else
|
11
|
+
data[:transactions][key][:durations] << ms(transaction.duration)
|
12
|
+
end
|
13
|
+
|
14
|
+
combine_traces transaction.traces, data[:traces]
|
15
|
+
|
16
|
+
data
|
17
|
+
end.reduce({}) do |data, kv|
|
18
|
+
key, collection = kv
|
19
|
+
data[key] = collection.values
|
20
|
+
data
|
21
|
+
end
|
22
|
+
|
23
|
+
reduced[:traces].each do |trace|
|
24
|
+
# traces' start time is average across collected
|
25
|
+
trace[:start_time] = trace[:start_time].reduce(0, :+) / trace[:start_time].length
|
26
|
+
end
|
27
|
+
|
28
|
+
# preserve root
|
29
|
+
root = reduced[:traces].shift
|
30
|
+
# re-add root
|
31
|
+
reduced[:traces].unshift root
|
32
|
+
|
33
|
+
reduced
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def combine_traces traces, into
|
39
|
+
traces.each do |trace|
|
40
|
+
key = [trace.transaction.endpoint, trace.signature, trace.timestamp]
|
41
|
+
|
42
|
+
if into[key].nil?
|
43
|
+
into[key] = build_trace(trace)
|
44
|
+
else
|
45
|
+
into[key][:durations] << [
|
46
|
+
ms(trace.duration),
|
47
|
+
ms(trace.transaction.duration)
|
48
|
+
]
|
49
|
+
into[key][:start_time] << ms(trace.relative_start)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def build_transaction transaction
|
55
|
+
{
|
56
|
+
transaction: transaction.endpoint,
|
57
|
+
result: transaction.result,
|
58
|
+
kind: transaction.kind,
|
59
|
+
timestamp: transaction.timestamp,
|
60
|
+
durations: [ms(transaction.duration)]
|
61
|
+
}
|
62
|
+
end
|
63
|
+
|
64
|
+
def build_trace trace
|
65
|
+
{
|
66
|
+
transaction: trace.transaction.endpoint,
|
67
|
+
signature: trace.signature,
|
68
|
+
durations: [[
|
69
|
+
ms(trace.duration),
|
70
|
+
ms(trace.transaction.duration)
|
71
|
+
]],
|
72
|
+
start_time: [ms(trace.relative_start)],
|
73
|
+
kind: trace.kind,
|
74
|
+
timestamp: trace.timestamp,
|
75
|
+
parents: trace.parents && trace.parents.map(&:signature) || [],
|
76
|
+
extra: trace.extra
|
77
|
+
}
|
78
|
+
end
|
79
|
+
|
80
|
+
def ms nanos
|
81
|
+
nanos.to_f / 1_000_000
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
data/lib/opbeat/error.rb
CHANGED
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'opbeat/line_cache'
|
2
|
+
require 'opbeat/error_message/exception'
|
3
|
+
require 'opbeat/error_message/stacktrace'
|
4
|
+
require 'opbeat/error_message/http'
|
5
|
+
require 'opbeat/error_message/user'
|
6
|
+
|
7
|
+
module Opbeat
|
8
|
+
class ErrorMessage
|
9
|
+
extend Logging
|
10
|
+
|
11
|
+
DEFAULTS = {
|
12
|
+
level: :error,
|
13
|
+
logger: 'root'.freeze
|
14
|
+
}.freeze
|
15
|
+
|
16
|
+
def initialize config, message, attrs = {}
|
17
|
+
@config = config
|
18
|
+
|
19
|
+
@message = message
|
20
|
+
@timestamp = Time.now.utc.to_i
|
21
|
+
DEFAULTS.merge(attrs).each do |k,v|
|
22
|
+
send(:"#{k}=", v)
|
23
|
+
end
|
24
|
+
@filter = Filter.new config
|
25
|
+
|
26
|
+
yield self if block_given?
|
27
|
+
end
|
28
|
+
|
29
|
+
attr_reader :config
|
30
|
+
attr_accessor :message
|
31
|
+
attr_reader :timestamp
|
32
|
+
attr_accessor :level
|
33
|
+
attr_accessor :logger
|
34
|
+
attr_accessor :culprit
|
35
|
+
attr_accessor :machine
|
36
|
+
attr_accessor :extra
|
37
|
+
attr_accessor :param_message
|
38
|
+
attr_accessor :exception
|
39
|
+
attr_accessor :stacktrace
|
40
|
+
attr_accessor :http
|
41
|
+
attr_accessor :user
|
42
|
+
|
43
|
+
def self.from_exception config, exception, opts = {}
|
44
|
+
message = "#{exception.class}: #{exception.message}"
|
45
|
+
|
46
|
+
if config.excluded_exceptions.include? exception.class.to_s
|
47
|
+
info "Skipping excluded exception #{exception.class}"
|
48
|
+
return nil
|
49
|
+
end
|
50
|
+
|
51
|
+
error_message = new(config, message) do |msg|
|
52
|
+
msg.level = :error
|
53
|
+
msg.exception = Exception.from(exception)
|
54
|
+
msg.stacktrace = Stacktrace.from(config, exception)
|
55
|
+
end
|
56
|
+
|
57
|
+
if frames = error_message.stacktrace && error_message.stacktrace.frames
|
58
|
+
if first_frame = frames[0]
|
59
|
+
error_message.culprit = "#{first_frame.filename}:#{first_frame.lineno}:in `#{first_frame.function}'"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
if env = opts[:rack_env]
|
64
|
+
error_message.http = HTTP.from_rack_env env, filter: @filter
|
65
|
+
error_message.user = User.from_rack_env config, env
|
66
|
+
end
|
67
|
+
|
68
|
+
error_message
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module Opbeat
|
2
|
+
class ErrorMessage
|
3
|
+
class Exception < Struct.new(:type, :value, :module)
|
4
|
+
SPLIT = '::'.freeze
|
5
|
+
|
6
|
+
def self.from exception
|
7
|
+
new exception.class.to_s, exception.message,
|
8
|
+
exception.class.to_s.split(SPLIT)[0...-1].join(SPLIT)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Opbeat
|
2
|
+
class ErrorMessage
|
3
|
+
class HTTP < Struct.new(:url, :method, :data, :query_string, :cookies,
|
4
|
+
:headers, :remote_host, :http_host, :user_agent,
|
5
|
+
:secure, :env)
|
6
|
+
|
7
|
+
HTTP_ENV_KEY = /^HTTP_/.freeze
|
8
|
+
UNDERSCORE = "_".freeze
|
9
|
+
DASH = "-".freeze
|
10
|
+
QUESTION = "?".freeze
|
11
|
+
|
12
|
+
def self.from_rack_env env, opts = {}
|
13
|
+
req = Rack::Request.new env
|
14
|
+
|
15
|
+
http = new(
|
16
|
+
req.url.split(QUESTION).first, # url
|
17
|
+
req.request_method, # method
|
18
|
+
nil, # data
|
19
|
+
req.query_string, # query string
|
20
|
+
env['HTTP_COOKIE'], # cookies
|
21
|
+
{}, # headers
|
22
|
+
req.ip, # remote host
|
23
|
+
req.host_with_port, # http host
|
24
|
+
req.user_agent, # user agent
|
25
|
+
req.scheme == 'https'.freeze ? true : false, # secure
|
26
|
+
{} # env
|
27
|
+
)
|
28
|
+
|
29
|
+
env.each do |k, v|
|
30
|
+
next unless k.upcase == k # lower case stuff isn't relevant
|
31
|
+
|
32
|
+
if k.match(HTTP_ENV_KEY)
|
33
|
+
header = k.gsub(HTTP_ENV_KEY, '')
|
34
|
+
.split(UNDERSCORE).map(&:capitalize).join(DASH)
|
35
|
+
http.headers[header] = v.to_s
|
36
|
+
else
|
37
|
+
http.env[k] = v.to_s
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
if req.form_data?
|
42
|
+
http.data = req.POST
|
43
|
+
elsif req.body
|
44
|
+
http.data = req.body.read
|
45
|
+
req.body.rewind
|
46
|
+
end
|
47
|
+
|
48
|
+
if filter = opts[:filter]
|
49
|
+
http.apply_filter filter
|
50
|
+
end
|
51
|
+
|
52
|
+
http
|
53
|
+
end
|
54
|
+
|
55
|
+
def apply_filter filter
|
56
|
+
self.data = filter.apply data
|
57
|
+
self.query_string = filter.apply query_string
|
58
|
+
self.cookies = filter.apply cookies
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
module Opbeat
|
2
|
+
class ErrorMessage
|
3
|
+
class Stacktrace
|
4
|
+
|
5
|
+
def initialize config, frames
|
6
|
+
@config, @frames = config, frames
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_reader :frames
|
10
|
+
|
11
|
+
def self.from config, exception
|
12
|
+
return unless exception.backtrace
|
13
|
+
|
14
|
+
new(config, exception.backtrace.map do |line|
|
15
|
+
Frame.from_line config, line
|
16
|
+
end)
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_h
|
20
|
+
{ frames: frames.map(&:to_h) }
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
class Frame < Struct.new(:filename, :lineno, :abs_path, :function, :vars,
|
26
|
+
:pre_context, :context_line, :post_context)
|
27
|
+
|
28
|
+
BACKTRACE_REGEX = /^(.+?):(\d+)(?::in `(.+?)')?$/.freeze
|
29
|
+
|
30
|
+
class << self
|
31
|
+
def from_line config, line
|
32
|
+
_, abs_path, lineno, function = line.match(BACKTRACE_REGEX).to_a
|
33
|
+
lineno = lineno.to_i
|
34
|
+
filename = strip_load_path(abs_path)
|
35
|
+
|
36
|
+
if lines = config.context_lines
|
37
|
+
pre_context, context_line, post_context =
|
38
|
+
get_contextlines(abs_path, lineno, lines)
|
39
|
+
end
|
40
|
+
|
41
|
+
new filename, lineno, abs_path, function, nil,
|
42
|
+
pre_context, context_line, post_context
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def strip_load_path path
|
48
|
+
prefix = $:
|
49
|
+
.map(&:to_s)
|
50
|
+
.select { |s| path.start_with?(s) }
|
51
|
+
.sort_by { |s| s.length }
|
52
|
+
.last
|
53
|
+
|
54
|
+
return path unless prefix
|
55
|
+
|
56
|
+
path[prefix.chomp(File::SEPARATOR).length + 1..-1]
|
57
|
+
end
|
58
|
+
|
59
|
+
def get_contextlines path, line, context
|
60
|
+
lines = (2 * context + 1).times.map do |i|
|
61
|
+
LineCache.find(path, line - context + i)
|
62
|
+
end
|
63
|
+
|
64
|
+
pre = lines[0..(context-1)]
|
65
|
+
line = lines[context]
|
66
|
+
post = lines[(context+1)..-1]
|
67
|
+
|
68
|
+
[pre, line, post]
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|