sentry-ruby 5.13.0 → 5.19.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +4 -18
  3. data/README.md +20 -10
  4. data/Rakefile +1 -1
  5. data/bin/console +1 -0
  6. data/lib/sentry/attachment.rb +42 -0
  7. data/lib/sentry/background_worker.rb +9 -2
  8. data/lib/sentry/backpressure_monitor.rb +45 -0
  9. data/lib/sentry/backtrace.rb +7 -3
  10. data/lib/sentry/check_in_event.rb +1 -1
  11. data/lib/sentry/client.rb +69 -16
  12. data/lib/sentry/configuration.rb +57 -14
  13. data/lib/sentry/cron/configuration.rb +23 -0
  14. data/lib/sentry/cron/monitor_check_ins.rb +40 -26
  15. data/lib/sentry/cron/monitor_schedule.rb +1 -1
  16. data/lib/sentry/dsn.rb +1 -1
  17. data/lib/sentry/envelope.rb +18 -1
  18. data/lib/sentry/error_event.rb +2 -2
  19. data/lib/sentry/event.rb +13 -39
  20. data/lib/sentry/faraday.rb +77 -0
  21. data/lib/sentry/graphql.rb +9 -0
  22. data/lib/sentry/hub.rb +17 -4
  23. data/lib/sentry/integrable.rb +4 -0
  24. data/lib/sentry/interface.rb +1 -0
  25. data/lib/sentry/interfaces/exception.rb +5 -3
  26. data/lib/sentry/interfaces/mechanism.rb +20 -0
  27. data/lib/sentry/interfaces/request.rb +2 -2
  28. data/lib/sentry/interfaces/single_exception.rb +7 -4
  29. data/lib/sentry/interfaces/stacktrace_builder.rb +8 -0
  30. data/lib/sentry/metrics/aggregator.rb +248 -0
  31. data/lib/sentry/metrics/configuration.rb +47 -0
  32. data/lib/sentry/metrics/counter_metric.rb +25 -0
  33. data/lib/sentry/metrics/distribution_metric.rb +25 -0
  34. data/lib/sentry/metrics/gauge_metric.rb +35 -0
  35. data/lib/sentry/metrics/local_aggregator.rb +53 -0
  36. data/lib/sentry/metrics/metric.rb +19 -0
  37. data/lib/sentry/metrics/set_metric.rb +28 -0
  38. data/lib/sentry/metrics/timing.rb +43 -0
  39. data/lib/sentry/metrics.rb +56 -0
  40. data/lib/sentry/net/http.rb +22 -39
  41. data/lib/sentry/propagation_context.rb +9 -8
  42. data/lib/sentry/puma.rb +1 -1
  43. data/lib/sentry/rack/capture_exceptions.rb +14 -2
  44. data/lib/sentry/rake.rb +3 -14
  45. data/lib/sentry/redis.rb +2 -1
  46. data/lib/sentry/release_detector.rb +1 -1
  47. data/lib/sentry/scope.rb +47 -37
  48. data/lib/sentry/session.rb +2 -2
  49. data/lib/sentry/session_flusher.rb +6 -38
  50. data/lib/sentry/span.rb +40 -5
  51. data/lib/sentry/test_helper.rb +2 -1
  52. data/lib/sentry/threaded_periodic_worker.rb +39 -0
  53. data/lib/sentry/transaction.rb +25 -16
  54. data/lib/sentry/transaction_event.rb +5 -0
  55. data/lib/sentry/transport/configuration.rb +73 -1
  56. data/lib/sentry/transport/http_transport.rb +68 -37
  57. data/lib/sentry/transport/spotlight_transport.rb +50 -0
  58. data/lib/sentry/transport.rb +32 -37
  59. data/lib/sentry/utils/argument_checking_helper.rb +6 -0
  60. data/lib/sentry/utils/http_tracing.rb +41 -0
  61. data/lib/sentry/utils/logging_helper.rb +0 -4
  62. data/lib/sentry/utils/real_ip.rb +1 -1
  63. data/lib/sentry/utils/request_id.rb +1 -1
  64. data/lib/sentry/version.rb +1 -1
  65. data/lib/sentry-ruby.rb +57 -24
  66. data/sentry-ruby.gemspec +12 -5
  67. metadata +42 -7
