sentry-ruby 5.3.1 → 5.16.1

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 (76) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +11 -0
  3. data/.rspec +2 -0
  4. data/.yardopts +2 -0
  5. data/CHANGELOG.md +313 -0
  6. data/Gemfile +26 -0
  7. data/Makefile +4 -0
  8. data/README.md +11 -8
  9. data/Rakefile +20 -0
  10. data/bin/console +18 -0
  11. data/bin/setup +8 -0
  12. data/lib/sentry/background_worker.rb +79 -0
  13. data/lib/sentry/backpressure_monitor.rb +75 -0
  14. data/lib/sentry/backtrace.rb +124 -0
  15. data/lib/sentry/baggage.rb +70 -0
  16. data/lib/sentry/breadcrumb/sentry_logger.rb +90 -0
  17. data/lib/sentry/breadcrumb.rb +76 -0
  18. data/lib/sentry/breadcrumb_buffer.rb +64 -0
  19. data/lib/sentry/check_in_event.rb +60 -0
  20. data/lib/sentry/client.rb +248 -0
  21. data/lib/sentry/configuration.rb +650 -0
  22. data/lib/sentry/core_ext/object/deep_dup.rb +61 -0
  23. data/lib/sentry/core_ext/object/duplicable.rb +155 -0
  24. data/lib/sentry/cron/configuration.rb +23 -0
  25. data/lib/sentry/cron/monitor_check_ins.rb +75 -0
  26. data/lib/sentry/cron/monitor_config.rb +53 -0
  27. data/lib/sentry/cron/monitor_schedule.rb +42 -0
  28. data/lib/sentry/dsn.rb +53 -0
  29. data/lib/sentry/envelope.rb +93 -0
  30. data/lib/sentry/error_event.rb +38 -0
  31. data/lib/sentry/event.rb +156 -0
  32. data/lib/sentry/exceptions.rb +9 -0
  33. data/lib/sentry/hub.rb +316 -0
  34. data/lib/sentry/integrable.rb +32 -0
  35. data/lib/sentry/interface.rb +16 -0
  36. data/lib/sentry/interfaces/exception.rb +43 -0
  37. data/lib/sentry/interfaces/request.rb +134 -0
  38. data/lib/sentry/interfaces/single_exception.rb +67 -0
  39. data/lib/sentry/interfaces/stacktrace.rb +87 -0
  40. data/lib/sentry/interfaces/stacktrace_builder.rb +79 -0
  41. data/lib/sentry/interfaces/threads.rb +42 -0
  42. data/lib/sentry/linecache.rb +47 -0
  43. data/lib/sentry/logger.rb +20 -0
  44. data/lib/sentry/net/http.rb +106 -0
  45. data/lib/sentry/profiler.rb +233 -0
  46. data/lib/sentry/propagation_context.rb +134 -0
  47. data/lib/sentry/puma.rb +32 -0
  48. data/lib/sentry/rack/capture_exceptions.rb +79 -0
  49. data/lib/sentry/rack.rb +5 -0
  50. data/lib/sentry/rake.rb +28 -0
  51. data/lib/sentry/redis.rb +108 -0
  52. data/lib/sentry/release_detector.rb +39 -0
  53. data/lib/sentry/scope.rb +360 -0
  54. data/lib/sentry/session.rb +33 -0
  55. data/lib/sentry/session_flusher.rb +90 -0
  56. data/lib/sentry/span.rb +273 -0
  57. data/lib/sentry/test_helper.rb +84 -0
  58. data/lib/sentry/transaction.rb +359 -0
  59. data/lib/sentry/transaction_event.rb +80 -0
  60. data/lib/sentry/transport/configuration.rb +98 -0
  61. data/lib/sentry/transport/dummy_transport.rb +21 -0
  62. data/lib/sentry/transport/http_transport.rb +206 -0
  63. data/lib/sentry/transport/spotlight_transport.rb +50 -0
  64. data/lib/sentry/transport.rb +225 -0
  65. data/lib/sentry/utils/argument_checking_helper.rb +19 -0
  66. data/lib/sentry/utils/custom_inspection.rb +14 -0
  67. data/lib/sentry/utils/encoding_helper.rb +22 -0
  68. data/lib/sentry/utils/exception_cause_chain.rb +20 -0
  69. data/lib/sentry/utils/logging_helper.rb +26 -0
  70. data/lib/sentry/utils/real_ip.rb +84 -0
  71. data/lib/sentry/utils/request_id.rb +18 -0
  72. data/lib/sentry/version.rb +5 -0
  73. data/lib/sentry-ruby.rb +580 -0
  74. data/sentry-ruby-core.gemspec +23 -0
  75. data/sentry-ruby.gemspec +24 -0
  76. metadata +75 -16
