sentry-ruby-core 4.2.2 → 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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +31 -0
- data/README.md +6 -2
- data/lib/sentry-ruby.rb +2 -4
- data/lib/sentry/background_worker.rb +1 -0
- data/lib/sentry/breadcrumb/sentry_logger.rb +1 -0
- data/lib/sentry/breadcrumb_buffer.rb +3 -2
- data/lib/sentry/client.rb +46 -27
- data/lib/sentry/configuration.rb +16 -0
- data/lib/sentry/event.rb +28 -47
- data/lib/sentry/exceptions.rb +7 -0
- data/lib/sentry/hub.rb +4 -2
- data/lib/sentry/interfaces/exception.rb +19 -1
- data/lib/sentry/interfaces/request.rb +10 -10
- data/lib/sentry/interfaces/single_exception.rb +16 -5
- data/lib/sentry/interfaces/stacktrace.rb +9 -26
- data/lib/sentry/interfaces/stacktrace_builder.rb +50 -0
- data/lib/sentry/interfaces/threads.rb +9 -3
- data/lib/sentry/rack/capture_exceptions.rb +18 -11
- data/lib/sentry/scope.rb +9 -3
- data/lib/sentry/transaction.rb +13 -7
- data/lib/sentry/transport.rb +0 -12
- data/lib/sentry/transport/configuration.rb +3 -1
- data/lib/sentry/transport/http_transport.rb +18 -2
- data/lib/sentry/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 66ef212c06425a29b56b64935c1fb662633fa80fb9f58b9680b1d95220ee8b5d
|
4
|
+
data.tar.gz: 18e211d0c93d18717795b23290c2cb7da503b0dcd1690c1c8b51861c3a37ef98
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e334875a26a04e45a78c37e7bb0014604cd2b400b89eb01faa3ae21adc1f9072e46227ce880c1406148e32d6a125504dc08ffab5e1ac61c0b3befbd31e5352ea
|
7
|
+
data.tar.gz: 4c01fb8d489a3d143a738180f3f68ba62d3882267ecf17b82c3cf0e509ab36a8b458699d022d5d55699baacaccd2acc3bfede0d332ae5b50776128ac5c164a2a
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,36 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## 4.3.0
|
4
|
+
|
5
|
+
### Features
|
6
|
+
|
7
|
+
- Allow configuring BreadcrumbBuffer's size limit [#1310](https://github.com/getsentry/sentry-ruby/pull/1310)
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
# the SDK will only store 10 breadcrumbs (default is 100)
|
11
|
+
config.max_breadcrumbs = 10
|
12
|
+
```
|
13
|
+
|
14
|
+
- Compress event payload by default [#1314](https://github.com/getsentry/sentry-ruby/pull/1314)
|
15
|
+
|
16
|
+
### Refatorings
|
17
|
+
|
18
|
+
- Refactor interface construction [#1296](https://github.com/getsentry/sentry-ruby/pull/1296)
|
19
|
+
- Refactor tracing implementation [#1309](https://github.com/getsentry/sentry-ruby/pull/1309)
|
20
|
+
|
21
|
+
### Bug Fixes
|
22
|
+
- Improve SDK's error handling [#1298](https://github.com/getsentry/sentry-ruby/pull/1298)
|
23
|
+
- Fixes [#1246](https://github.com/getsentry/sentry-ruby/issues/1246) and [#1289](https://github.com/getsentry/sentry-ruby/issues/1289)
|
24
|
+
- Please read [#1290](https://github.com/getsentry/sentry-ruby/issues/1290) to see the full specification
|
25
|
+
- Treat query string as pii too [#1302](https://github.com/getsentry/sentry-ruby/pull/1302)
|
26
|
+
- Fixes [#1301](https://github.com/getsentry/sentry-ruby/issues/1301)
|
27
|
+
- Ignore sentry-trace when tracing is not enabled [#1308](https://github.com/getsentry/sentry-ruby/pull/1308)
|
28
|
+
- Fixes [#1307](https://github.com/getsentry/sentry-ruby/issues/1307)
|
29
|
+
- Return nil from logger methods instead of breadcrumb buffer [#1299](https://github.com/getsentry/sentry-ruby/pull/1299)
|
30
|
+
- Exceptions with nil message shouldn't cause issues [#1327](https://github.com/getsentry/sentry-ruby/pull/1327)
|
31
|
+
- Fixes [#1323](https://github.com/getsentry/sentry-ruby/issues/1323)
|
32
|
+
- Fix sampling decision with sentry-trace and add more tests [#1326](https://github.com/getsentry/sentry-ruby/pull/1326)
|
33
|
+
|
3
34
|
## 4.2.2
|
4
35
|
|
5
36
|
- Add thread_id to Exception interface [#1291](https://github.com/getsentry/sentry-ruby/pull/1291)
|
data/README.md
CHANGED
@@ -2,10 +2,14 @@
|
|
2
2
|
<a href="https://sentry.io" target="_blank" align="center">
|
3
3
|
<img src="https://sentry-brand.storage.googleapis.com/sentry-logo-black.png" width="280">
|
4
4
|
</a>
|
5
|
-
<br
|
5
|
+
<br />
|
6
6
|
</p>
|
7
7
|
|
8
|
-
|
8
|
+
_Bad software is everywhere, and we're tired of it. Sentry is on a mission to help developers write better software faster, so we can get back to enjoying technology. If you want to join us [<kbd>**Check out our open positions**</kbd>](https://sentry.io/careers/)_
|
9
|
+
|
10
|
+
Sentry SDK for Ruby
|
11
|
+
===========
|
12
|
+
|
9
13
|
|
10
14
|
**The old `sentry-raven` client has entered maintenance mode and was moved to [here](https://github.com/getsentry/sentry-ruby/tree/master/sentry-raven).**
|
11
15
|
|
data/lib/sentry-ruby.rb
CHANGED
@@ -3,6 +3,7 @@ require "forwardable"
|
|
3
3
|
require "time"
|
4
4
|
|
5
5
|
require "sentry/version"
|
6
|
+
require "sentry/exceptions"
|
6
7
|
require "sentry/core_ext/object/deep_dup"
|
7
8
|
require "sentry/utils/argument_checking_helper"
|
8
9
|
require "sentry/configuration"
|
@@ -26,9 +27,6 @@ require "sentry/background_worker"
|
|
26
27
|
end
|
27
28
|
|
28
29
|
module Sentry
|
29
|
-
class Error < StandardError
|
30
|
-
end
|
31
|
-
|
32
30
|
META = { "name" => "sentry.ruby", "version" => Sentry::VERSION }.freeze
|
33
31
|
|
34
32
|
LOGGER_PROGNAME = "sentry".freeze
|
@@ -68,7 +66,7 @@ module Sentry
|
|
68
66
|
config = Configuration.new
|
69
67
|
yield(config) if block_given?
|
70
68
|
client = Client.new(config)
|
71
|
-
scope = Scope.new
|
69
|
+
scope = Scope.new(max_breadcrumbs: config.max_breadcrumbs)
|
72
70
|
hub = Hub.new(client, scope)
|
73
71
|
Thread.current[THREAD_LOCAL] = hub
|
74
72
|
@main_hub = hub
|
@@ -2,12 +2,13 @@ require "sentry/breadcrumb"
|
|
2
2
|
|
3
3
|
module Sentry
|
4
4
|
class BreadcrumbBuffer
|
5
|
+
DEFAULT_SIZE = 100
|
5
6
|
include Enumerable
|
6
7
|
|
7
8
|
attr_accessor :buffer
|
8
9
|
|
9
|
-
def initialize(size =
|
10
|
-
@buffer = Array.new(size)
|
10
|
+
def initialize(size = nil)
|
11
|
+
@buffer = Array.new(size || DEFAULT_SIZE)
|
11
12
|
end
|
12
13
|
|
13
14
|
def record(crumb)
|
data/lib/sentry/client.rb
CHANGED
@@ -2,10 +2,11 @@ require "sentry/transport"
|
|
2
2
|
|
3
3
|
module Sentry
|
4
4
|
class Client
|
5
|
-
attr_reader :transport, :configuration
|
5
|
+
attr_reader :transport, :configuration, :logger
|
6
6
|
|
7
7
|
def initialize(configuration)
|
8
8
|
@configuration = configuration
|
9
|
+
@logger = configuration.logger
|
9
10
|
|
10
11
|
if transport_class = configuration.transport.transport_class
|
11
12
|
@transport = transport_class.new(configuration)
|
@@ -26,32 +27,17 @@ module Sentry
|
|
26
27
|
scope.apply_to_event(event, hint)
|
27
28
|
|
28
29
|
if async_block = configuration.async
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
event_hash = event.to_json_compatible
|
33
|
-
|
34
|
-
if async_block.arity == 2
|
35
|
-
hint = JSON.parse(JSON.generate(hint))
|
36
|
-
async_block.call(event_hash, hint)
|
37
|
-
else
|
38
|
-
async_block.call(event_hash)
|
39
|
-
end
|
40
|
-
rescue => e
|
41
|
-
configuration.logger.error(LOGGER_PROGNAME) { "async event sending failed: #{e.message}" }
|
42
|
-
send_event(event, hint)
|
43
|
-
end
|
30
|
+
dispatch_async_event(async_block, event, hint)
|
31
|
+
elsif hint.fetch(:background, true)
|
32
|
+
dispatch_background_event(event, hint)
|
44
33
|
else
|
45
|
-
|
46
|
-
Sentry.background_worker.perform do
|
47
|
-
send_event(event, hint)
|
48
|
-
end
|
49
|
-
else
|
50
|
-
send_event(event, hint)
|
51
|
-
end
|
34
|
+
send_event(event, hint)
|
52
35
|
end
|
53
36
|
|
54
37
|
event
|
38
|
+
rescue => e
|
39
|
+
logger.error(LOGGER_PROGNAME) { "Event capturing failed: #{e.message}" }
|
40
|
+
nil
|
55
41
|
end
|
56
42
|
|
57
43
|
def event_from_exception(exception, hint = {})
|
@@ -85,16 +71,49 @@ module Sentry
|
|
85
71
|
|
86
72
|
def send_event(event, hint = nil)
|
87
73
|
event_type = event.is_a?(Event) ? event.type : event["type"]
|
88
|
-
event = configuration.before_send.call(event, hint) if configuration.before_send && event_type == "event"
|
89
74
|
|
90
|
-
if event.
|
91
|
-
configuration.
|
92
|
-
|
75
|
+
if event_type == "event" && configuration.before_send
|
76
|
+
event = configuration.before_send.call(event, hint)
|
77
|
+
|
78
|
+
if event.nil?
|
79
|
+
logger.info(LOGGER_PROGNAME) { "Discarded event because before_send returned nil" }
|
80
|
+
return
|
81
|
+
end
|
93
82
|
end
|
94
83
|
|
95
84
|
transport.send_event(event)
|
96
85
|
|
97
86
|
event
|
87
|
+
rescue => e
|
88
|
+
logger.error(LOGGER_PROGNAME) { "#{event_type.capitalize} sending failed: #{e.message}" }
|
89
|
+
logger.error(LOGGER_PROGNAME) { "Unreported #{event_type.capitalize}: #{Event.get_log_message(event.to_hash)}" }
|
90
|
+
raise
|
98
91
|
end
|
92
|
+
|
93
|
+
private
|
94
|
+
|
95
|
+
def dispatch_background_event(event, hint)
|
96
|
+
Sentry.background_worker.perform do
|
97
|
+
send_event(event, hint)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def dispatch_async_event(async_block, event, hint)
|
102
|
+
# We have to convert to a JSON-like hash, because background job
|
103
|
+
# processors (esp ActiveJob) may not like weird types in the event hash
|
104
|
+
event_hash = event.to_json_compatible
|
105
|
+
|
106
|
+
if async_block.arity == 2
|
107
|
+
hint = JSON.parse(JSON.generate(hint))
|
108
|
+
async_block.call(event_hash, hint)
|
109
|
+
else
|
110
|
+
async_block.call(event_hash)
|
111
|
+
end
|
112
|
+
rescue => e
|
113
|
+
event_type = event_hash["type"]
|
114
|
+
logger.error(LOGGER_PROGNAME) { "Async #{event_type} sending failed: #{e.message}" }
|
115
|
+
send_event(event, hint)
|
116
|
+
end
|
117
|
+
|
99
118
|
end
|
100
119
|
end
|
data/lib/sentry/configuration.rb
CHANGED
@@ -4,6 +4,7 @@ require "sentry/utils/exception_cause_chain"
|
|
4
4
|
require "sentry/dsn"
|
5
5
|
require "sentry/transport/configuration"
|
6
6
|
require "sentry/linecache"
|
7
|
+
require "sentry/interfaces/stacktrace_builder"
|
7
8
|
|
8
9
|
module Sentry
|
9
10
|
class Configuration
|
@@ -61,6 +62,9 @@ module Sentry
|
|
61
62
|
# - :active_support_logger
|
62
63
|
attr_reader :breadcrumbs_logger
|
63
64
|
|
65
|
+
# Max number of breadcrumbs a breadcrumb buffer can hold
|
66
|
+
attr_accessor :max_breadcrumbs
|
67
|
+
|
64
68
|
# Number of lines of code context to capture, or nil for none
|
65
69
|
attr_accessor :context_lines
|
66
70
|
|
@@ -171,6 +175,7 @@ module Sentry
|
|
171
175
|
|
172
176
|
def initialize
|
173
177
|
self.background_worker_threads = Concurrent.processor_count
|
178
|
+
self.max_breadcrumbs = BreadcrumbBuffer::DEFAULT_SIZE
|
174
179
|
self.breadcrumbs_logger = []
|
175
180
|
self.context_lines = 3
|
176
181
|
self.environment = environment_from_env
|
@@ -195,6 +200,7 @@ module Sentry
|
|
195
200
|
|
196
201
|
@transport = Transport::Configuration.new
|
197
202
|
@gem_specs = Hash[Gem::Specification.map { |spec| [spec.name, spec.version.to_s] }] if Gem::Specification.respond_to?(:map)
|
203
|
+
|
198
204
|
run_post_initialization_callbacks
|
199
205
|
end
|
200
206
|
|
@@ -290,6 +296,16 @@ module Sentry
|
|
290
296
|
!!((@traces_sample_rate && @traces_sample_rate > 0.0) || @traces_sampler)
|
291
297
|
end
|
292
298
|
|
299
|
+
def stacktrace_builder
|
300
|
+
@stacktrace_builder ||= StacktraceBuilder.new(
|
301
|
+
project_root: @project_root.to_s,
|
302
|
+
app_dirs_pattern: @app_dirs_pattern,
|
303
|
+
linecache: @linecache,
|
304
|
+
context_lines: @context_lines,
|
305
|
+
backtrace_cleanup_callback: @backtrace_cleanup_callback
|
306
|
+
)
|
307
|
+
end
|
308
|
+
|
293
309
|
private
|
294
310
|
|
295
311
|
def detect_release
|
data/lib/sentry/event.rb
CHANGED
@@ -20,7 +20,7 @@ module Sentry
|
|
20
20
|
MAX_MESSAGE_SIZE_IN_BYTES = 1024 * 8
|
21
21
|
|
22
22
|
attr_accessor(*ATTRIBUTES)
|
23
|
-
attr_reader :configuration, :request, :exception, :
|
23
|
+
attr_reader :configuration, :request, :exception, :threads
|
24
24
|
|
25
25
|
def initialize(configuration:, integration_meta: nil, message: nil)
|
26
26
|
# this needs to go first because some setters rely on configuration
|
@@ -52,19 +52,26 @@ module Sentry
|
|
52
52
|
class << self
|
53
53
|
def get_log_message(event_hash)
|
54
54
|
message = event_hash[:message] || event_hash['message']
|
55
|
-
|
56
|
-
|
57
|
-
|
55
|
+
|
56
|
+
return message unless message.nil? || message.empty?
|
57
|
+
|
58
|
+
message = get_message_from_exception(event_hash)
|
59
|
+
|
60
|
+
return message unless message.nil? || message.empty?
|
61
|
+
|
62
|
+
message = event_hash[:transaction] || event_hash["transaction"]
|
63
|
+
|
64
|
+
return message unless message.nil? || message.empty?
|
65
|
+
|
66
|
+
'<no message value>'
|
58
67
|
end
|
59
68
|
|
60
69
|
def get_message_from_exception(event_hash)
|
61
|
-
(
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
"#{event_hash[:exception][:values][0][:type]}: #{event_hash[:exception][:values][0][:value]}"
|
67
|
-
)
|
70
|
+
if exception = event_hash.dig(:exception, :values, 0)
|
71
|
+
"#{exception[:type]}: #{exception[:value]}"
|
72
|
+
elsif exception = event_hash.dig("exception", "values", 0)
|
73
|
+
"#{exception["type"]}: #{exception["value"]}"
|
74
|
+
end
|
68
75
|
end
|
69
76
|
end
|
70
77
|
|
@@ -99,7 +106,6 @@ module Sentry
|
|
99
106
|
def to_hash
|
100
107
|
data = serialize_attributes
|
101
108
|
data[:breadcrumbs] = breadcrumbs.to_hash if breadcrumbs
|
102
|
-
data[:stacktrace] = stacktrace.to_hash if stacktrace
|
103
109
|
data[:request] = request.to_hash if request
|
104
110
|
data[:exception] = exception.to_hash if exception
|
105
111
|
data[:threads] = threads.to_hash if threads
|
@@ -112,48 +118,23 @@ module Sentry
|
|
112
118
|
end
|
113
119
|
|
114
120
|
def add_request_interface(env)
|
115
|
-
@request = Sentry::RequestInterface.
|
121
|
+
@request = Sentry::RequestInterface.build(env: env)
|
116
122
|
end
|
117
123
|
|
118
124
|
def add_threads_interface(backtrace: nil, **options)
|
119
|
-
@threads = ThreadsInterface.
|
120
|
-
|
125
|
+
@threads = ThreadsInterface.build(
|
126
|
+
backtrace: backtrace,
|
127
|
+
stacktrace_builder: configuration.stacktrace_builder,
|
128
|
+
**options
|
129
|
+
)
|
121
130
|
end
|
122
131
|
|
123
|
-
def add_exception_interface(
|
124
|
-
if
|
125
|
-
@extra.merge!(
|
132
|
+
def add_exception_interface(exception)
|
133
|
+
if exception.respond_to?(:sentry_context)
|
134
|
+
@extra.merge!(exception.sentry_context)
|
126
135
|
end
|
127
136
|
|
128
|
-
@exception = Sentry::ExceptionInterface.
|
129
|
-
exceptions = Sentry::Utils::ExceptionCauseChain.exception_to_array(exc).reverse
|
130
|
-
backtraces = Set.new
|
131
|
-
exc_int.values = exceptions.map do |e|
|
132
|
-
SingleExceptionInterface.new.tap do |int|
|
133
|
-
int.type = e.class.to_s
|
134
|
-
int.value = e.message.byteslice(0..MAX_MESSAGE_SIZE_IN_BYTES)
|
135
|
-
int.module = e.class.to_s.split('::')[0...-1].join('::')
|
136
|
-
int.thread_id = Thread.current.object_id
|
137
|
-
|
138
|
-
int.stacktrace =
|
139
|
-
if e.backtrace && !backtraces.include?(e.backtrace.object_id)
|
140
|
-
backtraces << e.backtrace.object_id
|
141
|
-
initialize_stacktrace_interface(e.backtrace)
|
142
|
-
end
|
143
|
-
end
|
144
|
-
end
|
145
|
-
end
|
146
|
-
end
|
147
|
-
|
148
|
-
def initialize_stacktrace_interface(backtrace)
|
149
|
-
StacktraceInterface.new(
|
150
|
-
backtrace: backtrace,
|
151
|
-
project_root: configuration.project_root.to_s,
|
152
|
-
app_dirs_pattern: configuration.app_dirs_pattern,
|
153
|
-
linecache: configuration.linecache,
|
154
|
-
context_lines: configuration.context_lines,
|
155
|
-
backtrace_cleanup_callback: configuration.backtrace_cleanup_callback
|
156
|
-
)
|
137
|
+
@exception = Sentry::ExceptionInterface.build(exception: exception, stacktrace_builder: configuration.stacktrace_builder)
|
157
138
|
end
|
158
139
|
|
159
140
|
private
|
data/lib/sentry/hub.rb
CHANGED
@@ -69,9 +69,11 @@ module Sentry
|
|
69
69
|
@stack.pop
|
70
70
|
end
|
71
71
|
|
72
|
-
def start_transaction(transaction: nil, **options)
|
72
|
+
def start_transaction(transaction: nil, configuration: Sentry.configuration, **options)
|
73
|
+
return unless configuration.tracing_enabled?
|
74
|
+
|
73
75
|
transaction ||= Transaction.new(**options)
|
74
|
-
transaction.
|
76
|
+
transaction.set_initial_sample_decision(configuration: current_client.configuration)
|
75
77
|
transaction
|
76
78
|
end
|
77
79
|
|
@@ -1,11 +1,29 @@
|
|
1
1
|
module Sentry
|
2
2
|
class ExceptionInterface < Interface
|
3
|
-
|
3
|
+
def initialize(values:)
|
4
|
+
@values = values
|
5
|
+
end
|
4
6
|
|
5
7
|
def to_hash
|
6
8
|
data = super
|
7
9
|
data[:values] = data[:values].map(&:to_hash) if data[:values]
|
8
10
|
data
|
9
11
|
end
|
12
|
+
|
13
|
+
def self.build(exception:, stacktrace_builder:)
|
14
|
+
exceptions = Sentry::Utils::ExceptionCauseChain.exception_to_array(exception).reverse
|
15
|
+
processed_backtrace_ids = Set.new
|
16
|
+
|
17
|
+
exceptions = exceptions.map do |e|
|
18
|
+
if e.backtrace && !processed_backtrace_ids.include?(e.backtrace.object_id)
|
19
|
+
processed_backtrace_ids << e.backtrace.object_id
|
20
|
+
SingleExceptionInterface.build_with_stacktrace(exception: e, stacktrace_builder: stacktrace_builder)
|
21
|
+
else
|
22
|
+
SingleExceptionInterface.new(exception: exception)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
new(values: exceptions)
|
27
|
+
end
|
10
28
|
end
|
11
29
|
end
|
@@ -17,10 +17,10 @@ module Sentry
|
|
17
17
|
|
18
18
|
attr_accessor :url, :method, :data, :query_string, :cookies, :headers, :env
|
19
19
|
|
20
|
-
def self.
|
20
|
+
def self.build(env:)
|
21
21
|
env = clean_env(env)
|
22
|
-
|
23
|
-
self.new(
|
22
|
+
request = ::Rack::Request.new(env)
|
23
|
+
self.new(request: request)
|
24
24
|
end
|
25
25
|
|
26
26
|
def self.clean_env(env)
|
@@ -34,17 +34,17 @@ module Sentry
|
|
34
34
|
env
|
35
35
|
end
|
36
36
|
|
37
|
-
def initialize(
|
38
|
-
env =
|
37
|
+
def initialize(request:)
|
38
|
+
env = request.env
|
39
39
|
|
40
40
|
if Sentry.configuration.send_default_pii
|
41
|
-
self.data = read_data_from(
|
42
|
-
self.cookies =
|
41
|
+
self.data = read_data_from(request)
|
42
|
+
self.cookies = request.cookies
|
43
|
+
self.query_string = request.query_string
|
43
44
|
end
|
44
45
|
|
45
|
-
self.url =
|
46
|
-
self.method =
|
47
|
-
self.query_string = req.query_string
|
46
|
+
self.url = request.scheme && request.url.split('?').first
|
47
|
+
self.method = request.request_method
|
48
48
|
|
49
49
|
self.headers = filter_and_format_headers(env)
|
50
50
|
self.env = filter_and_format_env(env)
|
@@ -1,15 +1,26 @@
|
|
1
1
|
module Sentry
|
2
2
|
class SingleExceptionInterface < Interface
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
3
|
+
attr_reader :type, :value, :module, :thread_id, :stacktrace
|
4
|
+
|
5
|
+
def initialize(exception:, stacktrace: nil)
|
6
|
+
@type = exception.class.to_s
|
7
|
+
@value = (exception.message || "").byteslice(0..Event::MAX_MESSAGE_SIZE_IN_BYTES)
|
8
|
+
@module = exception.class.to_s.split('::')[0...-1].join('::')
|
9
|
+
@thread_id = Thread.current.object_id
|
10
|
+
@stacktrace = stacktrace
|
11
|
+
end
|
8
12
|
|
9
13
|
def to_hash
|
10
14
|
data = super
|
11
15
|
data[:stacktrace] = data[:stacktrace].to_hash if data[:stacktrace]
|
12
16
|
data
|
13
17
|
end
|
18
|
+
|
19
|
+
# patch this method if you want to change an exception's stacktrace frames
|
20
|
+
# also see `StacktraceBuilder.build`.
|
21
|
+
def self.build_with_stacktrace(exception:, stacktrace_builder:)
|
22
|
+
stacktrace = stacktrace_builder.build(backtrace: exception.backtrace)
|
23
|
+
new(exception: exception, stacktrace: stacktrace)
|
24
|
+
end
|
14
25
|
end
|
15
26
|
end
|
@@ -2,18 +2,8 @@ module Sentry
|
|
2
2
|
class StacktraceInterface
|
3
3
|
attr_reader :frames
|
4
4
|
|
5
|
-
def initialize(
|
6
|
-
@
|
7
|
-
@frames = []
|
8
|
-
|
9
|
-
parsed_backtrace_lines = Backtrace.parse(
|
10
|
-
backtrace, project_root, app_dirs_pattern, &backtrace_cleanup_callback
|
11
|
-
).lines
|
12
|
-
|
13
|
-
parsed_backtrace_lines.reverse.each_with_object(@frames) do |line, frames|
|
14
|
-
frame = convert_parsed_line_into_frame(line, project_root, linecache, context_lines)
|
15
|
-
frames << frame if frame.filename
|
16
|
-
end
|
5
|
+
def initialize(frames:)
|
6
|
+
@frames = frames
|
17
7
|
end
|
18
8
|
|
19
9
|
def to_hash
|
@@ -22,30 +12,24 @@ module Sentry
|
|
22
12
|
|
23
13
|
private
|
24
14
|
|
25
|
-
def convert_parsed_line_into_frame(line, project_root, linecache, context_lines)
|
26
|
-
frame = StacktraceInterface::Frame.new(@project_root, line)
|
27
|
-
frame.set_context(linecache, context_lines) if context_lines
|
28
|
-
frame
|
29
|
-
end
|
30
|
-
|
31
15
|
# Not actually an interface, but I want to use the same style
|
32
16
|
class Frame < Interface
|
33
|
-
attr_accessor :abs_path, :context_line, :function, :in_app,
|
34
|
-
|
17
|
+
attr_accessor :abs_path, :context_line, :function, :in_app, :filename,
|
18
|
+
:lineno, :module, :pre_context, :post_context, :vars
|
35
19
|
|
36
20
|
def initialize(project_root, line)
|
37
21
|
@project_root = project_root
|
38
22
|
|
39
|
-
@abs_path = line.file
|
23
|
+
@abs_path = line.file
|
40
24
|
@function = line.method if line.method
|
41
25
|
@lineno = line.number
|
42
26
|
@in_app = line.in_app
|
43
27
|
@module = line.module_name if line.module_name
|
28
|
+
@filename = compute_filename
|
44
29
|
end
|
45
30
|
|
46
|
-
def
|
31
|
+
def compute_filename
|
47
32
|
return if abs_path.nil?
|
48
|
-
return @filename if instance_variable_defined?(:@filename)
|
49
33
|
|
50
34
|
prefix =
|
51
35
|
if under_project_root? && in_app
|
@@ -56,19 +40,18 @@ module Sentry
|
|
56
40
|
longest_load_path
|
57
41
|
end
|
58
42
|
|
59
|
-
|
43
|
+
prefix ? abs_path[prefix.to_s.chomp(File::SEPARATOR).length + 1..-1] : abs_path
|
60
44
|
end
|
61
45
|
|
62
46
|
def set_context(linecache, context_lines)
|
63
47
|
return unless abs_path
|
64
48
|
|
65
|
-
|
49
|
+
@pre_context, @context_line, @post_context = \
|
66
50
|
linecache.get_file_context(abs_path, lineno, context_lines)
|
67
51
|
end
|
68
52
|
|
69
53
|
def to_hash(*args)
|
70
54
|
data = super(*args)
|
71
|
-
data[:filename] = filename
|
72
55
|
data.delete(:vars) unless vars && !vars.empty?
|
73
56
|
data.delete(:pre_context) unless pre_context && !pre_context.empty?
|
74
57
|
data.delete(:post_context) unless post_context && !post_context.empty?
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Sentry
|
2
|
+
class StacktraceBuilder
|
3
|
+
attr_reader :project_root, :app_dirs_pattern, :linecache, :context_lines, :backtrace_cleanup_callback
|
4
|
+
|
5
|
+
def initialize(project_root:, app_dirs_pattern:, linecache:, context_lines:, backtrace_cleanup_callback: nil)
|
6
|
+
@project_root = project_root
|
7
|
+
@app_dirs_pattern = app_dirs_pattern
|
8
|
+
@linecache = linecache
|
9
|
+
@context_lines = context_lines
|
10
|
+
@backtrace_cleanup_callback = backtrace_cleanup_callback
|
11
|
+
end
|
12
|
+
|
13
|
+
# you can pass a block to customize/exclude frames:
|
14
|
+
#
|
15
|
+
# ```ruby
|
16
|
+
# builder.build(backtrace) do |frame|
|
17
|
+
# if frame.module.match?(/a_gem/)
|
18
|
+
# nil
|
19
|
+
# else
|
20
|
+
# frame
|
21
|
+
# end
|
22
|
+
# end
|
23
|
+
# ```
|
24
|
+
def build(backtrace:, &frame_callback)
|
25
|
+
parsed_lines = parse_backtrace_lines(backtrace).select(&:file)
|
26
|
+
|
27
|
+
frames = parsed_lines.reverse.map do |line|
|
28
|
+
frame = convert_parsed_line_into_frame(line)
|
29
|
+
frame = frame_callback.call(frame) if frame_callback
|
30
|
+
frame
|
31
|
+
end.compact
|
32
|
+
|
33
|
+
StacktraceInterface.new(frames: frames)
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def convert_parsed_line_into_frame(line)
|
39
|
+
frame = StacktraceInterface::Frame.new(project_root, line)
|
40
|
+
frame.set_context(linecache, context_lines) if context_lines
|
41
|
+
frame
|
42
|
+
end
|
43
|
+
|
44
|
+
def parse_backtrace_lines(backtrace)
|
45
|
+
Backtrace.parse(
|
46
|
+
backtrace, project_root, app_dirs_pattern, &backtrace_cleanup_callback
|
47
|
+
).lines
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -1,12 +1,11 @@
|
|
1
1
|
module Sentry
|
2
2
|
class ThreadsInterface
|
3
|
-
|
4
|
-
|
5
|
-
def initialize(crashed: false)
|
3
|
+
def initialize(crashed: false, stacktrace: nil)
|
6
4
|
@id = Thread.current.object_id
|
7
5
|
@name = Thread.current.name
|
8
6
|
@current = true
|
9
7
|
@crashed = crashed
|
8
|
+
@stacktrace = stacktrace
|
10
9
|
end
|
11
10
|
|
12
11
|
def to_hash
|
@@ -22,5 +21,12 @@ module Sentry
|
|
22
21
|
]
|
23
22
|
}
|
24
23
|
end
|
24
|
+
|
25
|
+
# patch this method if you want to change a threads interface's stacktrace frames
|
26
|
+
# also see `StacktraceBuilder.build`.
|
27
|
+
def self.build(backtrace:, stacktrace_builder:, **options)
|
28
|
+
stacktrace = stacktrace_builder.build(backtrace: backtrace) if backtrace
|
29
|
+
new(**options, stacktrace: stacktrace)
|
30
|
+
end
|
25
31
|
end
|
26
32
|
end
|
@@ -16,27 +16,24 @@ module Sentry
|
|
16
16
|
scope.set_transaction_name(env["PATH_INFO"]) if env["PATH_INFO"]
|
17
17
|
scope.set_rack_env(env)
|
18
18
|
|
19
|
-
|
20
|
-
|
21
|
-
span ||= Sentry.start_transaction(name: scope.transaction_name, op: transaction_op)
|
22
|
-
|
23
|
-
scope.set_span(span)
|
19
|
+
transaction = start_transaction(env, scope)
|
20
|
+
scope.set_span(transaction) if transaction
|
24
21
|
|
25
22
|
begin
|
26
23
|
response = @app.call(env)
|
27
24
|
rescue Sentry::Error
|
28
|
-
|
25
|
+
finish_transaction(transaction, 500)
|
29
26
|
raise # Don't capture Sentry errors
|
30
27
|
rescue Exception => e
|
31
28
|
capture_exception(e)
|
32
|
-
|
29
|
+
finish_transaction(transaction, 500)
|
33
30
|
raise
|
34
31
|
end
|
35
32
|
|
36
33
|
exception = collect_exception(env)
|
37
34
|
capture_exception(exception) if exception
|
38
35
|
|
39
|
-
|
36
|
+
finish_transaction(transaction, response[0])
|
40
37
|
|
41
38
|
response
|
42
39
|
end
|
@@ -56,9 +53,19 @@ module Sentry
|
|
56
53
|
Sentry.capture_exception(exception)
|
57
54
|
end
|
58
55
|
|
59
|
-
def
|
60
|
-
|
61
|
-
|
56
|
+
def start_transaction(env, scope)
|
57
|
+
sentry_trace = env["HTTP_SENTRY_TRACE"]
|
58
|
+
options = { name: scope.transaction_name, op: transaction_op }
|
59
|
+
transaction = Sentry::Transaction.from_sentry_trace(sentry_trace, **options) if sentry_trace
|
60
|
+
Sentry.start_transaction(transaction: transaction, **options)
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
def finish_transaction(transaction, status_code)
|
65
|
+
return unless transaction
|
66
|
+
|
67
|
+
transaction.set_http_status(status_code)
|
68
|
+
transaction.finish
|
62
69
|
end
|
63
70
|
end
|
64
71
|
end
|
data/lib/sentry/scope.rb
CHANGED
@@ -9,7 +9,8 @@ module Sentry
|
|
9
9
|
|
10
10
|
attr_reader(*ATTRIBUTES)
|
11
11
|
|
12
|
-
def initialize
|
12
|
+
def initialize(max_breadcrumbs: nil)
|
13
|
+
@max_breadcrumbs = max_breadcrumbs
|
13
14
|
set_default_value
|
14
15
|
end
|
15
16
|
|
@@ -47,7 +48,7 @@ module Sentry
|
|
47
48
|
end
|
48
49
|
|
49
50
|
def clear_breadcrumbs
|
50
|
-
|
51
|
+
set_new_breadcrumb_buffer
|
51
52
|
end
|
52
53
|
|
53
54
|
def dup
|
@@ -171,7 +172,6 @@ module Sentry
|
|
171
172
|
private
|
172
173
|
|
173
174
|
def set_default_value
|
174
|
-
@breadcrumbs = BreadcrumbBuffer.new
|
175
175
|
@contexts = { :os => self.class.os_context, :runtime => self.class.runtime_context }
|
176
176
|
@extra = {}
|
177
177
|
@tags = {}
|
@@ -182,8 +182,14 @@ module Sentry
|
|
182
182
|
@event_processors = []
|
183
183
|
@rack_env = {}
|
184
184
|
@span = nil
|
185
|
+
set_new_breadcrumb_buffer
|
185
186
|
end
|
186
187
|
|
188
|
+
def set_new_breadcrumb_buffer
|
189
|
+
@breadcrumbs = BreadcrumbBuffer.new(@max_breadcrumbs)
|
190
|
+
end
|
191
|
+
|
192
|
+
|
187
193
|
class << self
|
188
194
|
def os_context
|
189
195
|
@os_context ||=
|
data/lib/sentry/transaction.rb
CHANGED
@@ -25,14 +25,20 @@ module Sentry
|
|
25
25
|
@span_recorder.add(self)
|
26
26
|
end
|
27
27
|
|
28
|
-
def self.from_sentry_trace(sentry_trace, **options)
|
28
|
+
def self.from_sentry_trace(sentry_trace, configuration: Sentry.configuration, **options)
|
29
|
+
return unless configuration.tracing_enabled?
|
29
30
|
return unless sentry_trace
|
30
31
|
|
31
32
|
match = SENTRY_TRACE_REGEXP.match(sentry_trace)
|
32
33
|
return if match.nil?
|
33
34
|
trace_id, parent_span_id, sampled_flag = match[1..3]
|
34
35
|
|
35
|
-
sampled =
|
36
|
+
sampled =
|
37
|
+
if sampled_flag.nil?
|
38
|
+
nil
|
39
|
+
else
|
40
|
+
sampled_flag != "0"
|
41
|
+
end
|
36
42
|
|
37
43
|
new(trace_id: trace_id, parent_span_id: parent_span_id, parent_sampled: sampled, sampled: sampled, **options)
|
38
44
|
end
|
@@ -67,8 +73,8 @@ module Sentry
|
|
67
73
|
copy
|
68
74
|
end
|
69
75
|
|
70
|
-
def
|
71
|
-
unless
|
76
|
+
def set_initial_sample_decision(sampling_context: {}, configuration: Sentry.configuration)
|
77
|
+
unless configuration.tracing_enabled?
|
72
78
|
@sampled = false
|
73
79
|
return
|
74
80
|
end
|
@@ -77,9 +83,9 @@ module Sentry
|
|
77
83
|
|
78
84
|
transaction_description = generate_transaction_description
|
79
85
|
|
80
|
-
logger =
|
81
|
-
sample_rate =
|
82
|
-
traces_sampler =
|
86
|
+
logger = configuration.logger
|
87
|
+
sample_rate = configuration.traces_sample_rate
|
88
|
+
traces_sampler = configuration.traces_sampler
|
83
89
|
|
84
90
|
if traces_sampler.is_a?(Proc)
|
85
91
|
sampling_context = sampling_context.merge(
|
data/lib/sentry/transport.rb
CHANGED
@@ -31,9 +31,6 @@ module Sentry
|
|
31
31
|
send_data(encoded_data)
|
32
32
|
|
33
33
|
event
|
34
|
-
rescue => e
|
35
|
-
failed_for_exception(e, event)
|
36
|
-
nil
|
37
34
|
end
|
38
35
|
|
39
36
|
def generate_auth_header
|
@@ -72,15 +69,6 @@ module Sentry
|
|
72
69
|
configuration.logger.info(LOGGER_PROGNAME) { "Sending #{event_type} #{event_id} to Sentry" }
|
73
70
|
encode(event_hash)
|
74
71
|
end
|
75
|
-
|
76
|
-
def failed_for_exception(e, event)
|
77
|
-
configuration.logger.warn(LOGGER_PROGNAME) { "Unable to record event with remote Sentry server (#{e.class} - #{e.message}):\n#{e.backtrace[0..10].join("\n")}" }
|
78
|
-
log_not_sending(event)
|
79
|
-
end
|
80
|
-
|
81
|
-
def log_not_sending(event)
|
82
|
-
configuration.logger.warn(LOGGER_PROGNAME) { "Failed to submit event. Unreported Event: #{Event.get_log_message(event.to_hash)}" }
|
83
|
-
end
|
84
72
|
end
|
85
73
|
end
|
86
74
|
|
@@ -1,12 +1,14 @@
|
|
1
1
|
module Sentry
|
2
2
|
class Transport
|
3
3
|
class Configuration
|
4
|
-
attr_accessor :timeout, :open_timeout, :proxy, :ssl, :ssl_ca_file, :ssl_verification, :http_adapter, :faraday_builder,
|
4
|
+
attr_accessor :timeout, :open_timeout, :proxy, :ssl, :ssl_ca_file, :ssl_verification, :http_adapter, :faraday_builder,
|
5
|
+
:transport_class, :encoding
|
5
6
|
|
6
7
|
def initialize
|
7
8
|
@ssl_verification = true
|
8
9
|
@open_timeout = 1
|
9
10
|
@timeout = 2
|
11
|
+
@encoding = HTTPTransport::GZIP_ENCODING
|
10
12
|
end
|
11
13
|
|
12
14
|
def transport_class=(klass)
|
@@ -1,8 +1,12 @@
|
|
1
1
|
require 'faraday'
|
2
|
+
require 'zlib'
|
2
3
|
|
3
4
|
module Sentry
|
4
5
|
class HTTPTransport < Transport
|
5
|
-
|
6
|
+
GZIP_ENCODING = "gzip"
|
7
|
+
GZIP_THRESHOLD = 1024 * 30
|
8
|
+
CONTENT_TYPE = 'application/x-sentry-envelope'
|
9
|
+
|
6
10
|
attr_reader :conn, :adapter
|
7
11
|
|
8
12
|
def initialize(*args)
|
@@ -13,8 +17,16 @@ module Sentry
|
|
13
17
|
end
|
14
18
|
|
15
19
|
def send_data(data)
|
20
|
+
encoding = ""
|
21
|
+
|
22
|
+
if should_compress?(data)
|
23
|
+
data = Zlib.gzip(data)
|
24
|
+
encoding = GZIP_ENCODING
|
25
|
+
end
|
26
|
+
|
16
27
|
conn.post @endpoint do |req|
|
17
28
|
req.headers['Content-Type'] = CONTENT_TYPE
|
29
|
+
req.headers['Content-Encoding'] = encoding
|
18
30
|
req.headers['X-Sentry-Auth'] = generate_auth_header
|
19
31
|
req.body = data
|
20
32
|
end
|
@@ -26,11 +38,15 @@ module Sentry
|
|
26
38
|
error_info += " Error in headers is: #{e.response[:headers]['x-sentry-error']}" if e.response[:headers]['x-sentry-error']
|
27
39
|
end
|
28
40
|
|
29
|
-
raise Sentry::
|
41
|
+
raise Sentry::ExternalError, error_info
|
30
42
|
end
|
31
43
|
|
32
44
|
private
|
33
45
|
|
46
|
+
def should_compress?(data)
|
47
|
+
@transport_configuration.encoding == GZIP_ENCODING && data.bytesize >= GZIP_THRESHOLD
|
48
|
+
end
|
49
|
+
|
34
50
|
def set_conn
|
35
51
|
server = @dsn.server
|
36
52
|
|
data/lib/sentry/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sentry-ruby-core
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.
|
4
|
+
version: 4.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sentry Team
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-03-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: faraday
|
@@ -71,6 +71,7 @@ files:
|
|
71
71
|
- lib/sentry/core_ext/object/duplicable.rb
|
72
72
|
- lib/sentry/dsn.rb
|
73
73
|
- lib/sentry/event.rb
|
74
|
+
- lib/sentry/exceptions.rb
|
74
75
|
- lib/sentry/hub.rb
|
75
76
|
- lib/sentry/integrable.rb
|
76
77
|
- lib/sentry/interface.rb
|
@@ -78,6 +79,7 @@ files:
|
|
78
79
|
- lib/sentry/interfaces/request.rb
|
79
80
|
- lib/sentry/interfaces/single_exception.rb
|
80
81
|
- lib/sentry/interfaces/stacktrace.rb
|
82
|
+
- lib/sentry/interfaces/stacktrace_builder.rb
|
81
83
|
- lib/sentry/interfaces/threads.rb
|
82
84
|
- lib/sentry/linecache.rb
|
83
85
|
- lib/sentry/logger.rb
|