@@ -17,6 +17,9 @@ module Sentry
17
17
  # @return [Hash, nil]
18
18
  attr_accessor :profile
19
19
 
20
+ # @return [Hash, nil]
21
+ attr_accessor :metrics_summary
22
+
20
23
  def initialize(transaction:, **options)
21
24
  super(**options)
22
25
 
@@ -29,6 +32,7 @@ module Sentry
29
32
  self.tags = transaction.tags
30
33
  self.dynamic_sampling_context = transaction.get_baggage.dynamic_sampling_context
31
34
  self.measurements = transaction.measurements
35
+ self.metrics_summary = transaction.metrics_summary
32
36
 
33
37
  finished_spans = transaction.span_recorder.spans.select { |span| span.timestamp && span != transaction }
34
38
  self.spans = finished_spans.map(&:to_hash)
@@ -49,6 +53,7 @@ module Sentry
49
53
  data[:spans] = @spans.map(&:to_hash) if @spans
50
54
  data[:start_timestamp] = @start_timestamp
51
55
  data[:measurements] = @measurements
56
+ data[:_metrics_summary] = @metrics_summary if @metrics_summary
52
57
  data
53
58
  end
54
59
 
@@ -3,7 +3,79 @@
3
3
  module Sentry
4
4
  class Transport
5
5
  class Configuration
6
- attr_accessor :timeout, :open_timeout, :proxy, :ssl, :ssl_ca_file, :ssl_verification, :encoding
6
+ # The timeout in seconds to open a connection to Sentry, in seconds.
7
+ # Default value is 2.
8
+ #
9
+ # @return [Integer]
10
+ attr_accessor :timeout
11
+
12
+ # The timeout in seconds to read data from Sentry, in seconds.
13
+ # Default value is 1.
14
+ #
15
+ # @return [Integer]
16
+ attr_accessor :open_timeout
17
+
18
+ # The proxy configuration to use to connect to Sentry.
19
+ # Accepts either a URI formatted string, URI, or a hash with the `uri`,
20
+ # `user`, and `password` keys.
21
+ #
22
+ # @example
23
+ # # setup proxy using a string:
24
+ # config.transport.proxy = "https://user:password@proxyhost:8080"
25
+ #
26
+ # # setup proxy using a URI:
27
+ # config.transport.proxy = URI("https://user:password@proxyhost:8080")
28
+ #
29
+ # # setup proxy using a hash:
30
+ # config.transport.proxy = {
31
+ # uri: URI("https://proxyhost:8080"),
32
+ # user: "user",
33
+ # password: "password"
34
+ # }
35
+ #
36
+ # If you're using the default transport (`Sentry::HTTPTransport`),
37
+ # proxy settings will also automatically be read from tne environment
38
+ # variables (`HTTP_PROXY`, `HTTPS_PROXY`, `NO_PROXY`).
39
+ #
40
+ # @return [String, URI, Hash, nil]
41
+ attr_accessor :proxy
42
+
43
+ # The SSL configuration to use to connect to Sentry.
44
+ # You can either pass a `Hash` containing `ca_file` and `verification` keys,
45
+ # or you can set those options directly on the `Sentry::HTTPTransport::Configuration` object:
46
+ #
47
+ # @example
48
+ # config.transport.ssl = {
49
+ # ca_file: "/path/to/ca_file",
50
+ # verification: true
51
+ # end
52
+ #
53
+ # @return [Hash, nil]
54
+ attr_accessor :ssl
55
+
56
+ # The path to the CA file to use to verify the SSL connection.
57
+ # Default value is `nil`.
58
+ #
59
+ # @return [String, nil]
60
+ attr_accessor :ssl_ca_file
61
+
62
+ # Whether to verify that the peer certificate is valid in SSL connections.
63
+ # Default value is `true`.
64
+ #
65
+ # @return [Boolean]
66
+ attr_accessor :ssl_verification
67
+
68
+ # The encoding to use to compress the request body.
69
+ # Default value is `Sentry::HTTPTransport::GZIP_ENCODING`.
70
+ #
71
+ # @return [String]
72
+ attr_accessor :encoding
73
+
74
+ # The class to use as a transport to connect to Sentry.
75
+ # If this option not set, it will return `nil`, and Sentry will use
76
+ # `Sentry::HTTPTransport` by default.
77
+ #
78
+ # @return [Class, nil]
7
79
  attr_reader :transport_class
