sentry-ruby 0.3.0 → 4.3.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 (50) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +102 -24
  3. metadata +41 -54
  4. data/.craft.yml +0 -19
  5. data/.gitignore +0 -11
  6. data/.rspec +0 -3
  7. data/.travis.yml +0 -6
  8. data/CHANGELOG.md +0 -31
  9. data/CODE_OF_CONDUCT.md +0 -74
  10. data/Gemfile +0 -16
  11. data/Rakefile +0 -8
  12. data/bin/console +0 -14
  13. data/bin/setup +0 -8
  14. data/lib/sentry/backtrace.rb +0 -128
  15. data/lib/sentry/benchmarks/benchmark_transport.rb +0 -14
  16. data/lib/sentry/breadcrumb/sentry_logger.rb +0 -87
  17. data/lib/sentry/breadcrumb.rb +0 -25
  18. data/lib/sentry/breadcrumb_buffer.rb +0 -47
  19. data/lib/sentry/client.rb +0 -80
  20. data/lib/sentry/configuration.rb +0 -387
  21. data/lib/sentry/core_ext/object/deep_dup.rb +0 -57
  22. data/lib/sentry/core_ext/object/duplicable.rb +0 -153
  23. data/lib/sentry/dsn.rb +0 -48
  24. data/lib/sentry/event.rb +0 -177
  25. data/lib/sentry/hub.rb +0 -137
  26. data/lib/sentry/interface.rb +0 -22
  27. data/lib/sentry/interfaces/exception.rb +0 -11
  28. data/lib/sentry/interfaces/request.rb +0 -95
  29. data/lib/sentry/interfaces/single_exception.rb +0 -14
  30. data/lib/sentry/interfaces/stacktrace.rb +0 -57
  31. data/lib/sentry/linecache.rb +0 -44
  32. data/lib/sentry/logger.rb +0 -20
  33. data/lib/sentry/rack/capture_exception.rb +0 -45
  34. data/lib/sentry/rack/tracing.rb +0 -39
  35. data/lib/sentry/rack.rb +0 -5
  36. data/lib/sentry/scope.rb +0 -214
  37. data/lib/sentry/span.rb +0 -155
  38. data/lib/sentry/transaction.rb +0 -113
  39. data/lib/sentry/transaction_event.rb +0 -29
  40. data/lib/sentry/transport/configuration.rb +0 -21
  41. data/lib/sentry/transport/dummy_transport.rb +0 -14
  42. data/lib/sentry/transport/http_transport.rb +0 -65
  43. data/lib/sentry/transport/state.rb +0 -40
  44. data/lib/sentry/transport.rb +0 -97
  45. data/lib/sentry/utils/exception_cause_chain.rb +0 -20
  46. data/lib/sentry/utils/real_ip.rb +0 -70
  47. data/lib/sentry/utils/request_id.rb +0 -16
  48. data/lib/sentry/version.rb +0 -3
  49. data/lib/sentry-ruby.rb +0 -123
  50. data/sentry-ruby.gemspec +0 -26
