sentry-ruby 5.16.1 → 5.19.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 (62) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +3 -0
  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 +1 -1
  8. data/lib/sentry/backpressure_monitor.rb +2 -32
  9. data/lib/sentry/backtrace.rb +7 -3
  10. data/lib/sentry/check_in_event.rb +1 -1
  11. data/lib/sentry/client.rb +59 -9
  12. data/lib/sentry/configuration.rb +25 -12
  13. data/lib/sentry/cron/monitor_schedule.rb +1 -1
  14. data/lib/sentry/dsn.rb +1 -1
  15. data/lib/sentry/envelope.rb +18 -1
  16. data/lib/sentry/error_event.rb +2 -2
  17. data/lib/sentry/event.rb +13 -11
  18. data/lib/sentry/faraday.rb +77 -0
  19. data/lib/sentry/graphql.rb +9 -0
  20. data/lib/sentry/hub.rb +15 -2
  21. data/lib/sentry/integrable.rb +4 -0
  22. data/lib/sentry/interface.rb +1 -0
  23. data/lib/sentry/interfaces/exception.rb +5 -3
  24. data/lib/sentry/interfaces/mechanism.rb +20 -0
  25. data/lib/sentry/interfaces/request.rb +2 -2
  26. data/lib/sentry/interfaces/single_exception.rb +6 -4
  27. data/lib/sentry/interfaces/stacktrace_builder.rb +8 -0
  28. data/lib/sentry/metrics/aggregator.rb +248 -0
  29. data/lib/sentry/metrics/configuration.rb +47 -0
  30. data/lib/sentry/metrics/counter_metric.rb +25 -0
  31. data/lib/sentry/metrics/distribution_metric.rb +25 -0
  32. data/lib/sentry/metrics/gauge_metric.rb +35 -0
  33. data/lib/sentry/metrics/local_aggregator.rb +53 -0
  34. data/lib/sentry/metrics/metric.rb +19 -0
  35. data/lib/sentry/metrics/set_metric.rb +28 -0
  36. data/lib/sentry/metrics/timing.rb +43 -0
  37. data/lib/sentry/metrics.rb +56 -0
  38. data/lib/sentry/net/http.rb +17 -38
  39. data/lib/sentry/propagation_context.rb +9 -8
  40. data/lib/sentry/puma.rb +1 -1
  41. data/lib/sentry/rack/capture_exceptions.rb +14 -2
  42. data/lib/sentry/rake.rb +3 -1
  43. data/lib/sentry/redis.rb +2 -1
  44. data/lib/sentry/scope.rb +35 -26
  45. data/lib/sentry/session.rb +2 -2
  46. data/lib/sentry/session_flusher.rb +6 -38
  47. data/lib/sentry/span.rb +40 -5
  48. data/lib/sentry/test_helper.rb +2 -1
  49. data/lib/sentry/threaded_periodic_worker.rb +39 -0
  50. data/lib/sentry/transaction.rb +16 -14
  51. data/lib/sentry/transaction_event.rb +5 -0
  52. data/lib/sentry/transport/configuration.rb +0 -1
  53. data/lib/sentry/transport.rb +14 -22
  54. data/lib/sentry/utils/argument_checking_helper.rb +6 -0
  55. data/lib/sentry/utils/http_tracing.rb +41 -0
  56. data/lib/sentry/utils/logging_helper.rb +0 -4
  57. data/lib/sentry/utils/real_ip.rb +1 -1
  58. data/lib/sentry/utils/request_id.rb +1 -1
  59. data/lib/sentry/version.rb +1 -1
  60. data/lib/sentry-ruby.rb +34 -3
  61. data/sentry-ruby.gemspec +12 -5
  62. metadata +39 -7
data/lib/sentry/span.rb CHANGED
@@ -1,10 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "securerandom"
4
+ require "sentry/metrics/local_aggregator"
4
5
 
5
6
  module Sentry
6
7
  class Span
7
-
8
8
  # We will try to be consistent with OpenTelemetry on this front going forward.