8
80
 
9
81
  def initialize
@@ -14,11 +14,19 @@ module Sentry
14
14
  RATE_LIMIT_HEADER = "x-sentry-rate-limits"
15
15
  USER_AGENT = "sentry-ruby/#{Sentry::VERSION}"
16
16
 
17
+ # The list of errors ::Net::HTTP is known to raise
18
+ # See https://github.com/ruby/ruby/blob/b0c639f249165d759596f9579fa985cb30533de6/lib/bundler/fetcher.rb#L281-L286
19
+ HTTP_ERRORS = [
20
+ Timeout::Error, EOFError, SocketError, Errno::ENETDOWN, Errno::ENETUNREACH,
21
+ Errno::EINVAL, Errno::ECONNRESET, Errno::ETIMEDOUT, Errno::EAGAIN,
22
+ Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError,
23
+ Zlib::BufError, Errno::EHOSTUNREACH, Errno::ECONNREFUSED
24
+ ].freeze
25
+
26
+
17
27
  def initialize(*args)
18
28
  super
19
- @endpoint = @dsn.envelope_endpoint
20
-
21
- log_debug("Sentry HTTP Transport will connect to #{@dsn.server}")
29
+ log_debug("Sentry HTTP Transport will connect to #{@dsn.server}") if @dsn
22
30
  end
23
31
 
24
32
  def send_data(data)
@@ -32,34 +40,76 @@ module Sentry
32
40
  headers = {
33
41
  'Content-Type' => CONTENT_TYPE,
34
42
  'Content-Encoding' => encoding,
35
- 'X-Sentry-Auth' => generate_auth_header,
36
43
  'User-Agent' => USER_AGENT
37
44
  }
38
45
 
46
+ auth_header = generate_auth_header
47
+ headers['X-Sentry-Auth'] = auth_header if auth_header
48
+
39
49
  response = conn.start do |http|
40
- request = ::Net::HTTP::Post.new(@endpoint, headers)
50
+ request = ::Net::HTTP::Post.new(endpoint, headers)
41
51
  request.body = data
42
52
  http.request(request)
43
53
  end
44
54
 
45
55
  if response.code.match?(/\A2\d{2}/)
46
- if has_rate_limited_header?(response)
47
- handle_rate_limited_response(response)
48
- end
56
+ handle_rate_limited_response(response) if has_rate_limited_header?(response)
57
+ elsif response.code == "429"
58
+ log_debug("the server responded with status 429")
59
+ handle_rate_limited_response(response)
49
60
  else
50
61
  error_info = "the server responded with status #{response.code}"
62
+ error_info += "\nbody: #{response.body}"
63
+ error_info += " Error in headers is: #{response['x-sentry-error']}" if response['x-sentry-error']
64
+
65
+ raise Sentry::ExternalError, error_info
66
+ end
67
+ rescue SocketError, *HTTP_ERRORS => e
68
+ on_error if respond_to?(:on_error)
69
+ raise Sentry::ExternalError.new(e&.message)
70
+ end
71
+
72
+ def endpoint
73
+ @dsn.envelope_endpoint
74
+ end
75
+
76
+ def generate_auth_header
77
+ return nil unless @dsn
78
+
79
+ now = Sentry.utc_now.to_i
80
+ fields = {
81
+ 'sentry_version' => PROTOCOL_VERSION,
82
+ 'sentry_client' => USER_AGENT,
83
+ 'sentry_timestamp' => now,
84
+ 'sentry_key' => @dsn.public_key
85
+ }
86
+ fields['sentry_secret'] = @dsn.secret_key if @dsn.secret_key
87
+ 'Sentry ' + fields.map { |key, value| "#{key}=#{value}" }.join(', ')
88
+ end
51
89
 