data/lib/sentry/event.rb DELETED
@@ -1,177 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'socket'
4
- require 'securerandom'
5
- require 'sentry/interface'
6
- require 'sentry/backtrace'
7
- require 'sentry/utils/real_ip'
8
- require 'sentry/utils/request_id'
9
-
10
- module Sentry
11
- class Event
12
- ATTRIBUTES = %i(
13
- event_id level timestamp
14
- release environment server_name modules
15
- message user tags contexts extra
16
- fingerprint breadcrumbs backtrace transaction
17
- platform sdk type
18
- )
19
-
20
- attr_accessor(*ATTRIBUTES)
21
- attr_reader :configuration
22
-
23
- def initialize(configuration:, message: nil)
24
- # this needs to go first because some setters rely on configuration
25
- @configuration = configuration
26
-
27
- # Set some simple default values
28
- @event_id = SecureRandom.uuid.delete("-")
29
- @timestamp = Sentry.utc_now.iso8601
30
- @platform = :ruby
31
- @sdk = Sentry.sdk_meta
32
-
33
- @user = {}
34
- @extra = {}
35
- @contexts = {}
36
- @tags = {}
37
-
38
- @fingerprint = []
39
-
40
- @server_name = configuration.server_name
41
- @environment = configuration.environment
42
- @release = configuration.release
43
- @modules = configuration.gem_specs if configuration.send_modules
44
-
45
- @message = message || ""
46
-
47
- self.level = :error
48
- end
49
-
50
- class << self
51
- def get_log_message(event_hash)
52
- message = event_hash[:message] || event_hash['message']
53
- message = get_message_from_exception(event_hash) if message.nil? || message.empty?
54
- message = '<no message value>' if message.nil? || message.empty?
55
- message
56
- end
57
-
58
- def get_message_from_exception(event_hash)
59
- (
60
- event_hash &&
61
- event_hash[:exception] &&
62
- event_hash[:exception][:values] &&
63
- event_hash[:exception][:values][0] &&
64
- "#{event_hash[:exception][:values][0][:type]}: #{event_hash[:exception][:values][0][:value]}"
65
- )
66
- end
67
- end
68
-
69
- def timestamp=(time)
70
- @timestamp = time.is_a?(Time) ? time.to_f : time
71
- end
72
-
73
- def level=(new_level) # needed to meet the Sentry spec
74
- @level = new_level.to_s == "warn" ? :warning : new_level
75
- end
76
-
77
- def rack_env=(env)
78
- unless @request || env.empty?
79
- @request = Sentry::RequestInterface.new.tap do |int|
80
- int.from_rack(env)
81
- end
82
-
83
- if configuration.send_default_pii && ip = calculate_real_ip_from_rack(env.dup)
84
- user[:ip_address] = ip
85
- end
86
- if request_id = Utils::RequestId.read_from(env)
87
- tags[:request_id] = request_id
88
- end
89
- end
90
- end
91
-
92
- def type
93
- "event"
94
- end
95
-
96
- def to_hash
97
- data = serialize_attributes
98
- data[:breadcrumbs] = breadcrumbs.to_hash if breadcrumbs
99
- data[:stacktrace] = @stacktrace.to_hash if @stacktrace
100
- data[:request] = @request.to_hash if @request
101
- data[:exception] = @exception.to_hash if @exception
102
-
103
- data
104
- end
105
-
106
- def to_json_compatible
107
- JSON.parse(JSON.generate(to_hash))
108
- end
109
-
110
- def add_exception_interface(exc)
111
- if exc.respond_to?(:sentry_context)
112
- @extra.merge!(exc.sentry_context)
113
- end
114
-
115
- @exception = Sentry::ExceptionInterface.new.tap do |exc_int|
116
- exceptions = Sentry::Utils::ExceptionCauseChain.exception_to_array(exc).reverse
117
- backtraces = Set.new
118
- exc_int.values = exceptions.map do |e|
119
- SingleExceptionInterface.new.tap do |int|
120
- int.type = e.class.to_s
121
- int.value = e.to_s
122
- int.module = e.class.to_s.split('::')[0...-1].join('::')
123
-
124
- int.stacktrace =
125
- if e.backtrace && !backtraces.include?(e.backtrace.object_id)
126
- backtraces << e.backtrace.object_id
127
- StacktraceInterface.new.tap do |stacktrace|
128
- stacktrace.frames = stacktrace_interface_from(e.backtrace)
129
- end
130
- end
131
- end
132
- end
133
- end
134
- end
135
-
136
- def stacktrace_interface_from(backtrace)
137
- project_root = configuration.project_root.to_s
138
-
139
- Backtrace.parse(backtrace, configuration: configuration).lines.reverse.each_with_object([]) do |line, memo|
140
- frame = StacktraceInterface::Frame.new(project_root)
141
- frame.abs_path = line.file if line.file
142
- frame.function = line.method if line.method
143
- frame.lineno = line.number
144
- frame.in_app = line.in_app
145
- frame.module = line.module_name if line.module_name
146
-
147
- if configuration.context_lines && frame.abs_path
148
- frame.pre_context, frame.context_line, frame.post_context = \
149
- configuration.linecache.get_file_context(frame.abs_path, frame.lineno, configuration.context_lines)
150
- end
151
-
152
- memo << frame if frame.filename
153
- end
154
- end
155
-
156
- private
157
-
158
- def serialize_attributes
159
- self.class::ATTRIBUTES.each_with_object({}) do |att, memo|
160
- if value = public_send(att)
161
- memo[att] = value
162
- end
163
- end
164
- end
165
-
166
- # When behind a proxy (or if the user is using a proxy), we can't use
167
- # REMOTE_ADDR to determine the Event IP, and must use other headers instead.
168
- def calculate_real_ip_from_rack(env)
169
- Utils::RealIp.new(
170
- :remote_addr => env["REMOTE_ADDR"],
171
- :client_ip => env["HTTP_CLIENT_IP"],
172
- :real_ip => env["HTTP_X_REAL_IP"],
173
- :forwarded_for => env["HTTP_X_FORWARDED_FOR"]
174
- ).calculate_ip
175
- end
176
- end
177
- end
data/lib/sentry/hub.rb DELETED
@@ -1,137 +0,0 @@
1
- require "sentry/scope"
2
- require "sentry/client"
3
-
4
- module Sentry
5
- class Hub
6
- attr_reader :last_event_id
7
-
8
- def initialize(client, scope)
9
- first_layer = Layer.new(client, scope)
10
- @stack = [first_layer]
11
- @last_event_id = nil
12
- end
13
-
14
- def new_from_top
15
- Hub.new(current_client, current_scope)
16
- end
17
-
18
- def current_client
19
- current_layer&.client
20
- end
21
-
22
- def current_scope
23
- current_layer&.scope
24
- end
25
-
26
- def clone
27
- layer = current_layer
28
-
29
- if layer
30
- scope = layer.scope&.dup
31
-
32
- Hub.new(layer.client, scope)
33
- end
34
- end
35
-
36
- def bind_client(client)
37
- layer = current_layer
38
-
39
- if layer
40
- layer.client = client
41
- end
42
- end
43
-
44
- def configure_scope(&block)
45
- block.call(current_scope)
46
- end
47
-
48
- def with_scope(&block)
49
- push_scope
50
- yield(current_scope)
51
- ensure
52
- pop_scope
53
- end
54
-
55
- def push_scope
56
- new_scope =
57
- if current_scope
58
- current_scope.dup
59
- else
60
- Scope.new
61
- end
62
-
63
- @stack << Layer.new(current_client, new_scope)
64
- end
65
-
66
- def pop_scope
67
- @stack.pop
68
- end
69
-
70
- def start_transaction(transaction: nil, **options)
71
- transaction ||= Transaction.new(**options)
72
- transaction.set_initial_sample_desicion
73
- transaction
74
- end
75
-
76
- def capture_exception(exception, **options, &block)
77
- return unless current_client
78
-
79
- event = current_client.event_from_exception(exception)
80
-
81
- return unless event
82
-
83
- options[:hint] ||= {}
84
- options[:hint] = options[:hint].merge(exception: exception)
85
- capture_event(event, **options, &block)
86
- end
87
-
88
- def capture_message(message, **options, &block)
89
- return unless current_client
90
-
91
- options[:hint] ||= {}
92
- options[:hint] = options[:hint].merge(message: message)
93
- event = current_client.event_from_message(message)
94
- capture_event(event, **options, &block)
95
- end
96
-
97
- def capture_event(event, **options, &block)
98
- return unless current_client
99
-
100
- hint = options.delete(:hint)
101
- scope = current_scope.dup
102
-
103
- if block
104
- block.call(scope)
105
- elsif custom_scope = options[:scope]
106
- scope.update_from_scope(custom_scope)
107
- elsif !options.empty?
108
- scope.update_from_options(**options)
109
- end
110
-
111
- event = current_client.capture_event(event, scope, hint)
112
-
113
- @last_event_id = event.event_id
114
- event
115
- end
116
-
117
- def add_breadcrumb(breadcrumb)
118
- current_scope.add_breadcrumb(breadcrumb)
119
- end
120
-
121
- private
122
-
123
- def current_layer
124
- @stack.last
125
- end
126
-
127
- class Layer
128
- attr_accessor :client
129
- attr_reader :scope
130
-
131
- def initialize(client, scope)
132
- @client = client
133
- @scope = scope
134
- end
135
- end
136
- end
137
- end
@@ -1,22 +0,0 @@
1
- module Sentry
2
- class Interface
3
- def self.inherited(klass)
4
- name = klass.name.split("::").last.downcase.gsub("interface", "")
5
- registered[name.to_sym] = klass
6
- super
7
- end
8
-
9
- def self.registered
10
- @@registered ||= {} # rubocop:disable Style/ClassVars
11
- end
12
-
13
- def to_hash
14
- Hash[instance_variables.map { |name| [name[1..-1].to_sym, instance_variable_get(name)] }]
15
- end
16
- end
17
- end
18
-
19
- require "sentry/interfaces/exception"
20
- require "sentry/interfaces/request"
21
- require "sentry/interfaces/single_exception"
22
- require "sentry/interfaces/stacktrace"
@@ -1,11 +0,0 @@
1
- module Sentry
2
- class ExceptionInterface < Interface
3
- attr_accessor :values
4
-
5
- def to_hash
6
- data = super
7
- data[:values] = data[:values].map(&:to_hash) if data[:values]
8
- data
9
- end
10
- end
11
- end
@@ -1,95 +0,0 @@
1
- require 'rack'
2
-
3
- module Sentry
4
- class RequestInterface < Interface
5
- REQUEST_ID_HEADERS = %w(action_dispatch.request_id HTTP_X_REQUEST_ID).freeze
6
- IP_HEADERS = [
7
- "REMOTE_ADDR",
8
- "HTTP_CLIENT_IP",
9
- "HTTP_X_REAL_IP",
10
- "HTTP_X_FORWARDED_FOR"
11
- ].freeze
12
-
13
- attr_accessor :url, :method, :data, :query_string, :cookies, :headers, :env
14
-
15
- def initialize
16
- self.headers = {}
17
- self.env = {}
18
- self.cookies = nil
19
- end
20
-
21
- def from_rack(env_hash)
22
- req = ::Rack::Request.new(env_hash)
23
-
24
- if Sentry.configuration.send_default_pii
25
- self.data = read_data_from(req)
26
- self.cookies = req.cookies
27
- else
28
- # need to completely wipe out ip addresses
29
- IP_HEADERS.each { |h| env_hash.delete(h) }
30
- end
31
-
32
- self.url = req.scheme && req.url.split('?').first
33
- self.method = req.request_method
34
- self.query_string = req.query_string
35
-
36
- self.headers = format_headers_for_sentry(env_hash)
37
- self.env = format_env_for_sentry(env_hash)
38
- end
39
-
40
- private
41
-
42
- # See Sentry server default limits at
43
- # https://github.com/getsentry/sentry/blob/master/src/sentry/conf/server.py
44
- def read_data_from(request)
45
- if request.form_data?
46
- request.POST
47
- elsif request.body # JSON requests, etc
48
- data = request.body.read(4096 * 4) # Sentry server limit
49
- request.body.rewind
50
- data
51
- end
52
- rescue IOError => e
53
- e.message
54
- end
55
-
56
- def format_headers_for_sentry(env_hash)
57
- env_hash.each_with_object({}) do |(key, value), memo|
58
- begin
59
- key = key.to_s # rack env can contain symbols
60
- value = value.to_s
61
- next memo['X-Request-Id'] ||= Utils::RequestId.read_from(env_hash) if Utils::RequestId::REQUEST_ID_HEADERS.include?(key)
62
- next unless key.upcase == key # Non-upper case stuff isn't either
63
-
64
- # Rack adds in an incorrect HTTP_VERSION key, which causes downstream
65
- # to think this is a Version header. Instead, this is mapped to
66
- # env['SERVER_PROTOCOL']. But we don't want to ignore a valid header
67
- # if the request has legitimately sent a Version header themselves.
68
- # See: https://github.com/rack/rack/blob/028438f/lib/rack/handler/cgi.rb#L29
69
- next if key == 'HTTP_VERSION' && value == env_hash['SERVER_PROTOCOL']
70
- next if key == 'HTTP_COOKIE' # Cookies don't go here, they go somewhere else
71
- next unless key.start_with?('HTTP_') || %w(CONTENT_TYPE CONTENT_LENGTH).include?(key)
72
-
73
- # Rack stores headers as HTTP_WHAT_EVER, we need What-Ever
74
- key = key.sub(/^HTTP_/, "")
75
- key = key.split('_').map(&:capitalize).join('-')
76
- memo[key] = value
77
- rescue StandardError => e
78
- # Rails adds objects to the Rack env that can sometimes raise exceptions
79
- # when `to_s` is called.
80
- # See: https://github.com/rails/rails/blob/master/actionpack/lib/action_dispatch/middleware/remote_ip.rb#L134
81
- Sentry.logger.warn(LOGGER_PROGNAME) { "Error raised while formatting headers: #{e.message}" }
82
- next
83
- end
84
- end
85
- end
86
-
87
- def format_env_for_sentry(env_hash)
88
- return env_hash if Sentry.configuration.rack_env_whitelist.empty?
89
-
90
- env_hash.select do |k, _v|
91
- Sentry.configuration.rack_env_whitelist.include? k.to_s
92
- end
93
- end
94
- end
95
- end
@@ -1,14 +0,0 @@
1
- module Sentry
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
9
- data = super
10
- data[:stacktrace] = data[:stacktrace].to_hash if data[:stacktrace]
11
- data
12
- end
13
- end
14
- end
@@ -1,57 +0,0 @@
1
- module Sentry
2
- class StacktraceInterface < Interface
3
- attr_accessor :frames
4
-
5
- def to_hash
6
- data = super
7
- data[:frames] = data[:frames].map(&:to_hash)
8
- data
9
- end
10
-
11
- # Not actually an interface, but I want to use the same style
12
- class Frame < Interface
13
- attr_accessor :abs_path, :context_line, :function, :in_app,
14
- :lineno, :module, :pre_context, :post_context, :vars
15
-
16
- def initialize(project_root)
17
- @project_root = project_root
18
- end
19
-
20
- def filename
21
- return if abs_path.nil?
22
- return @filename if instance_variable_defined?(:@filename)
23
-
24
- prefix =
25
- if under_project_root? && in_app
26
- @project_root
27
- elsif under_project_root?
28
- longest_load_path || @project_root
29
- else
30
- longest_load_path
31
- end
32
-
33
- @filename = prefix ? abs_path[prefix.to_s.chomp(File::SEPARATOR).length + 1..-1] : abs_path
34
- end
35
-
36
- def to_hash(*args)
37
- data = super(*args)
38
- data[:filename] = filename
39
- data.delete(:vars) unless vars && !vars.empty?
40
- data.delete(:pre_context) unless pre_context && !pre_context.empty?
41
- data.delete(:post_context) unless post_context && !post_context.empty?
42
- data.delete(:context_line) unless context_line && !context_line.empty?
43
- data
44
- end
45
-
46
- private
47
-
48
- def under_project_root?
49
- @project_root && abs_path.start_with?(@project_root)
50
- end
51
-
52
- def longest_load_path
53
- $LOAD_PATH.select { |path| abs_path.start_with?(path.to_s) }.max_by(&:size)
54
- end
55
- end
56
- end
57
- end
@@ -1,44 +0,0 @@
1
- module Sentry
2
- class LineCache
3
- def initialize
4
- @cache = {}
5
- end
6
-
7
- # Any linecache you provide to Sentry 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
-
14
- lines = Array.new(2 * context + 1) do |i|
15
- getline(filename, lineno - context + i)
16
- end
17
- [lines[0..(context - 1)], lines[context], lines[(context + 1)..-1]]
18
- end
19
-
20
- private
21
-
22
- def valid_path?(path)
23
- lines = getlines(path)
24
- !lines.nil?
25
- end
26
-
27
- def getlines(path)
28
- @cache[path] ||= begin
29
- IO.readlines(path)
30
- rescue
31
- nil
32
- end
33
- end
34
-
35
- def getline(path, n)
36
- return nil if n < 1
37
-
38
- lines = getlines(path)
39
- return nil if lines.nil?
40
-
41
- lines[n - 1]
42
- end
43
- end
44
- end
data/lib/sentry/logger.rb DELETED
@@ -1,20 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'logger'
4
-
5
- module Sentry
6
- class Logger < ::Logger
7
- LOG_PREFIX = "** [Sentry] "
8
- PROGNAME = "sentry"
9
-
10
- def initialize(*)
11
- super
12
- @level = ::Logger::INFO
13
- original_formatter = ::Logger::Formatter.new
14
- @default_formatter = proc do |severity, datetime, _progname, msg|
15
- msg = "#{LOG_PREFIX}#{msg}"
16
- original_formatter.call(severity, datetime, PROGNAME, msg)
17
- end
18
- end
19
- end
20
- end
@@ -1,45 +0,0 @@
1
- module Sentry
2
- module Rack
3
- class CaptureException
4
- def initialize(app)
5
- @app = app
6
- end
7
-
8
- def call(env)
9
- # this call clones the main (global) hub
10
- # and assigns it to the current thread's Sentry#get_current_hub
11
- # it's essential for multi-thread servers (e.g. puma)
12
- Sentry.clone_hub_to_current_thread unless Sentry.get_current_hub
13
- # this call creates an isolated scope for every request
14
- # it's essential for multi-process servers (e.g. unicorn)
15
- Sentry.with_scope do |scope|
16
- # there could be some breadcrumbs already stored in the top-level scope
17
- # and for request information, we don't need those breadcrumbs
18
- scope.clear_breadcrumbs
19
- env['sentry.client'] = Sentry.get_current_client
20
-
21
- scope.set_transaction_name(env["PATH_INFO"]) if env["PATH_INFO"]
22
- scope.set_rack_env(env)
23
-
24
- begin
25
- response = @app.call(env)
26
- rescue Sentry::Error
27
- raise # Don't capture Sentry errors
28
- rescue Exception => e
29
- Sentry.capture_exception(e)
30
- raise
31
- end
32
-
33
- exception = collect_exception(env)
34
- Sentry.capture_exception(exception) if exception
35
-
36
- response
37
- end
38
- end
39
-
40
- def collect_exception(env)
41
- env['rack.exception'] || env['sinatra.error']
42
- end
43
- end
44
- end
45
- end
@@ -1,39 +0,0 @@
1
- module Sentry
2
- module Rack
3
- class Tracing
4
- def initialize(app)
5
- @app = app
6
- end
7
-
8
- def call(env)
9
- Sentry.clone_hub_to_current_thread unless Sentry.get_current_hub
10
-
11
- if Sentry.configuration.traces_sample_rate.to_f == 0.0
12
- return @app.call(env)
13
- end
14
-
15
- Sentry.with_scope do |scope|
16
- scope.clear_breadcrumbs
17
- scope.set_transaction_name(env["PATH_INFO"]) if env["PATH_INFO"]
18
- span = Sentry.start_transaction(name: scope.transaction_name, op: "rack.request")
19
- scope.set_span(span)
20
-
21
- begin
22
- response = @app.call(env)
23
- rescue
24
- finish_span(span, 500)
25
- raise
26
- end
27
-
28
- finish_span(span, response[0])
29
- response
30
- end
31
- end
32
-
33
- def finish_span(span, status_code)
34
- span.set_http_status(status_code)
35
- span.finish
36
- end
37
- end
38
- end
39
- end
data/lib/sentry/rack.rb DELETED
@@ -1,5 +0,0 @@
1
- require 'time'
2
- require 'rack'
3
-
4
- require 'sentry/rack/capture_exception'
5
- require 'sentry/rack/tracing'