9
9
  # https://develop.sentry.dev/sdk/performance/span-data-conventions/
10
10
  module DataConventions
@@ -39,6 +39,11 @@ module Sentry
39
39
  # Recommended: If different than server.port.
40
40
  # Example: 16456
41
41
  SERVER_SOCKET_PORT = "server.socket.port"
42
+
43
+ FILEPATH = "code.filepath"
44
+ LINENO = "code.lineno"
45
+ FUNCTION = "code.function"
46
+ NAMESPACE = "code.namespace"
42
47
  end
43
48
 
44
49
  STATUS_MAP = {
@@ -55,6 +60,8 @@ module Sentry
55
60
  504 => "deadline_exceeded"
56
61
  }
57
62
 
63
+ DEFAULT_SPAN_ORIGIN = "manual"
64
+
58
65
  # An uuid that can be used to identify a trace.
59
66
  # @return [String]
60
67
  attr_reader :trace_id
@@ -88,6 +95,9 @@ module Sentry
88
95
  # Span data
89
96
  # @return [Hash]
90
97
  attr_reader :data
98
+ # Span origin that tracks what kind of instrumentation created a span
99
+ # @return [String]
100
+ attr_reader :origin
91
101
 
92
102
  # The SpanRecorder the current span belongs to.
93
103
  # SpanRecorder holds all spans under the same Transaction object (including the Transaction itself).
@@ -109,7 +119,8 @@ module Sentry
109
119
  parent_span_id: nil,
110
120
  sampled: nil,
111
121
  start_timestamp: nil,
112
- timestamp: nil
122
+ timestamp: nil,
123
+ origin: nil
113
124
  )
114
125
  @trace_id = trace_id || SecureRandom.uuid.delete("-")
115
126
  @span_id = span_id || SecureRandom.uuid.delete("-").slice(0, 16)
@@ -123,6 +134,7 @@ module Sentry
123
134
  @status = status
124
135
  @data = {}
125
136
  @tags = {}
137
+ @origin = origin || DEFAULT_SPAN_ORIGIN
126
138
  end
127
139
 
128
140
  # Finishes the span by adding a timestamp.
@@ -150,7 +162,7 @@ module Sentry
150
162
 
151
163
  # @return [Hash]
152
164
  def to_hash
153
- {
165
+ hash = {
154
166
  trace_id: @trace_id,
155
167
  span_id: @span_id,
156
168
  parent_span_id: @parent_span_id,
@@ -160,8 +172,14 @@ module Sentry
160
172
  op: @op,
161
173
  status: @status,
162
174
  tags: @tags,
163
- data: @data
175
+ data: @data,
176
+ origin: @origin
164
177
  }
178
+
179
+ summary = metrics_summary
180
+ hash[:_metrics_summary] = summary if summary
181
+
182
+ hash
165
183
  end
166
184
 
167
185
  # Returns the span's context that can be used to embed in an Event.
@@ -173,7 +191,9 @@ module Sentry
173
191
  parent_span_id: @parent_span_id,
174
192
  description: @description,
175
193
  op: @op,
176
- status: @status
194
+ status: @status,
195
+ origin: @origin,
196
+ data: @data
177
197
  }
178
198
  end
179
199
 
@@ -269,5 +289,20 @@ module Sentry
269
289
  def set_tag(key, value)
270
290
  @tags[key] = value
271
291
  end
292
+
293
+ # Sets the origin of the span.
294
+ # @param origin [String]
295
+ def set_origin(origin)
296
+ @origin = origin
297
+ end
298
+
299
+ # Collects gauge metrics on the span for metric summaries.
300
+ def metrics_local_aggregator
301
+ @metrics_local_aggregator ||= Sentry::Metrics::LocalAggregator.new
302
+ end
303
+
304
+ def metrics_summary
305
+ @metrics_local_aggregator&.to_hash
306
+ end
272
307
  end
273
308
  end
@@ -20,7 +20,7 @@ module Sentry
20
20
  # set transport to DummyTransport, so we can easily intercept the captured events