52
- if response.code == "429"
53
- handle_rate_limited_response(response)
90
+ def conn
91
+ server = URI(@dsn.server)
92
+
93
+ # connection respects proxy setting from @transport_configuration, or environment variables (HTTP_PROXY, HTTPS_PROXY, NO_PROXY)
94
+ # Net::HTTP will automatically read the env vars.
95
+ # See https://ruby-doc.org/3.2.2/stdlibs/net/Net/HTTP.html#class-Net::HTTP-label-Proxies
96
+ connection =
97
+ if proxy = normalize_proxy(@transport_configuration.proxy)
98
+ ::Net::HTTP.new(server.hostname, server.port, proxy[:uri].hostname, proxy[:uri].port, proxy[:user], proxy[:password])
54
99
  else
55
- error_info += "\nbody: #{response.body}"
56
- error_info += " Error in headers is: #{response['x-sentry-error']}" if response['x-sentry-error']
100
+ ::Net::HTTP.new(server.hostname, server.port)
57
101
  end
58
102
 
59
- raise Sentry::ExternalError, error_info
103
+ connection.use_ssl = server.scheme == "https"
104
+ connection.read_timeout = @transport_configuration.timeout
105
+ connection.write_timeout = @transport_configuration.timeout if connection.respond_to?(:write_timeout)
106
+ connection.open_timeout = @transport_configuration.open_timeout
107
+
108
+ ssl_configuration.each do |key, value|
109
+ connection.send("#{key}=", value)
60
110
  end
61
- rescue SocketError => e
62
- raise Sentry::ExternalError.new(e.message)
111
+
112
+ connection
63
113
  end
64
114
 
65
115
  private
@@ -126,28 +176,9 @@ module Sentry
126
176
  @transport_configuration.encoding == GZIP_ENCODING && data.bytesize >= GZIP_THRESHOLD
127
177
  end
128
178
 
129
- def conn
130
- server = URI(@dsn.server)
131
-
132
- connection =
133
- if proxy = normalize_proxy(@transport_configuration.proxy)
134
- ::Net::HTTP.new(server.hostname, server.port, proxy[:uri].hostname, proxy[:uri].port, proxy[:user], proxy[:password])
135
- else
136
- ::Net::HTTP.new(server.hostname, server.port, nil)
137
- end
138
-
139
- connection.use_ssl = server.scheme == "https"
140
- connection.read_timeout = @transport_configuration.timeout
141
- connection.write_timeout = @transport_configuration.timeout if connection.respond_to?(:write_timeout)
142
- connection.open_timeout = @transport_configuration.open_timeout
143
-
144
- ssl_configuration.each do |key, value|
145
- connection.send("#{key}=", value)
146
- end
147
-
148
- connection
149
- end
150
-
179
+ # @param proxy [String, URI, Hash] Proxy config value passed into `config.transport`.
180
+ # Accepts either a URI formatted string, URI, or a hash with the `uri`, `user`, and `password` keys.
181
+ # @return [Hash] Normalized proxy config that will be passed into `Net::HTTP`
151
182
  def normalize_proxy(proxy)
152
183
  return proxy unless proxy