@@ -0,0 +1,248 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "sentry/transport"
4
+
5
+ module Sentry
6
+ class Client
7
+ include LoggingHelper
8
+
9
+ # The Transport object that'll send events for the client.
10
+ # @return [Transport]
11
+ attr_reader :transport
12
+
13
+ # The Transport object that'll send events for the client.
14
+ # @return [SpotlightTransport, nil]
15
+ attr_reader :spotlight_transport
16
+
17
+ # @!macro configuration
18
+ attr_reader :configuration
19
+
20
+ # @deprecated Use Sentry.logger to retrieve the current logger instead.
21
+ attr_reader :logger
22
+
23
+ # @param configuration [Configuration]
24
+ def initialize(configuration)
25
+ @configuration = configuration
26
+ @logger = configuration.logger
27
+
28
+ if transport_class = configuration.transport.transport_class
29
+ @transport = transport_class.new(configuration)
30
+ else
31
+ @transport =
32
+ case configuration.dsn&.scheme
33
+ when 'http', 'https'
34
+ HTTPTransport.new(configuration)
35
+ else
36
+ DummyTransport.new(configuration)
37
+ end
38
+ end
39
+
40
+ @spotlight_transport = SpotlightTransport.new(configuration) if configuration.spotlight
41
+ end
42
+
43
+ # Applies the given scope's data to the event and sends it to Sentry.
44
+ # @param event [Event] the event to be sent.
45
+ # @param scope [Scope] the scope with contextual data that'll be applied to the event before it's sent.
46
+ # @param hint [Hash] the hint data that'll be passed to `before_send` callback and the scope's event processors.
47
+ # @return [Event, nil]
48
+ def capture_event(event, scope, hint = {})
49
+ return unless configuration.sending_allowed?
50
+
51
+ if event.is_a?(ErrorEvent) && !configuration.sample_allowed?
52
+ transport.record_lost_event(:sample_rate, 'event')
53
+ return
54
+ end
55
+
56
+ event_type = event.is_a?(Event) ? event.type : event["type"]
57
+ event = scope.apply_to_event(event, hint)
58
+
59
+ if event.nil?
60
+ log_debug("Discarded event because one of the event processors returned nil")
61
+ transport.record_lost_event(:event_processor, event_type)
62
+ return
63
+ end
64
+
65
+ if async_block = configuration.async
66
+ dispatch_async_event(async_block, event, hint)
67
+ elsif configuration.background_worker_threads != 0 && hint.fetch(:background, true)
68
+ queued = dispatch_background_event(event, hint)
69
+ transport.record_lost_event(:queue_overflow, event_type) unless queued
70
+ else
71
+ send_event(event, hint)
72
+ end
73
+
74
+ event
75
+ rescue => e
76
+ log_error("Event capturing failed", e, debug: configuration.debug)
77
+ nil
78
+ end
79
+
80
+ # Initializes an Event object with the given exception. Returns `nil` if the exception's class is excluded from reporting.
81
+ # @param exception [Exception] the exception to be reported.
82
+ # @param hint [Hash] the hint data that'll be passed to `before_send` callback and the scope's event processors.
83
+ # @return [Event, nil]
84
+ def event_from_exception(exception, hint = {})
85
+ return unless @configuration.sending_allowed?
86
+
87
+ ignore_exclusions = hint.delete(:ignore_exclusions) { false }
88
+ return if !ignore_exclusions && !@configuration.exception_class_allowed?(exception)
89
+
90
+ integration_meta = Sentry.integrations[hint[:integration]]
91
+
92
+ ErrorEvent.new(configuration: configuration, integration_meta: integration_meta).tap do |event|
93
+ event.add_exception_interface(exception)
94
+ event.add_threads_interface(crashed: true)
95
+ event.level = :error
96
+ end
97
+ end
98
+
99
+ # Initializes an Event object with the given message.
100
+ # @param message [String] the message to be reported.
101
+ # @param hint [Hash] the hint data that'll be passed to `before_send` callback and the scope's event processors.
102
+ # @return [Event]
103
+ def event_from_message(message, hint = {}, backtrace: nil)
104
+ return unless @configuration.sending_allowed?
105
+
106
+ integration_meta = Sentry.integrations[hint[:integration]]
107
+ event = ErrorEvent.new(configuration: configuration, integration_meta: integration_meta, message: message)
108
+ event.add_threads_interface(backtrace: backtrace || caller)
109
+ event.level = :error
110
+ event
111
+ end
112
+
113
+ # Initializes a CheckInEvent object with the given options.
114
+ #
115
+ # @param slug [String] identifier of this monitor
116
+ # @param status [Symbol] status of this check-in, one of {CheckInEvent::VALID_STATUSES}
117
+ # @param hint [Hash] the hint data that'll be passed to `before_send` callback and the scope's event processors.
118
+ # @param duration [Integer, nil] seconds elapsed since this monitor started
119
+ # @param monitor_config [Cron::MonitorConfig, nil] configuration for this monitor
120
+ # @param check_in_id [String, nil] for updating the status of an existing monitor
121
+ #
122
+ # @return [Event]
123
+ def event_from_check_in(
124
+ slug,
125
+ status,
126
+ hint = {},
127
+ duration: nil,
128
+ monitor_config: nil,
129
+ check_in_id: nil
130
+ )
131
+ return unless configuration.sending_allowed?
132
+
133
+ CheckInEvent.new(
134
+ configuration: configuration,
135
+ integration_meta: Sentry.integrations[hint[:integration]],
136
+ slug: slug,
137
+ status: status,
138
+ duration: duration,
139
+ monitor_config: monitor_config,
140
+ check_in_id: check_in_id
141
+ )
142
+ end
143
+
144
+ # Initializes an Event object with the given Transaction object.
145
+ # @param transaction [Transaction] the transaction to be recorded.
146
+ # @return [TransactionEvent]
147
+ def event_from_transaction(transaction)
148
+ TransactionEvent.new(configuration: configuration, transaction: transaction)
149
+ end
150
+
151
+ # @!macro send_event
152
+ def send_event(event, hint = nil)
153
+ event_type = event.is_a?(Event) ? event.type : event["type"]
154
+
155
+ if event_type != TransactionEvent::TYPE && configuration.before_send
156
+ event = configuration.before_send.call(event, hint)
157
+
158
+ if event.nil?
159
+ log_debug("Discarded event because before_send returned nil")
160
+ transport.record_lost_event(:before_send, 'event')
161
+ return
162
+ end
163
+ end
164
+
165
+ if event_type == TransactionEvent::TYPE && configuration.before_send_transaction
166
+ event = configuration.before_send_transaction.call(event, hint)
167
+
168
+ if event.nil?
169
+ log_debug("Discarded event because before_send_transaction returned nil")
170
+ transport.record_lost_event(:before_send, 'transaction')
171
+ return
172
+ end
173
+ end
174
+
175
+ transport.send_event(event)
176
+ spotlight_transport&.send_event(event)
177
+
178
+ event
179
+ rescue => e
180
+ log_error("Event sending failed", e, debug: configuration.debug)
181
+ transport.record_lost_event(:network_error, event_type)
182
+ raise
183
+ end
184
+
185
+ # @deprecated use Sentry.get_traceparent instead.
186
+ #
187
+ # Generates a Sentry trace for distribted tracing from the given Span.
188
+ # Returns `nil` if `config.propagate_traces` is `false`.
189
+ # @param span [Span] the span to generate trace from.
190
+ # @return [String, nil]
191
+ def generate_sentry_trace(span)
192
+ return unless configuration.propagate_traces
193
+
194
+ trace = span.to_sentry_trace
195
+ log_debug("[Tracing] Adding #{SENTRY_TRACE_HEADER_NAME} header to outgoing request: #{trace}")
196
+ trace
197
+ end
198
+
199
+ # @deprecated Use Sentry.get_baggage instead.
200
+ #
201
+ # Generates a W3C Baggage header for distributed tracing from the given Span.
202
+ # Returns `nil` if `config.propagate_traces` is `false`.
203
+ # @param span [Span] the span to generate trace from.
204
+ # @return [String, nil]
205
+ def generate_baggage(span)
206
+ return unless configuration.propagate_traces
207
+
208
+ baggage = span.to_baggage
209
+
210
+ if baggage && !baggage.empty?
211
+ log_debug("[Tracing] Adding #{BAGGAGE_HEADER_NAME} header to outgoing request: #{baggage}")
212
+ end
213
+
214
+ baggage
215
+ end
216
+
217
+ private
218
+
219
+ def dispatch_background_event(event, hint)
220
+ Sentry.background_worker.perform do
221
+ send_event(event, hint)
222
+ end
223
+ end
224
+
225
+ def dispatch_async_event(async_block, event, hint)
226
+ # We have to convert to a JSON-like hash, because background job
227
+ # processors (esp ActiveJob) may not like weird types in the event hash
228
+
229
+ event_hash =
230
+ begin
231
+ event.to_json_compatible
232
+ rescue => e
233
+ log_error("Converting #{event.type} (#{event.event_id}) to JSON compatible hash failed", e, debug: configuration.debug)
234
+ return
235
+ end
236
+
237
+ if async_block.arity == 2
238
+ hint = JSON.parse(JSON.generate(hint))
239
+ async_block.call(event_hash, hint)
240
+ else
241
+ async_block.call(event_hash)
242
+ end
243
+ rescue => e
244
+ log_error("Async #{event_hash["type"]} sending failed", e, debug: configuration.debug)
245
+ send_event(event, hint)
246
+ end
247
+ end
248
+ end