21
21
  dummy_config.transport.transport_class = Sentry::DummyTransport
22
22
  # make sure SDK allows sending under the current environment
23
- dummy_config.enabled_environments << dummy_config.environment unless dummy_config.enabled_environments.include?(dummy_config.environment)
23
+ dummy_config.enabled_environments += [dummy_config.environment] unless dummy_config.enabled_environments.include?(dummy_config.environment)
24
24
  # disble async event sending
25
25
  dummy_config.background_worker_threads = 0
26
26
 
@@ -50,6 +50,7 @@ module Sentry
50
50
  if Sentry.get_current_hub.instance_variable_get(:@stack).size > 1
51
51
  Sentry.get_current_hub.pop_scope
52
52
  end
53
+ Sentry::Scope.global_event_processors.clear
53
54
  end
54
55
 
55
56
  # @return [Transport]
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sentry
4
+ class ThreadedPeriodicWorker
5
+ include LoggingHelper
6
+
7
+ def initialize(logger, internal)
8
+ @thread = nil
9
+ @exited = false
10
+ @interval = internal
11
+ @logger = logger
12
+ end
13
+
14
+ def ensure_thread
15
+ return false if @exited
16
+ return true if @thread&.alive?
17
+
18
+ @thread = Thread.new do
19
+ loop do
20
+ sleep(@interval)
21
+ run
22
+ end
23
+ end
24
+
25
+ true
26
+ rescue ThreadError
27
+ log_debug("[#{self.class.name}] thread creation failed")
28
+ @exited = true
29
+ false
30
+ end
31
+
32
+ def kill
33
+ log_debug("[#{self.class.name}] thread killed")
34
+
35
+ @exited = true
36
+ @thread&.kill
37
+ end
38
+ end
39
+ end
@@ -13,7 +13,7 @@ module Sentry
13
13
  MESSAGE_PREFIX = "[Tracing]"
14
14
 
15
15
  # https://develop.sentry.dev/sdk/event-payloads/transaction/#transaction-annotations
16
- SOURCES = %i(custom url route view component task)
16
+ SOURCES = %i[custom url route view component task]
17
17
 
18
18
  include LoggingHelper
19
19
 
@@ -110,14 +110,15 @@ module Sentry
110
110
 
111
111
  trace_id, parent_span_id, parent_sampled = sentry_trace_data
112
112
 
113
- baggage = if baggage && !baggage.empty?
114
- Baggage.from_incoming_header(baggage)
115
- else
116
- # If there's an incoming sentry-trace but no incoming baggage header,
117
- # for instance in traces coming from older SDKs,
118
- # baggage will be empty and frozen and won't be populated as head SDK.
119
- Baggage.new({})
120
- end
113
+ baggage =
114
+ if baggage && !baggage.empty?
115
+ Baggage.from_incoming_header(baggage)
116
+ else
117
+ # If there's an incoming sentry-trace but no incoming baggage header,
118
+ # for instance in traces coming from older SDKs,
119
+ # baggage will be empty and frozen and won't be populated as head SDK.
120
+ Baggage.new({})
121
+ end
121
122
 
122
123
  baggage.freeze!
123
124
 
@@ -265,6 +266,7 @@ module Sentry
265
266
  is_backpressure = Sentry.backpressure_monitor&.downsample_factor&.positive?
266
267
  reason = is_backpressure ? :backpressure : :sample_rate
267
268
  hub.current_client.transport.record_lost_event(reason, 'transaction')
269
+ hub.current_client.transport.record_lost_event(reason, 'span')
268
270
  end
269
271
  end
270
272
 
@@ -301,6 +303,11 @@ module Sentry
301
303
  profiler.start
302
304
  end
303
305
 
306
+ # These are high cardinality and thus bad
307
+ def source_low_quality?
308
+ source == :url
309
+ end
310
+
304
311
  protected
305
312
 
306
313
  def init_span_recorder(limit = 1000)
@@ -336,11 +343,6 @@ module Sentry
336
343
  @baggage = Baggage.new(items, mutable: false)