153
184
 
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "net/http"
4
+ require "zlib"
5
+
6
+ module Sentry
7
+ # Designed to just report events to Spotlight in development.
8
+ class SpotlightTransport < HTTPTransport
9
+ DEFAULT_SIDECAR_URL = "http://localhost:8969/stream"
10
+ MAX_FAILED_REQUESTS = 3
11
+
12
+ def initialize(configuration)
13
+ super
14
+ @sidecar_url = configuration.spotlight.is_a?(String) ? configuration.spotlight : DEFAULT_SIDECAR_URL
15
+ @failed = 0
16
+ @logged = false
17
+
18
+ log_debug("[Spotlight] initialized for url #{@sidecar_url}")
19
+ end
20
+
21
+ def endpoint
22
+ "/stream"
23
+ end
24
+
25
+ def send_data(data)
26
+ if @failed >= MAX_FAILED_REQUESTS
27
+ unless @logged
28
+ log_debug("[Spotlight] disabling because of too many request failures")
29
+ @logged = true
30
+ end
31
+
32
+ return
33
+ end
34
+
35
+ super
36
+ end
37
+
38
+ def on_error
39
+ @failed += 1
40
+ end
41
+
42
+ # Similar to HTTPTransport connection, but does not support Proxy and SSL
43
+ def conn
44
+ sidecar = URI(@sidecar_url)
45
+ connection = ::Net::HTTP.new(sidecar.hostname, sidecar.port, nil)
46
+ connection.use_ssl = false
47
+ connection
48
+ end
49
+ end
50
+ end
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "json"
4
- require "base64"
5
4
  require "sentry/envelope"
6
5
 
7
6
  module Sentry
@@ -19,7 +18,8 @@ module Sentry
19
18
  :sample_rate,
20
19
  :before_send,
21
20
  :event_processor,
22
- :insufficient_data
21
+ :insufficient_data,
22
+ :backpressure
23
23
  ]
24
24
 
25
25
  include LoggingHelper
@@ -61,7 +61,7 @@ module Sentry
61
61
  data, serialized_items = serialize_envelope(envelope)
62
62
 
63
63
  if data
64
- log_info("[Transport] Sending envelope with items [#{serialized_items.map(&:type).join(', ')}] #{envelope.event_id} to Sentry")
64
+ log_debug("[Transport] Sending envelope with items [#{serialized_items.map(&:type).join(', ')}] #{envelope.event_id} to Sentry")
65
65
  send_data(data)
66
66
  end
67
67
  end
@@ -74,7 +74,7 @@ module Sentry
74
74
  result, oversized = item.serialize
75
75
 
76
76
  if oversized
77
- log_info("Envelope item [#{item.type}] is still oversized after size reduction: {#{item.size_breakdown}}")
77
+ log_debug("Envelope item [#{item.type}] is still oversized after size reduction: {#{item.size_breakdown}}")
78
78
 
79
79
  next
80
80
  end
@@ -88,18 +88,9 @@ module Sentry
88
88
  [data, serialized_items]
89
89
  end
90
90
 
91
- def is_rate_limited?(item_type)
91
+ def is_rate_limited?(data_category)
92
92
  # check category-specific limit
93
- category_delay =
94
- case item_type
95
- when "transaction"
96
- @rate_limits["transaction"]
97
- when "sessions"
98
- @rate_limits["session"]
99
- else
100
- @rate_limits["error"]
101
- end
102
-
93
+ category_delay = @rate_limits[data_category]
103
94
  # check universal limit if not category limit
104
95
  universal_delay = @rate_limits[nil]
105
96
 
@@ -119,16 +110,8 @@ module Sentry
119
110
  !!delay && delay > Time.now
120
111
  end
121
112
 
122
- def generate_auth_header
123
- now = Sentry.utc_now.to_i
124
- fields = {
125
- 'sentry_version' => PROTOCOL_VERSION,
126
- 'sentry_client' => USER_AGENT,
127
- 'sentry_timestamp' => now,
128
- 'sentry_key' => @dsn.public_key
129
- }
130
- fields['sentry_secret'] = @dsn.secret_key if @dsn.secret_key
131
- 'Sentry ' + fields.map { |key, value| "#{key}=#{value}" }.join(', ')
113
+ def any_rate_limited?
114
+ @rate_limits.values.any? { |t| t && t > Time.now }
132
115
  end
133
116
 
134
117
  def envelope_from_event(event)
@@ -162,32 +145,43 @@ module Sentry
162
145
  )
163
146
  end
164
147
 
148
+ if event.is_a?(Event) && event.attachments.any?
149
+ event.attachments.each do |attachment|
150
+ envelope.add_item(attachment.to_envelope_headers, attachment.payload)
151
+ end
152
+ end
153
+
165
154
  client_report_headers, client_report_payload = fetch_pending_client_report
