jones-gem 2.7.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.rspec +1 -0
- data/.rubocop.yml +74 -0
- data/.travis.yml +47 -0
- data/Gemfile +38 -0
- data/LICENSE +201 -0
- data/README.md +132 -0
- data/Rakefile +29 -0
- data/changelog.md +442 -0
- data/docs/Makefile +130 -0
- data/docs/breadcrumbs.rst +51 -0
- data/docs/conf.py +228 -0
- data/docs/config.rst +260 -0
- data/docs/context.rst +141 -0
- data/docs/index.rst +113 -0
- data/docs/install.rst +40 -0
- data/docs/integrations/heroku.rst +11 -0
- data/docs/integrations/index.rst +59 -0
- data/docs/integrations/puma.rst +30 -0
- data/docs/integrations/rack.rst +27 -0
- data/docs/integrations/rails.rst +84 -0
- data/docs/make.bat +155 -0
- data/docs/processors.rst +124 -0
- data/docs/sentry-doc-config.json +31 -0
- data/docs/usage.rst +176 -0
- data/exe/raven +32 -0
- data/jones-gem.gemspec +22 -0
- data/lib/raven.rb +3 -0
- data/lib/raven/backtrace.rb +137 -0
- data/lib/raven/base.rb +106 -0
- data/lib/raven/breadcrumbs.rb +76 -0
- data/lib/raven/breadcrumbs/activesupport.rb +19 -0
- data/lib/raven/breadcrumbs/logger.rb +93 -0
- data/lib/raven/cli.rb +59 -0
- data/lib/raven/client.rb +142 -0
- data/lib/raven/configuration.rb +434 -0
- data/lib/raven/context.rb +43 -0
- data/lib/raven/event.rb +259 -0
- data/lib/raven/instance.rb +221 -0
- data/lib/raven/integrations/delayed_job.rb +58 -0
- data/lib/raven/integrations/rack-timeout.rb +19 -0
- data/lib/raven/integrations/rack.rb +139 -0
- data/lib/raven/integrations/rails.rb +79 -0
- data/lib/raven/integrations/rails/active_job.rb +55 -0
- data/lib/raven/integrations/rails/controller_methods.rb +13 -0
- data/lib/raven/integrations/rails/controller_transaction.rb +13 -0
- data/lib/raven/integrations/rails/overrides/debug_exceptions_catcher.rb +31 -0
- data/lib/raven/integrations/rails/overrides/streaming_reporter.rb +23 -0
- data/lib/raven/integrations/railties.rb +1 -0
- data/lib/raven/integrations/rake.rb +18 -0
- data/lib/raven/integrations/sidekiq.rb +87 -0
- data/lib/raven/integrations/tasks.rb +11 -0
- data/lib/raven/interface.rb +25 -0
- data/lib/raven/interfaces/exception.rb +15 -0
- data/lib/raven/interfaces/http.rb +16 -0
- data/lib/raven/interfaces/message.rb +20 -0
- data/lib/raven/interfaces/single_exception.rb +14 -0
- data/lib/raven/interfaces/stack_trace.rb +69 -0
- data/lib/raven/linecache.rb +41 -0
- data/lib/raven/logger.rb +19 -0
- data/lib/raven/processor.rb +15 -0
- data/lib/raven/processor/cookies.rb +26 -0
- data/lib/raven/processor/http_headers.rb +55 -0
- data/lib/raven/processor/post_data.rb +22 -0
- data/lib/raven/processor/removecircularreferences.rb +17 -0
- data/lib/raven/processor/removestacktrace.rb +24 -0
- data/lib/raven/processor/sanitizedata.rb +88 -0
- data/lib/raven/processor/utf8conversion.rb +52 -0
- data/lib/raven/transports.rb +15 -0
- data/lib/raven/transports/dummy.rb +16 -0
- data/lib/raven/transports/http.rb +66 -0
- data/lib/raven/utils/deep_merge.rb +22 -0
- data/lib/raven/utils/real_ip.rb +62 -0
- data/lib/raven/version.rb +5 -0
- data/lib/sentry-raven-without-integrations.rb +1 -0
- data/lib/sentry-raven.rb +1 -0
- metadata +141 -0
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/task'
|
3
|
+
require 'raven/integrations/tasks'
|
4
|
+
|
5
|
+
module Rake
|
6
|
+
class Application
|
7
|
+
alias orig_display_error_messsage display_error_message
|
8
|
+
def display_error_message(ex)
|
9
|
+
Raven.capture_exception(
|
10
|
+
ex,
|
11
|
+
:transaction => top_level_tasks.join(' '),
|
12
|
+
:logger => 'rake',
|
13
|
+
:tags => { 'rake_task' => top_level_tasks.join(' ') }
|
14
|
+
)
|
15
|
+
orig_display_error_messsage(ex)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'time'
|
2
|
+
require 'sidekiq'
|
3
|
+
|
4
|
+
module Raven
|
5
|
+
class SidekiqCleanupMiddleware
|
6
|
+
def call(_worker, job, queue)
|
7
|
+
Raven.context.transaction.push "Sidekiq/#{job['class']}"
|
8
|
+
Raven.extra_context(:sidekiq => job.merge("queue" => queue))
|
9
|
+
yield
|
10
|
+
Context.clear!
|
11
|
+
BreadcrumbBuffer.clear!
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class SidekiqErrorHandler
|
16
|
+
ACTIVEJOB_RESERVED_PREFIX = "_aj_".freeze
|
17
|
+
HAS_GLOBALID = const_defined?('GlobalID')
|
18
|
+
|
19
|
+
def call(ex, context)
|
20
|
+
context = filter_context(context)
|
21
|
+
Raven.context.transaction.push transaction_from_context(context)
|
22
|
+
Raven.capture_exception(
|
23
|
+
ex,
|
24
|
+
:message => ex.message,
|
25
|
+
:extra => { :sidekiq => context }
|
26
|
+
)
|
27
|
+
Context.clear!
|
28
|
+
BreadcrumbBuffer.clear!
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
# Once an ActiveJob is queued, ActiveRecord references get serialized into
|
34
|
+
# some internal reserved keys, such as _aj_globalid.
|
35
|
+
#
|
36
|
+
# The problem is, if this job in turn gets queued back into ActiveJob with
|
37
|
+
# these magic reserved keys, ActiveJob will throw up and error. We want to
|
38
|
+
# capture these and mutate the keys so we can sanely report it.
|
39
|
+
def filter_context(context)
|
40
|
+
case context
|
41
|
+
when Array
|
42
|
+
context.map { |arg| filter_context(arg) }
|
43
|
+
when Hash
|
44
|
+
Hash[context.map { |key, value| filter_context_hash(key, value) }]
|
45
|
+
else
|
46
|
+
format_globalid(context)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def filter_context_hash(key, value)
|
51
|
+
(key = key[3..-1]) if key [0..3] == ACTIVEJOB_RESERVED_PREFIX
|
52
|
+
[key, filter_context(value)]
|
53
|
+
end
|
54
|
+
|
55
|
+
# this will change in the future:
|
56
|
+
# https://github.com/mperham/sidekiq/pull/3161
|
57
|
+
def transaction_from_context(context)
|
58
|
+
classname = (context["wrapped"] || context["class"] ||
|
59
|
+
(context[:job] && (context[:job]["wrapped"] || context[:job]["class"]))
|
60
|
+
)
|
61
|
+
if classname
|
62
|
+
"Sidekiq/#{classname}"
|
63
|
+
elsif context[:event]
|
64
|
+
"Sidekiq/#{context[:event]}"
|
65
|
+
else
|
66
|
+
"Sidekiq"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def format_globalid(context)
|
71
|
+
if HAS_GLOBALID && context.is_a?(GlobalID)
|
72
|
+
context.to_s
|
73
|
+
else
|
74
|
+
context
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
if Sidekiq::VERSION > '3'
|
81
|
+
Sidekiq.configure_server do |config|
|
82
|
+
config.error_handlers << Raven::SidekiqErrorHandler.new
|
83
|
+
config.server_middleware do |chain|
|
84
|
+
chain.add Raven::SidekiqCleanupMiddleware
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'raven/cli'
|
3
|
+
|
4
|
+
namespace :raven do
|
5
|
+
desc "Send a test event to the remote Sentry server"
|
6
|
+
task :test, [:dsn] do |_t, args|
|
7
|
+
Rake::Task["environment"].invoke if Rake::Task.tasks.map(&:to_s).include?("environment")
|
8
|
+
|
9
|
+
Raven::CLI.test(args.dsn)
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Raven
|
2
|
+
class Interface
|
3
|
+
def initialize(attributes = nil)
|
4
|
+
attributes.each do |attr, value|
|
5
|
+
public_send "#{attr}=", value
|
6
|
+
end if attributes
|
7
|
+
|
8
|
+
yield self if block_given?
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.inherited(klass)
|
12
|
+
name = klass.name.split("::").last.downcase.gsub("interface", "")
|
13
|
+
registered[name.to_sym] = klass
|
14
|
+
super
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.registered
|
18
|
+
@@registered ||= {} # rubocop:disable Style/ClassVars
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_hash
|
22
|
+
Hash[instance_variables.map { |name| [name[1..-1].to_sym, instance_variable_get(name)] }]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Raven
|
2
|
+
class ExceptionInterface < Interface
|
3
|
+
attr_accessor :values
|
4
|
+
|
5
|
+
def self.sentry_alias
|
6
|
+
:exception
|
7
|
+
end
|
8
|
+
|
9
|
+
def to_hash(*args)
|
10
|
+
data = super(*args)
|
11
|
+
data[:values] = data[:values].map(&:to_hash) if data[:values]
|
12
|
+
data
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Raven
|
2
|
+
class HttpInterface < Interface
|
3
|
+
attr_accessor :url, :method, :data, :query_string, :cookies, :headers, :env
|
4
|
+
|
5
|
+
def initialize(*arguments)
|
6
|
+
self.headers = {}
|
7
|
+
self.env = {}
|
8
|
+
self.cookies = nil
|
9
|
+
super(*arguments)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.sentry_alias
|
13
|
+
:request
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'raven/interface'
|
2
|
+
|
3
|
+
module Raven
|
4
|
+
class MessageInterface < Interface
|
5
|
+
attr_accessor :message, :params
|
6
|
+
|
7
|
+
def initialize(*arguments)
|
8
|
+
self.params = []
|
9
|
+
super(*arguments)
|
10
|
+
end
|
11
|
+
|
12
|
+
def unformatted_message
|
13
|
+
Array(params).empty? ? message : message % params
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.sentry_alias
|
17
|
+
:logentry
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Raven
|
2
|
+
class SingleExceptionInterface < Interface
|
3
|
+
attr_accessor :type
|
4
|
+
attr_accessor :value
|
5
|
+
attr_accessor :module
|
6
|
+
attr_accessor :stacktrace
|
7
|
+
|
8
|
+
def to_hash(*args)
|
9
|
+
data = super(*args)
|
10
|
+
data[:stacktrace] = data[:stacktrace].to_hash if data[:stacktrace]
|
11
|
+
data
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module Raven
|
2
|
+
class StacktraceInterface < Interface
|
3
|
+
attr_accessor :frames
|
4
|
+
|
5
|
+
def initialize(*arguments)
|
6
|
+
super(*arguments)
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.sentry_alias
|
10
|
+
:stacktrace
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_hash(*args)
|
14
|
+
data = super(*args)
|
15
|
+
data[:frames] = data[:frames].map(&:to_hash)
|
16
|
+
data
|
17
|
+
end
|
18
|
+
|
19
|
+
# Not actually an interface, but I want to use the same style
|
20
|
+
class Frame < Interface
|
21
|
+
attr_accessor :abs_path, :context_line, :function, :in_app,
|
22
|
+
:lineno, :module, :pre_context, :post_context, :vars
|
23
|
+
|
24
|
+
def initialize(*arguments)
|
25
|
+
super(*arguments)
|
26
|
+
end
|
27
|
+
|
28
|
+
def filename
|
29
|
+
return if abs_path.nil?
|
30
|
+
return @filename if instance_variable_defined?(:@filename)
|
31
|
+
|
32
|
+
prefix =
|
33
|
+
if under_project_root? && in_app
|
34
|
+
project_root
|
35
|
+
elsif under_project_root?
|
36
|
+
longest_load_path || project_root
|
37
|
+
else
|
38
|
+
longest_load_path
|
39
|
+
end
|
40
|
+
|
41
|
+
@filename = prefix ? abs_path[prefix.to_s.chomp(File::SEPARATOR).length + 1..-1] : abs_path
|
42
|
+
end
|
43
|
+
|
44
|
+
def to_hash(*args)
|
45
|
+
data = super(*args)
|
46
|
+
data[:filename] = filename
|
47
|
+
data.delete(:vars) unless vars && !vars.empty?
|
48
|
+
data.delete(:pre_context) unless pre_context && !pre_context.empty?
|
49
|
+
data.delete(:post_context) unless post_context && !post_context.empty?
|
50
|
+
data.delete(:context_line) unless context_line && !context_line.empty?
|
51
|
+
data
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def under_project_root?
|
57
|
+
project_root && abs_path.start_with?(project_root)
|
58
|
+
end
|
59
|
+
|
60
|
+
def project_root
|
61
|
+
@project_root ||= Raven.configuration.project_root && Raven.configuration.project_root.to_s
|
62
|
+
end
|
63
|
+
|
64
|
+
def longest_load_path
|
65
|
+
$LOAD_PATH.select { |path| abs_path.start_with?(path.to_s) }.max_by(&:size)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Raven
|
2
|
+
class LineCache
|
3
|
+
def initialize
|
4
|
+
@cache = {}
|
5
|
+
end
|
6
|
+
|
7
|
+
# Any linecache you provide to Raven must implement this method.
|
8
|
+
# Returns an Array of Strings representing the lines in the source
|
9
|
+
# file. The number of lines retrieved is (2 * context) + 1, the middle
|
10
|
+
# line should be the line requested by lineno. See specs for more information.
|
11
|
+
def get_file_context(filename, lineno, context)
|
12
|
+
return nil, nil, nil unless valid_path?(filename)
|
13
|
+
lines = Array.new(2 * context + 1) do |i|
|
14
|
+
getline(filename, lineno - context + i)
|
15
|
+
end
|
16
|
+
[lines[0..(context - 1)], lines[context], lines[(context + 1)..-1]]
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def valid_path?(path)
|
22
|
+
lines = getlines(path)
|
23
|
+
!lines.nil?
|
24
|
+
end
|
25
|
+
|
26
|
+
def getlines(path)
|
27
|
+
@cache[path] ||= begin
|
28
|
+
IO.readlines(path)
|
29
|
+
rescue
|
30
|
+
nil
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def getline(path, n)
|
35
|
+
return nil if n < 1
|
36
|
+
lines = getlines(path)
|
37
|
+
return nil if lines.nil?
|
38
|
+
lines[n - 1]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
data/lib/raven/logger.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'logger'
|
3
|
+
|
4
|
+
module Raven
|
5
|
+
class Logger < ::Logger
|
6
|
+
LOG_PREFIX = "** [Raven] ".freeze
|
7
|
+
PROGNAME = "sentry".freeze
|
8
|
+
|
9
|
+
def initialize(*)
|
10
|
+
super
|
11
|
+
@level = ::Logger::INFO
|
12
|
+
original_formatter = ::Logger::Formatter.new
|
13
|
+
@default_formatter = proc do |severity, datetime, _progname, msg|
|
14
|
+
msg = "#{LOG_PREFIX}#{msg}"
|
15
|
+
original_formatter.call(severity, datetime, PROGNAME, msg)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Raven
|
2
|
+
class Processor
|
3
|
+
STRING_MASK = '********'.freeze
|
4
|
+
INT_MASK = 0
|
5
|
+
REGEX_SPECIAL_CHARACTERS = %w(. $ ^ { [ ( | ) * + ?).freeze
|
6
|
+
|
7
|
+
def initialize(client = nil)
|
8
|
+
@client = client
|
9
|
+
end
|
10
|
+
|
11
|
+
def process(_data)
|
12
|
+
raise NotImplementedError
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Raven
|
2
|
+
class Processor::Cookies < Processor
|
3
|
+
def process(data)
|
4
|
+
process_if_symbol_keys(data) if data[:request]
|
5
|
+
process_if_string_keys(data) if data["request"]
|
6
|
+
|
7
|
+
data
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def process_if_symbol_keys(data)
|
13
|
+
data[:request][:cookies] = STRING_MASK if data[:request][:cookies]
|
14
|
+
|
15
|
+
return unless data[:request][:headers] && data[:request][:headers]["Cookie"]
|
16
|
+
data[:request][:headers]["Cookie"] = STRING_MASK
|
17
|
+
end
|
18
|
+
|
19
|
+
def process_if_string_keys(data)
|
20
|
+
data["request"]["cookies"] = STRING_MASK if data["request"]["cookies"]
|
21
|
+
|
22
|
+
return unless data["request"]["headers"] && data["request"]["headers"]["Cookie"]
|
23
|
+
data["request"]["headers"]["Cookie"] = STRING_MASK
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Raven
|
2
|
+
class Processor::HTTPHeaders < Processor
|
3
|
+
DEFAULT_FIELDS = ["Authorization"].freeze
|
4
|
+
|
5
|
+
attr_accessor :sanitize_http_headers
|
6
|
+
|
7
|
+
def initialize(client)
|
8
|
+
super
|
9
|
+
self.sanitize_http_headers = client.configuration.sanitize_http_headers
|
10
|
+
end
|
11
|
+
|
12
|
+
def process(data)
|
13
|
+
process_if_symbol_keys(data) if data[:request]
|
14
|
+
process_if_string_keys(data) if data["request"]
|
15
|
+
|
16
|
+
data
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def process_if_symbol_keys(data)
|
22
|
+
return unless data[:request][:headers]
|
23
|
+
|
24
|
+
data[:request][:headers].keys.select { |k| fields_re.match(k.to_s) }.each do |k|
|
25
|
+
data[:request][:headers][k] = STRING_MASK
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def process_if_string_keys(data)
|
30
|
+
return unless data["request"]["headers"]
|
31
|
+
|
32
|
+
data["request"]["headers"].keys.select { |k| fields_re.match(k) }.each do |k|
|
33
|
+
data["request"]["headers"][k] = STRING_MASK
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def matches_regexes?(k)
|
38
|
+
fields_re.match(k.to_s)
|
39
|
+
end
|
40
|
+
|
41
|
+
def fields_re
|
42
|
+
@fields_re ||= /#{(DEFAULT_FIELDS | sanitize_http_headers).map do |f|
|
43
|
+
use_boundary?(f) ? "\\b#{f}\\b" : f
|
44
|
+
end.join("|")}/i
|
45
|
+
end
|
46
|
+
|
47
|
+
def use_boundary?(string)
|
48
|
+
!DEFAULT_FIELDS.include?(string) && !special_characters?(string)
|
49
|
+
end
|
50
|
+
|
51
|
+
def special_characters?(string)
|
52
|
+
REGEX_SPECIAL_CHARACTERS.select { |r| string.include?(r) }.any?
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|