337
344
  end
338
345
 
339
- # These are high cardinality and thus bad
340
- def source_low_quality?
341
- source == :url
342
- end
343
-
344
346
  class SpanRecorder
345
347
  attr_reader :max_length, :spans
346
348
 
@@ -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,6 @@
3
3
  module Sentry
4
4
  class Transport
5
5
  class Configuration
6
-
7
6
  # The timeout in seconds to open a connection to Sentry, in seconds.
8
7
  # Default value is 2.
9
8
  #
@@ -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
@@ -62,7 +61,7 @@ module Sentry
62
61
  data, serialized_items = serialize_envelope(envelope)
63
62
 
64
63
  if data
65
- 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")
66
65
  send_data(data)
67
66
  end
68
67
  end
@@ -89,18 +88,9 @@ module Sentry
89
88
  [data, serialized_items]
90
89
  end
91
90
 
92
- def is_rate_limited?(item_type)
91
+ def is_rate_limited?(data_category)
93
92
  # check category-specific limit
94
- category_delay =
95
- case item_type
96
- when "transaction"
97
- @rate_limits["transaction"]
98
- when "sessions"
99
- @rate_limits["session"]
100
- else
101
- @rate_limits["error"]
102
- end
103
-
93
+ category_delay = @rate_limits[data_category]
104
94
  # check universal limit if not category limit
105
95
  universal_delay = @rate_limits[nil]
106
96
 
@@ -155,17 +145,23 @@ module Sentry
155
145
  )
156
146
  end
157
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
+
158
154
  client_report_headers, client_report_payload = fetch_pending_client_report
159
155
  envelope.add_item(client_report_headers, client_report_payload) if client_report_headers
160
156
 
161
157
  envelope
162
158
  end
163
159
 
164
- def record_lost_event(reason, item_type)
160
+ def record_lost_event(reason, data_category, num: 1)
165
161
  return unless @send_client_reports
166
162
  return unless CLIENT_REPORT_REASONS.include?(reason)
167
163
 
168
- @discarded_events[[reason, item_type]] += 1
164
+ @discarded_events[[reason, data_category]] += num
169
165
  end
170
166
 
171
167
  def flush
@@ -185,11 +181,7 @@ module Sentry
185
181
  return nil if @discarded_events.empty?
186
182
 
187
183
  discarded_events_hash = @discarded_events.map do |key, val|
188
- reason, type = key
189
-
190
- # 'event' has to be mapped to 'error'
191
- category = type == 'event' ? 'error' : type
192
-
184
+ reason, category = key
193
185
  { reason: reason, category: category, quantity: val }
194
186
  end
195
187
 
@@ -207,9 +199,9 @@ module Sentry
207
199
 
208
200
  def reject_rate_limited_items(envelope)
209
201
  envelope.items.reject! do |item|
210
- if is_rate_limited?(item.type)
202
+ if is_rate_limited?(item.data_category)
211
203
  log_debug("[Transport] Envelope item [#{item.type}] not sent: rate limiting")
212
- record_lost_event(:ratelimit_backoff, item.type)
204
+ record_lost_event(:ratelimit_backoff, item.data_category)
213
205
 
214
206
  true
215
207
  else
@@ -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.16.1"
4
+ VERSION = "5.19.0"
5
5
  end
data/lib/sentry-ruby.rb CHANGED
@@ -20,13 +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"
24
25
  require "sentry/backpressure_monitor"
25
26
  require "sentry/cron/monitor_check_ins"
27
+ require "sentry/metrics"
26
28
 
27
29
  [
28
30
  "sentry/rake",
29
- "sentry/rack",
31
+ "sentry/rack"
30
32
  ].each do |lib|
31
33
  begin
32
34
  require lib
@@ -77,6 +79,10 @@ module Sentry
77
79
  # @return [BackpressureMonitor, nil]
78
80
  attr_reader :backpressure_monitor
79
81
 