166
155
  envelope.add_item(client_report_headers, client_report_payload) if client_report_headers
167
156
 
168
157
  envelope
169
158
  end
170
159
 
171
- def record_lost_event(reason, item_type)
160
+ def record_lost_event(reason, data_category, num: 1)
172
161
  return unless @send_client_reports
173
162
  return unless CLIENT_REPORT_REASONS.include?(reason)
174
163
 
175
- @discarded_events[[reason, item_type]] += 1
164
+ @discarded_events[[reason, data_category]] += num
165
+ end
166
+
167
+ def flush
168
+ client_report_headers, client_report_payload = fetch_pending_client_report(force: true)
169
+ return unless client_report_headers
170
+
171
+ envelope = Envelope.new
172
+ envelope.add_item(client_report_headers, client_report_payload)
173
+ send_envelope(envelope)
176
174
  end
177
175
 
178
176
  private
179
177
 
180
- def fetch_pending_client_report
178
+ def fetch_pending_client_report(force: false)
181
179
  return nil unless @send_client_reports
182
- return nil if @last_client_report_sent > Time.now - CLIENT_REPORT_INTERVAL
180
+ return nil if !force && @last_client_report_sent > Time.now - CLIENT_REPORT_INTERVAL
183
181
  return nil if @discarded_events.empty?
184
182
 
185
183
  discarded_events_hash = @discarded_events.map do |key, val|
186
- reason, type = key
187
-
188
- # 'event' has to be mapped to 'error'
189
- category = type == 'event' ? 'error' : type
190
-
184
+ reason, category = key
191
185
  { reason: reason, category: category, quantity: val }
192
186
  end
193
187
 
@@ -205,9 +199,9 @@ module Sentry
205
199
 
206
200
  def reject_rate_limited_items(envelope)
207
201
  envelope.items.reject! do |item|
208
- if is_rate_limited?(item.type)
209
- log_info("[Transport] Envelope item [#{item.type}] not sent: rate limiting")
210
- record_lost_event(:ratelimit_backoff, item.type)
202
+ if is_rate_limited?(item.data_category)
203
+ log_debug("[Transport] Envelope item [#{item.type}] not sent: rate limiting")
204
+ record_lost_event(:ratelimit_backoff, item.data_category)
211
205
 
212
206
  true
213
207
  else
@@ -220,3 +214,4 @@ end
220
214
 
221
215
  require "sentry/transport/dummy_transport"
222
216
  require "sentry/transport/http_transport"
217
+ require "sentry/transport/spotlight_transport"
@@ -15,5 +15,11 @@ module Sentry
15
15
  raise ArgumentError, "expect the argument to be one of #{values.map(&:inspect).join(' or ')}, got #{argument.inspect}"
16
16
  end
17
17
  end
18
+
19
+ def check_callable!(name, value)
20
+ unless value == nil || value.respond_to?(:call)
21
+ raise ArgumentError, "#{name} must be callable (or nil to disable)"
22
+ end
23
+ end
18
24
  end
19
25
  end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sentry
4
+ module Utils
5
+ module HttpTracing
6
+ def set_span_info(sentry_span, request_info, response_status)
7
+ sentry_span.set_description("#{request_info[:method]} #{request_info[:url]}")
8
+ sentry_span.set_data(Span::DataConventions::URL, request_info[:url])
9
+ sentry_span.set_data(Span::DataConventions::HTTP_METHOD, request_info[:method])
10
+ sentry_span.set_data(Span::DataConventions::HTTP_QUERY, request_info[:query]) if request_info[:query]
11
+ sentry_span.set_data(Span::DataConventions::HTTP_STATUS_CODE, response_status)
12
+ end
13
+
14
+ def set_propagation_headers(req)
15
+ Sentry.get_trace_propagation_headers&.each { |k, v| req[k] = v }
16
+ end
17
+
18
+ def record_sentry_breadcrumb(request_info, response_status)
19
+ crumb = Sentry::Breadcrumb.new(
20
+ level: :info,
21
+ category: self.class::BREADCRUMB_CATEGORY,
22
+ type: :info,
23
+ data: { status: response_status, **request_info }
24
+ )
25
+
26
+ Sentry.add_breadcrumb(crumb)
27
+ end
28
+
29
+ def record_sentry_breadcrumb?
30
+ Sentry.initialized? && Sentry.configuration.breadcrumbs_logger.include?(:http_logger)
31
+ end
32
+
33
+ def propagate_trace?(url)
34
+ url &&
35
+ Sentry.initialized? &&
36
+ Sentry.configuration.propagate_traces &&
37
+ Sentry.configuration.trace_propagation_targets.any? { |target| url.match?(target) }
38
+ end
39
+ end
40
+ end
41
+ end
@@ -11,10 +11,6 @@ module Sentry
11
11
  end
