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