82
+ # @!attribute [r] metrics_aggregator
83
+ # @return [Metrics::Aggregator, nil]
84
+ attr_reader :metrics_aggregator
85
+
80
86
  ##### Patch Registration #####
81
87
 
82
88
  # @!visibility private
@@ -205,6 +211,13 @@ module Sentry
205
211
  get_current_scope.set_context(*args)
206
212
  end
207
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
+
208
221
  ##### Main APIs #####
209
222
 
210
223
  # Initializes the SDK with given configuration.
@@ -222,8 +235,9 @@ module Sentry
222
235
  Thread.current.thread_variable_set(THREAD_LOCAL, hub)
223
236
  @main_hub = hub
224
237
  @background_worker = Sentry::BackgroundWorker.new(config)
225
- @session_flusher = config.auto_session_tracking ? Sentry::SessionFlusher.new(config, client) : nil
238
+ @session_flusher = config.session_tracking? ? Sentry::SessionFlusher.new(config, client) : nil
226
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
227
241
  exception_locals_tp.enable if config.include_local_variables
228
242
  at_exit { close }
229
243
  end
@@ -244,8 +258,14 @@ module Sentry
244
258
  @backpressure_monitor = nil
245
259
  end
246
260
 
261
+ if @metrics_aggregator
262
+ @metrics_aggregator.flush(force: true)
263
+ @metrics_aggregator.kill
264
+ @metrics_aggregator = nil
265
+ end
266
+
247
267
  if client = get_current_client
248
- client.transport.flush
268
+ client.flush
249
269
 
250
270
  if client.configuration.include_local_variables
251
271
  exception_locals_tp.disable
@@ -538,6 +558,15 @@ module Sentry
538
558
  get_current_hub.get_trace_propagation_headers
539
559
  end
540
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
+
541
570
  # Continue an incoming trace from a rack env like hash.
542
571
  #
543
572
  # @param env [Hash]
@@ -578,3 +607,5 @@ end
578
607
  require "sentry/net/http"
579
608
  require "sentry/redis"
580
609
  require "sentry/puma"
610
+ require "sentry/graphql"
611
+ require "sentry/faraday"
data/sentry-ruby.gemspec CHANGED
@@ -7,18 +7,25 @@ Gem::Specification.new do |spec|
7
7
  spec.description = spec.summary = "A gem that provides a client interface for the Sentry error logger"
8
8
  spec.email = "accounts@sentry.io"
9
9
  spec.license = 'MIT'
10
- spec.homepage = "https://github.com/getsentry/sentry-ruby"
11
10
 
12
11
  spec.platform = Gem::Platform::RUBY
13
12
  spec.required_ruby_version = '>= 2.4'
14
13
  spec.extra_rdoc_files = ["README.md", "LICENSE.txt"]
15
14
  spec.files = `git ls-files | grep -Ev '^(spec|benchmarks|examples)'`.split("\n")
16
15
 
17
- spec.metadata["homepage_uri"] = spec.homepage
18
- spec.metadata["source_code_uri"] = spec.homepage
19
- spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/master/CHANGELOG.md"
16
+ github_root_uri = 'https://github.com/getsentry/sentry-ruby'
17
+ spec.homepage = "#{github_root_uri}/tree/#{spec.version}/#{spec.name}"
18
+
19
+ spec.metadata = {
20
+ "homepage_uri" => spec.homepage,
21
+ "source_code_uri" => spec.homepage,
22
+ "changelog_uri" => "#{github_root_uri}/blob/#{spec.version}/CHANGELOG.md",
23
+ "bug_tracker_uri" => "#{github_root_uri}/issues",
24
+ "documentation_uri" => "http://www.rubydoc.info/gems/#{spec.name}/#{spec.version}"
25
+ }
20
26
 
21
27
  spec.require_paths = ["lib"]
22
28
 
23
- spec.add_dependency "concurrent-ruby", '~> 1.0', '>= 1.0.2'
29
+ spec.add_dependency "concurrent-ruby", "~> 1.0", ">= 1.0.2"
30
+ spec.add_dependency "bigdecimal"
24
31
  end