12
12
  end
13
13
 
14
- def log_info(message)
15
- @logger.info(LOGGER_PROGNAME) { message }
16
- end
17
-
18
14
  def log_debug(message)
19
15
  @logger.debug(LOGGER_PROGNAME) { message }
20
16
  end
@@ -15,7 +15,7 @@ module Sentry
15
15
  "fc00::/7", # private IPv6 range fc00::/7
16
16
  "10.0.0.0/8", # private IPv4 range 10.x.x.x
17
17
  "172.16.0.0/12", # private IPv4 range 172.16.0.0 .. 172.31.255.255
18
- "192.168.0.0/16", # private IPv4 range 192.168.x.x
18
+ "192.168.0.0/16" # private IPv4 range 192.168.x.x
19
19
  ]
20
20
 
21
21
  attr_reader :ip
@@ -3,7 +3,7 @@
3
3
  module Sentry
4
4
  module Utils
5
5
  module RequestId
6
- REQUEST_ID_HEADERS = %w(action_dispatch.request_id HTTP_X_REQUEST_ID).freeze
6
+ REQUEST_ID_HEADERS = %w[action_dispatch.request_id HTTP_X_REQUEST_ID].freeze
7
7
 
8
8
  # Request ID based on ActionDispatch::RequestId
9
9
  def self.read_from(env)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Sentry
4
- VERSION = "5.13.0"
4
+ VERSION = "5.19.0"
5
5
  end
data/lib/sentry-ruby.rb CHANGED
@@ -20,12 +20,15 @@ require "sentry/span"
20
20
  require "sentry/transaction"
21
21
  require "sentry/hub"
22
22
  require "sentry/background_worker"
23
+ require "sentry/threaded_periodic_worker"
23
24
  require "sentry/session_flusher"
25
+ require "sentry/backpressure_monitor"
24
26
  require "sentry/cron/monitor_check_ins"
27
+ require "sentry/metrics"
25
28
 
26
29
  [
27
30
  "sentry/rake",
28
- "sentry/rack",
31
+ "sentry/rack"
29
32
  ].each do |lib|
30
33
  begin
31
34
  require lib
@@ -65,13 +68,21 @@ module Sentry
65
68
  end
66
69
 
67
70
  # @!attribute [rw] background_worker
68
- # @return [BackgroundWorker, nil]
71
+ # @return [BackgroundWorker]
69
72
  attr_accessor :background_worker
70
73
 
71
74
  # @!attribute [r] session_flusher
72
75
  # @return [SessionFlusher, nil]
73
76
  attr_reader :session_flusher
74
77
 
78
+ # @!attribute [r] backpressure_monitor
79
+ # @return [BackpressureMonitor, nil]
80
+ attr_reader :backpressure_monitor
81
+
82
+ # @!attribute [r] metrics_aggregator
83
+ # @return [Metrics::Aggregator, nil]
84
+ attr_reader :metrics_aggregator
85
+
75
86
  ##### Patch Registration #####
76
87
 
77
88
  # @!visibility private
@@ -200,6 +211,13 @@ module Sentry
200
211
  get_current_scope.set_context(*args)
201
212
  end
202
213
 
214
+ # @!method add_attachment
215
+ # @!macro add_attachment
216
+ def add_attachment(**opts)
217
+ return unless initialized?
218
+ get_current_scope.add_attachment(**opts)
219
+ end
220
+
203
221
  ##### Main APIs #####
204
222
 
205
223
  # Initializes the SDK with given configuration.
@@ -217,17 +235,10 @@ module Sentry
217
235
  Thread.current.thread_variable_set(THREAD_LOCAL, hub)
218
236
  @main_hub = hub
219
237
  @background_worker = Sentry::BackgroundWorker.new(config)
220
-
221
- @session_flusher = if config.auto_session_tracking
222
- Sentry::SessionFlusher.new(config, client)
223
- else
224
- nil
225
- end
226
-
227
- if config.include_local_variables
228
- exception_locals_tp.enable
229
- end
230
-
238
+ @session_flusher = config.session_tracking? ? Sentry::SessionFlusher.new(config, client) : nil
239
+ @backpressure_monitor = config.enable_backpressure_handling ? Sentry::BackpressureMonitor.new(config, client) : nil
240
+ @metrics_aggregator = config.metrics.enabled ? Sentry::Metrics::Aggregator.new(config, client) : nil
241
+ exception_locals_tp.enable if config.include_local_variables
231
242
  at_exit { close }
232
243
  end
233
244
 
@@ -236,20 +247,33 @@ module Sentry
236
247
  #
237
248
  # @return [void]
238
249
  def close
239
- if @background_worker
240
- @background_worker.shutdown
241
- @background_worker = nil
242
- end
243
-
244
250
  if @session_flusher
251
+ @session_flusher.flush
245
252
  @session_flusher.kill
246
253
  @session_flusher = nil
247
254
  end
248
255
 
249
- if configuration&.include_local_variables
250
- exception_locals_tp.disable
256
+ if @backpressure_monitor
257
+ @backpressure_monitor.kill
258
+ @backpressure_monitor = nil
259
+ end
260
+
261
+ if @metrics_aggregator
262
+ @metrics_aggregator.flush(force: true)
263
+ @metrics_aggregator.kill
264
+ @metrics_aggregator = nil
251
265
  end
252
266
 
267
+ if client = get_current_client
268
+ client.flush
269
+
270
+ if client.configuration.include_local_variables
271
+ exception_locals_tp.disable
272
+ end
273
+ end
274
+
275
+ @background_worker.shutdown
276
+
253
277
  @main_hub = nil
254
278
  Thread.current.thread_variable_set(THREAD_LOCAL, nil)
255
279
  end
@@ -442,12 +466,10 @@ module Sentry
442
466
  # @option options [Integer] duration seconds elapsed since this monitor started
443
467
  # @option options [Cron::MonitorConfig] monitor_config configuration for this monitor
444
468
  #
445
- # @yieldparam scope [Scope]
446
- #
447
469
  # @return [String, nil] The {CheckInEvent#check_in_id} to use for later updates on the same slug
448
- def capture_check_in(slug, status, **options, &block)
470
+ def capture_check_in(slug, status, **options)
449
471
  return unless initialized?
450
- get_current_hub.capture_check_in(slug, status, **options, &block)
472
+ get_current_hub.capture_check_in(slug, status, **options)
451
473
  end
452
474
 
453
475
  # Takes or initializes a new Sentry::Transaction and makes a sampling decision for it.
@@ -536,6 +558,15 @@ module Sentry
536
558
  get_current_hub.get_trace_propagation_headers
537
559
  end
538
560
 
561
+ # Returns the a Hash containing sentry-trace and baggage.
562
+ # Can be either from the currently active span or the propagation context.
563
+ #
564
+ # @return [String]
565
+ def get_trace_propagation_meta
566
+ return '' unless initialized?
567
+ get_current_hub.get_trace_propagation_meta
568
+ end
569
+
539
570
  # Continue an incoming trace from a rack env like hash.
540
571
  #
541
572
  # @param env [Hash]
@@ -576,3 +607,5 @@ end
576
607
  require "sentry/net/http"
577
608
  require "sentry/redis"
578
609
  require "sentry/puma"
610
+ require "sentry/graphql"
611
+ require "sentry/faraday"